diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index be5b956c9c..b734296b46 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -54,11 +54,13 @@ def get_brand_addrs() -> Dict[str, Set[Tuple[int, Optional[int]]]]: return dict(brand_addrs) -def match_fw_to_car_fuzzy(fw_versions_dict, config, log=True, exclude=None): +def match_fw_to_car_fuzzy(fw_versions_dict, config=None, log=True, exclude=None): """Do a fuzzy FW match. This function will return a match, and the number of firmware version that were matched uniquely to that specific car. If multiple ECUs uniquely match to different cars the match is rejected.""" + use_config = config is not None and config.fuzzy_get_platform_codes is not None + # Build lookup table from (addr, sub_addr, fw) to list of candidate cars all_fw_versions = defaultdict(list) # Platform codes are brand-specific unique identifiers for each platform, less specific than a FW version @@ -77,7 +79,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, config, log=True, exclude=None): all_fw_versions[(addr[1], addr[2], f)].append(candidate) # Add platform codes to lookup dict if config specifies a function - if addr[0] in config.fuzzy_ecus and config.fuzzy_get_platform_codes is not None: + if use_config and addr[0] in config.fuzzy_ecus: for platform_code in config.fuzzy_get_platform_codes(fws): all_platform_codes[(addr[1], addr[2], platform_code)].append(candidate) @@ -90,11 +92,11 @@ def match_fw_to_car_fuzzy(fw_versions_dict, config, log=True, exclude=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[(*ecu_key, version)] - - # If no exact FW matches, try brand-specific fuzzy fingerprinting - if len(candidates) != 1 and config.fuzzy_get_platform_codes is not None: + # Fall back to matching with full FW versions if brand does not implement platform codes + candidates = set() + if not use_config: + candidates = all_fw_versions[(*ecu_key, version)] + else: # Returns one or none, all cars that have this platform code for platform_code in config.fuzzy_get_platform_codes([version]): candidates = all_platform_codes[(*ecu_key, platform_code)] @@ -109,7 +111,8 @@ def match_fw_to_car_fuzzy(fw_versions_dict, config, log=True, exclude=None): # Note that it is possible to match to a candidate without all its ECUs being present # if there are enough matches. FIXME: parameterize this or require all ECUs to exist like exact matching - if len(matched_ecus) >= config.fuzzy_min_match_count: + fuzzy_min_match_count = config.fuzzy_min_match_count if use_config else 2 + if len(matched_ecus) >= fuzzy_min_match_count: if log: cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {len(matched_ecus)} matching ECUs") return {candidate} @@ -158,14 +161,17 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True): if allow_exact: exact_matches = [(True, match_fw_to_car_exact)] if allow_fuzzy: - exact_matches.append((False, match_fw_to_car_fuzzy)) + # Try first with standard fuzzy fingerprinting, then with config + exact_matches.extend([(False, False, match_fw_to_car_fuzzy), + (False, True, match_fw_to_car_fuzzy)]) - for exact_match, match_func in exact_matches: + for exact_match, use_config, match_func in exact_matches: # For each brand, attempt to fingerprint using all FW returned from its queries matches = set() for brand in VERSIONS.keys(): fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand) - matches |= match_func(fw_versions_dict, FW_QUERY_CONFIGS[brand], log=log) + config = FW_QUERY_CONFIGS[brand] if use_config else None + matches |= match_func(fw_versions_dict, config, log=log) if len(matches): return exact_match, matches