@@ -266,6 +266,94 @@ silence). Only the initial variable-size chunks contain actual signal.
Removed. Terminator detection is via `page_key == 0x0000` in `read_bulk_waveform_stream` ,
not frame index.
### SUB 5A — re-probe at counter=0x1000 (DLE collision, FIXED 2026-04-15)
**Root cause (confirmed from diagnostic output, desk-thump event key=01110000): **
`bulk_waveform_params()` sets `p[3] = (counter >> 8) & 0xFF` . For chunk 4, counter =
`4 * 0x0400 = 0x1000` , so `p[3] = 0x10` — the DLE byte. Because `build_5a_frame` writes
params RAW (no DLE stuffing), the on-wire byte sequence contains a bare `0x10` . The
device DLE-decodes its own receive buffer: `10 00` (the p[3]/p[4] pair) is collapsed to
`00` , so the counter field reads 0 — a probe request. The device re-sends the initial
probe response (containing the STRT record and first waveform bytes).
**Effect: ** In a 36-frame stream for key=01110000, fi=4 was a byte-for-byte duplicate of
fi=0 (same db=1101B, same w[0:32] bytes, STRT present at w[10]). The old code treated it
as a regular chunk, decoded the STRT bytes as int16 samples (producing T=21587="ST",
V=21586="RT"), and shifted the running byte alignment for all subsequent frames.
**Fix (`_decode_a5_waveform`): ** Check for STRT in every frame, not just fi==0. Any
non-fi=0 frame containing STRT is a re-probe — log it and skip (do NOT add to
`all_chunks` ). Regular chunk path is reached only when `w.find(b"STRT") < 0` .
**Note: ** This DLE collision is key-specific. For key=01110000 (`key4[2:4]=0x0000` ),
counter = `chunk_num * 0x0400` , so counter=0x1000 occurs at chunk 4 for every event with
this key. For other keys (e.g. key=0111245a, `key4[2:4]=0x245A` ), the chunk counter
formula `key4[2:4] + n*0x0400` produces different values; the collision only occurs when
the high byte of any counter is 0x10.
### SUB 5A — metadata false-positive (FIXED 2026-04-15)
**Root cause (confirmed from diagnostic output): **
The old metadata-frame test was `b"Project:" in w` (single anchor). For the 36-frame
desk-thump stream, fi=15 had `b"Project:"` at w[93] inside live ADC data — a coincidental
4-byte pattern in the waveform. The frame (134 live sample-sets) was incorrectly skipped.
**Fix: ** Require BOTH `b"Project:" in w` AND `b"Client:" in w` to classify a frame as
metadata. The real metadata frame (fi=6 in the desk-thump stream) contains both strings
as part of the compliance-setup ASCII block; random ADC data is statistically unlikely to
contain both 8-byte sequences.
**Updated check in `_decode_a5_waveform`: **
``` python
elif b " Project: " in w and b " Client: " in w :
log . info ( " _decode_a5_waveform: fi= %d skipped (metadata frame) " , fi )
continue
```
### SUB 5A — 0xFF tail frames beyond record window (confirmed 2026-04-15)
The device bulk-streams flash pages beyond the configured record window. Un-written flash
pages read as 0xFF. Decoded as int16 LE, `0xFF 0xFF = -1` , which maps to ~0 in/s after
the geo scale factor is applied — producing a visible flat-line at the end of the waveform.
**Confirmed from raw capture analysis (desk-thump event key=01110000, 1024 sps,
record_time=3.0 s, ach_inbound_20260412_231505):**
- total_samples = 256 (pretrig) + 3072 (post) = 3328
- A5 stream: 37 frames total; frames 8– 35 are uniform 1036-byte 0xFF data (unwritten flash)
- Actual ADC signal: frames 0 (probe) + 1– 3 + 5 + 7 = 6 data chunks ≈ 815 sample-sets (0.80 s)
- The desk-thump vibration was genuinely short; the remaining ~2.2 s of the record window
is unwritten flash (0xFF), not signal. The flatline is physically correct.
- After 0xFF tail clip: display = min(decoded, 3328) excludes the all-zeros post-record padding
**Fix (two parts): **
1. `_decode_a5_waveform` (Python): already returns `total_samples` alongside
`samples_decoded` ; the field is populated from compliance config:
`total_samples = pretrig_samples + round(record_time * sample_rate)` .
2. `sfm_webapp.html` (`_buildWaveformCharts` ): `display = Math.min(decoded, total_samples)` ;
`times` array and per-channel `samples` are both sliced to `display` length before
plotting — `(channels[ch] || []).slice(0, display)` . Without this, the chart rendered
all `decoded` samples including the 0xFF tail.
### SUB 5A — chunk frame header is 5 bytes, NOT 8 (CORRECTED 2026-04-15)
* * `_decode_a5_waveform` uses `wave = w[5:]` to strip the per-chunk frame header.**
The header is 5 bytes: 2 counter bytes + 3 zero bytes. The code previously used `w[8:]`
(8-byte strip) — this was WRONG and discarded 3 bytes of real waveform data per frame,
misaligning the channel decode for all frames after any non-multiple-of-8 frame.
**Proof — "Standard Recording Setup" cross-frame continuity test (MITM capture 4-11-26): **
The ASCII string `"Standard Recording Setup"` spans the A5[5]→A5[6] frame boundary:
- A5[5].w last 2 bytes: `53 74` = `"St"` (end of "Standard")
- A5[6].w[0:5] = `00 00 00 00 00` (5 null bytes = header)
- A5[6].w[5:] begins with `61 6e 64` = `"and"` (start of "andard Recording Setup…")
Only `header=5` produces the contiguous string `"Standard Recording Setup"` .
`header=6/7/8` all produce broken text (tested exhaustively).
**Also updated: ** The probe frame (fi=0) strip of `w[10:]` is unchanged — the STRT magic
itself is at `w[10]` inside the probe frame, which has a different 10-byte preamble.
### SUB 1E / 1F — event iteration null sentinel and token position (FIXED, do not re-introduce)
**token_params bug (FIXED): ** The token byte was at `params[6]` (wrong). Both 3-31-26 and
@@ -1012,6 +1100,32 @@ call-home.
---
## Known broken / deferred
### Waveform chart Y-axis scaling (see GitHub issue)
**The waveform chart in the web viewer shows incorrect amplitudes — DO NOT trust the Y-axis values. **
PPV values reported by the system (stored in the DB, shown in event summaries) come from
the **0C waveform record ** computed on-device and are correct. The issue is purely in the
ADC→in/s conversion used to draw the chart.
What we know:
- Decoded ADC peaks from 5A (~30703– 32661 counts) imply ~9× larger PPV than the 0C record reports
(e.g. Vert 5.8 in/s decoded vs 0.690 in/s from 0C for the same event)
- ALL four channels show near-full-scale peaks, which is implausible for a desk-thump event
where Vert should dominate
- The `max_range_geo` value in the compliance config is 6.206053 in/s (not 10.000 or 1.25
as displayed in the Blastware UI) — unclear whether this is an ADC full-scale or something else
- The empirical full-scale implied by 0C data is ~0.736 in/s, not 6.206
- Tried: LE vs BE decode, header=5 vs 8, float32 format — all produce near-full-scale peaks
- Root cause unknown; likely requires a Blastware-generated waveform export for the same
event to reverse-engineer the correct scaling formula
Tracking: **GitHub issue #TODO ** (create before resuming this work)
---
## What's next
- **Database** — SQLite store for events + monitor log entries; dedup by key; queryable
@@ -1021,3 +1135,4 @@ call-home.
- Modem manager — push RV50/RV55 configs via Sierra Wireless API
- RV55 DCD/DTR issue — newer RV55 firmware doesn't assert DCD by default; units don't
resume monitoring after call-home disconnect (`--restart-monitoring` flag deferred)