From 4cf5c9e098ecdedaceca64a1e446402f86770536 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:25:13 -0600 Subject: [PATCH] Keep Subaru fixed-angle handoff within native rate limits --- .../opendbc/car/subaru/carcontroller.py | 3 ++- .../subaru/tests/test_stock_aol_callers.py | 26 ++++++++++++++----- prebuilt.json | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/opendbc_repo/opendbc/car/subaru/carcontroller.py b/opendbc_repo/opendbc/car/subaru/carcontroller.py index 230f49072e..707c2057af 100644 --- a/opendbc_repo/opendbc/car/subaru/carcontroller.py +++ b/opendbc_repo/opendbc/car/subaru/carcontroller.py @@ -132,7 +132,8 @@ class CarController(CarControllerBase): self.apply_angle_last = CS.out.steeringAngleDeg self.ascent_angle_initialized = True angle_active = False - if angle_active and not self.angle_was_active and self.CP.carFingerprint != CAR.SUBARU_ASCENT_2023: + if (angle_active and not self.angle_was_active and + self.CP.carFingerprint in (CAR.SUBARU_CROSSTREK_2025, CAR.SUBARU_FORESTER_2022)): self.apply_angle_last = CS.out.steeringAngleDeg if self.CP.carFingerprint in (CAR.SUBARU_LEGACY_2025, CAR.SUBARU_ASCENT_2023, CAR.SUBARU_OUTBACK_2023): apply_angle = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, diff --git a/openpilot/starpilot/car/subaru/tests/test_stock_aol_callers.py b/openpilot/starpilot/car/subaru/tests/test_stock_aol_callers.py index f0d00c6fd9..48d4cd9469 100644 --- a/openpilot/starpilot/car/subaru/tests/test_stock_aol_callers.py +++ b/openpilot/starpilot/car/subaru/tests/test_stock_aol_callers.py @@ -24,7 +24,7 @@ from openpilot.starpilot.car.subaru.tests.native_fixture import subaru_native_pa @pytest.mark.parametrize('candidate', [CAR.SUBARU_ASCENT_2023, CAR.SUBARU_LEGACY_2025, CAR.SUBARU_CROSSTREK_2025, CAR.SUBARU_FORESTER, CAR.SUBARU_OUTBACK]) def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate, subaru_native_pair, tmp_path, aol=True, manual=False, stop_resume=False, hard_stop_native_first=False, main_only=False, preferences=None, firmware=None, - deadline_gap=False): + deadline_gap=False, fixed_angle_handoff=False): fw = firmware_records(candidate, FW_VERSIONS, 'subaru') if firmware is None else firmware rows = [] with actual_factory(candidate, gen_empty_fingerprint(), fw, '0' * 17, manual=manual, aol=aol, preferences=preferences) as (card, params, matches): @@ -74,9 +74,10 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate, stopped = stop_resume and (125 <= tick < 135 if hard_stop_native_first else 185 <= tick < 195) first_resume = 145 <= tick < 150 if hard_stop_native_first else 125 <= tick < 145 values = { - 'Steering_Torque': {'Steer_Warning': int(temporary)}, - 'Steering_2': {'Steering_Angle': 0}, - 'Wheel_Speeds': dict.fromkeys(('FL', 'FR', 'RL', 'RR'), 0 if stopped else 54), + 'Steering_Torque': {'Steer_Warning': int(temporary), + 'Steer_Torque_Sensor': (250 if 70 <= tick < 76 else 143 if 76 <= tick < 90 else 0) if fixed_angle_handoff else 0}, + 'Steering_2': {'Steering_Angle': (5.65 if tick < 78 else 5.61) if fixed_angle_handoff else 0}, + 'Wheel_Speeds': dict.fromkeys(('FL', 'FR', 'RL', 'RR'), 0 if stopped else 121.5 if fixed_angle_handoff else 54), 'Brake_Status': {'Brake': int(brake)}, 'CruiseControl': {'Cruise_On': int(main), 'Cruise_Activated': int(cruise)}, 'ES_Status': {'Cruise_Activated': int(cruise)}, @@ -202,11 +203,18 @@ def test_actual_full_engage_brake_reverse_temporary_recovery_mainoff(candidate, output = decoder.vl[steering_name]['LKAS_Output'] decoded_steering.append({'request': request, 'output': output, 'nativeAccepted': accepted}) if not stopped and (65 <= tick < 90 or aol and (first_resume or 205 <= tick < 220 or 280 <= tick < 300)): - assert request and accepted, (tick, decoded_steering) + if fixed_angle_handoff and 72 <= tick < 78: + assert not request and accepted, (tick, decoded_steering) + else: + assert request, (tick, decoded_steering) + assert accepted, ('SUBARU_ANGLE_HANDOFF_NATIVE_REJECTED', tick, decoded_steering) if output != 0: active_emissions.append(tick) if 165 <= tick < 180 or 240 <= tick < 255 or not aol and (125 <= tick < 145 or 205 <= tick < 220 or 280 <= tick < 300): - assert not request and output == 0, (tick, decoded_steering) + if fixed_angle_handoff: + assert not request and abs(output - cs.steeringAngleDeg) <= 0.011, (tick, decoded_steering) + else: + assert not request and output == 0, (tick, decoded_steering) intent = decode_intent(published['aolIntentWire'].aolIntentWire) if aol else None row = { 'tick': tick, @@ -374,3 +382,9 @@ def test_actual_subaru_expired_output_keeps_user_disable(event, subaru_native_pa with patch.object(SelfdriveD, '_expire_axis_continuity', check), pytest.raises(ObservedVeto): test_actual_full_engage_brake_reverse_temporary_recovery_mainoff( CAR.SUBARU_IMPREZA_2020, subaru_native_pair, tmp_path, main_only=True, deadline_gap=True) + + +@pytest.mark.parametrize('candidate', [CAR.SUBARU_OUTBACK_2023, CAR.SUBARU_LEGACY_2025, CAR.SUBARU_ASCENT_2023]) +def test_actual_fixed_angle_driver_handoff_keeps_native_rate_limit(candidate, subaru_native_pair, tmp_path, preferences=None): + test_actual_full_engage_brake_reverse_temporary_recovery_mainoff( + candidate, subaru_native_pair, tmp_path, preferences=preferences, fixed_angle_handoff=True) diff --git a/prebuilt.json b/prebuilt.json index eb74c950f2..bef3ce5b9a 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "c3181aba1c5e722d00cacb30fc4a200375ef0384963551cb571911bd68e57ff1", "schema_version": 1} +{"protected_inventory_sha256": "47dde56d9f0c263f2daa94de5da700cebe1f713d9f248b7a74fcc02a5a6689b5", "schema_version": 1}