Skip to content

ford: show "Radar Not Detected" when the radar bus is silent - #170

Open
ghbarker wants to merge 1 commit into
BluePilotDev:bp-devfrom
ghbarker:radar-not-detected
Open

ford: show "Radar Not Detected" when the radar bus is silent#170
ghbarker wants to merge 1 commit into
BluePilotDev:bp-devfrom
ghbarker:radar-not-detected

Conversation

@ghbarker

@ghbarker ghbarker commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

The evidence that motivated this

  • A recurring support pattern: someone installs a comma on a Ford without ACC/radar hardware. The car fingerprints fine (its platform's DBC lists a radar bus, so radarUnavailable is False), radard spins up expecting radar — and hears nothing.
  • Today that surfaces as a generic "Radar Error" or a confusing controls mismatch, and diagnosing "your car just doesn't have the module" costs hours per case.
  • The gap is real in code, not just in messaging: RadarInterface.update() returns None whenever the trigger message hasn't arrived — so a completely silent radar bus never reaches the canError / radarUnavailableTemporary paths at all. Pure absence produced no signal.

Why the fix is shaped this way

  • Detect where the bus is visible. The radar CAN only exists inside the radar interface (card process) — carstate never parses it — so the detector lives in radar_interface.py, keyed on the trigger message.
  • A grace window, counted in frames: 500 update calls at card's 100 Hz ≈ 5 s of continuous silence before declaring absence — frame-counted (not wall-clock) so replay is deterministic, and long enough that a slow-waking radar never false-trips.
  • A one-way "ever heard it" latch (radar_working): a radar that worked and then dropped out can never trip this flag — that stays radarUnavailableTemporary's job. Permanent means never seen since startup.
  • Free gating for cars that legitimately lack radar: this code is unreachable when radarUnavailable is set (the constructor leaves rcp = None), so alpha-long cars can't false-positive.
  • Reuse the whole alert pipeline: one new RadarData.Error flag (radarUnavailablePermanent @4, sibling of radarUnavailableTemporary), one new EventName.radarIsNotDetected in the live log.capnp enum, one elif in selfdrived ahead of the generic radarFault catch-all, one alert entry ("Car may not have ACC"). No new process, no new UI surface.

The evidence afterwards

  • A real Ford RadarInterface driven with a fully silent bus raises radarUnavailablePermanent at exactly frame 500 and stays quiet before the grace elapses; a radar that speaks first (even once, even late) never trips it.
  • Schema checks: the new field and event resolve through capnp against the enums the code actually reads (log.OnroadEvent.EventName, not the deprecated car.capnp copy).

What each change is

  • opendbc/car/ford/radar_interface.py: the detector — RADAR_ABSENT_ grace counter + radar_working latch in the silent branch of update().
  • opendbc/car/car.capnp: radarUnavailablePermanent @4 in RadarData.Error.
  • cereal/log.capnp: radarIsNotDetected @100 in the live OnroadEvent.EventName enum.
  • selfdrive/selfdrived/selfdrived.py: map the flag to the event, ahead of the generic catch-all.
  • selfdrive/selfdrived/events.py: the permanent "Car may not have ACC" alert + no-entry.

Test it yourself

  • Unplug/absent radar (or a bench harness with no radar node): within ~5 s of onroad the permanent banner reads the real cause instead of "Radar Error".
  • Normal car: drive as usual — the flag requires zero radar messages since startup, so any functioning radar (even a briefly glitchy one) never shows it.

Risks / limits

  • Ford-scoped detector; other brands keep today's behavior.
  • Cars that fail to fingerprint at all still show the generic unknown-vehicle path — that's a separate (fingerprinting) problem this PR deliberately doesn't touch.
  • The alert blocks engagement (no-entry) exactly as radar faults do today; it adds clarity, not new restriction.

Cars fingerprinted as a radar-equipped Ford but physically missing the
radar/ACC module currently show a generic "Radar Error" or a confusing
controls mismatch, which costs hours of support triage.

RadarInterface returns None on a fully silent bus and never raises
anything, so pure absence was invisible. This adds a small detector:
after ~5s (500 frames at card's 100 Hz) with the radar trigger message
never arriving, on a car that should have radar, it sets a new
RadarData.errors flag (radarUnavailablePermanent). A radar_working latch
means a radar that worked and then dropped out never trips it - that stays
radarUnavailableTemporary's job. selfdrived maps the flag to a new
radarIsNotDetected event: a permanent "Car may not have ACC" banner.

Tested: a real Ford RadarInterface driven with a silent bus raises the
flag at frame 500 and stays quiet before the grace period.
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