fix: waveform decode improved for accuracy.
feat: adds 5a diagnostic script to parse raw binary
This commit is contained in:
+43
-38
@@ -1440,29 +1440,50 @@ def _decode_a5_waveform(
|
||||
for fi, db in enumerate(frames_data):
|
||||
w = db[7:]
|
||||
|
||||
# A5[0]: waveform begins immediately after the 21-byte STRT record.
|
||||
# Confirmed 2026-04-14: there is NO preamble after STRT — bytes 21+
|
||||
# are raw ADC sample data. The earlier sp+27 skip was eating 6 bytes
|
||||
# of real waveform, misaligning the channel decode for all subsequent
|
||||
# frames.
|
||||
if fi == 0:
|
||||
sp = w.find(b"STRT")
|
||||
if sp < 0:
|
||||
# ── Probe frames (fi==0 AND any re-probe the device sends mid-stream) ────
|
||||
# A5[0] always contains the STRT record. For event key 0x01110000,
|
||||
# chunk 4 (counter=0x1000) has 0x10 in the counter high byte; the device
|
||||
# DLE-decodes the params and sees counter=0x0000 (probe), so it responds
|
||||
# with a duplicate probe frame containing the same STRT. The diagnostic
|
||||
# from 2026-04-15 confirmed this: fi=4 was byte-for-byte identical to fi=0
|
||||
# (same db length 1101B, same STRT at w[10], same first 32 bytes).
|
||||
#
|
||||
# Handling: any frame — not just fi==0 — that contains the STRT magic is
|
||||
# treated as a probe frame. Waveform starts at strt_pos + 21 (no preamble).
|
||||
# Re-probe frames are complete duplicates of fi=0 (device re-sends the
|
||||
# beginning of the event), so their post-STRT waveform bytes are DROPPED
|
||||
# to avoid injecting duplicate data into the stream.
|
||||
sp = w.find(b"STRT")
|
||||
if sp >= 0:
|
||||
if fi == 0:
|
||||
wave = w[sp + 21 :]
|
||||
log.info(
|
||||
"_decode_a5_waveform: A5[0] probe — STRT at w[%d], "
|
||||
"waveform starts at sp+21; first 24 wave bytes: %s",
|
||||
sp, wave[:24].hex(' '),
|
||||
)
|
||||
else:
|
||||
# Re-probe frame: device re-sent probe in response to a chunk
|
||||
# request whose counter byte happened to be 0x10 (DLE).
|
||||
# The post-STRT bytes are a duplicate of the initial waveform
|
||||
# — drop this frame entirely to avoid double-counting data.
|
||||
log.info(
|
||||
"_decode_a5_waveform: fi=%d re-probe (STRT at w[%d]) — "
|
||||
"skipped (duplicate probe response from device)",
|
||||
fi, sp,
|
||||
)
|
||||
continue
|
||||
wave = w[sp + 21 :]
|
||||
log.info(
|
||||
"_decode_a5_waveform: A5[0] waveform starts at sp+21; "
|
||||
"first 24 wave bytes: %s",
|
||||
wave[:24].hex(' '),
|
||||
)
|
||||
|
||||
# Metadata frame: contains "Project:", "Client:", etc. strings.
|
||||
# Originally assumed to be always fi==7 (A5[7] in 4-2-26 blast capture),
|
||||
# but confirmed variable position — it appears at whatever chunk index the
|
||||
# device places it (observed at fi=6 for desk-thump events 2026-04-14).
|
||||
# Skip ANY frame whose raw bytes contain b"Project:" — this is the same
|
||||
# anchor used by stop_after_metadata in read_bulk_waveform_stream.
|
||||
elif b"Project:" in w:
|
||||
# Metadata frame: contains BOTH "Project:" and "Client:" strings.
|
||||
# Requiring two compliance anchors prevents false positives where ADC
|
||||
# bytes accidentally spell "Project:" (confirmed false positive at fi=15
|
||||
# in the 2026-04-15 desk-thump download — only "Project:" appeared there,
|
||||
# not "Client:"). The real metadata frame always contains both.
|
||||
# This is the same anchor used by stop_after_metadata in
|
||||
# read_bulk_waveform_stream (which only checks "Project:" — see note
|
||||
# there about the asymmetry: stopping early is fine with one anchor,
|
||||
# but skipping a waveform frame requires higher confidence).
|
||||
elif b"Project:" in w and b"Client:" in w:
|
||||
log.info("_decode_a5_waveform: fi=%d skipped (metadata frame)", fi)
|
||||
continue
|
||||
|
||||
@@ -2248,20 +2269,4 @@ def _decode_monitor_status(data: bytes) -> MonitorStatus:
|
||||
# Payload length varies (46–49 bytes) but the battery/memory block is always
|
||||
# the last 10 bytes. No checksum byte — it was stripped by S3FrameParser.
|
||||
#
|
||||
# section[-10:-8] battery × 100 uint16 BE 0x02A8 = 6.80 V
|
||||
# section[-8 :-4] memory_total uint32 BE ≈ 960 KB on BE11529
|
||||
# section[-4:] memory_free uint32 BE decreases as events fill
|
||||
#
|
||||
# Confirmed stable across IDLE (46b), MONITORING (48-49b) variants.
|
||||
if len(section) >= 10:
|
||||
batt_raw = struct.unpack(">H", section[-10:-8])[0]
|
||||
battery_v = batt_raw / 100.0
|
||||
memory_total = struct.unpack(">I", section[-8:-4])[0]
|
||||
memory_free = struct.unpack(">I", section[-4:])[0]
|
||||
|
||||
return MonitorStatus(
|
||||
is_monitoring=is_monitoring,
|
||||
battery_v=battery_v,
|
||||
memory_total=memory_total,
|
||||
memory_free=memory_free,
|
||||
)
|
||||
# section[-1
|
||||
Reference in New Issue
Block a user