Support stock Prius retrofit AOL

This commit is contained in:
firestar5683
2026-10-23 15:54:01 -05:00
parent 37dc1533ef
commit 076cda2d6c
6 changed files with 168 additions and 3 deletions
+13
View File
@@ -85,7 +85,20 @@ def matrix_stock_qualified(cp, *, marked_only=False):
return config.safetyModel == structs.CarParams.SafetyModel.toyota and config.safetyParam == 856
def stock_prius_retrofit_qualified(cp, *, marked_only=False):
if (cp is None or cp.brand != 'toyota' or cp.carFingerprint != CAR.TOYOTA_PRIUS_RETROFIT or
cp.notCar or cp.passive or cp.dashcamOnly or cp.secOcRequired or not cp.pcmCruise or
cp.openpilotLongitudinalControl or cp.steerControlType != structs.CarParams.SteerControlType.torque or
len(cp.safetyConfigs) != 1 or cp.flags & ~int(ToyotaFlags.ZSS) != int(ToyotaFlags.HYBRID) or
cp.alternativeExperience not in ((32,) if marked_only else (0, 32))):
return False
config = cp.safetyConfigs[0]
return config.safetyModel == structs.CarParams.SafetyModel.toyota and config.safetyParam == 585
def qualified(cp, *, marked_only=False):
if stock_prius_retrofit_qualified(cp, marked_only=marked_only):
return True
if matrix_stock_qualified(cp, marked_only=marked_only):
return True
if angle_qualified(cp, marked_only=marked_only):
@@ -0,0 +1,69 @@
import pytest
import struct
from opendbc.car import gen_empty_fingerprint, structs
from opendbc.car.toyota.interface import CarInterface
from opendbc.car.toyota.values import CAR, ToyotaFlags
from openpilot.starpilot.car.toyota.aol import policy_for, qualified, stock_prius_retrofit_qualified
def factory(*, alpha=False, zss=False):
fp = gen_empty_fingerprint()
if zss:
fp[0][0x23] = 8
fw = [structs.CarParams.CarFw(ecu=structs.CarParams.Ecu.eps, address=0x7A1,
fwVersion=b'8965B47070' + bytes(6), brand='toyota', bus=0)]
return CarInterface.get_params(CAR.TOYOTA_PRIUS_RETROFIT, fp, fw, alpha, False, False)
@pytest.mark.parametrize('alpha', (False, True))
@pytest.mark.parametrize('zss', (False, True))
def test_manual_stock_prius_retrofit_factory_and_policy(alpha, zss):
cp = factory(alpha=alpha, zss=zss)
before = cp.to_dict()
assert not cp.openpilotLongitudinalControl and cp.pcmCruise and not cp.dashcamOnly
assert cp.safetyConfigs[0].safetyParam == 585
assert cp.flags == int(ToyotaFlags.HYBRID) | (int(ToyotaFlags.ZSS) if zss else 0)
assert cp.steerActuatorDelay == struct.unpack('<f', struct.pack('<f', .14))[0]
assert cp.lateralTuning.torque.steeringAngleDeadzoneDeg == struct.unpack('<f', struct.pack('<f', .3))[0]
assert stock_prius_retrofit_qualified(cp) and qualified(cp)
assert policy_for(cp).runtime_supported
assert cp.to_dict() == before, 'Admission must not change any existing tune or configuration'
assert not stock_prius_retrofit_qualified(cp, marked_only=True)
cp.alternativeExperience = 32
assert stock_prius_retrofit_qualified(cp, marked_only=True) and qualified(cp, marked_only=True)
@pytest.mark.parametrize('field,value', (
('notCar', True), ('passive', True), ('dashcamOnly', True), ('secOcRequired', True),
('pcmCruise', False), ('openpilotLongitudinalControl', True), ('flags', 0),
('flags', int(ToyotaFlags.HYBRID | ToyotaFlags.LONG_FILTER)),
('flags', int(ToyotaFlags.HYBRID | ToyotaFlags.GAS_INTERCEPTOR)),
('flags', int(ToyotaFlags.HYBRID | ToyotaFlags.AUTO_BRAKE_HOLD)),
('steerControlType', structs.CarParams.SteerControlType.angle),
('carFingerprint', CAR.TOYOTA_PRIUS), ('carFingerprint', CAR.TOYOTA_MATRIX_RETROFIT),
('brand', 'gm'), ('alternativeExperience', 128), ('alternativeExperience', 160),
))
def test_stock_prius_retrofit_admission_refuses_foreign_configuration(field, value):
cp = factory()
setattr(cp, field, value)
assert not stock_prius_retrofit_qualified(cp)
@pytest.mark.parametrize('word', (73, 584, 586, 4169, 4681, 0x8042, 0x8242, 0xC042, 0xC242))
def test_stock_prius_retrofit_rejects_other_native_owners(word):
cp = factory()
cp.safetyConfigs[0].safetyParam = word
assert not stock_prius_retrofit_qualified(cp)
def test_stock_prius_retrofit_refuses_invalid_native_inventory():
cp = factory()
cp.safetyConfigs = []
assert not stock_prius_retrofit_qualified(cp)
cp = factory()
cp.safetyConfigs = [cp.safetyConfigs[0], cp.safetyConfigs[0]]
assert not stock_prius_retrofit_qualified(cp)
cp = factory()
cp.safetyConfigs[0].safetyModel = structs.CarParams.SafetyModel.gm
assert not stock_prius_retrofit_qualified(cp)
assert not stock_prius_retrofit_qualified(None)
@@ -0,0 +1,74 @@
"""Manual EPS73 Prius through the current factory, callers, C++ peer and native.
Manual selection, acquired firmware, CAN/perception and USB are input fixtures.
These tests do not establish physical vehicle or scheduling acceptance.
"""
import struct
import pytest
from opendbc.car import structs
from opendbc.car.toyota.values import CAR, ToyotaFlags
from openpilot.starpilot.car.toyota.tests import test_rav4h_caller_native as helpers
from openpilot.starpilot.car.toyota.tests.test_toyota_cpp_transport import host_heartbeat_bridge as host_heartbeat_bridge
source_matched_native = helpers.source_matched_native
daemon_gc_scope = helpers.daemon_gc_scope
@pytest.mark.parametrize('scenario', ('startup-main', 'physical', 'saved-long-off', 'ordinary-cruise'))
def test_actual_prius_retrofit_stock_lifecycle(scenario, host_heartbeat_bridge):
def physical(packer, **fields):
fields['sdsu'] = None
if scenario == 'startup-main':
fields['cruise'] = False
frames = helpers.physical(packer, **fields)
frames.append(packer.make_can_msg('ACC_CONTROL', 0, {}))
assert len(next(data for address, data, bus in frames if address == 0x262 and bus == 0)) == 8
assert len(next(data for address, data, bus in frames if address == 0x1D3 and bus == 0)) == 8
return frames
def on_state(tick, host, cs):
cp = host.CP
assert cp.carFingerprint == CAR.TOYOTA_PRIUS_RETROFIT
assert cp.safetyConfigs[0].safetyParam == 585 and cp.flags == int(ToyotaFlags.HYBRID)
assert cp.pcmCruise and not cp.openpilotLongitudinalControl
assert cp.steerActuatorDelay == struct.unpack('<f', struct.pack('<f', .14))[0]
assert cp.lateralTuning.torque.steeringAngleDeadzoneDeg == struct.unpack('<f', struct.pack('<f', .3))[0]
def observe(tick, host, cs, cc, safety, native):
assert not cc.longActive and not safety.aol_get_permission_mask() & 2
if scenario == 'physical':
if tick == 265:
assert cs.gasPressed and not cs.brakePressed
assert cc.latActive and safety.aol_get_permission_mask() == 1
if tick == 355:
assert cs.brakePressed and cs.standstill
assert cc.latActive and safety.aol_get_permission_mask() == 1
if tick == 415:
assert cs.gearShifter == structs.CarState.GearShifter.neutral
assert not cc.latActive and not cc.longActive and safety.aol_get_permission_mask() == 0
if tick == 425:
assert cs.gearShifter == structs.CarState.GearShifter.reverse
assert not cc.latActive and not cc.longActive and safety.aol_get_permission_mask() == 0
if tick == 505:
assert cs.gearShifter == structs.CarState.GearShifter.drive
assert cc.latActive and safety.aol_get_permission_mask() == 1
if scenario == 'startup-main' and tick >= 220:
assert cs.cruiseState.available and not cs.cruiseState.enabled
assert host.aol_card_intent.allowed_latch and cc.latActive
assert safety.aol_get_permission_mask() == 1
if scenario == 'ordinary-cruise' and tick >= 220:
assert not host.aol_card_intent.settings.enabled
assert not host.aol_card_intent.allowed_latch
assert cs.cruiseState.enabled and cc.enabled and cc.latActive
if scenario == 'saved-long-off' and tick >= 310:
assert not host.CP.openpilotLongitudinalControl and not cc.longActive
assert host.CP.safetyConfigs[0].safetyParam == 585
fw = [structs.CarParams.CarFw(ecu=structs.CarParams.Ecu.eps, address=0x7A1,
fwVersion=b'8965B47070' + bytes(6), brand='toyota', bus=0)]
actual_scenario = 'physical' if scenario == 'physical' else 'off' if scenario == 'saved-long-off' else 'nominal'
helpers.run_case(True, actual_scenario, candidate=CAR.TOYOTA_PRIUS_RETROFIT,
bridge=host_heartbeat_bridge, input_clock=True, aol=scenario != 'ordinary-cruise',
sdsu_words=(585, 585), firmware_override=fw, fingerprint_frames=[], manual_selection=True,
physical_frames=physical, on_state=on_state, on_sample=observe)
@@ -248,8 +248,13 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H,
params.put('AolBrakePauseSpeedMps', float(brake_pause_mps), block=True)
fw = firmware(candidate) if firmware_override is None else firmware_override
if manual_selection:
assert candidate == CAR.TOYOTA_MATRIX_RETROFIT and stock and not fw
assert firmware_override == [] and fingerprint_frames == []
assert stock and fingerprint_frames == []
if candidate == CAR.TOYOTA_MATRIX_RETROFIT:
assert not fw and firmware_override == []
else:
assert candidate == CAR.TOYOTA_PRIUS_RETROFIT and len(fw) == 1
assert fw[0].ecu == structs.CarParams.Ecu.eps and fw[0].address == 0x7A1 and fw[0].bus == 0
assert bytes(fw[0].fwVersion) == b'8965B47070' + bytes(6)
else:
exact, found = match_fw_to_car(fw, '', log=False)
assert exact and found == {candidate}
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "4e7069cd3da0a1f7965dac62ce06f6bfc8711c9ca7ae71c48488a1b1ad2be14d", "schema_version": 1}
{"protected_inventory_sha256": "b62ac613ee4e66f5782643aff34ead6342241e888d716d437c4e70f75c09d6b3", "schema_version": 1}
+4
View File
@@ -24,6 +24,8 @@ from tools.ci import lifecycle_shards, vehicle_pytest
# Representative production callers plus their source/native refusal cases.
# The emitted selected_test_ids record exact parametrized configurations.
AOL_LIFECYCLE_TARGETS = (
'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock.py',
'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock_callers.py',
'openpilot/starpilot/aol/tests/test_crossmake_native_admission.py',
'openpilot/starpilot/aol/tests/test_hkg_source_policy_cache.py',
'openpilot/starpilot/aol/tests/test_hkg_native_admission.py',
@@ -206,6 +208,8 @@ DEBUG_SAFETY_CLASS_TARGETS = {
RELEASE_TARGETS = (
'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock.py',
'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock_callers.py',
'opendbc_repo/opendbc/car/hyundai/tests/test_long_cp_copy.py',
'opendbc_repo/opendbc/car/ford/tests/test_mache_display_handoff.py',
'opendbc_repo/opendbc/safety/tests/test_ford_mache_display.py',