mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
Support SecOC Yaris AOL with stock and alpha longitudinal
This commit is contained in:
@@ -159,6 +159,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.openpilotLongitudinalControl = ((bool(ret.flags & ToyotaFlags.TSS2) and not (ret.flags & ToyotaFlags.RADAR_ACC)) or
|
||||
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value) or (radar_filter and alpha_long))
|
||||
|
||||
if candidate == CAR.TOYOTA_YARIS and ret.flags & ToyotaFlags.DISABLE_RADAR:
|
||||
ret.safetyConfigs[0].safetyParam |= int(ToyotaSafetyFlags.SECOC_RADAR_LONG)
|
||||
|
||||
from opendbc.car.toyota import matrix_filter
|
||||
manual_matrix_filter = any(matrix_filter.hardware_supported(candidate, fingerprint, car_fw, length=size) for size in (4, 8))
|
||||
if candidate in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT):
|
||||
|
||||
@@ -57,6 +57,7 @@ class ToyotaSafetyFlags(IntFlag):
|
||||
LTA = (4 << 8)
|
||||
SECOC = (8 << 8)
|
||||
LONG_FILTER = (16 << 8)
|
||||
SECOC_RADAR_LONG = SECOC | LONG_FILTER # Exact radar-disable SecOC profile; separates its keepalive permission.
|
||||
GAS_INTERCEPTOR = (32 << 8)
|
||||
ALT_CRUISE = (64 << 8) # Exact Lexus IS DSU MAIN profile
|
||||
RAV4H_FILTER_PROFILE = (128 << 8) # Exact smartDSU owner; bit14 remains reserved for ALT_CRUISE
|
||||
|
||||
@@ -681,14 +681,14 @@ 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;
|
||||
|
||||
#ifdef ALLOW_DEBUG
|
||||
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
|
||||
#ifdef ALLOW_DEBUG
|
||||
toyota_secoc = GET_FLAG(base_param, TOYOTA_PARAM_SECOC);
|
||||
#endif
|
||||
|
||||
toyota_alt_brake = GET_FLAG(base_param, TOYOTA_PARAM_ALT_BRAKE);
|
||||
toyota_stock_longitudinal = GET_FLAG(base_param, TOYOTA_PARAM_STOCK_LONGITUDINAL);
|
||||
toyota_radar_diagnostics_allowed = !toyota_dsu_bypass && !toyota_stock_longitudinal && !toyota_secoc && (base_param != 0x1049U) && (base_param != 0x1449U) && !toyota_eps8_sdsu && !toyota_legacy_sdsu && !toyota_legacy_advert_param(base_param) && !toyota_camera_2aa_param(base_param) && !toyota_prius_filter_param(base_param);
|
||||
toyota_radar_diagnostics_allowed = !toyota_dsu_bypass && !toyota_stock_longitudinal && (!GET_FLAG(base_param, TOYOTA_PARAM_SECOC) || (toyota_secoc && (base_param == 6217U))) && (base_param != 0x1049U) && (base_param != 0x1449U) && !toyota_eps8_sdsu && !toyota_legacy_sdsu && !toyota_legacy_advert_param(base_param) && !toyota_camera_2aa_param(base_param) && !toyota_prius_filter_param(base_param);
|
||||
toyota_lta = !toyota_legacy_advert_param(base_param) && GET_FLAG(base_param, TOYOTA_PARAM_LTA);
|
||||
toyota_dbc_eps_torque_factor = base_param & TOYOTA_EPS_FACTOR;
|
||||
|
||||
|
||||
@@ -217,13 +217,13 @@ static void toyota_aol_configure(uint16_t param) {
|
||||
toyota_pedal = (param == 0x2049U) || (param == 0x2249U) || (param == 0x2449U) || (param == 0x2649U);
|
||||
toyota_aol_rav4h_filter = (param == 0x9049U) || (param == 0x9249U);
|
||||
toyota_sdsu_status = toyota_aol_rav4h_filter || toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_prius_sdsu_param(param);
|
||||
toyota_aol_stock = (toyota_dsu_bypass && ((param & 0x200U) != 0U)) || (param == 0x2649U) || (param == 585U) || (param == 856U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x649U) || (param == 0x1649U) || (param == 0x8249U) || (param == 0x8649U) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && ((param & 0x200U) != 0U));
|
||||
#ifdef ALLOW_DEBUG
|
||||
toyota_aol_secoc = (param == 2121U);
|
||||
toyota_aol_secoc = (param == 2121U) || (param == 2633U) || (param == 6217U);
|
||||
#else
|
||||
toyota_aol_secoc = false;
|
||||
#endif
|
||||
toyota_aol_enabled = (toyota_dsu_bypass && aol_requested) || ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && aol_requested) || (toyota_aol_lta && aol_requested) || (toyota_aol_stock && aol_requested) ||
|
||||
toyota_aol_stock = (toyota_dsu_bypass && ((param & 0x200U) != 0U)) || (param == 0x2649U) || (param == 585U) || (param == 856U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x649U) || (param == 0x1649U) || (param == 0x8249U) || (param == 0x8649U) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && ((param & 0x200U) != 0U)) || (toyota_aol_secoc && (param == 2633U));
|
||||
toyota_aol_enabled = (toyota_dsu_bypass && aol_requested) || ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && aol_requested) || (toyota_aol_lta && aol_requested) || (toyota_aol_stock && (!toyota_aol_secoc || (alternative_experience == 32)) && aol_requested) ||
|
||||
#ifdef ALLOW_DEBUG
|
||||
(toyota_aol_secoc && (alternative_experience == 32)) ||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Always On Lateral on SecOC TSS2 Toyotas (2021+ Sienna, safety param 2121 = EPS scale 73 | SECOC).
|
||||
"""Always On Lateral on SecOC TSS2 Toyotas: camera2121, Yaris stock2633 and radar takeover6217.
|
||||
|
||||
They have no 0x3BC GEAR_PACKET: the gear is read from 0x127 GEAR_PACKET_HYBRID (P 0, R 1, N 2, D 3, B 4; checksummed,
|
||||
~60 Hz). Everything else is the TSS2 rule: main switch, doors, EPS state, and for longitudinal also cruise and belt.
|
||||
@@ -25,6 +25,9 @@ def checksum(address, data):
|
||||
|
||||
|
||||
class TestToyotaSecocAol(unittest.TestCase):
|
||||
WORD = WORD
|
||||
BOTH_PERMISSION = 3
|
||||
|
||||
def setUp(self):
|
||||
self.safety = libsafety_py.libsafety
|
||||
|
||||
@@ -32,7 +35,8 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
self.safety.set_alternative_experience(0)
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.noOutput, 0)
|
||||
|
||||
def init(self, word=WORD, ae=32):
|
||||
def init(self, word=None, ae=32):
|
||||
word = self.WORD if word is None else word
|
||||
self.omit_addresses = set()
|
||||
self.safety.init_tests()
|
||||
self.safety.set_alternative_experience(ae)
|
||||
@@ -74,7 +78,7 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
self.physical(gear=g)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
self.physical(gear=g, cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
|
||||
def test_park_reverse_neutral_deny(self):
|
||||
for g in (P, R, N, 5, 15):
|
||||
@@ -151,7 +155,7 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
for changes in ({"belt": False}, {"brake": True}, {"gas": True}):
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
self.physical(cruise=True, **changes)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1, changes)
|
||||
|
||||
@@ -172,11 +176,11 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
def test_lta_stays_blocked(self):
|
||||
self.init()
|
||||
self.physical(gear=D, cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
self.assertFalse(self.safety.safety_tx_hook(libsafety_py.make_CANPacket(0x131, 0, bytes([8]) + bytes(7))))
|
||||
|
||||
def test_exact_admission(self):
|
||||
for word, ae in ((WORD, 0), (WORD, 160), (WORD, 288), (WORD, 33), (WORD | 512, 32), (2120, 32), (2122, 32)):
|
||||
for word, ae in ((self.WORD, 0), (self.WORD, 160), (self.WORD, 288), (self.WORD, 33), (self.WORD ^ 1, 32), (self.WORD ^ 2, 32)):
|
||||
self.init(word, ae)
|
||||
self.physical()
|
||||
self.assertEqual(self.safety.aol_get_request_mask(), 0, (word, ae))
|
||||
@@ -220,7 +224,7 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
def test_host_heartbeat_request_and_required_rx_health_are_independent(self):
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
self.safety.set_aol_test_heartbeat(False)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.safety.set_aol_test_heartbeat(True)
|
||||
@@ -241,17 +245,17 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
self.physical(cruise=True)
|
||||
self.safety.set_timer(1_000_000 + elapsed)
|
||||
self.physical(cruise=True, request=False)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3 if allowed else 0)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION if allowed else 0)
|
||||
|
||||
def test_optional_source_corruption_revokes_until_fresh_recovery(self):
|
||||
for address in (0x1D3, 0x127, 0x620, 0x262):
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
self.safety.safety_rx_hook(libsafety_py.make_CANPacket(address, 0, bytes(7)))
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
self.physical(cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 3)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), self.BOTH_PERMISSION)
|
||||
if address != 0x620:
|
||||
self.safety.safety_rx_hook(libsafety_py.make_CANPacket(address, 0, bytes(8)))
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 0)
|
||||
@@ -265,12 +269,59 @@ class TestToyotaSecocAol(unittest.TestCase):
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
packet = libsafety_py.make_CANPacket(0x183, 0, (accel & 0xffff).to_bytes(2, 'big') + bytes(6))
|
||||
self.assertEqual(bool(self.safety.safety_tx_hook(packet)), allowed)
|
||||
self.assertEqual(bool(self.safety.safety_tx_hook(packet)), allowed and self.BOTH_PERMISSION == 3)
|
||||
self.init()
|
||||
self.physical(cruise=False)
|
||||
self.assertFalse(self.safety.safety_tx_hook(libsafety_py.make_CANPacket(0x183, 0, bytes([0, 1]) + bytes(6))))
|
||||
self.assertFalse(self.safety.safety_tx_hook(libsafety_py.make_CANPacket(0x343, 0, bytes([0, 1]) + bytes(6))))
|
||||
|
||||
|
||||
class TestYarisStockSecocAol(TestToyotaSecocAol):
|
||||
WORD = 2633
|
||||
BOTH_PERMISSION = 1
|
||||
|
||||
def test_stock_never_requests_long_or_blocks_stock_accel_forwarding(self):
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
self.assertEqual(self.safety.aol_get_request_mask(), 1)
|
||||
self.assertEqual(self.safety.aol_get_permission_mask(), 1)
|
||||
for address in (0x183, 0x343):
|
||||
self.assertEqual(self.safety.safety_fwd_hook(2, address), 0)
|
||||
for value in (0, 100, -100):
|
||||
self.assertFalse(self.safety.safety_tx_hook(libsafety_py.make_CANPacket(
|
||||
0x183, 0, (value & 0xffff).to_bytes(2, 'big') + bytes(6))))
|
||||
|
||||
|
||||
class TestYarisAlphaSecocAol(TestToyotaSecocAol):
|
||||
WORD = 6217
|
||||
|
||||
def test_release_refuses_secoc_aol_and_radar_keepalive(self):
|
||||
release = libsafety_py.ffi.dlopen(libsafety_py._build_libsafety(release=True))
|
||||
native = TestToyotaSecocAol(methodName='runTest')
|
||||
native.safety = release
|
||||
for word in (2121, 2633, 6217):
|
||||
native.init(word)
|
||||
native.physical(cruise=True)
|
||||
self.assertEqual(release.aol_get_request_mask(), 0)
|
||||
self.assertEqual(release.aol_get_permission_mask(), 0)
|
||||
self.assertFalse(release.safety_tx_hook(libsafety_py.make_CANPacket(
|
||||
0x750, 0, bytes.fromhex('0f023e0000000000'))))
|
||||
native.tearDown()
|
||||
|
||||
def test_radar_keepalive_is_exact_and_profile_scoped(self):
|
||||
for word in (2121, 2633, 6217, 6216, 6218):
|
||||
self.init(word)
|
||||
for bus in (0, 1, 2):
|
||||
for payload in (bytes.fromhex('0f023e0000000000'), bytes.fromhex('0f03280101000000'),
|
||||
bytes.fromhex('6d023e0000000000'), bytes.fromhex('0f023e0000000001')):
|
||||
packet = libsafety_py.make_CANPacket(0x750, bus, payload)
|
||||
self.assertEqual(bool(self.safety.safety_tx_hook(packet)),
|
||||
word == 6217 and bus == 0 and payload == bytes.fromhex('0f023e0000000000'))
|
||||
self.init()
|
||||
self.physical(cruise=True)
|
||||
for address in (0x183, 0x343):
|
||||
self.assertEqual(self.safety.safety_fwd_hook(2, address), -1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Always On Lateral on the SecOC Sienna (TOYOTA_SIENNA_4TH_GEN, safety param 2121)."""
|
||||
"""SecOC AOL factories, key admission, and hybrid gear policy including radar Yaris."""
|
||||
import unittest
|
||||
|
||||
from opendbc.car import gen_empty_fingerprint, structs
|
||||
@@ -11,11 +11,11 @@ from openpilot.starpilot.longitudinal.toyota_output_policy import production_ena
|
||||
TOYOTA = int(structs.CarParams.SafetyModel.toyota)
|
||||
|
||||
|
||||
def secoc_params(vehicle, key=True):
|
||||
def secoc_params(vehicle, key=True, *, alpha=False):
|
||||
fp = gen_empty_fingerprint()
|
||||
fp[0][0x3F6] = 8
|
||||
cp = CarInterface.get_params(vehicle, fp, [structs.CarParams.CarFw(ecu=structs.CarParams.Ecu.hybrid)],
|
||||
False, False, False)
|
||||
alpha, False, False)
|
||||
cp.secOcKeyAvailable = key
|
||||
return cp
|
||||
|
||||
@@ -108,7 +108,7 @@ TEST_KEY = '11' * 16
|
||||
|
||||
|
||||
@contextmanager
|
||||
def actual_card(raw_key=TEST_KEY, *, simulation=True, vehicle=CAR.TOYOTA_SIENNA_4TH_GEN):
|
||||
def actual_card(raw_key=TEST_KEY, *, simulation=True, vehicle=CAR.TOYOTA_SIENNA_4TH_GEN, alpha=False):
|
||||
with OpenpilotPrefix(), mock.patch.dict(os.environ, {'SIMULATION': '1' if simulation else '0', 'AOL_REPLAY_RUNTIME': '0',
|
||||
'TOYOTA_LONG_OUTPUT_REPLAY_RUNTIME': '0'}):
|
||||
params = Params()
|
||||
@@ -118,7 +118,7 @@ def actual_card(raw_key=TEST_KEY, *, simulation=True, vehicle=CAR.TOYOTA_SIENNA_
|
||||
params.put('LKASButtonControl', 8, block=True)
|
||||
if raw_key is not None:
|
||||
params.put('SecOCKey', raw_key, block=True)
|
||||
cp = secoc_params(vehicle, key=False)
|
||||
cp = secoc_params(vehicle, key=False, alpha=alpha)
|
||||
interface = CarInterface(cp)
|
||||
|
||||
with mock.patch('openpilot.selfdrive.car.card.open', side_effect=FileNotFoundError, create=True):
|
||||
@@ -130,7 +130,7 @@ def actual_card(raw_key=TEST_KEY, *, simulation=True, vehicle=CAR.TOYOTA_SIENNA_
|
||||
del selected
|
||||
|
||||
|
||||
def parsed_state(interface, *, gear=3, main=True, door=False, belt=True, eps=1, lda=0, now_ns=1_000_000_000, ticks=8):
|
||||
def parsed_state(interface, *, gear=3, main=True, door=False, belt=True, eps=1, lda=0, now_ns=1_000_000_000, ticks=8, cruise=False):
|
||||
interface.update([])
|
||||
packer = getattr(interface, '_secoc_fixture_packer', None)
|
||||
if packer is None:
|
||||
@@ -138,7 +138,7 @@ def parsed_state(interface, *, gear=3, main=True, door=False, belt=True, eps=1,
|
||||
interface._secoc_fixture_packer = packer
|
||||
tracked = {bus: tuple(parser.vl) for bus, parser in interface.can_parsers.items()}
|
||||
values: dict[str, dict[str, float]] = {
|
||||
'PCM_CRUISE': {'CRUISE_ACTIVE': 0}, 'PCM_CRUISE_2': {'MAIN_ON': main},
|
||||
'PCM_CRUISE': {'CRUISE_ACTIVE': cruise}, 'PCM_CRUISE_2': {'MAIN_ON': main},
|
||||
'GEAR_PACKET_HYBRID': {'GEAR': gear}, 'EPS_STATUS': {'LKA_STATE': eps},
|
||||
'BODY_CONTROL_STATE': {'DOOR_OPEN_FL': door, 'SEATBELT_DRIVER_UNLATCHED': not belt},
|
||||
'WHEEL_SPEEDS': {f'WHEEL_SPEED_{wheel}': 72 for wheel in ('FR', 'FL', 'RR', 'RL')},
|
||||
@@ -162,22 +162,24 @@ def parsed_state(interface, *, gear=3, main=True, door=False, belt=True, eps=1,
|
||||
return state, frames
|
||||
|
||||
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME))
|
||||
@pytest.mark.parametrize('vehicle,alpha', ((CAR.TOYOTA_SIENNA_4TH_GEN, False), (CAR.TOYOTA_RAV4_PRIME, False),
|
||||
(CAR.TOYOTA_YARIS, False), (CAR.TOYOTA_YARIS, True)))
|
||||
@pytest.mark.parametrize('key', (None, '', 'not-a-key', '11' * 15, TEST_KEY))
|
||||
def test_actual_card_decodes_key_before_aol_admission_and_publishes_exact_profile(key, vehicle):
|
||||
with actual_card(key, vehicle=vehicle) as (selected, saved):
|
||||
def test_actual_card_decodes_key_before_aol_admission_and_publishes_exact_profile(key, vehicle, alpha):
|
||||
with actual_card(key, vehicle=vehicle, alpha=alpha) as (selected, saved):
|
||||
admitted = decode_key(key) is not None
|
||||
assert selected.CP.secOcKeyAvailable == admitted
|
||||
assert selected.aol_qualified == admitted
|
||||
assert selected.CP.alternativeExperience == (32 if admitted else 0)
|
||||
assert selected.CP.safetyConfigs[0].safetyParam == 2121
|
||||
assert selected.CP.secOcRequired and selected.CP.openpilotLongitudinalControl
|
||||
word = (6217 if alpha else 2633) if vehicle == CAR.TOYOTA_YARIS else 2121
|
||||
assert selected.CP.safetyConfigs[0].safetyParam == word
|
||||
assert selected.CP.secOcRequired and selected.CP.openpilotLongitudinalControl == (vehicle != CAR.TOYOTA_YARIS or alpha)
|
||||
assert policy_for(selected.CP).auxiliary_lkas_supported
|
||||
assert saved.get('LKASButtonControl') == 8
|
||||
if admitted:
|
||||
assert selected.aol_card_intent.settings.lkas_action == selected.aol_card_intent.settings.main_action == 0
|
||||
assert selected.CI.CS.secoc_key == selected.CI.CC.secoc_key == bytes.fromhex(TEST_KEY)
|
||||
assert native_matches_cp(selected.CP, TOYOTA, 2121)
|
||||
assert native_matches_cp(selected.CP, TOYOTA, word)
|
||||
assert production_enabled(selected.CP) == (vehicle == CAR.TOYOTA_SIENNA_4TH_GEN)
|
||||
else:
|
||||
assert selected.aol_card_intent is None
|
||||
@@ -199,10 +201,11 @@ def test_actual_card_decodes_key_before_aol_admission_and_publishes_exact_profil
|
||||
del controls
|
||||
|
||||
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME))
|
||||
@pytest.mark.parametrize('vehicle,alpha', ((CAR.TOYOTA_SIENNA_4TH_GEN, False), (CAR.TOYOTA_RAV4_PRIME, False),
|
||||
(CAR.TOYOTA_YARIS, False), (CAR.TOYOTA_YARIS, True)))
|
||||
@pytest.mark.parametrize('gear,expected', ((0, 'park'), (1, 'reverse'), (2, 'neutral'), (3, 'drive'), (4, 'brake')))
|
||||
def test_actual_secoc_factory_parser_uses_hybrid_gear_without_legacy_gear(gear, expected, vehicle):
|
||||
cp = secoc_params(vehicle)
|
||||
def test_actual_secoc_factory_parser_uses_hybrid_gear_without_legacy_gear(gear, expected, vehicle, alpha):
|
||||
cp = secoc_params(vehicle, alpha=alpha)
|
||||
interface = CarInterface(cp)
|
||||
state, frames = parsed_state(interface, gear=gear)
|
||||
assert state.canValid and not state.canTimeout
|
||||
@@ -220,7 +223,7 @@ def test_actual_secoc_factory_parser_uses_hybrid_gear_without_legacy_gear(gear,
|
||||
assert str(after_legacy.gearShifter) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_YARIS, CAR.TOYOTA_RAV4_TSS2_2023))
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_RAV4_TSS2_2023,))
|
||||
def test_other_secoc_and_angle_factories_never_join_sienna_aol(vehicle):
|
||||
cp = CarInterface.get_params(vehicle, gen_empty_fingerprint(), [], False, False, False)
|
||||
cp.secOcKeyAvailable = True
|
||||
@@ -316,12 +319,38 @@ def test_secoc_fixture_advances_real_counter_and_duplicate_counter_invalidates_c
|
||||
assert not state.canValid and not state.canTimeout
|
||||
|
||||
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME))
|
||||
@pytest.mark.parametrize('vehicle,alpha', ((CAR.TOYOTA_SIENNA_4TH_GEN, False), (CAR.TOYOTA_RAV4_PRIME, False),
|
||||
(CAR.TOYOTA_YARIS, False), (CAR.TOYOTA_YARIS, True)))
|
||||
@pytest.mark.parametrize('gear,drivable', ((0, False), (1, False), (2, False), (3, True), (4, True)))
|
||||
def test_secoc_parsed_gear_matches_common_entry_policy(vehicle, gear, drivable):
|
||||
def test_secoc_parsed_gear_matches_common_entry_policy(vehicle, alpha, gear, drivable):
|
||||
from openpilot.selfdrive.car.car_events import CarEvents
|
||||
from openpilot.cereal import log
|
||||
cp = secoc_params(vehicle)
|
||||
cp = secoc_params(vehicle, alpha=alpha)
|
||||
current, _ = parsed_state(CarInterface(cp), gear=gear)
|
||||
events = CarEvents(cp).create_common_events(current, current)
|
||||
assert (log.OnroadEvent.EventName.wrongGear not in events.names) == drivable
|
||||
|
||||
|
||||
def test_every_secoc_factory_is_accounted_for():
|
||||
assert {vehicle for vehicle in CAR if vehicle.config.flags & ToyotaFlags.SECOC} == {CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME, CAR.TOYOTA_YARIS}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('alpha', (False, True))
|
||||
@pytest.mark.parametrize('mutation', ('missing_radar', 'wrong_disable', 'wrong_long', 'wrong_word', 'no_key', 'release'))
|
||||
def test_yaris_secoc_refuses_inconsistent_configuration(alpha, mutation):
|
||||
cp = secoc_params(CAR.TOYOTA_YARIS, alpha=alpha)
|
||||
assert qualified(cp)
|
||||
if mutation == 'missing_radar':
|
||||
cp.flags &= ~int(ToyotaFlags.RADAR_ACC)
|
||||
elif mutation == 'wrong_disable':
|
||||
cp.flags ^= int(ToyotaFlags.DISABLE_RADAR)
|
||||
elif mutation == 'wrong_long':
|
||||
cp.openpilotLongitudinalControl = not cp.openpilotLongitudinalControl
|
||||
elif mutation == 'wrong_word':
|
||||
cp.safetyConfigs[0].safetyParam = 2121
|
||||
elif mutation == 'no_key':
|
||||
cp.secOcKeyAvailable = False
|
||||
else:
|
||||
cp.dashcamOnly = True
|
||||
assert not qualified(cp)
|
||||
assert not policy_for(cp).intent_supported
|
||||
|
||||
@@ -27,18 +27,21 @@ class CapturePublisher:
|
||||
self.messages[service] = messaging.log_from_bytes(raw)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('vehicle', (CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME))
|
||||
def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle):
|
||||
@pytest.mark.parametrize('vehicle,alpha,ordinary', ((CAR.TOYOTA_SIENNA_4TH_GEN, False, False),
|
||||
(CAR.TOYOTA_RAV4_PRIME, False, False), (CAR.TOYOTA_YARIS, False, False),
|
||||
(CAR.TOYOTA_YARIS, True, False), (CAR.TOYOTA_YARIS, False, True), (CAR.TOYOTA_YARIS, True, True)))
|
||||
def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle, alpha, ordinary):
|
||||
case = IoniqAvailabilityTests(methodName='runTest')
|
||||
with actual_card(simulation=False, vehicle=vehicle) as (selected, _), ExitStack() as cleanup:
|
||||
with actual_card(simulation=False, vehicle=vehicle, alpha=alpha) as (selected, _), ExitStack() as cleanup:
|
||||
cleanup.callback(case.doCleanups)
|
||||
sd, controls, step = case._authority_callers(
|
||||
False, cp=selected.CP.as_reader().as_builder(), genuine_events=True)
|
||||
ordinary, cp=selected.CP.as_reader().as_builder(), genuine_events=True)
|
||||
assert not selected.sm.simulation and not sd.sm.simulation and not controls.sm.simulation
|
||||
capture = CapturePublisher()
|
||||
cleanup.enter_context(mock.patch.object(selected, 'pm', capture))
|
||||
cleanup.enter_context(mock.patch('openpilot.selfdrive.car.card.REPLAY', False))
|
||||
transition = cleanup.enter_context(mock.patch.object(sd.state_machine, 'update', wraps=sd.state_machine.update))
|
||||
radar_startup = cleanup.enter_context(mock.patch('opendbc.car.toyota.interface.disable_ecu'))
|
||||
apply = cleanup.enter_context(mock.patch.object(selected.CI, 'apply', wraps=selected.CI.apply))
|
||||
original_update_msgs = controls.sm.update_msgs
|
||||
def model_target(timestamp, messages):
|
||||
@@ -57,13 +60,14 @@ def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle):
|
||||
safety = libsafety_py.libsafety
|
||||
safety.init_tests()
|
||||
safety.set_alternative_experience(32)
|
||||
safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, 2121)
|
||||
word = selected.CP.safetyConfigs[0].safetyParam
|
||||
safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, word)
|
||||
def reset_native():
|
||||
safety.set_alternative_experience(0)
|
||||
safety.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0)
|
||||
cleanup.callback(reset_native)
|
||||
base = 10_000_000_000
|
||||
warm, _ = parsed_state(selected.CI, now_ns=base - 80_000_000)
|
||||
warm, _ = parsed_state(selected.CI, now_ns=base - 80_000_000, cruise=ordinary)
|
||||
assert warm.canValid and not warm.canTimeout
|
||||
original_message = messaging.new_message
|
||||
driving_torques = []
|
||||
@@ -78,16 +82,18 @@ def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle):
|
||||
|
||||
with mock.patch('openpilot.selfdrive.car.card.time.monotonic_ns', return_value=now), \
|
||||
mock.patch('openpilot.selfdrive.car.card.messaging.new_message', side_effect=clocked_message):
|
||||
physical, frames = parsed_state(selected.CI, gear=gear, now_ns=now, ticks=1)
|
||||
physical, frames = parsed_state(selected.CI, gear=gear, now_ns=now, ticks=1, cruise=ordinary)
|
||||
assert physical.canValid and not physical.canTimeout
|
||||
safety.set_timer(now // 1000)
|
||||
safety.set_aol_test_heartbeat(True)
|
||||
for address, payload, bus in frames:
|
||||
assert safety.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, payload))
|
||||
safety.aol_set_host_request(1)
|
||||
want_long = ordinary and selected.CP.openpilotLongitudinalControl
|
||||
wanted = 3 if want_long else 1
|
||||
safety.aol_set_host_request(wanted)
|
||||
request = safety.aol_get_request_mask()
|
||||
permission = safety.aol_get_permission_mask()
|
||||
assert request == 1 and permission == (1 if gear in (3, 4) else 0)
|
||||
assert request == wanted and permission == (wanted if gear in (3, 4) else 0)
|
||||
selected.aol_card_intent.update(physical.as_reader(), now_ns=now)
|
||||
selected.state_publish(physical, None)
|
||||
car_source = capture.messages['carState']
|
||||
@@ -97,21 +103,22 @@ def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle):
|
||||
before = transition.call_count
|
||||
axis, _, command = step(
|
||||
tick, car_state_message=car_source.as_builder(), intent_message=intent_source.as_builder(),
|
||||
allowed=bool(permission & 1), long_allowed=False,
|
||||
native_request_lat=bool(request & 1), native_request_long=False,
|
||||
panda_controls_allowed=False)
|
||||
allowed=bool(permission & 1), long_allowed=bool(permission & 2),
|
||||
native_request_lat=bool(request & 1), native_request_long=bool(request & 2),
|
||||
panda_controls_allowed=bool(safety.get_controls_allowed()))
|
||||
assert transition.call_count == before + 1
|
||||
assert axis.sourceCarStateMonoTime == car_source.logMonoTime == now
|
||||
assert axis.observedMonoTime == now and axis.validUntilMonoTime == now + 30_000_000
|
||||
assert axis.intentProducerSessionId == selected.slc_producer_session
|
||||
assert command.latActive == (gear in (3, 4)) and not command.longActive
|
||||
assert command.latActive == (gear in (3, 4))
|
||||
assert command.longActive == (want_long and gear in (3, 4))
|
||||
native = decode_safety(sd.sm['aolSafetyWire'])
|
||||
assert native is not None and native.observedMonoTime == now
|
||||
assert native.safetyParam == 2121 and native.axisSessionId == axis.sessionId
|
||||
assert native.safetyParam == word and native.axisSessionId == axis.sessionId
|
||||
assert native.requestedLateral == bool(request & 1)
|
||||
assert native.lateralAllowed == bool(permission & 1)
|
||||
assert len(native.pandaInventory) == 1
|
||||
assert native.pandaInventory[0].safetyParam == 2121
|
||||
assert native.pandaInventory[0].safetyParam == word
|
||||
assert native.pandaInventory[0].alternativeExperience == 32
|
||||
control_source = controls.pm.messages['carControl']
|
||||
assert control_source.logMonoTime == now
|
||||
@@ -132,10 +139,23 @@ def test_fresh_secoc_source_permission_sd_controls_card_sendcan_join(vehicle):
|
||||
assert bool(steer[1][0] & 1) == command.latActive
|
||||
assert steer == add_mac(bytes.fromhex(TEST_KEY), 1, 1, counter, (steer[0], steer[1][:4], steer[2]))
|
||||
assert safety.safety_tx_hook(libsafety_py.make_CANPacket(steer[0], steer[2], steer[1]))
|
||||
if alpha:
|
||||
keepalive = [packet for packet in sent.sendcan if packet.address == 0x750]
|
||||
for packet in keepalive:
|
||||
assert bytes(packet.dat) == bytes.fromhex('0f023e0000000000')
|
||||
assert safety.safety_tx_hook(libsafety_py.make_CANPacket(packet.address, packet.src, packet.dat))
|
||||
accel_frames = [packet for packet in sent.sendcan if packet.address == 0x183]
|
||||
if not selected.CP.openpilotLongitudinalControl:
|
||||
assert not accel_frames
|
||||
for packet in accel_frames:
|
||||
assert safety.safety_tx_hook(libsafety_py.make_CANPacket(packet.address, packet.src, packet.dat))
|
||||
torque = int.from_bytes(steer[1][1:3], 'big', signed=True)
|
||||
if gear in (3, 4):
|
||||
driving_torques.append(torque)
|
||||
assert abs(torque) <= min(1500, 15 * (tick + 1))
|
||||
else:
|
||||
assert not axis.lateralActive and torque == 0
|
||||
assert radar_startup.call_count == int(alpha)
|
||||
if alpha:
|
||||
assert radar_startup.call_args.kwargs == {'bus': 0, 'addr': 0x750, 'sub_addr': 0xf, 'com_cont_req': bytes.fromhex('280101')}
|
||||
assert any(torque != 0 for torque in driving_torques), 'Fresh joined Controls must produce actual steering torque'
|
||||
|
||||
@@ -17,14 +17,14 @@ AOL_WORDS = frozenset((
|
||||
0x8042, 0x8242, 0xC042, 0xC242, 0xD049, 0xD249,
|
||||
0xC049, 0xC249,
|
||||
0x9449, 0x9549, 0x9558, 0x9564, 0x9649, 0x9749, 0x9758, 0x9764, 0xD44D, 0xD549, 0xD64D, 0xD749,
|
||||
73, 585, 856, 4169, 4681, 2121, 0x9049, 0x9249,
|
||||
73, 585, 856, 4169, 4681, 2121, 2633, 6217, 0x9049, 0x9249,
|
||||
0xB049, 0xB249, 0x2449, 0x2649, 0x2049, 0x2249, 0x424D, 0x449, 0x649, 0x1449, 0x1649,
|
||||
0x8049, 0x8249, 0x8449, 0x8649, 0x9158, 0x9358, 0x9149,
|
||||
0x9349, 0x9164, 0x9364, 0xd149, 0xd349, 0xd04d, 0xd24d,
|
||||
))
|
||||
|
||||
|
||||
SECOC_TORQUE_CARS = frozenset((CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME))
|
||||
SECOC_TORQUE_CARS = frozenset((CAR.TOYOTA_SIENNA_4TH_GEN, CAR.TOYOTA_RAV4_PRIME, CAR.TOYOTA_YARIS))
|
||||
RADAR_TORQUE_CARS = frozenset((CAR.TOYOTA_CHR_TSS2, CAR.TOYOTA_RAV4_TSS2_2022))
|
||||
CAMERA_TORQUE_CARS = frozenset((
|
||||
CAR.TOYOTA_ALPHARD_TSS2, CAR.TOYOTA_AVALON_TSS2, CAR.TOYOTA_CAMRY_TSS2, CAR.TOYOTA_COROLLA_TSS2,
|
||||
@@ -35,18 +35,25 @@ CAMERA_TORQUE_CARS = frozenset((
|
||||
|
||||
|
||||
def secoc_qualified(cp, *, marked_only=False):
|
||||
radar = cp is not None and cp.carFingerprint == CAR.TOYOTA_YARIS
|
||||
required = ToyotaFlags.TSS2 | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC | ToyotaFlags.RAISED_ACCEL_LIMIT
|
||||
if radar:
|
||||
required |= ToyotaFlags.RADAR_ACC
|
||||
if cp.openpilotLongitudinalControl:
|
||||
required |= ToyotaFlags.DISABLE_RADAR
|
||||
allowed = required | ToyotaFlags.HYBRID | ToyotaFlags.HAS_BSM
|
||||
if (cp is None or cp.brand != 'toyota' or cp.carFingerprint not in SECOC_TORQUE_CARS or
|
||||
cp.notCar or cp.passive or cp.dashcamOnly or not cp.pcmCruise or not cp.openpilotLongitudinalControl or
|
||||
cp.notCar or cp.passive or cp.dashcamOnly or not cp.pcmCruise or (not cp.openpilotLongitudinalControl and not radar) or
|
||||
not cp.secOcRequired or not cp.secOcKeyAvailable or
|
||||
cp.steerControlType != structs.CarParams.SteerControlType.torque or len(cp.safetyConfigs) != 1 or
|
||||
cp.alternativeExperience not in ((32,) if marked_only else (0, 32)) or
|
||||
cp.flags & required != required or int(cp.flags) & ~int(allowed)):
|
||||
return False
|
||||
config = cp.safetyConfigs[0]
|
||||
mode = (ToyotaSafetyFlags.SECOC_RADAR_LONG if cp.openpilotLongitudinalControl else
|
||||
ToyotaSafetyFlags.SECOC | ToyotaSafetyFlags.STOCK_LONGITUDINAL) if radar else ToyotaSafetyFlags.SECOC
|
||||
return (config.safetyModel == structs.CarParams.SafetyModel.toyota and
|
||||
config.safetyParam == EPS_SCALE[cp.carFingerprint] | int(ToyotaSafetyFlags.SECOC))
|
||||
config.safetyParam == EPS_SCALE[cp.carFingerprint] | int(mode))
|
||||
|
||||
|
||||
def lexus_is_qualified(cp, *, marked_only=False):
|
||||
|
||||
Reference in New Issue
Block a user