001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.targeting;
019
020import org.photonvision.common.dataflow.structures.PacketSerde;
021import org.photonvision.struct.PhotonPipelineMetadataSerde;
022import org.photonvision.targeting.serde.PhotonStructSerializable;
023
024public class PhotonPipelineMetadata implements PhotonStructSerializable<PhotonPipelineMetadata> {
025    // Image capture and NT publish timestamp, in microseconds
026    // The timebase is nt::Now on the time sync server
027    public long captureTimestampMicros;
028    public long publishTimestampMicros;
029
030    // Mirror of the heartbeat entry -- monotonically increasing
031    public long sequenceID;
032
033    // Time from last Time Sync Pong received and the construction of this metadata, in uS
034    public long timeSinceLastPong;
035
036    public PhotonPipelineMetadata(
037            long captureTimestampMicros,
038            long publishTimestampMicros,
039            long sequenceID,
040            long timeSinceLastPong) {
041        this.captureTimestampMicros = captureTimestampMicros;
042        this.publishTimestampMicros = publishTimestampMicros;
043        this.sequenceID = sequenceID;
044        this.timeSinceLastPong = timeSinceLastPong;
045    }
046
047    public PhotonPipelineMetadata() {
048        this(-1, -1, -1, Long.MAX_VALUE);
049    }
050
051    /**
052     * Returns the time between image capture and publish to NT
053     *
054     * @return The time in milliseconds
055     */
056    public double getLatencyMillis() {
057        return (publishTimestampMicros - captureTimestampMicros) / 1e3;
058    }
059
060    /**
061     * The time that this image was captured, in the coprocessor's time base.
062     *
063     * @return The time in microseconds
064     */
065    public long getCaptureTimestampMicros() {
066        return captureTimestampMicros;
067    }
068
069    /**
070     * The time that this result was published to NT, in the coprocessor's time base.
071     *
072     * @return The time in microseconds
073     */
074    public long getPublishTimestampMicros() {
075        return publishTimestampMicros;
076    }
077
078    /**
079     * The number of non-empty frames processed by this camera since boot. Useful to checking if a
080     * camera is alive.
081     *
082     * @return The number of non-empty frames
083     */
084    public long getSequenceID() {
085        return sequenceID;
086    }
087
088    @Override
089    public String toString() {
090        return "PhotonPipelineMetadata [captureTimestampMicros="
091                + captureTimestampMicros
092                + ", publishTimestampMicros="
093                + publishTimestampMicros
094                + ", sequenceID="
095                + sequenceID
096                + ", timeSinceLastPong="
097                + timeSinceLastPong
098                + "]";
099    }
100
101    @Override
102    public int hashCode() {
103        final int prime = 31;
104        int result = 1;
105        result = prime * result + (int) (captureTimestampMicros ^ (captureTimestampMicros >>> 32));
106        result = prime * result + (int) (publishTimestampMicros ^ (publishTimestampMicros >>> 32));
107        result = prime * result + (int) (sequenceID ^ (sequenceID >>> 32));
108        result = prime * result + (int) (timeSinceLastPong ^ (timeSinceLastPong >>> 32));
109        return result;
110    }
111
112    @Override
113    public boolean equals(Object obj) {
114        if (this == obj) return true;
115        if (obj == null) return false;
116        if (getClass() != obj.getClass()) return false;
117        PhotonPipelineMetadata other = (PhotonPipelineMetadata) obj;
118        if (captureTimestampMicros != other.captureTimestampMicros) return false;
119        if (publishTimestampMicros != other.publishTimestampMicros) return false;
120        if (sequenceID != other.sequenceID) return false;
121        if (timeSinceLastPong != other.timeSinceLastPong) return false;
122        return true;
123    }
124
125    public static final PhotonPipelineMetadataSerde photonStruct = new PhotonPipelineMetadataSerde();
126
127    @Override
128    public PacketSerde<PhotonPipelineMetadata> getSerde() {
129        return photonStruct;
130    }
131}