fuzzy fingerprint: track number of matching ECUs, not FW versions (#28423)

* first attempt trying to do it implicitly bad

* back to lists and explicitly do this

* continue checking if candidate switches on these, that's fine to check

* debugging

* clean up

* more clean up

* spot free

* only need to create/update on change

* fix test

* draft

* Revert "draft"

This reverts commit dbe32f58b1.

* Revert "fix test"

This reverts commit 1d34269fe2.

* fix test for real

* first draft of test

* this should be good

* can replace existing fuzzy test

* rm

* matching

* test non-empty fws

* TEST DOWN HERE

* no test

* unique + fix skip message + comment + clean up

* less confusing

* ing

* better
pull/28441/head
Shane Smiskol 2 years ago committed by GitHub
parent 42eb7c527e
commit 9e8936be98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      selfdrive/car/fw_versions.py
  2. 28
      selfdrive/car/tests/test_fw_fingerprint.py

@ -75,24 +75,25 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
for f in fws:
all_fw_versions[(addr[1], addr[2], f)].append(candidate)
match_count = 0
matched_ecus = set()
candidate = None
for addr, versions in fw_versions_dict.items():
ecu_key = (addr[0], addr[1])
for version in versions:
# All cars that have this FW response on the specified address
candidates = all_fw_versions[(addr[0], addr[1], version)]
candidates = all_fw_versions[(*ecu_key, version)]
if len(candidates) == 1:
match_count += 1
matched_ecus.add(ecu_key)
if candidate is None:
candidate = candidates[0]
# We uniquely matched two different cars. No fuzzy match possible
elif candidate != candidates[0]:
return set()
if match_count >= 2:
if len(matched_ecus) >= 2:
if log:
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {match_count} matching ECUs")
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {len(matched_ecus)} matching ECUs")
return {candidate}
else:
return set()

@ -48,24 +48,26 @@ class TestFwFingerprint(unittest.TestCase):
self.assertFingerprints(matches, car_model)
@parameterized.expand([(b, c, e[c]) for b, e in VERSIONS.items() for c in e])
def test_fuzzy_match(self, brand, car_model, ecus):
# TODO: speed up fuzzy matching and test more
CP = car.CarParams.new_message()
for _ in range(5):
def test_fuzzy_match_ecu_count(self, brand, car_model, ecus):
# Asserts that fuzzy matching does not count matching FW, but ECU address keys
valid_ecus = [e for e in ecus if e[0] not in FUZZY_EXCLUDE_ECUS and len(ecus[e])]
if not len(valid_ecus):
raise unittest.SkipTest("Car model has no compatible ECUs for fuzzy matching")
fw = []
for ecu, fw_versions in ecus.items():
if not len(fw_versions):
raise unittest.SkipTest("Car model has no FW versions")
for ecu in valid_ecus:
ecu_name, addr, sub_addr = ecu
fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand,
for _ in range(5):
# Add multiple FW versions to simulate ECU returning to multiple queries in a brand
fw.append({"ecu": ecu_name, "fwVersion": random.choice(ecus[ecu]), 'brand': brand,
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr})
CP.carFw = fw
CP = car.CarParams.new_message(carFw=fw)
_, matches = match_fw_to_car(CP.carFw, allow_exact=False, log=False)
# Assert no match if there are not enough valid ECUs
valid_ecus = [f['ecu'] for f in fw if f['ecu'] not in FUZZY_EXCLUDE_ECUS]
if len(valid_ecus) < 2:
self.assertEqual(len(matches), 0, valid_ecus)
# Assert no match if there are not enough unique ECUs
unique_ecus = {(f['address'], f['subAddress']) for f in fw}
if len(unique_ecus) < 2:
self.assertEqual(len(matches), 0)
# There won't always be a match due to shared FW, but if there is it should be correct
elif len(matches):
self.assertFingerprints(matches, car_model)

Loading…
Cancel
Save