diff --git a/opendbc_repo/opendbc/car/hyundai/interface.py b/opendbc_repo/opendbc/car/hyundai/interface.py index 1a51f712a..6fcfacc64 100644 --- a/opendbc_repo/opendbc/car/hyundai/interface.py +++ b/opendbc_repo/opendbc/car/hyundai/interface.py @@ -115,6 +115,10 @@ class CarInterface(CarInterfaceBase): ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value if 0x110 in fingerprint[CAN.CAM]: ret.flags |= HyundaiFlags.CANFD_LKA_STEERING_ALT.value + # This HDA II Carnival uses the alternate 0x1AA cruise-button frame even + # though other LKA-steering platforms use 0x1CF. + if candidate == CAR.KIA_CARNIVAL_2025 and 0x1aa in fingerprint[CAN.ECAN] and 0x1cf not in fingerprint[CAN.ECAN]: + ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value else: # no LKA steering if 0x1cf not in fingerprint[CAN.ECAN]: diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py index 03aadc9bb..1ff72ff01 100644 --- a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py @@ -352,6 +352,24 @@ class TestHyundaiFingerprint: assert CP.flags & HyundaiFlags.HYBRID assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS + def test_carnival_2025_hda2_detects_alternate_buttons(self): + fingerprint = gen_empty_fingerprint() + CAN = CanBus(None, fingerprint) + fingerprint[CAN.CAM] = {0x110: 32} + fingerprint[1] = {0x1aa: 16} + + carnival_cp = CarInterface.get_params(CAR.KIA_CARNIVAL_2025, fingerprint, [], False, False, False, None) + assert carnival_cp.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT + assert carnival_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS + assert carnival_cp.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ALT_BUTTONS + + carnival_state = CarState(carnival_cp, None) + pt_states = {state.name for state in carnival_state.get_can_parsers(carnival_cp)[Bus.pt].message_states.values()} + assert "CRUISE_BUTTONS" not in pt_states + + k4_cp = CarInterface.get_params(CAR.KIA_K4_2025, fingerprint, [], False, False, False, None) + assert not (k4_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS) + def test_ioniq_6_hda1_layout_stays_non_lka(self): fingerprint = gen_empty_fingerprint() fingerprint[1] = {0x100: 8, 0x110: 8} diff --git a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h index 2859b5b00..416e46bba 100644 --- a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h +++ b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h @@ -342,6 +342,16 @@ static safety_config hyundai_canfd_init(uint16_t param) { HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1) }; + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS[] = { + HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1) + HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false) + }; + + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS[] = { + HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1) + HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false) + }; + static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = { HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1) HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1) @@ -420,7 +430,15 @@ static safety_config hyundai_canfd_init(uint16_t param) { HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1) }; - SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret); + static RxCheck hyundai_canfd_lka_steering_alt_buttons_long_rx_checks[] = { + HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1) + }; + + if (hyundai_canfd_alt_buttons) { + SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_long_rx_checks, ret); + } else { + SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret); + } if (hyundai_canfd_lka_steering_alt) { SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_LONG_TX_MSGS, ret); } else { @@ -467,17 +485,33 @@ static safety_config hyundai_canfd_init(uint16_t param) { if (hyundai_canfd_lka_steering) { // *** LKA steering checks *** // E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU. - // Does not use the alt buttons message static RxCheck hyundai_canfd_lka_steering_rx_checks[] = { HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1) HYUNDAI_CANFD_SCC_ADDR_CHECK(1) }; - SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret); - if (hyundai_canfd_lka_steering_alt) { - SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret); + static RxCheck hyundai_canfd_lka_steering_alt_buttons_rx_checks[] = { + HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1) + HYUNDAI_CANFD_SCC_ADDR_CHECK(1) + }; + + if (hyundai_canfd_alt_buttons) { + SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_rx_checks, ret); } else { - SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret); + SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret); + } + if (hyundai_canfd_lka_steering_alt) { + if (hyundai_canfd_alt_buttons) { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS, ret); + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret); + } + } else { + if (hyundai_canfd_alt_buttons) { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS, ret); + } else { + SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret); + } } } else if (!hyundai_camera_scc) { diff --git a/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py b/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py index a736e1458..80728e003 100755 --- a/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py +++ b/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py @@ -13,7 +13,8 @@ from opendbc.safety import ALTERNATIVE_EXPERIENCE from opendbc.safety.tests.libsafety import libsafety_py import opendbc.safety.tests.common as common from opendbc.safety.tests.common import CANPackerSafety, away_round, round_speed -from opendbc.safety.tests.hyundai_common import HyundaiAolLkasOnEngageBase, HyundaiAolLkasOnEngageStockBase, HyundaiButtonBase, HyundaiLongitudinalBase +from opendbc.safety.tests.hyundai_common import Buttons, HyundaiAolLkasOnEngageBase, HyundaiAolLkasOnEngageStockBase, HyundaiButtonBase, \ + HyundaiLongitudinalBase # All combinations of radar/camera-SCC and gas/hybrid/EV cars ALL_GAS_EV_HYBRID_COMBOS = [ @@ -525,6 +526,56 @@ class TestHyundaiCanfdLKASteeringAltEV(TestHyundaiCanfdBase): self.safety.init_tests() +class TestHyundaiCanfdLKASteeringAltButtonsICE(TestHyundaiCanfdLKASteeringAltEV): + + TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x1A0, 1], [0x362, 0]] + GAS_MSG = ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED") + + def setUp(self): + self.packer = CANPackerSafety("hyundai_canfd_generated") + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_LKA_STEERING | + HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT | HyundaiSafetyFlags.CANFD_ALT_BUTTONS) + self.safety.init_tests() + + def _button_msg(self, buttons, main_button=0, bus=None): + if bus is None: + bus = self.PT_BUS + values = { + "CRUISE_BUTTONS": buttons, + "ADAPTIVE_CRUISE_MAIN_BTN": main_button, + } + return self.packer.make_can_msg_safety("CRUISE_BUTTONS_ALT", bus, values) + + def _acc_cancel_msg(self, cancel, accel=0): + values = {"ACCMode": 4 if cancel else 0, "aReqRaw": accel, "aReqValue": accel} + return self.packer.make_can_msg_safety("SCC_CONTROL", self.SCC_BUS, values) + + def test_button_sends(self): + for enabled in (True, False): + for btn in range(8): + self.safety.set_controls_allowed(enabled) + self.assertFalse(self._tx(self._button_msg(btn, bus=self.BUTTONS_TX_BUS))) + + def test_acc_cancel(self): + for enabled in (True, False): + self.safety.set_controls_allowed(enabled) + self.assertTrue(self._tx(self._acc_cancel_msg(True))) + self.assertFalse(self._tx(self._acc_cancel_msg(True, accel=1))) + self.assertFalse(self._tx(self._acc_cancel_msg(False))) + + def test_longitudinal_uses_alternate_button_rx(self): + safety_param = HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.CANFD_LKA_STEERING | \ + HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT | HyundaiSafetyFlags.CANFD_ALT_BUTTONS + self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, safety_param) + self.safety.init_tests() + + self._rx(self._button_msg(Buttons.SET)) + self.assertFalse(self.safety.get_controls_allowed()) + self._rx(self._button_msg(Buttons.NONE)) + self.assertTrue(self.safety.get_controls_allowed()) + + class TestHyundaiCanfdLKASteeringLongEV(HyundaiLongitudinalBase, TestHyundaiCanfdLKASteeringEV): TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0], [0x51, 0], [0x730, 1], [0x12a, 1], [0x160, 1], @@ -771,6 +822,5 @@ class TestHyundaiCanfdLKASteeringAolLkasOnEngageEV(HyundaiAolLkasOnEngageStockBa self.safety.init_tests() - if __name__ == "__main__": unittest.main()