Files
StarPilot/openpilot/starpilot/tests/test_ford_configuration_parity.py
T

119 lines
6.2 KiB
Python

"""Actual Ford factory, parser, Card, SelfdriveD, Controls and native matrix.
CAN, perception, USB and clocks are synthetic; no physical acceptance claim.
"""
import pytest
from openpilot.starpilot.tests.ford_lifecycle_helpers import FORD_FACTORIES, ford_joined as ford_joined, run_actual_producer
@pytest.fixture(autouse=True)
def configuration_topology(monkeypatch, topology, request):
monkeypatch.ford_topology = topology
if request.node.name.startswith(('test_brake_retains_lateral_with_native_first_feedback', 'test_cancel_retains_lateral_with_native_first_feedback')):
monkeypatch.ford_native_10hz = True
monkeypatch.ford_angle = request.node.callspec.params['angle']
if request.node.name.startswith('test_recorded_radar_creep_enters_native_stopping'):
monkeypatch.ford_angle = request.node.callspec.params['angle']
if request.node.name.startswith('test_route49_first_main_engagement'):
monkeypatch.ford_first_main = True
monkeypatch.ford_angle = request.node.callspec.params['angle']
@pytest.mark.parametrize('identity', FORD_FACTORIES)
@pytest.mark.parametrize('alpha', [False, True])
@pytest.mark.parametrize('hands_free', [False])
@pytest.mark.parametrize('topology', ['automatic', 'manual', 'multi-panda', 'manual-multi'])
def test_actual_configuration_lifecycle(ford_joined, monkeypatch):
run_actual_producer(ford_joined, monkeypatch, scenario='configuration-parity')
@pytest.mark.parametrize('identity', [FORD_FACTORIES[11]])
@pytest.mark.parametrize('alpha', [False])
@pytest.mark.parametrize('hands_free', [False, True])
@pytest.mark.parametrize('topology', ['automatic', 'manual-multi'])
@pytest.mark.parametrize('angle', [False, True])
def test_route49_first_main_engagement_arms_before_cruise_cancel(ford_joined, monkeypatch, angle):
run_actual_producer(ford_joined, monkeypatch, scenario='route49')
@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_brake_retains_lateral_with_native_first_feedback(ford_joined, monkeypatch, angle):
run_actual_producer(ford_joined, monkeypatch, scenario='brake-native-cadence')
@pytest.mark.parametrize('identity', [FORD_FACTORIES[11]])
@pytest.mark.parametrize('alpha', [False, 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(ford_joined, monkeypatch, angle):
run_actual_producer(ford_joined, monkeypatch, scenario='cancel-native-cadence')
@pytest.mark.parametrize('identity', [FORD_FACTORIES[11]])
@pytest.mark.parametrize('alpha', [True])
@pytest.mark.parametrize('hands_free', [True])
@pytest.mark.parametrize('topology', ['automatic', 'manual-multi'])
@pytest.mark.parametrize('angle', [False, True])
def test_recorded_radar_creep_enters_native_stopping(ford_joined, angle):
import json
import gzip
from pathlib import Path
from opendbc.car import structs
from opendbc.car.ford import fordcan
from opendbc.safety.tests.libsafety import libsafety_py
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.starpilot.longitudinal.tests.test_lead_approach_planner import Frame, DRIVE_NS
recorded = json.loads(gzip.decompress((Path(__file__).parent / 'testdata/ford_mache_radar_creep.json.gz').read_bytes()))['frames']
joined = ford_joined
endpoint, safety = joined.native, joined.safety
endpoint.arm()
endpoint.pump(10, main=5, mask=3)
assert safety.aol_get_permission_mask() == 3
planner, controller = LongitudinalPlanner(joined.host.CP, init_v=recorded[0]['vEgo']), LongControl(joined.host.CP)
checked = 0
for index, sample in enumerate(recorded):
sm = Frame(index)
# Retain the route's actual cross-service age and validity, including radar cadence.
for service, age in sample['sourceAgeNs'].items():
sm.logMonoTime[service] = sm.now_ns - 10_000_000 - age
sm.valid[service] = sample['sourceValid'][service]
car = sm['carState']
car.vEgo, car.vEgoRaw, car.aEgo = sample['vEgo'], sample['vEgoRaw'], sample['aEgo']
car.standstill, car.gasPressed, car.brakePressed = sample['standstill'], sample['gas'], sample['brake']
car.gearShifter = 'drive'
car.cruiseState.available = car.cruiseState.enabled = True
sm['carControl'].longActive = sample['longActive']
sm['selfdriveState'].enabled = sample['enabled']
sm['selfdriveState'].experimentalMode = sample['experimental']
sm['controlsState'].longControlState = controller.long_control_state
sm['modelV2'].action.shouldStop = sample['shouldStop']
sm['modelV2'].action.desiredAcceleration = sample['aTarget']
for key, values in sample['model'].items():
getattr(sm['modelV2'], key).x = values
for lead, values in zip((sm['radarState'].leadOne, sm['radarState'].leadTwo), sample['leads'], strict=True):
for key, value in values.items():
setattr(lead, key, value)
planner.update(sm, now_ns=sm.now_ns, drive_id=DRIVE_NS)
assert planner.mpc.solution_status == 0
for _ in range(5):
output = controller.update(sample['longActive'], car, float(planner.output_a_target), planner.output_should_stop, (-3.5, 2.))
endpoint.pump(1, main=5, mask=3, speed=sample['vEgoRaw'])
stopping = controller.long_control_state == structs.CarControl.Actuators.LongControlState.stopping
active = sample['longActive']
gas = -5. if output < 0. or not active else float(output)
address, data, bus = fordcan.create_acc_msg(endpoint.packer, joined.host.CI.CC.CAN, active, gas,
float(output) if active else 0., stopping if active else False, bool(output < 0. and active), v_ego_kph=80.)
assert safety.safety_tx_hook(libsafety_py.make_CANPacket(address, bus - joined.offset, data))
if 821. <= sample['routeSeconds'] <= 828.:
assert planner.output_should_stop, ('Creeping toward stopped radar lead', sample['routeSeconds'])
assert stopping and output < 0.
checked += 1
assert checked > 500