feat: mapped record_mode protocol
This commit is contained in:
@@ -360,6 +360,7 @@ Do NOT use fixed absolute offsets for sample_rate or record_time.
|
||||
|
||||
| Field | How to find it |
|
||||
|---|---|
|
||||
| **recording_mode** | **uint8 at anchor − 3 (write payload) / anchor − 4 (read response)** ✅ confirmed 2026-04-20 |
|
||||
| sample_rate | uint16 BE at anchor − 2 |
|
||||
| record_time | float32 BE at anchor + 10 |
|
||||
| trigger_level_geo | float32 BE, located in channel block |
|
||||
@@ -371,6 +372,18 @@ Do NOT use fixed absolute offsets for sample_rate or record_time.
|
||||
|
||||
Anchor: `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`, search `cfg[0:150]`
|
||||
|
||||
**recording_mode enum** (confirmed 2026-04-20 from 4-20-26 captures):
|
||||
|
||||
| Value | Mode |
|
||||
|---|---|
|
||||
| `0x00` | Single Shot |
|
||||
| `0x01` | Continuous |
|
||||
| `0x02` | ❓ not observed |
|
||||
| `0x03` | Histogram |
|
||||
| `0x04` | Histogram + Continuous |
|
||||
|
||||
**Offset note:** The write payload (SUB 71 cfg) has recording_mode 3 bytes before the anchor (`anchor_pos − 3`). The E5 read response has it 4 bytes before (`anchor_pos − 4`), with an extra `0x10` byte sitting between recording_mode and sample_rate in the read format. Use `anchor_pos − 3` when encoding writes; use `anchor_pos − 4` when decoding reads.
|
||||
|
||||
### SUB 0C — Waveform Record (210 bytes = data[11:11+0xD2])
|
||||
|
||||
**sub_code=0x10 (Waveform single-shot) — 9-byte timestamp header:**
|
||||
@@ -704,8 +717,8 @@ Fields visible in the Blastware Compliance Setup dialog — most are NOT YET dec
|
||||
offsets in the raw 1A/E5 payload. Only fields with `✅` have confirmed offsets in the code.
|
||||
|
||||
**Recording Setup tab:**
|
||||
- Recording Mode: Continuous / Single Shot / Histogram (enum)
|
||||
- Record Stop Mode: Fixed Record Time / Auto / Manual Stop (enum)
|
||||
- Recording Mode: Continuous / Single Shot / Histogram / Histogram+Continuous ✅ (uint8 at anchor−3 in write, anchor−4 in read; 0x00=Single Shot, 0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous) — confirmed 2026-04-20
|
||||
- Record Stop Mode: Fixed Record Time / Auto / Manual Stop (enum) ❓ (byte near recording_mode; data[40] in E5 sf1 changed 0x01→0x00 alongside Continuous→Single Shot — may be this field)
|
||||
- Sample Rate: Standard 1024 / Fast 2048 / Faster 4096 sps ✅ (anchor−2)
|
||||
- Record Time: float, seconds ✅ (anchor+10)
|
||||
- Histogram Interval: 5 / 15 / 30 / 60 minutes (enum, mode-gated)
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
| 2026-04-11 | §7.11 (NEW) | **NEW — §7.11 Erase-All Protocol added.** Full wire sequence, SUB 0x06 storage range payload layout, post-erase key counter reset (resets to `0x01110000`). Confirmed from 4-11-26 MITM capture of live Blastware ACH session. |
|
||||
| 2026-04-11 | §14.6 | **RESOLVED — ACH Session Lifecycle is no longer "Future".** `bridges/ach_server.py` fully implements inbound ACH: POLL handshake, device info, event download. State tracked via `ach_state.json` (key-based, with `max_downloaded_key` for post-erase detection). `--clear-after-download` flag added for the standard delete-after-upload workflow. |
|
||||
| 2026-04-17 | §7.6.2, §14 | **RESOLVED — Float 6.206053 at channel_label+28 is the ADC-to-velocity scale factor.** Confirmed from Series III Interface Handbook §4.5 formula: `Range (×1) = 1.61133 V / Sensitivity (V/unit)`. For the standard Instantel geophone at Normal range (10.000 in/s): Sensitivity = 1.61133 / 10 = 0.161133 V/(in/s). The stored value is the **inverse sensitivity** = 1/0.161133 = **6.206053 (in/s)/V**. Cross-check: 1.61133 V × 6.206053 = 10.000 in/s ✅. The firmware uses it as: `PPV (in/s) = ADC_voltage (V) × 6.206053`. Value is identical on all Instantel standard geophones — it is a hardware/firmware constant, NOT a user-configurable setting. Do NOT write this field. Open question §14 item "Max Geo Range float 6.2061" is now **RESOLVED**. |
|
||||
| 2026-04-20 | §7.6.4 (NEW), §7.9, Appendix B | **CONFIRMED — Recording Mode byte location.** Three targeted captures (4-20-26) confirmed `recording_mode` at **`cfg[5]`** in the SUB 71 write payload (3-chunk compliance write). Method: single Blastware session, one initial E5 config pull, then three sequential "Send to unit" writes changing Recording Mode only. Diff of SUB 71 chunk-1 payloads: only `cfg[5]` and `cfg[1024]` changed; `cfg[1024]` delta exactly equals `cfg[5]` delta (chunk running checksum). In the E5 read response (sub-frame 1, page=0x0010), the field is at **`data[17]`** (= **anchor − 4** from the 10-byte anchor), one position earlier than in the write payload due to an extra `0x10` byte at `data[18]` present only in the read format. Enum: `0x00`=Single Shot, `0x01`=Continuous, `0x03`=Histogram, `0x04`=Histogram+Continuous. `0x02` value not yet observed. See §7.6.4 for full details. |
|
||||
|
||||
---
|
||||
|
||||
@@ -258,7 +259,7 @@ Step 4 — Device sends actual data payload:
|
||||
| `24` | **WAVEFORM PAGE A?** | Paged waveform read, possibly channel group A. | 🔶 INFERRED |
|
||||
| `25` | **WAVEFORM PAGE B?** | Paged waveform read, possibly channel group B. | 🔶 INFERRED |
|
||||
| `09` | **UNKNOWN READ A** | Read command, response (`F6`) returns 0xCA (202) bytes. Purpose unknown. | 🔶 INFERRED |
|
||||
| `1A` | **COMPLIANCE CONFIG READ** | Multi-step sequence (A+B+C+D frames). Response (E5) carries sample_rate (uint16 BE at anchor−2), record_time (float32 BE at anchor+10), trigger/alarm/max_range floats, and project strings. Anchor: `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00`, search cfg[0:150]. Total ~2126 cfg bytes. | ✅ CONFIRMED 2026-04-02 |
|
||||
| `1A` | **COMPLIANCE CONFIG READ** | Multi-step sequence (A+B+C+D frames). Response (E5) carries recording_mode (uint8 at anchor−4 in E5 sf1), sample_rate (uint16 BE at anchor−2), record_time (float32 BE at anchor+10), trigger/alarm/max_range floats, and project strings. Anchor: `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00`, search cfg[0:150]. Total ~2126 cfg bytes. See §7.6.4 for recording_mode enum. | ✅ CONFIRMED 2026-04-02; recording_mode added 2026-04-20 |
|
||||
| `2E` | **UNKNOWN READ B** | Read command, response (`D1`) returns 0x1A (26) bytes. Purpose unknown. | 🔶 INFERRED |
|
||||
| `0E` | **CHANNEL SENSOR DATA** | Real-time sensor reading for one channel. Two-step read, data length 0x0A (10 bytes). Channel selector in params[6:8] (0x0000–0x0007 for 8 channels). Response (F1) carries amplitude, frequency, overswing data for that channel. Used by Blastware "Unit Channel Test" comms check. | ✅ CONFIRMED 2026-04-08 |
|
||||
| `98` | **TRIGGER TEST** | Trigger-test command. Single probe frame; `params[0] = 0xFF`. Response (0x67) is all-zero data. Sent twice per Blastware comms-check cycle. Not a full POLL, no monitor state change. | ✅ CONFIRMED 2026-04-08 |
|
||||
@@ -621,6 +622,53 @@ The sample rate bytes sit immediately before a `0x10` (DLE) prefix byte in the r
|
||||
|
||||
---
|
||||
|
||||
### 7.6.4 Recording Mode
|
||||
|
||||
> ✅ **CONFIRMED — 2026-04-20** (BE11529 / firmware S338.17). Three targeted captures in a single Blastware session (4-20-26 directory), changing Recording Mode only between each write.
|
||||
|
||||
Recording mode is stored as a **uint8** with different anchor-relative positions depending on whether you are reading from a device response or constructing a write payload.
|
||||
|
||||
**In the SUB 71 write payload (3-chunk compliance write, `cfg[5]`):**
|
||||
|
||||
| Enum | Mode |
|
||||
|---|---|
|
||||
| `0x00` | Single Shot |
|
||||
| `0x01` | Continuous |
|
||||
| `0x02` | Unknown (not yet observed) |
|
||||
| `0x03` | Histogram |
|
||||
| `0x04` | Histogram + Continuous (combined mode) |
|
||||
|
||||
Anchor-relative position: **anchor − 3** (3 bytes before the 10-byte anchor in the write payload). The write payload layout in the region around the anchor:
|
||||
|
||||
```
|
||||
cfg[anchor - 3] = recording_mode (uint8)
|
||||
cfg[anchor - 2] = sample_rate_hi (uint8, MSB of uint16 BE)
|
||||
cfg[anchor - 1] = sample_rate_lo (uint8, LSB of uint16 BE)
|
||||
cfg[anchor:anchor+10] = \x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00 ← anchor
|
||||
cfg[anchor + 10:anchor + 14] = record_time (float32 BE)
|
||||
```
|
||||
|
||||
**In the E5 read response (sub-frame 1, page=`0x0010`, `data[17]`):**
|
||||
|
||||
The anchor appears at `data[21]` in this sub-frame. Recording mode is at `data[17]` = **anchor − 4** (one position earlier than in the write payload). This is because an extra `0x10` byte is present at `data[18]` in the read format (between recording_mode and sample_rate), which is NOT present in the write payload. The read-format layout:
|
||||
|
||||
```
|
||||
data[17] = recording_mode (uint8)
|
||||
data[18] = 0x10 ← extra byte present in E5 read only; absent in SUB 71 write
|
||||
data[19] = sample_rate_hi (uint8, MSB of uint16 BE)
|
||||
data[20] = sample_rate_lo (uint8, LSB of uint16 BE)
|
||||
data[21:31] = anchor (\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00)
|
||||
data[31:35] = record_time (float32 BE)
|
||||
```
|
||||
|
||||
**Chunk checksum at `cfg[1024]`:** The first of the three SUB 71 write chunks (1027 bytes) contains a running checksum byte at `cfg[1024]` whose delta exactly equals the delta of `cfg[5]` (recording_mode). This byte reflects the cumulative change from `recording_mode` through to its position and should not be mistaken for a second copy of the recording_mode field.
|
||||
|
||||
**Decode path (`_decode_compliance_config_into`):** use `data[anchor_pos - 4]` where `anchor_pos` is the index of the first byte of the anchor in the assembled E5 cfg bytes.
|
||||
|
||||
**Encode path (`_encode_compliance_config`):** use `cfg[anchor_pos - 3]` = recording_mode value (write-payload offset; no extra `0x10` byte).
|
||||
|
||||
---
|
||||
|
||||
### 7.7 Blastware `.set` File Format
|
||||
|
||||
> 🔶 **INFERRED — 2026-03-01** from `Standard_Recording_Setup.set` cross-referenced against known wire payloads.
|
||||
@@ -1264,8 +1312,8 @@ Fields visible in the Blastware "Compliance Setup" dialog. ✅ = byte offset co
|
||||
|
||||
| Field | Values / Type | Status |
|
||||
|---|---|---|
|
||||
| Recording Mode | Continuous / Single Shot / Histogram | ❓ |
|
||||
| Record Stop Mode | Fixed Record Time / Auto / Manual Stop | ❓ |
|
||||
| Recording Mode | Single Shot (`0x00`) / Continuous (`0x01`) / Histogram (`0x03`) / Histogram+Continuous (`0x04`) | ✅ `recording_mode` — write: `cfg[anchor−3]`; read E5 sf1: `data[anchor−4]` — confirmed 2026-04-20 |
|
||||
| Record Stop Mode | Fixed Record Time / Auto / Manual Stop | ❓ Hint: `data[40]` in E5 sf1 changed `01 7F` → `00 00` alongside Continuous → Single Shot; may be related but unconfirmed independently |
|
||||
| Sample Rate | Standard 1024 / Fast 2048 / Faster 4096 sps | ✅ `sample_rate` (anchor−2) |
|
||||
| Record Time | float, seconds (3, 5, 8, 10, 13…) | ✅ `record_time` (anchor+10) |
|
||||
| Histogram Interval | 5 / 15 / 30 / 60 min (mode-gated behind Histogram mode) | ❓ |
|
||||
@@ -1974,7 +2022,7 @@ The `.bin` files produced by `s3_bridge` are **not raw wire bytes**. The logger
|
||||
| Max Geo Range Enum (max_range_geo_enum) | §3.8.4 | Channel block, Tran+20, uint8 | uint8 | ❓ UNCONFIRMED — reads `0x01` on both tested units (Normal range). Hypothesis: `0x01`=Normal (Gain=1, 10 in/s), `0x00`=Sensitive (Gain=8, 1.25 in/s). Need 1.25 in/s capture to verify. |
|
||||
| Microphone Units | §3.9.7 | Inline unit string | char[4] | `"psi\0"`, `"pa.\0"`, `"dB\0\0"` |
|
||||
| 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 |
|
||||
| Record Mode | §3.8.1 | Write: `cfg[anchor−3]`, uint8. Read (E5 sf1): `data[anchor−4]`, uint8. Note: extra `0x10` byte at read `data[anchor−3]` shifts offset by 1 vs write. | uint8 | `0x00`=Single Shot, `0x01`=Continuous, `0x02`=unknown, `0x03`=Histogram, `0x04`=Histogram+Continuous. ✅ CONFIRMED 2026-04-20 |
|
||||
| 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. |
|
||||
| Auxiliary Trigger | §3.13.1d | SUB `FE` (FULL_CONFIG_RESPONSE) offset `0x0109` (read); write path not yet isolated | uint8 (bool) | `0x00`=disabled, `0x01`=enabled; confirmed 2026-03-11 |
|
||||
|
||||
+46
-3
@@ -847,6 +847,7 @@ class MiniMateClient:
|
||||
self,
|
||||
*,
|
||||
# Recording parameters
|
||||
recording_mode: Optional[int] = None,
|
||||
sample_rate: Optional[int] = None,
|
||||
record_time: Optional[float] = None,
|
||||
# Threshold parameters (geo channels, in/s)
|
||||
@@ -869,6 +870,7 @@ class MiniMateClient:
|
||||
Configurable fields
|
||||
-------------------
|
||||
Recording parameters:
|
||||
recording_mode : int — 0x00=Single Shot, 0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous
|
||||
sample_rate : int — samples/sec; valid values: 1024, 2048, 4096
|
||||
record_time : float — record duration in seconds (e.g. 2.0, 3.0)
|
||||
|
||||
@@ -914,6 +916,7 @@ class MiniMateClient:
|
||||
# 2. Patch the compliance buffer and build the 2128-byte write payload
|
||||
compliance_data = _encode_compliance_config(
|
||||
compliance_raw,
|
||||
recording_mode=recording_mode,
|
||||
sample_rate=sample_rate,
|
||||
record_time=record_time,
|
||||
trigger_level_geo=trigger_level_geo,
|
||||
@@ -1650,6 +1653,7 @@ def _extract_project_strings(data: bytes) -> Optional[ProjectInfo]:
|
||||
def _encode_compliance_config(
|
||||
raw: bytes,
|
||||
*,
|
||||
recording_mode: Optional[int] = None,
|
||||
sample_rate: Optional[int] = None,
|
||||
record_time: Optional[float] = None,
|
||||
trigger_level_geo: Optional[float] = None,
|
||||
@@ -1670,6 +1674,11 @@ def _encode_compliance_config(
|
||||
DLE-jitter shifts):
|
||||
|
||||
Anchor: b'\\xbe\\x80\\x00\\x00\\x00\\x00' (confirmed stable, both BE11529 and BE18189)
|
||||
recording_mode → uint8 at anchor_pos - 7 (write payload; one byte earlier than read format)
|
||||
Values: 0x00=Single Shot, 0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous
|
||||
NOTE: In the E5 read response the field is at anchor_pos - 8 due to an extra
|
||||
0x10 byte at anchor_pos - 7 present only in the read format. The write
|
||||
payload does NOT have this extra byte — use anchor_pos - 7 here.
|
||||
sample_rate → uint16 BE at anchor_pos - 6
|
||||
record_time → float32 BE at anchor_pos + 6
|
||||
|
||||
@@ -1702,10 +1711,18 @@ def _encode_compliance_config(
|
||||
|
||||
buf = bytearray(raw)
|
||||
|
||||
# ── Numeric: sample_rate + record_time (anchor-relative) ─────────────────
|
||||
# ── Numeric: recording_mode + sample_rate + record_time (anchor-relative) ──
|
||||
_ANC = b'\xbe\x80\x00\x00\x00\x00'
|
||||
_anc = buf.find(_ANC, 0, 150)
|
||||
|
||||
if recording_mode is not None:
|
||||
if _anc < 7:
|
||||
log.warning("_encode_compliance_config: anchor not found — cannot write recording_mode")
|
||||
else:
|
||||
buf[_anc - 7] = recording_mode & 0xFF
|
||||
log.debug("_encode_compliance_config: recording_mode=0x%02X -> offset %d",
|
||||
recording_mode, _anc - 7)
|
||||
|
||||
if sample_rate is not None:
|
||||
if _anc < 6:
|
||||
log.warning("_encode_compliance_config: anchor not found — cannot write sample_rate")
|
||||
@@ -1838,7 +1855,22 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
# record_time : float32 BE at anchor_pos + 6
|
||||
_ANCHOR = b'\xbe\x80\x00\x00\x00\x00'
|
||||
_anchor = data.find(_ANCHOR, 0, 150)
|
||||
if _anchor >= 6 and _anchor + 10 <= len(data):
|
||||
if _anchor >= 8 and _anchor + 10 <= len(data):
|
||||
# Layout (E5 read format, relative to 6-byte anchor suffix):
|
||||
# _anchor - 8 : recording_mode (uint8)
|
||||
# _anchor - 7 : 0x10 (extra byte present in E5 read only; absent in SUB 71 write)
|
||||
# _anchor - 6 : sample_rate_hi (uint8, MSB of uint16 BE)
|
||||
# _anchor - 5 : sample_rate_lo (uint8, LSB of uint16 BE)
|
||||
# _anchor - 4 : \x01\x2c\x00\x00 (10-byte anchor prefix)
|
||||
# _anchor : \xbe\x80\x00\x00\x00\x00 (6-byte anchor suffix)
|
||||
# _anchor + 6 : record_time (float32 BE)
|
||||
try:
|
||||
config.recording_mode = data[_anchor - 8]
|
||||
log.debug(
|
||||
"compliance_config: recording_mode = 0x%02X (anchor@%d)", config.recording_mode, _anchor
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning("compliance_config: recording_mode extraction failed: %s", exc)
|
||||
try:
|
||||
config.sample_rate = struct.unpack_from(">H", data, _anchor - 6)[0]
|
||||
log.debug(
|
||||
@@ -1853,10 +1885,21 @@ def _decode_compliance_config_into(data: bytes, info: DeviceInfo) -> None:
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning("compliance_config: record_time extraction failed: %s", exc)
|
||||
elif _anchor >= 6 and _anchor + 10 <= len(data):
|
||||
# Fallback: anchor found but not enough bytes before it for recording_mode
|
||||
log.warning("compliance_config: anchor too close to start (anchor@%d) — skipping recording_mode", _anchor)
|
||||
try:
|
||||
config.sample_rate = struct.unpack_from(">H", data, _anchor - 6)[0]
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
config.record_time = struct.unpack_from(">f", data, _anchor + 6)[0]
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
log.warning(
|
||||
"compliance_config: anchor %s not found in cfg[0:150] (len=%d) "
|
||||
"— sample_rate and record_time will be None",
|
||||
"— sample_rate, record_time and recording_mode will be None",
|
||||
_ANCHOR.hex(), len(data),
|
||||
)
|
||||
|
||||
|
||||
@@ -338,8 +338,12 @@ class ComplianceConfig:
|
||||
raw: Optional[bytes] = None # full 2090-byte payload (for debugging)
|
||||
|
||||
# Recording parameters (✅ CONFIRMED from §7.6)
|
||||
recording_mode: Optional[int] = None # uint8: 0x00=Single Shot, 0x01=Continuous,
|
||||
# 0x03=Histogram, 0x04=Histogram+Continuous ✅ confirmed 2026-04-20
|
||||
# Read (E5 sf1): data[anchor_pos - 4]
|
||||
# Write (SUB 71 payload): cfg[anchor_pos - 3]
|
||||
record_time: Optional[float] = None # seconds (7.0, 10.0, 13.0, etc.)
|
||||
sample_rate: Optional[int] = None # sps (1024, 2048, 4096, etc.) — NOT YET FOUND ❓
|
||||
sample_rate: Optional[int] = None # sps (1024, 2048, 4096, etc.)
|
||||
|
||||
# Trigger/alarm levels (✅ CONFIRMED per-channel at §7.6)
|
||||
# For now we store the first geo channel (Transverse) as representatives;
|
||||
|
||||
Reference in New Issue
Block a user