From 256628e50b1ba4f7e64c023bd1e7fc7a184cdc15 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 29 Oct 2026 13:07:02 -0500 Subject: [PATCH] Keep Ford AOL through Cancel during accelerator override --- openpilot/starpilot/aol/transport_pause.py | 3 ++- .../starpilot/tests/ford_lifecycle_helpers.py | 18 ++++++++++++++++-- .../tests/test_ford_configuration_parity.py | 11 +++++++++++ prebuilt.json | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/openpilot/starpilot/aol/transport_pause.py b/openpilot/starpilot/aol/transport_pause.py index f9429bb2d4..e9fecee79c 100644 --- a/openpilot/starpilot/aol/transport_pause.py +++ b/openpilot/starpilot/aol/transport_pause.py @@ -357,7 +357,8 @@ class TransportPauseFeedback: from opendbc.car.ford.values import CAR # Before 10Hz LONG echo: physical pedal/cancel retains only real independent LAT intent. ford_withdrawal = bool(cp.brand == 'ford' and ford_qualified(cp, marked_only=True) and cp.openpilotLongitudinalControl and - (cs.brakePressed or not cs.cruiseState.enabled or physical_cancel(cs, cp)) and not cs.gasPressed and not cs.regenBraking and + (cs.brakePressed or not cs.cruiseState.enabled or physical_cancel(cs, cp) or + not axis.desiredLongitudinal and not axis.longitudinalActive) and not (cs.brakePressed and cs.gasPressed) and not cs.regenBraking and cs.cruiseState.available and not cs.doorOpen and not cs.seatbeltUnlatched and not cs.vehicleSensorsInvalid and cs.gearShifter == structs.CarState.GearShifter.drive) # Mach-E angle/curvature share native independent axes: intent-only 10Hz LONG withdrawal/recovery echo join. diff --git a/openpilot/starpilot/tests/ford_lifecycle_helpers.py b/openpilot/starpilot/tests/ford_lifecycle_helpers.py index 20530c21e1..30eed22167 100644 --- a/openpilot/starpilot/tests/ford_lifecycle_helpers.py +++ b/openpilot/starpilot/tests/ford_lifecycle_helpers.py @@ -777,18 +777,32 @@ def run_actual_producer(ford_joined, monkeypatch, *, scenario='reverse', reengag joined.route45_trace = route45_trace return if scenario in ('cancel-native-cadence', 'neutral-main-cadence'): + cancel_gas = 'gas' if getattr(monkeypatch, 'ford_cancel_gas', False) else None for _ in range(41): state, axis, sd_state, command = step(engaged=True) assert command.latActive and host.aol_card_intent.allowed_latch + if cancel_gas: + # Settle gas on a real 10Hz receipt before testing the later Cancel. + while steps % 10 != 9: + state, axis, sd_state, command = step(engaged=True) + for gas_tick in range(25): + state, axis, sd_state, command = step(engaged=True, fault=cancel_gas) + assert host.aol_card_intent.allowed_latch and command.latActive + if gas_tick >= 2: + assert not command.longActive for _ in range(12): - state, axis, sd_state, command = step(engaged=True, cancel=True) + state, axis, sd_state, command = step(engaged=True, cancel=True, fault=cancel_gas) assert host.aol_card_intent.allowed_latch, (host.aol_card_intent.latch_clear_reason, transition_trace[-12:]) assert command.latActive if not joined.last_permission & 2: assert not command.longActive for _ in range(35): - state, axis, sd_state, command = step() + state, axis, sd_state, command = step(fault=cancel_gas) assert host.aol_card_intent.allowed_latch and command.latActive and not command.longActive, transition_trace[-12:] + if cancel_gas: + for _ in range(15): + state, axis, sd_state, command = step() + assert host.aol_card_intent.allowed_latch and command.latActive and not command.longActive for _ in range(30): state, axis, sd_state, command = step(main_off=True) assert not host.aol_card_intent.allowed_latch and not command.latActive and not command.longActive diff --git a/openpilot/starpilot/tests/test_ford_configuration_parity.py b/openpilot/starpilot/tests/test_ford_configuration_parity.py index 4c6266fc9d..81a9e6fe73 100644 --- a/openpilot/starpilot/tests/test_ford_configuration_parity.py +++ b/openpilot/starpilot/tests/test_ford_configuration_parity.py @@ -137,3 +137,14 @@ def test_gas_first_engagement_native_cadence(ford_joined, monkeypatch, angle): def test_first_main_gas_native_cadence(ford_joined, monkeypatch, angle): monkeypatch.ford_main_off_warmup = True run_actual_producer(ford_joined, monkeypatch, scenario='route22') + + +@pytest.mark.parametrize('identity', [FORD_FACTORIES[11]]) +@pytest.mark.parametrize('alpha', [True]) +@pytest.mark.parametrize('hands_free', [False, True]) +@pytest.mark.parametrize('angle', [False, True]) +@pytest.mark.parametrize('topology', ['automatic', 'manual-multi']) +def test_cancel_retains_lateral_with_native_first_feedback_while_gas_held(ford_joined, monkeypatch, angle, topology): + monkeypatch.ford_native_10hz = True + monkeypatch.ford_cancel_gas = True + run_actual_producer(ford_joined, monkeypatch, scenario='cancel-native-cadence') diff --git a/prebuilt.json b/prebuilt.json index cc6a1282b4..21bdf68a78 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "fd87cd5ed34840cfecffedb8d655b20ed2e800516419170e4684ee8dbef761cb", "schema_version": 1} +{"protected_inventory_sha256": "f5d223b542ea53e06d33ae95394033d6a648ac123ae3aca3c7c84bc46b171439", "schema_version": 1}