IQ.Pilot Release Commit @ a87e9e5

This commit is contained in:
IQ.Lvbs CI [bot]
2026-07-22 20:15:51 -05:00
parent f2c02d71a6
commit ff4dfcb728
39 changed files with 249 additions and 63 deletions

View File

@@ -209,6 +209,8 @@ struct CarState {
lateralAvailable @61 :Bool; # lateral control is available even if cruise is faulted
cruiseFaultLateralMode @62 :Bool; # cruise is faulted but lateral control is still active
radarDisableFailed @66 :Bool;
# Physical vehicle odometer in kilometers. Zero means unavailable on this platform.
odometer @67 :Float64;
# cruise state
cruiseState @10 :CruiseState;

View File

@@ -8,6 +8,10 @@ from iqdbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, Tesla
from iqdbc.iqpilot.car.tesla.carstate_ext import CarStateExt
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ
from openpilot.common.params import Params
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
ButtonType = structs.CarState.ButtonEvent.Type
@@ -27,6 +31,7 @@ class CarState(CarStateBase, CarStateExt):
self.acc_state_last = 0
self.das_control = None
self.das_body_controls_dat = b""
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, Params())
self.cruise_override = False
def update_summon_state(self, summon_state: str, cruise_enabled: bool):
@@ -155,6 +160,8 @@ class CarState(CarStateBase, CarStateExt):
self.das_body_controls_dat = bytes(can_parsers[Bus.cam].dat.get(0x3E9, b""))
CarStateExt.update(self, ret, ret_iq, can_parsers)
if ret.odometer > 0.0:
ret.odometer = self._odometer_store.record(ret.odometer) or 0.0
return ret, ret_iq

View File

@@ -59,7 +59,10 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TESLA_MODEL_X:
stock_cp.dashcamOnly = False
if 0x3DF in fingerprint[1]:
# Vehicle-bus messages can be slow enough to miss the initial capture window.
# Accept either the established 0x3DF marker or the absolute odometer frame.
vehicle_bus_seen = any(0x3DF in bus or 0x3B6 in bus for bus in fingerprint.values())
if vehicle_bus_seen:
ret.flags |= TeslaFlagsIQ.HAS_VEHICLE_BUS.value
ret.iqSafetyFlags |= TeslaSafetyFlagsIQ.HAS_VEHICLE_BUS

View File

@@ -4,6 +4,7 @@ from iqdbc.car.tesla.teslacan import TeslaCAN
from iqdbc.car.tesla.radar_interface import RADAR_START_ADDR
from iqdbc.car.tesla.carcontroller import CarController
from iqdbc.car.tesla.values import CAR
from iqdbc.can import CANPacker, CANParser
class TestTeslaFingerprint:
@@ -31,6 +32,19 @@ class TestTeslaCan:
def make_can_msg(self, name, bus, values):
return name, bus, values
def test_vehicle_bus_odometer_decodes_kilometers(self):
packer = CANPacker("tesla_model3_vehicle")
parser = CANParser("tesla_model3_vehicle", [("ID3B6UI_odometer", 1)], 1)
message = packer.make_can_msg("ID3B6UI_odometer", 1, {
"UI_odometer": 29150.377,
"UI_odometerCounter": 1,
"UI_odometerChecksum": 0,
})
parser.update([1_000_000_000, [message]])
assert parser.vl["ID3B6UI_odometer"]["UI_odometer"] == 29150.377
def test_longitudinal_command_does_not_reference_missing_jerk_attr(self):
CP = CarInterface.get_non_essential_params(CAR.TESLA_MODEL_3)
tesla_can = TeslaCAN(CP, self.DummyPacker())

View File

