feat: add endpoint to download full raw ADC waveform for a single event
This commit is contained in:
@@ -385,6 +385,81 @@ def device_event(
|
|||||||
return _serialise_event(matching[0])
|
return _serialise_event(matching[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/device/event/{index}/waveform")
|
||||||
|
def device_event_waveform(
|
||||||
|
index: int,
|
||||||
|
port: Optional[str] = Query(None, description="Serial port (e.g. COM5)"),
|
||||||
|
baud: int = Query(38400, description="Serial baud rate"),
|
||||||
|
host: Optional[str] = Query(None, description="TCP host — modem IP or ACH relay"),
|
||||||
|
tcp_port: int = Query(DEFAULT_TCP_PORT, description=f"TCP port (default {DEFAULT_TCP_PORT})"),
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Download the full raw ADC waveform for a single event (0-based index).
|
||||||
|
|
||||||
|
Supply either *port* (serial) or *host* (TCP/modem).
|
||||||
|
|
||||||
|
Performs: POLL startup → get_events() (to locate the 4-byte waveform key) →
|
||||||
|
download_waveform() (full SUB 5A stream, stop_after_metadata=False).
|
||||||
|
|
||||||
|
Response includes:
|
||||||
|
- **total_samples**: expected sample-sets from the STRT record
|
||||||
|
- **pretrig_samples**: pre-trigger sample count
|
||||||
|
- **rectime_seconds**: record duration
|
||||||
|
- **samples_decoded**: actual sample-sets decoded (may be less than total_samples
|
||||||
|
if the device is not storing all frames yet, or the capture was partial)
|
||||||
|
- **sample_rate**: samples per second (from compliance config)
|
||||||
|
- **channels**: dict of channel name → list of signed int16 ADC counts
|
||||||
|
(keys: "Tran", "Vert", "Long", "Mic")
|
||||||
|
"""
|
||||||
|
log.info("GET /device/event/%d/waveform port=%s host=%s", index, port, host)
|
||||||
|
|
||||||
|
try:
|
||||||
|
def _do():
|
||||||
|
with _build_client(port, baud, host, tcp_port) as client:
|
||||||
|
info = client.connect()
|
||||||
|
events = client.get_events()
|
||||||
|
matching = [ev for ev in events if ev.index == index]
|
||||||
|
if not matching:
|
||||||
|
return None, None, info
|
||||||
|
ev = matching[0]
|
||||||
|
client.download_waveform(ev)
|
||||||
|
return ev, events, info
|
||||||
|
ev, events, info = _run_with_retry(_do, is_tcp=_is_tcp(host))
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except ProtocolError as exc:
|
||||||
|
raise HTTPException(status_code=502, detail=f"Protocol error: {exc}") from exc
|
||||||
|
except OSError as exc:
|
||||||
|
raise HTTPException(status_code=502, detail=f"Connection error: {exc}") from exc
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Device error: {exc}") from exc
|
||||||
|
|
||||||
|
if ev is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=404,
|
||||||
|
detail=f"Event index {index} not found on device",
|
||||||
|
)
|
||||||
|
|
||||||
|
raw = getattr(ev, "raw_samples", None) or {}
|
||||||
|
samples_decoded = len(raw.get("Tran", []))
|
||||||
|
|
||||||
|
# Resolve sample_rate from compliance config if not on the event itself
|
||||||
|
sample_rate = ev.sample_rate
|
||||||
|
if sample_rate is None and info.compliance_config:
|
||||||
|
sample_rate = info.compliance_config.sample_rate
|
||||||
|
|
||||||
|
return {
|
||||||
|
"index": ev.index,
|
||||||
|
"timestamp": _serialise_timestamp(ev.timestamp),
|
||||||
|
"total_samples": ev.total_samples,
|
||||||
|
"pretrig_samples": ev.pretrig_samples,
|
||||||
|
"rectime_seconds": ev.rectime_seconds,
|
||||||
|
"samples_decoded": samples_decoded,
|
||||||
|
"sample_rate": sample_rate,
|
||||||
|
"channels": raw,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# ── Entry point ────────────────────────────────────────────────────────────────
|
# ── Entry point ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user