From 2ee9ef93049c868e22acde6969be221db30f6f52 Mon Sep 17 00:00:00 2001 From: ghbarker <117389362+ghbarker@users.noreply.github.com> Date: Wed, 29 Jul 2026 23:21:12 -0400 Subject: [PATCH] ford: show "Radar Not Detected" when the radar bus is silent 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. --- cereal/log.capnp | 1 + opendbc_repo/opendbc/car/car.capnp | 1 + opendbc_repo/opendbc/car/ford/radar_interface.py | 11 +++++++++++ selfdrive/selfdrived/events.py | 5 +++++ selfdrive/selfdrived/selfdrived.py | 2 ++ 5 files changed, 20 insertions(+) diff --git a/cereal/log.capnp b/cereal/log.capnp index f39d9441e2..b20ea8d90d 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -134,6 +134,7 @@ struct OnroadEvent @0xc4fa6047f024e718 { audioFeedback @97; soundsUnavailableDEPRECATED @47; + radarIsNotDetected @100; } } diff --git a/opendbc_repo/opendbc/car/car.capnp b/opendbc_repo/opendbc/car/car.capnp index 8d6b75d3c9..baef94acfe 100644 --- a/opendbc_repo/opendbc/car/car.capnp +++ b/opendbc_repo/opendbc/car/car.capnp @@ -305,6 +305,7 @@ struct RadarData @0x888ad6581cf0aacb { radarFault @1 :Bool; wrongConfig @2 :Bool; radarUnavailableTemporary @3 :Bool; # radar data is temporarily unavailable due to conditions the car sets + radarUnavailablePermanent @4 :Bool; # radar data is permanently unavailable due to not having ACC } # similar to LiveTracks diff --git a/opendbc_repo/opendbc/car/ford/radar_interface.py b/opendbc_repo/opendbc/car/ford/radar_interface.py index 4a9aa2188c..2ef043f489 100644 --- a/opendbc_repo/opendbc/car/ford/radar_interface.py +++ b/opendbc_repo/opendbc/car/ford/radar_interface.py @@ -25,6 +25,7 @@ DELPHI_MRR_CLUSTER_THRESHOLD = 5 # meters, lateral distance and relative velocity are weighted STEER_ASSIST_DATA_MSGS = 0x3d7 +RADAR_ABSENT_ = 500 #Used to detect if the radar is absent for 5s and then set the radarUnavailablePermanent flag to True @dataclass class Cluster: @@ -123,6 +124,8 @@ def __init__(self, CP, CP_SP): self.radar = DBC[CP.carFingerprint].get(Bus.radar) self.scan_index_invalid_cnt = 0 self.radar_unavailable_cnt = 0 + self.radar_perm_cnt = 0 + self.radar_working = False self.prev_headerScanIndex = 0 if CP.radarUnavailable: self.rcp = None @@ -151,8 +154,16 @@ def update(self, can_strings): self.updated_messages.update(vls) if self.trigger_msg not in self.updated_messages: + if not self.radar_working: + self.radar_perm_cnt += 1 + if self.radar_perm_cnt >= RADAR_ABSENT_: + ret = structs.RadarData() + ret.errors.radarUnavailablePermanent = True + return ret return None self.updated_messages.clear() + self.radar_working = True + self.radar_perm_cnt = 0 ret = structs.RadarData() if not self.rcp.can_valid: diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index c9b281436c..64478f33eb 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -706,6 +706,11 @@ def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messagin ET.NO_ENTRY: NoEntryAlert("Radar Temporarily Unavailable"), }, + EventName.radarIsNotDetected: { + ET.PERMANENT: NormalPermanentAlert("Car may not have ACC"), + ET.NO_ENTRY: NoEntryAlert("Radar Is Not Detected"), + }, + # Every frame from the camera should be processed by the model. If modeld # is not processing frames fast enough they have to be dropped. This alert is # thrown when over 20% of frames are dropped. diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 30605d0a27..202e705829 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -380,6 +380,8 @@ def update_events(self, CS): self.events.add(EventName.canError) elif self.sm['radarState'].radarErrors.radarUnavailableTemporary: self.events.add(EventName.radarTempUnavailable) + elif self.sm['radarState'].radarErrors.radarUnavailablePermanent: + self.events.add(EventName.radarIsNotDetected) elif any(self.sm['radarState'].radarErrors.to_dict().values()): self.events.add(EventName.radarFault) if not self.sm.valid['pandaStates']: