Keep Polestar discovery on normal harness wiring

This commit is contained in:
firestar5683
2026-10-30 22:09:43 -05:00
parent 7f8aa3dceb
commit c3d5939ffe
6 changed files with 914 additions and 6 deletions
+4 -2
View File
@@ -13,6 +13,7 @@ from opendbc.car.gm.pedal_capability import automatic_bolt_candidate
from opendbc.car.gm.discovery import automatic_trax_candidate, automatic_volt_candidate, normal_bus0_topology as gm_normal_bus0_topology
from opendbc.car.ford.discovery import (normal_bus0_selection as ford_normal_bus0_selection,
normal_bus0_queries as ford_normal_bus0_queries, normal_bus_query_brand)
from opendbc.car.volvo.discovery import normal_bus_topology as volvo_normal_bus_topology
from opendbc.car.hyundai.discovery import normal_bus_topology
from opendbc.car.values import BRANDS
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
@@ -149,13 +150,14 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
ecu_rx_addrs = {(fw.responseAddress, fw.subAddress or None, fw.bus) for fw in car_fw if fw.responseAddress > 0}
elif (normal_brand := normal_bus_query_brand(
can_recv, normal_bus_topology,
gm_probe=gm_normal_bus0_topology if _gm_fw_uses_normal_bus0() else None)) is not None:
gm_probe=gm_normal_bus0_topology if _gm_fw_uses_normal_bus0() else None,
volvo_probe=volvo_normal_bus_topology if not fw_versions.FW_QUERY_CONFIGS['volvo'].requests else None)) is not None:
set_obd_multiplexing(False)
warmup_remaining = 0.1 - (time.monotonic() - start_time)
if warmup_remaining > 0:
time.sleep(warmup_remaining)
vin_buses = (0,) if normal_brand in ('ford', 'gm') else (0, 1)
vin_buses = (0,) if normal_brand in ('ford', 'gm', 'volvo') else (0, 1)
vin_rx_addr, vin_rx_bus, vin = get_vin(can_recv, can_send, vin_buses)
car_fw = get_fw_versions(can_recv, can_send, set_obd_multiplexing,
query_brand=normal_brand, normal_bus_only=True)
+5 -3
View File
@@ -45,7 +45,7 @@ NORMAL_BUS0_LAYOUT = {(0, 0x82): 8, (0, 0x165): 8, (0, 0x204): 8,
(0, 0x217): 8, (2, 0x3CA): 8, (2, 0x3D6): 8}
def normal_bus_query_brand(can_recv, hyundai_probe, *, gm_probe=None, clock=time.monotonic):
def normal_bus_query_brand(can_recv, hyundai_probe, *, gm_probe=None, volvo_probe=None, clock=time.monotonic):
# Share the existing bounded passive HKG read, rather than consuming a second
# startup window or enabling the OBD mux to find out whether this is a Ford.
received = {}
@@ -62,7 +62,7 @@ def normal_bus_query_brand(can_recv, hyundai_probe, *, gm_probe=None, clock=time
for packet in packets:
for frame in packet:
key = (frame.src, frame.address)
if gm_probe is not None:
if gm_probe is not None or volvo_probe is not None:
bus = gm_received.setdefault(frame.src, {})
length = len(frame.dat)
gm_malformed |= frame.address in bus and bus[frame.address] != length
@@ -78,11 +78,13 @@ def normal_bus_query_brand(can_recv, hyundai_probe, *, gm_probe=None, clock=time
# Rejecting HKG or Ford does not reject GM. Finish the shared passive window
# within its original budget even when an early frame excludes Ford.
while not (not malformed and received == NORMAL_BUS0_LAYOUT) and reads < 24 and clock() < deadline:
if malformed and (gm_probe is None or gm_malformed):
if malformed and ((gm_probe is None and volvo_probe is None) or gm_malformed):
break
observe(wait_for_one=True)
if not malformed and received == NORMAL_BUS0_LAYOUT and normal_bus0_queries():
return 'ford'
if gm_probe is not None and not gm_malformed and gm_probe(gm_received):
return 'gm'
if volvo_probe is not None and not gm_malformed and volvo_probe(gm_received):
return 'volvo'
return None
@@ -0,0 +1,12 @@
"""Recognize recorded Polestar normal wiring before the generic OBD scan."""
# Mirrored PT/camera and independent front-ECM sources select diagnostic wiring
# only. Live CAN fingerprinting still owns vehicle and control identity.
NORMAL_BUS_LAYOUT = {(bus, address): 8 for bus in (0, 2)
for address in (0x15, 0x16, 0x17, 0x45, 0x55, 0x58)}
NORMAL_BUS_LAYOUT.update({(1, address): 8 for address in (0x23, 0x35, 0x70, 0x116)})
def normal_bus_topology(fingerprint):
return all(fingerprint.get(bus, {}).get(address) == size
for (bus, address), size in NORMAL_BUS_LAYOUT.items())
@@ -0,0 +1,105 @@
"""Recorded Polestar discovery must not switch its normal harness into OBD."""
import json
from pathlib import Path
from unittest.mock import patch
import pytest
from opendbc.car import car_helpers
from opendbc.car.can_definitions import CanData
from opendbc.car.ford.discovery import normal_bus_query_brand
from opendbc.car.hyundai.discovery import normal_bus_topology as hyundai_probe
from opendbc.car.volvo.discovery import NORMAL_BUS_LAYOUT, normal_bus_topology
from opendbc.car.volvo.values import CAR
from opendbc.car.vin import VIN_UNKNOWN
CAPTURE = json.loads((Path(__file__).parent / 'testdata/polestar_startup_discovery.json').read_text())
def frames(key='frames'):
return [CanData(f['address'], bytes.fromhex(f['hex']), f['bus']) for f in CAPTURE[key]]
def fingerprint(rows):
result = {}
for f in rows:
result.setdefault(f.src, {})[f.address] = len(f.dat)
return result
def test_recorded_pre_query_packet_selects_only_normal_query_wiring():
rows = frames()
assert normal_bus_topology(fingerprint(rows))
assert normal_bus_query_brand(lambda **kw: [rows], hyundai_probe,
volvo_probe=normal_bus_topology, clock=lambda: 0.) == 'volvo'
@pytest.mark.parametrize('key', NORMAL_BUS_LAYOUT)
@pytest.mark.parametrize('defect', ('missing', 'length', 'bus', 'echo', 'contradiction'))
def test_every_recorded_source_must_be_consistent(key, defect):
rows = [CanData(a, bytes(n), b) for (b, a), n in NORMAL_BUS_LAYOUT.items()]
original = next(f for f in rows if (f.src, f.address) == key)
rows.remove(original)
if defect in ('length', 'contradiction'):
rows.append(CanData(original.address, original.dat[:-1], original.src))
if defect == 'contradiction':
rows.append(original)
elif defect != 'missing':
rows.append(CanData(original.address, original.dat, original.src + (128 if defect == 'echo' else 1)))
assert normal_bus_query_brand(lambda **kw: [rows], hyundai_probe,
volvo_probe=normal_bus_topology, clock=lambda: 0.) is None
class CapturedCan:
def __init__(self):
self.clock = 10.
self.sent = []
self.switches = []
def recv(self, wait_for_one=False):
self.clock += .01
return [frames('fingerprintFrames')]
def send(self, rows):
self.sent.extend(rows)
def discover(*, old=False):
peer = CapturedCan()
with patch.dict(car_helpers.os.environ, {'FINGERPRINT': '', 'SKIP_FW_QUERY': '', 'DISABLE_FW_CACHE': ''}), \
patch.object(car_helpers.time, 'monotonic', side_effect=lambda: peer.clock), \
patch.object(car_helpers.time, 'sleep', side_effect=lambda seconds: setattr(peer, 'clock', peer.clock + seconds)), \
patch.object(car_helpers, 'get_present_ecus', return_value=set()) as present, \
patch.object(car_helpers, 'get_fw_versions_ordered', return_value=[]) as generic, \
patch.object(car_helpers, 'volvo_normal_bus_topology', side_effect=(lambda fp: False) if old else normal_bus_topology):
ci = car_helpers.get_car(peer.recv, peer.send, peer.switches.append, False, False)
return ci, peer, present, generic
def test_actual_vin_and_live_can_discovery_keep_normal_wiring_and_profile():
ci, peer, present, generic = discover()
assert peer.switches and set(peer.switches) == {False}
assert peer.sent and all(f.src == 0 for f in peer.sent)
assert ci.CP.carFingerprint == CAR.POLESTAR_2
assert ci.CP.safetyConfigs[0].safetyParam == 5
assert ci.CP.pcmCruise and not ci.CP.openpilotLongitudinalControl
assert ci.CP.carVin == VIN_UNKNOWN and not ci.CP.carFw
assert str(ci.CP.steerControlType) == 'angle'
present.assert_not_called()
generic.assert_not_called()
def test_same_recorded_can_reproduces_old_mux_switch_without_probe():
ci, peer, present, generic = discover(old=True)
assert True in peer.switches
assert any(f.src == 1 for f in peer.sent)
assert ci.CP.carFingerprint == CAR.POLESTAR_2
present.assert_called_once()
generic.assert_called_once()
def test_discovery_fix_keeps_reported_irq_fault_blocking():
from openpilot.cereal import log
from openpilot.starpilot.aol.panda_faults import blocking_fault_list, blocking_fault_mask
ci, _, _, _ = discover()
assert blocking_fault_mask(8, ci.CP)
assert blocking_fault_list([log.PandaState.FaultType.interruptRateCan2], ci.CP)
@@ -0,0 +1,787 @@
{
"source": "Reported Polestar route first CAN packet before diagnostic startup",
"frames": [
{
"address": 22,
"bus": 0,
"hex": "8225428098fff9ec"
},
{
"address": 23,
"bus": 0,
"hex": "2d7087468000ffff"
},
{
"address": 22,
"bus": 2,
"hex": "8225428098fff9ec"
},
{
"address": 23,
"bus": 2,
"hex": "2d7087468000ffff"
},
{
"address": 58,
"bus": 1,
"hex": "de714f2bac91c019"
},
{
"address": 278,
"bus": 1,
"hex": "00008000d9900000"
},
{
"address": 899,
"bus": 1,
"hex": "3d98000000000000"
},
{
"address": 112,
"bus": 1,
"hex": "f2c4f004e200ba00"
},
{
"address": 128,
"bus": 0,
"hex": "13b2000301173308"
},
{
"address": 128,
"bus": 2,
"hex": "13b2000301173308"
},
{
"address": 117,
"bus": 1,
"hex": "8000f32e008d2f7a"
},
{
"address": 26,
"bus": 0,
"hex": "4000000000000000"
},
{
"address": 26,
"bus": 2,
"hex": "4000000000000000"
},
{
"address": 128,
"bus": 1,
"hex": "8000e12f0001727a"
},
{
"address": 87,
"bus": 0,
"hex": "00fe00000000e180"
},
{
"address": 87,
"bus": 2,
"hex": "00fe00000000e180"
},
{
"address": 133,
"bus": 1,
"hex": "8000fb2f00016482"
},
{
"address": 256,
"bus": 1,
"hex": "ff05000080000904"
},
{
"address": 69,
"bus": 0,
"hex": "93c19bd9a0110b7b"
},
{
"address": 69,
"bus": 2,
"hex": "93c19bd9a0110b7b"
},
{
"address": 67,
"bus": 1,
"hex": "c400480880000000"
},
{
"address": 70,
"bus": 1,
"hex": "4000000000000000"
},
{
"address": 37,
"bus": 1,
"hex": "80000000ed100368"
},
{
"address": 53,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 138,
"bus": 1,
"hex": "7c00040004000000"
},
{
"address": 320,
"bus": 1,
"hex": "000200b7ff03ff83"
},
{
"address": 325,
"bus": 1,
"hex": "000000000008a000"
},
{
"address": 440,
"bus": 1,
"hex": "ec0050000000fc00"
},
{
"address": 85,
"bus": 0,
"hex": "8225c00049000000"
},
{
"address": 85,
"bus": 2,
"hex": "8225c00049000000"
},
{
"address": 1320,
"bus": 1,
"hex": "2800400000000000"
},
{
"address": 1336,
"bus": 0,
"hex": "3850402904000000"
},
{
"address": 1336,
"bus": 2,
"hex": "3850402904000000"
},
{
"address": 35,
"bus": 1,
"hex": "93c19bd940010000"
},
{
"address": 88,
"bus": 0,
"hex": "c8003800fbc000f0"
},
{
"address": 88,
"bus": 2,
"hex": "c8003800fbc000f0"
},
{
"address": 1302,
"bus": 0,
"hex": "1640000100000000"
},
{
"address": 1302,
"bus": 2,
"hex": "1640000100000000"
},
{
"address": 22,
"bus": 0,
"hex": "8225428098fff6ac"
},
{
"address": 22,
"bus": 2,
"hex": "8225428098fff6ac"
},
{
"address": 21,
"bus": 0,
"hex": "e8d2c000c000ff9f"
},
{
"address": 21,
"bus": 2,
"hex": "e8d2c000c000ff9f"
}
],
"fingerprintFrames": [
{
"address": 22,
"bus": 0,
"hex": "8225428098fff9ec"
},
{
"address": 23,
"bus": 0,
"hex": "2d7087468000ffff"
},
{
"address": 22,
"bus": 2,
"hex": "8225428098fff9ec"
},
{
"address": 23,
"bus": 2,
"hex": "2d7087468000ffff"
},
{
"address": 58,
"bus": 1,
"hex": "de714f2bac91c019"
},
{
"address": 278,
"bus": 1,
"hex": "00008000d9900000"
},
{
"address": 899,
"bus": 1,
"hex": "3d98000000000000"
},
{
"address": 112,
"bus": 1,
"hex": "f2c4f004e200ba00"
},
{
"address": 128,
"bus": 0,
"hex": "13b2000301173308"
},
{
"address": 128,
"bus": 2,
"hex": "13b2000301173308"
},
{
"address": 117,
"bus": 1,
"hex": "8000f32e008d2f7a"
},
{
"address": 26,
"bus": 0,
"hex": "4000000000000000"
},
{
"address": 26,
"bus": 2,
"hex": "4000000000000000"
},
{
"address": 128,
"bus": 1,
"hex": "8000e12f0001727a"
},
{
"address": 87,
"bus": 0,
"hex": "00fe00000000e180"
},
{
"address": 87,
"bus": 2,
"hex": "00fe00000000e180"
},
{
"address": 133,
"bus": 1,
"hex": "8000fb2f00016482"
},
{
"address": 256,
"bus": 1,
"hex": "ff05000080000904"
},
{
"address": 69,
"bus": 0,
"hex": "93c19bd9a0110b7b"
},
{
"address": 69,
"bus": 2,
"hex": "93c19bd9a0110b7b"
},
{
"address": 67,
"bus": 1,
"hex": "c400480880000000"
},
{
"address": 70,
"bus": 1,
"hex": "4000000000000000"
},
{
"address": 37,
"bus": 1,
"hex": "80000000ed100368"
},
{
"address": 53,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 138,
"bus": 1,
"hex": "7c00040004000000"
},
{
"address": 320,
"bus": 1,
"hex": "000200b7ff03ff83"
},
{
"address": 325,
"bus": 1,
"hex": "000000000008a000"
},
{
"address": 440,
"bus": 1,
"hex": "ec0050000000fc00"
},
{
"address": 85,
"bus": 0,
"hex": "8225c00049000000"
},
{
"address": 85,
"bus": 2,
"hex": "8225c00049000000"
},
{
"address": 1320,
"bus": 1,
"hex": "2800400000000000"
},
{
"address": 1336,
"bus": 0,
"hex": "3850402904000000"
},
{
"address": 1336,
"bus": 2,
"hex": "3850402904000000"
},
{
"address": 35,
"bus": 1,
"hex": "93c19bd940010000"
},
{
"address": 88,
"bus": 0,
"hex": "c8003800fbc000f0"
},
{
"address": 88,
"bus": 2,
"hex": "c8003800fbc000f0"
},
{
"address": 1302,
"bus": 0,
"hex": "1640000100000000"
},
{
"address": 1302,
"bus": 2,
"hex": "1640000100000000"
},
{
"address": 21,
"bus": 0,
"hex": "e8d2c000c000ff9f"
},
{
"address": 21,
"bus": 2,
"hex": "e8d2c000c000ff9f"
},
{
"address": 1407,
"bus": 0,
"hex": "1135000011990000"
},
{
"address": 1407,
"bus": 1,
"hex": "11350100000007fc"
},
{
"address": 7,
"bus": 1,
"hex": "05000000"
},
{
"address": 429,
"bus": 1,
"hex": "23a8160000000224"
},
{
"address": 309,
"bus": 1,
"hex": "7e4ce800ce17af00"
},
{
"address": 1422,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 1423,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 1429,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 1430,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 1424,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 1431,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 1425,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 1432,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 1426,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 284,
"bus": 1,
"hex": "a000800000200000"
},
{
"address": 370,
"bus": 1,
"hex": "00008000a517d54d"
},
{
"address": 389,
"bus": 1,
"hex": "1000000a000049d3"
},
{
"address": 400,
"bus": 1,
"hex": "7fff018a0ea6b809"
},
{
"address": 408,
"bus": 1,
"hex": "207d02711e0c7f07"
},
{
"address": 96,
"bus": 0,
"hex": "8267455ca0000000"
},
{
"address": 277,
"bus": 1,
"hex": "a89d000001780000"
},
{
"address": 293,
"bus": 1,
"hex": "1000d68000000000"
},
{
"address": 348,
"bus": 1,
"hex": "ce20127f1e0c1e0c"
},
{
"address": 352,
"bus": 1,
"hex": "4ac2a26f0548ef24"
},
{
"address": 103,
"bus": 0,
"hex": "852cbeef0a40ba00"
},
{
"address": 368,
"bus": 1,
"hex": "a2ff8a0355029c86"
},
{
"address": 104,
"bus": 0,
"hex": "8522e0ef0a4e23b4"
},
{
"address": 426,
"bus": 1,
"hex": "800000000a03ea00"
},
{
"address": 832,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 105,
"bus": 0,
"hex": "1800d60000be6b00"
},
{
"address": 837,
"bus": 1,
"hex": "e0eb2800003784b1"
},
{
"address": 151,
"bus": 0,
"hex": "002aff3b63cf0021"
},
{
"address": 395,
"bus": 0,
"hex": "771200000000000c"
},
{
"address": 666,
"bus": 0,
"hex": "2800010101000001"
},
{
"address": 556,
"bus": 1,
"hex": "2800010101000001"
},
{
"address": 373,
"bus": 1,
"hex": "0000058fece80800"
},
{
"address": 417,
"bus": 1,
"hex": "00000000123e13d7"
},
{
"address": 873,
"bus": 1,
"hex": "a403200000055500"
},
{
"address": 339,
"bus": 0,
"hex": "0000000000000000"
},
{
"address": 341,
"bus": 0,
"hex": "20eb294a8a000000"
},
{
"address": 805,
"bus": 1,
"hex": "0400000000000000"
},
{
"address": 411,
"bus": 1,
"hex": "80cc000003d33243"
},
{
"address": 144,
"bus": 0,
"hex": "004c001000fcac5f"
},
{
"address": 349,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 146,
"bus": 0,
"hex": "f55f00cdff1e093b"
},
{
"address": 435,
"bus": 1,
"hex": "3c008c0000149e3b"
},
{
"address": 147,
"bus": 0,
"hex": "bfff0000f580d000"
},
{
"address": 376,
"bus": 1,
"hex": "f9789c032c00c37b"
},
{
"address": 420,
"bus": 1,
"hex": "afe53e6e08490000"
},
{
"address": 841,
"bus": 1,
"hex": "a6800280ffffffff"
},
{
"address": 889,
"bus": 1,
"hex": "0334333100000000"
},
{
"address": 336,
"bus": 0,
"hex": "b400000000000000"
},
{
"address": 375,
"bus": 1,
"hex": "07e509901d898d2e"
},
{
"address": 848,
"bus": 1,
"hex": "8000000003fc0000"
},
{
"address": 893,
"bus": 1,
"hex": "7f7b677c325b026c"
},
{
"address": 800,
"bus": 1,
"hex": "0400000000000000"
},
{
"address": 807,
"bus": 1,
"hex": "a561900000000000"
},
{
"address": 860,
"bus": 1,
"hex": "6c65083f10000800"
},
{
"address": 791,
"bus": 1,
"hex": "0000000000000064"
},
{
"address": 854,
"bus": 1,
"hex": "0000000000000000"
},
{
"address": 882,
"bus": 1,
"hex": "00b0072804008000"
},
{
"address": 892,
"bus": 1,
"hex": "4b97ee5a48002387"
},
{
"address": 1061,
"bus": 1,
"hex": "d6ac013e00630045"
},
{
"address": 1077,
"bus": 1,
"hex": "771ca4c239a20000"
},
{
"address": 640,
"bus": 0,
"hex": "d2c0078700000000"
},
{
"address": 773,
"bus": 0,
"hex": "4b97ee5a93e80000"
},
{
"address": 778,
"bus": 0,
"hex": "7bd4021100000000"
},
{
"address": 1127,
"bus": 1,
"hex": "0000000000780000"
},
{
"address": 464,
"bus": 1,
"hex": "0400000000000000"
},
{
"address": 1072,
"bus": 2,
"hex": "00007fffffffffff"
},
{
"address": 789,
"bus": 1,
"hex": "0000000000008080"
},
{
"address": 853,
"bus": 1,
"hex": "3aeea00007ff0032"
},
{
"address": 656,
"bus": 2,
"hex": "d000000000000000"
},
{
"address": 1428,
"bus": 1,
"hex": "0000000000000000"
}
]
}
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "525678c0d1155fc613717e32fcd31216bcdc0fc98c63b907e0fe5a02cb849db0", "schema_version": 1}
{"protected_inventory_sha256": "3bfb478274fd8bd02cd951524981a57bf184f36adc8e110373e53ea9f4ecfb59", "schema_version": 1}