fix(device): suppress firmware's malformed warmup stream frame (closes #351) - #362
Conversation
…#351) The firmware's fast streaming encoder emits a leading frame carrying fewer analog values than the enabled channel mask (a warmup frame with a normal one-sample-period timestamp). Core decoded it unfiltered, so every per-channel consumer (IChannel.SampleReceived, the StreamSamplesAsync live stream) received a partial first DataSample — silently corrupting first-value baselining, gap detection, min/max, and calibration/export. Gate the decode path at stream start: arm a first-full-frame guard in StartStreaming and, before timestamp/gap processing, drop any leading analog-bearing frame whose value count is below the enabled-analog count. The suppressed frame is a complete non-event (the next frame anchors the session clock), only *leading* short frames are dropped (mid-stream short frames stay best-effort mapped), and suppression is bounded by MaxSuppressedWarmupFrames so a genuinely short stream is never withheld. Raw MessageReceived still fires, so hand-demuxing consumers are unaffected. Bench-validated on Nq1 (FW 3.7.2, USB/serial): the first decoded LiveSample frame now carries the full [0,1] complement instead of a single value. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoSuppress malformed warmup streaming frames in device decode (fixes #351)
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
#351) Address Qodo review: the warmup guard was armed unconditionally in StartStreaming but only cleared on an analog-bearing frame, so a digital-only start left it armed. If analog channels were enabled mid-stream, the first short analog frames could be suppressed far from session start. Arm the guard only when >=1 analog channel is enabled at StartStreaming — the reproduced failure mode (#351) is a leading partial-analog frame at the start of an analog stream. A digital-only start is now disarmed, so a mid-stream analog short frame is best-effort mapped rather than suppressed. Adds a test covering digital-only start -> mid-stream analog enable -> short frame not suppressed. Full suite green net9+net10; bench re-validated (first analog frame still full). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 565a9ba |
…oad (#351) Address Qodo review: the fast streaming encoder packs analog+digital into one frame (issue evidence: "analog=[1] digital=00-04"), but suppressing a leading short-analog warmup frame via early-return also dropped its valid digital payload and skipped timestamp/gap processing. Suppress only the analog decode for warmup frames: still run timestamp/gap processing and DecodeDigital. The warmup frame's timestamp is a normal one sample period, so anchoring the session clock on it is correct — digital state/edges in a combined frame are no longer lost. Reworked the anchoring test (warmup now anchors, verified no false gap on steady cadence) and added a combined analog+digital warmup test (analog suppressed, digital preserved). Full suite green net9+net10 (1732); bench re-validated (analog-at-start still drops leading partial frame). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit d94a274 |
|
✅ Ready for review — Qodo is clean (0 bugs / 0 rule violations, both prior findings resolved) and CI is green on |
Summary
Closes #351. The firmware's fast streaming encoder (
Nanopb_EncodeStreamingFast) emits a leading warmup frame carrying fewer analog values than the enabled channel mask — with a normal one-sample-period timestamp, so the desktop #573 timestamp-jump machinery would not catch it. Core decoded this frame unfiltered, so every per-channel consumer (IChannel.SampleReceived, theStreamSamplesAsynclive stream) received a partial firstDataSample, silently corrupting first-value baselining, gap detection, min/max, and calibration/export.Fix
Gate the decode path at stream start (
DaqifiStreamingDevice):StartStreaming()arms a first-full-frame guard.MaxSuppressedWarmupFrames(5) so a genuinely short stream is never withheld indefinitely.MessageReceivedstill fires, so hand-demuxing consumers are unaffected (the raw frame is genuinely short on the wire; only the decoded per-channel surface is gated).Scoped deliberately to the reproduced, headline bug (partial-channel warmup frame). The stale-prior-session leftover-frame port (recommendation #1, timestamp-jump based) is a distinct, larger concern and is left for a follow-up.
Tests
DaqifiStreamingDeviceDecodeTestscovering: warmup suppression + subsequent full frame, no-clock-anchoring, raw passthrough, full-first-frame passthrough, digital-only stream, per-session re-arm, and the suppression cap.Decode_FewerValuesThanChannelstest to exercise the mid-stream short-frame path (its actual intent) now that the leading short frame is suppressed.Bench validation
Nq1, FW 3.7.2, USB/serial. A minimal harness consuming Core's decoded
StreamSamplesAsyncstream (channels 0+1 @ 50 Hz):Exactly inverts the issue's evidence (
analog=[1]first).🤖 Not merging — opened for review.