Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opendbc/car/car.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ struct CarControl {

cruiseControl @4 :CruiseControl;
hudControl @5 :HUDControl;
forceDecel @18 :Bool;

struct Actuators {
# lateral commands, mutually exclusive
Expand Down
7 changes: 5 additions & 2 deletions opendbc/car/nissan/carcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand All @@ -58,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.latActive, 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:
Expand Down
12 changes: 8 additions & 4 deletions opendbc/car/nissan/carstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -91,9 +92,13 @@ 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"]
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
self.steer_torque_sensor_msgs = cp.vl_all["STEER_TORQUE_SENSOR"]

self.steeringTorqueSamples.append(ret.steeringTorque)
# Filtering driver torque to prevent steeringPressed false positives
Expand All @@ -114,12 +119,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"])

Expand Down
42 changes: 42 additions & 0 deletions opendbc/car/nissan/nissancan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -21,6 +39,30 @@ 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, lat_active: bool, 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
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)


def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg):
values = {s: cruise_throttle_msg[s] for s in [
"COUNTER",
Expand Down
12 changes: 10 additions & 2 deletions opendbc/car/tests/car_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("<details><summary><b>Show errors</b></summary>\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</details>")

if with_diffs:
print("<details><summary><b>Show changes</b></summary>\n\n```")
Expand Down
3 changes: 3 additions & 0 deletions opendbc/dbc/generator/nissan/_nissan_common.dbc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion opendbc/safety/modes/nissan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading