From 6a42facf02ef048e8ed554d1b8111edb7b5b27df Mon Sep 17 00:00:00 2001 From: Brian Harrison Date: Wed, 1 Apr 2026 16:54:51 -0400 Subject: [PATCH] docs: update protocol reference for SUB 1A compliance config read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New/corrected sections: §7.6.1 Record Time — corrected: anchor+10 supersedes the +0x28 absolute offset. Added 3 s, 5 s, 8 s confirmations alongside existing 7/10/13. Warning added: do NOT use fixed offset for BE11529. §7.6.2 (NEW) SUB 1A Multi-Frame Read Protocol — 4-frame A/B/C/D sequence documented (reverse-engineered from raw_bw capture). E5 page_key field explained. BE11529 duplicate-page behaviour and (page_key, chunk_len) dedup strategy documented. §7.6.3 (NEW) Sample Rate and DLE Jitter — Normal/Fast/Faster = 1024/ 2048/4096 Sa/s confirmed. Root cause of ±1 byte cfg jitter explained: 4096 = 0x1000 → `10 10 00` in raw frame → `10 00` after DLE unstuffing = 1 byte shorter than 04 00/08 00. Anchor search requirement explained. Changelog — 5 new entries covering the 4-frame sequence, duplicate-page detection, record_time anchor correction, sample_rate confirmation, and _pending_frames / reset_parser=False implementation notes. Quick Reference — Record Time and Sample Rate rows updated with correct locations, types, and confirmed values. Open Questions — SUB 1A item updated to "substantially resolved". Record time item updated. Sample rate added as resolved. --- docs/instantel_protocol_reference.md | 76 ++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/docs/instantel_protocol_reference.md b/docs/instantel_protocol_reference.md index 8202c7a..f00273b 100644 --- a/docs/instantel_protocol_reference.md +++ b/docs/instantel_protocol_reference.md @@ -67,6 +67,11 @@ | 2026-04-01 | §7.7.5 | **CONFIRMED — Record type** encoded in byte[1] (sub_code), not as ASCII string. `0x10` = Waveform ✅. Histogram sub_code not yet captured. ASCII string search approach removed. | | 2026-04-01 | §7.7.5, §14 | **CONFIRMED — Per-channel PPV** at label+6 (✅ all four channels), cross-referenced vs Blastware: Tran=0.420, Vert=3.870, Long=0.495 in/s. **CONFIRMED — Peak Vector Sum** at fixed offset 87 = 3.906 in/s ✅ matches Blastware "Peak Vector Sum". Is √(Tran²+Vert²+Long²) at max instantaneous vector moment, not vector sum of per-channel peaks. Open question "offset 87 purpose" closed. | | 2026-04-01 | §8 | **RESOLVED — §8 unknown byte at offset 3.** Field is confirmed absent in the 9-byte waveform record format (no such field). The 6-byte event-index format has a separator byte at [3] whose purpose remains ❓ but is no longer actively blocking anything. | +| 2026-04-01 | §7.6.2 (NEW) | **NEW — SUB 1A multi-frame read protocol documented.** SUB 1A (compliance config read) requires a 4-frame sequence, not a simple 2-step probe+fetch. Reverse-engineered from `raw_bw_20260311_155355.bin` (Blastware TX capture, ACK+STX/ETX format, BW CMD=0x10). Sequence: (A) probe `offset=0x0000, params=...00 64 00 00`; (B) data request `offset=0x0400, params=...00 64 00 00`; (C) data request `offset=0x0400, params=...04 00 00 64 00 00`; (D) data request `offset=0x002A, params=...08 00 00 64 00 00`. E5 response to each frame carries a `page_key` field (bytes `data[3:5]`): page `0x0000` = 44-byte header chunk, page `0x0010` = main config data (~1027+1055 bytes across C and D). | +| 2026-04-01 | §7.6.2 | **NEW — BE11529 duplicate-page behaviour.** BE11529 (firmware S338.17) sometimes responds to frame D with page `0x0000` (44 bytes) instead of page `0x0010` (~1055 bytes). When this happens, frame D is an exact duplicate of frame B and must be dropped to prevent cfg mis-alignment. Detection: track `(page_key, chunk_len)` pairs and skip repeats. When frame D delivers page `0x0010` correctly, total cfg = 44 + 1027 + 1055 = 2126 bytes; with duplicate D, cfg = 44 + 1027 = 1071 bytes. Both are valid inputs to the decoder. | +| 2026-04-01 | §7.6.1 | **CORRECTED — Record time offset.** Previous doc (`+0x28` from E5 data page2 start) was correct for single-frame reads but unreliable for BE11529 due to a 1-byte DLE jitter (see §7.6.3). The `minimateplus` library now uses an anchor-based approach: search for `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` in cfg[40:100]; record time float32 BE is at anchor+10. Validated at 3.0, 5.0, and 8.0 seconds. | +| 2026-04-01 | §7.6.3 (NEW) | **NEW — Sample rate confirmed and documented.** Sample rate (Normal=1024 / Fast=2048 / Faster=4096 Sa/s) is stored as uint16 BE at anchor−2, where anchor is the 10-byte sequence `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00`. DLE jitter root cause: 4096 = 0x1000, so in the raw S3 frame the sample-rate bytes are sent as `10 10 00` (DLE-escaped `10`); after DLE unstuffing → `10 00` (2 bytes instead of 3 for 1024/2048), making frame C 1 byte shorter and shifting all subsequent offsets by −1. Anchor search is immune to this shift. All three modes confirmed on BE11529 firmware S338.17. | +| 2026-04-01 | §5.1 | **CONFIRMED — `_pending_frames` buffer and `reset_parser=False` parameter.** `MiniMateProtocol._recv_one()` now supports `reset_parser=False` to preserve parser state between consecutive reads within a multi-frame sequence. A `_pending_frames: list[S3Frame]` buffer stores extra frames parsed from a single TCP chunk when multiple E5 responses arrive together. Required for reliable SUB 1A frame B/C/D sequence on BE11529. | --- @@ -484,23 +489,73 @@ The SUB `1A` read response (`E5`) and SUB `71` write block contain per-channel t Values are stored natively in **imperial units (in/s)** — unit strings `"in."` and `"/s"` embedded inline confirm this regardless of display locale. -### 7.6.1 Record Time (SUB E5 data page2 `+0x28`) +### 7.6.1 Record Time -> ✅ **CONFIRMED — 2026-03-09** from two controlled captures (record time 7→13 sec, raw_s3-3-9-26_2.bin and raw_s3-3-9-26_3.bin). +> ✅ **CONFIRMED — 2026-04-01** (BE11529 / firmware S338.17). Updated from 2026-03-09 offset-based confirmation; anchor approach supersedes the `+0x28` absolute offset. -Record time is stored as a **32-bit IEEE 754 float, big-endian** at offset `+0x28` from the start of the E5 data page2 payload. +Record time is stored as a **32-bit IEEE 754 float, big-endian**, located via an anchor pattern (see §7.6.3 below). + +**Anchor-relative location:** search for the 10-byte sequence `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` in cfg[40:100]. Record time float is at **anchor + 10**. | Record Time | float32 BE bytes | Decoded | |---|---|---| +| 3 seconds | `40 40 00 00` | 3.0 | +| 5 seconds | `40 A0 00 00` | 5.0 | | 7 seconds | `40 E0 00 00` | 7.0 | +| 8 seconds | `41 00 00 00` | 8.0 | | 10 seconds | `41 20 00 00` | 10.0 | | 13 seconds | `41 50 00 00` | 13.0 | -**Disambiguation note:** Max geo range (also a float in this block) only takes values 1.25 or 10.0 in/s. The values 7 and 13 are not valid geo range selections, confirming this field is record time. +**⚠️ Do NOT use absolute offset `+0x28` from page2 start for BE11529.** The total cfg length varies by ±1 byte depending on sample rate setting (see §7.6.3 DLE jitter note). Absolute offsets are unreliable; anchor search is the correct approach. -**`0x0A` after "Extended Notes" label:** The byte `0x0A` that appears after the null-padded "Extended Notes" string in the E5 payload is **not** record time. It is an unknown field that equals 10 and is invariant across record time changes. Do not confuse it with the record time float at `+0x28`. +**`0x0A` after "Extended Notes" label:** invariant across record time changes — not record time. -> ✅ **`0x082A` (= 2090) — RESOLVED:** This value is the fixed payload length of the E5 response block (2090 bytes). It is constant regardless of record time, trigger level, or any other setting. It appears in the E5 frame header as the declared data length for the paged read, not as a settings field. +> ✅ **`0x082A` (= 2090) — RESOLVED:** Fixed payload length of the E5 response block. Constant regardless of any setting. + +--- + +### 7.6.2 SUB 1A Multi-Frame Read Protocol + +> ✅ **CONFIRMED — 2026-04-01** (BE11529 / firmware S338.17). Reverse-engineered from `raw_bw_20260311_155355.bin` (Blastware TX capture). + +SUB 1A (compliance config read) requires **four frames**, not the standard 2-step probe+fetch used by other SUBs. The Blastware TX frame format for these is ACK (`0x41`) + STX (`0x02`) + [DLE-stuffed body] + ETX (`0x03`) with CMD=`0x10`. + +**Frame sequence:** + +| Step | Name | BW offset param | Notes | +|---|---|---|---| +| A | Probe | `0x0000` | Triggers E5 response with page_key=`0x0000` (44-byte header) | +| B | Data B | `0x0400` | params: `00 00 00 00 00 00 00 64 00 00`; E5 page_key=`0x0000` (44 bytes) | +| C | Data C | `0x0400` | params: `00 00 04 00 00 00 00 64 00 00`; E5 page_key=`0x0010` (~1027 bytes) | +| D | Data D | `0x002A` | params: `00 00 08 00 00 00 00 64 00 00`; E5 page_key=`0x0010` (~1055 bytes, or 0x0000/44 bytes when BE11529 sends a duplicate) | + +**E5 response `page_key` field:** bytes `data[3:5]` of the E5 payload. `0x0000` = header/short chunk, `0x0010` = main config data. + +**Assembling cfg:** concatenate non-duplicate chunks in order (B+C+D). Track `(page_key, chunk_len)` pairs; drop any repeat to avoid mis-alignment. + +**Expected cfg lengths:** +- Frame D delivers page `0x0010` correctly: **44 + 1027 + 1055 = 2126 bytes** +- Frame D duplicates page `0x0000` (BE11529 occasional): **44 + 1027 = 1071 bytes** (still fully decodable) + +--- + +### 7.6.3 Sample Rate and DLE Jitter + +> ✅ **CONFIRMED — 2026-04-01** (BE11529 / firmware S338.17). Validated across Normal (1024), Fast (2048), and Faster (4096) modes. + +**Location:** `uint16 BE` at **anchor − 2**, where anchor = `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` in cfg[40:100]. + +| Device Mode | uint16 BE value | Sa/s | +|---|---|---| +| Normal | `04 00` | 1024 | +| Fast | `08 00` | 2048 | +| Faster | `10 00` | 4096 | + +**DLE jitter (critical — explains the ±1 byte cfg length variation):** + +The sample rate bytes sit immediately before a `0x10` (DLE) prefix byte in the raw S3 frame. For the "Faster" mode (4096 = `0x1000`), the high byte `0x10` is itself a DLE character and must be escaped in the S3 frame as `10 10`. After DLE unstuffing: `10 10 00` → `10 00` (2 bytes). For Normal/Fast modes (high byte = `0x04`/`0x08`), no escaping needed: payload stays 3 bytes. Result: "Faster" mode produces a cfg that is **1 byte shorter** than Normal/Fast, shifting all subsequent absolute offsets by −1. + +**Why anchor search is required:** any decoder that uses fixed absolute offsets for record_time or sample_rate will produce garbage values when the device is set to "Faster" mode. The 10-byte anchor `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` (`0x012C` = 300 s max-record-length constant, followed by two alarm-level floats `\xbe\x80\x00\x00\x00\x00`) straddles the boundary and is unaffected by this shift. --- @@ -1330,9 +1385,10 @@ The `.bin` files produced by `s3_bridge` are **not raw wire bytes**. The logger | Exact byte boundaries of project string fields in SUB `71` write frame — padding rules unconfirmed | MEDIUM | 2026-02-26 | | | Purpose of SUB `09` / response `F6` — 202-byte read block | MEDIUM | 2026-02-26 | | | Purpose of SUB `2E` / response `D1` — 26-byte read block | MEDIUM | 2026-02-26 | | -| Full field mapping of SUB `1A` / response `E5` — channel scaling / compliance config block | MEDIUM | 2026-02-26 | | +| Full field mapping of SUB `1A` / response `E5` — channel scaling / compliance config block. **SUBSTANTIALLY RESOLVED 2026-04-01:** Multi-frame sequence (§7.6.2), page_key chunking, duplicate-page detection, record_time (§7.6.1), sample_rate (§7.6.3), setup_name, project strings all confirmed. Trigger/alarm level floats in the channel block (§7.6) also confirmed. Remaining unknowns: trigger level (geo), alarm level (geo), max range (in CFG header not yet decoded). | MEDIUM | 2026-02-26 | Substantially resolved 2026-04-01 | | `0x082A` in channel config block — not trigger, alarm, or record time directly. **RESOLVED: fixed E5 payload length (2090 bytes).** Constant regardless of all settings. | RESOLVED | 2026-03-01 | Resolved 2026-03-09 | -| **Record time in wire protocol** — float32 BE at E5 data page2 `+0x28`. **RESOLVED.** See §7.6.1. | RESOLVED | 2026-03-09 | Confirmed via 7→13 sec captures | +| **Record time in wire protocol** — float32 BE, anchor-relative in cfg (see §7.6.1/§7.6.3). **RESOLVED.** Previous `+0x28` offset was unreliable due to DLE jitter — superseded by anchor search. Confirmed at 3, 5, 7, 8, 10, 13 seconds. | RESOLVED | 2026-03-09 | Confirmed 2026-03-09; anchor method confirmed 2026-04-01 | +| **Sample rate** — uint16 BE at anchor−2 in cfg. **RESOLVED.** Normal=1024, Fast=2048, Faster=4096. Anchor required to handle DLE jitter. See §7.6.3. | RESOLVED | 2026-04-01 | Confirmed 2026-04-01 | | Unknown uint16 fields at channel block +0A (=80), +0C (=15), +0E (=40), +10 (=21) — manual describes "Sensitive (Gain=8) / Normal (Gain=1)" per-channel range; 80/15/40/21 might encode gain, sensitivity, or ADC config. | LOW | 2026-03-01 | | | Full trigger configuration field mapping (SUB `1C` / write `82`) | LOW | 2026-02-26 | | | Whether SUB `24`/`25` are distinct from SUB `5A` or redundant | LOW | 2026-02-26 | | @@ -1365,10 +1421,10 @@ The `.bin` files produced by `s3_bridge` are **not raw wire bytes**. The logger | Alarm Level (Geo) | §3.9.9 | Channel block, float | float32 BE | higher than trigger level | | Trigger Level (Mic) | §3.8.6 | Channel block, float | float32 BE | 100–148 dB in 1 dB steps | | Alarm Level (Mic) | §3.9.10 | Channel block, float | float32 BE | higher than mic trigger | -| Record Time | §3.8.9 | E5 data page2 `+0x28` (wire); `.set` +16 (file) | float32 BE (wire); uint32 LE (file) | 1–105 seconds (menu label `<105`); confirmed 7→`40E00000`, 10→`41200000`, 13→`41500000` | +| Record Time | §3.8.9 | cfg anchor+10, float32 BE (wire); `.set` +16, uint32 LE (file) | float32 BE (wire) | 1–105 s; confirmed 3→`40400000`, 5→`40A00000`, 8→`41000000`, 13→`41500000`. Use anchor §7.6.1/§7.6.3 — NOT fixed offset. | | Max Geo Range | §3.8.4 | Channel block, float | float32 BE | 1.25 or 10.0 in/s (user); 6.2061 in protocol = internal constant | | Microphone Units | §3.9.7 | Inline unit string | char[4] | `"psi\0"`, `"pa.\0"`, `"dB\0\0"` | -| Sample Rate | §3.8.2 | Unknown — needs capture | — | 1024, 2048, 4096 (compliance); up to 65536 (advanced) | +| Sample Rate | §3.8.2 | cfg anchor−2, uint16 BE — anchor=`\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` in cfg[40:100] | uint16 BE | Normal=1024, Fast=2048, Faster=4096 ✅ CONFIRMED 2026-04-01 (BE11529 S338.17). Anchor required — see §7.6.3 DLE jitter. | | Record Mode | §3.8.1 | Unknown | — | Single Shot, Continuous, Manual, Histogram, Histogram Combo | | Trigger Sample Width | §3.13.1h | BW→S3 SUB `0x82` write frame, destuffed `[22]`, uint8 | uint8 | Default=2; confirmed 4=`0x04`, 3=`0x03`. **BW-side write only** — not visible in S3 compliance reads. Mode-gated: only sent in Compliance/Single-Shot/Fixed mode. | | Auto Window | §3.13.1b | **Mode-gated — NOT YET MAPPED** | uint8? | 1–9 seconds; only active when Record Stop Mode = Auto. Capture in Fixed mode produced no wire change. |