feat: implement raw ADC waveform decoding and download functionality

- Added `_decode_a5_waveform()` to parse SUB 5A frames into per-channel time-series data.
- Introduced `download_waveform(event)` method in `MiniMateClient` to fetch full waveform data.
- Updated `Event` model to include new fields: `total_samples`, `pretrig_samples`, `rectime_seconds`, and `_waveform_key`.
- Enhanced documentation in `CHANGELOG.md` and `instantel_protocol_reference.md` to reflect new features and confirmed protocol details.
This commit is contained in:
Brian Harrison
2026-04-03 13:53:09 -04:00
parent 5d0f0855f2
commit 790e442a7a
5 changed files with 671 additions and 9 deletions

View File

@@ -4,6 +4,50 @@ All notable changes to seismo-relay are documented here.
---
## v0.7.0 — 2026-04-03
### Added
- **Raw ADC waveform decode — `_decode_a5_waveform(frames_data, event)`** in `client.py`.
Parses the complete set of SUB 5A A5 response frames into per-channel time-series:
- Reads the STRT record from A5[0] (bytes 7+): extracts `total_samples` (BE uint16 at +8),
`pretrig_samples` (BE uint16 at +16), and `rectime_seconds` (uint8 at +18) into
`event.total_samples / pretrig_samples / rectime_seconds`.
- Skips the 6-byte preamble (`00 00 ff ff ff ff`) that follows the 21-byte STRT header;
waveform data begins at `strt_pos + 27`.
- Strips the 8-byte per-frame counter header from A5[16, 8] before appending waveform bytes.
- Skips A5[7] (metadata-only) and A5[9] (terminator).
- **Cross-frame alignment correction**: accumulates `running_offset % 8` across all frames
and discards `(8 align) % 8` leading bytes per frame to re-align to a T/V/L/M boundary.
Required because individual frame waveform payloads are not always multiples of 8 bytes.
- Decodes as 4-channel interleaved signed 16-bit LE at 8 bytes per sample-set:
bytes 01 = Tran, 23 = Vert, 45 = Long, 67 = Mic.
- Stores result in `event.raw_samples = {"Tran": [...], "Vert": [...], "Long": [...], "Mic": [...]}`.
- **`download_waveform(event)` public method** on `MiniMateClient`.
Issues a full SUB 5A stream with `stop_after_metadata=False`, then calls
`_decode_a5_waveform()` to populate `event.raw_samples` and `event.total_samples /
pretrig_samples / rectime_seconds`. Previously only metadata frames were fetched during
`get_events()`; raw waveform data is now available on demand.
- **`Event` model new fields** (`models.py`): `total_samples`, `pretrig_samples`,
`rectime_seconds` (from STRT record), and `_waveform_key` (4-byte key stored during
`get_events()` for later use by `download_waveform()`).
### Protocol / Documentation
- **SUB 5A A5[0] STRT record layout confirmed** (✅ 2026-04-03, 4-2-26 blast capture):
- STRT header is 21 bytes: `b"STRT"` + length fields + `total_samples` (BE uint16 at +8) +
`pretrig_samples` (BE uint16 at +16) + `rectime_seconds` (uint8 at +18).
- Followed by 6-byte preamble: `00 00 ff ff ff ff`. Waveform begins at `strt_pos + 27`.
- Confirmed: 4-2-26 blast → `total_samples=9306`, `pretrig_samples=298`, `rectime_seconds=70`.
- **Blast/waveform mode A5 format confirmed** (✅ 2026-04-03, 4-2-26 blast capture):
4-channel interleaved int16 LE at 8 bytes per sample-set; cross-frame alignment correction
required. 948 of 9306 total sample-sets captured via `stop_after_metadata=True` (10 frames).
- **Noise/histogram mode A5 format — endianness corrected** (✅ 2026-04-03, 3-31-26 capture):
32-byte block samples are signed 16-bit **little-endian** (previously documented as BE).
`0a 00` → LE int16 = 10 (correct noise floor); BE would give 2560 (wrong).
- Protocol reference §7.6 rewritten — split into §7.6.1 (Blast/Waveform mode) and §7.6.2
(Noise/Histogram mode), each with confirmed field layouts and open questions noted.
---
## v0.6.0 — 2026-04-02
### Added