The channel block is only present in the full ~2126-byte cfg (when frame D
delivers correctly rather than duplicating frame B's page). Layout per §7.6:
[00 00][max_range f32][00 00][trigger f32]["in.\0"][alarm f32]["/s\0\0"][00 01][label]
Relative offsets from the "Tran" label position (label-24/label-18/label-10)
are validated by checking the unit strings "in.\0" at label-14 and "/s\0\0"
at label-6 before reading the floats. Guard against "Tran2" false-match.
When frame D duplicates, cfg is ~1071 bytes and tran_pos search returns a hit
without the unit string sentinels — we log the miss and leave fields None.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-step probe+fetch for SUB 08 (EVENT_INDEX), returning the raw 88-byte
(0x58) index block. SUB_EVENT_INDEX and DATA_LENGTHS[0x08]=0x58 were
already registered — this just wires the method that calls them.
Docstring notes the partially-decoded layout (event count at +3 as uint32 BE,
timestamps at +7) pending live device confirmation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
record_time (float32_BE) and sample_rate (uint16_BE) both validated
against live device across normal / fast / faster modes and multiple
record time settings. Diagnostic scaffolding no longer needed.
Fixed a 1-byte offset jitter that produced garbage values when the
device was set to "faster" (4096 Sa/s) mode.
Root cause: 4096 = 0x1000, so the sample_rate bytes in the raw S3
frame are `10 10 00` (DLE-escaped). After DLE unstuffing → `10 00`
(2 bytes vs 3 for 1024/2048), making frame C 1 byte shorter and
shifting all subsequent field offsets by -1.
Fix: locate the stable 10-byte anchor `01 2c 00 00 be 80 00 00 00 00`
(max-record-limit constant + first two alarm-level floats) and read:
sample_rate = uint16_BE at anchor - 2
record_time = float32_BE at anchor + 10
Offline-validated against all five logged hex dumps (1071 and 1070
byte cfg, record times 3/5/8 s, sample rates 1024 and 4096):
all five: correct values with anchor approach.
Both offsets identified from _cfg_diagnostic scan on BE11529:
cfg[64] float32_BE = 3.0 → record_time in seconds
cfg[52] uint16_BE = 1024 → sample_rate in Sa/s
Values are plausible but NOT yet validated by changing device settings
and re-reading. Marked as ✅ candidate in docstring — confirm and
remove the ⚠️ note once a device-side change is observed here.
BE11529 sometimes returns frame D with page_key=0x0000 (44 bytes),
identical to the frame B response, inflating cfg to ~1115 bytes and
mis-aligning all field offsets. Track (page_key, chunk_size) pairs
and drop any repeat before appending to the running config buffer.
- setup_name was broken: _find_string_after(b"Standard Recording Setup")
returned what comes AFTER the string (i.e. "Project:"), not the name
itself. Fixed by searching for the first long (>=8 char) ASCII string
in cfg[40:250] with _find_first_string().
- record_time offset 0x28 was wrong (that location holds "(L)", a unit
label string). Disabled for now to avoid returning garbage; correct
offset will be determined from _cfg_diagnostic() output.
- Added _cfg_diagnostic(): logs all strings and all plausible float32/uint16
values across the full cfg so record_time and sample_rate offsets can be
pinpointed from a single device run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reverse-engineered the full Blastware 4-frame sequence for SUB 1A:
A (probe): offset=0x0000, params[7]=0x64
B (data req 1): offset=0x0400, params[2]=0x00, params[7]=0x64 → bytes 0..1023
C (data req 2): offset=0x0400, params[2]=0x04, params[7]=0x64 → bytes 1024..2047
D (data req 3): offset=0x002A, params[2]=0x08, params[7]=0x64 → bytes 2048..2089
We were only sending A+D and getting 44 bytes (the last chunk).
Now sends B, C, D in sequence; each E5 response has 11-byte echo header
stripped, and chunks are concatenated. Devices that return all data in
one frame (BE18189 style) are handled — timeouts on B/C are skipped
gracefully and data from D still accumulates.
Total expected: 0x0400 + 0x0400 + 0x002A = 0x082A = 2090 bytes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BE11529 sends the compliance config (SUB 1A / E5 response) as a stream
of small frames (~44 bytes each) rather than one large frame like BE18189.
Changes:
- read_compliance_config(): loop calling _recv_one() until 0x082A bytes
accumulated or 2s inter-frame gap; first frame strips 11-byte echo header,
subsequent frames logged in full for structure analysis
- _recv_one(): add reset_parser=False option to preserve parser state and
_pending_frames buffer between consecutive reads from one device response;
also stash any extra frames parsed in a single TCP chunk so they are not lost
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The heuristic offsets for trigger/alarm levels were causing struct unpack
errors. These fields require detailed field mapping from actual E5 captures
to determine exact byte positions relative to channel labels.
For now, skip extraction and leave trigger_level_geo/alarm_level_geo as None.
This prevents the '500 Device error: bytes must be in range(0, 256)' error.
Once we capture an E5 response and map the exact float positions, we can
re-enable this section with correct offsets.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
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>
Confirmed 2026-04-01 against Blastware event report for BE11529 thump
event ("00:28:12 April 1, 2026", PVS 3.906 in/s).
models.py:
- Timestamp.from_waveform_record(): decode 9-byte format from 0C record
bytes[0-8]: [day][sub_code][month][year:2BE][?][hour][min][sec]
- Timestamp: add hour/minute/second optional fields; __str__ includes
time when available
- PeakValues: add peak_vector_sum field (confirmed fixed offset 87)
client.py:
- _decode_waveform_record_into: add timestamp decode from bytes[0:9]
- _extract_record_type: decode byte[1] (sub_code), not ASCII string
search; 0x10 → "Waveform", histogram TBD
- _extract_peak_floats: add PVS from offset 87 (IEEE 754 BE float32)
= √(T²+V²+L²) at max instantaneous vector moment
sfm/server.py:
- _serialise_timestamp: add hour/minute/second/day fields to JSON
- _serialise_peak_values: add peak_vector_sum to JSON
docs: update §7.7.5 and §8 with confirmed 9-byte timestamp layout,
PVS field, and byte[1] record type encoding; update command table;
close resolved open questions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _decode_serial_number: read from data[11:] not data[:8] — was returning
the LENGTH_ECHO byte (0x0A = '\n') instead of the serial string
- _extract_peak_floats: search for channel label strings ("Tran" etc) and
read float at label+6; old step-4 aligned scan was reading trigger levels
instead of PPV values
- get_events: add debug=False param; stashes raw 210-byte record on
Event._raw_record when True for field-layout inspection
- server /device/events: add ?debug=true query param; includes
raw_record_hex + raw_record_len in response when set
- models: add Event._raw_record optional bytes field
- Establish v0.5.0 as first versioned release
- README rewritten to reflect current scope: Blastware replacement in
progress, not just a reverse-engineering capture tool
- Documents all current components: seismo_lab.py, minimateplus,
sfm/server.py, Console tab, TCP/cellular transport
- Adds ACEmanager required settings table (Quiet Mode etc.)
- Adds roadmap section
- CHANGELOG.md created with entries from v0.1.0 through v0.5.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
seismo_lab.py:
- Add ConsolePanel — third tab for direct device connections over serial
or TCP without the bridge subprocess
- Commands: POLL, Serial #, Full Config, Event Index (open/close per cmd)
- Colour-coded output: TX blue, RX raw teal, parsed green, errors red
- Save Log and Send to Analyzer buttons; auto-saves to bridges/captures/
- Queue/after(100) pattern — no performance impact
- Add SCRIPT_DIR to sys.path so minimateplus imports work from GUI
docs/instantel_protocol_reference.md:
- Confirm calibration year field at SUB FE payload offset 0x56–0x57
(uint16 BE): 0x07E7=2023 (BE18189), 0x07E9=2025 (BE11529)
- Document full Sierra Wireless RV50/RV55 required ACEmanager settings
(Quiet Mode, Data Forwarding Timeout, TCP Connect Response Delay, etc.)
- Correct §14.2: RV50/RV55 sends RING/CONNECT over TCP to caller even
with Quiet Mode on; parser handles by scanning for DLE+STX
- Confirm "Operating System" boot string capture via cold-start Console
- Resolve open question: 0x07E7 field = calibration year
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bridges/tcp_serial_bridge.py: increase default boot_delay 2s → 8s to
cover MiniMate Plus cold-start time (unit wakes from RS-232 line
assertion but takes 5-10s to be ready for POLL_PROBE).
- sfm/server.py: add _run_with_retry() — on TCP connections only, retries
once on ProtocolError. Serial timeouts are not retried (usually a real
fault). Confirmed behaviour: unit wakes purely from RS-232 line voltage,
no software wake-up frame needed.
- minimateplus/transport.py: add TcpTransport — stdlib socket-based transport
with same interface as SerialTransport. Overrides read_until_idle() with
idle_gap=1.5s to absorb the modem's 1-second serial data forwarding buffer.
- minimateplus/client.py: make `port` param optional (default "") so
MiniMateClient works cleanly when a pre-built transport is injected.
- minimateplus/__init__.py: export SerialTransport and TcpTransport.
- sfm/server.py: add `host` / `tcp_port` query params to all device endpoints.
New _build_client() helper selects TCP or serial transport automatically.
OSError (connection refused, timeout) now returns HTTP 502.
- docs/instantel_protocol_reference.md: add changelog entry and full §14
(TCP/Modem Transport) documenting confirmed transparent passthrough, no ENQ
on connect, modem forwarding delay, call-up vs ACH modes, and hardware note
deprecating Raven X in favour of RV55/RX55.
Usage: GET /device/info?host=<modem_ip>&tcp_port=12345
- Implement functionality to read and parse raw_s3.bin and raw_bw.bin files.
- Define protocol constants and mappings for various command and response identifiers.
- Create data structures for frames, sessions, and diffs to facilitate analysis.
- Develop functions for annotating frames, splitting sessions, and generating reports.
- Include live mode for continuous monitoring and reporting of protocol frames.
- Add command-line interface for user interaction and configuration.