v0.14.3 - Full waveform DL pipeline tested and working. #15

Merged
serversdown merged 12 commits from protocol-fix into main 2026-05-05 20:49:48 -04:00
3 changed files with 108 additions and 4 deletions
Showing only changes of commit a27693242d - Show all commits
+49
View File
@@ -4,6 +4,55 @@ All notable changes to seismo-relay are documented here.
---
## v0.14.3 — 2026-05-05
### Fixed
- **`build_5a_frame` — DLE-stuffing rule for 0x10 bytes in params (the
long-standing >1-sec event 0 "won't open in BW" bug).**
Previously `build_5a_frame` wrote params bytes RAW with no DLE stuffing,
based on the incorrect assumption that the device handled all `0x10`
bytes in params literally. It does not. The device's actual de-stuffing
rule for the params region is:
- `10 10` → de-stuffs to `10`
- `10 02/03/04` → kept literal (inner-frame markers)
- `10 X` for other X → de-stuffs to just `X` (drops the `0x10`)
When the counter passed in params has `0x10` in the high byte (e.g.
counter=`0x1000` produces params bytes `... 10 00 ...`), the device
silently corrupts the request to counter=`0x__00` and responds with
whatever lives at that wrong address. For counter=0x1000 the wrong
address was 0x0000, so the response was a copy of the file header +
STRT record. That STRT block then got embedded in the assembled body
at file offset `0x1016`, and Blastware refused to open the file
(interprets the second STRT as a malformed multi-event file).
This explains the entire >1-sec event-0 failure pattern:
- 1-sec events have `end_offset < 0x1000`, so the chunk walk never
requests counter `0x10__` and the bug never triggers.
- 2-sec / 3-sec / longer events all need a chunk at counter `0x1000`
(and longer events also need `0x1200`, `0x1400`, etc., none of which
have `0x10` in the high byte except `0x1000`). Just one corrupted
response is enough to embed STRT in the body and break the file.
Verified against BW 5-1-26 "copy 3sec" capture: all 17 5A request
frames (probe + 2 metadata pages + 13 sample chunks + TERM) now match
BW's wire output **byte-for-byte**, including the doubled `10 10 00`
for counter=0x1000.
### Notes
- `0x10` bytes in `offset_hi` (the standalone offset field at body[5])
are still written RAW — confirmed correct per the 1-2-26 capture.
- BW's actual encoding of `10 02` / `10 04` for meta pages 0x1002 /
0x1004 is *not* doubled — it relies on the device keeping `10 02`
and `10 04` as literal pairs. This is preserved by the fix.
---
## v0.14.2 — 2026-05-04
### Fixed
+26 -3
View File
@@ -2,7 +2,7 @@
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
(Sierra Wireless RV50 / RV55). Current version: **v0.14.2**.
(Sierra Wireless RV50 / RV55). Current version: **v0.14.3**.
When new information about the protocol is discovered, please update the instantel_protocol_reference.md with the findings in addition to this document
@@ -115,8 +115,31 @@ S3→BW (response):
section contribute only `XX` to the running sum; lone bytes contribute normally. This
differs from the standard SUM8-of-destuffed-payload that all other commands use.
Both differences confirmed by reproducing Blastware's exact wire bytes from the 1-2-26
BW TX capture. All 10 frames verified.
3. **Params region uses partial DLE stuffing (CONFIRMED 2026-05-05).** The device's
de-stuffing rule for bytes inside the params region is:
- `10 10` → de-stuffs to `10`
- `10 02 / 03 / 04` → kept literal (these are inner-frame markers)
- `10 X` for other X → de-stuffs to just `X` (drops the leading `0x10`)
Therefore any `0x10` byte in the *logical* params that is followed by a byte NOT in
`{0x02, 0x03, 0x04, 0x10}` MUST be doubled on the wire (`10 X``10 10 X`) so the
device's de-stuffer reproduces the original `10 X` pair. This applies most commonly
to counters with `0x10` in the high byte (e.g. counter=`0x1000` produces logical
params bytes `... 10 00 ...`, which BW encodes on the wire as `... 10 10 00 ...`).
Without this stuffing the device interprets counter=`0x1000` as `0x0000` and returns
the probe response (which contains a copy of the file header + STRT record). That
STRT block then gets embedded in the assembled file body at offset `0x1016`, and
Blastware refuses to open the file — see the v0.14.3 entry in `CHANGELOG.md`.
`0x10` bytes in `offset_hi` (body[5]) are still written RAW — only the params region
has this stuffing requirement. The metadata-page params for counter `0x1002` /
`0x1004` survive without stuffing because `10 02` and `10 04` fall in the "kept
literal" carve-out.
Both differences (1) and (2) confirmed by reproducing Blastware's exact wire bytes from
the 1-2-26 BW TX capture (10 frames). Difference (3) confirmed against the 5-1-26
"bwcap3sec" capture (17 frames, all match byte-for-byte after fix).
### SUB 5A — chunk counter formula (REWRITTEN 2026-05-01 — see 5-1-26 captures)
+33 -1
View File
@@ -137,8 +137,40 @@ def build_5a_frame(offset_word: int, raw_params: bytes) -> bytes:
s += b"\x00" # field3
s += bytes([(offset_word >> 8) & 0xFF, # offset_hi — raw, NOT stuffed
offset_word & 0xFF]) # offset_lo
for b in raw_params: # params — NOT DLE-stuffed (raw bytes, match BW wire format)
# Params — partial DLE stuffing of 0x10 bytes (CONFIRMED 2026-05-05).
#
# The device's de-stuffing rule for params is:
# • `10 10` → de-stuffs to `10`
# • `10 02/03/04` → kept literal (these are inner-frame markers)
# • `10 X` other → de-stuffs to just `X` (drops the 0x10)
#
# So for any 0x10 byte in the *logical* params that is followed by a
# byte NOT in {0x02, 0x03, 0x04, 0x10}, we must double the 0x10 on the
# wire (`10 X` → `10 10 X`) so the device's de-stuffer reproduces the
# original `10 X` pair. Without this, counter values with `0x10` in
# the high byte (e.g. counter=0x1000 has params bytes `10 00`) are
# silently corrupted to `0x__00` on the device side, and the device
# responds for the wrong address — for counter=0x1000 it returns the
# probe response (counter=0x0000), which contains the file header +
# STRT. That STRT block then lands in the assembled file body and
# Blastware rejects the file as malformed.
#
# Confirmed against BW capture 5-1-26 / bwcap3sec frame 20: params
# logical bytes `00 01 11 10 00 00 00 00 00 00 00` (counter=0x1000)
# are encoded on the wire as `00 01 11 10 10 00 00 00 00 00 00 00`.
# BW frames 13/14 (meta @ 0x1002 / 0x1004) leave `10 02` and `10 04`
# raw — the device handles those literal pairs correctly.
i = 0
while i < len(raw_params):
b = raw_params[i]
s.append(b)
if (
b == 0x10
and i + 1 < len(raw_params)
and raw_params[i + 1] not in (0x02, 0x03, 0x04, 0x10)
):
s.append(0x10) # double the 0x10 so it survives device de-stuffing
i += 1
# DLE-aware checksum: for 0x10 XX pairs count XX; for lone bytes count them
chk, i = 0, 0