Recognize Camry filter with independent cluster radar traffic

This commit is contained in:
firestar5683
2026-10-28 08:54:56 -05:00
parent 63243d7e2d
commit c03f84cf61
3 changed files with 79 additions and 2 deletions
@@ -13,6 +13,14 @@ PROFILES = {
}
def _camry_radar_bus(candidate, bus, messages):
# The 2018–20 Camry exposes an independent eight-byte 2FF stream alongside
# its six cluster-radar frames on bus1 (cluster_radar.CLUSTERS). It is not
# the four-byte filter advertisement or a bus0 smartDSU status source.
return (candidate == CAR.TOYOTA_CAMRY and bus == 1 and messages.get(0x2FF) == 8 and
all(messages.get(address) == 8 for address in range(0x680, 0x686)))
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.
@@ -21,11 +29,13 @@ def hardware_supported(candidate, fingerprint):
# Panda discovery may also expose its forwarded camera-bus copy.
if (candidate in PROFILES 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)):
all((bus in (0, 2) and messages[0x2FF] == 4) or _camry_radar_bus(candidate, bus, messages)
for bus, messages in fingerprint.items() if 0x2FF in messages)):
return True
return (candidate in PROFILES and fingerprint.get(0, {}).get(0x2AA) == 8 and
all(bus in (0, 2) and frames[0x2AA] == 8 for bus, frames in fingerprint.items() if 0x2AA in frames) and
not any(addr in frames for frames in fingerprint.values() for addr in (0x201, 0x2FF)))
not any(0x201 in frames or 0x2FF in frames and not _camry_radar_bus(candidate, bus, frames)
for bus, frames in fingerprint.items()))
def configured(cp):
@@ -0,0 +1,57 @@
"""Camry cluster-radar traffic must not impersonate a bus-0 filter status."""
import pytest
from opendbc.car import gen_empty_fingerprint
from opendbc.car.toyota import radar_filter
from opendbc.car.toyota.cluster_radar import CLUSTERS
from opendbc.car.toyota.interface import CarInterface
from opendbc.car.toyota.values import CAR
from openpilot.starpilot.car.toyota.aol import qualified
def observed():
fp = gen_empty_fingerprint()
fp[0][0x2FF] = fp[2][0x2FF] = 4
fp[1] = {0x2FF: 8, **dict.fromkeys(CLUSTERS, 8)}
return fp
@pytest.mark.parametrize('alpha', (False, True))
@pytest.mark.parametrize('primary', (0x2FF, 0x2AA))
def test_camry_radar_bus_preserves_exact_filter_owner(alpha, primary):
fp = observed()
if primary == 0x2AA:
fp[0] = fp[2] = {primary: 8}
canonical = gen_empty_fingerprint()
canonical[0][primary] = 4 if primary == 0x2FF else 8
expected = CarInterface.get_params(CAR.TOYOTA_CAMRY, canonical, [], alpha, False, False)
actual = CarInterface.get_params(CAR.TOYOTA_CAMRY, fp, [], alpha, False, False)
assert radar_filter.hardware_supported(CAR.TOYOTA_CAMRY, fp)
assert actual.to_dict() == expected.to_dict()
assert actual.safetyConfigs[0].safetyParam == (0x1049 if alpha else 0x1249)
assert actual.openpilotLongitudinalControl == alpha and qualified(actual)
@pytest.mark.parametrize('bad', ('missing_filter', 'bus1_short', 'missing_cluster', 'bad_cluster',
'wrong_cluster_bus', 'wrong_model', 'bus1_advert', 'mixed_status', 'pedal'))
def test_camry_radar_bus_is_not_general_adapter_evidence(bad):
fp = observed()
candidate = CAR.TOYOTA_CAMRY
if bad == 'missing_filter':
fp[0].clear()
elif bad == 'bus1_short':
fp[1][0x2FF] = 7
elif bad == 'missing_cluster':
del fp[1][CLUSTERS[-1]]
elif bad == 'bad_cluster':
fp[1][CLUSTERS[-1]] = 7
elif bad == 'wrong_cluster_bus':
fp[3] = fp.pop(1)
elif bad == 'wrong_model':
candidate = CAR.TOYOTA_CHR
elif bad == 'bus1_advert':
fp[1][0x2FF] = 4
elif bad == 'mixed_status':
fp[2][0x2FF] = 8
else:
fp[0][0x201] = 6
assert not radar_filter.hardware_supported(candidate, fp)
@@ -13,3 +13,13 @@ daemon_gc_scope=helpers.daemon_gc_scope
def test_other_filter_variants_full_callers(candidate,alpha,disable,aol,tmp_path,host_heartbeat_bridge):
advertisements=[(0x2FF,bytes.fromhex('00000409'),bus) for bus in (0,2)]
helpers.run_case(candidate,alpha,disable,0,tmp_path,aol=aol,bridge=host_heartbeat_bridge,filter_advertisements=advertisements)
@pytest.mark.parametrize('alpha,disable', [(False, False), (True, False), (True, True)])
def test_camry_cluster_bus_advertisement_actual_callers(alpha, disable, tmp_path, host_heartbeat_bridge):
from opendbc.car.toyota.cluster_radar import CLUSTERS
advertisements = [(0x2FF, bytes.fromhex('00000409'), bus) for bus in (0, 2)]
advertisements += [(0x2FF, bytes.fromhex('100068010000b5ff'), 1)]
advertisements += [(address, bytes(8), 1) for address in CLUSTERS]
helpers.run_case(CAR.TOYOTA_CAMRY, alpha, disable, 0, tmp_path, aol=True,
bridge=host_heartbeat_bridge, filter_advertisements=advertisements)