Implemented qt port + e2e

This commit is contained in:
Martino Ferrari
2026-06-26 09:11:10 +02:00
parent 0d7d8f396b
commit 4702d0a217
146 changed files with 57272 additions and 128 deletions
@@ -94,6 +94,8 @@ void UDPSourceSession::ResetCalibration() {
for (uint32 i = 0u; i < UDPSS_MAX_SIGNALS; i++) {
timeSigCalibValid_[i] = false;
timeSigCalib_[i] = 0.0;
timeSigLastValid_[i] = false;
timeSigLastTimerS_[i] = 0.0;
lastPktWallValid_[i] = false;
lastPktWallS_[i] = 0.0;
accScalarPrevN_[i] = 0u;
@@ -359,6 +361,28 @@ void UDPSourceSession::ParseDataPayload(const uint8 *payload, uint32 size) {
off += elemsToRead * wireElemBytes;
}
/* The decode scratch is sized at CONFIG time to the largest per-signal
* element count, but an Accumulate packet writes numSamples elements per
* accumulated scalar — for a lone scalar source that count is 1, so the
* batch decode would overrun valScratch_/tsBatchScratch_ (heap corruption
* that scrambled timestamps into the value stream). Grow the scratch to
* the largest element count actually present in THIS packet before pass 2.
* Runs on the receive thread (the only writer of these buffers) and only
* when the batch first exceeds the current capacity. */
uint32 maxNeeded = 1u;
for (uint32 s = 0u; s < nSigs; s++) {
if (sigElems[s] > maxNeeded) { maxNeeded = sigElems[s]; }
}
if (maxNeeded > timeScratchLen_) {
delete[] timeScratch_;
delete[] valScratch_;
delete[] tsBatchScratch_;
timeScratch_ = new float64[maxNeeded];
valScratch_ = new float64[maxNeeded];
tsBatchScratch_ = new float64[maxNeeded];
timeScratchLen_ = maxNeeded;
}
/* Pass 2: decode values and timestamps; write ring buffers.
* Timestamp logic mirrors Go buildBinaryDataMessageForSource (hub.go). */
static const float64 kRecalibThresholdS = 2.0;
@@ -390,12 +414,8 @@ void UDPSourceSession::ParseDataPayload(const uint8 *payload, uint32 size) {
float64 tv0 = 0.0;
DecodeElems(payload, sigOff[tIdx], descs[tIdx], 1u, &tv0);
const float64 timerS = tv0 * timerToSec;
if ((!timeSigCalibValid_[tIdx]) ||
(Fabs((timeSigCalib_[tIdx] + timerS) - wallNowS) > kRecalibThresholdS)) {
timeSigCalib_[tIdx] = wallNowS - timerS;
timeSigCalibValid_[tIdx] = true;
}
anchor = timeSigCalib_[tIdx] + timerS;
const float64 calib = CalibrateTimeSignal(tIdx, timerS, wallNowS);
anchor = calib + timerS;
} else {
anchorIsFirstSample = false;
}
@@ -416,12 +436,7 @@ void UDPSourceSession::ParseDataPayload(const uint8 *payload, uint32 size) {
(nElems <= timeScratchLen_)) {
DecodeElems(payload, sigOff[tIdx], descs[tIdx], nElems, timeScratch_);
const float64 timer0S = timeScratch_[0] * timerToSec;
if ((!timeSigCalibValid_[tIdx]) ||
(Fabs((timeSigCalib_[tIdx] + timer0S) - wallNowS) > kRecalibThresholdS)) {
timeSigCalib_[tIdx] = wallNowS - timer0S;
timeSigCalibValid_[tIdx] = true;
}
const float64 calib = timeSigCalib_[tIdx];
const float64 calib = CalibrateTimeSignal(tIdx, timer0S, wallNowS);
DecodeElems(payload, sigOff[s], desc, nElems, valScratch_);
for (uint32 e = 0u; e < nElems; e++) {
tsBatchScratch_[e] = calib + timeScratch_[e] * timerToSec;
@@ -236,6 +236,34 @@ private:
}
}
/**
* @brief Resolve (and maintain) the wall-clock calibration offset for time
* signal @p tIdx given the first decoded timer value @p timer0S of the
* current packet and the arrival wall time @p wallNowS.
*
* Re-anchors the offset (offset = wallNowS timer0S) when (a) it is the
* first packet, (b) the source clock jumped backward versus the previous
* packet (a looping/rewinding producer), or (c) the computed wall time has
* drifted past kRecalibThresholdS from the true arrival wall time.
* @return the calibration offset to add to timer-seconds for this signal.
*/
inline float64 CalibrateTimeSignal(uint32 tIdx, float64 timer0S,
float64 wallNowS) {
static const float64 kRecalibThresholdS = 2.0;
const bool reset = timeSigLastValid_[tIdx] &&
(timer0S < timeSigLastTimerS_[tIdx]);
const float64 drift = (timeSigCalib_[tIdx] + timer0S) - wallNowS;
const float64 absDrift = (drift < 0.0) ? -drift : drift;
if ((!timeSigCalibValid_[tIdx]) || reset ||
(absDrift > kRecalibThresholdS)) {
timeSigCalib_[tIdx] = wallNowS - timer0S;
timeSigCalibValid_[tIdx] = true;
}
timeSigLastTimerS_[tIdx] = timer0S;
timeSigLastValid_[tIdx] = true;
return timeSigCalib_[tIdx];
}
/**
* @brief Decode @p nElems consecutive elements of signal @p desc starting
* at payload offset @p off into @p out (dequantised physical values).
@@ -301,6 +329,15 @@ private:
float64 timeSigCalib_[UDPSS_MAX_SIGNALS];
bool timeSigCalibValid_[UDPSS_MAX_SIGNALS];
/* Previous packet's first time-signal value (seconds) per time signal —
* used to detect a backward reset of a looping/rewinding source clock
* (e.g. a FileReader with EOF = "Rewind"). When the new packet's clock
* jumps backward the calibration is re-anchored to the current wall time
* so the published timeline stays monotonic instead of overwriting the
* previous pass's window (which renders as periodic gaps). */
float64 timeSigLastTimerS_[UDPSS_MAX_SIGNALS];
bool timeSigLastValid_[UDPSS_MAX_SIGNALS];
/* Last packet arrival wall time per signal — used to interpolate per-element
* timestamps for packed TIMEMODE_PACKET arrays (Go hub lastPktNs).
* For accumulated scalars this instead holds the previous packet's
+2 -2
View File
@@ -124,8 +124,8 @@ int main(int argc, char **argv) {
signal(SIGTERM, SignalHandler);
/* Allocate on the heap: StreamHub embeds 32 UDPSourceSession objects,
* each ~327 KB (4 reassembly slots × 65 KB + recv buffer), totalling
* ~10 MB — well above the default 8 MB thread stack limit. */
* each ~4 MB (4 reassembly slots × 1 MiB + recv buffer), totalling
* ~128 MB — far above the default 8 MB thread stack limit. */
StreamHub::StreamHub *hub = new StreamHub::StreamHub();
gHub = hub;