diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index 34cfa355..dc4ba0e2 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -16,12 +16,30 @@ const int GM_MAX_RATE_DOWN = 17; const int GM_DRIVER_TORQUE_ALLOWANCE = 50; const int GM_DRIVER_TORQUE_FACTOR = 4; -const int GM_STANDSTILL_THRSLD = 10; // 0.311kph +typedef struct { + const int max_gas; + const int max_regen; + const int inactive_regen; + const int max_brake; +} GmLongLimits; -const int GM_MAX_GAS = 3072; -const int GM_MAX_REGEN = 1404; -const int GM_MAX_BRAKE = 400; -const int GM_INACTIVE_REGEN = 1404; +const GmLongLimits GM_ASCM_LONG_LIMITS = { + .max_gas = 3072, + .max_regen = 1404, + .inactive_regen = 1404, + .max_brake = 400, +}; + +const GmLongLimits GM_CAM_LONG_LIMITS = { + .max_gas = 3400, + .max_regen = 1514, + .inactive_regen = 1554, + .max_brake = 400, +}; + +const GmLongLimits *gm_long_limits; + +const int GM_STANDSTILL_THRSLD = 10; // 0.311kph const CanMsg GM_ASCM_TX_MSGS[] = {{384, 0, 4}, {1033, 0, 7}, {1034, 0, 7}, {715, 0, 8}, {880, 0, 6}, // pt bus {161, 1, 7}, {774, 1, 8}, {776, 1, 7}, {784, 1, 2}, // obs bus @@ -31,6 +49,9 @@ const CanMsg GM_ASCM_TX_MSGS[] = {{384, 0, 4}, {1033, 0, 7}, {1034, 0, 7}, {715, const CanMsg GM_CAM_TX_MSGS[] = {{384, 0, 4}, // pt bus {481, 2, 7}, {388, 2, 8}}; // camera bus +const CanMsg GM_CAM_LONG_TX_MSGS[] = {{384, 0, 4}, {789, 0, 5}, {715, 0, 8}, {880, 0, 6}, // pt bus + {388, 2, 8}}; // camera bus + // TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. AddrCheckStruct gm_addr_checks[] = { {.msg = {{388, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, @@ -45,6 +66,7 @@ AddrCheckStruct gm_addr_checks[] = { addr_checks gm_rx_checks = {gm_addr_checks, GM_RX_CHECK_LEN}; const uint16_t GM_PARAM_HW_CAM = 1; +const uint16_t GM_PARAM_HW_CAM_LONG = 2; enum { GM_BTN_UNPRESS = 1, @@ -54,6 +76,7 @@ enum { }; enum {GM_ASCM, GM_CAM} gm_hw = GM_ASCM; +bool gm_cam_long = false; bool gm_pcm_cruise = false; static int gm_rx_hook(CANPacket_t *to_push) { @@ -118,8 +141,8 @@ static int gm_rx_hook(CANPacket_t *to_push) { bool stock_ecu_detected = (addr == 384); // ASCMLKASteeringCmd - // Only check ASCMGasRegenCmd if ASCM, GM_CAM uses stock longitudinal - if ((gm_hw == GM_ASCM) && (addr == 715)) { + // Check ASCMGasRegenCmd only if we're blocking it + if (!gm_pcm_cruise && (addr == 715)) { stock_ecu_detected = true; } generic_rx_checks(stock_ecu_detected); @@ -139,7 +162,11 @@ static int gm_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { int addr = GET_ADDR(to_send); if (gm_hw == GM_CAM) { - tx = msg_allowed(to_send, GM_CAM_TX_MSGS, sizeof(GM_CAM_TX_MSGS)/sizeof(GM_CAM_TX_MSGS[0])); + if (gm_cam_long) { + tx = msg_allowed(to_send, GM_CAM_LONG_TX_MSGS, sizeof(GM_CAM_LONG_TX_MSGS)/sizeof(GM_CAM_LONG_TX_MSGS[0])); + } else { + tx = msg_allowed(to_send, GM_CAM_TX_MSGS, sizeof(GM_CAM_TX_MSGS)/sizeof(GM_CAM_TX_MSGS[0])); + } } else { tx = msg_allowed(to_send, GM_ASCM_TX_MSGS, sizeof(GM_ASCM_TX_MSGS)/sizeof(GM_ASCM_TX_MSGS[0])); } @@ -153,7 +180,7 @@ static int gm_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { tx = 0; } } - if (brake > GM_MAX_BRAKE) { + if (brake > gm_long_limits->max_brake) { tx = 0; } } @@ -212,7 +239,7 @@ static int gm_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { // Disabled message is !engaged with gas // value that corresponds to inactive regen. if (!longitudinal_allowed) { - if (gas_regen != GM_INACTIVE_REGEN) { + if (gas_regen != gm_long_limits->inactive_regen) { tx = 0; } } @@ -224,7 +251,7 @@ static int gm_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) { } } // Enforce gas/regen actuation limits (max_regen <= gas_regen <= max_gas) - if ((gas_regen < GM_MAX_REGEN) || (gas_regen > GM_MAX_GAS)) { + if ((gas_regen < gm_long_limits->max_regen) || (gas_regen > gm_long_limits->max_gas)) { tx = 0; } } @@ -258,9 +285,11 @@ static int gm_fwd_hook(int bus_num, CANPacket_t *to_fwd) { } if (bus_num == 2) { - // block lkas message, forward all others + // block lkas message and acc messages if gm_cam_long, forward all others bool is_lkas_msg = (addr == 384); - if (!is_lkas_msg) { + bool is_acc_msg = (addr == 789) || (addr == 715) || (addr == 880); + int block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); + if (!block_msg) { bus_fwd = 0; } } @@ -271,7 +300,18 @@ static int gm_fwd_hook(int bus_num, CANPacket_t *to_fwd) { static const addr_checks* gm_init(uint16_t param) { gm_hw = GET_FLAG(param, GM_PARAM_HW_CAM) ? GM_CAM : GM_ASCM; - gm_pcm_cruise = gm_hw == GM_CAM; + + if (gm_hw == GM_ASCM) { + gm_long_limits = &GM_ASCM_LONG_LIMITS; + } else if (gm_hw == GM_CAM) { + gm_long_limits = &GM_CAM_LONG_LIMITS; + } else { + } + +#ifdef ALLOW_DEBUG + gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG); +#endif + gm_pcm_cruise = (gm_hw == GM_CAM) && !gm_cam_long; return &gm_rx_checks; } diff --git a/python/__init__.py b/python/__init__.py index 0024efdb..89870073 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -227,6 +227,7 @@ class Panda: FLAG_SUBARU_GEN2 = 1 FLAG_GM_HW_CAM = 1 + FLAG_GM_HW_CAM_LONG = 2 def __init__(self, serial: Optional[str] = None, claim: bool = True, disable_checks: bool = True): self._serial = serial diff --git a/tests/safety/common.py b/tests/safety/common.py index 89e736f4..3b2b8f9a 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -710,6 +710,8 @@ class PandaSafetyTest(PandaSafetyTestBase): continue if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}): continue + if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety'}): + continue if attr.startswith('TestHyundaiCanfd') and current_test.startswith('TestHyundaiCanfd'): continue diff --git a/tests/safety/test_gm.py b/tests/safety/test_gm.py index 1739a018..92507e0d 100755 --- a/tests/safety/test_gm.py +++ b/tests/safety/test_gm.py @@ -6,11 +6,6 @@ from panda.tests.safety import libpandasafety_py import panda.tests.safety.common as common from panda.tests.safety.common import CANPackerPanda -MAX_BRAKE = 400 -MAX_GAS = 3072 -MAX_REGEN = 1404 -INACTIVE_REGEN = 1404 - class Buttons: UNPRESS = 1 @@ -19,11 +14,48 @@ class Buttons: CANCEL = 6 +class GmLongitudinalBase(common.PandaSafetyTest): + # pylint: disable=no-member,abstract-method + def test_set_resume_buttons(self): + """ + SET and RESUME enter controls allowed on their falling edge. + """ + for btn in range(8): + self.safety.set_controls_allowed(0) + for _ in range(10): + self._rx(self._button_msg(btn)) + self.assertFalse(self.safety.get_controls_allowed()) + + # should enter controls allowed on falling edge + if btn in (Buttons.RES_ACCEL, Buttons.DECEL_SET): + self._rx(self._button_msg(Buttons.UNPRESS)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_cancel_button(self): + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(Buttons.CANCEL)) + self.assertFalse(self.safety.get_controls_allowed()) + + # override these tests from PandaSafetyTest, GM longitudinal uses button enable + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def _pcm_status_msg(self, enable): + pass + + class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): STANDSTILL_THRESHOLD = 10 * 0.0311 RELAY_MALFUNCTION_ADDR = 384 RELAY_MALFUNCTION_BUS = 0 - BUTTONS_BUS = 0 + BUTTONS_BUS = 0 # rx or tx + BRAKE_BUS = 0 # tx only MAX_RATE_UP = 7 MAX_RATE_DOWN = 17 @@ -33,6 +65,11 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety DRIVER_TORQUE_ALLOWANCE = 50 DRIVER_TORQUE_FACTOR = 4 + MAX_GAS = 0 + MAX_REGEN = 0 + INACTIVE_REGEN = 0 + MAX_BRAKE = 0 + @classmethod def setUpClass(cls): if cls.__name__ == "TestGmSafetyBase": @@ -69,7 +106,7 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety def _send_brake_msg(self, brake): values = {"FrictionBrakeCmd": -brake} - return self.packer_chassis.make_can_msg_panda("EBCMFrictionBrakeCmd", 2, values) + return self.packer_chassis.make_can_msg_panda("EBCMFrictionBrakeCmd", self.BRAKE_BUS, values) def _send_gas_msg(self, gas): values = {"GasRegenCmd": gas} @@ -91,7 +128,7 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety for enabled in [0, 1]: for b in range(0, 500): self.safety.set_controls_allowed(enabled) - if abs(b) > MAX_BRAKE or (not enabled and b != 0): + if abs(b) > self.MAX_BRAKE or (not enabled and b != 0): self.assertFalse(self._tx(self._send_brake_msg(b))) else: self.assertTrue(self._tx(self._send_brake_msg(b))) @@ -101,18 +138,24 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety for enabled in [0, 1]: for gas_regen in range(0, 2 ** 12 - 1): self.safety.set_controls_allowed(enabled) - should_tx = ((enabled and MAX_REGEN <= gas_regen <= MAX_GAS) or - (not enabled and gas_regen == INACTIVE_REGEN)) + should_tx = ((enabled and self.MAX_REGEN <= gas_regen <= self.MAX_GAS) or + (not enabled and gas_regen == self.INACTIVE_REGEN)) self.assertEqual(should_tx, self._tx(self._send_gas_msg(gas_regen)), (enabled, gas_regen)) -class TestGmAscmSafety(TestGmSafetyBase): +class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase): TX_MSGS = [[384, 0], [1033, 0], [1034, 0], [715, 0], [880, 0], # pt bus [161, 1], [774, 1], [776, 1], [784, 1], # obs bus [789, 2], # ch bus [0x104c006c, 3], [0x10400060, 3]] # gmlan FWD_BLACKLISTED_ADDRS: Dict[int, List[int]] = {} FWD_BUS_LOOKUP: Dict[int, int] = {} + BRAKE_BUS = 2 + + MAX_GAS = 3072 + MAX_REGEN = 1404 + INACTIVE_REGEN = 1404 + MAX_BRAKE = 400 def setUp(self): self.packer = CANPackerPanda("gm_global_a_powertrain_generated") @@ -121,39 +164,6 @@ class TestGmAscmSafety(TestGmSafetyBase): self.safety.set_safety_hooks(Panda.SAFETY_GM, 0) self.safety.init_tests() - # override these tests from PandaSafetyTest, ASCM GM uses button enable - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def _pcm_status_msg(self, enable): - raise NotImplementedError - - def test_set_resume_buttons(self): - """ - SET and RESUME enter controls allowed on their falling edge. - """ - for btn in range(8): - self.safety.set_controls_allowed(0) - for _ in range(10): - self._rx(self._button_msg(btn)) - self.assertFalse(self.safety.get_controls_allowed()) - - # should enter controls allowed on falling edge - if btn in (Buttons.RES_ACCEL, Buttons.DECEL_SET): - self._rx(self._button_msg(Buttons.UNPRESS)) - self.assertTrue(self.safety.get_controls_allowed()) - - def test_cancel_button(self): - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Buttons.CANCEL)) - self.assertFalse(self.safety.get_controls_allowed()) - class TestGmCameraSafety(TestGmSafetyBase): TX_MSGS = [[384, 0], # pt bus @@ -200,5 +210,25 @@ class TestGmCameraSafety(TestGmSafetyBase): pass +class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmSafetyBase): + TX_MSGS = [[384, 0], [789, 0], [715, 0], [880, 0], # pt bus + [388, 2]] # camera bus + FWD_BLACKLISTED_ADDRS = {2: [384, 715, 880, 789], 0: [388]} # block LKAS, ACC messages and PSCMStatus + FWD_BUS_LOOKUP = {0: 2, 2: 0} + BUTTONS_BUS = 0 # rx only + + MAX_GAS = 3400 + MAX_REGEN = 1514 + INACTIVE_REGEN = 1554 + MAX_BRAKE = 400 + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpandasafety_py.libpandasafety + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_CAM | Panda.FLAG_GM_HW_CAM_LONG) + self.safety.init_tests() + + if __name__ == "__main__": unittest.main()