fix(udpscope): carry warm-up state across the hrt handover

An undeclared-rate accumulated scalar is served by packetBurst until
HrtRateFit is ready, then by the hrt branch. The two place a burst
differently -- packetBurst ends it at wallNow, the hrt branch at
wallNow - (nElems-1)*hrtDt -- and the warm-up left no state behind, so
the handover packet skipped the monotonic clamp and stepped the signal
backwards by up to a burst width (-6.5 ms at 10 samples per 2.5 ms
packet, -0.99 s at 1000 samples per 10 ms).

Seeding lastEmitted* alone would only restore ordering. Without
lastAccHrt/prevAccCount the first hrt packet also has no tick delta to
measure, falls back to kDefaultDt and latches ClockOffset against a
burst width that is wrong whenever the cadence is not 1 kHz -- 89 ms of
permanent displacement at 100 samples per 10 ms, below the
recalibration threshold that would otherwise heal it. Seed both.

Also close the wallElapsed <= 0 bypass in both branches: skipping the
bleed cap when the wall has not moved hands back the full proportional
advance, letting a run of same-tick arrivals gain lead while no wall
time passes at all.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-08-28 05:49:01 +02:00
co-authored by Claude Opus 4.6
parent 440b805afd
commit 3add2c42b9
4 changed files with 288 additions and 34 deletions
+10 -2
View File
@@ -10,7 +10,7 @@
* Source/Applications/StreamHub/UDPSourceSession.cpp documents this failure and
* solves it; these are the same rules, computed from udps_frame_t's own fields.
*
* Two rules deliberately differ, both in the accumulated-scalar case (rule 3).
* Three rules deliberately differ, all in the accumulated-scalar case (rule 3).
*
* First, the anchor. StreamHub anchors every accumulated-scalar burst on the
* packet's own hrt, converted with the LOCAL MARTe HighResolutionTimer
@@ -24,7 +24,15 @@
* reconstructed timeline drift, and drift that only arrival time can observe
* must be corrected against arrival time — see rule 3.
*
* Second, the entry condition. UDPSourceSession.cpp:554 routes any update
* Second, which end of the burst is anchored. StreamHub converts the packet's
* hrt into the position of sample 0 and steps forward, so the burst STARTS at
* the anchor. Here the anchor is arrival time, and the samples were acquired
* before the packet carrying them landed — so the burst must END there instead.
* Both branches of rule 3 do this, or two accumulated scalars in one scope, one
* with a declared rate and one without, would sit a whole burst apart on the
* shared X axis.
*
* Third, the entry condition. UDPSourceSession.cpp:554 routes any update
* carrying nElems <= 1 to plain arrival time. That is safe for a host-local
* consumer whose arrival time is the producer's own clock, but wrong here:
* Accumulate mode flushes on a TIMER, so a short RT cycle legitimately delivers