Skip to content

Add network ADS-B adapter runner + fusion wiring (M3.2b)#15

Merged
kninetimmy merged 1 commit into
mainfrom
m3.2b-network-adsb-runner
Jun 18, 2026
Merged

Add network ADS-B adapter runner + fusion wiring (M3.2b)#15
kninetimmy merged 1 commit into
mainfrom
m3.2b-network-adsb-runner

Conversation

@kninetimmy

Copy link
Copy Markdown
Owner

Summary

Wires the M3.2a network ADS-B provider into a running adapter and fuses its records with local ADS-B — closing the M3 exit criterion (PRD §32): an aircraft seen by both the local radio and an Internet feed appears as one fused track with correct provenance.

  • network_adsb.py — tiled AOI poll loop (tile_region + provider.fetch_region), polite inter-tile rate limit, cross-tile dedupe_observations, network TrackRecords with local_rf=False under the shared aircraft:icao:<hex> identity. Per-tile failure is isolated (PartialSweep → degraded); a whole-sweep failure yields degraded + jittered backoff so last-good tracks age out via fusion freshness. build_provider selects adsb.fi (live) or the fake feeder.
  • network_adsb_fake_feeder.py — in-process FakeAircraftProvider for the no-hardware path; roster overlaps local identities so fusion is demonstrable end to end.
  • config.pyAETHER_NETWORK_ADSB toggle + provider / AOI center / radius / poll / rate-limit / timeout. AOI center defaults to null-island 0,0no station coordinates in the repo; the operator supplies their own.
  • main.pyrun_network_adsb wired into the lifespan behind cfg.network_adsb, mirroring run_local_adsb.
  • README — documented the no-hardware network + fusion demo.

Test plan

  • +10 tests (182 → 192), ruff + mypy strict clean.
  • 8 unit: tiled sweep, cross-tile dedupe, per-tile + whole-sweep failure isolation, provider selection, fake roster.
  • 2 integration (against a real broker): local + network a1b2c3 fuse into one track (locally_received: true, two provenance entries local_rf True+False, fusion.fused_count == 2, exactly one /api/state entry); network-only cafe01 stays a single network track.

🤖 Generated with Claude Code

Wire the M3.2a network ADS-B provider into a running adapter and fuse its
records with local ADS-B — the M3 exit criterion (PRD §32): an aircraft seen
by both the local radio and an Internet feed appears as one fused track with
correct provenance.

- network_adsb.py: tiled AOI poll loop (tile_region + provider.fetch_region),
  polite inter-tile rate limit, cross-tile dedupe_observations, network
  TrackRecords with local_rf=False under the shared aircraft:icao:<hex> identity.
  Per-tile failure isolated (PartialSweep -> degraded); whole-sweep failure
  yields degraded + jittered backoff so last good tracks age out via fusion
  freshness. build_provider selects adsb.fi (live) or the fake feeder.
- network_adsb_fake_feeder.py: in-process FakeAircraftProvider for the
  no-hardware path; roster overlaps local identities so fusion is demonstrable.
- config.py: AETHER_NETWORK_ADSB toggle + provider / AOI center / radius / poll /
  rate-limit / timeout. AOI center defaults to null-island 0,0 — no station
  coordinates in the repo; the operator supplies their own.
- main.py: run_network_adsb wired into the lifespan behind cfg.network_adsb.
- Tests: 8 unit (tiled sweep, dedupe, per-tile + whole-sweep failure isolation,
  provider selection, fake roster) + 2 integration (local+network fuse into one
  track; network-only stays a network track), green against a real broker.
- README: documented the no-hardware network + fusion demo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kninetimmy kninetimmy merged commit 1ed6803 into main Jun 18, 2026
5 checks passed
@kninetimmy kninetimmy deleted the m3.2b-network-adsb-runner branch June 18, 2026 01:15

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a network ADS-B adapter and an in-process fake provider to support Internet fusion of aircraft tracks without requiring live hardware or APIs. It integrates this adapter into the backend lifespan and configuration, updates the README, and adds integration and unit tests. The review feedback suggests tracking last_record_at cumulatively across sweeps, logging a warning when the live provider is enabled with default Null Island coordinates, and enforcing minimum politeness limits for the live production feed to prevent aggressive polling.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +166 to +167
received = 0
backoff = INITIAL_BACKOFF_S

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Initialize last_record_at outside the while True: loop so that it is tracked cumulatively across sweeps, matching the behavior of received. Currently, it is reset to None on every sweep, which causes the status record to report last_record_at=None whenever a sweep has zero visible aircraft, even if records were successfully received in previous sweeps.

    received = 0
    last_record_at: datetime | None = None
    backoff = INITIAL_BACKOFF_S

Comment on lines +198 to +199
deduped = dedupe_observations(observations)
last_record_at: datetime | None = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove the local re-initialization of last_record_at so that the cumulative variable initialized outside the loop is used instead.

Suggested change
deduped = dedupe_observations(observations)
last_record_at: datetime | None = None
deduped = dedupe_observations(observations)

Comment on lines +248 to +249
prov = provider if provider is not None else build_provider(cfg)
aoi = aoi_from_settings(cfg)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Log a warning if the network ADS-B adapter is enabled with a live provider but the AOI center is left at the default Null Island (0.0, 0.0). This is almost certainly a configuration mistake by the operator, and logging a warning helps them troubleshoot why no aircraft are appearing on the map.

Suggested change
prov = provider if provider is not None else build_provider(cfg)
aoi = aoi_from_settings(cfg)
prov = provider if provider is not None else build_provider(cfg)
aoi = aoi_from_settings(cfg)
if aoi.center_lat == 0.0 and aoi.center_lon == 0.0 and prov.name != "fake":
log.warning(
"Network ADS-B is enabled but AOI center is at Null Island (0.0, 0.0). "
"Please configure AETHER_NETWORK_ADSB_LAT and AETHER_NETWORK_ADSB_LON."
)

Comment on lines +250 to +251
resolved_poll = poll_s if poll_s is not None else cfg.network_adsb_poll_s
resolved_rate = rate_limit_s if rate_limit_s is not None else cfg.network_adsb_rate_limit_s

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Enforce minimum politeness limits for the live production feed (adsb.fi) to prevent accidental aggressive polling or DDoS due to misconfiguration.

    resolved_poll = poll_s if poll_s is not None else cfg.network_adsb_poll_s
    resolved_rate = rate_limit_s if rate_limit_s is not None else cfg.network_adsb_rate_limit_s
    if prov.name == "adsb.fi":
        # Enforce minimum politeness limits for the live production feed
        resolved_poll = max(resolved_poll, 5.0)
        resolved_rate = max(resolved_rate, 1.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant