mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 17:13:54 +08:00
Recognize four-byte RAV4 radar filter advertisement
This commit is contained in:
@@ -16,6 +16,13 @@ PROFILES = {
|
||||
def hardware_supported(candidate, fingerprint):
|
||||
# The filter's advertisement is a hardware discriminator, not an actuation
|
||||
# permission or a copy of ACC_CONTROL to decode under a different checksum.
|
||||
# The 2025 radar filter observed in the field uses a four-byte 2FF
|
||||
# advertisement. It is distinct from the legacy eight-byte SDSU state.
|
||||
# Panda discovery may also expose its forwarded camera-bus copy.
|
||||
if (candidate == CAR.TOYOTA_RAV4_TSS2_2023 and fingerprint.get(0, {}).get(0x2FF) == 4 and
|
||||
not any(address in messages for messages in fingerprint.values() for address in (0x201, 0x2AA)) and
|
||||
all(bus in (0, 2) and messages[0x2FF] == 4 for bus, messages in fingerprint.items() if 0x2FF in messages)):
|
||||
return True
|
||||
return (candidate in PROFILES and fingerprint.get(0, {}).get(0x2AA) == 8 and
|
||||
not any(0x2AA in frames for bus, frames in fingerprint.items() if bus != 0) and
|
||||
not any(addr in frames for frames in fingerprint.values() for addr in (0x201, 0x2FF)))
|
||||
|
||||
@@ -82,7 +82,7 @@ class ToyotaFlags(IntFlag):
|
||||
RAISED_ACCEL_LIMIT = 1024
|
||||
SECOC = 2048
|
||||
AUTO_BRAKE_HOLD = 4096
|
||||
RADAR_CAN_FILTER = 1 << 18 # Bus-0 0x2AA radar adapter; distinct from smartDSU
|
||||
RADAR_CAN_FILTER = 1 << 18 # Exact radar adapter; includes four-byte 2FF, distinct from legacy SDSU status
|
||||
GAS_INTERCEPTOR = 1 << 17 # Exact paired pedal owner
|
||||
ZSS = 1 << 16 # Detected Prius secondary steering sensor; host-only, no safetyParam allocation
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"platform": "TOYOTA_RAV4_TSS2_2023",
|
||||
"firmware": [
|
||||
{
|
||||
"ecu": "eps",
|
||||
"fwVersion": "38393635423432333731000000000000",
|
||||
"address": 1953,
|
||||
"subAddress": 0,
|
||||
"brand": "toyota",
|
||||
"bus": 0,
|
||||
"logging": false
|
||||
},
|
||||
{
|
||||
"ecu": "engine",
|
||||
"fwVersion": "0138393636333441533830303100000000",
|
||||
"address": 1792,
|
||||
"subAddress": 0,
|
||||
"brand": "toyota",
|
||||
"bus": 0,
|
||||
"logging": false
|
||||
},
|
||||
{
|
||||
"ecu": "abs",
|
||||
"fwVersion": "0146313532363432463831303000000000",
|
||||
"address": 1968,
|
||||
"subAddress": 0,
|
||||
"brand": "toyota",
|
||||
"bus": 0,
|
||||
"logging": false
|
||||
},
|
||||
{
|
||||
"ecu": "fwdCamera",
|
||||
"fwVersion": "023836343646305230353330300000000038363436473052303233303000000000",
|
||||
"address": 1872,
|
||||
"subAddress": 109,
|
||||
"brand": "toyota",
|
||||
"bus": 0,
|
||||
"logging": false
|
||||
},
|
||||
{
|
||||
"ecu": "fwdRadar",
|
||||
"fwVersion": "0138383231463052303331303000000000",
|
||||
"address": 1872,
|
||||
"subAddress": 15,
|
||||
"brand": "toyota",
|
||||
"bus": 0,
|
||||
"logging": false
|
||||
}
|
||||
],
|
||||
"advertisements": [
|
||||
{
|
||||
"bus": 0,
|
||||
"address": 767,
|
||||
"data": "00000409"
|
||||
},
|
||||
{
|
||||
"bus": 2,
|
||||
"address": 767,
|
||||
"data": "00000409"
|
||||
}
|
||||
],
|
||||
"scope": "Anonymized 2025 RAV4 Hybrid discovery evidence. Four-byte filter presence is not the legacy eight-byte SDSU status signal. No VIN, GPS, credentials or driver data."
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from opendbc.car import gen_empty_fingerprint, structs
|
||||
from opendbc.car.fw_versions import match_fw_to_car
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.radar_filter import hardware_supported, configured
|
||||
from opendbc.car.toyota.values import CAR, ToyotaFlags
|
||||
from openpilot.starpilot.car.toyota.aol import qualified
|
||||
|
||||
DATA=Path(__file__).with_name('data')/'rav4_2025_filter_discovery.json'
|
||||
|
||||
|
||||
def observed():
|
||||
evidence=json.loads(DATA.read_text())
|
||||
fw=[structs.CarParams.CarFw(**{**f,'fwVersion':bytes.fromhex(f['fwVersion'])}) for f in evidence['firmware']]
|
||||
fp=gen_empty_fingerprint()
|
||||
for f in evidence['advertisements']:
|
||||
fp[f['bus']][f['address']]=len(bytes.fromhex(f['data']))
|
||||
return fw,fp
|
||||
|
||||
|
||||
@pytest.mark.parametrize('alpha',[False,True])
|
||||
def test_observed_firmware_and_filter_keep_radar_enabled(alpha):
|
||||
fw,fp=observed()
|
||||
exact,found=match_fw_to_car(fw,'',log=False)
|
||||
assert exact and found=={CAR.TOYOTA_RAV4_TSS2_2023}
|
||||
cp=CarInterface.get_params(CAR.TOYOTA_RAV4_TSS2_2023,fp,fw,alpha,False,False)
|
||||
old=gen_empty_fingerprint()
|
||||
old[0][0x2AA]=8
|
||||
canonical=CarInterface.get_params(CAR.TOYOTA_RAV4_TSS2_2023,old,fw,alpha,False,False)
|
||||
assert cp.to_dict()==canonical.to_dict()
|
||||
assert hardware_supported(CAR.TOYOTA_RAV4_TSS2_2023,fp) and configured(cp) and qualified(cp)
|
||||
assert cp.flags & ToyotaFlags.RADAR_CAN_FILTER and not cp.flags & ToyotaFlags.DISABLE_RADAR
|
||||
assert cp.safetyConfigs[0].safetyParam==(0x1449 if alpha else 0x1649)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('bad',['missing_bus0','wrong_bus','bad_dlc0','bad_dlc2','pedal_mix','mixed_advertisement','legacy_sdsu','other_model'])
|
||||
def test_observed_filter_admission_rejects_other_topologies(bad):
|
||||
_,fp=observed()
|
||||
candidate=CAR.TOYOTA_RAV4_TSS2_2023
|
||||
if bad=='missing_bus0':
|
||||
fp[0].clear()
|
||||
elif bad=='wrong_bus':
|
||||
fp[1][0x2FF]=4
|
||||
elif bad=='bad_dlc0':
|
||||
fp[0][0x2FF]=3
|
||||
elif bad=='bad_dlc2':
|
||||
fp[2][0x2FF]=8
|
||||
elif bad=='pedal_mix':
|
||||
fp[0][0x201]=6
|
||||
elif bad=='mixed_advertisement':
|
||||
fp[0][0x2AA]=8
|
||||
elif bad=='legacy_sdsu':
|
||||
fp[0][0x2FF]=8
|
||||
fp[2].clear()
|
||||
elif bad=='other_model':
|
||||
candidate=CAR.TOYOTA_RAV4H
|
||||
assert not hardware_supported(candidate,fp)
|
||||
@@ -0,0 +1,17 @@
|
||||
import pytest
|
||||
from opendbc.car.toyota.values import CAR
|
||||
from openpilot.starpilot.car.toyota.tests.test_rav4_2025_filter_advert import observed
|
||||
from openpilot.starpilot.car.toyota.tests import test_rav4_angle_filter_callers 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('alpha,disable,radar_mode',[(False,False,0),(True,False,0),(True,False,1),(True,False,2),(True,True,1)])
|
||||
@pytest.mark.parametrize('aol',[False,True])
|
||||
def test_recorded_2025_filter_actual_callers(alpha,disable,radar_mode,aol,tmp_path,host_heartbeat_bridge):
|
||||
fw,_=observed()
|
||||
advertisements=[(0x2FF,bytes.fromhex('00000409'),bus) for bus in (0,2)]
|
||||
helpers.run_case(CAR.TOYOTA_RAV4_TSS2_2023,alpha,disable,radar_mode,tmp_path,aol=aol,
|
||||
bridge=host_heartbeat_bridge,filter_advertisements=advertisements,recorded_firmware=fw)
|
||||
@@ -36,7 +36,8 @@ def test_rav4_filter_actual_callers(alpha, disable, radar_mode, tmp_path):
|
||||
run_case(CAR.TOYOTA_RAV4_TSS2_2023, alpha, disable, radar_mode, tmp_path)
|
||||
|
||||
|
||||
def run_case(candidate, alpha, disable, radar_mode, tmp_path, *, aol=True, has_filter=True, bridge=None):
|
||||
def run_case(candidate, alpha, disable, radar_mode, tmp_path, *, aol=True, has_filter=True, bridge=None,
|
||||
filter_advertisements=None, recorded_firmware=None):
|
||||
helpers.SYNTHETIC_LAST.clear()
|
||||
helpers.NATIVE_LAST.clear()
|
||||
safety = libsafety_py.libsafety
|
||||
@@ -77,12 +78,13 @@ def run_case(candidate, alpha, disable, radar_mode, tmp_path, *, aol=True, has_f
|
||||
('DisableOpenpilotLongitudinal', disable), ('AlwaysOnLateral', aol), ('SafeMode', False)]:
|
||||
params.put_bool(key, value, block=True)
|
||||
params.put('ToyotaRadarMode', radar_mode, block=True)
|
||||
fw = helpers.firmware(candidate)
|
||||
fw = helpers.firmware(candidate) if recorded_firmware is None else recorded_firmware
|
||||
exact, found = match_fw_to_car(fw, '', log=False)
|
||||
assert exact and found == {candidate}
|
||||
fp = gen_empty_fingerprint()
|
||||
if has_filter:
|
||||
fp[0][0x2AA] = 8
|
||||
advertisements = ([(0x2AA, bytes(8), 0)] if has_filter else []) if filter_advertisements is None else filter_advertisements
|
||||
for address, data, bus in advertisements:
|
||||
fp[bus][address] = len(data)
|
||||
observed = (candidate, fp, '0' * 17, fw, structs.CarParams.FingerprintSource.fw, True)
|
||||
with (patch.object(car_helpers, 'fingerprint', return_value=observed),
|
||||
patch.object(card_module, 'can_comm_callbacks', return_value=(diag_recv, diag_send)),
|
||||
@@ -143,7 +145,7 @@ def run_case(candidate, alpha, disable, radar_mode, tmp_path, *, aol=True, has_f
|
||||
packer.make_can_msg('EPS_STATUS', 0, {'LKA_STATE': 9 if fault else 1, 'LTA_STATE': 9 if fault else 1}),
|
||||
packer.make_can_msg('PCS_HUD', 0, {}),
|
||||
# The physical filter advertises itself; it does not grant controls.
|
||||
*([(0x2AA, bytes(8), 0)] if has_filter else [])]
|
||||
*advertisements]
|
||||
if not has_filter and not long:
|
||||
frames.append(packer.make_can_msg('ACC_CONTROL', 0, {'ACC_TYPE': 1}))
|
||||
# Stock AEB/PCS traffic must not trip the ACC relay check.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "408bff97273ca6b8bb9f4ce499b11438df2c64c5e2f8fa922c3c641d6da56a97", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "9fa186b72e7a6036ed6d85896b79c0919308108e1b71d7ccad3d44dbf6c28c5a", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user