@@ -15,6 +15,7 @@ from iqdbc.car.volkswagen.speed_limit_manager import SpeedLimitManager
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
iq_lvbs_alc = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.alc")
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, iqpilot_path)
@@ -100,8 +101,15 @@ class CarState(CarStateBase):
self.cruise_main_switch = False
self.motor3_stock = {}
self.motor1_stock = {}
self.motor1_frame = 0
self.motor3_frame = 0
self.motor1_frame = 0
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, self._params)
def _update_odometer(self, ret: structs.CarState, raw_km: float) -> None:
"""Publish the cluster value while proprietary Konn3kt code owns persistence."""
odometer_km = self._odometer_store.record(raw_km)
if odometer_km is not None:
ret.odometer = odometer_km
def _apply_iq_private_flags(self, ret_iq: structs.IQCarState) -> None:
ret_iq.alcOverrideAlert = bool(self.alcOverrideAlert)
@@ -285,6 +293,7 @@ class CarState(CarStateBase):
ret.fuelGauge = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, aux_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
self.cruise_faulted = ret.accFaulted
self._apply_iq_private_flags(ret_iq)
@@ -400,6 +409,7 @@ class CarState(CarStateBase):
psd_06_values = main_cp.vl["PSD_06"] if self.CP.flags & VolkswagenFlags.STOCK_PSD_PRESENT else {}
psd_06_values = pt_cp.vl["PSD_06"] if not psd_06_values and self.CP.flags & VolkswagenFlags.STOCK_PSD_06_PRESENT else psd_06_values
diagnose_01_values = pt_cp.vl["Diagnose_01"] if self.CP.flags & VolkswagenFlags.STOCK_DIAGNOSE_01_PRESENT else {}
self._update_odometer(ret, pt_cp.vl["Diagnose_01"]["KBI_Kilometerstand"])
if self.enable_speed_limit_predicative and not self.enable_predicative_speed_limit:
self.enable_predicative_speed_limit = True
@@ -631,6 +641,8 @@ class CarState(CarStateBase):
ret.fuelGauge = 0 # pt_cp.vl["Kombi_1"]["Tankinhalt"] / 55.0
ret.fuelTankLevelL = 0 # pt_cp.vl["Kombi_1"]["Tankinhalt"] # raw liters for konn3kt
if aux_cp is not None:
self._update_odometer(ret, aux_cp.vl["Kombi_3"]["Kilometerstand"])
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
ret.cruiseState.standstill = self.CP.pcmCruise and bool(pt_cp.vl["Bremse_5"]["BR5_Stillstand"]) and ret.cruiseState.enabled
@@ -706,6 +718,7 @@ class CarState(CarStateBase):
ret.fuelGauge = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, br_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
@@ -794,8 +807,11 @@ class CarState(CarStateBase):
]
if CP.flags & VolkswagenFlags.MLB:
pt_messages += [
("Blinkmodi_01", math.nan) # From J519 BCM (is inactive when no lights active, 50Hz when active)
("Blinkmodi_01", math.nan), # From J519 BCM (is inactive when no lights active, 50Hz when active)
("Kombi_02", math.nan), # Auxiliary-bus cluster odometer
]
else:
pt_messages += [("Kombi_02", math.nan)] # Auxiliary-bus cluster odometer
if CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
cam_messages += [
("HCA_01", 1), # From R242 Driver assistance camera, 50Hz if steering/1Hz if not
@@ -809,7 +825,7 @@ class CarState(CarStateBase):
@staticmethod
def get_can_parsers_pq(CP):
aux_messages = []
aux_messages = [("Kombi_3", math.nan)] # Bus 1 cluster odometer
if CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
aux_messages.append(("Motor_3", 0))
return {
@@ -826,6 +842,7 @@ class CarState(CarStateBase):
# TA_01 lives on bus 0 (car ECU / OP-generated when long is active).
# math.nan → ignore_alive=True so it never contributes to can_valid.
("TA_01", math.nan),
("Diagnose_01", math.nan), # Bus 0 cluster odometer
]
if CP.networkLocation == NetworkLocation.fwdCamera:
pt_messages.append(("AWV_03", 1))

View File

@@ -397,5 +397,10 @@ BO_ 826 ID33AUI_rangeSOC: 8 VehicleBus
BO_ 306 ID132HVBattAmpVolt: 8 VehicleBus
SG_ BattVoltage132 : 0|16@1+ (0.01,0) [0|655.35] "V" Receiver
BO_ 950 ID3B6UI_odometer: 8 VehicleBus
SG_ UI_odometer : 0|32@1+ (0.001,0) [0|4294967.295] "km" Receiver
SG_ UI_odometerCounter : 52|4@1+ (1,0) [0|15] "" Receiver
SG_ UI_odometerChecksum : 56|8@1+ (1,0) [0|255] "" Receiver
BO_ 658 ID292BMS_SOC: 8 VehicleBus
SG_ SOCUI292 : 10|10@1+ (0.1,0) [0|102.3] "%" Receiver

View File

@@ -18,9 +18,18 @@ class CarStateExt:
self.CP_IQ = CP_IQ
self.infotainment_3_finger_press = 0
self.vehicle_bus_available = bool(CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS)
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
if self.CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS:
if Bus.adas in can_parsers:
cp_adas = can_parsers[Bus.adas]
odometer_km = float(cp_adas.vl["ID3B6UI_odometer"].get("UI_odometer", 0.0))
if 0.0 < odometer_km < 4294967.296:
self.vehicle_bus_available = True
ret.odometer = odometer_km
if self.vehicle_bus_available and Bus.adas in can_parsers:
cp_adas = can_parsers[Bus.adas]
prev_infotainment_3_finger_press = self.infotainment_3_finger_press
@@ -66,7 +75,9 @@ class CarStateExt:
def get_parser(CP: structs.CarParams, CP_IQ: structs.IQCarParams) -> dict[StrEnum, CANParser]:
messages = {}
if CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS:
if Bus.adas in DBC[CP.carFingerprint]:
# Parse the absolute odometer even if the initial fingerprint missed a
# slow vehicle-bus marker. Runtime data latches support safely.
messages[Bus.adas] = CANParser(DBC[CP.carFingerprint][Bus.adas], [], CANBUS.vehicle)
return messages