/** * @file FrameDecoder.h * @brief Per-element timestamp reconstruction for UDPS frames. * * The C client's udps_frame_element_time() is explicitly an arrival-anchored * estimate. It is not sufficient: the kernel frequently delivers several queued * datagrams in one burst, so two packets are processed microseconds apart even * though each represents ~10 ms of signal, and arrival-time interpolation then * crams a packet's samples into that tiny gap — the trace renders as a sawtooth. * Source/Applications/StreamHub/UDPSourceSession.cpp documents this failure and * solves it; these are the same rules, computed from udps_frame_t's own fields * so the scope and StreamHub agree on the same stream. */ #pragma once #include "TimeBase.h" #include "Types.h" #include namespace udpscope { class FrameDecoder { public: /** Installs the signal table. Clears all per-signal timing history. */ void setSignals(const std::vector& signals); const std::vector& signals() const { return signals_; } /** Call once per frame, before any timestamps() call for that frame. */ void beginFrame(const FrameView& f); /** * @brief Timestamps for every value of signal @p idx in this frame. * @return false when the signal produced nothing usable — an empty slot, or * the first PACKET burst after connect, which has no previous * arrival to span from and would otherwise poison the ring with * wrongly spaced timestamps. */ bool timestamps(const FrameView& f, uint32_t idx, std::vector& tsOut); /** Forgets all timing history; call on reconnect. */ void reset(); private: bool packetBurst(uint32_t idx, uint32_t nElems, double wallNow, std::vector& tsOut); struct SigState { ClockOffset offset; double lastPacketWall = 0.0; bool lastPacketValid = false; double lastAccHrtSec = 0.0; bool lastAccValid = false; uint32_t prevAccCount = 0; /** For accumulated scalars with a declared sampling rate: end timestamp * of the most recently emitted burst. The next burst is PREDICTED to * start one sample period after it — immune to arrival-time jitter — * but the prediction is discarded when arrival time disagrees with it * by more than a delivery backlog can explain, so packet loss cannot * displace the trace permanently. */ double lastEmittedEnd = 0.0; bool lastEmittedValid = false; }; std::vector signals_; std::vector state_; HrtRateFit hrtFit_; }; } /* namespace udpscope */