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>
104 lines
3.3 KiB
C++
104 lines
3.3 KiB
C++
/**
|
|
* @file Types.h
|
|
* @brief Plain data shared across UDPScope modules. No logic, no dependencies.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace udpscope {
|
|
|
|
/** A time series as two parallel arrays, which is what ImPlot wants. */
|
|
struct Series {
|
|
std::vector<double> t;
|
|
std::vector<double> v;
|
|
|
|
void clear() { t.clear(); v.clear(); }
|
|
size_t size() const { return t.size(); }
|
|
bool empty() const { return t.empty(); }
|
|
};
|
|
|
|
/** RGBA in 0..1. Framework-free so PaneTree needs no ImGui. */
|
|
struct Color {
|
|
float r = 1.f, g = 1.f, b = 1.f, a = 1.f;
|
|
};
|
|
|
|
/** Screen rectangle in pixels. */
|
|
struct Rect {
|
|
double x = 0.0, y = 0.0, w = 0.0, h = 0.0;
|
|
|
|
bool contains(double px, double py) const {
|
|
return px >= x && px < (x + w) && py >= y && py < (y + h);
|
|
}
|
|
};
|
|
|
|
/* 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 */
|