Keep alternate HKG CAN-FD discovery on normal wiring

This commit is contained in:
firestar5683
2026-10-30 16:29:48 -05:00
parent 1d7e01f9f1
commit 320e399f98
4 changed files with 793 additions and 1 deletions
@@ -1,4 +1,5 @@
import time
from itertools import product
NORMAL_BUS_LAYOUT = {(2, 0x110): 32, (2, 0x362): 32, (1, 0x35): 32,
@@ -21,6 +22,29 @@ HDA2_NORMAL_BUS_LAYOUT = {(2, 0x50): 16, (2, 0x2A4): 24, (1, 0x35): 32, (1, 0xEA
(1, 0xA0): 24, (1, 0x1A0): 32, (1, 0x1CF): 8, (1, 0x130): 16}
def _canfd_variant_layouts():
layouts = []
originals = (NORMAL_BUS_LAYOUT, HDA1_NORMAL_BUS_LAYOUT, HDA2_NORMAL_BUS_LAYOUT)
for base in originals:
bus = next(bus for bus, address in base if address == 0xEA)
gears = ((0x130, 16), (0x40, 32), (0x70, 32)) if (bus, 0x130) in base else ((None, 0),)
for accelerator, (buttons, length), (gear, gear_length) in product(
(0x35, 0x100, 0x105), ((0x1CF, 8), (0x1AA, 16)), gears):
layout = {key: value for key, value in base.items() if key not in ((bus, 0x35), (bus, 0x1CF), (bus, 0x130))}
layout[(bus, accelerator)] = 32
layout[(bus, buttons)] = length
if gear is not None:
layout[(bus, gear)] = gear_length
if layout not in originals:
layouts.append(layout)
return tuple(layouts)
# Same camera/chassis proof, with the DBC-defined fuel, button and shifter sources.
# This selects only query wiring; fresh VIN/FW/CAN still determine vehicle identity.
CANFD_VARIANT_LAYOUTS = _canfd_variant_layouts()
def normal_bus_topology(can_recv, *, clock=time.monotonic):
deadline = clock() + .12
layouts = (NORMAL_BUS_LAYOUT, CLASSIC_NORMAL_BUS_LAYOUT)
@@ -30,6 +54,8 @@ def normal_bus_topology(can_recv, *, clock=time.monotonic):
hda2_invalid = False
hda1_observed = {}
hda1_invalid = False
variants_observed = [{} for _ in CANFD_VARIANT_LAYOUTS]
variants_invalid = [False for _ in CANFD_VARIANT_LAYOUTS]
for _ in range(24):
if clock() >= deadline:
break
@@ -45,6 +71,12 @@ def normal_bus_topology(can_recv, *, clock=time.monotonic):
return False
original_invalid = True
received[key] = length
for index, layout in enumerate(CANFD_VARIANT_LAYOUTS):
if key in layout:
if len(frame.dat) != layout[key]:
variants_invalid[index] = True
else:
variants_observed[index][key] = len(frame.dat)
if key in HDA2_NORMAL_BUS_LAYOUT:
if len(frame.dat) != HDA2_NORMAL_BUS_LAYOUT[key]:
hda2_invalid = True
@@ -60,4 +92,7 @@ def normal_bus_topology(can_recv, *, clock=time.monotonic):
not original_invalid and (any(received == layout for layout, received in zip(layouts, observed, strict=True)) or
not hda1_invalid and hda1_observed == HDA1_NORMAL_BUS_LAYOUT)):
return True
if any(not invalid and received == layout and (not original_invalid or (2, 0x50) in layout)
for layout, received, invalid in zip(CANFD_VARIANT_LAYOUTS, variants_observed, variants_invalid, strict=True)):
return True
return False
@@ -0,0 +1,124 @@
import json
import pytest
from pathlib import Path
from unittest.mock import patch
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import fingerprint
from opendbc.car.hyundai.discovery import normal_bus_topology
from opendbc.car.vin import VIN_UNKNOWN
CAPTURE = json.loads((Path(__file__).parent / 'testdata/canfd_alternate_discovery.json').read_text())
def batches() -> list[list[CanData]]:
return [[CanData(f['address'], bytes.fromhex(f['hex']), f['src']) for f in packet['frames']]
for packet in CAPTURE['packets']]
def test_recorded_alternate_powertrain_and_buttons_select_normal_wiring():
source = iter(batches())
assert normal_bus_topology(lambda **kw: [next(source, [])]), 'ALTERNATE_HKG_ENTERED_OBD_MUX_PATH'
def test_actual_fingerprint_keeps_fresh_queries_without_switching_obd_wiring():
switches = []
with patch('opendbc.car.car_helpers.get_vin', return_value=(-1, -1, VIN_UNKNOWN)) as vin, \
patch('opendbc.car.car_helpers.get_fw_versions', return_value=[]) as query, \
patch('opendbc.car.car_helpers.get_present_ecus', return_value=set()) as present, \
patch('opendbc.car.car_helpers.get_fw_versions_ordered', return_value=[]) as generic, \
patch('opendbc.car.car_helpers.match_fw_to_car', return_value=(True, set())), \
patch('opendbc.car.car_helpers.can_fingerprint', return_value=(None, {})):
source = [f for packet in batches() for f in packet]
actual = fingerprint(lambda wait_for_one=False: [source], lambda packets: None, switches.append, None)
assert actual[0] is None
assert switches and not any(switches), 'ALTERNATE_HKG_SWITCHED_OBD_WIRING'
assert vin.call_args.args[2] == (0, 1)
query.assert_called_once()
assert query.call_args.kwargs == {'query_brand': 'hyundai', 'normal_bus_only': True}
present.assert_not_called()
generic.assert_not_called()
from opendbc.car.hyundai.discovery import CANFD_VARIANT_LAYOUTS
@pytest.mark.parametrize('layout', CANFD_VARIANT_LAYOUTS)
def test_all_dbc_defined_powertrain_button_and_gear_variants(layout):
source = [CanData(address, bytes(length), bus) for (bus, address), length in layout.items()]
assert normal_bus_topology(lambda **kw: [source])
@pytest.mark.parametrize('layout', CANFD_VARIANT_LAYOUTS)
@pytest.mark.parametrize('index', range(8))
@pytest.mark.parametrize('defect', ['missing', 'length', 'bus', 'echo', 'contradiction'])
def test_variants_require_every_consistent_unmodified_source(layout, index, defect):
source = [CanData(address, bytes(length), bus) for (bus, address), length in layout.items()]
frame = source.pop(index)
if defect in ('length', 'contradiction'):
source.append(CanData(frame.address, frame.dat[:-1], frame.src))
if defect == 'contradiction':
source.append(frame)
elif defect != 'missing':
source.append(CanData(frame.address, frame.dat, frame.src + (128 if defect == 'echo' else 1)))
clock = iter([0., 0., .13])
assert not normal_bus_topology(lambda **kw: [source], clock=lambda: next(clock))
def recorded_firmware():
from opendbc.car.structs import CarParams
values = []
for row in CAPTURE['carFw']:
data = {k: row[k] for k in ('ecu', 'address', 'subAddress', 'bus', 'brand', 'logging', 'obdMultiplexing', 'responseAddress')}
data['fwVersion'] = bytes.fromhex(row['fwVersion']['hex'])
data['request'] = [bytes.fromhex(v['hex']) for v in row['request']]
values.append(CarParams.CarFw(**data))
return values
def test_recorded_firmware_identity_and_normal_query_coverage_are_preserved():
from opendbc.car.fw_versions import FW_QUERY_CONFIGS, match_fw_to_car
from opendbc.car.hyundai.values import CAR
fw = recorded_firmware()
exact, matches = match_fw_to_car(fw, VIN_UNKNOWN)
assert exact and matches == {CAR.KIA_CARNIVAL_2025}
normal = [r for r in FW_QUERY_CONFIGS['hyundai'].requests if not (r.bus % 4 == 1 and r.obd_multiplexing)]
for row in fw:
assert any(r.bus == row.bus and r.request == list(row.request) and r.obd_multiplexing == row.obdMultiplexing
for r in normal)
def test_actual_fingerprint_still_requires_recorded_fresh_firmware_for_identity():
from opendbc.car.hyundai.values import CAR
switches = []
with patch('opendbc.car.car_helpers.get_vin', return_value=(-1, -1, VIN_UNKNOWN)), \
patch('opendbc.car.car_helpers.get_fw_versions', return_value=recorded_firmware()) as query, \
patch('opendbc.car.car_helpers.get_present_ecus') as present, \
patch('opendbc.car.car_helpers.get_fw_versions_ordered') as generic, \
patch('opendbc.car.car_helpers.can_fingerprint', return_value=(None, {})):
source = [f for packet in batches() for f in packet]
actual = fingerprint(lambda wait_for_one=False: [source], lambda packets: None, switches.append, None)
assert actual[0] == CAR.KIA_CARNIVAL_2025
assert switches and not any(switches)
assert query.call_args.kwargs == {'query_brand': 'hyundai', 'normal_bus_only': True}
present.assert_not_called()
generic.assert_not_called()
def test_exact_recorded_configuration_keeps_irq_fault_blocking():
from opendbc.car import gen_empty_fingerprint
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai.values import CAR
from openpilot.cereal import log
from openpilot.starpilot.aol.panda_faults import blocking_fault_list, blocking_fault_mask
from openpilot.starpilot.car.hyundai.aol import policy_for
observed = gen_empty_fingerprint()
for packet in batches():
for f in packet:
observed[f.src][f.address] = len(f.dat)
cp = CarInterface.get_params(CAR.KIA_CARNIVAL_2025, observed, [], False, False, False)
cp.safetyConfigs[0].safetyParam |= policy_for(cp).safety_param_addition
assert cp.safetyConfigs[0].safetyParam == 0x28B0
assert cp.alternativeExperience == 0 and cp.pcmCruise and not cp.openpilotLongitudinalControl
assert blocking_fault_mask(8, cp)
assert blocking_fault_list([log.PandaState.FaultType.interruptRateCan2], cp)
@@ -0,0 +1,633 @@
{
"sourceCommit": "b2604f4b8a3e57f17f8d79d0d3603cad78a904a2",
"packets": [
{
"t": 5.149616509,
"frames": [
{
"src": 2,
"address": 272,
"hex": "102cb8002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "c08bb90011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "3fb0b70017ff0708eeeeff070000ff009609200050ff00002000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "13a25bfef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 160,
"hex": "5849af00010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "24e9b60001410000d707ab0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.159691717,
"frames": [
{
"src": 2,
"address": 272,
"hex": "5675b9002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "0a60ba0011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "2ec7b80017ff0708eeeeff070000ff008e09200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "3b24b000010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "02b7b70001410000d807ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "291abf10006000000000626200000000"
}
]
},
{
"t": 5.16972755,
"frames": [
{
"src": 2,
"address": 272,
"hex": "9c9eba002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "4c39bb0011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "689eb90017ff0708eeeeff070000ff008e09200050ff00002000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "e03c5cfef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 160,
"hex": "3813b100010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "17f3b80001410000d707ab0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.179683019,
"frames": [
{
"src": 2,
"address": 272,
"hex": "dac7bb002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "bfa7bc0011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "9586ba0017ff0708eeeeff070000ff009209200050ff00002000000000000000"
},
{
"src": 0,
"address": 933,
"hex": "0377f10000000000000000000000000000000000d0020000"
},
{
"src": 1,
"address": 160,
"hex": "3d4ab200010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "31adb90001410000d807ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "058ac010006000000000626200000000"
}
]
},
{
"t": 5.189698227,
"frames": [
{
"src": 2,
"address": 272,
"hex": "2959bc002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "9871bd0011977e6c4000270000000e4b0a00010831010000fe0f00846d000000"
},
{
"src": 2,
"address": 866,
"hex": "8ed0f20000220000220000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "a6655dfef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 64,
"hex": "ffdebb0017ff0708eeeeff070000ff009809200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "3e7db300010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "34f4ba0001410000d807ac0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.199713488,
"frames": [
{
"src": 2,
"address": 272,
"hex": "6f00bd002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "3315be0011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "0c40bc0017ff0708eeeeff070000ff009809200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "37f8b400010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "37c3bb0001410000d807ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "2122c110006000000000626200000000"
}
]
},
{
"t": 5.209677915,
"frames": [
{
"src": 2,
"address": 272,
"hex": "a5ebbe002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "754cbf0011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "6c8e5efef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 64,
"hex": "4a19bd0017ff0708eeeeff070000ff009809200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "34cfb500010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "3e46bc0001410000d807ac0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.219615779,
"frames": [
{
"src": 2,
"address": 272,
"hex": "e3b2bf002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "ed46c00011977e6c4000270000000e4b0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "80f2be0017ff0708eeeeff070000ff009809200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "3196b600010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "0c81bd0001410000d907ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "6ccac210006000000000626200000000"
}
]
},
{
"t": 5.229618488,
"frames": [
{
"src": 2,
"address": 272,
"hex": "7bb8c0002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "ca90c10011977e6c4000270000000e4b0a00010831010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "2ad75ffef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 64,
"hex": "4d5abf0017ff0708eeeeff070000ff009609200050ff00002000000000000000"
},
{
"src": 0,
"address": 933,
"hex": "062ef20000000000000000000000000000000000d0020000"
},
{
"src": 1,
"address": 160,
"hex": "32a1b700010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "09d8be0001410000d907ac0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.239618904,
"frames": [
{
"src": 2,
"address": 272,
"hex": "3de1c1002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "6110c20011977e6c4000270000000e4d0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 2,
"address": 866,
"hex": "c889f30000220000220000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "f951c00017ff0708eeeeff070000ff009c09200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "028cb800010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "3b1fbf0001410000d807ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "4862c310006000000000626200000000"
}
]
},
{
"t": 5.249550727,
"frames": [
{
"src": 2,
"address": 272,
"hex": "f70ac2002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "2749c30011977e6c4000270000000e4d0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 416,
"hex": "c5fe60fef77f64000000000000080000fff33f120a000000fe07000000000000"
},
{
"src": 1,
"address": 64,
"hex": "bf08c10017ff0708eeeeff070000ff009c09200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "01bbb900010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "0c8bc00001410000d807ab0f0000000025000010ff7f0000"
}
]
},
{
"t": 5.259624425,
"frames": [
{
"src": 2,
"address": 272,
"hex": "b153c3002000080064010000000000000000000000000000000000009200ffff"
},
{
"src": 1,
"address": 256,
"hex": "d4d7c40011977e6c4000270000000e4d0a00000031010000fe0f00846d000000"
},
{
"src": 1,
"address": 261,
"hex": "0000000000000000000000000000000000000000000000000000000000000000"
},
{
"src": 1,
"address": 64,
"hex": "de15c20017ff0708eeeeff070000ff00a209200050ff00002000000000000000"
},
{
"src": 1,
"address": 160,
"hex": "04e2ba00010002000000000000000000003a010000000000"
},
{
"src": 1,
"address": 234,
"hex": "a124c10001410000d907ac0f0000000025000010ff7f0000"
},
{
"src": 1,
"address": 426,
"hex": "d70ac410006000000000626200000000"
}
]
}
],
"carFw": [
{
"ecu": "fwdRadar",
"fwVersion": {
"hex": "f1004b41345f20524452202d2d2d2d2d202020202020312e303020312e30312039393131302d5230353130202020202020202020"
},
"address": 2000,
"subAddress": 0,
"responseAddress": 2008,
"request": [
{
"hex": "22f100"
}
],
"brand": "hyundai",
"bus": 0,
"logging": false,
"obdMultiplexing": true
},
{
"ecu": "combinationMeter",
"fwVersion": {
"hex": "f100333531"
},
"address": 1990,
"subAddress": 0,
"responseAddress": 1998,
"request": [
{
"hex": "22f100"
}
],
"brand": "hyundai",
"bus": 1,
"logging": true,
"obdMultiplexing": false
},
{
"ecu": "adas",
"fwVersion": {
"hex": "f1004b41342046424c3620312e303020312e303120323530393133"
},
"address": 1840,
"subAddress": 0,
"responseAddress": 1848,
"request": [
{
"hex": "22f100"
}
],
"brand": "hyundai",
"bus": 1,
"logging": true,
"obdMultiplexing": false
},
{
"ecu": "fwdCamera",
"fwVersion": {
"hex": "f1004b4134204d4643202041542043414e204c484420312e303020312e30302039393231302d523037303020323530333234"
},
"address": 1988,
"subAddress": 0,
"responseAddress": 1996,
"request": [
{
"hex": "22f100"
}
],
"brand": "hyundai",
"bus": 1,
"logging": false,
"obdMultiplexing": false
},
{
"ecu": "fwdRadar",
"fwVersion": {
"hex": "f1004b41345f20524452202d2d2d2d2d202020202020312e303020312e30312039393131302d5230353130202020202020202020"
},
"address": 2000,
"subAddress": 0,
"responseAddress": 2008,
"request": [
{
"hex": "22f100"
}
],
"brand": "hyundai",
"bus": 1,
"logging": false,
"obdMultiplexing": false
},
{
"ecu": "fwdRadar",
"fwVersion": {
"hex": "f18b20251201"
},
"address": 2000,
"subAddress": 0,
"responseAddress": 2008,
"request": [
{
"hex": "22f18b"
}
],
"brand": "hyundai",
"bus": 0,
"logging": true,
"obdMultiplexing": true
},
{
"ecu": "combinationMeter",
"fwVersion": {
"hex": "f110"
},
"address": 1990,
"subAddress": 0,
"responseAddress": 1998,
"request": [
{
"hex": "22f110"
}
],
"brand": "hyundai",
"bus": 1,
"logging": true,
"obdMultiplexing": false
}
]
}
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "006844e7f98b0e3854b7974cfd8e297d6fccfe15d681c42f80b78018e05b10ef", "schema_version": 1}
{"protected_inventory_sha256": "ae583fdcc5160210f561425b9f97e338b9eb61e23728ae16333c5ea6e70e3ab8", "schema_version": 1}