IQ.Pilot Prebuilt Release @ 7a91404

This commit is contained in:
IQ.Lvbs CI [bot]
2026-08-31 23:04:09 -05:00
commit e2b219bcf7
2545 changed files with 677873 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
from iqdbc.can import CANPacker
from iqdbc.car import Bus, structs
from iqdbc.car.lateral import apply_std_steer_angle_limits
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.nissan import nissancan
from iqdbc.car.nissan.values import CAR, CarControllerParams
VisualAlert = structs.CarControl.HUDControl.VisualAlert
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP, CP_IQ):
super().__init__(dbc_names, CP, CP_IQ)
self.car_fingerprint = CP.carFingerprint
self.apply_angle_last = 0
self.packer = CANPacker(dbc_names[Bus.pt])
def update(self, CC, CC_IQ, CS, now_nanos):
actuators = CC.actuators
hud_control = CC.hudControl
pcm_cancel_cmd = CC.cruiseControl.cancel
can_sends = []
### STEER ###
steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0
# windup slower
self.apply_angle_last = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS)
lkas_max_torque = 0
if CC.latActive:
# Max torque from driver before EPS will give up and not apply torque
if not bool(CS.out.steeringPressed):
lkas_max_torque = CarControllerParams.LKAS_MAX_TORQUE
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,
# Start scaling torque at STEER_THRESHOLD
CarControllerParams.LKAS_MAX_TORQUE - 0.6 * max(0, abs(CS.out.steeringTorque) - CarControllerParams.STEER_THRESHOLD)
)
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA) and pcm_cancel_cmd:
can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg))
# TODO: Find better way to cancel!
# For some reason spamming the cancel button is unreliable on the Leaf
# We now cancel by making propilot think the seatbelt is unlatched,
# this generates a beep and a warning message every time you disengage
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC) and self.frame % 2 == 0:
can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, pcm_cancel_cmd))
can_sends.append(nissancan.create_steering_control(
self.packer, self.apply_angle_last, self.frame, CC.latActive, lkas_max_torque))
# Below are the HUD messages. We copy the stock message and modify
if self.CP.carFingerprint != CAR.NISSAN_ALTIMA:
if self.frame % 2 == 0:
can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC_IQ.aol.enabled, hud_control.leftLaneVisible,
hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart))
if self.frame % 50 == 0:
can_sends.append(nissancan.create_lkas_hud_info_msg(
self.packer, CS.lkas_hud_info_msg, steer_hud_alert
))
new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last
self.frame += 1
return new_actuators, can_sends

View File

@@ -0,0 +1,148 @@
import copy
from collections import deque
from iqdbc.can import CANDefine, CANParser
from iqdbc.car import Bus, create_button_events, structs
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.nissan.values import CAR, DBC, CarControllerParams
from iqdbc.lvbs.car.nissan.iq_carstate import IQCarState
ButtonType = structs.CarState.ButtonEvent.Type
TORQUE_SAMPLES = 12
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.lkas_hud_msg = {}
self.lkas_hud_info_msg = {}
self.steeringTorqueSamples = deque(TORQUE_SAMPLES*[0], TORQUE_SAMPLES)
self.shifter_values = can_define.dv["GEARBOX"]["GEAR_SHIFTER"]
self.distance_button = 0
def update(self, can_parsers) -> tuple[structs.CarState, structs.IQCarState]:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_adas = can_parsers[Bus.adas]
ret = structs.CarState()
ret_iq = structs.IQCarState()
prev_distance_button = self.distance_button
self.distance_button = cp.vl["CRUISE_THROTTLE"]["FOLLOW_DISTANCE_BUTTON"]
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA):
ret.gasPressed = bool(cp.vl["GAS_PEDAL"]["GAS_PEDAL"] > 3)
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
ret.gasPressed = bool(cp.vl["CRUISE_THROTTLE"]["GAS_PEDAL"] > 3)
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA):
ret.brakePressed = bool(cp.vl["DOORS_LIGHTS"]["USER_BRAKE_PRESSED"])
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
ret.brakePressed = bool(cp.vl["CRUISE_THROTTLE"]["USER_BRAKE_PRESSED"])
fl = cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FL"] * CV.KPH_TO_MS
fr = cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FR"] * CV.KPH_TO_MS
rl = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RL"] * CV.KPH_TO_MS
rr = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RR"] * CV.KPH_TO_MS
# safety uses the rear wheel speeds for the speed measurement and angle limiting
ret.vEgoRaw = (rl + rr) / 2.0
v_ego_raw_full = (fl + fr + rl + rr) / 4.0
ret.vEgo, ret.aEgo = self.update_speed_kf(v_ego_raw_full)
ret.standstill = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RL"] == 0.0 and cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RR"] == 0.0
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
ret.cruiseState.enabled = bool(cp.vl["CRUISE_STATE"]["CRUISE_ENABLED"])
else:
ret.cruiseState.enabled = bool(cp_adas.vl["CRUISE_STATE"]["CRUISE_ENABLED"])
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL):
ret.seatbeltUnlatched = cp.vl["HUD"]["SEATBELT_DRIVER_LATCHED"] == 0
ret.cruiseState.available = bool(cp_cam.vl["PRO_PILOT"]["CRUISE_ON"])
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
if self.CP.carFingerprint == CAR.NISSAN_LEAF:
ret.seatbeltUnlatched = cp.vl["SEATBELT"]["SEATBELT_DRIVER_LATCHED"] == 0
elif self.CP.carFingerprint == CAR.NISSAN_LEAF_IC:
ret.seatbeltUnlatched = cp.vl["CANCEL_MSG"]["CANCEL_SEATBELT"] == 1
ret.cruiseState.available = bool(cp.vl["CRUISE_THROTTLE"]["CRUISE_AVAILABLE"])
elif self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
ret.seatbeltUnlatched = cp.vl["HUD"]["SEATBELT_DRIVER_LATCHED"] == 0
ret.cruiseState.available = bool(cp_adas.vl["PRO_PILOT"]["CRUISE_ON"])
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
speed = cp.vl["PROPILOT_HUD"]["SET_SPEED"]
else:
speed = cp_adas.vl["PROPILOT_HUD"]["SET_SPEED"]
if speed != 255:
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
conversion = CV.MPH_TO_MS if cp.vl["HUD_SETTINGS"]["SPEED_MPH"] else CV.KPH_TO_MS
else:
conversion = CV.MPH_TO_MS if cp.vl["HUD"]["SPEED_MPH"] else CV.KPH_TO_MS
ret.cruiseState.speed = speed * conversion
ret.cruiseState.speedCluster = (speed - 1) * conversion # Speed on HUD is always 1 lower than actually sent on can bus
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
ret.steeringTorque = cp_cam.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"]
else:
ret.steeringTorque = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"]
self.steeringTorqueSamples.append(ret.steeringTorque)
# Filtering driver torque to prevent steeringPressed false positives
ret.steeringPressed = bool(abs(sum(self.steeringTorqueSamples) / TORQUE_SAMPLES) > CarControllerParams.STEER_THRESHOLD)
ret.steeringAngleDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_ANGLE"]
ret.leftBlinker = bool(cp.vl["LIGHTS"]["LEFT_BLINKER"])
ret.rightBlinker = bool(cp.vl["LIGHTS"]["RIGHT_BLINKER"])
ret.doorOpen = any([cp.vl["DOORS_LIGHTS"]["DOOR_OPEN_RR"],
cp.vl["DOORS_LIGHTS"]["DOOR_OPEN_RL"],
cp.vl["DOORS_LIGHTS"]["DOOR_OPEN_FR"],
cp.vl["DOORS_LIGHTS"]["DOOR_OPEN_FL"]])
ret.espDisabled = bool(cp.vl["ESP"]["ESP_DISABLED"])
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?
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
ret.invalidLkasSetting = bool(cp.vl["LKAS_SETTINGS"]["LKAS_ENABLED"])
else:
ret.invalidLkasSetting = bool(cp_adas.vl["LKAS_SETTINGS"]["LKAS_ENABLED"])
self.cruise_throttle_msg = copy.copy(cp.vl["CRUISE_THROTTLE"])
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
self.cancel_msg = copy.copy(cp.vl["CANCEL_MSG"])
if self.CP.carFingerprint != CAR.NISSAN_ALTIMA:
self.lkas_hud_msg = copy.copy(cp_adas.vl["PROPILOT_HUD"])
self.lkas_hud_info_msg = copy.copy(cp_adas.vl["PROPILOT_HUD_INFO_MSG"])
IQCarState.update(self, ret, ret_iq, can_parsers)
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
*self.button_events,
]
return ret, ret_iq
@staticmethod
def get_can_parsers(CP, CP_IQ):
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 1 if CP.carFingerprint == CAR.NISSAN_ALTIMA else 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0 if CP.carFingerprint == CAR.NISSAN_ALTIMA else 1),
Bus.adas: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
}

View File

@@ -0,0 +1,108 @@
""" AUTO-FORMATTED USING iqdbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from iqdbc.car.structs import CarParams
from iqdbc.car.nissan.values import CAR
Ecu = CarParams.Ecu
FW_VERSIONS = {
CAR.NISSAN_ALTIMA: {
(Ecu.fwdCamera, 0x707, None): [
b'284N86CA1D',
],
(Ecu.eps, 0x742, None): [
b'6CA2B\xa9A\x02\x02G8A89P90D6A\x00\x00\x01\x80',
b'6CA2C\xa9A\x02\x02G8A89P90D6A\x00\x00\x01\x80',
],
(Ecu.engine, 0x7e0, None): [
b'237106GU3B',
b'237109HE2B',
],
(Ecu.gateway, 0x18dad0f1, None): [
b'284U29HE0A',
b'284U29HF0A',
],
},
CAR.NISSAN_LEAF: {
(Ecu.abs, 0x740, None): [
b'476605SA1C',
b'476605SA7D',
b'476605SC2D',
b'476606WK7B',
b'476606WK9B',
],
(Ecu.eps, 0x742, None): [
b'5SA2A\x99A\x05\x02N123F\x15b\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SA2A\xb7A\x05\x02N123F\x15\xa2\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SN2A\xb7A\x05\x02N123F\x15\xa2\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SN2A\xb7A\x05\x02N126F\x15\xb2\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.fwdCamera, 0x707, None): [
b'5SA0ADB\x04\x18\x00\x00\x00\x00\x00_*6\x04\x94a\x00\x00\x00\x80',
b'5SA2ADB\x04\x18\x00\x00\x00\x00\x00_*6\x04\x94a\x00\x00\x00\x80',
b'6WK2ADB\x04\x18\x00\x00\x00\x00\x00R;1\x18\x99\x10\x00\x00\x00\x80',
b'6WK2BDB\x04\x18\x00\x00\x00\x00\x00R;1\x18\x99\x10\x00\x00\x00\x80',
b'6WK2CDB\x04\x18\x00\x00\x00\x00\x00R=1\x18\x99\x10\x00\x00\x00\x80',
],
(Ecu.gateway, 0x18dad0f1, None): [
b'284U25SA3C',
b'284U25SP0C',
b'284U25SP1C',
b'284U26WK0A',
b'284U26WK0C',
],
},
CAR.NISSAN_LEAF_IC: {
(Ecu.fwdCamera, 0x707, None): [
b'5SH1BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
b'5SH3BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
b'5SH4BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
b'5SK0ADB\x04\x18\x00\x00\x00\x00\x00_(5\x07\x9aQ\x00\x00\x00\x80',
],
(Ecu.abs, 0x740, None): [
b'476605SD2D',
b'476605SD2E',
b'476605SH1D',
b'476605SH7D',
b'476605SH7E',
b'476605SK2A',
],
(Ecu.eps, 0x742, None): [
b'5SH2A\x99A\x05\x02N123F\x15\x81\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SH2A\xb7A\x05\x02N123F\x15\xa3\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SH2C\xb7A\x05\x02N123F\x15\xa3\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SH3A\x99A\x05\x02N123F\x15\x81\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SH3C\xb7A\x05\x02N123F\x15\xa3\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SK3A\x99A\x05\x02N123F\x15u\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.gateway, 0x18dad0f1, None): [
b'284U25SF0C',
b'284U25SH3A',
b'284U25SH3B',
b'284U25SK2D',
b'284U25SR0B',
b'284U25SR0C',
],
},
CAR.NISSAN_XTRAIL: {
(Ecu.fwdCamera, 0x707, None): [
b'284N86FR2A',
],
(Ecu.abs, 0x740, None): [
b'6FU0AD\x11\x02\x00\x02e\x95e\x80iQ#\x01\x00\x00\x00\x00\x00\x80',
b'6FU1BD\x11\x02\x00\x02e\x95e\x80iX#\x01\x00\x00\x00\x00\x00\x80',
],
(Ecu.eps, 0x742, None): [
b'6FP2A\x99A\x05\x02N123F\x18\x02\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.combinationMeter, 0x743, None): [
b'6FR2A\x18B\x05\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.engine, 0x7e0, None): [
b'6FR9A\xa0A\x06\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80',
b'6FU9B\xa0A\x06\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80',
],
(Ecu.gateway, 0x18dad0f1, None): [
b'284U26FR0E',
],
},
}

View File

@@ -0,0 +1,40 @@
from iqdbc.car import get_safety_config, structs
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.nissan.carcontroller import CarController
from iqdbc.car.nissan.carstate import CarState
from iqdbc.car.nissan.values import CAR, NissanSafetyFlags
from iqdbc.lvbs.car.nissan.values import NissanSafetyFlagsIQ
class CarInterface(CarInterfaceBase):
CarState = CarState
CarController = CarController
DRIVABLE_GEARS = (structs.CarState.GearShifter.brake,)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "nissan"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.nissan)]
ret.autoResumeSng = False
ret.steerAtStandstill = True
ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.1
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.radarUnavailable = True
if candidate == CAR.NISSAN_ALTIMA:
# Altima has EPS on C-CAN unlike the others that have it on V-CAN
ret.safetyConfigs[0].safetyParam |= NissanSafetyFlags.ALT_EPS_BUS.value
return ret
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
if candidate in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
ret.iqSafetyFlags |= NissanSafetyFlagsIQ.LEAF
return ret

View File

@@ -0,0 +1,154 @@
import crcmod
from iqdbc.car.nissan.values import CAR
# TODO: add this checksum to the CANPacker
nissan_checksum = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff)
def create_steering_control(packer, apply_torque, frame, steer_on, lkas_max_torque):
values = {
"COUNTER": frame % 0x10,
"DESIRED_ANGLE": apply_torque,
"SET_0x80_2": 0x80,
"SET_0x80": 0x80,
"MAX_TORQUE": lkas_max_torque if steer_on else 0,
"LKA_ACTIVE": steer_on,
}
dat = packer.make_can_msg("LKAS", 0, values)[1]
values["CHECKSUM"] = nissan_checksum(dat[:7])
return packer.make_can_msg("LKAS", 0, values)
def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg):
values = {s: cruise_throttle_msg[s] for s in [
"COUNTER",
"PROPILOT_BUTTON",
"CANCEL_BUTTON",
"GAS_PEDAL_INVERTED",
"SET_BUTTON",
"RES_BUTTON",
"FOLLOW_DISTANCE_BUTTON",
"NO_BUTTON_PRESSED",
"GAS_PEDAL",
"USER_BRAKE_PRESSED",
"NEW_SIGNAL_2",
"GAS_PRESSED_INVERTED",
"unsure1",
"unsure2",
"unsure3",
]}
can_bus = 1 if car_fingerprint == CAR.NISSAN_ALTIMA else 2
values["CANCEL_BUTTON"] = 1
values["NO_BUTTON_PRESSED"] = 0
values["PROPILOT_BUTTON"] = 0
values["SET_BUTTON"] = 0
values["RES_BUTTON"] = 0
values["FOLLOW_DISTANCE_BUTTON"] = 0
return packer.make_can_msg("CRUISE_THROTTLE", can_bus, values)
def create_cancel_msg(packer, cancel_msg, cruise_cancel):
values = {s: cancel_msg[s] for s in [
"CANCEL_SEATBELT",
"NEW_SIGNAL_1",
"NEW_SIGNAL_2",
"NEW_SIGNAL_3",
]}
if cruise_cancel:
values["CANCEL_SEATBELT"] = 1
return packer.make_can_msg("CANCEL_MSG", 2, values)
def create_lkas_hud_msg(packer, lkas_hud_msg, enabled, left_line, right_line, left_lane_depart, right_lane_depart):
values = {s: lkas_hud_msg[s] for s in [
"LARGE_WARNING_FLASHING",
"SIDE_RADAR_ERROR_FLASHING1",
"SIDE_RADAR_ERROR_FLASHING2",
"LEAD_CAR",
"LEAD_CAR_ERROR",
"FRONT_RADAR_ERROR",
"FRONT_RADAR_ERROR_FLASHING",
"SIDE_RADAR_ERROR_FLASHING3",
"LKAS_ERROR_FLASHING",
"SAFETY_SHIELD_ACTIVE",
"RIGHT_LANE_GREEN_FLASH",
"LEFT_LANE_GREEN_FLASH",
"FOLLOW_DISTANCE",
"AUDIBLE_TONE",
"SPEED_SET_ICON",
"SMALL_STEERING_WHEEL_ICON",
"unknown59",
"unknown55",
"unknown26",
"unknown28",
"unknown31",
"SET_SPEED",
"unknown43",
"unknown08",
"unknown05",
"unknown02",
]}
values["RIGHT_LANE_YELLOW_FLASH"] = 1 if right_lane_depart else 0
values["LEFT_LANE_YELLOW_FLASH"] = 1 if left_lane_depart else 0
values["LARGE_STEERING_WHEEL_ICON"] = 2 if enabled else 0
values["RIGHT_LANE_GREEN"] = 1 if right_line and enabled else 0
values["LEFT_LANE_GREEN"] = 1 if left_line and enabled else 0
return packer.make_can_msg("PROPILOT_HUD", 0, values)
def create_lkas_hud_info_msg(packer, lkas_hud_info_msg, steer_hud_alert):
values = {s: lkas_hud_info_msg[s] for s in [
"NA_HIGH_ACCEL_TEMP",
"SIDE_RADAR_NA_HIGH_CABIN_TEMP",
"SIDE_RADAR_MALFUNCTION",
"LKAS_MALFUNCTION",
"FRONT_RADAR_MALFUNCTION",
"SIDE_RADAR_NA_CLEAN_REAR_CAMERA",
"NA_POOR_ROAD_CONDITIONS",
"CURRENTLY_UNAVAILABLE",
"SAFETY_SHIELD_OFF",
"FRONT_COLLISION_NA_FRONT_RADAR_OBSTRUCTION",
"PEDAL_MISSAPPLICATION_SYSTEM_ACTIVATED",
"SIDE_IMPACT_NA_RADAR_OBSTRUCTION",
"WARNING_DO_NOT_ENTER",
"SIDE_IMPACT_SYSTEM_OFF",
"SIDE_IMPACT_MALFUNCTION",
"FRONT_COLLISION_MALFUNCTION",
"SIDE_RADAR_MALFUNCTION2",
"LKAS_MALFUNCTION2",
"FRONT_RADAR_MALFUNCTION2",
"PROPILOT_NA_MSGS",
"BOTTOM_MSG",
"HANDS_ON_WHEEL_WARNING",
"WARNING_STEP_ON_BRAKE_NOW",
"PROPILOT_NA_FRONT_CAMERA_OBSTRUCTED",
"PROPILOT_NA_HIGH_CABIN_TEMP",
"WARNING_PROPILOT_MALFUNCTION",
"ACC_UNAVAILABLE_HIGH_CABIN_TEMP",
"ACC_NA_FRONT_CAMERA_IMPARED",
"unknown07",
"unknown10",
"unknown15",
"unknown23",
"unknown19",
"unknown31",
"unknown32",
"unknown46",
"unknown61",
"unknown55",
"unknown50",
]}
if steer_hud_alert:
values["HANDS_ON_WHEEL_WARNING"] = 1
return packer.make_can_msg("PROPILOT_HUD_INFO_MSG", 0, values)

View File

@@ -0,0 +1,125 @@
from dataclasses import dataclass, field
from enum import Enum, IntFlag
from iqdbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
from iqdbc.car.lateral import AngleSteeringLimits
from iqdbc.car.structs import CarParams
from iqdbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column
from iqdbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
class CarControllerParams:
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
# When output steering Angle not within range -1311 and 1310,
# CANPacker packs wrong angle output to be decoded by panda
600, # deg, reasonable limit
([0., 5., 15.], [5., .8, .15]),
([0., 5., 15.], [5., 3.5, 0.4]),
)
LKAS_MAX_TORQUE = 1 # A value of 1 is easy to overpower
STEER_THRESHOLD = 1.0
def __init__(self, CP):
pass
class NissanSafetyFlags(IntFlag):
ALT_EPS_BUS = 1
class Footnote(Enum):
SETUP = CarFootnote(
"See more setup details for <a href=\"https://github.com/commaai/openpilot/wiki/nissan\" target=\"_blank\">Nissan</a>.",
Column.MAKE, setup_note=True)
@dataclass
class NissanCarDocs(CarDocs):
package: str = "ProPILOT Assist"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a]))
footnotes: list[Enum] = field(default_factory=lambda: [Footnote.SETUP])
@dataclass(frozen=True)
class NissanCarSpecs(CarSpecs):
centerToFrontRatio: float = 0.44
steerRatio: float = 17.
@dataclass
class NissanPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'nissan_x_trail_2017_generated'})
class CAR(Platforms):
NISSAN_XTRAIL = NissanPlatformConfig(
[NissanCarDocs("Nissan X-Trail 2017")],
NissanCarSpecs(mass=1610, wheelbase=2.705)
)
NISSAN_LEAF = NissanPlatformConfig(
[NissanCarDocs("Nissan Leaf 2018-23", video="https://youtu.be/vaMbtAh_0cY")],
NissanCarSpecs(mass=1610, wheelbase=2.705),
{Bus.pt: 'nissan_leaf_2018_generated'},
)
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
# Currently the only known difference between them is the inverted seatbelt signal.
NISSAN_LEAF_IC = NISSAN_LEAF.override(car_docs=[])
NISSAN_ROGUE = NissanPlatformConfig(
[NissanCarDocs("Nissan Rogue 2018-20")],
NissanCarSpecs(mass=1610, wheelbase=2.705)
)
NISSAN_ALTIMA = NissanPlatformConfig(
[NissanCarDocs("Nissan Altima 2019-20, 2024", car_parts=CarParts.common([CarHarness.nissan_b]))],
NissanCarSpecs(mass=1492, wheelbase=2.824)
)
DBC = CAR.create_dbc_map()
# Default diagnostic session
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81])
NISSAN_DIAGNOSTIC_RESPONSE_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, 0x81])
# Manufacturer specific
NISSAN_DIAGNOSTIC_REQUEST_KWP_2 = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0xda])
NISSAN_DIAGNOSTIC_RESPONSE_KWP_2 = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, 0xda])
NISSAN_VERSION_REQUEST_KWP = b'\x21\x83'
NISSAN_VERSION_RESPONSE_KWP = b'\x61\x83'
NISSAN_RX_OFFSET = 0x20
FW_QUERY_CONFIG = FwQueryConfig(
requests=[request for bus, logging in ((0, False), (1, True)) for request in [
Request(
[NISSAN_DIAGNOSTIC_REQUEST_KWP, NISSAN_VERSION_REQUEST_KWP],
[NISSAN_DIAGNOSTIC_RESPONSE_KWP, NISSAN_VERSION_RESPONSE_KWP],
bus=bus,
logging=logging,
),
Request(
[NISSAN_DIAGNOSTIC_REQUEST_KWP, NISSAN_VERSION_REQUEST_KWP],
[NISSAN_DIAGNOSTIC_RESPONSE_KWP, NISSAN_VERSION_RESPONSE_KWP],
rx_offset=NISSAN_RX_OFFSET,
bus=bus,
logging=logging,
),
# Rogue's engine solely responds to this
Request(
[NISSAN_DIAGNOSTIC_REQUEST_KWP_2, NISSAN_VERSION_REQUEST_KWP],
[NISSAN_DIAGNOSTIC_RESPONSE_KWP_2, NISSAN_VERSION_RESPONSE_KWP],
bus=bus,
logging=logging,
),
Request(
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
rx_offset=NISSAN_RX_OFFSET,
bus=bus,
logging=logging,
),
]],
)