start_utc / end_utc were HH:MM:SS.s only. A probe runs for days, so a
filter or a --summary by hour mixed the same hour of different days: on
2131 an on-air A/B test picked up yesterday's announcement at the same
time and looked like two TG 213 streams reaching the hotspot at once.
- Rows now carry YYYY-MM-DDTHH:MM:SS.s (UTC).
- --summary groups by date and hour (YYYY-MM-DDTHH); rows written before
this change (no date) still group by HH.
- Tests: a row carries its date; the summary keeps the same hour of two
days apart and still reads old rows. Both fail without the change.
- Docs (en/es) give the format.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnjAx7G27d9q88j9PFLj11
Logs in to a MASTER like a hotspot (RPTL/RPTK/RPTC/RPTO), subscribes to
talkgroups through OPTIONS and never transmits. One CSV row per group
voice stream: start/end UTC, slot, TG, source, stream ID, frames, the
longest silence between two frames, and whether it ended with a
terminator. --summary prints a per-hour/TG table.
Matched by stream ID with the server's *CALL END* (Loss / Max gap), it
tells whether a cut or silence was already in the call when it came in
over the mesh or appeared on the way to the hotspot. On 2131 it is what
found the bridge reset (#123) and the mesh announcement hold (#124).
- Standard library only; password from ADN_PROBE_PASSWORD, never argv.
- A fresh socket per login attempt and a growing back-off, so a server
restart or a refused login cannot turn it into a login flood.
- Tests: the login against a fake master that checks the key with the
server's own _calc_hash, a refused password, stream rows with their
longest silence, private calls ignored.
- Docs (en/es): Monitoring and reports, "What reaches a hotspot".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JnjAx7G27d9q88j9PFLj11