feat: implement SUB 1A (compliance config) read

Adds full support for reading device compliance configuration (2090-byte E5
response) containing record time, trigger/alarm levels, and project strings.

protocol.py:
- Implement read_compliance_config() two-step read (SUB 1A → E5)
- Fixed length 0x082A (2090 bytes)

models.py:
- Add ComplianceConfig dataclass with fields: record_time, sample_rate,
  trigger_level_geo, alarm_level_geo, max_range_geo, project strings
- Add compliance_config field to DeviceInfo

client.py:
- Implement _decode_compliance_config_into() to extract:
  * Record time float at offset +0x28 
  * Trigger/alarm levels per-channel (heuristic parsing) 🔶
  * Project/setup strings from E5 payload
  * Placeholder for sample_rate (location TBD )
- Update connect() to read SUB 1A after SUB 01, cache in device_info
- Add ComplianceConfig to imports

sfm/server.py:
- Add _serialise_compliance_config() JSON encoder
- Include compliance_config in /device/info response
- Updated _serialise_device_info() to output compliance config

Both record_time (at fixed offset 0x28) and project strings are  CONFIRMED
from protocol reference §7.6. Trigger/alarm extraction uses heuristics
pending more detailed field mapping from captured data.

Sample rate remains undiscovered in the E5 payload — likely in the
mystery flags at offset +0x12 or requires a "fast mode" capture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Brian Harrison
2026-04-01 12:08:43 -04:00
parent a8187eccd0
commit 32b9d3050c
4 changed files with 222 additions and 9 deletions

View File

@@ -395,6 +395,44 @@ class MiniMateProtocol:
)
return key4
def read_compliance_config(self) -> bytes:
"""
Send the SUB 1A (CHANNEL_SCALING / COMPLIANCE_CONFIG) two-step read.
Returns the full 2090-byte compliance config block (E5 response)
containing:
- Trigger and alarm levels per channel (IEEE 754 BE floats)
- Record time (float at offset +0x28)
- Project strings (Project, Client, User Name, Seis Loc, Extended Notes)
- Channel labels and unit strings
- Per-channel max range values
Returns:
2090-byte compliance config data (data[11:11+0x082A]).
Raises:
ProtocolError: on timeout, bad checksum, or wrong response SUB.
Confirmed from protocol reference §7.6:
- SUB 1A request uses all-zero params
- E5 response is 2090 bytes (0x082A fixed length)
- Response data section: data[11:11+0x082A]
"""
rsp_sub = _expected_rsp_sub(SUB_COMPLIANCE)
length = 0x082A # 2090 bytes, fixed length for compliance config
log.debug("read_compliance_config: 1A probe")
self._send(build_bw_frame(SUB_COMPLIANCE, 0))
self._recv_one(expected_sub=rsp_sub)
log.debug("read_compliance_config: 1A data request offset=0x%04X", length)
self._send(build_bw_frame(SUB_COMPLIANCE, length))
data_rsp = self._recv_one(expected_sub=rsp_sub)
config = data_rsp.data[11:11 + length]
log.debug("read_compliance_config: received %d config bytes", len(config))
return config
# ── Internal helpers ──────────────────────────────────────────────────────
def _send(self, frame: bytes) -> None: