diff --git a/opendbc_repo/opendbc.egg-info/SOURCES.txt b/opendbc_repo/opendbc.egg-info/SOURCES.txt index 73e7ef4..6daebf2 100644 --- a/opendbc_repo/opendbc.egg-info/SOURCES.txt +++ b/opendbc_repo/opendbc.egg-info/SOURCES.txt @@ -57,6 +57,13 @@ opendbc/car/body/carstate.py opendbc/car/body/fingerprints.py opendbc/car/body/interface.py opendbc/car/body/values.py +opendbc/car/byd/__init__.py +opendbc/car/byd/bydcan.py +opendbc/car/byd/carcontroller.py +opendbc/car/byd/carstate.py +opendbc/car/byd/fingerprints.py +opendbc/car/byd/interface.py +opendbc/car/byd/values.py opendbc/car/chrysler/__init__.py opendbc/car/chrysler/carcontroller.py opendbc/car/chrysler/carstate.py @@ -214,6 +221,7 @@ opendbc/dbc/FORD_CADS_64.dbc opendbc/dbc/__init__.py opendbc/dbc/acura_ilx_2016_nidec.dbc opendbc/dbc/bmw_e9x_e8x.dbc +opendbc/dbc/byd_common.dbc opendbc/dbc/cadillac_ct6_chassis.dbc opendbc/dbc/cadillac_ct6_object.dbc opendbc/dbc/cadillac_ct6_powertrain.dbc @@ -345,6 +353,7 @@ opendbc/safety/lateral.h opendbc/safety/longitudinal.h opendbc/safety/safety.h opendbc/safety/modes/body.h +opendbc/safety/modes/byd.h opendbc/safety/modes/chrysler.h opendbc/safety/modes/chrysler_common.h opendbc/safety/modes/chrysler_cusw.h @@ -416,6 +425,8 @@ opendbc/sunnypilot/car/intelligent_cruise_button_management_interface_base.py opendbc/sunnypilot/car/interfaces.py opendbc/sunnypilot/car/lateral_ext.py opendbc/sunnypilot/car/platform_list.py +opendbc/sunnypilot/car/byd/__init__.py +opendbc/sunnypilot/car/byd/mads.py opendbc/sunnypilot/car/chrysler/__init__.py opendbc/sunnypilot/car/chrysler/carcontroller_ext.py opendbc/sunnypilot/car/chrysler/carstate_ext.py diff --git a/opendbc_repo/opendbc/car/byd/README.md b/opendbc_repo/opendbc/car/byd/README.md new file mode 100644 index 0000000..6126410 --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/README.md @@ -0,0 +1 @@ +# byd diff --git a/opendbc_repo/opendbc/car/byd/__init__.py b/opendbc_repo/opendbc/car/byd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/opendbc_repo/opendbc/car/byd/bydcan.py b/opendbc_repo/opendbc/car/byd/bydcan.py new file mode 100644 index 0000000..47f6ea9 --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/bydcan.py @@ -0,0 +1,161 @@ +from opendbc.car.byd.values import CANBUS + + +def byd_checksum(data: bytearray) -> int: + byte_key = 0xAF + sum_first = sum(byte >> 4 for byte in data) # Extract upper nibble. + sum_second = sum(byte & 0xF for byte in data) # Extract lower nibble. + + remainder = sum_second >> 4 + + sum_first += (byte_key & 0xF) # Low nibble of byte_key. + sum_second += (byte_key >> 4) # High nibble of byte_key. + + # Inline inverse computation for each sum: + # inv = (-sum + 0x9) & 0xF + inv_first = ((-sum_first + 0x9) & 0xF) + inv_second = ((-sum_second + 0x9) & 0xF) + + return (((inv_first + (5 - remainder)) << 4) + inv_second) & 0xFF + + +def byd_checksum_short(data: bytearray) -> int: + # checksum for CHECKSUM_S 4byte + pass + + +class BydCAN: + LKAS_MODE_ACTIVE2 = 3 + LKAS_MODE_PASSIVE = 1 + + def __init__(self, packer): + self.packer = packer + self.mpc_lkas_counter = 0 + self.eps_steering_torque_counter = 0 + self.mpc_acc_cmd_counter = 0 + + def update_mpc_lkas_counter(self, val): + self.mpc_lkas_counter = val + + def _generate_mpc_lkas_new_counter(self): + counter = self.mpc_lkas_counter + self.mpc_lkas_counter = int(self.mpc_lkas_counter + 1) & 0xF + return counter + + def update_eps_steering_torque_counter(self, val): + self.eps_steering_torque_counter = val + + def _generate_eps_steering_torque_new_counter(self): + counter = self.eps_steering_torque_counter + self.eps_steering_torque_counter = int( + self.eps_steering_torque_counter + 1) & 0xF + return counter + + def update_acc_cmd_counter(self, val): + self.mpc_acc_cmd_counter = val + + def _generate_acc_cmd_new_counter(self): + counter = self.mpc_acc_cmd_counter + self.mpc_acc_cmd_counter = int(self.mpc_acc_cmd_counter + 1) & 0xF + return counter + + # MPC -> Panda -> EPS + def create_steering_control_torque(self, mpc_lkas_cmd_msg, torque, + request_prepare, active, mode, + eps_active): + lkas_output = torque if active and eps_active else 0 + lkas_prepare = request_prepare + lkas_active = active + lkas_mode = mode + left_lane = mpc_lkas_cmd_msg["LeftLane"] + right_lane = mpc_lkas_cmd_msg["RightLane"] + if active or request_prepare: + left_lane = 1 + right_lane = 1 + + values = { + "SETME_0x1": mpc_lkas_cmd_msg["SETME_0x1"], + "LeftLane": left_lane, + "Config": mpc_lkas_cmd_msg["Config"], + "SETME2_0x1": mpc_lkas_cmd_msg["SETME2_0x1"], + "Keep_Hands_On_Wheel": 0, + "MPCErr": mpc_lkas_cmd_msg["MPCErr"], + "SETME3_0x1": mpc_lkas_cmd_msg["SETME3_0x1"], + "LKAS_Output": lkas_output, + "LKASPrepare": lkas_prepare, + "LKAS_ACTIVE": lkas_active, + "TSRStatus": mpc_lkas_cmd_msg["TSRStatus"], + "SETME4_0x1": mpc_lkas_cmd_msg["SETME4_0x1"], + "RightLane": right_lane, + "LKAS_Mode": lkas_mode, + "SETME_0x0": mpc_lkas_cmd_msg["SETME_0x0"], + "TSRResult": mpc_lkas_cmd_msg["TSRResult"], + "Unknow1": mpc_lkas_cmd_msg["Unknow1"], + "COUNTER": self._generate_mpc_lkas_new_counter(), + } + data = self.packer.make_can_msg("MPC_LKAS_CMD", CANBUS.main_bus, values)[1] + values["CHECKSUM"] = byd_checksum(data) + return self.packer.make_can_msg("MPC_LKAS_CMD", CANBUS.main_bus, values) + + def create_steering_torque(self, eps_steering_torque_msg, mpc_lkas_output, + mpc_lkas_request_prepare, mpc_lkas_active): + lks_prepared = eps_steering_torque_msg["LKSPrepare"] + cruise_activated = eps_steering_torque_msg["Cruise_Activated"] + main_torque = eps_steering_torque_msg["MAIN_TORQUE"] + + if mpc_lkas_active: + lks_prepared = 0 + cruise_activated = 1 + main_torque = mpc_lkas_output + elif mpc_lkas_request_prepare: + lks_prepared = 1 + cruise_activated = 0 + main_torque = 0 + else: + lks_prepared = 0 + cruise_activated = 0 + main_torque = 0 + + values = { + "LKSPrepare": lks_prepared, + "Cruise_Activated": cruise_activated, + "TORQUE_FAILED": eps_steering_torque_msg["TORQUE_FAILED"], + "SET_ME_1_1": eps_steering_torque_msg["SET_ME_1_1"], + "Steer_Warning": eps_steering_torque_msg["Steer_Warning"], + "Steer_Error_1": eps_steering_torque_msg["Steer_Error_1"], + "Steer_Error_2": eps_steering_torque_msg["Steer_Error_2"], + "SET_ME_1": eps_steering_torque_msg["SET_ME_1"], + "MAIN_TORQUE": main_torque, + "SET_ME_1_2": eps_steering_torque_msg["SET_ME_1_2"], + "Keep_Hands_On_Wheel": 0, + "SET_ME_3": eps_steering_torque_msg["SET_ME_3"], + "Steer_Torque_Sensor": eps_steering_torque_msg["Steer_Torque_Sensor"], + "SET_ME_XF_1": eps_steering_torque_msg["SET_ME_XF_1"], + "SET_ME_XFFF": eps_steering_torque_msg["SET_ME_XFFF"], + "COUNTER": self._generate_eps_steering_torque_new_counter(), + } + data = self.packer.make_can_msg("STEERING_TORQUE", CANBUS.cam_bus, values)[1] + values["CHECKSUM"] = byd_checksum(data) + return self.packer.make_can_msg("STEERING_TORQUE", CANBUS.cam_bus, values) + + def create_acc_cmd(self, acc_cmd_msg, accel): + values = { + "ACCEL_CMD": accel, + "ComfortBandUpper": acc_cmd_msg["ComfortBandUpper"], + "ComfortBandLower": acc_cmd_msg["ComfortBandLower"], + "JerkUpperLimit": 1, + "SET_ME_1": acc_cmd_msg["SET_ME_1"], + "JerkLowerLimit": -1, + "STANDSTILL_RESUME": 0, + "STANDSTILL_STATE": 0, + "BRAKE_BEHAVIOR": acc_cmd_msg["BRAKE_BEHAVIOR"], + "ACC_REQ_NOT_STANDSTILL": acc_cmd_msg["ACC_REQ_NOT_STANDSTILL"], + "ACC_CONTROLLABLE_AND_ON": acc_cmd_msg["ACC_CONTROLLABLE_AND_ON"], + "ACC_OVERRIDE_OR_STANDSTILL": acc_cmd_msg["ACC_OVERRIDE_OR_STANDSTILL"], + "ESP_BEHAVIOR": acc_cmd_msg["ESP_BEHAVIOR"], + "COUNTER": self._generate_acc_cmd_new_counter(), + "SET_ME_XF": acc_cmd_msg["SET_ME_XF"], + } + data = self.packer.make_can_msg("ACC_CMD", CANBUS.main_bus, values)[1] + values["CHECKSUM"] = byd_checksum(data) + return self.packer.make_can_msg("ACC_CMD", CANBUS.main_bus, values) diff --git a/opendbc_repo/opendbc/car/byd/carcontroller.py b/opendbc_repo/opendbc/car/byd/carcontroller.py new file mode 100644 index 0000000..fea956a --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/carcontroller.py @@ -0,0 +1,104 @@ +import numpy as np +from opendbc.can.packer import CANPacker +from opendbc.car import Bus, structs +from opendbc.car.lateral import apply_meas_steer_torque_limits +from opendbc.car.interfaces import CarControllerBase +from opendbc.car.byd.bydcan import BydCAN +from opendbc.car.byd.values import CarControllerParams + +VisualAlert = structs.CarControl.HUDControl.VisualAlert +ButtonType = structs.CarState.ButtonEvent.Type +LongCtrlState = structs.CarControl.Actuators.LongControlState + + +class LKASState: + INACTIVE = 0 + PREPARING = 1 + ACTIVE = 2 + + +class CarController(CarControllerBase): + def __init__(self, dbc_names, CP, CP_SP): + super().__init__(dbc_names, CP, CP_SP) + self.params = CarControllerParams(CP) + self.packer = CANPacker(dbc_names[Bus.pt]) + self.can = BydCAN(self.packer) + self.apply_torque_last = 0 + + self.lkas_state = LKASState.INACTIVE + self.lkas_counter_updated = False + + self.apply_accel_last = 0 + + def update(self, CC, CC_SP, CS, now_nanos): + # car control running in 100Hz + actuators = CC.actuators + can_sends = [] + + if (self.frame % self.params.STEER_STEP) == 0: + # steering control running in 50Hz + if not self.lkas_counter_updated: + self.lkas_counter_updated = True + self.can.update_mpc_lkas_counter( + int(CS.mpc_lkas_cmd_msg["COUNTER"] + 1) & 0xF) + self.can.update_eps_steering_torque_counter( + int(CS.eps_steering_torque_msg["COUNTER"] + 1) & 0xF) + self.can.update_acc_cmd_counter( + int(CS.acc_cmd_msg["COUNTER"] + 1) & 0xF) + + # update lkas state + if self.lkas_state == LKASState.INACTIVE: + if CC.latActive: + self.lkas_state = LKASState.PREPARING + elif self.lkas_state == LKASState.PREPARING: + if CS.eps_prepared: + self.lkas_state = LKASState.ACTIVE + elif self.lkas_state == LKASState.ACTIVE: + if not CC.latActive: + self.lkas_state = LKASState.INACTIVE + + lkas_request_prepare = int(self.lkas_state == LKASState.PREPARING) + lkas_active = int(self.lkas_state == LKASState.ACTIVE) + if self.lkas_state in (LKASState.PREPARING, LKASState.ACTIVE): + lkas_mode = BydCAN.LKAS_MODE_ACTIVE2 + else: + lkas_mode = BydCAN.LKAS_MODE_PASSIVE + + # calc apply torque + apply_torque = 0 + if self.lkas_state == LKASState.ACTIVE: + apply_torque = int(round(actuators.torque * self.params.STEER_MAX)) + apply_torque = apply_meas_steer_torque_limits(apply_torque, self.apply_torque_last, + CS.out.steeringTorqueEps, self.params) + + pack = self.can.create_steering_control_torque(CS.mpc_lkas_cmd_msg, apply_torque, + lkas_request_prepare, lkas_active, + lkas_mode, CS.eps_activated) + can_sends.append(pack) + pack = self.can.create_steering_torque(CS.eps_steering_torque_msg, + CS.mpc_lkas_output, + CS.mpc_lkas_request_prepare, + CS.mpc_lkas_active) + can_sends.append(pack) + + self.apply_torque_last = apply_torque + + # Longitudinal control + if self.CP.openpilotLongitudinalControl: + if (self.frame % self.params.ACC_SETP) == 0: + accel = 0 + + if CC.longActive: + accel = float(np.clip(actuators.accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)) + + pack = self.can.create_acc_cmd(CS.acc_cmd_msg, accel) + can_sends.append(pack) + self.apply_accel_last = accel + + new_actuators = actuators.as_builder() + new_actuators.torque = self.apply_torque_last / self.params.STEER_MAX + new_actuators.torqueOutputCan = self.apply_torque_last + new_actuators.accel = self.apply_accel_last + + self.frame += 1 + return new_actuators, can_sends diff --git a/opendbc_repo/opendbc/car/byd/carstate.py b/opendbc_repo/opendbc/car/byd/carstate.py new file mode 100644 index 0000000..519264d --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/carstate.py @@ -0,0 +1,125 @@ +import copy +from opendbc.car import Bus, create_button_events, structs +from opendbc.can.parser import CANParser +from opendbc.car.common.conversions import Conversions as CV +from opendbc.car.interfaces import CarStateBase +from opendbc.car.byd.values import DBC, CANBUS, STEER_THRESHOLD, GEAR_MAP +from opendbc.sunnypilot.car.byd.mads import MadsCarState + +GearShifter = structs.CarState.GearShifter +ButtonType = structs.CarState.ButtonEvent.Type + + +class CarState(CarStateBase, MadsCarState): + def __init__(self, CP, CP_SP): + CarStateBase.__init__(self, CP, CP_SP) + MadsCarState.__init__(self, CP, CP_SP) + + self.mpc_lkas_cmd_msg = None + self.eps_steering_torque_msg = None + self.eps_prepared = False + self.eps_activated = False + self.acc_cmd_msg = None + + self.mpc_lkas_output = 0 + self.mpc_lkas_active = False + self.mpc_lkas_request_prepare = False + + def update(self, can_parsers) -> tuple[structs.CarState, structs.CarStateSP]: + cp = can_parsers[Bus.pt] + cp_cam = can_parsers[Bus.cam] + ret = structs.CarState() + ret_sp = structs.CarStateSP() + + # car Speed + ret.vEgoRaw = cp.vl["ESC"]["VEHICLE_SPEED"] * CV.KPH_TO_MS + ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) + ret.standstill = cp.vl["BRAKE_APPLIED"]["STANDSTILL"] == 1 + + # Gas pedal + ret.gasPressed = cp.vl["PEDAL"]["GAS_PEDAL"] > 0 + + # Brake pedal + ret.brakePressed = cp.vl["PEDAL"]["BRAKE_PEDAL"] > 0 + + # Steering wheel + ret.steeringAngleDeg = cp.vl["STEER_MODULE"]["STEER_ANGLE"] + ret.steeringRateDeg = cp.vl["STEER_MODULE"]["STEERING_RATE"] + ret.steeringTorque = cp.vl["STEERING_TORQUE"]["Steer_Torque_Sensor"] + ret.steeringTorqueEps = cp.vl["STEERING_TORQUE"]["MAIN_TORQUE"] + # TODO: looks like STEER_THRESHOLD is too small + ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5) + ret.steerFaultPermanent = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0 + ret.steerFaultTemporary = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0 + + ret.stockAeb = cp_cam.vl["ACC_HUD_ADAS"]["AEB"] == 1 + ret.stockFcw = cp_cam.vl["ACC_HUD_ADAS"]["FCW"] == 1 # Forward Collision Warning + + # Cruise state + ret.cruiseState.enabled = cp_cam.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] in (3, 5) # (Active, Override) + ret.cruiseState.speed = cp_cam.vl["ACC_HUD_ADAS"]["SET_SPEED"] * 10 * CV.KPH_TO_MS + ret.cruiseState.available = cp_cam.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] not in (8, 9) # (Failure, PermanentFailure) + ret.cruiseState.standstill = False # This needs to be false, since we can resume from stop without sending anything special + + # Gear + ret.gearShifter = GEAR_MAP.get(int(cp.vl["DRIVE_STATE"]["GEAR"]), GearShifter.unknown) + + # MADS: track the dedicated LKAS steering wheel button + MadsCarState.update_mads(self, ret, can_parsers) + + # button presses + ret.leftBlinker = cp.vl["STALKS"]["TURN_SIGNAL_SWITCH"] == 3 # LeftSteeringLongLift + ret.rightBlinker = cp.vl["STALKS"]["TURN_SIGNAL_SWITCH"] == 5 # RightSteeringLongLift + + ret.buttonEvents = [*create_button_events(self.lkas_button, self.prev_lkas_button, {1: ButtonType.lkas})] + + # lock info + ret.doorOpen = any([cp.vl["METER_CLUSTER"]["FRONT_LEFT_DOOR"], + cp.vl["METER_CLUSTER"]["FRONT_RIGHT_DOOR"], + cp.vl["METER_CLUSTER"]["BACK_LEFT_DOOR"], + cp.vl["METER_CLUSTER"]["BACK_RIGHT_DOOR"]]) + ret.seatbeltUnlatched = cp.vl["METER_CLUSTER"]["SEATBELT_DRIVER"] != 1 + + # blindspot sensors + # ret.leftBlindspot = False + # ret.rightBlindspot = False + + # Messages needed by carcontroller + # for generate MPC_LKAS_CMD + self.mpc_lkas_cmd_msg = copy.copy(cp_cam.vl["MPC_LKAS_CMD"]) + self.eps_prepared = cp.vl["STEERING_TORQUE"]["LKSPrepare"] != 0 + self.eps_activated = cp.vl["STEERING_TORQUE"]["Cruise_Activated"] != 0 + # for generate STEERING_TORQUE + self.eps_steering_torque_msg = copy.copy(cp.vl["STEERING_TORQUE"]) + self.mpc_lkas_output = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_Output"] + self.mpc_lkas_active = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_ACTIVE"] != 0 + self.mpc_lkas_request_prepare = cp_cam.vl["MPC_LKAS_CMD"]["LKASPrepare"] != 0 + # for generate ACC_CMD + self.acc_cmd_msg = copy.copy(cp_cam.vl["ACC_CMD"]) + return ret, ret_sp + + @staticmethod + def get_can_parsers(CP, CP_SP): + pt_messages = [ + # sig_address, frequency + ("ESC", 50), + ("BRAKE_APPLIED", 50), + ("PEDAL", 50), + ("STEER_MODULE", 100), + ("STEERING_TORQUE", 50), + ("DRIVE_STATE", 50), + ("STALKS", 20), + ("METER_CLUSTER", 20), + ("PCM_BUTTONS", 20), + ] + + cam_messages = [ + ("ACC_HUD_ADAS", 50), + ("MPC_LKAS_CMD", 50), + ("ACC_CMD", 25), + ] + + return { + Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CANBUS.main_bus), + Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CANBUS.cam_bus), + } diff --git a/opendbc_repo/opendbc/car/byd/fingerprints.py b/opendbc_repo/opendbc/car/byd/fingerprints.py new file mode 100644 index 0000000..63dd91c --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/fingerprints.py @@ -0,0 +1,22 @@ +from opendbc.car.structs import CarParams +from opendbc.car.byd.values import CAR + +Ecu = CarParams.Ecu + +FINGERPRINTS = { + # NOTE: the HAN EV 23 fingerprint is a minimal subset of the SEAL fingerprint, + # so a HAN cannot be uniquely identified by live CAN fingerprinting. + # Set FINGERPRINT=BYD_HAN_EV_23 (and SKIP_FW_QUERY) to force it. + CAR.BYD_HAN_EV_23: [{ + 140: 8, 213: 8, 287: 5, 289: 8, 291: 8, 301: 8, 307: 8, 337: 8, 496: 8, 536: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8 + }], + CAR.BYD_SEAL_PERFORMANCE_25: [{ + 140: 8, 213: 8, 287: 5, 289: 8, 291: 8, 301: 8, 307: 8, 311: 8, 312: 8, 337: 8, 359: 8, 482: 8, 493: 8, 496: 8, 508: 8, + 511: 8, 536: 8, 544: 8, 546: 8, 547: 8, 567: 8, 575: 8, 576: 8, 578: 8, 588: 8, 604: 8, 612: 8, 626: 8, 657: 8, 660: 8, + 661: 8, 663: 8, 668: 8, 692: 8, 694: 8, 714: 8, 724: 8, 748: 8, 758: 8, 790: 8, 797: 8, 798: 8, 801: 8, 802: 8, 803: 8, + 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 837: 8, 854: 8, 860: 8, 868: 8, 877: 8, 904: 8, 906: 8, 944: 8, 948: 8, + 951: 8, 965: 8, 973: 8, 985: 8, 1023: 8, 1028: 8, 1031: 8, 1033: 8, 1040: 8, 1048: 8, 1058: 8, 1074: 8, 1092: 8, 1093: 8, + 1107: 8, 1108: 8, 1141: 8, 1166: 8, 1168: 8, 1178: 8, 1189: 8, 1193: 8, 1203: 64, 1204: 64, 1226: 16, 1246: 8, 1297: 8, + 1319: 8, 1322: 8, 1365: 8, 1366: 8 + }], +} diff --git a/opendbc_repo/opendbc/car/byd/interface.py b/opendbc_repo/opendbc/car/byd/interface.py new file mode 100644 index 0000000..03b6727 --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/interface.py @@ -0,0 +1,49 @@ +from opendbc.car import get_safety_config, structs +from opendbc.car.common.conversions import Conversions as CV +from opendbc.car.interfaces import CarInterfaceBase +from opendbc.car.byd.carcontroller import CarController +from opendbc.car.byd.carstate import CarState +from opendbc.car.byd.values import CAR, BydFlags, BydSafetyFlags + + +class CarInterface(CarInterfaceBase): + CarState = CarState + CarController = CarController + + @staticmethod + def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams: + ret.brand = "byd" + ret.dashcamOnly = False + + ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.byd)] + + # lateral control parameters + ret.steerActuatorDelay = 0.3 + ret.steerLimitTimer = 0.5 + + if candidate in (CAR.BYD_HAN_EV_23, CAR.BYD_SEAL_PERFORMANCE_25): + ret.steerControlType = structs.CarParams.SteerControlType.torque + + ret.minEnableSpeed = -1. + ret.minSteerSpeed = 0.1 * CV.KPH_TO_MS + ret.steerLimitTimer = 0.5 + + if ret.steerControlType == structs.CarParams.SteerControlType.torque: + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + + # longitudinal control parameters + ret.alphaLongitudinalAvailable = True + if alpha_long: + ret.openpilotLongitudinalControl = True + ret.startingState = True + ret.startAccel = 0.1 + ret.flags |= BydFlags.LONG_CONTROL.value + ret.safetyConfigs[0].safetyParam |= BydSafetyFlags.LONG_CONTROL.value + ret.vEgoStopping = 0.1 + ret.vEgoStarting = 0.1 + ret.stoppingDecelRate = 0.3 + + # TODO: add radar support + ret.radarUnavailable = True + + return ret diff --git a/opendbc_repo/opendbc/car/byd/values.py b/opendbc_repo/opendbc/car/byd/values.py new file mode 100644 index 0000000..f0331b9 --- /dev/null +++ b/opendbc_repo/opendbc/car/byd/values.py @@ -0,0 +1,78 @@ +from dataclasses import dataclass, field +from enum import IntFlag +from opendbc.car import Bus, DbcDict, PlatformConfig, Platforms, CarSpecs, STD_CARGO_KG +from opendbc.car.structs import CarParams, CarState +from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts + +Ecu = CarParams.Ecu + + +class CANBUS: + main_bus = 0 + radar_bus = 1 + cam_bus = 2 + + +class BydSafetyFlags(IntFlag): + LONG_CONTROL = 1 + + +class BydFlags(IntFlag): + LONG_CONTROL = 1 + + +@dataclass +class BydCarDocs(CarDocs): + package: str = "All" + car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.custom])) + #todo add docs and harness info + + +@dataclass +class BydPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "byd_common"}) + + +class CAR(Platforms): + BYD_HAN_EV_23 = BydPlatformConfig( + [BydCarDocs("BYD HAN EV 2023")], + CarSpecs(mass=1940 + STD_CARGO_KG, wheelbase=2.92, steerRatio=16.5, + centerToFrontRatio=0.44, tireStiffnessFactor=1.0) + ) + BYD_SEAL_PERFORMANCE_25 = BydPlatformConfig( + [BydCarDocs("BYD SEAL PERFORMANCE 2025")], + # TODO: verify steerRatio and centerToFrontRatio on a real car + CarSpecs(mass=2100 + STD_CARGO_KG, wheelbase=2.92, steerRatio=16.0, + centerToFrontRatio=0.44, tireStiffnessFactor=1.0) + ) + + +class CarControllerParams: + def __init__(self, CP): + self.STEER_STEP = 2 # 50Hz + self.ACC_SETP = 4 # 25Hz + + self.STEER_ERROR_MAX = 46 + if CP.carFingerprint in (CAR.BYD_HAN_EV_23, CAR.BYD_SEAL_PERFORMANCE_25): + self.STEER_MAX = 300 + self.STEER_DELTA_UP = 6 + self.STEER_DELTA_DOWN = 6 + + self.ACCEL_MAX = 2.0 # m/s^2 + self.ACCEL_MIN = -4.0 # m/s^2 + self.JERK_LIMIT_MAX = 4.0 # m/s^3 + self.JERK_LIMIT_MIN = -4.0 # m/s^3 + + +STEER_THRESHOLD = 1 + +GEAR_MAP = { + 4: CarState.GearShifter.drive, + 2: CarState.GearShifter.reverse, + 3: CarState.GearShifter.neutral, + 1: CarState.GearShifter.park, +} + +# TODO: Get a BYD VDS to see how firmware could be queried. Until then BYD is +# identified by legacy CAN fingerprinting only. +DBC = CAR.create_dbc_map() diff --git a/opendbc_repo/opendbc/car/car.capnp b/opendbc_repo/opendbc/car/car.capnp index 31a1587..34a4aa7 100644 --- a/opendbc_repo/opendbc/car/car.capnp +++ b/opendbc_repo/opendbc/car/car.capnp @@ -634,6 +634,7 @@ struct CarParams { fcaGiorgio @32; rivian @33; volkswagenMeb @34; + byd @35; } enum SteerControlType { diff --git a/opendbc_repo/opendbc/car/fingerprints.py b/opendbc_repo/opendbc/car/fingerprints.py index 81800aa..324fbd9 100644 --- a/opendbc_repo/opendbc/car/fingerprints.py +++ b/opendbc_repo/opendbc/car/fingerprints.py @@ -1,5 +1,6 @@ from opendbc.car.interfaces import get_interface_attr from opendbc.car.body.values import CAR as BODY +from opendbc.car.byd.values import CAR as BYD from opendbc.car.chrysler.values import CAR as CHRYSLER from opendbc.car.ford.values import CAR as FORD from opendbc.car.gm.values import CAR as GM @@ -56,6 +57,8 @@ def all_legacy_fingerprint_cars(): # A dict that maps old platform strings to their latest representations MIGRATION = { + "BYD HAN EV 23": BYD.BYD_HAN_EV_23, + "BYD SEAL PERFORMANCE 2025": BYD.BYD_SEAL_PERFORMANCE_25, "ACURA ILX 2016 ACURAWATCH PLUS": HONDA.ACURA_ILX, "ACURA RDX 2018 ACURAWATCH PLUS": HONDA.ACURA_RDX, "ACURA RDX 2020 TECH": HONDA.ACURA_RDX_3G, diff --git a/opendbc_repo/opendbc/car/torque_data/params.toml b/opendbc_repo/opendbc/car/torque_data/params.toml index 1fbabf6..0e79259 100644 --- a/opendbc_repo/opendbc/car/torque_data/params.toml +++ b/opendbc_repo/opendbc/car/torque_data/params.toml @@ -1,4 +1,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] +"BYD_HAN_EV_23" = [1.807, 1.674, 0.04] +"BYD_SEAL_PERFORMANCE_25" = [1.807, 1.674, 0.04] "ACURA_ILX" = [1.524988973896102, 0.519011053086259, 0.34236219253028] "ACURA_RDX" = [0.9987728568686902, 0.5323765166196301, 0.303218805715844] "ACURA_RDX_3G" = [1.4314459806646749, 0.33874701282109954, 0.18048847083897598] diff --git a/opendbc_repo/opendbc/car/values.py b/opendbc_repo/opendbc/car/values.py index f606249..5a9a3a5 100644 --- a/opendbc_repo/opendbc/car/values.py +++ b/opendbc_repo/opendbc/car/values.py @@ -1,5 +1,6 @@ from typing import get_args from opendbc.car.body.values import CAR as BODY +from opendbc.car.byd.values import CAR as BYD from opendbc.car.chrysler.values import CAR as CHRYSLER from opendbc.car.ford.values import CAR as FORD from opendbc.car.gm.values import CAR as GM @@ -15,7 +16,7 @@ from opendbc.car.tesla.values import CAR as TESLA from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.volkswagen.values import CAR as VOLKSWAGEN -Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | MOCK | NISSAN | PSA | RIVIAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN +Platform = BODY | BYD | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | MOCK | NISSAN | PSA | RIVIAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN BRANDS = get_args(Platform) PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in brand} diff --git a/opendbc_repo/opendbc/dbc/byd_common.dbc b/opendbc_repo/opendbc/dbc/byd_common.dbc new file mode 100644 index 0000000..ca01638 --- /dev/null +++ b/opendbc_repo/opendbc/dbc/byd_common.dbc @@ -0,0 +1,375 @@ +VERSION "" + + +NS_ : + NS_DESC_ + CM_ + BA_DEF_ + BA_ + VAL_ + CAT_DEF_ + CAT_ + FILTER + BA_DEF_DEF_ + EV_DATA_ + ENVVAR_DATA_ + SGTYPE_ + SGTYPE_VAL_ + BA_DEF_SGTYPE_ + BA_SGTYPE_ + SIG_TYPE_REF_ + VAL_TABLE_ + SIG_GROUP_ + SIG_VALTYPE_ + SIGTYPE_VALTYPE_ + BO_TX_BU_ + BA_DEF_REL_ + BA_REL_ + BA_DEF_DEF_REL_ + BU_SG_REL_ + BU_EV_REL_ + BU_BO_REL_ + SG_MUL_VAL_ + +BS_: + +BU_: XXX + +BO_ 85 EPB: 8 XXX + SG_ EPB_ACTIVE : 40|1@1+ (1,0) [0|1] "" XXX + +BO_ 234 STEERING_TORQUE_ALT: 8 XXX + SG_ LKSPrepare : 0|1@1+ (1,0) [0|1] "" XXX + SG_ Cruise_Activated : 1|1@1+ (1,0) [0|1] "" XXX + SG_ TORQUE_FAILED : 4|1@1+ (1,0) [0|1] "" XXX + SG_ Steer_Torque_Sensor : 8|12@1- (1,0) [0|4095] "" XXX + SG_ MAIN_TORQUE : 24|12@1+ (0.15,-316) [0|4095] "" XXX + SG_ WAIT_INIT : 36|1@1+ (1,0) [0|1] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 235 MPC_LKAS_CMD_ALT: 8 XXX + SG_ TORQUE_FACTOR : 0|8@1+ (1,0) [0|255] "" XXX + SG_ LKAS_Output : 8|12@1- (0.15,0) [0|4095] "" XXX + SG_ LKASPrepare : 20|1@1+ (1,0) [0|1] "" XXX + SG_ LKAS_ACTIVE : 21|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_2 : 22|2@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_FF_1 : 24|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_FF_2 : 32|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_FF_3 : 40|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_F : 48|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 269 ECM: 8 XXX + SG_ ENG_RPM : 0|16@1+ (0.25,0) [0|255] "" XXX + SG_ GAS_PEDAL : 40|8@1+ (0.00392,0) [0|255] "" XXX + +BO_ 287 STEER_MODULE: 5 XXX + SG_ STEER_ANGLE : 0|16@1- (0.1,0) [-450|450] "" XXX + SG_ STEERING_RATE : 16|8@1+ (4,0) [0|1020] "" XXX + SG_ PAYLOAD : 24|8@1+ (1,0) [0|255] "" XXX + SG_ COUNTER : 32|4@1+ (1,0) [0|255] "" XXX + SG_ CHECKSUM_S : 36|4@1+ (1,0) [0|15] "" XXX + +BO_ 289 ESC: 8 XXX + SG_ VEHICLE_SPEED : 0|12@1+ (0.0725,0) [0|4095] "" XXX + +BO_ 291 IPB: 8 XXX + SG_ BRAKE_PRESSED : 43|1@0+ (1,0) [0|1] "" XXX + +BO_ 301 BRAKE_PRESSED: 8 XXX + SG_ BRAKE_PRESSED : 8|1@1+ (1,0) [0|1] "" XXX + +BO_ 307 STALKS: 8 XXX + SG_ EPB_ACTIVE : 1|1@1+ (1,0) [0|1] "" XXX + SG_ HEADLIGHT : 2|1@1+ (1,0) [0|1] "" XXX + SG_ HIGH_BEAM : 3|1@1+ (1,0) [0|1] "" XXX + SG_ LEFT_BLINKER : 4|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_BLINKER : 5|1@0+ (1,0) [0|1] "" XXX + SG_ TURN_SIGNAL_SWITCH : 36|3@1+ (1,0) [0|7] "" XXX + SG_ AUTO_HEADLIGHT : 40|1@0+ (1,0) [0|1] "" XXX + +BO_ 455 STEER_ANGLE_ALT: 8 XXX + SG_ STEER_ANGLE : 0|16@1- (0.1,0) [0|4095] "" XXX + SG_ STEER_RATE : 16|8@1+ (4,0) [0|255] "" XXX + +BO_ 482 MPC_LKAS_CMD_ANGLE: 8 XXX + SG_ JerkUpperLimit : 0|10@1- (1,0) [0|65535] "" XXX + SG_ JerkLowerLimit : 10|10@1- (1,0) [0|15] "" XXX + SG_ LKASPrepare : 20|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_ACTIVE : 21|1@0+ (1,0) [0|1] "" XXX + SG_ SET_ME_3 : 22|2@1+ (1,0) [0|1] "" XXX + SG_ LKAS_Output : 24|16@1- (0.1,0) [0|255] "" XXX + SG_ SET_ME_FF : 47|8@0+ (1,0) [0|255] "" XXX + SG_ SET_ME_F : 48|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 496 WHEEL_SPEED: 8 XXX + SG_ WHEELSPEED_FL : 0|12@1+ (0.0725,0) [0|281.4625] "km/h" XXX + SG_ FAULT_FR : 12|1@1+ (1,0) [0|1] "" XXX + SG_ FAULT_FL : 13|1@1+ (1,0) [0|1] "" XXX + SG_ FAULT_BR : 14|1@1+ (1,0) [0|1] "" XXX + SG_ FAULT_BL : 15|1@1+ (1,0) [0|1] "" XXX + SG_ WHEELSPEED_FR : 16|12@1+ (0.0725,0) [0|281.4625] "km/h" XXX + SG_ WHEELSPEED_BL : 28|12@1+ (0.0725,0) [0|281.4625] "km/h" XXX + SG_ WHEELSPEED_BR : 40|12@1+ (0.0725,0) [0|281.4625] "km/h" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 508 STEERING_TORQUE_ANGLE: 8 XXX + SG_ LKSPrepare : 0|1@1+ (1,0) [0|1] "" XXX + SG_ Cruise_Activated : 1|1@1+ (1,0) [0|1] "" XXX + SG_ TORQUE_FAILED : 2|1@1+ (1,0) [0|1] "" XXX + SG_ Steer_Torque_Sensor : 4|12@1- (0.1,0) [0|65535] "" XXX + SG_ TARGET_ANGLE : 16|16@1- (0.1,0) [0|255] "" XXX + SG_ MAIN_TORQUE : 32|12@1- (1,0) [0|63] "" XXX + SG_ Keep_Hands_On_Wheel : 45|1@1+ (1,0) [0|255] "" XXX + SG_ TORQUE_TEMP_FAILED : 46|2@1+ (1,0) [0|3] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 522 ENGINE_RPM: 8 XXX + SG_ ENG_RPM : 31|8@0+ (1,0) [0|255] "" XXX + +BO_ 523 ENGINE_GAUGE: 8 XXX + SG_ ENG_RPM : 15|8@0+ (12,0) [0|0] "" XXX + +BO_ 530 TCU: 8 XXX + SG_ GEAR : 48|4@1+ (1,0) [0|15] "" XXX + +BO_ 536 EPB_NG: 8 XXX + SG_ EPB_ACTIVE : 0|2@1+ (1,0) [0|3] "" XXX + SG_ EPB_STATUS : 9|3@1+ (1,0) [0|7] "" XXX + SG_ COUNTER : 48|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 544 BRAKE_APPLIED: 8 XXX + SG_ BRAKE_HOLD_ACTIVE : 8|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_APPLIED_ACTIVE_LOW : 12|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL : 19|2@0+ (1,0) [0|3] "" XXX + SG_ VLC_SENSOR : 24|8@1+ (0.05,-7) [0|255] "" XXX + SG_ NEW_SIGNAL_2 : 32|13@1+ (1,0) [0|8191] "" XXX + +BO_ 546 YAW_SENSOR: 8 XXX + SG_ YAW_RATE : 0|12@1+ (0.002132603,-2.094216146) [0|4095] "" XXX + SG_ YAW_OFFSET : 12|12@1+ (0.002132603,-0.130088783) [0|4095] "" XXX + +BO_ 547 ACCEL_SENSOR: 8 XXX + SG_ LONGITUDE : 0|12@1- (0.027126736,-21.592881856) [-2848|1247] "" XXX + SG_ LONGITUDE_OFFSET : 12|12@1+ (0.027126736,-21.592881856) [0|4095] "" XXX + SG_ LATERAL : 24|12@1- (0.027126736,-21.592881856) [-2848|1247] "" XXX + SG_ LATERAL_OFFSET : 36|12@1+ (0.027126736,-21.592881856) [0|4095] "" XXX + +BO_ 578 DRIVE_STATE: 8 XXX + SG_ WHEELSPEED_HR : 0|16@1+ (0.01706666666,-512) [0|65535] "" XXX + SG_ RAW_THROTTLE : 30|7@0+ (1,0) [0|127] "" XXX + SG_ BRAKE_PRESSED : 37|1@0+ (1,0) [0|1] "" XXX + SG_ GEAR : 40|3@1+ (1,0) [0|7] "" XXX + +BO_ 660 METER_CLUSTER: 8 XXX + SG_ FRONT_RIGHT_DOOR : 1|1@1+ (1,0) [0|511] "" XXX + SG_ FRONT_LEFT_DOOR : 3|1@0+ (1,0) [0|1] "" XXX + SG_ BACK_LEFT_DOOR : 5|1@1+ (1,0) [0|511] "" XXX + SG_ BACK_RIGHT_DOOR : 7|1@0+ (1,0) [0|1] "" XXX + SG_ SEATBELT_DRIVER : 17|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_PRESSED_ALT : 26|1@1+ (1,0) [0|1] "" XXX + SG_ COUNTER : 48|4@1+ (1,0) [0|255] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 665 IKEY: 3 XXX + SG_ IKEY_REVERT : 20|1@1+ (1,0) [0|1] "" XXX + SG_ IKEY_MOVE_OR_STEERING : 21|1@1+ (1,0) [0|1] "" XXX + SG_ IKEY_ENABLE : 22|1@1+ (1,0) [0|1] "" XXX + +BO_ 790 MPC_LKAS_CMD: 8 XXX + SG_ SETME_0x1 : 0|4@1+ (1,0) [0|3] "" XXX + SG_ LeftLane : 4|2@1+ (1,0) [0|3] "" XXX + SG_ Config : 6|2@1+ (1,0) [0|3] "" XXX + SG_ SETME2_0x1 : 8|2@1+ (1,0) [0|3] "" XXX + SG_ Keep_Hands_On_Wheel : 10|1@1+ (1,0) [0|1] "" XXX + SG_ MPCErr : 11|4@1+ (1,0) [0|1] "" XXX + SG_ SETME3_0x1 : 15|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_Output : 16|11@1- (1,0) [-1024|1023] "" XXX + SG_ LKASPrepare : 27|1@1+ (1,0) [0|1] "" XXX + SG_ LKAS_ACTIVE : 28|1@1+ (1,0) [0|3] "" XXX + SG_ TSRStatus : 29|3@1+ (1,0) [0|1] "" XXX + SG_ SETME4_0x1 : 32|2@1+ (1,0) [0|3] "" XXX + SG_ RightLane : 34|2@1+ (1,0) [0|3] "" XXX + SG_ LKAS_Mode : 36|3@1+ (1,0) [0|3] "" XXX + SG_ SETME_0x0 : 39|1@1+ (1,0) [0|3] "" XXX + SG_ TSRResult : 40|8@1+ (5,-5) [-5|1270] "" XXX + SG_ Unknow1 : 48|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 792 STEERING_TORQUE: 8 XXX + SG_ LKSPrepare : 0|1@1+ (1,0) [0|1] "" XXX + SG_ Cruise_Activated : 1|1@1+ (1,0) [0|1] "" XXX + SG_ TORQUE_FAILED : 2|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_1_1 : 3|1@1+ (1,0) [0|1] "" XXX + SG_ Steer_Warning : 4|1@1+ (1,0) [0|1] "" XXX + SG_ Steer_Error_1 : 5|1@1+ (1,0) [0|1] "" XXX + SG_ Steer_Error_2 : 6|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_1 : 7|1@1+ (1,0) [0|1] "" XXX + SG_ MAIN_TORQUE : 8|12@1- (1,0) [-2048|2047] "" XXX + SG_ SET_ME_1_2 : 20|1@1+ (1,0) [0|1] "" XXX + SG_ Keep_Hands_On_Wheel : 21|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_3 : 22|2@1+ (1,0) [0|15] "" XXX + SG_ Steer_Torque_Sensor : 24|12@1- (1,0) [-2048|2047] "" XXX + SG_ SET_ME_XF_1 : 36|4@1+ (1,0) [0|15] "" XXX + SG_ SET_ME_XFFF : 40|12@1+ (1,0) [0|4095] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 801 BRAKE_PEDAL_LEG: 8 XXX + SG_ BRAKE_PEDAL : 0|12@1+ (0.001,-0.278) [0|255] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 813 ACC_HUD_ADAS: 8 XXX + SG_ SET_SPEED : 0|9@1+ (0.5,0) [0|255.5] "" XXX + SG_ HAS_LEAD : 9|1@1+ (1,0) [0|1] "" XXX + SG_ SET_DISTANCE : 10|3@1+ (1,0) [0|7] "" XXX + SG_ LEAD_DISTANCE : 13|3@1+ (1,0) [0|7] "" XXX + SG_ AEB : 16|1@1+ (1,0) [0|1] "" XXX + SG_ FCW : 17|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_1 : 18|1@1+ (1,0) [0|1] "" XXX + SG_ ACC_ON2 : 19|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_CONTROLLABLE_AND_ON : 20|1@1+ (1,0) [0|1] "" XXX + SG_ OVERRIDE : 21|1@1+ (1,0) [0|1] "" XXX + SG_ ACC_ON1 : 22|1@0+ (1,0) [0|1] "" XXX + SG_ TOO_CLOSE : 23|1@0+ (1,0) [0|1] "" XXX + SG_ SET_ME_1_1 : 24|3@1+ (1,0) [0|1] "" XXX + SG_ NOTIFY : 27|5@1+ (1,0) [0|127] "" XXX + SG_ ERROR : 32|4@1+ (1,0) [0|15] "" XXX + SG_ SET_ME_XFF : 36|8@1+ (1,0) [0|4095] "" XXX + SG_ CRUISE_STATE : 44|4@1+ (1,0) [0|15] "" XXX + SG_ COUNTER : 48|4@1+ (1,0) [0|15] "" XXX + SG_ SET_ME_XF : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 814 ACC_CMD: 8 XXX + SG_ ACCEL_CMD : 0|8@1+ (0.05,-5) [-100|155] "m/s^2" XXX + SG_ ComfortBandUpper : 8|8@1+ (0.05,-5) [0|1.26] "m/s^2" XXX + SG_ ComfortBandLower : 16|8@1+ (0.05,-5) [0|1.26] "m/s^2" XXX + SG_ JerkUpperLimit : 24|7@1+ (0.2,0) [0|12.7] "m/s^3" XXX + SG_ SET_ME_1 : 31|1@0+ (1,0) [0|3] "" XXX + SG_ JerkLowerLimit : 32|7@1+ (0.2,-16) [0|12.7] "m/s^3" XXX + SG_ STANDSTILL_RESUME : 39|1@0+ (1,0) [0|1] "" XXX + SG_ STANDSTILL_STATE : 40|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_BEHAVIOR : 41|2@1+ (1,0) [0|3] "" XXX + SG_ ACC_REQ_NOT_STANDSTILL : 43|1@1+ (1,0) [0|1] "" XXX + SG_ ACC_CONTROLLABLE_AND_ON : 44|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_OVERRIDE_OR_STANDSTILL : 45|1@0+ (1,0) [0|1] "" XXX + SG_ ESP_BEHAVIOR : 46|2@1+ (1,0) [0|3] "" XXX + SG_ COUNTER : 48|4@1+ (1,0) [0|15] "" XXX + SG_ SET_ME_XF : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 815 ACC_AEB: 8 XXX + SG_ PAYLOAD : 0|48@1+ (1,0) [0|4.5035996273705e+15] "" XXX + SG_ COUNTER : 48|4@1+ (1,0) [0|15] "" XXX + SG_ SETME_0xF : 55|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 834 PEDAL: 8 XXX + SG_ GAS_PEDAL : 0|8@1+ (0.01,0) [0|2.55] "" XXX + SG_ BRAKE_PEDAL : 8|8@1+ (0.01,0) [0|2.55] "" XXX + SG_ STILL_GENERATOR : 19|1@1+ (1,0) [0|1] "" XXX + SG_ DRIVE_MODE : 20|2@1+ (1,0) [0|255] "" XXX + SG_ POWER_MODE : 28|2@1+ (1,0) [0|1] "" XXX + +BO_ 848 RADAR_UNUSED_0: 8 XXX + +BO_ 849 RADAR_TRACK_1: 8 XXX + SG_ LONG_DIST : 16|12@1- (0.06,-4) [-61.44|61.41] "m" XXX + SG_ LAT_DIST : 28|10@1+ (0.05,-25.6) [-15.36|15.33] "m" XXX + SG_ TRACK_ID : 38|6@1+ (1,0) [0|63] "" XXX + SG_ REL_SPEED : 44|10@1+ (1,-512) [-512|511] "km/s" XXX + SG_ SETME_0x3 : 54|2@1+ (1,0) [0|3] "" XXX + SG_ VALID : 56|1@0+ (1,0) [0|1] "" XXX + SG_ SETME_0x0 : 57|7@1+ (1,0) [0|127] "" XXX + +BO_ 850 RADAR_TRACK_2: 8 XXX + SG_ LONG_DIST : 16|12@1- (0.06,-4) [-61.44|61.41] "m" XXX + SG_ LAT_DIST : 28|10@1+ (-0.05,-25.6) [-15.36|15.33] "m" XXX + SG_ TRACK_ID : 38|6@1+ (1,0) [0|63] "" XXX + SG_ REL_SPEED : 44|10@1+ (1,-512) [-512|511] "km/s" XXX + SG_ SETME_0x3 : 54|2@1+ (1,0) [0|3] "" XXX + SG_ VALID : 56|1@0+ (1,0) [0|1] "" XXX + SG_ SETME_0x0 : 57|7@1+ (1,0) [0|127] "" XXX + +BO_ 851 RADAR_TRACK_3: 8 XXX + SG_ LONG_DIST : 16|12@1- (0.06,-4) [-61.44|61.41] "m" XXX + SG_ LAT_DIST : 28|10@1+ (0.05,-25.6) [-15.36|15.33] "m" XXX + SG_ TRACK_ID : 38|6@1+ (1,0) [0|63] "" XXX + SG_ REL_SPEED : 44|10@1+ (1,-512) [-512|511] "km/s" XXX + SG_ SETME_0x3 : 54|2@1+ (1,0) [0|3] "" XXX + SG_ VALID : 56|1@0+ (1,0) [0|1] "" XXX + SG_ SETME_0x0 : 57|7@1+ (1,0) [0|127] "" XXX + +BO_ 852 RADAR_TRACK_4: 8 XXX + SG_ LONG_DIST : 16|12@1- (0.06,-4) [-61.44|61.41] "m" XXX + SG_ LAT_DIST : 28|10@1+ (0.05,-25.6) [-15.36|15.33] "m" XXX + SG_ TRACK_ID : 38|6@1+ (1,0) [0|63] "" XXX + SG_ REL_SPEED : 44|10@1+ (1,-512) [-512|511] "km/s" XXX + SG_ SETME_0x3 : 54|2@1+ (1,0) [0|3] "" XXX + SG_ VALID : 56|1@0+ (1,0) [0|1] "" XXX + SG_ SETME_0x0 : 57|7@1+ (1,0) [0|127] "" XXX + +BO_ 884 RADAR_TRACK_COMBINE: 8 XXX + SG_ TRACK_ID : 0|3@1+ (1,0) [0|7] "bit" XXX + SG_ TargetType : 4|4@1+ (1,0) [0|8] "bit" XXX + SG_ LAT_DIST : 8|8@1+ (0.1,-12) [-100|100] "bit" XXX + SG_ LONG_DIST : 16|8@1+ (1,-100) [-100|100] "bit" XXX + SG_ REL_SPEED : 24|8@1+ (1,0) [0|254] "km/h" XXX + SG_ REL_SPEED_H : 32|8@1+ (1,0) [0|254] "km/h" XXX + SG_ VALID : 40|2@1+ (1,0) [0|3] "bit" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "bit" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "bit" XXX + +BO_ 926 GAUGE_SPEED: 8 XXX + SG_ GAUGE_SPEED : 8|8@1+ (1,0) [0|255] "" XXX + +BO_ 944 PCM_BUTTONS: 8 XXX + SG_ DISTANCE_BTN : 0|3@1+ (1,0) [0|7] "" XXX + SG_ BTN_ACC_DEC : 3|3@1+ (1,0) [0|7] "" XXX + SG_ SET_BTN : 6|1@0+ (1,0) [0|1] "" XXX + SG_ SET_ME_0_1 : 7|1@1+ (1,0) [0|1] "" XXX + SG_ ACC_ON_BTN : 8|1@0+ (1,0) [0|1] "" XXX + SG_ SET_ME_0 : 9|1@1+ (1,0) [0|1] "" XXX + SG_ RES_BTN : 10|1@0+ (1,0) [0|1] "" XXX + SG_ SET_ME_2 : 11|3@1+ (1,0) [0|7] "" XXX + SG_ LKAS_ON_BTN : 14|1@0+ (1,0) [0|1] "" XXX + SG_ BTN_DIST_DOWN : 15|1@1+ (1,0) [0|1] "" XXX + SG_ BTN_DIST_UP : 16|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_0_2 : 17|2@1+ (1,0) [0|1] "" XXX + SG_ ACC_SWITCH : 19|1@1+ (1,0) [0|1] "" XXX + SG_ SET_ME_XFFFFF : 20|32@1+ (1,0) [0|68719476735] "" XXX + SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX + +BO_ 1048 BSM: 8 XXX + SG_ LEFT_APPROACH : 9|2@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_APPROACH : 11|2@0+ (1,0) [0|1] "" XXX + SG_ APPROACH : 17|1@0+ (1,0) [0|1] "" XXX + +BO_ 1172 BRAKE_APPLIED_2: 8 XXX + SG_ BRAKE_APPLIED_ACTIVE_LOW : 47|1@1+ (1,0) [0|1] "" XXX + +VAL_ 307 TURN_SIGNAL_SWITCH 0 "Invalid" 1 "OFFGear" 2 "LeftSteeringShortLift" 3 "LeftSteeringLongLift" 4 "RightSteeringShortLift" 5 "RightSteeringLongLift" 7 "Unavaliable"; +VAL_ 508 TORQUE_TEMP_FAILED 2 "TooFast" 1 "TooLarge" 3 "Both"; +VAL_ 530 GEAR 4 "D" 2 "R" 3 "N" 1 "P"; +VAL_ 536 EPB_ACTIVE 0 "NotActive" 1 "Release" 2 "Apply" 3 "Reserved"; +VAL_ 536 EPB_STATUS 0 "Releasing" 1 "Released" 2 "Applying" 3 "Applied" 4 "FaultProhibitionOfRemoteDriving"; +VAL_ 578 GEAR 4 "D" 2 "R" 3 "N" 1 "P"; +VAL_ 790 Config 3 "FULL_TERRAIN" 2 "LKAS" 1 "ALERT" 0 "DISABLE"; +VAL_ 790 LKAS_Mode 0 "OFF" 1 "Passive" 2 "Active1" 3 "Active2" 4 "Fault" 5 "StandBy"; +VAL_ 813 SET_DISTANCE 4 "4bar" 3 "3bar" 2 "2bar" 1 "1bar"; +VAL_ 813 NOTIFY 4 "OVERRIDE" 3 "ENGAGE" 2 "DISENGAGE" 7 "ACC_ON" 8 "ACC_OFF" 0 "NONE"; +VAL_ 813 CRUISE_STATE 0 "OFF" 1 "Passive" 2 "StandBy" 3 "Active" 4 "BrakeOnly" 5 "Override" 6 "StandStillActive" 7 "StandStillWait" 8 "Failure" 9 "PermanentFailure"; +VAL_ 834 DRIVE_MODE 3 "NORMAL" 2 "SPORT" 1 "ECO"; +VAL_ 834 POWER_MODE 3 "HEV" 2 "FORCE_EV" 1 "EV"; +VAL_ 944 BTN_ACC_DEC 4 "ACC 10" 3 "ACC 1" 2 \ No newline at end of file diff --git a/opendbc_repo/opendbc/safety/declarations.h b/opendbc_repo/opendbc/safety/declarations.h index 66f1e2c..db081e7 100644 --- a/opendbc_repo/opendbc/safety/declarations.h +++ b/opendbc_repo/opendbc/safety/declarations.h @@ -34,6 +34,7 @@ #define SAFETY_PSA 31U #define SAFETY_RIVIAN 33U #define SAFETY_VOLKSWAGEN_MEB 34U +#define SAFETY_BYD 35U #define GET_BIT(msg, b) ((bool)!!(((msg)->data[((b) / 8U)] >> ((b) % 8U)) & 0x1U)) #define GET_FLAG(value, mask) (((value) & (mask)) == (mask)) @@ -368,3 +369,4 @@ extern const safety_hooks volkswagen_meb_hooks; extern const safety_hooks volkswagen_pq_hooks; extern const safety_hooks rivian_hooks; extern const safety_hooks psa_hooks; +extern const safety_hooks byd_hooks; diff --git a/opendbc_repo/opendbc/safety/modes/byd.h b/opendbc_repo/opendbc/safety/modes/byd.h new file mode 100644 index 0000000..e2a3389 --- /dev/null +++ b/opendbc_repo/opendbc/safety/modes/byd.h @@ -0,0 +1,145 @@ +#include "opendbc/safety/declarations.h" + +// MPC_LKAS_CMD (790), little endian +#define BYD_GET_LKAS_OUTPUT(msg) (to_signed((((msg)->data[3] & 0x7U) << 8U) | (msg)->data[2], 11)) +#define BYD_LKAS_ACTIVE_BIT 28U +// STEERING_TORQUE (792), little endian +#define BYD_GET_MAIN_TORQUE(msg) (to_signed((((msg)->data[2] & 0xFU) << 8U) | (msg)->data[1], 12)) +#define BYD_GET_DRIVER_TORQUE(msg) (to_signed((((msg)->data[4] & 0xFU) << 8U) | (msg)->data[3], 12)) +// PCM_BUTTONS (944) +#define BYD_LKAS_ON_BTN_BIT 14U + +static bool byd_tx_hook(const CANPacket_t *msg) { + const TorqueSteeringLimits BYD_STEERING_LIMITS = { + .max_torque = 300, + .max_rate_up = 6, + .max_rate_down = 6, + .max_rt_delta = 112, // 75 per 250ms at 50Hz steer rate, plus 50% buffer + .type = TorqueMotorLimited, + .driver_torque_allowance = 46, + .driver_torque_multiplier = 1, + .max_torque_error = 46, + }; + + const LongitudinalLimits BYD_LONG_LIMITS = { + .max_accel = 140, // 2.0 m/s^2 (raw = (m/s^2 + 5) / 0.05) + .min_accel = 20, // -4.0 m/s^2 + .inactive_accel = 100, // 0 m/s^2, sent when longitudinal control is inactive + }; + + bool tx = true; + + // MPC_LKAS_CMD: steering actuation towards the EPS + if (msg->addr == 790U) { + int desired_torque = BYD_GET_LKAS_OUTPUT(msg); + bool steer_req = GET_BIT(msg, BYD_LKAS_ACTIVE_BIT); + + if (steer_torque_cmd_checks(desired_torque, steer_req, BYD_STEERING_LIMITS)) { + tx = false; + } + } + + // STEERING_TORQUE: feedback message towards the camera, torque echoes the + // camera's own request. Only sanity-check the absolute limit so stock LKAS + // requests don't fault when openpilot is inactive. + if (msg->addr == 792U) { + int main_torque = BYD_GET_MAIN_TORQUE(msg); + if (safety_max_limit_check(main_torque, BYD_STEERING_LIMITS.max_torque, -BYD_STEERING_LIMITS.max_torque)) { + tx = false; + } + } + + // ACC_CMD: longitudinal actuation + if (msg->addr == 814U) { + int desired_accel = msg->data[0]; + + if (longitudinal_accel_checks(desired_accel, BYD_LONG_LIMITS)) { + tx = false; + } + } + + return tx; +} + +static void byd_rx_hook(const CANPacket_t *msg) { + // Main Bus + if (msg->bus == 0U) { + // PEDAL + if (msg->addr == 834U) { + gas_pressed = msg->data[0] != 0U; + brake_pressed = msg->data[1] != 0U; + } + // ESC + if (msg->addr == 289U) { + vehicle_moving = (((msg->data[1] & 0x0FU) << 8U) | msg->data[0]) != 0U; + } + // STEERING_TORQUE: sample EPS output torque and driver torque + if (msg->addr == 792U) { + int torque_meas_new = BYD_GET_MAIN_TORQUE(msg); + update_sample(&torque_meas, torque_meas_new); + torque_meas.min--; + torque_meas.max++; + + int torque_driver_new = BYD_GET_DRIVER_TORQUE(msg); + update_sample(&torque_driver, torque_driver_new); + } + // PCM_BUTTONS: dedicated LKAS steering wheel button for MADS + if (msg->addr == 944U) { + mads_button_press = GET_BIT(msg, BYD_LKAS_ON_BTN_BIT) ? MADS_BUTTON_PRESSED : MADS_BUTTON_NOT_PRESSED; + } + } + // Cam Bus + else if (msg->bus == 2U) { + // ACC_HUD_ADAS + if (msg->addr == 813U) { + // CRUISE_STATE in (3, 5) == (Active, Override) + uint8_t cruise_state = (msg->data[5] >> 4U) & 0x0FU; + pcm_cruise_check((cruise_state == 3U) || (cruise_state == 5U)); + } + } else { + } +} + +static safety_config byd_init(uint16_t param) { + static const CanMsg BYD_TX_MSGS[] = { + {790, 0, 8, .check_relay = true}, // MPC_LKAS_CMD + {792, 2, 8, .check_relay = true}, // STEERING_TORQUE + }; + + static const CanMsg BYD_TX_LONG_MSGS[] = { + {790, 0, 8, .check_relay = true}, // MPC_LKAS_CMD + {792, 2, 8, .check_relay = true}, // STEERING_TORQUE + {814, 0, 8, .check_relay = true}, // ACC_CMD + }; + + static RxCheck byd_rx_checks[] = { + {.msg = {{578, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DRIVE_STATE + {.msg = {{544, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // BRAKE_APPLIED + {.msg = {{834, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PEDAL + {.msg = {{289, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ESC + {.msg = {{287, 0, 5, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEER_MODULE + {.msg = {{792, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEERING_TORQUE + {.msg = {{944, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PCM_BUTTONS + {.msg = {{813, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACC_HUD_ADAS + {.msg = {{307, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STALKS + {.msg = {{660, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // METER_CLUSTER + {.msg = {{790, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // MPC_LKAS_CMD + }; + + const uint16_t BYD_FLAG_LONG_CONTROL = 1; + const bool byd_longitudinal = GET_FLAG(param, BYD_FLAG_LONG_CONTROL); + + safety_config ret; + if (byd_longitudinal) { + ret = BUILD_SAFETY_CFG(byd_rx_checks, BYD_TX_LONG_MSGS); + } else { + ret = BUILD_SAFETY_CFG(byd_rx_checks, BYD_TX_MSGS); + } + return ret; +} + +const safety_hooks byd_hooks = { + .init = byd_init, + .rx = byd_rx_hook, + .tx = byd_tx_hook, +}; diff --git a/opendbc_repo/opendbc/safety/safety.h b/opendbc_repo/opendbc/safety/safety.h index 7a5dfb4..77e08df 100644 --- a/opendbc_repo/opendbc/safety/safety.h +++ b/opendbc_repo/opendbc/safety/safety.h @@ -29,6 +29,7 @@ #include "opendbc/safety/modes/body.h" #include "opendbc/safety/modes/psa.h" #include "opendbc/safety/modes/hyundai_canfd.h" +#include "opendbc/safety/modes/byd.h" uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len) { uint32_t ret = 0U; @@ -419,6 +420,7 @@ int set_safety_hooks(uint16_t mode, uint16_t param) { {SAFETY_RIVIAN, &rivian_hooks}, {SAFETY_TESLA, &tesla_hooks}, {SAFETY_HYUNDAI_CANFD, &hyundai_canfd_hooks}, + {SAFETY_BYD, &byd_hooks}, #ifdef ALLOW_DEBUG {SAFETY_CHRYSLER_CUSW, &chrysler_cusw_hooks}, {SAFETY_PSA, &psa_hooks}, diff --git a/opendbc_repo/opendbc/safety/tests/common.py b/opendbc_repo/opendbc/safety/tests/common.py index 90a0df3..7cd77de 100644 --- a/opendbc_repo/opendbc/safety/tests/common.py +++ b/opendbc_repo/opendbc/safety/tests/common.py @@ -1017,6 +1017,8 @@ class SafetyTest(SafetyTestBase): continue if attr.startswith('TestFord') and current_test.startswith('TestFord'): continue + if attr.startswith('TestByd') and current_test.startswith('TestByd'): + continue if attr.startswith('TestHyundaiCanfd') and current_test.startswith('TestHyundaiCanfd'): continue if {attr, current_test}.issubset({'TestHyundaiLongitudinalSafety', 'TestHyundaiLongitudinalSafetyCameraSCC', 'TestHyundaiSafetyFCEVLong'}): diff --git a/opendbc_repo/opendbc/safety/tests/test_byd.py b/opendbc_repo/opendbc/safety/tests/test_byd.py new file mode 100644 index 0000000..9548ae9 --- /dev/null +++ b/opendbc_repo/opendbc/safety/tests/test_byd.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +import unittest + +from opendbc.car.structs import CarParams +from opendbc.safety.tests.libsafety import libsafety_py +import opendbc.safety.tests.common as common +from opendbc.safety.tests.common import CANPackerSafety + +MSG_MPC_LKAS_CMD = 790 # TX by OP, lateral actuation towards EPS +MSG_STEERING_TORQUE = 792 # RX from EPS / TX by OP, torque feedback towards camera +MSG_ACC_HUD_ADAS = 813 # RX from camera, ACC state +MSG_ACC_CMD = 814 # TX by OP, longitudinal actuation +MSG_PCM_BUTTONS = 944 # RX from PCM, steering wheel buttons + + +def byd_checksum(msg): + addr, dat, bus = msg + ret = bytearray(dat) + + if addr in (MSG_MPC_LKAS_CMD, MSG_STEERING_TORQUE, MSG_ACC_CMD): + byte_key = 0xAF + sum_first = sum(byte >> 4 for byte in ret) # upper nibbles + sum_second = sum(byte & 0xF for byte in ret) # lower nibbles + + remainder = sum_second >> 4 + + sum_first += (byte_key & 0xF) + sum_second += (byte_key >> 4) + + inv_first = ((-sum_first + 0x9) & 0xF) + inv_second = ((-sum_second + 0x9) & 0xF) + + ret[7] = (((inv_first + (5 - remainder)) << 4) + inv_second) & 0xFF + + return addr, ret, bus + + +class TestBydSafetyBase(common.CarSafetyTest, common.MotorTorqueSteeringSafetyTest): + TX_MSGS = [[MSG_MPC_LKAS_CMD, 0], [MSG_STEERING_TORQUE, 2]] + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS = {0: (MSG_MPC_LKAS_CMD,), 2: (MSG_STEERING_TORQUE,)} + FWD_BLACKLISTED_ADDRS = {0: [MSG_STEERING_TORQUE], 2: [MSG_MPC_LKAS_CMD]} + + MAX_TORQUE_LOOKUP = ([0], [300]) + MAX_RATE_UP = 6 + MAX_RATE_DOWN = 6 + MAX_RT_DELTA = 112 + MAX_TORQUE_ERROR = 46 + TORQUE_MEAS_TOLERANCE = 1 + + def setUp(self): + self.packer = CANPackerSafety("byd_common") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.byd, 0) + self.safety.init_tests() + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"LKAS_Output": torque, "LKAS_ACTIVE": steer_req} + return self.packer.make_can_msg_safety("MPC_LKAS_CMD", 0, values, byd_checksum) + + def _torque_meas_msg(self, torque): + values = {"MAIN_TORQUE": torque} + return self.packer.make_can_msg_safety("STEERING_TORQUE", 0, values) + + def _torque_driver_msg(self, torque): + values = {"Steer_Torque_Sensor": torque} + return self.packer.make_can_msg_safety("STEERING_TORQUE", 0, values) + + def _speed_msg(self, speed): + values = {"VEHICLE_SPEED": speed} + return self.packer.make_can_msg_safety("ESC", 0, values) + + def _user_brake_msg(self, brake): + values = {"BRAKE_PEDAL": brake} + return self.packer.make_can_msg_safety("PEDAL", 0, values) + + def _user_gas_msg(self, gas): + values = {"GAS_PEDAL": gas} + return self.packer.make_can_msg_safety("PEDAL", 0, values) + + def _pcm_status_msg(self, enable): + # CRUISE_STATE: 3 == Active, 0 == Off + values = {"CRUISE_STATE": 3 if enable else 0} + return self.packer.make_can_msg_safety("ACC_HUD_ADAS", 2, values) + + def _lkas_button_msg(self, enabled): + values = {"LKAS_ON_BTN": enabled} + return self.packer.make_can_msg_safety("PCM_BUTTONS", 0, values) + + def _acc_state_msg(self, enable): + # BYD has no separate ACC main switch state in panda; MADS is toggled + # with the dedicated LKAS steering wheel button instead + raise NotImplementedError + + +class TestBydStockSafety(TestBydSafetyBase): + pass + + +class TestBydLongitudinalSafety(TestBydSafetyBase): + TX_MSGS = [[MSG_MPC_LKAS_CMD, 0], [MSG_STEERING_TORQUE, 2], [MSG_ACC_CMD, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (MSG_MPC_LKAS_CMD, MSG_ACC_CMD), 2: (MSG_STEERING_TORQUE,)} + FWD_BLACKLISTED_ADDRS = {0: [MSG_STEERING_TORQUE], 2: [MSG_MPC_LKAS_CMD, MSG_ACC_CMD]} + + MIN_ACCEL = 20 # -4.0 m/s^2, raw = (m/s^2 + 5) / 0.05 + MAX_ACCEL = 140 # 2.0 m/s^2 + INACTIVE_ACCEL = 100 # 0 m/s^2 + + def setUp(self): + self.packer = CANPackerSafety("byd_common") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.byd, 1) # BydSafetyFlags.LONG_CONTROL + self.safety.init_tests() + + def _accel_msg(self, accel: float): + # accel in m/s^2, ACCEL_CMD raw = (m/s^2 + 5) / 0.05 + values = {"ACCEL_CMD": accel} + return self.packer.make_can_msg_safety("ACC_CMD", 0, values, byd_checksum) + + def test_accel_actuation_limits(self): + self.safety.set_controls_allowed(True) + for accel in [self.MIN_ACCEL - 1, self.MIN_ACCEL, 100, self.MAX_ACCEL, self.MAX_ACCEL + 1]: + send = self.MIN_ACCEL <= accel <= self.MAX_ACCEL + self.assertEqual(send, self._tx(self._accel_raw_msg(accel)), f"{accel=}") + + def _accel_raw_msg(self, raw: int): + msg = self._accel_msg(0) + msg[0].data[0] = raw & 0xFF + return msg + + def test_accel_when_not_allowed(self): + # when controls are not allowed, only 0 m/s^2 (inactive) may be sent + self.safety.set_controls_allowed(False) + self.assertTrue(self._tx(self._accel_raw_msg(self.INACTIVE_ACCEL))) + self.assertFalse(self._tx(self._accel_raw_msg(self.INACTIVE_ACCEL + 5))) + self.assertFalse(self._tx(self._accel_raw_msg(self.INACTIVE_ACCEL - 5))) + + +if __name__ == "__main__": + unittest.main() diff --git a/opendbc_repo/opendbc/sunnypilot/car/byd/__init__.py b/opendbc_repo/opendbc/sunnypilot/car/byd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/opendbc_repo/opendbc/sunnypilot/car/byd/mads.py b/opendbc_repo/opendbc/sunnypilot/car/byd/mads.py new file mode 100644 index 0000000..2d346d5 --- /dev/null +++ b/opendbc_repo/opendbc/sunnypilot/car/byd/mads.py @@ -0,0 +1,24 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" + +from enum import StrEnum + +from opendbc.car import Bus, structs + +from opendbc.sunnypilot.mads_base import MadsCarStateBase +from opendbc.can.parser import CANParser + + +class MadsCarState(MadsCarStateBase): + def __init__(self, CP: structs.CarParams, CP_SP: structs.CarParamsSP): + super().__init__(CP, CP_SP) + + def update_mads(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None: + cp = can_parsers[Bus.pt] + + self.prev_lkas_button = self.lkas_button + self.lkas_button = cp.vl["PCM_BUTTONS"]["LKAS_ON_BTN"] diff --git a/openpilot/sunnypilot/mads/mads.py b/openpilot/sunnypilot/mads/mads.py index 4ae3534..d519e1f 100644 --- a/openpilot/sunnypilot/mads/mads.py +++ b/openpilot/sunnypilot/mads/mads.py @@ -47,6 +47,9 @@ class ModularAssistiveDrivingSystem: self.allow_always = True if self.CP.brand == "tesla": self.allow_always = True + if self.CP.brand == "byd": + # BYD has a dedicated LKAS steering wheel button + self.allow_always = True if self.CP.brand in MADS_NO_ACC_MAIN_BUTTON: self.no_main_cruise = True diff --git a/panda/SConscript b/panda/SConscript new file mode 100644 index 0000000..de77092 --- /dev/null +++ b/panda/SConscript @@ -0,0 +1,176 @@ +import os +import hashlib +import opendbc +import subprocess + +PREFIX = "arm-none-eabi-" +BUILDER = "DEV" + +common_flags = [] + +if os.getenv("RELEASE"): + BUILD_TYPE = "RELEASE" + cert_fn = os.getenv("CERT") + assert cert_fn is not None, 'No certificate file specified. Please set CERT env variable' + assert os.path.exists(cert_fn), 'Certificate file not found. Please specify absolute path' +else: + BUILD_TYPE = "DEBUG" + cert_fn = File("./board/certs/debug").srcnode().relpath + common_flags += ["-DALLOW_DEBUG"] + + if os.getenv("DEBUG"): + common_flags += ["-DDEBUG"] + +def objcopy(source, target, env, for_signature): + return '$OBJCOPY -O binary %s %s' % (source[0], target[0]) + +def get_version(builder, build_type): + try: + git = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"], encoding='utf8').strip() + except subprocess.CalledProcessError: + git = "unknown" + return f"{builder}-{git}-{build_type}" + +def get_key_header(name): + from Crypto.PublicKey import RSA + + public_fn = File(f'./board/certs/{name}.pub').srcnode().get_path() + with open(public_fn) as f: + rsa = RSA.importKey(f.read()) + assert(rsa.size_in_bits() == 1024) + + rr = pow(2**1024, 2, rsa.n) + n0inv = 2**32 - pow(rsa.n, -1, 2**32) + + r = [ + f"RSAPublicKey {name}_rsa_key = {{", + f" .len = 0x20,", + f" .n0inv = {n0inv}U,", + f" .n = {to_c_uint32(rsa.n)},", + f" .rr = {to_c_uint32(rr)},", + f" .exponent = {rsa.e},", + f"}};", + ] + return r + +def to_c_uint32(x): + nums = [] + for _ in range(0x20): + nums.append(x % (2**32)) + x //= (2**32) + return "{" + 'U,'.join(map(str, nums)) + "U}" + + +def build_project(project_name, project, main, extra_flags): + project_dir = Dir(f'./board/obj/{project_name}/') + + flags = project["FLAGS"] + extra_flags + common_flags + [ + "-Wall", + "-Wextra", + "-Wstrict-prototypes", + "-Werror", + "-mlittle-endian", + "-mthumb", + "-nostdlib", + "-fno-builtin", + "-std=gnu11", + "-fmax-errors=1", + f"-T{File(project['LINKER_SCRIPT']).srcnode().relpath}", + "-fsingle-precision-constant", + "-Os", + "-g", + ] + + env = Environment( + ENV=os.environ, + CC=PREFIX + 'gcc', + AS=PREFIX + 'gcc', + OBJCOPY=PREFIX + 'objcopy', + OBJDUMP=PREFIX + 'objdump', + OBJPREFIX=project_dir, + CFLAGS=flags, + ASFLAGS=flags, + LINKFLAGS=flags, + CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.INCLUDE_PATH], + ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES", + BUILDERS={ + 'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf') + }, + tools=["default", "compilation_db"], + ) + + startup = env.Object(project["STARTUP_FILE"]) + + # Build bootstub + bs_env = env.Clone() + bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB") + bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [ + startup, + "./board/crypto/rsa.c", + "./board/crypto/sha.c", + "./board/bootstub.c", + ]) + bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf) + + # Build + sign main (aka app) + main_elf = env.Program(f"{project_dir}/main.elf", [ + startup, + main + ], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) + main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf) + sign_py = File(f"./board/crypto/sign.py").srcnode().relpath + env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}") + + + +base_project_h7 = { + "STARTUP_FILE": "./board/stm32h7/startup_stm32h7x5xx.s", + "LINKER_SCRIPT": "./board/stm32h7/stm32h7x5_flash.ld", + "APP_START_ADDRESS": "0x8020000", + "FLAGS": [ + "-mcpu=cortex-m7", + "-mhard-float", + "-DSTM32H7", + "-DSTM32H725xx", + "-Iboard/stm32h7/inc", + "-mfpu=fpv5-d16", + ], +} + +# Common autogenerated includes +with open("board/obj/gitversion.h", "w") as f: + version = get_version(BUILDER, BUILD_TYPE) + f.write(f'extern const uint8_t gitversion[{len(version)+1}];\n') + f.write(f'const uint8_t gitversion[{len(version)+1}] = "{version}";\n') + +with open("board/obj/version", "w") as f: + f.write(f'{get_version(BUILDER, BUILD_TYPE)}') + +certs = [get_key_header(n) for n in ["debug", "release"]] +with open("board/obj/cert.h", "w") as f: + for cert in certs: + f.write("\n".join(cert) + "\n") + +# Packet version defines: SHA hash of the struct header files +def version_hash(path): + with open(path, "rb") as f: + # Normalize line endings on Windows + return int.from_bytes(hashlib.sha256(f.read().replace(b'\r', b'')).digest()[:4], 'little') +hh, ch, jh = version_hash("board/health.h"), version_hash(os.path.join(opendbc.INCLUDE_PATH, "opendbc/safety/can.h")), version_hash("board/jungle/jungle_health.h") +common_flags += [f"-DHEALTH_PACKET_VERSION=0x{hh:08X}U", f"-DCAN_PACKET_VERSION_HASH=0x{ch:08X}U", + f"-DJUNGLE_HEALTH_PACKET_VERSION=0x{jh:08X}U"] + +# panda fw +build_project("panda_h7", base_project_h7, "./board/main.c", []) + +# panda jungle fw +flags = [ + "-DPANDA_JUNGLE", +] +build_project("panda_jungle_h7", base_project_h7, "./board/jungle/main.c", flags) + +# body fw +build_project("body_h7", base_project_h7, "./board/body/main.c", ["-DPANDA_BODY"]) + +# test files +SConscript('tests/libpanda/SConscript') diff --git a/panda/SConstruct b/panda/SConstruct new file mode 100644 index 0000000..6e40beb --- /dev/null +++ b/panda/SConstruct @@ -0,0 +1,13 @@ +import os + +env = Environment( + COMPILATIONDB_USE_ABSPATH=True, + tools=["default", "compilation_db"], +) + +SetOption('num_jobs', max(1, int((os.cpu_count() or 1)-1))) + +env.CompilationDatabase("compile_commands.json") + +# panda fw & test files +SConscript('SConscript') diff --git a/panda/board/obj/body_h7.bin.signed b/panda/board/obj/body_h7.bin.signed index 3288847..eda2dbb 100644 Binary files a/panda/board/obj/body_h7.bin.signed and b/panda/board/obj/body_h7.bin.signed differ diff --git a/panda/board/obj/body_h7/bootstub.elf b/panda/board/obj/body_h7/bootstub.elf index 298d989..618bf1a 100755 Binary files a/panda/board/obj/body_h7/bootstub.elf and b/panda/board/obj/body_h7/bootstub.elf differ diff --git a/panda/board/obj/body_h7/main.bin b/panda/board/obj/body_h7/main.bin index 5647aa3..991ed8b 100755 Binary files a/panda/board/obj/body_h7/main.bin and b/panda/board/obj/body_h7/main.bin differ diff --git a/panda/board/obj/body_h7/main.elf b/panda/board/obj/body_h7/main.elf index f8257cc..478e118 100755 Binary files a/panda/board/obj/body_h7/main.elf and b/panda/board/obj/body_h7/main.elf differ diff --git a/panda/board/obj/bootstub.body_h7.bin b/panda/board/obj/bootstub.body_h7.bin index aa2d3a0..6c30fb2 100755 Binary files a/panda/board/obj/bootstub.body_h7.bin and b/panda/board/obj/bootstub.body_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin index b4fe5e7..92f70b0 100755 Binary files a/panda/board/obj/bootstub.panda_h7.bin and b/panda/board/obj/bootstub.panda_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_jungle_h7.bin b/panda/board/obj/bootstub.panda_jungle_h7.bin index b2e3184..a63cbb8 100755 Binary files a/panda/board/obj/bootstub.panda_jungle_h7.bin and b/panda/board/obj/bootstub.panda_jungle_h7.bin differ diff --git a/panda/board/obj/gitversion.h b/panda/board/obj/gitversion.h index 1c43694..14f9e0f 100755 --- a/panda/board/obj/gitversion.h +++ b/panda/board/obj/gitversion.h @@ -1,2 +1,2 @@ -extern const uint8_t gitversion[18]; -const uint8_t gitversion[18] = "DEV-unknown-DEBUG"; +extern const uint8_t gitversion[19]; +const uint8_t gitversion[19] = "DEV-de77c89c-DEBUG"; diff --git a/panda/board/obj/panda_h7.bin.signed b/panda/board/obj/panda_h7.bin.signed index fa5e524..905296b 100644 Binary files a/panda/board/obj/panda_h7.bin.signed and b/panda/board/obj/panda_h7.bin.signed differ diff --git a/panda/board/obj/panda_h7/bootstub.elf b/panda/board/obj/panda_h7/bootstub.elf index eee2d1c..e18e9ac 100755 Binary files a/panda/board/obj/panda_h7/bootstub.elf and b/panda/board/obj/panda_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_h7/main.bin b/panda/board/obj/panda_h7/main.bin index d7c3121..5575cf9 100755 Binary files a/panda/board/obj/panda_h7/main.bin and b/panda/board/obj/panda_h7/main.bin differ diff --git a/panda/board/obj/panda_h7/main.elf b/panda/board/obj/panda_h7/main.elf index 51c8222..dfd68f9 100755 Binary files a/panda/board/obj/panda_h7/main.elf and b/panda/board/obj/panda_h7/main.elf differ diff --git a/panda/board/obj/panda_jungle_h7.bin.signed b/panda/board/obj/panda_jungle_h7.bin.signed index 5c601a5..83de861 100644 Binary files a/panda/board/obj/panda_jungle_h7.bin.signed and b/panda/board/obj/panda_jungle_h7.bin.signed differ diff --git a/panda/board/obj/panda_jungle_h7/bootstub.elf b/panda/board/obj/panda_jungle_h7/bootstub.elf index c52327d..08cac8f 100755 Binary files a/panda/board/obj/panda_jungle_h7/bootstub.elf and b/panda/board/obj/panda_jungle_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_jungle_h7/main.bin b/panda/board/obj/panda_jungle_h7/main.bin index a4576d5..3ffd2c0 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.bin and b/panda/board/obj/panda_jungle_h7/main.bin differ diff --git a/panda/board/obj/panda_jungle_h7/main.elf b/panda/board/obj/panda_jungle_h7/main.elf index 36323fa..8b78e31 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.elf and b/panda/board/obj/panda_jungle_h7/main.elf differ diff --git a/panda/board/obj/version b/panda/board/obj/version index 230584a..389996d 100755 --- a/panda/board/obj/version +++ b/panda/board/obj/version @@ -1 +1 @@ -DEV-unknown-DEBUG \ No newline at end of file +DEV-de77c89c-DEBUG \ No newline at end of file diff --git a/panda/pandacan.egg-info/SOURCES.txt b/panda/pandacan.egg-info/SOURCES.txt index 8dda7a8..1f173ed 100644 --- a/panda/pandacan.egg-info/SOURCES.txt +++ b/panda/pandacan.egg-info/SOURCES.txt @@ -1,6 +1,7 @@ LICENSE LICENSE.md README.md +__init__.py pyproject.toml ./__init__.py ./board/__init__.py @@ -22,8 +23,27 @@ pyproject.toml ./python/spi.py ./python/usb.py ./python/utils.py +board/__init__.py +board/flash.py +board/health.h +board/recover.py +board/body/__init__.py +board/body/flash.py +board/jungle/__init__.py +board/jungle/flash.py +board/jungle/jungle_health.h +board/jungle/recover.py pandacan.egg-info/PKG-INFO pandacan.egg-info/SOURCES.txt pandacan.egg-info/dependency_links.txt pandacan.egg-info/requires.txt -pandacan.egg-info/top_level.txt \ No newline at end of file +pandacan.egg-info/top_level.txt +python/__init__.py +python/base.py +python/constants.py +python/dfu.py +python/serial.py +python/socketpanda.py +python/spi.py +python/usb.py +python/utils.py \ No newline at end of file diff --git a/panda/tests/libpanda/SConscript b/panda/tests/libpanda/SConscript new file mode 100644 index 0000000..1b82b0e --- /dev/null +++ b/panda/tests/libpanda/SConscript @@ -0,0 +1,15 @@ +import opendbc + +env = Environment( + CFLAGS=[ + '-nostdlib', + '-fno-builtin', + '-std=gnu11', + '-Wfatal-errors', + '-Wno-pointer-to-int-cast', + ], + CPPPATH=[".", "../../", "../../board/", opendbc.INCLUDE_PATH], +) + +panda = env.SharedObject("panda.os", "panda.c") +libpanda = env.SharedLibrary("libpanda.so", [panda]) diff --git a/panda/tests/libpanda/libpanda.so b/panda/tests/libpanda/libpanda.so index 20201d8..8807e78 100755 Binary files a/panda/tests/libpanda/libpanda.so and b/panda/tests/libpanda/libpanda.so differ