Recognize four-byte RAV4 radar filter advertisement

This commit is contained in:
firestar5683
2026-10-22 17:32:58 -05:00
parent 59c29e6e9c
commit b781fa90d7
7 changed files with 156 additions and 7 deletions
@@ -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)))
+1 -1
View File
@@ -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
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "408bff97273ca6b8bb9f4ce499b11438df2c64c5e2f8fa922c3c641d6da56a97", "schema_version": 1}
{"protected_inventory_sha256": "9fa186b72e7a6036ed6d85896b79c0919308108e1b71d7ccad3d44dbf6c28c5a", "schema_version": 1}