openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

128 lines
4.6 KiB

#!/usr/bin/env python3
import unittest
from collections.abc import Iterable
import capnp
from hypothesis import settings, given, strategies as st
from parameterized import parameterized
from cereal import car
from openpilot.selfdrive.car.ford.values import CAR, FW_QUERY_CONFIG, get_platform_codes
from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu
ECU_ADDRESSES = {
Ecu.eps: 0x730, # Power Steering Control Module (PSCM)
Ecu.abs: 0x760, # Anti-Lock Brake System (ABS)
Ecu.fwdRadar: 0x764, # Cruise Control Module (CCM)
Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA)
Ecu.engine: 0x7E0, # Powertrain Control Module (PCM)
Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM)
Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM)
}
ECU_FW_CORE = {
Ecu.eps: [
b"14D003",
],
Ecu.abs: [
b"2D053",
],
Ecu.fwdRadar: [
b"14D049",
],
Ecu.fwdCamera: [
b"14F397", # Ford Q3
b"14H102", # Ford Q4
],
}
class TestFordFW(unittest.TestCase):
def test_fw_query_config(self):
for (ecu, addr, subaddr) in FW_QUERY_CONFIG.extra_ecus:
self.assertIn(ecu, ECU_ADDRESSES, "Unknown ECU")
self.assertEqual(addr, ECU_ADDRESSES[ecu], "ECU address mismatch")
self.assertIsNone(subaddr, "Unexpected ECU subaddress")
@parameterized.expand(FW_VERSIONS.items())
def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[capnp.lib.capnp._EnumModule, int, int | None], Iterable[bytes]]):
for (ecu, addr, subaddr), fws in fw_versions.items():
self.assertIn(ecu, ECU_FW_CORE, "Unexpected ECU")
self.assertEqual(addr, ECU_ADDRESSES[ecu], "ECU address mismatch")
self.assertIsNone(subaddr, "Unexpected ECU subaddress")
for fw in fws:
codes = get_platform_codes([fw])
self.assertEqual(1, len(codes), f"Unable to parse FW: {fw!r}")
@settings(max_examples=100)
@given(data=st.data())
def test_platform_codes_fuzzy_fw(self, data):
"""Ensure function doesn't raise an exception"""
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)
def test_platform_codes_spot_check(self):
# Asserts basic platform code parsing behavior for a few cases
results = get_platform_codes([
b"JX6A-14C204-BPL\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00",
])
self.assertEqual(results, {(b"X6A", b"J"), (b"Z6T", b"N"), (b"J6T", b"P"), (b"B5A", b"L")})
def test_match_fw_fuzzy(self):
offline_fw = {
(Ecu.eps, 0x730, None): [
b"L1MC-14D003-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
],
(Ecu.abs, 0x760, None): [
b"L1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"L1MC-2D053-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
],
(Ecu.fwdRadar, 0x764, None): [
b"LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"LB5T-14D049-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
],
(Ecu.fwdCamera, 0x706, None): [
b"LB5T-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
b"LB5T-14F397-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
],
}
live_fw = {
(0x730, None): {b"L1MC-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
(0x760, None): {b"L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
(0x764, None): {b"LB5T-14D049-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
(0x706, None): {b"LB5T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
}
# for this test, check that none of the live FW matches matches the offline FW exactly
for (_, addr, subaddr), fws in offline_fw.items():
live_ecu_fw = live_fw.get((addr, subaddr), set())
self.assertEqual(0, len(set(fws).intersection(live_ecu_fw)))
expected_fingerprint = CAR.EXPLORER_MK6
candidates = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fw, {
expected_fingerprint: offline_fw,
})
self.assertEqual(candidates, {expected_fingerprint})
# change one of the fw to have a new unseen model year hint
live_fw[(0x760, None)] = {b"M1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}
candidates = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fw, {
expected_fingerprint: offline_fw,
})
self.assertEqual(candidates, {}, "Should not match new model year hint")
if __name__ == "__main__":
unittest.main()