Node: report every radio heard on a call, not just the last (server-26#304)
CI / lint (push) Successful in 8s
CI / test-op25 (push) Successful in 16s
CI / test (push) Successful in 50s
Build edge-node / build (push) Successful in 9m46s

P1 of the radio-ID plan. metadata_watcher records each transmission as
[radio, first seen, last seen] from channel rows and call_log grants in both
audio and console modes. Sightings of the same radio under 2 s apart are
merged, because the P0 probe found the ID in only 44% of polls during a
burst. call_end gains `radios: [{radio, start, end}]`.

main.py rebases the offsets into the trimmed audio file's frame, so
wall clock = audio_start_epoch + offset, the mapping C2 already uses. It
also sends the list on /upload (UploadMeta.radios), because C2 starts the
pipeline from /upload before call_end arrives.

srcaddr keeps its meaning (latest talker). Metadata only: no boundary,
timestamp or attribution change (drb-audio-review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-11-08 23:03:18 -05:00
co-authored by Claude Opus 5.5
parent c57a5b9068
commit 6330609a75
6 changed files with 172 additions and 1 deletions
@@ -71,6 +71,10 @@ class UploadMeta:
system_id: Optional[str] = None
audio_start_epoch: Optional[float] = None
audio_end_epoch: Optional[float] = None
# JSON list of {radio, start, end} — every radio heard on the call
# (server-26#304). Sent here as well as on call_end because C2 starts the
# pipeline from /upload, before call_end arrives.
radios: Optional[str] = None
@dataclass
@@ -108,6 +112,8 @@ def _form(meta: UploadMeta) -> Dict[str, str]:
form["audio_start_epoch"] = f"{meta.audio_start_epoch:.3f}"
if meta.audio_end_epoch is not None:
form["audio_end_epoch"] = f"{meta.audio_end_epoch:.3f}"
if meta.radios:
form["radios"] = meta.radios
return form