mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-02-18 18:53:55 +08:00
Volkswagen: fingerprint on VIN chassis code (#32148)
* add function signature and behavior comment
* add test
* move chassis codes to platform config!
* add a shared chassis code test
* function
* test matching
* this commit isn't complete yet
* Revert "this commit isn't complete yet"
This reverts commit ae77d5cd54e1f43d390fb70c4da38a95ac34f8da.
* need to check WMI
* TODO: test WMI
* test wmi
* radar FW sanity check
* fix test
* fixes from merge
fixes from merge
* whoops
* fix static analysis!
* do match_fw_to_car
match_fw_to_car takes vin
* makes sense to keep it one function, and we can return exact or fuzzy!
* clean up
* kinda pointless
* fix more tests
* back to function being only fuzzy
* revert test_fw_fingerprint
* revert test_fw_fingerprint
* simplify
* clean up/fixes
* rename test
* less duplicatey WMI descriptions
* fix
* convert to enum
* I am confident about these WMIs
* these are also good
* we support 5N AUS/NZ and NAR (North American) AX Tiguans
fixes
* Tiguan also Mexico
* only one user for caddy
* got from the test route
* check that the gateway type matches the platform (each platform has 1 or 2 types)
* ~gateway~ -> exact FW match
* remove re
* ensure WMIs are set
* actually no reason to delete
* move comment up to the platform config
* proper wmis typing
* spacing
* flip
old-commit-hash: 6acf763db4
This commit is contained in:
@@ -139,10 +139,10 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
# VIN query only reliably works through OBDII
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(logcan, sendcan, (0, 1))
|
||||
ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(logcan, sendcan, vin, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
cached = False
|
||||
|
||||
exact_fw_match, fw_candidates = match_fw_to_car(car_fw)
|
||||
exact_fw_match, fw_candidates = match_fw_to_car(car_fw, vin)
|
||||
else:
|
||||
vin_rx_addr, vin_rx_bus, vin = -1, -1, VIN_UNKNOWN
|
||||
exact_fw_match, fw_candidates, car_fw = True, set(), []
|
||||
|
||||
@@ -97,9 +97,9 @@ class FwQueryConfig:
|
||||
non_essential_ecus: dict[capnp.lib.capnp._EnumModule, list[str]] = field(default_factory=dict)
|
||||
# Ecus added for data collection, not to be fingerprinted on
|
||||
extra_ecus: list[tuple[capnp.lib.capnp._EnumModule, int, int | None]] = field(default_factory=list)
|
||||
# Function a brand can implement to provide better fuzzy matching. Takes in FW versions,
|
||||
# Function a brand can implement to provide better fuzzy matching. Takes in FW versions and VIN,
|
||||
# returns set of candidates. Only will match if one candidate is returned
|
||||
match_fw_to_car_fuzzy: Callable[[LiveFwVersions, OfflineFwVersions], set[str]] | None = None
|
||||
match_fw_to_car_fuzzy: Callable[[LiveFwVersions, str, OfflineFwVersions], set[str]] | None = None
|
||||
|
||||
def __post_init__(self):
|
||||
for i in range(len(self.requests)):
|
||||
|
||||
@@ -144,8 +144,8 @@ def match_fw_to_car_exact(live_fw_versions: LiveFwVersions, match_brand: str = N
|
||||
return set(candidates.keys()) - invalid
|
||||
|
||||
|
||||
def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], allow_exact: bool = True, allow_fuzzy: bool = True,
|
||||
log: bool = True) -> tuple[bool, set[str]]:
|
||||
def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], vin: str,
|
||||
allow_exact: bool = True, allow_fuzzy: bool = True, log: bool = True) -> tuple[bool, set[str]]:
|
||||
# Try exact matching first
|
||||
exact_matches: list[tuple[bool, MatchFwToCar]] = []
|
||||
if allow_exact:
|
||||
@@ -163,7 +163,7 @@ def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], al
|
||||
# If specified and no matches so far, fall back to brand's fuzzy fingerprinting function
|
||||
config = FW_QUERY_CONFIGS[brand]
|
||||
if not exact_match and not len(matches) and config.match_fw_to_car_fuzzy is not None:
|
||||
matches |= config.match_fw_to_car_fuzzy(fw_versions_dict, VERSIONS[brand])
|
||||
matches |= config.match_fw_to_car_fuzzy(fw_versions_dict, vin, VERSIONS[brand])
|
||||
|
||||
if len(matches):
|
||||
return exact_match, matches
|
||||
@@ -237,7 +237,7 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool):
|
||||
cloudlog.warning("OBD multiplexing set successfully")
|
||||
|
||||
|
||||
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1,
|
||||
def get_fw_versions_ordered(logcan, sendcan, vin: str, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1,
|
||||
debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
|
||||
|
||||
@@ -253,7 +253,7 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs: set[EcuAddrBusType],
|
||||
all_car_fw.extend(car_fw)
|
||||
|
||||
# If there is a match using this brand's FW alone, finish querying early
|
||||
_, matches = match_fw_to_car(car_fw, log=False)
|
||||
_, matches = match_fw_to_car(car_fw, vin, log=False)
|
||||
if len(matches) == 1:
|
||||
break
|
||||
|
||||
@@ -381,7 +381,7 @@ if __name__ == "__main__":
|
||||
|
||||
t = time.time()
|
||||
fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, num_pandas=num_pandas, debug=args.debug, progress=True)
|
||||
_, candidates = match_fw_to_car(fw_vers)
|
||||
_, candidates = match_fw_to_car(fw_vers, vin)
|
||||
|
||||
print()
|
||||
print("Found FW versions")
|
||||
|
||||
@@ -209,7 +209,7 @@ class TestHyundaiFingerprint(unittest.TestCase):
|
||||
"subAddress": 0 if sub_addr is None else sub_addr})
|
||||
|
||||
CP = car.CarParams.new_message(carFw=car_fw)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), FW_VERSIONS)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
|
||||
if len(matches) == 1:
|
||||
self.assertEqual(list(matches)[0], platform)
|
||||
else:
|
||||
|
||||
@@ -568,7 +568,7 @@ def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | Non
|
||||
return codes
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> set[str]:
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str]:
|
||||
# Non-electric CAN FD platforms often do not have platform code specifiers needed
|
||||
# to distinguish between hybrid and ICE. All EVs so far are either exclusively
|
||||
# electric or specify electric in the platform code.
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestFwFingerprint(unittest.TestCase):
|
||||
fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand,
|
||||
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr})
|
||||
CP.carFw = fw
|
||||
_, matches = match_fw_to_car(CP.carFw, allow_fuzzy=False)
|
||||
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_fuzzy=False)
|
||||
if not test_non_essential:
|
||||
self.assertFingerprints(matches, car_model)
|
||||
else:
|
||||
@@ -72,8 +72,8 @@ class TestFwFingerprint(unittest.TestCase):
|
||||
fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand,
|
||||
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr})
|
||||
CP.carFw = fw
|
||||
_, matches = match_fw_to_car(CP.carFw, allow_exact=False, log=False)
|
||||
brand_matches = config.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), VERSIONS[brand])
|
||||
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False)
|
||||
brand_matches = config.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, VERSIONS[brand])
|
||||
|
||||
# If both have matches, they must agree
|
||||
if len(matches) == 1 and len(brand_matches) == 1:
|
||||
@@ -94,7 +94,7 @@ class TestFwFingerprint(unittest.TestCase):
|
||||
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 = car.CarParams.new_message(carFw=fw)
|
||||
_, matches = match_fw_to_car(CP.carFw, allow_exact=False, log=False)
|
||||
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False)
|
||||
|
||||
# Assert no match if there are not enough unique ECUs
|
||||
unique_ecus = {(f['address'], f['subAddress']) for f in fw}
|
||||
|
||||
@@ -160,7 +160,7 @@ class TestToyotaFingerprint(unittest.TestCase):
|
||||
"subAddress": 0 if sub_addr is None else sub_addr})
|
||||
|
||||
CP = car.CarParams.new_message(carFw=car_fw)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), FW_VERSIONS)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
|
||||
if len(matches) == 1:
|
||||
self.assertEqual(list(matches)[0], platform)
|
||||
else:
|
||||
|
||||
@@ -415,7 +415,7 @@ def get_platform_codes(fw_versions: list[bytes]) -> dict[bytes, set[bytes]]:
|
||||
return dict(codes)
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> set[str]:
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str]:
|
||||
candidates = set()
|
||||
|
||||
for candidate, fws in offline_fw_versions.items():
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
|
||||
from openpilot.selfdrive.car.volkswagen.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
CHASSIS_CODE_PATTERN = re.compile('[A-Z0-9]{2}')
|
||||
# TODO: determine the unknown groups
|
||||
SPARE_PART_FW_PATTERN = re.compile(b'\xf1\x87(?P<gateway>[0-9][0-9A-Z]{2})(?P<unknown>[0-9][0-9A-Z][0-9])(?P<unknown2>[0-9A-Z]{2}[0-9])([A-Z0-9]| )')
|
||||
|
||||
@@ -17,6 +22,44 @@ class TestVolkswagenPlatformConfigs(unittest.TestCase):
|
||||
for fw in fws:
|
||||
self.assertNotEqual(SPARE_PART_FW_PATTERN.match(fw), None, f"Bad FW: {fw}")
|
||||
|
||||
def test_chassis_codes(self):
|
||||
for platform in CAR:
|
||||
with self.subTest(platform=platform):
|
||||
self.assertTrue(len(platform.config.wmis) > 0, "WMIs not set")
|
||||
self.assertTrue(len(platform.config.chassis_codes) > 0, "Chassis codes not set")
|
||||
self.assertTrue(all(CHASSIS_CODE_PATTERN.match(cc) for cc in
|
||||
platform.config.chassis_codes), "Bad chassis codes")
|
||||
|
||||
# No two platforms should share chassis codes
|
||||
for comp in CAR:
|
||||
if platform == comp:
|
||||
continue
|
||||
self.assertEqual(set(), platform.config.chassis_codes & comp.config.chassis_codes,
|
||||
f"Shared chassis codes: {comp}")
|
||||
|
||||
def test_custom_fuzzy_fingerprinting(self):
|
||||
for platform in CAR:
|
||||
expected_radar_fw = FW_VERSIONS[platform][Ecu.fwdRadar, 0x757, None]
|
||||
|
||||
with self.subTest(platform=platform):
|
||||
for wmi in WMI:
|
||||
for chassis_code in platform.config.chassis_codes | {"00"}:
|
||||
vin = ["0"] * 17
|
||||
vin[0:3] = wmi
|
||||
vin[6:8] = chassis_code
|
||||
vin = "".join(vin)
|
||||
|
||||
# Check a few FW cases - expected, unexpected
|
||||
for radar_fw in expected_radar_fw + [b'\xf1\x877H9907572AA\xf1\x890396']:
|
||||
should_match = ((wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes) and
|
||||
radar_fw in expected_radar_fw)
|
||||
|
||||
live_fws = {(0x757, None): [radar_fw]}
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fws, vin, FW_VERSIONS)
|
||||
|
||||
expected_matches = {platform} if should_match else set()
|
||||
self.assertEqual(expected_matches, matches, "Bad match")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections import namedtuple
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag
|
||||
from enum import Enum, IntFlag, StrEnum
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
@@ -109,6 +109,27 @@ class CANBUS:
|
||||
cam = 2
|
||||
|
||||
|
||||
class WMI(StrEnum):
|
||||
VOLKSWAGEN_USA_SUV = "1V2"
|
||||
VOLKSWAGEN_USA_CAR = "1VW"
|
||||
VOLKSWAGEN_MEXICO_SUV = "3VV"
|
||||
VOLKSWAGEN_MEXICO_CAR = "3VW"
|
||||
VOLKSWAGEN_ARGENTINA = "8AW"
|
||||
VOLKSWAGEN_BRASIL = "9BW"
|
||||
SAIC_VOLKSWAGEN = "LSV"
|
||||
SKODA = "TMB"
|
||||
SEAT = "VSS"
|
||||
AUDI_EUROPE_MPV = "WA1"
|
||||
AUDI_GERMANY_CAR = "WAU"
|
||||
MAN = "WMA"
|
||||
AUDI_SPORT = "WUA"
|
||||
VOLKSWAGEN_COMMERCIAL = "WV1"
|
||||
VOLKSWAGEN_COMMERCIAL_BUS_VAN = "WV2"
|
||||
VOLKSWAGEN_EUROPE_SUV = "WVG"
|
||||
VOLKSWAGEN_EUROPE_CAR = "WVW"
|
||||
VOLKSWAGEN_GROUP_RUS = "XW8"
|
||||
|
||||
|
||||
class VolkswagenFlags(IntFlag):
|
||||
# Detected flags
|
||||
STOCK_HCA_PRESENT = 1
|
||||
@@ -120,10 +141,14 @@ class VolkswagenFlags(IntFlag):
|
||||
@dataclass
|
||||
class VolkswagenMQBPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None))
|
||||
# Volkswagen uses the VIN WMI and chassis code to match in the absence of the comma power
|
||||
# on camera-integrated cars, as we lose too many ECUs to reliably identify the vehicle
|
||||
chassis_codes: set[str] = field(default_factory=set)
|
||||
wmis: set[WMI] = field(default_factory=set)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VolkswagenPQPlatformConfig(PlatformConfig):
|
||||
class VolkswagenPQPlatformConfig(VolkswagenMQBPlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None))
|
||||
|
||||
def init(self):
|
||||
@@ -176,7 +201,9 @@ class VWCarDocs(CarDocs):
|
||||
# FW_VERSIONS for that existing CAR.
|
||||
|
||||
class CAR(Platforms):
|
||||
VOLKSWAGEN_ARTEON_MK1 = VolkswagenMQBPlatformConfig( # Chassis AN
|
||||
config: VolkswagenMQBPlatformConfig | VolkswagenPQPlatformConfig
|
||||
|
||||
VOLKSWAGEN_ARTEON_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Arteon 2018-23", video_link="https://youtu.be/FAomFKPFlDA"),
|
||||
VWCarDocs("Volkswagen Arteon R 2020-23", video_link="https://youtu.be/FAomFKPFlDA"),
|
||||
@@ -184,8 +211,10 @@ class CAR(Platforms):
|
||||
VWCarDocs("Volkswagen CC 2018-22", video_link="https://youtu.be/FAomFKPFlDA"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1733, wheelbase=2.84),
|
||||
chassis_codes={"AN"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_ATLAS_MK1 = VolkswagenMQBPlatformConfig( # Chassis CA
|
||||
VOLKSWAGEN_ATLAS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Atlas 2018-23"),
|
||||
VWCarDocs("Volkswagen Atlas Cross Sport 2020-22"),
|
||||
@@ -194,15 +223,19 @@ class CAR(Platforms):
|
||||
VWCarDocs("Volkswagen Teramont X 2021-22"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=2011, wheelbase=2.98),
|
||||
chassis_codes={"CA"},
|
||||
wmis={WMI.VOLKSWAGEN_USA_SUV},
|
||||
)
|
||||
VOLKSWAGEN_CADDY_MK3 = VolkswagenPQPlatformConfig( # Chassis 2K
|
||||
VOLKSWAGEN_CADDY_MK3 = VolkswagenPQPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Caddy 2019"),
|
||||
VWCarDocs("Volkswagen Caddy Maxi 2019"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1613, wheelbase=2.6, minSteerSpeed=21 * CV.KPH_TO_MS),
|
||||
chassis_codes={"2K"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL_BUS_VAN},
|
||||
)
|
||||
VOLKSWAGEN_CRAFTER_MK2 = VolkswagenMQBPlatformConfig( # Chassis SY/SZ
|
||||
VOLKSWAGEN_CRAFTER_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Crafter 2017-23", video_link="https://youtu.be/4100gLeabmo"),
|
||||
VWCarDocs("Volkswagen e-Crafter 2018-23", video_link="https://youtu.be/4100gLeabmo"),
|
||||
@@ -211,8 +244,10 @@ class CAR(Platforms):
|
||||
VWCarDocs("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS),
|
||||
chassis_codes={"SY", "SZ"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL, WMI.MAN},
|
||||
)
|
||||
VOLKSWAGEN_GOLF_MK7 = VolkswagenMQBPlatformConfig( # Chassis 5G/AU/BA/BE
|
||||
VOLKSWAGEN_GOLF_MK7 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen e-Golf 2014-20"),
|
||||
VWCarDocs("Volkswagen Golf 2015-20", auto_resume=False),
|
||||
@@ -224,71 +259,95 @@ class CAR(Platforms):
|
||||
VWCarDocs("Volkswagen Golf SportsVan 2015-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1397, wheelbase=2.62),
|
||||
chassis_codes={"5G", "AU", "BA", "BE"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_CAR, WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_JETTA_MK7 = VolkswagenMQBPlatformConfig( # Chassis BU
|
||||
VOLKSWAGEN_JETTA_MK7 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Jetta 2018-24"),
|
||||
VWCarDocs("Volkswagen Jetta GLI 2021-24"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1328, wheelbase=2.71),
|
||||
chassis_codes={"BU"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_CAR, WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_PASSAT_MK8 = VolkswagenMQBPlatformConfig( # Chassis 3G
|
||||
VOLKSWAGEN_PASSAT_MK8 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]),
|
||||
VWCarDocs("Volkswagen Passat Alltrack 2015-22"),
|
||||
VWCarDocs("Volkswagen Passat GTE 2015-22"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1551, wheelbase=2.79),
|
||||
chassis_codes={"3G"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_PASSAT_NMS = VolkswagenPQPlatformConfig( # Chassis A3
|
||||
VOLKSWAGEN_PASSAT_NMS = VolkswagenPQPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Passat NMS 2017-22")],
|
||||
VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50 * CV.KPH_TO_MS, minEnableSpeed=20 * CV.KPH_TO_MS),
|
||||
chassis_codes={"A3"},
|
||||
wmis={WMI.VOLKSWAGEN_USA_CAR},
|
||||
)
|
||||
VOLKSWAGEN_POLO_MK6 = VolkswagenMQBPlatformConfig( # Chassis AW
|
||||
VOLKSWAGEN_POLO_MK6 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
VWCarDocs("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1230, wheelbase=2.55),
|
||||
chassis_codes={"AW"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_SHARAN_MK2 = VolkswagenPQPlatformConfig( # Chassis 7N
|
||||
VOLKSWAGEN_SHARAN_MK2 = VolkswagenPQPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Sharan 2018-22"),
|
||||
VWCarDocs("SEAT Alhambra 2018-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50 * CV.KPH_TO_MS),
|
||||
chassis_codes={"7N"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig( # Chassis B2
|
||||
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Taos 2022-23")],
|
||||
VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
|
||||
chassis_codes={"B2"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_SUV, WMI.VOLKSWAGEN_ARGENTINA},
|
||||
)
|
||||
VOLKSWAGEN_TCROSS_MK1 = VolkswagenMQBPlatformConfig( # Chassis C1
|
||||
VOLKSWAGEN_TCROSS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0])],
|
||||
VolkswagenCarSpecs(mass=1150, wheelbase=2.60),
|
||||
chassis_codes={"C1"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TIGUAN_MK2 = VolkswagenMQBPlatformConfig( # Chassis 5N/AD/AX/BW
|
||||
VOLKSWAGEN_TIGUAN_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Tiguan 2018-24"),
|
||||
VWCarDocs("Volkswagen Tiguan eHybrid 2021-23"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1715, wheelbase=2.74),
|
||||
chassis_codes={"5N", "AD", "AX", "BW"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV, WMI.VOLKSWAGEN_MEXICO_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TOURAN_MK2 = VolkswagenMQBPlatformConfig( # Chassis 1T
|
||||
VOLKSWAGEN_TOURAN_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Touran 2016-23")],
|
||||
VolkswagenCarSpecs(mass=1516, wheelbase=2.79),
|
||||
chassis_codes={"1T"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TRANSPORTER_T61 = VolkswagenMQBPlatformConfig( # Chassis 7H/7L
|
||||
VOLKSWAGEN_TRANSPORTER_T61 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Caravelle 2020"),
|
||||
VWCarDocs("Volkswagen California 2021-23"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0),
|
||||
chassis_codes={"7H", "7L"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL_BUS_VAN},
|
||||
)
|
||||
VOLKSWAGEN_TROC_MK1 = VolkswagenMQBPlatformConfig( # Chassis A1
|
||||
VOLKSWAGEN_TROC_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen T-Roc 2018-22", footnotes=[Footnote.VW_MQB_A0])],
|
||||
VolkswagenCarSpecs(mass=1413, wheelbase=2.63),
|
||||
chassis_codes={"A1"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
AUDI_A3_MK3 = VolkswagenMQBPlatformConfig( # Chassis 8V/FF
|
||||
AUDI_A3_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Audi A3 2014-19"),
|
||||
VWCarDocs("Audi A3 Sportback e-tron 2017-18"),
|
||||
@@ -296,55 +355,109 @@ class CAR(Platforms):
|
||||
VWCarDocs("Audi S3 2015-17"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1335, wheelbase=2.61),
|
||||
chassis_codes={"8V", "FF"},
|
||||
wmis={WMI.AUDI_GERMANY_CAR, WMI.AUDI_SPORT},
|
||||
)
|
||||
AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig( # Chassis GA
|
||||
AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Audi Q2 2018")],
|
||||
VolkswagenCarSpecs(mass=1205, wheelbase=2.61),
|
||||
chassis_codes={"GA"},
|
||||
wmis={WMI.AUDI_GERMANY_CAR},
|
||||
)
|
||||
AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig( # Chassis 8U/F3/FS
|
||||
AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Audi Q3 2019-23")],
|
||||
VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
|
||||
chassis_codes={"8U", "F3", "FS"},
|
||||
wmis={WMI.AUDI_EUROPE_MPV, WMI.AUDI_GERMANY_CAR},
|
||||
)
|
||||
SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig( # Chassis 5F
|
||||
SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("SEAT Ateca 2018"),
|
||||
VWCarDocs("SEAT Leon 2014-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1300, wheelbase=2.64),
|
||||
chassis_codes={"5F"},
|
||||
wmis={WMI.SEAT},
|
||||
)
|
||||
SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig( # Chassis PJ
|
||||
SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0])],
|
||||
VolkswagenCarSpecs(mass=1266, wheelbase=2.56),
|
||||
chassis_codes={"PJ"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig( # Chassis NW
|
||||
SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Škoda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]),
|
||||
VWCarDocs("Škoda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1230, wheelbase=2.66),
|
||||
chassis_codes={"NW"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig( # Chassis NU
|
||||
SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Karoq 2019-23")],
|
||||
VolkswagenCarSpecs(mass=1278, wheelbase=2.66),
|
||||
chassis_codes={"NU"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig( # Chassis NS
|
||||
SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Kodiaq 2017-23")],
|
||||
VolkswagenCarSpecs(mass=1569, wheelbase=2.79),
|
||||
chassis_codes={"NS"},
|
||||
wmis={WMI.SKODA, WMI.VOLKSWAGEN_GROUP_RUS},
|
||||
)
|
||||
SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig( # Chassis NE
|
||||
SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Škoda Octavia 2015-19"),
|
||||
VWCarDocs("Škoda Octavia RS 2016"),
|
||||
VWCarDocs("Škoda Octavia Scout 2017-19"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1388, wheelbase=2.68),
|
||||
chassis_codes={"NE"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig( # Chassis 3V/NP
|
||||
SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Superb 2015-22")],
|
||||
VolkswagenCarSpecs(mass=1505, wheelbase=2.84),
|
||||
chassis_codes={"3V", "NP"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str]:
|
||||
candidates = set()
|
||||
|
||||
# Check the WMI and chassis code to determine the platform
|
||||
wmi = vin[:3]
|
||||
chassis_code = vin[6:8]
|
||||
|
||||
for platform in CAR:
|
||||
valid_ecus = set()
|
||||
for ecu, expected_versions in offline_fw_versions[platform].items():
|
||||
addr = ecu[1:]
|
||||
if ecu[0] not in CHECK_FUZZY_ECUS:
|
||||
continue
|
||||
|
||||
# Sanity check that a subset of Volkswagen FW is in the database
|
||||
found_versions = live_fw_versions.get(addr, [])
|
||||
if not any(found_version in expected_versions for found_version in found_versions):
|
||||
break
|
||||
|
||||
valid_ecus.add(ecu[0])
|
||||
|
||||
if valid_ecus != CHECK_FUZZY_ECUS:
|
||||
continue
|
||||
|
||||
if wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes:
|
||||
candidates.add(platform)
|
||||
|
||||
return {str(c) for c in candidates}
|
||||
|
||||
|
||||
# These ECUs are required to match to gain a VIN match
|
||||
# TODO: do we want to check camera when we add its FW?
|
||||
CHECK_FUZZY_ECUS = {Ecu.fwdRadar}
|
||||
|
||||
# All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars
|
||||
# with a manual trans won't return transmission firmware, but all other cars will.
|
||||
#
|
||||
@@ -382,6 +495,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
]],
|
||||
non_essential_ecus={Ecu.eps: list(CAR)},
|
||||
extra_ecus=[(Ecu.fwdCamera, 0x74f, None)],
|
||||
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
|
||||
)
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -78,8 +78,8 @@ if __name__ == "__main__":
|
||||
print("not in supported cars")
|
||||
break
|
||||
|
||||
_, exact_matches = match_fw_to_car(car_fw, allow_exact=True, allow_fuzzy=False)
|
||||
_, fuzzy_matches = match_fw_to_car(car_fw, allow_exact=False, allow_fuzzy=True)
|
||||
_, exact_matches = match_fw_to_car(car_fw, CP.carVin, allow_exact=True, allow_fuzzy=False)
|
||||
_, fuzzy_matches = match_fw_to_car(car_fw, CP.carVin, allow_exact=False, allow_fuzzy=True)
|
||||
|
||||
if (len(exact_matches) == 1) and (list(exact_matches)[0] == live_fingerprint):
|
||||
good_exact += 1
|
||||
|
||||
@@ -41,7 +41,7 @@ if __name__ == "__main__":
|
||||
carPlatform = CP.carFingerprint
|
||||
|
||||
if args.platform is None: # attempt to auto-determine platform with other fuzzy fingerprints
|
||||
_, possible_platforms = match_fw_to_car(carFw, log=False)
|
||||
_, possible_platforms = match_fw_to_car(carFw, carVin, log=False)
|
||||
|
||||
if len(possible_platforms) != 1:
|
||||
print(f"Unable to auto-determine platform, possible platforms: {possible_platforms}")
|
||||
|
||||
Reference in New Issue
Block a user