diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index 75b85cab2f..3b749101c9 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -246,7 +246,7 @@ class CanSignalRateCalculator: CarInfos = CarInfo | list[CarInfo] -@dataclass(kw_only=True) +@dataclass(frozen=True, kw_only=True) class CarSpecs: mass: float wheelbase: float @@ -256,7 +256,7 @@ class CarSpecs: minEnableSpeed: float = field(default=-1.) -@dataclass(order=True) +@dataclass(frozen=True, order=True) class PlatformConfig: platform_str: str car_info: CarInfos diff --git a/selfdrive/car/ford/tests/test_ford.py b/selfdrive/car/ford/tests/test_ford.py index fbde6748a0..f6ffac5703 100755 --- a/selfdrive/car/ford/tests/test_ford.py +++ b/selfdrive/car/ford/tests/test_ford.py @@ -20,6 +20,7 @@ ECU_ADDRESSES = { Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA) Ecu.engine: 0x7E0, # Powertrain Control Module (PCM) Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM) + Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM) } diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 57aa623c56..17feddf9e6 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -3,11 +3,12 @@ from collections import defaultdict from dataclasses import dataclass, field from enum import Enum +import panda.python.uds as uds from cereal import car from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ Device -from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries +from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16 Ecu = car.CarParams.Ecu @@ -65,7 +66,7 @@ class FordCarInfo(CarInfo): self.car_parts = CarParts([Device.threex, harness]) -@dataclass +@dataclass(frozen=True) class FordPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) @@ -142,7 +143,6 @@ class CAR(Platforms): CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} - # FW response contains a combined software and part number # A-Z except no I, O or W # e.g. NZ6A-14C204-AAA @@ -234,9 +234,37 @@ def match_fw_to_car_fuzzy(live_fw_versions: LiveFwVersions, offline_fw_versions: return candidates + # All of these ECUs must be present and are expected to have platform codes we can match PLATFORM_CODE_ECUS = (Ecu.abs, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps) +DATA_IDENTIFIER_FORD_ASBUILT = 0xDE + +ASBUILT_BLOCKS: list[tuple[int, list]] = [ + (1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]), + (2, [Ecu.abs, Ecu.debug, Ecu.eps]), + (3, [Ecu.abs, Ecu.debug, Ecu.eps]), + (4, [Ecu.debug, Ecu.fwdCamera]), + (5, [Ecu.debug]), + (6, [Ecu.debug]), + (7, [Ecu.debug]), + (8, [Ecu.debug]), + (9, [Ecu.debug]), + (16, [Ecu.debug, Ecu.fwdCamera]), + (18, [Ecu.fwdCamera]), + (20, [Ecu.fwdCamera]), + (21, [Ecu.fwdCamera]), +] + + +def ford_asbuilt_block_request(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + + +def ford_asbuilt_block_response(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + + FW_QUERY_CONFIG = FwQueryConfig( requests=[ # CAN and CAN FD queries are combined. @@ -244,19 +272,29 @@ FW_QUERY_CONFIG = FwQueryConfig( Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], logging=True, ), Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], bus=0, auxiliary=True, ), + *[Request( + [StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)], + [StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)], + whitelist_ecus=ecus, + bus=0, + logging=True, + ) for block_id, ecus in ASBUILT_BLOCKS], ], extra_ecus=[ - # We are unlikely to get a response from the PCM from behind the gateway - (Ecu.engine, 0x7e0, None), - (Ecu.shiftByWire, 0x732, None), + (Ecu.engine, 0x7e0, None), # Powertrain Control Module + # Note: We are unlikely to get a response from behind the gateway + (Ecu.shiftByWire, 0x732, None), # Gear Shift Module + (Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module ], # Custom fuzzy fingerprinting function using platform codes, part numbers and software versions match_fw_to_car_fuzzy=match_fw_to_car_fuzzy, diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 53dbde87f4..35fdf3fb92 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -79,7 +79,7 @@ class GMCarInfo(CarInfo): self.footnotes.append(Footnote.OBD_II) -@dataclass +@dataclass(frozen=True) class GMPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 0a2d978004..0a5ec94aa0 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -4,7 +4,7 @@ import numpy as np import tomllib from abc import abstractmethod, ABC from enum import StrEnum -from typing import Any, NamedTuple, cast +from typing import Any, NamedTuple from collections.abc import Callable from cereal import car @@ -13,7 +13,7 @@ from openpilot.common.conversions import Conversions as CV from openpilot.common.simple_kalman import KF1D, get_kalman_gain from openpilot.common.numpy_fast import clip from openpilot.common.realtime import DT_CTRL -from openpilot.selfdrive.car import PlatformConfig, apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG +from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG from openpilot.selfdrive.car.values import Platform from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, get_friction from openpilot.selfdrive.controls.lib.events import Events @@ -113,14 +113,13 @@ class CarInterfaceBase(ABC): ret = CarInterfaceBase.get_std_params(candidate) if hasattr(candidate, "config"): - platform_config = cast(PlatformConfig, candidate.config) - if platform_config.specs is not None: - ret.mass = platform_config.specs.mass - ret.wheelbase = platform_config.specs.wheelbase - ret.steerRatio = platform_config.specs.steerRatio - ret.centerToFront = ret.wheelbase * platform_config.specs.centerToFrontRatio - ret.minEnableSpeed = platform_config.specs.minEnableSpeed - ret.minSteerSpeed = platform_config.specs.minSteerSpeed + if candidate.config.specs is not None: + ret.mass = candidate.config.specs.mass + ret.wheelbase = candidate.config.specs.wheelbase + ret.steerRatio = candidate.config.specs.steerRatio + ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio + ret.minEnableSpeed = candidate.config.specs.minEnableSpeed + ret.minSteerSpeed = candidate.config.specs.minSteerSpeed ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 0fd7ab9c13..a296550b12 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -89,7 +89,7 @@ class SubaruCarInfo(CarInfo): self.footnotes.append(Footnote.EXP_LONG) -@dataclass +@dataclass(frozen=True) class SubaruPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None)) diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index 1a745b4447..88c7225f22 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -263,13 +263,13 @@ class TestFwFingerprintTiming(unittest.TestCase): print(f'get_vin {name} case, query time={self.total_time / self.N} seconds') def test_fw_query_timing(self): - total_ref_time = {1: 6.5, 2: 7.4} + total_ref_time = {1: 7.8, 2: 8.7} brand_ref_times = { 1: { 'gm': 0.5, 'body': 0.1, 'chrysler': 0.3, - 'ford': 0.2, + 'ford': 1.5, 'honda': 0.55, 'hyundai': 1.05, 'mazda': 0.1, @@ -280,7 +280,7 @@ class TestFwFingerprintTiming(unittest.TestCase): 'volkswagen': 0.65, }, 2: { - 'ford': 0.3, + 'ford': 1.6, 'hyundai': 1.85, } } diff --git a/selfdrive/car/toyota/fingerprints.py b/selfdrive/car/toyota/fingerprints.py index 12a1d46aaf..6b48408a10 100644 --- a/selfdrive/car/toyota/fingerprints.py +++ b/selfdrive/car/toyota/fingerprints.py @@ -573,6 +573,7 @@ FW_VERSIONS = { b'\x018821F6201400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F12010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', @@ -843,6 +844,7 @@ FW_VERSIONS = { b'8965B47023\x00\x00\x00\x00\x00\x00', b'8965B47050\x00\x00\x00\x00\x00\x00', b'8965B47060\x00\x00\x00\x00\x00\x00', + b'8965B47070\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'F152647290\x00\x00\x00\x00\x00\x00', @@ -1024,6 +1026,7 @@ FW_VERSIONS = { b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', + b'\x02896634A13201\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index f029740284..4dff9205d6 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -112,15 +112,15 @@ class CANBUS: class VolkswagenFlags(IntFlag): STOCK_HCA_PRESENT = 1 -@dataclass +@dataclass(frozen=True) class VolkswagenMQBPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) -@dataclass +@dataclass(frozen=True) class VolkswagenPQPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) -@dataclass(kw_only=True) +@dataclass(frozen=True, kw_only=True) class VolkswagenCarSpecs(CarSpecs): steerRatio: float = field(default=15.6) diff --git a/system/hardware/tici/amplifier.py b/system/hardware/tici/amplifier.py index af82067467..5725cb8170 100755 --- a/system/hardware/tici/amplifier.py +++ b/system/hardware/tici/amplifier.py @@ -97,6 +97,7 @@ CONFIGS = { AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001), AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000), ], + "mici": [], } class Amplifier: diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index e553a665a8..f1d1f1e717 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -20,12 +20,12 @@ public: } static std::string get_name() { - std::string devicetree_model = util::read_file("/sys/firmware/devicetree/base/model"); - return (devicetree_model.find("tizi") != std::string::npos) ? "tizi" : "tici"; + std::string model = util::read_file("/sys/firmware/devicetree/base/model"); + return model.substr(std::string("comma ").size()); } static cereal::InitData::DeviceType get_device_type() { - return (get_name() == "tizi") ? cereal::InitData::DeviceType::TIZI : cereal::InitData::DeviceType::TICI; + return (get_name() == "tizi") ? cereal::InitData::DeviceType::TIZI : (get_name() == "mici" ? cereal::InitData::DeviceType::MICI : cereal::InitData::DeviceType::TICI); } static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); }