Big event bugfix. see details:
## v0.13.0 — 2026-05-01 ### Fixed - **SUB 5A bulk waveform stream — over-read bug for events ≥ 2 sec.** `read_bulk_waveform_stream` was walking the chunk counter past the actual end of the event, picking up post-event circular-buffer garbage that corrupted reconstructed Blastware files for any waveform > ~1 sec. The loop now extracts the event's `end_offset` from the STRT record at `data[23:27]` of the probe response and stops the chunk walk when the next counter would step past it. Verified against three BW MITM captures (4-27-26 + 5-1-26): 2-sec event drops from 37 over-read chunks to 7 bounded chunks; 3-sec drops to 9; non-zero-start "event 2" drops to 9. ### Added - `framing.bulk_waveform_term_v2(key4, end_offset, last_chunk_counter)` — computes the corrected SUB 5A TERM frame's `(offset_word, params)` per the formula confirmed across all 3 BW captures. Not yet wired into `read_bulk_waveform_stream` (the legacy TERM is still used to preserve the existing `blastware_file.write_blastware_file` frame-structure expectations); available for the next iteration that switches to BW's 0x0200 chunk step. - `framing.parse_strt_end_offset(a5_data)` — extracts the event-end pointer from the STRT record in an A5 response payload.
This commit is contained in:
+45
-10
@@ -35,6 +35,8 @@ from .framing import (
|
||||
token_params,
|
||||
bulk_waveform_params,
|
||||
bulk_waveform_term_params,
|
||||
bulk_waveform_term_v2,
|
||||
parse_strt_end_offset,
|
||||
POLL_PROBE,
|
||||
POLL_DATA,
|
||||
SESSION_RESET,
|
||||
@@ -122,16 +124,21 @@ DATA_LENGTHS: dict[int, int] = {
|
||||
}
|
||||
|
||||
# SUB 5A (BULK_WAVEFORM_STREAM) protocol constants.
|
||||
# Confirmed from 1-2-26 BW TX capture analysis (2026-04-02).
|
||||
_BULK_CHUNK_OFFSET = 0x1004 # offset field for probe + all regular chunk requests ✅
|
||||
_BULK_TERM_OFFSET = 0x005A # offset field for termination request ✅
|
||||
_BULK_COUNTER_STEP = 0x0400 # chunk counter increment per chunk ✅
|
||||
# Chunk counter formula: key4[2:4] + (chunk_num - 1) * 0x0400
|
||||
# where key4[2:4] is the event's circular-buffer base offset ((key4[2]<<8)|key4[3]).
|
||||
# Earlier captures showed 0x1004 for chunk 1 of key 01110000 — that was a Blastware
|
||||
# artifact. For keys where key4[2:4] != 0x0000 (e.g. key 01111884) the old
|
||||
# "n * 0x0400" formula sends counters from the wrong buffer region and the device
|
||||
# returns data from a different event. Confirmed correct 2026-04-24.
|
||||
#
|
||||
# 2026-05-01 minimal-fix: the chunk-counter walk is now bounded by the event's
|
||||
# `end_offset` extracted from the STRT record at data[23:27] of the probe
|
||||
# response. Without this bound the loop kept asking for chunks past the event
|
||||
# end and the device responded with post-event circular-buffer garbage,
|
||||
# corrupting reconstructed Blastware files for events ≥ 2 sec.
|
||||
#
|
||||
# We keep the OLD 0x0400 chunk step here (BW actually uses 0x0200 — see §7.8.5
|
||||
# of the protocol reference for the corrected understanding) because the
|
||||
# existing blastware_file.py builder relies on the 0x0400-step frame structure
|
||||
# to produce valid files. Switching to BW's 0x0200 step is a separate task
|
||||
# that also requires updating the file builder.
|
||||
_BULK_CHUNK_OFFSET = 0x1004 # offset_word for probe + all chunk requests
|
||||
_BULK_TERM_OFFSET = 0x005A # offset_word for the legacy terminator
|
||||
_BULK_COUNTER_STEP = 0x0400 # chunk counter increment
|
||||
|
||||
# Default timeout values (seconds).
|
||||
# MiniMate Plus is a slow device — keep these generous.
|
||||
@@ -610,6 +617,24 @@ class MiniMateProtocol:
|
||||
frames_data.append(rsp)
|
||||
log.debug("5A A5[0] page_key=0x%04X %d bytes", rsp.page_key, len(rsp.data))
|
||||
|
||||
# ── Parse STRT end_offset from probe response (NEW 2026-05-01) ────────
|
||||
# The first A5 response contains a STRT record at data[17:]. The
|
||||
# bytes at data[23:27] are the event's end-key, whose low 16 bits
|
||||
# are the absolute device-buffer address where the event ends. Use
|
||||
# this to bound the chunk loop and stop the over-read past event end.
|
||||
# See docs/instantel_protocol_reference.md §7.8.5 and CLAUDE.md
|
||||
# "SUB 5A — STRT record encodes end_offset".
|
||||
_end_offset = parse_strt_end_offset(rsp.data)
|
||||
if _end_offset is None:
|
||||
# Defensive fallback — let max_chunks cap the walk.
|
||||
log.warning("5A: STRT not found in probe; cannot bound chunk loop")
|
||||
_end_offset = 0xFFFF
|
||||
else:
|
||||
log.debug(
|
||||
"5A STRT start_offset=0x%04X end_offset=0x%04X size=0x%04X",
|
||||
_key4_offset, _end_offset, _end_offset - _key4_offset,
|
||||
)
|
||||
|
||||
# ── Step 2: chunk loop ───────────────────────────────────────────────
|
||||
# Counter formula: _chunk_base + (chunk_num - 1) * 0x0400
|
||||
# where _chunk_base = max(key4[2:4], 0x0400).
|
||||
@@ -629,6 +654,16 @@ class MiniMateProtocol:
|
||||
_chunk_base = max(_key4_offset, _BULK_COUNTER_STEP)
|
||||
for chunk_num in range(1, max_chunks + 1):
|
||||
counter = _chunk_base + (chunk_num - 1) * _BULK_COUNTER_STEP
|
||||
# Stop when we'd step past the event end (NEW 2026-05-01). Without
|
||||
# this, the device returns post-event circular-buffer data which
|
||||
# corrupts the reconstructed file for events ≥ 2 sec.
|
||||
if counter >= _end_offset:
|
||||
log.debug(
|
||||
"5A chunk loop done at counter=0x%04X (end=0x%04X); "
|
||||
"%d chunks fetched",
|
||||
counter, _end_offset, len(frames_data),
|
||||
)
|
||||
break
|
||||
params = bulk_waveform_params(key4, counter)
|
||||
log.debug("5A chunk %d counter=0x%04X", chunk_num, counter)
|
||||
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))
|
||||
|
||||
Reference in New Issue
Block a user