feat(udpscope): per-element timestamp reconstruction from UDPS frames
Implements FrameDecoder with five timing rules that mirror UDPSourceSession.cpp: FullArray (per-element time signal), FirstSample and LastSample (rate-spread from anchor), accumulated scalar with declared rate (forward-chain anchoring, immune to arrival jitter), and PACKET burst (backward-span from previous arrival). 9 new tests, 38 pass total. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
c89decef8e
commit
5a8479cda9
@@ -35,4 +35,69 @@ struct Rect {
|
||||
}
|
||||
};
|
||||
|
||||
/* Protocol constants, spelled out rather than included, so the framework-free
|
||||
* modules stay independent of udps_client.h. They mirror Common/UDP/UDPSProtocol.h. */
|
||||
constexpr uint8_t kTimePacket = 0;
|
||||
constexpr uint8_t kTimeFullArray = 1;
|
||||
constexpr uint8_t kTimeFirstSample = 2;
|
||||
constexpr uint8_t kTimeLastSample = 3;
|
||||
constexpr uint32_t kNoTimeSignal = 0xFFFFFFFFu;
|
||||
|
||||
/** Framework-free mirror of udps_signal_t, plus UI state. */
|
||||
struct SignalMeta {
|
||||
std::string name;
|
||||
uint8_t typeCode = 255;
|
||||
uint8_t quantType = 0;
|
||||
uint32_t numRows = 1;
|
||||
uint32_t numCols = 1;
|
||||
double rangeMin = 0.0;
|
||||
double rangeMax = 0.0;
|
||||
uint8_t timeMode = kTimePacket;
|
||||
double samplingRate = 0.0;
|
||||
uint32_t timeSignalIdx = kNoTimeSignal;
|
||||
std::string unit;
|
||||
|
||||
/** User override: treat an ambiguous PACKET array as a profile, not a burst. */
|
||||
bool profileOverride = false;
|
||||
|
||||
uint32_t numElements() const {
|
||||
const uint64_t n = static_cast<uint64_t>(numRows ? numRows : 1u) *
|
||||
static_cast<uint64_t>(numCols ? numCols : 1u);
|
||||
return n == 0u ? 1u : static_cast<uint32_t>(n);
|
||||
}
|
||||
|
||||
bool hasTimeSignal(uint32_t numSignals) const {
|
||||
return timeSignalIdx != kNoTimeSignal && timeSignalIdx < numSignals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief True when this array should be plotted against element index
|
||||
* rather than unrolled onto the time axis.
|
||||
*
|
||||
* Only PACKET arrays are ambiguous: the producer stamped the whole datagram
|
||||
* with one time, which is what a genuine vector looks like and also what a
|
||||
* burst carrying no time metadata looks like. Default is burst, matching
|
||||
* UDPSourceSession, with this flag as the user's override.
|
||||
*/
|
||||
bool isVectorProfile() const {
|
||||
return profileOverride && numElements() > 1u && timeMode == kTimePacket;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Non-owning mirror of udps_frame_t.
|
||||
*
|
||||
* Kept separate from the C struct so FrameDecoder can be tested with plain
|
||||
* arrays and no socket. Points at memory owned by the caller.
|
||||
*/
|
||||
struct FrameView {
|
||||
uint32_t counter = 0;
|
||||
uint64_t hrt = 0;
|
||||
double recvTime = 0.0;
|
||||
uint32_t numSamples = 1;
|
||||
uint32_t numSignals = 0;
|
||||
const double* const* values = nullptr; /**< values[i][0..counts[i]) */
|
||||
const uint32_t* counts = nullptr;
|
||||
};
|
||||
|
||||
} /* namespace udpscope */
|
||||
|
||||
Reference in New Issue
Block a user