d3f77d1d96
Decoded the structural framing of the Blastware waveform body — the bytes between the 21-byte STRT record and the 26-byte file footer. The body is a sequence of tagged variable-length blocks, NOT raw int16 LE. Five tag types (10/20/00/30/40 NN) and their lengths are now confirmed against the 4-event May 2026 fixture bundle. Body splits cleanly into ~16 segments (for a 1280-sample event) separated by 40 02 segment headers carrying a monotonically incrementing uint32 LE counter at bytes [8:12]. What's done: - minimateplus/waveform_codec.py — block walker, segment splitter, segment header parser. decode_waveform_v2 is a stub returning None until the byte-to-sample mapping is solved; client.py is unchanged. - tests/test_waveform_codec.py — 31 tests covering block detection, lengths, contiguous-walk, segment splitting, segment-header parsing, and counter monotonicity. All pass. - tests/fixtures/decode-re-5-8-26/ — bundled fixtures (4 events, BW binary + Blastware ASCII export each). - docs/instantel_protocol_reference.md §7.6.1 — replaced retraction box with the verified structural decoding plus an explicit list of what's still open. What's still open: the per-byte mapping inside 10 NN / 20 NN blocks. 96 channel-permutation × nibble-order × sign-convention combinations were brute-force tested; none match BW's ASCII export to within ±1 ADC count. The codec is more elaborate than uniform 4-bit deltas — likely a hybrid variable-bit-width scheme with segment-anchor resync points. Next recommended step: capture an event with a known calibration tone to pin down magnitude scaling. Walker also bails out partway through event-b (open issue documented in both the module and the protocol reference).
35 lines
1.2 KiB
Python
35 lines
1.2 KiB
Python
"""Find body byte ranges that look like absolute int8 sample data (smooth waveform)."""
|
|
import sys
|
|
sys.path.insert(0, ".")
|
|
from analysis.load_bundle import load_bundle
|
|
|
|
|
|
def looks_like_smooth_int8(buf):
|
|
"""Convert bytes to int8 and check if successive deltas are small (waveform-like)."""
|
|
if len(buf) < 8:
|
|
return 0.0
|
|
vals = [b if b < 128 else b - 256 for b in buf]
|
|
diffs = [abs(vals[i+1] - vals[i]) for i in range(len(vals)-1)]
|
|
avg_diff = sum(diffs) / len(diffs)
|
|
return avg_diff
|
|
|
|
|
|
def main():
|
|
for name in ("event-a", "event-c"):
|
|
b = load_bundle(name)
|
|
body = b.body
|
|
# Scan with sliding window of 64 bytes; find segments where the bytes look like a smooth wave
|
|
win = 64
|
|
scores = []
|
|
for i in range(len(body) - win):
|
|
scores.append((i, looks_like_smooth_int8(body[i:i+win])))
|
|
# Lowest avg_diff means smoothest
|
|
scores.sort(key=lambda x: x[1])
|
|
print(f"\n=== {name} (body={len(body)}) — smoothest 10 windows ===")
|
|
for off, s in scores[:10]:
|
|
print(f" +{off:>5} avg_diff={s:.2f} bytes={body[off:off+24].hex(' ')}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|