diff --git a/panda/board/drivers/can_common.h b/panda/board/drivers/can_common.h index bc9adde..ed611b5 100644 --- a/panda/board/drivers/can_common.h +++ b/panda/board/drivers/can_common.h @@ -227,6 +227,13 @@ void ignition_can_hook(CANPacket_t *to_push) { ignition_can_cnt = 0U; } + } else if (bus == 2) { + // GM exception, SDGM cars have this message on bus 2 + if ((addr == 0x1F1) && (len == 8)) { + // SystemPowerMode (2=Run, 3=Crank Request) + ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U; + ignition_can_cnt = 0U; + } } } diff --git a/panda/board/safety/safety_gm.h b/panda/board/safety/safety_gm.h index ed3e48c..055e7d5 100644 --- a/panda/board/safety/safety_gm.h +++ b/panda/board/safety/safety_gm.h @@ -43,6 +43,9 @@ const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, / const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, // pt bus {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus +const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus + {0x184, 2, 8}}; // camera bus + const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus {0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus @@ -50,18 +53,21 @@ const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus RxCheck gm_rx_checks[] = { {.msg = {{0x184, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, {.msg = {{0x34A, 0, 5, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0x1E1, 0, 7, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0xF1, 0, 6, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{0x1E1, 0, 7, .frequency = 10U}, // Non-SDGM Car + {0x1E1, 2, 7, .frequency = 100000U}}}, // SDGM Car + {.msg = {{0xF1, 0, 6, .frequency = 10U}, // Non-SDGM Car + {0xF1, 2, 6, .frequency = 100000U}}}, // SDGM Car {.msg = {{0x1C4, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, {.msg = {{0xC9, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, }; const uint16_t GM_PARAM_HW_CAM = 1; const uint16_t GM_PARAM_HW_CAM_LONG = 2; -const uint16_t GM_PARAM_CC_LONG = 4; -const uint16_t GM_PARAM_HW_ASCM_LONG = 8; -const uint16_t GM_PARAM_NO_CAMERA = 16; -const uint16_t GM_PARAM_NO_ACC = 32; +const uint16_t GM_PARAM_HW_SDGM = 4; +const uint16_t GM_PARAM_CC_LONG = 8; +const uint16_t GM_PARAM_HW_ASCM_LONG = 16; +const uint16_t GM_PARAM_NO_CAMERA = 32; +const uint16_t GM_PARAM_NO_ACC = 64; const uint16_t GM_PARAM_PEDAL_LONG = 128; // TODO: this can be inferred const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 256; @@ -72,7 +78,12 @@ enum { GM_BTN_CANCEL = 6, }; -enum {GM_ASCM, GM_CAM} gm_hw = GM_ASCM; +typedef enum { + GM_ASCM, + GM_CAM, + GM_SDGM +} GmHardware; +GmHardware gm_hw = GM_ASCM; bool gm_cam_long = false; bool gm_pcm_cruise = false; bool gm_has_acc = true; @@ -81,7 +92,29 @@ bool gm_cc_long = false; bool gm_skip_relay_check = false; bool gm_force_ascm = false; +static void handle_gm_wheel_buttons(const CANPacket_t *to_push) { + int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; + + // enter controls on falling edge of set or rising edge of resume (avoids fault) + bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET); + bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME); + if (set || res) { + controls_allowed = true; + } + + // exit controls on cancel press + if (button == GM_BTN_CANCEL) { + controls_allowed = false; + } + + cruise_button_prev = button; +} + static void gm_rx_hook(const CANPacket_t *to_push) { + if ((GET_BUS(to_push) == 2U) && (GET_ADDR(to_push) == 0x1E1) && (gm_hw == GM_SDGM)) { + // SDGM buttons are on bus 2 + handle_gm_wheel_buttons(to_push); + } if (GET_BUS(to_push) == 0U) { int addr = GET_ADDR(to_push); @@ -99,23 +132,9 @@ static void gm_rx_hook(const CANPacket_t *to_push) { vehicle_moving = (left_rear_speed > GM_STANDSTILL_THRSLD) || (right_rear_speed > GM_STANDSTILL_THRSLD); } - // ACC steering wheel buttons (GM_CAM is tied to the PCM) - if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long)) { - int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; - - // enter controls on falling edge of set or rising edge of resume (avoids fault) - bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET); - bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME); - if (set || res) { - controls_allowed = true; - } - - // exit controls on cancel press - if (button == GM_BTN_CANCEL) { - controls_allowed = false; - } - - cruise_button_prev = button; + // ACC steering wheel buttons (GM_CAM and GM_SDGM are tied to the PCM) + if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long) && (gm_hw != GM_SDGM)) { + handle_gm_wheel_buttons(to_push); } // Reference for brake pressed signals: @@ -124,7 +143,7 @@ static void gm_rx_hook(const CANPacket_t *to_push) { brake_pressed = GET_BYTE(to_push, 1) >= 8U; } - if ((addr == 0xC9) && (gm_hw == GM_CAM)) { + if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) { brake_pressed = GET_BIT(to_push, 40U); } @@ -240,7 +259,7 @@ static bool gm_tx_hook(const CANPacket_t *to_send) { static int gm_fwd_hook(int bus_num, int addr) { int bus_fwd = -1; - if (gm_hw == GM_CAM) { + if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) { if (bus_num == 0) { // block PSCMStatus; forwarded through openpilot to hide an alert from the camera bool is_pscm_msg = (addr == 0x184); @@ -264,13 +283,19 @@ static int gm_fwd_hook(int bus_num, int addr) { } static safety_config gm_init(uint16_t param) { - gm_hw = GET_FLAG(param, GM_PARAM_HW_CAM) ? GM_CAM : GM_ASCM; + if GET_FLAG(param, GM_PARAM_HW_CAM) { + gm_hw = GM_CAM; + } else if GET_FLAG(param, GM_PARAM_HW_SDGM) { + gm_hw = GM_SDGM; + } else { + gm_hw = GM_ASCM; + } gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG); if (gm_hw == GM_ASCM || gm_force_ascm) { gm_long_limits = &GM_ASCM_LONG_LIMITS; - } else if (gm_hw == GM_CAM) { + } else if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) { gm_long_limits = &GM_CAM_LONG_LIMITS; } else { } @@ -278,7 +303,7 @@ static safety_config gm_init(uint16_t param) { gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long; - gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long); + gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM); gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA); gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR); @@ -292,6 +317,8 @@ static safety_config gm_init(uint16_t param) { } else { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); } + } else if (gm_hw == GM_SDGM) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS); } return ret; } diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 046007b..c0e8d78 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -229,7 +229,11 @@ class Panda: FLAG_GM_HW_CAM = 1 FLAG_GM_HW_CAM_LONG = 2 - FLAG_GM_CC_LONG = 4 + FLAG_GM_HW_SDGM = 4 + FLAG_GM_CC_LONG = 8 + FLAG_GM_HW_ASCM_LONG = 16 + FLAG_GM_NO_CAMERA = 32 + FLAG_GM_NO_ACC = 64 FLAG_GM_PEDAL_LONG = 128 # TODO: This can be inferred FLAG_GM_GAS_INTERCEPTOR = 256 diff --git a/panda/tests/safety/common.py b/panda/tests/safety/common.py index fc99218..bd7daf3 100644 --- a/panda/tests/safety/common.py +++ b/panda/tests/safety/common.py @@ -849,7 +849,7 @@ class PandaSafetyTest(PandaSafetyTestBase): continue if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}): continue - if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety'}): + if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmSdgmSafety', 'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety'}): continue if attr.startswith('TestFord') and current_test.startswith('TestFord'): continue diff --git a/panda/tests/safety/test_gm.py b/panda/tests/safety/test_gm.py index 6e97cae..6b02991 100644 --- a/panda/tests/safety/test_gm.py +++ b/panda/tests/safety/test_gm.py @@ -223,6 +223,37 @@ class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase) self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_CAM | Panda.FLAG_GM_HW_CAM_LONG) self.safety.init_tests() +class TestGmSdgmSafety(TestGmSafetyBase): + FWD_BUS_LOOKUP = {0: 2, 2: 0} + TX_MSGS = [[0x180, 0], [0x1E1, 0], # pt bus + [0x184, 2]] # obj bus + FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184]} # block LKAS message and PSCMStatus + BUTTONS_BUS = 0 # tx + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_SDGM) + self.safety.init_tests() + + def _user_brake_msg(self, brake): + values = {"BrakePressed": brake} + return self.packer.make_can_msg_panda("ECMEngineStatus", 0, values) + + def test_buttons(self): + # Only CANCEL button is allowed while cruise is enabled + self.safety.set_controls_allowed(0) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) ##### OPGM TESTS ##### diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 019d707..c274ece 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -6,7 +6,7 @@ from openpilot.common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_driver_steer_torque_limits, create_gas_interceptor_command from openpilot.selfdrive.car.gm import gmcan -from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR +from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR from openpilot.selfdrive.car.interfaces import CarControllerBase VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -205,7 +205,10 @@ class CarController(CarControllerBase): if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES: self.last_button_frame = self.frame - can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL)) + if self.CP.carFingerprint in SDGM_CAR: + can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, CS.buttons_counter, CruiseButtons.CANCEL)) + else: + can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL)) if self.CP.networkLocation == NetworkLocation.fwdCamera: # Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1 diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 0a0fe38..d976f63 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -5,7 +5,7 @@ from openpilot.common.numpy_fast import mean from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from openpilot.selfdrive.car.interfaces import CarStateBase -from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CAMERA_ACC_CAR, CC_ONLY_CAR +from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CAMERA_ACC_CAR, CC_ONLY_CAR, SDGM_CAR TransmissionType = car.CarParams.TransmissionType NetworkLocation = car.CarParams.NetworkLocation @@ -38,9 +38,14 @@ class CarState(CarStateBase): self.prev_cruise_buttons = self.cruise_buttons self.prev_distance_button = self.distance_button - self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"] - self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"] - self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"] + if self.CP.carFingerprint not in SDGM_CAR: + self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"] + self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"] + self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"] + else: + self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"] + self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"] + self.buttons_counter = cam_cp.vl["ASCMSteeringButton"]["RollingCounter"] self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"]) # This is to avoid a fault where you engage while still moving backwards after shifting to D. # An Equinox has been seen with an unsupported status (3), so only check if either wheel is in reverse (2) @@ -107,18 +112,32 @@ class CarState(CarStateBase): ret.steerFaultTemporary = self.lkas_status == 2 ret.steerFaultPermanent = self.lkas_status == 3 - # 1 - open, 0 - closed - ret.doorOpen = (pt_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or - pt_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or - pt_cp.vl["BCMDoorBeltStatus"]["RearLeftDoor"] == 1 or - pt_cp.vl["BCMDoorBeltStatus"]["RearRightDoor"] == 1) + if self.CP.carFingerprint not in SDGM_CAR: + # 1 - open, 0 - closed + ret.doorOpen = (pt_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or + pt_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or + pt_cp.vl["BCMDoorBeltStatus"]["RearLeftDoor"] == 1 or + pt_cp.vl["BCMDoorBeltStatus"]["RearRightDoor"] == 1) - # 1 - latched - ret.seatbeltUnlatched = pt_cp.vl["BCMDoorBeltStatus"]["LeftSeatBelt"] == 0 - ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1 - ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2 + # 1 - latched + ret.seatbeltUnlatched = pt_cp.vl["BCMDoorBeltStatus"]["LeftSeatBelt"] == 0 + ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1 + ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2 - ret.parkingBrake = pt_cp.vl["BCMGeneralPlatformStatus"]["ParkBrakeSwActive"] == 1 + ret.parkingBrake = pt_cp.vl["BCMGeneralPlatformStatus"]["ParkBrakeSwActive"] == 1 + else: + # 1 - open, 0 - closed + ret.doorOpen = (cam_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or + cam_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or + cam_cp.vl["BCMDoorBeltStatus"]["RearLeftDoor"] == 1 or + cam_cp.vl["BCMDoorBeltStatus"]["RearRightDoor"] == 1) + + # 1 - latched + ret.seatbeltUnlatched = cam_cp.vl["BCMDoorBeltStatus"]["LeftSeatBelt"] == 0 + ret.leftBlinker = cam_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1 + ret.rightBlinker = cam_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2 + + ret.parkingBrake = cam_cp.vl["BCMGeneralPlatformStatus"]["ParkBrakeSwActive"] == 1 ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0 ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1 ret.accFaulted = (pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED or @@ -129,7 +148,10 @@ class CarState(CarStateBase): if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value: if self.CP.carFingerprint not in CC_ONLY_CAR: ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS - ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0 + if self.CP.carFingerprint not in SDGM_CAR: + ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0 + else: + ret.stockAeb = False # openpilot controls nonAdaptive when not pcmCruise if self.CP.pcmCruise: ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3) @@ -139,8 +161,12 @@ class CarState(CarStateBase): ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0 if self.CP.enableBsm: - ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1 - ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1 + if self.CP.carFingerprint not in SDGM_CAR: + ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1 + ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1 + else: + ret.leftBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1 + ret.rightBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1 return ret @@ -149,9 +175,21 @@ class CarState(CarStateBase): messages = [] if CP.networkLocation == NetworkLocation.fwdCamera and not CP.flags & GMFlags.NO_CAMERA.value: messages += [ - ("AEBCmd", 10), ("ASCMLKASteeringCmd", 10), ] + if CP.carFingerprint in SDGM_CAR: + messages += [ + ("BCMTurnSignals", 1), + ("BCMDoorBeltStatus", 10), + ("BCMGeneralPlatformStatus", 10), + ("ASCMSteeringButton", 33), + ] + if CP.enableBsm: + messages.append(("BCMBlindSpotMonitor", 10)) + else: + messages += [ + ("AEBCmd", 10), + ] if CP.carFingerprint not in CC_ONLY_CAR: messages += [ ("ASCMActiveCruiseControlStatus", 25), @@ -162,24 +200,33 @@ class CarState(CarStateBase): @staticmethod def get_can_parser(CP): messages = [ - ("BCMTurnSignals", 1), - ("ECMPRDNL2", 10), ("PSCMStatus", 10), ("ESPStatus", 10), - ("BCMDoorBeltStatus", 10), - ("BCMGeneralPlatformStatus", 10), ("EBCMWheelSpdFront", 20), ("EBCMWheelSpdRear", 20), ("EBCMFrictionBrakeStatus", 20), - ("AcceleratorPedal2", 33), - ("ASCMSteeringButton", 33), - ("ECMEngineStatus", 100), ("PSCMSteeringAngle", 100), ("ECMAcceleratorPos", 80), ] - if CP.enableBsm: - messages.append(("BCMBlindSpotMonitor", 10)) + if CP.carFingerprint in SDGM_CAR: + messages += [ + ("ECMPRDNL2", 40), + ("AcceleratorPedal2", 40), + ("ECMEngineStatus", 80), + ] + else: + messages += [ + ("ECMPRDNL2", 10), + ("AcceleratorPedal2", 33), + ("ECMEngineStatus", 100), + ("BCMTurnSignals", 1), + ("BCMDoorBeltStatus", 10), + ("BCMGeneralPlatformStatus", 10), + ("ASCMSteeringButton", 33), + ] + if CP.enableBsm: + messages.append(("BCMBlindSpotMonitor", 10)) # Used to read back last counter sent to PT by camera if CP.networkLocation == NetworkLocation.fwdCamera: diff --git a/selfdrive/car/gm/fingerprints.py b/selfdrive/car/gm/fingerprints.py index f37a894..5b2f99f 100644 --- a/selfdrive/car/gm/fingerprints.py +++ b/selfdrive/car/gm/fingerprints.py @@ -171,6 +171,11 @@ FINGERPRINTS = { { 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 4, 730: 4, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7 }], + CAR.XT4: [ + # Cadillac XT4 w/ ACC 2023 + { + 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8 + }], } FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = { diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 8cf5e5a..83ec5bd 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -8,7 +8,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG -from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR +from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel from openpilot.selfdrive.controls.lib.drive_helpers import get_friction @@ -129,6 +129,15 @@ class CarInterface(CarInterfaceBase): ret.openpilotLongitudinalControl = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG + elif candidate in SDGM_CAR: + ret.experimentalLongitudinalAvailable = False + ret.networkLocation = NetworkLocation.fwdCamera + ret.pcmCruise = True + ret.radarUnavailable = True + ret.minEnableSpeed = -1. # engage speed is decided by ASCM + ret.minSteerSpeed = 30 * CV.MPH_TO_MS + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_SDGM + else: # ASCM, OBD-II harness ret.openpilotLongitudinalControl = True ret.networkLocation = NetworkLocation.gateway @@ -217,6 +226,10 @@ class CarInterface(CarInterfaceBase): ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.XT4: + ret.steerActuatorDelay = 0.2 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.CT6_CC: CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) @@ -269,7 +282,7 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC # Exception for flashed cars, or cars whose camera was removed - if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[CanBus.CAMERA]: + if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[CanBus.CAMERA] and not candidate in SDGM_CAR: ret.flags |= GMFlags.NO_CAMERA.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_CAMERA @@ -303,7 +316,7 @@ class CarInterface(CarInterfaceBase): # TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs below_min_enable_speed = ret.vEgo < self.CP.minEnableSpeed or self.CS.moving_backward if below_min_enable_speed and not (ret.standstill and ret.brake >= 20 and - self.CP.networkLocation == NetworkLocation.fwdCamera): + (self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.carFingerprint in SDGM_CAR)): events.add(EventName.belowEngageSpeed) if ret.cruiseState.standstill and not self.CP.autoResumeSng: events.add(EventName.resumeRequired) diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 6df349a..4e571b6 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -47,6 +47,12 @@ class CarControllerParams: # Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly max_regen_acceleration = 0. + elif CP.carFingerprint in SDGM_CAR: + self.MAX_GAS = 3400 + self.MAX_ACC_REGEN = 1514 + self.INACTIVE_REGEN = 1554 + max_regen_acceleration = 0. + else: self.MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill. self.MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen @@ -211,6 +217,10 @@ class CAR(Platforms): [GMCarDocs("Chevrolet Trailblazer 2024 No ACC")], GMCarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0), ) + XT4 = GMPlatformConfig( + [GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")], + CarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4), + ) class CruiseButtons: @@ -294,6 +304,9 @@ FW_QUERY_CONFIG = FwQueryConfig( EV_CAR = {CAR.VOLT, CAR.BOLT_EUV, CAR.VOLT_CC, CAR.BOLT_CC} CC_ONLY_CAR = {CAR.VOLT_CC, CAR.BOLT_CC, CAR.EQUINOX_CC, CAR.SUBURBAN_CC, CAR.YUKON_CC, CAR.CT6_CC, CAR.TRAILBLAZER_CC} +# We're integrated at the Safety Data Gateway Module on these cars +SDGM_CAR = {CAR.XT4} + # We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness) CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER} CAMERA_ACC_CAR.update({CAR.VOLT_CC, CAR.BOLT_CC, CAR.EQUINOX_CC, CAR.YUKON_CC, CAR.CT6_CC, CAR.TRAILBLAZER_CC}) diff --git a/selfdrive/car/torque_data/override.toml b/selfdrive/car/torque_data/override.toml index 3de33b8..e3e7267 100644 --- a/selfdrive/car/torque_data/override.toml +++ b/selfdrive/car/torque_data/override.toml @@ -40,6 +40,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "SUBARU OUTBACK 6TH GEN" = [2.0, 2.0, 0.2] "CADILLAC ESCALADE 2017" = [1.899999976158142, 1.842270016670227, 0.1120000034570694] "CADILLAC ESCALADE ESV 2019" = [1.15, 1.3, 0.2] +"CADILLAC XT4 2023" = [1.45, 1.6, 0.2] "CHEVROLET BOLT EUV 2022" = [2.0, 2.0, 0.05] "CHEVROLET SILVERADO 1500 2020" = [1.9, 1.9, 0.112] "CHEVROLET TRAILBLAZER 2021" = [1.33, 1.9, 0.16]