From 66218b0a63d94a46a8a47c13e5ee2920816e2b42 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 26 May 2026 23:31:40 -0700 Subject: [PATCH 1/6] see if fix --- opendbc/car/nissan/carcontroller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opendbc/car/nissan/carcontroller.py b/opendbc/car/nissan/carcontroller.py index 16f990a8241..8e97dc9078c 100644 --- a/opendbc/car/nissan/carcontroller.py +++ b/opendbc/car/nissan/carcontroller.py @@ -39,8 +39,7 @@ def update(self, CC, CS, now_nanos): else: # Scale max torque based on how much torque the driver is applying to the wheel lkas_max_torque = max( - # Scale max torque down to half LKAX_MAX_TORQUE as a minimum - CarControllerParams.LKAS_MAX_TORQUE * 0.5, + 0.2, # Start scaling torque at STEER_THRESHOLD CarControllerParams.LKAS_MAX_TORQUE - 0.6 * max(0, abs(CS.out.steeringTorque) - CarControllerParams.STEER_THRESHOLD) ) From 2e61d40c8c7ae4e05b015e4a072d52f2b849abfd Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 27 May 2026 00:03:43 -0700 Subject: [PATCH 2/6] add ego accel --- opendbc/dbc/generator/nissan/_nissan_common.dbc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opendbc/dbc/generator/nissan/_nissan_common.dbc b/opendbc/dbc/generator/nissan/_nissan_common.dbc index a2ddd30905b..49e65a05a18 100644 --- a/opendbc/dbc/generator/nissan/_nissan_common.dbc +++ b/opendbc/dbc/generator/nissan/_nissan_common.dbc @@ -26,6 +26,9 @@ BO_ 645 WHEEL_SPEEDS_REAR: 8 XXX SG_ WHEEL_SPEED_RR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX SG_ WHEEL_SPEED_RL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX +BO_ 658 ACCEL: 8 XXX + SG_ EGO_ACCEL : 7|16@0+ (0.0005,-16.5) [-16.5|16.27] "m/s^2" XXX + BO_ 689 PROPILOT_HUD: 8 XXX SG_ LARGE_WARNING_FLASHING : 9|1@0+ (1,0) [0|1] "" XXX SG_ SIDE_RADAR_ERROR_FLASHING1 : 10|1@0+ (1,0) [0|1] "" XXX From 3fcf9f1e3e4b50af862ebdf4afcde470c47d6367 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 27 May 2026 00:42:19 -0700 Subject: [PATCH 3/6] add ego accel --- opendbc/car/car.capnp | 1 + opendbc/car/nissan/carcontroller.py | 4 +++ opendbc/car/nissan/carstate.py | 10 +++++--- opendbc/car/nissan/nissancan.py | 38 +++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/opendbc/car/car.capnp b/opendbc/car/car.capnp index d2b8d758a7c..d8ed3232ca8 100644 --- a/opendbc/car/car.capnp +++ b/opendbc/car/car.capnp @@ -358,6 +358,7 @@ struct CarControl { cruiseControl @4 :CruiseControl; hudControl @5 :HUDControl; + forceDecel @18 :Bool; struct Actuators { # lateral commands, mutually exclusive diff --git a/opendbc/car/nissan/carcontroller.py b/opendbc/car/nissan/carcontroller.py index 8e97dc9078c..1436a1c635c 100644 --- a/opendbc/car/nissan/carcontroller.py +++ b/opendbc/car/nissan/carcontroller.py @@ -57,6 +57,10 @@ def update(self, CC, CS, now_nanos): can_sends.append(nissancan.create_steering_control( self.packer, self.apply_angle_last, self.frame, CC.latActive, lkas_max_torque)) + # Use stock driver attentiveness warning when forcing a deceleration + for steer_torque_sensor_msg in CS.steer_torque_sensor_msgs: + can_sends.append(nissancan.create_steer_torque_sensor(self.packer, steer_torque_sensor_msg, CC.forceDecel)) + # Below are the HUD messages. We copy the stock message and modify if self.CP.carFingerprint != CAR.NISSAN_ALTIMA: if self.frame % 2 == 0: diff --git a/opendbc/car/nissan/carstate.py b/opendbc/car/nissan/carstate.py index 1bd5ad178b4..fe826d3dd04 100644 --- a/opendbc/car/nissan/carstate.py +++ b/opendbc/car/nissan/carstate.py @@ -18,6 +18,7 @@ def __init__(self, CP): self.lkas_hud_msg = {} self.lkas_hud_info_msg = {} + self.steer_torque_sensor_msgs = [{}] self.steeringTorqueSamples = deque(TORQUE_SAMPLES*[0], TORQUE_SAMPLES) self.shifter_values = can_define.dv["GEARBOX"]["GEAR_SHIFTER"] @@ -91,9 +92,11 @@ def update(self, can_parsers) -> structs.CarState: if self.CP.carFingerprint == CAR.NISSAN_ALTIMA: ret.steeringTorque = cp_cam.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"] ret.steerFaultTemporary = cp_cam.vl["STEER_TORQUE_SENSOR"]["LKAS_STATUS"] == 9 + self.steer_torque_sensor_msgs = cp_cam.vl_all["STEER_TORQUE_SENSOR"] else: ret.steeringTorque = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"] ret.steerFaultTemporary = cp.vl["STEER_TORQUE_SENSOR"]["LKAS_STATUS"] == 9 + self.steer_torque_sensor_msgs = cp.vl_all["STEER_TORQUE_SENSOR"] self.steeringTorqueSamples.append(ret.steeringTorque) # Filtering driver torque to prevent steeringPressed false positives @@ -114,12 +117,11 @@ def update(self, can_parsers) -> structs.CarState: can_gear = int(cp.vl["GEARBOX"]["GEAR_SHIFTER"]) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) - # stock lkas should be off - # TODO: is this needed? + # stock lkas should be on to allow stock driver monitoring system to progress when desired if self.CP.carFingerprint == CAR.NISSAN_ALTIMA: - ret.invalidLkasSetting = bool(cp.vl["LKAS_SETTINGS"]["LKAS_ENABLED"]) + ret.invalidLkasSetting = not bool(cp.vl["LKAS_SETTINGS"]["LKAS_ENABLED"]) else: - ret.invalidLkasSetting = bool(cp_adas.vl["LKAS_SETTINGS"]["LKAS_ENABLED"]) + ret.invalidLkasSetting = not bool(cp_adas.vl["LKAS_SETTINGS"]["LKAS_ENABLED"]) self.cruise_throttle_msg = copy.copy(cp.vl["CRUISE_THROTTLE"]) diff --git a/opendbc/car/nissan/nissancan.py b/opendbc/car/nissan/nissancan.py index 24a49e02714..dc058c0caf9 100644 --- a/opendbc/car/nissan/nissancan.py +++ b/opendbc/car/nissan/nissancan.py @@ -5,6 +5,24 @@ nissan_checksum = mk_crc8_fun(CRC8J1850, init_crc=0x00, xor_out=0xFF) +def create_steer_torque_spoof(packer, bus, steer_torque_msg, driver_torque): + # Re-emit EPS's STEER_TORQUE_SENSOR on the camera-side bus with a spoofed + # STEER_TORQUE_DRIVER to satisfy ProPilot's hands-on check. The real message + # from bus 0 must be blocked in safety so only this one reaches the ADAS ECU. + values = {s: steer_torque_msg[s] for s in [ + "LKAS_ACTIVE", + "LKAS_STATUS", + "STEER_TORQUE_LKAS", + "STEER_ANGLE", + "COUNTER", + ]} + values["STEER_TORQUE_DRIVER"] = driver_torque + + dat = packer.make_can_msg("STEER_TORQUE_SENSOR", bus, values)[1] + values["CHECKSUM"] = nissan_checksum(dat[:7]) + return packer.make_can_msg("STEER_TORQUE_SENSOR", bus, values) + + def create_steering_control(packer, apply_torque, frame, steer_on, lkas_max_torque): values = { "COUNTER": frame % 0x10, @@ -21,6 +39,26 @@ def create_steering_control(packer, apply_torque, frame, steer_on, lkas_max_torq return packer.make_can_msg("LKAS", 0, values) +def create_steer_torque_sensor(packer, steer_torque_sensor_msg: dict, force_decel: bool): + values = {s: steer_torque_sensor_msg[s] for s in [ + "STEER_TORQUE_DRIVER", + "STEER_ANGLE", + "LKAS_ACTIVE", + "STEER_TORQUE_LKAS", + "COUNTER", + "LKAS_STATUS", + "CHECKSUM", + ]} + + # Starts stock driver monitoring progression by setting driver torque to 0 + if force_decel: + values["STEER_TORQUE_DRIVER"] = 0.0 + + dat = packer.make_can_msg("STEER_TORQUE_SENSOR", 2, values)[1] + values["CHECKSUM"] = nissan_checksum(dat[:7]) + return packer.make_can_msg("STEER_TORQUE_SENSOR", 2, values) + + def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg): values = {s: cruise_throttle_msg[s] for s in [ "COUNTER", From 3b40af99711f099c8aeec1ba747896e841b5c078 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 27 May 2026 00:50:38 -0700 Subject: [PATCH 4/6] add stock dm --- opendbc/car/nissan/carcontroller.py | 2 +- opendbc/car/nissan/nissancan.py | 6 +++++- opendbc/safety/modes/nissan.h | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/opendbc/car/nissan/carcontroller.py b/opendbc/car/nissan/carcontroller.py index 1436a1c635c..ac73d3d1fc4 100644 --- a/opendbc/car/nissan/carcontroller.py +++ b/opendbc/car/nissan/carcontroller.py @@ -59,7 +59,7 @@ def update(self, CC, CS, now_nanos): # Use stock driver attentiveness warning when forcing a deceleration for steer_torque_sensor_msg in CS.steer_torque_sensor_msgs: - can_sends.append(nissancan.create_steer_torque_sensor(self.packer, steer_torque_sensor_msg, CC.forceDecel)) + can_sends.append(nissancan.create_steer_torque_sensor(self.packer, steer_torque_sensor_msg, CC.latActive, CC.forceDecel)) # Below are the HUD messages. We copy the stock message and modify if self.CP.carFingerprint != CAR.NISSAN_ALTIMA: diff --git a/opendbc/car/nissan/nissancan.py b/opendbc/car/nissan/nissancan.py index dc058c0caf9..1694f322727 100644 --- a/opendbc/car/nissan/nissancan.py +++ b/opendbc/car/nissan/nissancan.py @@ -39,7 +39,7 @@ def create_steering_control(packer, apply_torque, frame, steer_on, lkas_max_torq return packer.make_can_msg("LKAS", 0, values) -def create_steer_torque_sensor(packer, steer_torque_sensor_msg: dict, force_decel: bool): +def create_steer_torque_sensor(packer, steer_torque_sensor_msg: dict, lat_active: bool, force_decel: bool): values = {s: steer_torque_sensor_msg[s] for s in [ "STEER_TORQUE_DRIVER", "STEER_ANGLE", @@ -53,8 +53,12 @@ def create_steer_torque_sensor(packer, steer_torque_sensor_msg: dict, force_dece # Starts stock driver monitoring progression by setting driver torque to 0 if force_decel: values["STEER_TORQUE_DRIVER"] = 0.0 + elif lat_active: + # When steering normally we need to silence stock DM system + values["STEER_TORQUE_DRIVER"] = 1.0 dat = packer.make_can_msg("STEER_TORQUE_SENSOR", 2, values)[1] + values["CHECKSUM"] = nissan_checksum(dat[:7]) return packer.make_can_msg("STEER_TORQUE_SENSOR", 2, values) diff --git a/opendbc/safety/modes/nissan.h b/opendbc/safety/modes/nissan.h index 9289935f80c..8cd0851a531 100644 --- a/opendbc/safety/modes/nissan.h +++ b/opendbc/safety/modes/nissan.h @@ -104,7 +104,8 @@ static safety_config nissan_init(uint16_t param) { {0x4cc, 0, 8, .check_relay = true}, // PROPILOT_HUD_INFO_MSG {0x20b, 2, 6, .check_relay = false}, // CRUISE_THROTTLE (X-Trail) {0x20b, 1, 6, .check_relay = false}, // CRUISE_THROTTLE (Altima) - {0x280, 2, 8, .check_relay = true} // CANCEL_MSG (Leaf) + {0x280, 2, 8, .check_relay = true}, // CANCEL_MSG (Leaf) + {0x185, 2, 8, .check_relay = true}, // STEER_TORQUE_SENSOR }; // Signals duplicated below due to the fact that these messages can come in on either CAN bus, depending on car model. From 10ce7cb32c0a9168f70e4d19f554f1c1b24336b5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 27 May 2026 17:27:08 -0700 Subject: [PATCH 5/6] fix --- opendbc/car/nissan/carstate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opendbc/car/nissan/carstate.py b/opendbc/car/nissan/carstate.py index fe826d3dd04..6f06cf356bb 100644 --- a/opendbc/car/nissan/carstate.py +++ b/opendbc/car/nissan/carstate.py @@ -92,7 +92,9 @@ def update(self, can_parsers) -> structs.CarState: if self.CP.carFingerprint == CAR.NISSAN_ALTIMA: ret.steeringTorque = cp_cam.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"] ret.steerFaultTemporary = cp_cam.vl["STEER_TORQUE_SENSOR"]["LKAS_STATUS"] == 9 - self.steer_torque_sensor_msgs = cp_cam.vl_all["STEER_TORQUE_SENSOR"] + # self.steer_torque_sensor_msgs = cp_cam.vl_all["STEER_TORQUE_SENSOR"] + adas_status_msgs = cp_cam.vl_all["STEER_TORQUE_SENSOR"] + self.steer_torque_sensor_msgs = [dict(zip(adas_status_msgs, vals, strict=True)) for vals in zip(*adas_status_msgs.values(), strict=True)] else: ret.steeringTorque = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"] ret.steerFaultTemporary = cp.vl["STEER_TORQUE_SENSOR"]["LKAS_STATUS"] == 9 From 4ec9dfd349aaecbb5848f13a6acc97d46f9756c2 Mon Sep 17 00:00:00 2001 From: elkoled Date: Thu, 28 May 2026 21:30:00 -0700 Subject: [PATCH 6/6] car_diff: dedupe errors and render them in a collapsible dropdown A single failing carcontroller raised the same traceback for every segment, printing dozens of identical tracebacks at the top of the report. Group errors by their text, print each unique one once with the affected segment count, and wrap the whole block in a details dropdown like the diff section so it does not spam the PR comment. Co-Authored-By: Claude Opus 4.8 (1M context) --- opendbc/car/tests/car_diff.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/opendbc/car/tests/car_diff.py b/opendbc/car/tests/car_diff.py index b4424be8975..6bfe4914a78 100755 --- a/opendbc/car/tests/car_diff.py +++ b/opendbc/car/tests/car_diff.py @@ -293,8 +293,16 @@ def main(platform: str | None = None, segments_per_platform: int = 10, update_re icon = "⚠️" if with_diffs else "✅" print(f"\n{icon} {len(with_diffs)} changed, {n_passed} passed, {len(errors)} errors") - for plat, seg, err in errors: - print(f"\nERROR {plat} - {seg}: {err}") + if errors: + # Group identical errors so a single bug doesn't spam the report with one traceback per segment + by_err: dict[str, list[str]] = defaultdict(list) + for plat, seg, err in errors: + by_err[err].append(f"{plat} - {seg}") + print("
Show errors\n\n```") + for err, segs in sorted(by_err.items(), key=lambda kv: -len(kv[1])): + affected = ", ".join(segs[:3]) + (f" (+{len(segs) - 3} more)" if len(segs) > 3 else "") + print(f"\nERROR ({len(segs)}x) {affected}:\n{err.rstrip()}") + print("```\n
") if with_diffs: print("
Show changes\n\n```")