Second SDR: ADS-B (dump1090) / AIS (AIS-catcher) support #10

Open
logan wants to merge 3 commits from feat/second-sdr-adsb-ais into main
Owner

Closes node-26#9 (plumbing + ADS-B + AIS wiring; op25_2 mode and serial-based device binding remain open, see the issue).

Adds secondary-sdr-container, a new compose service that claims the node's SECOND physical SDR (RTL-SDR index 1 — op25 always claims index 0, no serial binding yet, same limitation op25 itself has). Its control API (mirroring op25_controller.py's start/stop/status shape) runs dump1090 for ADS-B or AIS-catcher for AIS, selected by the new secondary_sdr_mode node config field (none|adsb|ais|op25_2 — op25_2 not implemented yet). edge-node starts/stops/resumes it based on config and polls its /secondary/data every 10s, forwarding non-empty snapshots to the C2 server's new /telemetry/adsb and /telemetry/ais endpoints (server-26 PR, separate).

UNVERIFIED against real hardware — built from a sandboxed authoring machine with no docker/hardware access. dump1090's --write-json field names and AIS-catcher's JSON stdout field names are believed correct from published docs, not confirmed against a real capture. The AIS stdout-parsing merge logic (preserving a vessel's name across position-only updates) was unit-verified against a fake stdout stream, not real AIS-catcher output. Build + hardware smoke test before this reaches a real node.

Verified: drb-edge-node pytest 185/185, flake8 clean on both drb-edge-node/app and secondary-sdr-container/app.

Closes node-26#9 (plumbing + ADS-B + AIS wiring; op25_2 mode and serial-based device binding remain open, see the issue). Adds `secondary-sdr-container`, a new compose service that claims the node's SECOND physical SDR (RTL-SDR index 1 — op25 always claims index 0, no serial binding yet, same limitation op25 itself has). Its control API (mirroring op25_controller.py's start/stop/status shape) runs dump1090 for ADS-B or AIS-catcher for AIS, selected by the new `secondary_sdr_mode` node config field (none|adsb|ais|op25_2 — op25_2 not implemented yet). edge-node starts/stops/resumes it based on config and polls its `/secondary/data` every 10s, forwarding non-empty snapshots to the C2 server's new `/telemetry/adsb` and `/telemetry/ais` endpoints (server-26 PR, separate). **UNVERIFIED against real hardware** — built from a sandboxed authoring machine with no docker/hardware access. dump1090's `--write-json` field names and AIS-catcher's JSON stdout field names are believed correct from published docs, not confirmed against a real capture. The AIS stdout-parsing merge logic (preserving a vessel's name across position-only updates) was unit-verified against a fake stdout stream, not real AIS-catcher output. Build + hardware smoke test before this reaches a real node. Verified: drb-edge-node pytest 185/185, flake8 clean on both drb-edge-node/app and secondary-sdr-container/app.
logan added 3 commits 2026-09-20 19:11:24 -04:00
Adds a per-node secondary_sdr_mode config field (none|adsb|ais|op25_2),
applied the same way as hardware_preset/ppm_override so it survives system
reassignment. op25-container gets a GET /devices endpoint that counts
connected SDRs via lsusb; the edge-node checkin now reports sdr_count and
secondary_sdr_mode up to the server, the first node-initiated hardware
report (everything else was C2 pushing config down). Tracked as node-26#9.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
node-26#9. New secondary-sdr-container claims the node's SECOND physical
SDR (RTL-SDR index 1 — op25 always claims index 0; no serial-based binding
yet, same gap op25 itself has). Its control API (start/stop/status/data,
mirroring op25_controller.py) launches dump1090 in adsb mode and exposes
the decoded aircraft.json snapshot; AIS mode 400s until it's wired next.

edge-node: on_config_push starts/stops it when secondary_sdr_mode changes,
lifespan resumes it after a restart if already configured, and a new
telemetry_uplink_loop polls its /secondary/data every 10s and POSTs
non-empty snapshots to C2's new /telemetry/adsb (same bearer-key pattern
call_recorder.py already uses for audio upload).

UNVERIFIED: this container has not been built or run against real hardware
in this session (sandboxed authoring machine, no docker) — dump1090's
--write-json field names are believed correct from its docs but not
confirmed against a real capture. Build + hardware smoke test before this
reaches a real node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wire AIS end to end: AIS-catcher support in secondary-sdr container
CI / lint (push) Successful in 5s
CI / lint (pull_request) Successful in 5s
CI / test (push) Successful in 38s
CI / test (pull_request) Successful in 38s
52f31bbcc0
node-26#9. decoder_control.py's AIS mode now actually launches AIS-catcher
instead of 400ing: a background thread reads its stdout JSON stream
(one message per line) and keeps a live in-memory snapshot keyed by mmsi,
since AIS-catcher streams rather than writing a periodic file the way
dump1090's --write-json does. Messages are merged onto the existing entry
per mmsi rather than replacing it, since AIS-catcher emits static data
(name) and position reports (lat/lon) as separate message types — a naive
overwrite would blank the name back to null on every position-only update
(caught by a standalone unit check against a fake stdout stream before
this fix, not by pytest — this container has no test suite yet).

edge-node's telemetry_uplink_loop now posts non-empty vessel snapshots to
the new /telemetry/ais the same way it already does for aircraft.

UNVERIFIED against real hardware/binary in this session, same caveat as
the ADS-B commit — AIS-catcher's JSON field names are believed correct
from its docs, not confirmed against a real capture.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All checks were successful
CI / lint (push) Successful in 5s
CI / lint (pull_request) Successful in 5s
CI / test (push) Successful in 38s
CI / test (pull_request) Successful in 38s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/second-sdr-adsb-ais:feat/second-sdr-adsb-ais
git checkout feat/second-sdr-adsb-ais
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/node-26#10