feat: add histogram_interval setting and update UI with new field.

This commit is contained in:
2026-04-20 16:25:56 -04:00
parent 702e06873e
commit eec6c3dc6a
5 changed files with 119 additions and 56 deletions
+27 -7
View File
@@ -307,10 +307,16 @@ producing only ~1071 bytes instead of ~2126.
### SUB 1A — anchor search range
`_decode_compliance_config_into()` locates sample_rate and record_time via the anchor
`b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`. Search range is `cfg[0:150]`.
`_decode_compliance_config_into()` locates fields via the **6-byte stable anchor**
`b'\xbe\x80\x00\x00\x00\x00'`. Search range is `cfg[0:150]`.
Do not narrow this to `cfg[40:100]` — the old range was only accidentally correct because
**IMPORTANT — the "10-byte anchor" `\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00` is NOT fully constant.**
The first 2 bytes (`\x01\x2c` = 300) are the `histogram_interval_sec` field (uint16 BE, seconds) —
the value 300 is just the 5-minute default. When histogram interval is set to a different value
(e.g. 15min = 0x0384 = `\x03\x84`), those bytes change. Only the 6-byte suffix
`\xbe\x80\x00\x00\x00\x00` is truly constant. The code already uses the 6-byte anchor.
Do not narrow the search range to `cfg[40:100]` — the old range was only accidentally correct because
the orphaned-send bug was prepending a 44-byte spurious header, pushing the anchor from
its real position (cfg[11]) into the 40100 window.
@@ -360,8 +366,9 @@ 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 |
| **recording_mode** | **uint8 at anchor 3 (write) / anchor 4 (read)** ✅ confirmed 2026-04-20 |
| sample_rate | uint16 BE at anchor 2 |
| **histogram_interval_sec** | **uint16 BE at anchor 4 (seconds); same offset in read & write** ✅ confirmed 2026-04-20 |
| record_time | float32 BE at anchor + 10 |
| trigger_level_geo | float32 BE, located in channel block |
| alarm_level_geo | float32 BE, adjacent to trigger_level_geo |
@@ -370,7 +377,20 @@ Do NOT use fixed absolute offsets for sample_rate or record_time.
| setup_name | ASCII, null-padded, in cfg body |
| project / client / operator / sensor_location | ASCII, label-value pairs |
Anchor: `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`, search `cfg[0:150]`
**True stable anchor: `b'\xbe\x80\x00\x00\x00\x00'` (6-byte suffix), search `cfg[0:150]`.**
The old "10-byte anchor" `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'` is partially variable:
bytes `\x01\x2c` = 300 (5-minute default histogram interval); changes when interval changes.
**Field layout relative to the 6-byte anchor (write payload / E5 read — noted where different):**
| Offset | Field | Format | Notes |
|---|---|---|---|
| anchor 7 (write) / anchor 8 (read) | recording_mode | uint8 | E5 read has extra `0x10` at anchor7 |
| anchor 6 | sample_rate | uint16 BE | same in read & write |
| anchor 4 | histogram_interval_sec | uint16 BE | seconds; same in read & write ✅ 2026-04-20 |
| anchor 2 | `0x00 0x00` | padding | |
| anchor | `\xbe\x80\x00\x00\x00\x00` | anchor | |
| anchor + 6 | record_time | float32 BE | same in read & write |
**recording_mode enum** (confirmed 2026-04-20 from 4-20-26 captures):
@@ -382,7 +402,7 @@ Anchor: `b'\x01\x2c\x00\x00\xbe\x80\x00\x00\x00\x00'`, search `cfg[0:150]`
| `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.
**DLE escaping in write frames — CONFIRMED 2026-04-20:** Write frame data payloads DO escape `0x03` (ETX) bytes with a `0x10` DLE prefix. For histogram_interval = 900 (0x0384), the wire carries `10 03 84` — the `0x03` high byte is preceded by a DLE escape. After DLE destuffing (`10 XX → XX`), the logical field value is correctly `03 84` = 900. The CLAUDE.md claim that write frame data is "written RAW" was incorrect; at minimum ETX (0x03) bytes are escaped. S3FrameParser handles this transparently so the decoded `compliance_raw` always contains logical (destuffed) bytes.
### SUB 0C — Waveform Record (210 bytes = data[11:11+0xD2])
@@ -721,7 +741,7 @@ offsets in the raw 1A/E5 payload. Only fields with `✅` have confirmed offsets
- 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 ✅ (anchor2)
- Record Time: float, seconds ✅ (anchor+10)
- Histogram Interval: 5 / 15 / 30 / 60 minutes (enum, mode-gated)
- Histogram Interval: 2s / 5s / 15s / 1m / 5m / 15m ✅ (uint16 BE seconds at anchor4, same in read & write; mode-gated to Histogram/Histogram+Continuous) — confirmed 2026-04-20
- Storage Mode: Save All Data / Save Triggered (enum)
- Geophone Type: Standard Triaxial / 4.5 Hz (bool/enum)
- Geophone Channels: Enable all geophones (bool), Trigger Source (bool)