diff --git a/opendbc_repo/opendbc/car/hyundai/carcontroller.py b/opendbc_repo/opendbc/car/hyundai/carcontroller.py index e3a7fdceb4..2444ae8534 100644 --- a/opendbc_repo/opendbc/car/hyundai/carcontroller.py +++ b/opendbc_repo/opendbc/car/hyundai/carcontroller.py @@ -23,6 +23,7 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus from opendbc.car.hyundai.ray_pedal import create_ray_pedal_command, ray_pedal_gas, ray_pedal_enabled from opendbc.car.hyundai.ioniq6_longitudinal import Ioniq6LongitudinalPolicy from opendbc.car.hyundai.canfd_dash_icons import CanFDDashIcons +from opendbc.car.hyundai.classic_scc_aol import qualified as qualified_classic_stock_aol from opendbc.car.hyundai.canfd_stock_aol import qualified as qualified_canfd_stock_aol from opendbc.car.hyundai.ev6_aol import qualified as qualified_ev6_aol from opendbc.car.hyundai.g90_longitudinal import G90LongitudinalPolicy @@ -144,6 +145,8 @@ class CarController(CarControllerBase): self.ioniq6_bsm_enabled = ioniq6_status self.ev6_aol_status = CP.carFingerprint == CAR.KIA_EV6 and (qualified_canfd_stock_aol(CP) or qualified_ev6_aol(CP)) self.canfd_dash_icons = CanFDDashIcons() if ioniq6_status or self.torque_ev_scc_enabled or self.ev6_aol_status else None + self.classic_dash_icons = CanFDDashIcons() if qualified_classic_stock_aol(CP, marked_only=True) and CP.carFingerprint != CAR.KIA_RAY_EV else None + self.classic_lfa_icon = None self.ioniq6_bsm_counter = 0 self.ioniq6_bsm_last_can_ns = 0 self._ray_pedal = ray_pedal_enabled(CP) @@ -168,6 +171,8 @@ class CarController(CarControllerBase): if self.CP.carFingerprint == CAR.KIA_EV6_2025 and self.CP.dashcamOnly: return actuators.as_builder(), [] hud_control = CC.hudControl + if self.classic_dash_icons is not None: + self.classic_lfa_icon = self.classic_dash_icons.update(self.frame, CC.enabled, CC.latActive) if self.CP.carFingerprint == CAR.KIA_RAY_EV: if CC.latActive: self._ray_disengage_frame = None @@ -316,7 +321,8 @@ class CarController(CarControllerBase): torque_fault, CS.lkas11, sys_warning, sys_state, hud_enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, left_lane_warning, right_lane_warning, - ray_lka_icon=(self._ray_lfa_icon or 1) if self.CP.carFingerprint == CAR.KIA_RAY_EV else None)) + ray_lka_icon=(self._ray_lfa_icon or 1) if self.CP.carFingerprint == CAR.KIA_RAY_EV else None, + aol_lka_icon=(self.classic_lfa_icon or 1) if self.classic_lfa_icon is not None else None)) if _projection is not None and can_sends: if not is_blended(self.CP) and (self.CP.carFingerprint != CAR.KIA_RAY_EV or self._ray_lkas11_active): _projection.emit('hyundai.LKAS11', apply_torque, apply_steer_req, can_sends[-1]) @@ -365,7 +371,7 @@ class CarController(CarControllerBase): lfa_icon = 2 if CC.enabled or CC.latActive else 3 if disengaging else 0 can_sends.append(hyundaican.create_blended_lfahda(self.packer, self.CAN, self.frame, lfa_icon)) else: - can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) + can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled, lfa_icon=self.classic_lfa_icon)) # 5 Hz ACC options if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl and not self._ray_pedal and self.blended_longitudinal is None: diff --git a/opendbc_repo/opendbc/car/hyundai/hyundaican.py b/opendbc_repo/opendbc/car/hyundai/hyundaican.py index c6001e3b31..c8fc8a94b0 100644 --- a/opendbc_repo/opendbc/car/hyundai/hyundaican.py +++ b/opendbc_repo/opendbc/car/hyundai/hyundaican.py @@ -9,7 +9,7 @@ hyundai_checksum = mk_crc8_fun(CRC8J1850, init_crc=0xFD, xor_out=0xDF) def create_lkas11(packer, frame, CP, apply_torque, steer_req, torque_fault, lkas11, sys_warning, sys_state, enabled, left_lane, right_lane, - left_lane_depart, right_lane_depart, ray_lka_icon=None): + left_lane_depart, right_lane_depart, ray_lka_icon=None, aol_lka_icon=None): values = {s: lkas11[s] for s in [ "CF_Lkas_LdwsActivemode", "CF_Lkas_LdwsSysState", @@ -55,6 +55,8 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req, values["CF_Lkas_FcwOpt_USM"] = 2 if enabled else 1 if CP.carFingerprint == CAR.KIA_RAY_EV and ray_lka_icon is not None: values["CF_Lkas_FcwOpt_USM"] = ray_lka_icon + if aol_lka_icon is not None: + values["CF_Lkas_FcwOpt_USM"] = aol_lka_icon # SysWarning 4 = keep hands on wheel # SysWarning 5 = keep hands on wheel (red) @@ -123,9 +125,9 @@ def create_clu11(packer, frame, clu11, button, CP): return packer.make_can_msg("CLU11", bus, values) -def create_lfahda_mfc(packer, enabled): +def create_lfahda_mfc(packer, enabled, *, lfa_icon=None): values = { - "LFA_Icon_State": 2 if enabled else 0, + "LFA_Icon_State": (2 if enabled else 0) if lfa_icon is None else lfa_icon, } return packer.make_can_msg("LFAHDA_MFC", 0, values) diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_classic_aol_dash.py b/opendbc_repo/opendbc/car/hyundai/tests/test_classic_aol_dash.py new file mode 100644 index 0000000000..e23b75d82e --- /dev/null +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_classic_aol_dash.py @@ -0,0 +1,89 @@ +import pytest + +from opendbc.can import CANParser +from opendbc.car import Bus, gen_empty_fingerprint, structs +from opendbc.car.hyundai.classic_scc_aol import aol_word, qualified +from opendbc.car.hyundai.interface import CarInterface +from opendbc.car.hyundai.values import CAR, DBC +from opendbc.safety.tests.libsafety.libsafety_py import make_CANPacket +from opendbc.safety.tests import test_hyundai_classic_scc_aol as native_fixture + + +def controller(identity=CAR.HYUNDAI_SONATA_HYBRID, *, marked=True): + fp = gen_empty_fingerprint() + fp[0][0x391] = 8 + fp[2][0x485] = 4 + cp = CarInterface.get_params(identity, fp, [], False, False, False) + if marked: + cp.safetyConfigs[0].safetyParam = aol_word(cp) + cp.alternativeExperience = 32 + assert qualified(cp, marked_only=True) + ci = CarInterface(cp) + ci.update([]) + parser = CANParser(DBC[identity][Bus.pt], [('LKAS11', 0), ('LFAHDA_MFC', 0)], 0) + return ci, parser + + +def step(ci, parser, *, enabled=False, lateral=False): + cc = structs.CarControl(enabled=enabled, latActive=lateral) + frame = ci.CC.frame + actuators, packets = ci.CC.update(cc.as_reader(), ci.CS, 10_000_000_000 + frame * 10_000_000) + parser.update((10_000_000_000 + frame * 10_000_000, packets)) + return actuators, packets + + +@pytest.mark.parametrize('icon', ['LKAS11', 'LFAHDA_MFC']) +def test_recorded_sonata_lateral_only_native_permission_matches_active_dash(icon): + case = native_fixture.TestHyundaiClassicSccAol(methodName='runTest') + case.setUp() + try: + ci, parser = controller() + assert ci.CP.safetyConfigs[0].safetyParam == 0xC02 + assert not ci.CP.openpilotLongitudinalControl and ci.CP.pcmCruise + case.reset(0xC02) + for _ in range(6): + case.feed(0xC02, main=True, source='BCM_PO_11') + case.feed(0xC02, main=True, source='BCM_PO_11', pressed=True) + permission = case.request(1) + assert permission == 1 and case.safety.safety_config_valid() + assert not case.safety.get_controls_allowed() + actuators, packets = step(ci, parser, lateral=bool(permission & 1)) + for address, data, bus in packets: + assert case.safety.safety_tx_hook(make_CANPacket(address, bus, data)) + signal = 'CF_Lkas_FcwOpt_USM' if icon == 'LKAS11' else 'LFA_Icon_State' + assert parser.vl[icon][signal] == 2 + assert actuators.torque == 0.0 and actuators.accel == 0.0 + assert all(address not in (0x420, 0x421) for address, _, _ in packets) + finally: + case.tearDown() + + +@pytest.mark.parametrize('identity', [CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_ELANTRA_2021, CAR.HYUNDAI_KONA_EV]) +def test_qualified_classic_stock_icons_follow_actual_lateral_and_original_blink(identity): + ci, parser = controller(identity) + original = ci.CP.to_dict() + for frame in range(101): + step(ci, parser, lateral=frame == 0) + expected = 2 if frame == 0 else 3 if frame < 100 else 0 + assert parser.vl['LKAS11']['CF_Lkas_FcwOpt_USM'] == (expected or 1) + if frame % 5 == 0: + assert parser.vl['LFAHDA_MFC']['LFA_Icon_State'] == expected + assert parser.vl['LKAS11']['CR_Lkas_StrToqReq'] == 0 + assert parser.vl['LKAS11']['CF_Lkas_MsgCount'] == frame % 16 + assert ci.CP.to_dict() == original + + +@pytest.mark.parametrize('marked', [False, True]) +@pytest.mark.parametrize('enabled,lateral,lkas,lfa', [(False, False, 1, 0), (True, False, 2, 2), (True, True, 2, 2)]) +def test_existing_ordinary_and_inactive_dash_behavior_is_preserved(marked, enabled, lateral, lkas, lfa): + ci, parser = controller(marked=marked) + step(ci, parser, enabled=enabled, lateral=lateral) + assert parser.vl['LKAS11']['CF_Lkas_FcwOpt_USM'] == lkas + assert parser.vl['LFAHDA_MFC']['LFA_Icon_State'] == lfa + + +def test_unmarked_configuration_does_not_gain_lateral_only_icon_override(): + ci, parser = controller(marked=False) + step(ci, parser, lateral=True) + assert parser.vl['LKAS11']['CF_Lkas_FcwOpt_USM'] == 1 + assert parser.vl['LFAHDA_MFC']['LFA_Icon_State'] == 0 diff --git a/prebuilt.json b/prebuilt.json index 3a5ff81e92..5d0f3c79b2 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "f7b1b124343980c9dac4009161c3fb323e22c80c46877348fcdc16ffe82a040e", "schema_version": 1} +{"protected_inventory_sha256": "9a2493649474b945d892fae1e9bba846cf1ff7420c07b88e543ac59b436581de", "schema_version": 1} diff --git a/tools/ci/run_vehicle_tests.py b/tools/ci/run_vehicle_tests.py index 49b35660f9..05888ff899 100755 --- a/tools/ci/run_vehicle_tests.py +++ b/tools/ci/run_vehicle_tests.py @@ -33,6 +33,7 @@ DEBUG_SAFETY_CLASS_TARGETS = { RELEASE_TARGETS = ( + "opendbc_repo/opendbc/car/hyundai/tests/test_classic_aol_dash.py", "opendbc_repo/opendbc/car/hyundai/tests/test_g90_continuity.py", "opendbc_repo/opendbc/car/hyundai/tests/test_disable_response.py", "openpilot/starpilot/aol/tests/test_angle_stop_native_feedback.py",