diff --git a/opendbc_repo/opendbc/car/gm/carcontroller.py b/opendbc_repo/opendbc/car/gm/carcontroller.py index 0fc58014d..a3d854491 100644 --- a/opendbc_repo/opendbc/car/gm/carcontroller.py +++ b/opendbc_repo/opendbc/car/gm/carcontroller.py @@ -134,7 +134,7 @@ def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int: def supports_volt_auto_hold(CP, auto_hold_enabled: bool): safety_cfg = getattr(CP, "safetyConfigs", ()) safety_param = safety_cfg[0].safetyParam if safety_cfg else 0 - stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value) + stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value) return ( auto_hold_enabled and stock_hold_safety_ready and diff --git a/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py b/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py index 853b6aa68..aef983706 100644 --- a/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py +++ b/opendbc_repo/opendbc/car/gm/tests/test_carcontroller.py @@ -147,7 +147,7 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf stock_safety = [SimpleNamespace(safetyParam=0x8000)] no_safety = [SimpleNamespace(safetyParam=0)] - assert supports_volt_auto_hold( + assert not supports_volt_auto_hold( SimpleNamespace( carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, openpilotLongitudinalControl=True, @@ -165,6 +165,15 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf ), True, ) + assert supports_volt_auto_hold( + SimpleNamespace( + carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, + openpilotLongitudinalControl=True, + networkLocation=CarParams.NetworkLocation.fwdCamera, + safetyConfigs=stock_safety, + ), + True, + ) assert supports_volt_auto_hold( SimpleNamespace( carFingerprint=CAR.CHEVROLET_VOLT, diff --git a/opendbc_repo/opendbc/car/toyota/carcontroller.py b/opendbc_repo/opendbc/car/toyota/carcontroller.py index 14f04e7be..09d7c31ad 100644 --- a/opendbc_repo/opendbc/car/toyota/carcontroller.py +++ b/opendbc_repo/opendbc/car/toyota/carcontroller.py @@ -203,8 +203,8 @@ class CarController(CarControllerBase): if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive): return 0.0 - if self.CP.minEnableSpeed < 0.0: - return 0.12 if CS.out.standstill and self.accel > 0.0 else 0.0 + if CS.out.standstill: + return 0.12 if self.accel > 0.0 else 0.0 max_interceptor_gas = 0.5 if self.CP.carFingerprint == CAR.TOYOTA_RAV4: diff --git a/opendbc_repo/opendbc/car/toyota/interface.py b/opendbc_repo/opendbc/car/toyota/interface.py index fc5b7190a..18a88fc5c 100644 --- a/opendbc_repo/opendbc/car/toyota/interface.py +++ b/opendbc_repo/opendbc/car/toyota/interface.py @@ -139,6 +139,8 @@ class CarInterface(CarInterfaceBase): ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl + if ret.enableGasInterceptorDEPRECATED: + ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.GAS_INTERCEPTOR.value toyota_auto_hold = Params(return_defaults=True).get_bool("ToyotaAutoHold") if toyota_auto_hold and candidate in (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR): diff --git a/opendbc_repo/opendbc/car/toyota/tests/test_toyota.py b/opendbc_repo/opendbc/car/toyota/tests/test_toyota.py index 27524a6d5..88793485e 100644 --- a/opendbc_repo/opendbc/car/toyota/tests/test_toyota.py +++ b/opendbc_repo/opendbc/car/toyota/tests/test_toyota.py @@ -14,7 +14,7 @@ from opendbc.car.toyota.fingerprints import FW_VERSIONS from opendbc.car.toyota.interface import CarInterface from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \ FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \ - ToyotaFlags, get_platform_codes + ToyotaFlags, ToyotaSafetyFlags, get_platform_codes from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.common.params import Params @@ -371,6 +371,20 @@ class TestToyotaCarController: assert 0.0 < gas_cmd <= 0.5 + def test_interceptor_corolla_scales_with_accel_request_when_pedal_enables_sng(self): + controller = self._make_controller() + controller.CP.enableGasInterceptorDEPRECATED = True + controller.CP.carFingerprint = CAR.TOYOTA_COROLLA + controller.CP.minEnableSpeed = -1.0 + controller.accel = 0.8 + + gas_cmd = controller._compute_interceptor_gas_cmd( + SimpleNamespace(longActive=True), + SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)), + ) + + assert 0.0 < gas_cmd <= 0.5 + def test_interceptor_disabled_returns_zero(self): controller = self._make_controller() controller.accel = 1.0 @@ -449,6 +463,7 @@ class TestToyotaCarController: ) assert CP.enableGasInterceptorDEPRECATED + assert CP.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.GAS_INTERCEPTOR assert abs(CP.longitudinalActuatorDelay - 0.2) < 1e-6 assert CP.stopAccel == -1.5 diff --git a/opendbc_repo/opendbc/car/toyota/values.py b/opendbc_repo/opendbc/car/toyota/values.py index 1f9587381..02471b0b1 100644 --- a/opendbc_repo/opendbc/car/toyota/values.py +++ b/opendbc_repo/opendbc/car/toyota/values.py @@ -57,6 +57,7 @@ class ToyotaSafetyFlags(IntFlag): LTA = (4 << 8) SECOC = (8 << 8) LONG_FILTER = (16 << 8) + GAS_INTERCEPTOR = (32 << 8) class ToyotaFlags(IntFlag): diff --git a/opendbc_repo/opendbc/safety/modes/toyota.h b/opendbc_repo/opendbc/safety/modes/toyota.h index d297c289f..7b246e57d 100644 --- a/opendbc_repo/opendbc/safety/modes/toyota.h +++ b/opendbc_repo/opendbc/safety/modes/toyota.h @@ -69,6 +69,9 @@ {.msg = {{0x116, 0, 8, 42U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{0x101, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ +#define TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK \ + {.msg = {{0x201, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + static bool toyota_secoc = false; static bool toyota_alt_brake = false; static bool toyota_stock_longitudinal = false; @@ -90,6 +93,12 @@ static uint32_t toyota_get_checksum(const CANPacket_t *msg) { return (uint8_t)(msg->data[checksum_byte]); } +static int toyota_get_interceptor(const CANPacket_t *msg) { + uint16_t val1 = ((uint16_t)msg->data[0] << 8U) | (uint16_t)msg->data[1]; + uint16_t val2 = ((uint16_t)msg->data[2] << 8U) | (uint16_t)msg->data[3]; + return (int)((val1 + val2) / 2U); +} + static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) { bool valid = false; @@ -150,7 +159,9 @@ static void toyota_rx_hook(const CANPacket_t *msg) { if (msg->addr == 0x1D2U) { bool cruise_engaged = GET_BIT(msg, 5U); // PCM_CRUISE.CRUISE_ACTIVE pcm_cruise_check(cruise_engaged); - gas_pressed = !GET_BIT(msg, 4U); // PCM_CRUISE.GAS_RELEASED + if (!enable_gas_interceptor) { + gas_pressed = !GET_BIT(msg, 4U); // PCM_CRUISE.GAS_RELEASED + } } if (!toyota_alt_brake && (msg->addr == 0x226U)) { brake_pressed = GET_BIT(msg, 37U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_nodsu_pt_generated.dbc) @@ -181,6 +192,14 @@ static void toyota_rx_hook(const CANPacket_t *msg) { if (msg->addr == 0x365U) { acc_main_on = GET_BIT(msg, 0U); } + + if (enable_gas_interceptor && (msg->addr == 0x201U)) { + // Match the DBC's physical pedal threshold to avoid controls state mismatches. + const int toyota_gas_interceptor_threshold = 805; + int gas_interceptor = toyota_get_interceptor(msg); + gas_pressed = gas_interceptor > toyota_gas_interceptor_threshold; + gas_interceptor_prev = gas_interceptor; + } } } @@ -352,6 +371,10 @@ static bool toyota_tx_hook(const CANPacket_t *msg) { } } + if ((msg->addr == 0x200U) && longitudinal_interceptor_checks(msg)) { + tx = false; + } + // Auto brake hold replaces the camera AEB message only while stopped. if ((msg->addr == 0x344U) && ((alternative_experience & ALT_EXP_ALLOW_AEB) != 0)) { if (vehicle_moving || gas_pressed || !acc_main_on) { @@ -391,6 +414,16 @@ static safety_config toyota_init(uint16_t param) { TOYOTA_COMMON_LONG_TX_MSGS_FILTER }; + static const CanMsg TOYOTA_INTERCEPTOR_TX_MSGS[] = { + TOYOTA_COMMON_LONG_TX_MSGS + {0x200, 0, 6, .check_relay = false}, + }; + + static const CanMsg TOYOTA_INTERCEPTOR_TX_MSGS_FILTER[] = { + TOYOTA_COMMON_LONG_TX_MSGS_FILTER + {0x200, 0, 6, .check_relay = false}, + }; + static const CanMsg TOYOTA_SECOC_LONG_TX_MSGS[] = { TOYOTA_COMMON_SECOC_LONG_TX_MSGS }; @@ -403,6 +436,7 @@ static safety_config toyota_init(uint16_t param) { const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET; const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET; const uint32_t TOYOTA_PARAM_LONG_FILTER = 16UL << TOYOTA_PARAM_OFFSET; + const uint32_t TOYOTA_PARAM_GAS_INTERCEPTOR = 32UL << TOYOTA_PARAM_OFFSET; #ifdef ALLOW_DEBUG const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET; @@ -413,8 +447,13 @@ static safety_config toyota_init(uint16_t param) { toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL); toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA); toyota_long_filter = GET_FLAG(param, TOYOTA_PARAM_LONG_FILTER); + enable_gas_interceptor = GET_FLAG(param, TOYOTA_PARAM_GAS_INTERCEPTOR); toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR; + if (toyota_stock_longitudinal || toyota_secoc) { + enable_gas_interceptor = false; + } + safety_config ret; if (toyota_secoc) { if (toyota_stock_longitudinal) { @@ -425,6 +464,12 @@ static safety_config toyota_init(uint16_t param) { } else { if (toyota_stock_longitudinal) { SET_TX_MSGS(TOYOTA_TX_MSGS, ret); + } else if (enable_gas_interceptor) { + if (toyota_long_filter) { + SET_TX_MSGS(TOYOTA_INTERCEPTOR_TX_MSGS_FILTER, ret); + } else { + SET_TX_MSGS(TOYOTA_INTERCEPTOR_TX_MSGS, ret); + } } else { if (toyota_long_filter) { SET_TX_MSGS(TOYOTA_LONG_TX_MSGS_FILTER, ret); @@ -445,8 +490,16 @@ static safety_config toyota_init(uint16_t param) { static RxCheck toyota_lta_rx_checks[] = { TOYOTA_RX_CHECKS(true) }; + static RxCheck toyota_lta_interceptor_rx_checks[] = { + TOYOTA_RX_CHECKS(true) + TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK + }; - SET_RX_CHECKS(toyota_lta_rx_checks, ret); + if (enable_gas_interceptor) { + SET_RX_CHECKS(toyota_lta_interceptor_rx_checks, ret); + } else { + SET_RX_CHECKS(toyota_lta_rx_checks, ret); + } } else { static RxCheck toyota_lka_rx_checks[] = { TOYOTA_RX_CHECKS(false) @@ -454,8 +507,20 @@ static safety_config toyota_init(uint16_t param) { static RxCheck toyota_lka_alt_brake_rx_checks[] = { TOYOTA_ALT_BRAKE_RX_CHECKS(false) }; + static RxCheck toyota_lka_interceptor_rx_checks[] = { + TOYOTA_RX_CHECKS(false) + TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK + }; + static RxCheck toyota_lka_alt_brake_interceptor_rx_checks[] = { + TOYOTA_ALT_BRAKE_RX_CHECKS(false) + TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK + }; - if (!toyota_alt_brake) { + if (enable_gas_interceptor && !toyota_alt_brake) { + SET_RX_CHECKS(toyota_lka_interceptor_rx_checks, ret); + } else if (enable_gas_interceptor) { + SET_RX_CHECKS(toyota_lka_alt_brake_interceptor_rx_checks, ret); + } else if (!toyota_alt_brake) { SET_RX_CHECKS(toyota_lka_rx_checks, ret); } else { SET_RX_CHECKS(toyota_lka_alt_brake_rx_checks, ret); diff --git a/opendbc_repo/opendbc/safety/tests/test_toyota.py b/opendbc_repo/opendbc/safety/tests/test_toyota.py index c7c6712ec..07e8793b5 100644 --- a/opendbc_repo/opendbc/safety/tests/test_toyota.py +++ b/opendbc_repo/opendbc/safety/tests/test_toyota.py @@ -17,6 +17,8 @@ TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0 [0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1 [0x411, 0], # PCS_HUD [0x750, 0]] # radar diagnostic address +TOYOTA_COMMON_LONG_TX_MSGS_FILTER = TOYOTA_COMMON_LONG_TX_MSGS[:-1] +GAS_INTERCEPTOR_TX_MSGS = [[0x200, 0]] class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyTest): @@ -296,6 +298,47 @@ class TestToyotaAltBrakeSafety(TestToyotaSafetyTorque): pass +class TestToyotaSafetyGasInterceptorBase(common.GasInterceptorSafetyTest, TestToyotaSafetyBase): + + TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS + GAS_INTERCEPTOR_TX_MSGS + INTERCEPTOR_THRESHOLD = 805 + DBC = "toyota_nodsu_pt_generated" + SAFETY_PARAM = TestToyotaSafetyBase.EPS_SCALE | ToyotaSafetyFlags.GAS_INTERCEPTOR + + def setUp(self): + self.packer = CANPackerSafety(self.DBC) + self.safety = libsafety_py.libsafety + self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.SAFETY_PARAM) + self.safety.init_tests() + + def _user_gas_msg(self, gas): + return self._interceptor_user_gas(self.INTERCEPTOR_THRESHOLD + 1 if gas else 0) + + def test_stock_longitudinal_disables_interceptor(self): + self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, + self.SAFETY_PARAM | ToyotaSafetyFlags.STOCK_LONGITUDINAL) + self.safety.init_tests() + self.safety.set_controls_allowed(True) + self.assertFalse(self._tx(self._interceptor_gas_cmd(100))) + + +class TestToyotaSafetyTorqueGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaSafetyTorque): + pass + + +class TestToyotaAltBrakeLongFilterGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaAltBrakeSafety): + + TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS_FILTER + GAS_INTERCEPTOR_TX_MSGS + RELAY_MALFUNCTION_ADDRS = {0: (0x2E4, 0x191, 0x412)} + FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191]} + DBC = "toyota_new_mc_pt_generated" + SAFETY_PARAM = (TestToyotaSafetyBase.EPS_SCALE | ToyotaSafetyFlags.ALT_BRAKE | + ToyotaSafetyFlags.LONG_FILTER | ToyotaSafetyFlags.GAS_INTERCEPTOR) + + def test_diagnostics(self): + super().test_diagnostics(ecu_disabled=False) + + class TestToyotaStockLongitudinalBase(TestToyotaSafetyBase): TX_MSGS = TOYOTA_COMMON_TX_MSGS @@ -423,7 +466,5 @@ class TestToyotaSecOcSafety(TestToyotaSecOcSafetyBase): self.assertEqual(should_tx, self._tx(self._accel_msg_343(accel))) self.assertEqual(should_tx, self._tx(self._accel_msg_343(accel, cancel_req=1))) - - if __name__ == "__main__": unittest.main() diff --git a/starpilot/controls/starpilot_card.py b/starpilot/controls/starpilot_card.py index 2a3a17fc9..58fef1677 100644 --- a/starpilot/controls/starpilot_card.py +++ b/starpilot/controls/starpilot_card.py @@ -18,6 +18,11 @@ from openpilot.starpilot.common.starpilot_utilities import is_FrogsGoMoo from openpilot.starpilot.common.starpilot_variables import ERROR_LOGS_PATH, GearShifter, NON_DRIVING_GEARS class StarPilotCard: + @staticmethod + def _button_type_raw(button_event) -> int: + button_type = getattr(button_event, "type", button_event) + return int(getattr(button_type, "raw", button_type)) + def __init__(self, CP, FPCP): self.CP = CP @@ -101,6 +106,7 @@ class StarPilotCard: def update(self, carState, starpilotCarState, sm, starpilot_toggles): self.switchback_mode_enabled = self.params_memory.get_bool("SwitchbackModeEnabled") + button_event_types = [self._button_type_raw(be) for be in carState.buttonEvents] if self.hyundai_aol_needs_engagement: if carState.gearShifter in NON_DRIVING_GEARS: @@ -110,14 +116,14 @@ class StarPilotCard: self.hyundai_aol_ready = True if self.CP.brand == "hyundai" or starpilot_toggles.lkas_allowed_for_aol: - for be in carState.buttonEvents: - if be.type == ButtonType.lkas and be.pressed and starpilot_toggles.always_on_lateral_lkas: + for be, be_type in zip(carState.buttonEvents, button_event_types, strict=False): + if be_type == ButtonType.lkas and be.pressed and starpilot_toggles.always_on_lateral_lkas: if self.hyundai_aol_needs_engagement: self.hyundai_aol_ready = True self.always_on_lateral_allowed = not self.always_on_lateral_allowed if carState.cruiseState.enabled or self.pause_lateral: self.pause_lateral = not self.always_on_lateral_allowed - elif be.type == ButtonType.mainCruise and be.pressed: + elif be_type == ButtonType.mainCruise and be.pressed: if starpilot_toggles.main_cruise_aol_toggle: if self.hyundai_aol_needs_engagement: self.hyundai_aol_ready = True @@ -154,11 +160,11 @@ class StarPilotCard: self.always_on_lateral_enabled &= not (carState.brakePressed and carState.vEgo < starpilot_toggles.always_on_lateral_pause_speed) or carState.standstill self.always_on_lateral_enabled &= not self.error_log.is_file() or self.frogs_go_moo - if sm.updated["starpilotPlan"] or any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in carState.buttonEvents): - self.accel_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in carState.buttonEvents) + if sm.updated["starpilotPlan"] or any(be_type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be_type in button_event_types): + self.accel_pressed = any(be_type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be_type in button_event_types) - if sm.updated["starpilotPlan"] or any(be.type == ButtonType.decelCruise for be in carState.buttonEvents): - self.decel_pressed = any(be.type == ButtonType.decelCruise for be in carState.buttonEvents) + if sm.updated["starpilotPlan"] or any(be_type == ButtonType.decelCruise for be_type in button_event_types): + self.decel_pressed = any(be_type == ButtonType.decelCruise for be_type in button_event_types) self._distance_poll_counter += 1 if self._distance_poll_counter >= 10: @@ -197,7 +203,7 @@ class StarPilotCard: self.handle_button_event("cancel_long", sm, starpilot_toggles) self.handle_button_event("cancel_very_long", sm, starpilot_toggles) - if any(be.pressed and be.type == ButtonType.lkas for be in carState.buttonEvents): + if any(be.pressed and be_type == ButtonType.lkas for be, be_type in zip(carState.buttonEvents, button_event_types, strict=False)): self.handle_button_event("lkas", sm, starpilot_toggles) if getattr(starpilot_toggles, "has_canfd_media_buttons", False): diff --git a/starpilot/controls/tests/test_starpilot_card.py b/starpilot/controls/tests/test_starpilot_card.py index 63bedaf16..014a145aa 100644 --- a/starpilot/controls/tests/test_starpilot_card.py +++ b/starpilot/controls/tests/test_starpilot_card.py @@ -79,6 +79,10 @@ def make_car_state(available=False, enabled=False, button_events=None): ) +def make_wrapped_button_event(button_type, pressed): + return SimpleNamespace(type=SimpleNamespace(raw=int(button_type)), pressed=pressed) + + def test_honda_lkas_button_can_toggle_always_on_lateral(monkeypatch, tmp_path): monkeypatch.setattr(spc, "Params", FakeParams) monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False) @@ -165,6 +169,31 @@ def test_hyundai_canfd_lkas_button_can_toggle_aol_before_engagement(monkeypatch, assert ret.alwaysOnLateralEnabled is True +def test_hyundai_canfd_lkas_button_wrapped_enum_can_toggle_aol(monkeypatch, tmp_path): + monkeypatch.setattr(spc, "Params", FakeParams) + monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False) + monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path) + + card = spc.StarPilotCard( + SimpleNamespace(brand="hyundai", flags=spc.HyundaiFlags.CANFD), + SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL), + ) + + car_state = make_car_state(available=True, button_events=[make_wrapped_button_event(spc.ButtonType.lkas, True)]) + starpilot_car_state = SimpleNamespace(distancePressed=False) + sm = make_sm() + toggles = make_toggles(always_on_lateral=True, always_on_lateral_lkas=True) + + ret = card.update(car_state, starpilot_car_state, sm, toggles) + assert ret.alwaysOnLateralAllowed is True + assert ret.alwaysOnLateralEnabled is True + + car_state.buttonEvents = [make_wrapped_button_event(spc.ButtonType.lkas, True)] + ret = card.update(car_state, starpilot_car_state, sm, toggles) + assert ret.alwaysOnLateralAllowed is False + assert ret.alwaysOnLateralEnabled is False + + def test_kia_forte_non_scc_main_cruise_button_can_toggle_aol_before_engagement(monkeypatch, tmp_path): monkeypatch.setattr(spc, "Params", FakeParams) monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False) @@ -214,6 +243,31 @@ def test_hyundai_main_cruise_button_can_start_aol_before_normal_engagement(monke assert ret.alwaysOnLateralAllowed is False +def test_hyundai_main_cruise_button_wrapped_enum_can_toggle_aol(monkeypatch, tmp_path): + monkeypatch.setattr(spc, "Params", FakeParams) + monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False) + monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path) + + card = spc.StarPilotCard( + SimpleNamespace(brand="hyundai"), + SimpleNamespace(alternativeExperience=spc.ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL), + ) + + car_state = make_car_state(available=True, button_events=[make_wrapped_button_event(spc.ButtonType.mainCruise, True)]) + starpilot_car_state = SimpleNamespace(distancePressed=False) + sm = make_sm() + toggles = make_toggles(always_on_lateral=True, main_cruise_aol_toggle=True) + + ret = card.update(car_state, starpilot_car_state, sm, toggles) + assert ret.alwaysOnLateralAllowed is True + assert ret.alwaysOnLateralEnabled is True + + car_state.buttonEvents = [make_wrapped_button_event(spc.ButtonType.mainCruise, True)] + ret = card.update(car_state, starpilot_car_state, sm, toggles) + assert ret.alwaysOnLateralAllowed is False + assert ret.alwaysOnLateralEnabled is False + + def test_hyundai_main_cruise_button_adopts_slc_when_assigned_to_slc(monkeypatch, tmp_path): monkeypatch.setattr(spc, "Params", FakeParams) monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False) diff --git a/starpilot/system/the_pond/the_pond.py b/starpilot/system/the_pond/the_pond.py index 8a68c2e9b..bfef6dbf5 100644 --- a/starpilot/system/the_pond/the_pond.py +++ b/starpilot/system/the_pond/the_pond.py @@ -28,10 +28,11 @@ import time import traceback from urllib.parse import quote -from cereal import car, log, messaging +from cereal import car, custom, log, messaging from opendbc.can.parser import CANParser from opendbc.car.gm.values import GMFlags from opendbc.car.toyota.carcontroller import LOCK_CMD, UNLOCK_CMD +from opendbc.car.toyota.values import ToyotaStarPilotFlags from openpilot.common.constants import CV from openpilot.common.params import ParamKeyType, Params from openpilot.common.realtime import DT_HW @@ -2686,6 +2687,9 @@ def _get_hardware_snapshot_items(): if fpcp_bytes: try: fpcp = messaging.log_from_bytes(fpcp_bytes, custom.StarPilotCarParams) + fpcp_flags = int(getattr(fpcp, "flags", 0)) + has_sdsu = bool(fpcp_flags & ToyotaStarPilotFlags.SMART_DSU.value) + has_zss = bool(fpcp_flags & ToyotaStarPilotFlags.ZSS.value) can_use_pedal = bool(getattr(fpcp, "canUsePedal", can_use_pedal)) can_use_sdsu = bool(getattr(fpcp, "canUseSDSU", can_use_sdsu)) except Exception: