byd: add BYD car port (HAN EV 23 / Seal Performance 2025) with MADS support

Car port adapted from HotIce0/openpilot develop_byd_comma4 to sunnypilot's
opendbc API (CP_SP signatures, (ret, ret_sp) carstate returns):

- opendbc/car/byd/: interface, carstate, carcontroller, bydcan, values,
  fingerprints (BYD_SEAL_PERFORMANCE_25 with full 91-msg fingerprint)
- opendbc/dbc/byd_common.dbc
- Brand registration: car/values.py, car/fingerprints.py (+MIGRATION),
  car.capnp SafetyModel byd @35, torque_data/params.toml
- safety/modes/byd.h: SAFETY_BYD 35U with real TX checks (steer torque
  cmd checks, longitudinal accel checks), MADS LKAS button from
  PCM_BUTTONS (944), pcm_cruise_check, ESC vehicle_moving. MISRA clean.
- safety/tests/test_byd.py: 120 tests (stock + longitudinal variants)
- opendbc/sunnypilot/car/byd/mads.py: MadsCarState tracking the
  dedicated LKAS steering wheel button
- sunnypilot/mads/mads.py: allow_always for byd (dedicated LKAS button)
- panda: restore SConstruct/SConscript build system, rebuild
  panda_h7.bin.signed (TIZI) with SAFETY_BYD + MADS lateral gating

Known limitation: BYD_HAN_EV_23 fingerprint is a subset of the Seal's,
so HAN requires FINGERPRINT=BYD_HAN_EV_23 env var (live fingerprint
cannot distinguish it; Seal fingerprints natively).
This commit is contained in:
Quintine committed 2026-07-25 15:09:00 +10:00
1 parent de77c89cf6
commit c8903e592c
44 files changed
+1480 -5

No files matched your search

+11
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
# byd
Whitespace-only changes.
+161
View File
@@ -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)
@@ -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
+125
View File
@@ -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),
}
@@ -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
}],
}
+49
View File
@@ -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
+78
View File
@@ -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()
+1
View File
@@ -634,6 +634,7 @@ struct CarParams {
fcaGiorgio @32;
rivian @33;
volkswagenMeb @34;
byd @35;
}
enum SteerControlType {
+3
View File
@@ -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,
@@ -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]
+2 -1
View File
@@ -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}
+375
View File
@@ -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
@@ -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;
+145
View File
@@ -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,
};
+2
View File
@@ -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},
@@ -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'}):
@@ -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()
Whitespace-only changes.
@@ -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"]
+3
View File
@@ -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
+176
View File
@@ -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')
+13
View File
@@ -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')
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -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";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
DEV-unknown-DEBUG
DEV-de77c89c-DEBUG
+21 -1
View File
@@ -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
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
+15
View File
@@ -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])
Binary file not shown.