selfdrive/car: ban cereal and capnp (#33208)

* ban cereal and msgq

* common too

* do toyota/values.py

* do all fingerprints

* example without builder

* this still works, but no type checking anymore

* stash

* wtf, how does this work

* okay actually not bad

* safe

* epic!

* stash data_structures.py

* some clean up

* hell yeah

* clean up old file

* add to delete

* delete

This reverts commit 90239b7797.

* switch more CarParams stuff over

remove unused

* fix car tests by removing cereal! mypy forgets about dataclass if we wrap it :(

* fix this too

* fix this too

* remove more cereal and add some good hyundai tests

* bunch more typing

* override default with 20hz radar

* temp capnp converter helper

* more lateralTuning

* small union replicator is better than what i was trying, and fixes mypy dynamic typing issues

* can keep all this the same now!

* type ret: CarParams, add more missing structs, revert lateralTuning changes (smaller diff!)

* revert more

* get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :(

would have been `CarParams.NetworkLocation()`

* Revert "get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :("

This reverts commit bb28b228be.

* remove cereal from car_helpers (TODO: caching)

* remove a bunch of temp lines

* use dataclass_transform!

* remove some car.CarParams from the interfaces

* remove rest of car.CarParams from the interfaces

* same which() API

* sort

* from cereal/cache from fingerprinting!

* more typing

* dataclass to capnp helper for CarParams, cached it since it's kinda slow

* (partial) fix process replay fingerprintig for new API

* latcontrollers take capnp

* forgot this

* fix test_models

* fix unit tests

* not here

* VehicleModel and controller still takes capnp CP since they get it from Params()

* fix modeld test

* more fix

* need to namespace to structs, since CarState is both class and struct

* this was never in the base class?!

* clean that up again

* fix import error

fix import error

* cmts and more structs

* remove some more cereal from toyota + convert CarState to capnp

* bruh this was wrong

* replace more cereal

* EventName is one of the last things...

* replace a bunch more cereal.car

* missing imports

* more

* can fix this typing now

* proper toyota+others CS typing!

* mypy can detect return type of CS.update() now

* fix redeclaration of cruise_buttons type

* mypy is only complaining about events now

* temp fix

* add carControl struct

* replace CarControl

i hope there's no circular imports in hyundai's CC

* fine now

* lol this was wrong too

* fix crash

* include my failed attempts at recursively converting to dataclass (doesn't implicitly convert types/recursively :( )

but attrs does, maybe will switch in the future

* clean up

* try out attr.s for its converter (doesn't work recursively yet, but interesting!)

* Revert "try out attr.s for its converter (doesn't work recursively yet, but interesting!)"

This reverts commit ff2434f7bb.

* test processes doesn't fail anymore (on toyota)!

* fix honda crash

* stash

* Revert "stash"

This reverts commit c1762af4e7.

* remove a bunch more cereal!

* LET'S GOOO

* fix these tests

* and these

* and that

* stash, something is wrong with hyundai enable

* Revert "stash, something is wrong with hyundai enable"

This reverts commit 39cf327def.

* forgot these

* remove cereal from fw_versions

* Revert "remove cereal from fw_versions"

This reverts commit 232b37cd40.

* remove rest of the cereal exceptions!

* fix that

* add typing to radard since I didn't realize RI.update() switched from cereal to structs

* and here too!

* add TODO for slots

* needed CS to be capnp, fix comparisons, and type hint car_specific so it's easier to catch type issues (capnp isn't detected by mypy :( )

* remove the struct converter

* save ~4-5% CPU at 100hz, we don't modify after so no need to deepcopy

btw pickle.loads(pickle.dumps()) is faster by ~1% CPU

* deepcopy -> copy: we can technically make a reference, but copy is almost free and less error-prone

saves ~1% CPU

* add non-copying asdict function

* should save ~3% CPU (still 4% above baseline)

* fix that, no dict support

* ~27% decrease in time for 20k iterations on 3X (3.37857 -> 2.4821s)

* give a better name

* fix

* dont support none, capitalize

* sheesh, this called type() on every field

* remove CS.events, clean up

* bump card %

* this was a bug on master!

* add a which enum

* default to pid

* revert

* update refs

* not needed, but consistent

* just Ecu

* don't need to do this in this pr

* clean up

* no cast

* consistent typing

* rm

* fix

* can do this if we're desperate for the last few %

* Revert "can do this if we're desperate for the last few %"

This reverts commit 18e11ac788.

* type this

* don't need to convert carControl

* i guess don't support set either

* fix CP type hint

* simplify that
old-commit-hash: 6a15c42143
pull/33386/head
Shane Smiskol 8 months ago committed by GitHub
parent 8315623257
commit 95224db413
  1. 15
      .importlinter
  2. 22
      selfdrive/car/__init__.py
  3. 3
      selfdrive/car/body/carcontroller.py
  4. 8
      selfdrive/car/body/carstate.py
  5. 4
      selfdrive/car/body/fingerprints.py
  6. 9
      selfdrive/car/body/interface.py
  7. 4
      selfdrive/car/body/values.py
  8. 16
      selfdrive/car/car_helpers.py
  9. 32
      selfdrive/car/car_specific.py
  10. 91
      selfdrive/car/card.py
  11. 3
      selfdrive/car/chrysler/carcontroller.py
  12. 9
      selfdrive/car/chrysler/carstate.py
  13. 6
      selfdrive/car/chrysler/chryslercan.py
  14. 4
      selfdrive/car/chrysler/fingerprints.py
  15. 7
      selfdrive/car/chrysler/interface.py
  16. 6
      selfdrive/car/chrysler/radar_interface.py
  17. 4
      selfdrive/car/chrysler/values.py
  18. 4
      selfdrive/car/docs.py
  19. 6
      selfdrive/car/docs_definitions.py
  20. 10
      selfdrive/car/ford/carcontroller.py
  21. 13
      selfdrive/car/ford/carstate.py
  22. 4
      selfdrive/car/ford/fingerprints.py
  23. 5
      selfdrive/car/ford/fordcan.py
  24. 13
      selfdrive/car/ford/interface.py
  25. 8
      selfdrive/car/ford/radar_interface.py
  26. 12
      selfdrive/car/ford/tests/print_platform_codes.py
  27. 13
      selfdrive/car/ford/tests/test_ford.py
  28. 6
      selfdrive/car/ford/values.py
  29. 11
      selfdrive/car/fw_query_definitions.py
  30. 19
      selfdrive/car/fw_versions.py
  31. 14
      selfdrive/car/gm/carcontroller.py
  32. 13
      selfdrive/car/gm/carstate.py
  33. 21
      selfdrive/car/gm/interface.py
  34. 6
      selfdrive/car/gm/radar_interface.py
  35. 8
      selfdrive/car/gm/values.py
  36. 16
      selfdrive/car/honda/carcontroller.py
  37. 13
      selfdrive/car/honda/carstate.py
  38. 4
      selfdrive/car/honda/fingerprints.py
  39. 11
      selfdrive/car/honda/interface.py
  40. 6
      selfdrive/car/honda/radar_interface.py
  41. 9
      selfdrive/car/honda/values.py
  42. 12
      selfdrive/car/hyundai/carcontroller.py
  43. 13
      selfdrive/car/hyundai/carstate.py
  44. 4
      selfdrive/car/hyundai/fingerprints.py
  45. 15
      selfdrive/car/hyundai/interface.py
  46. 6
      selfdrive/car/hyundai/radar_interface.py
  47. 9
      selfdrive/car/hyundai/tests/print_platform_codes.py
  48. 15
      selfdrive/car/hyundai/tests/test_hyundai.py
  49. 6
      selfdrive/car/hyundai/values.py
  50. 63
      selfdrive/car/interfaces.py
  51. 8
      selfdrive/car/mazda/carcontroller.py
  52. 9
      selfdrive/car/mazda/carstate.py
  53. 4
      selfdrive/car/mazda/fingerprints.py
  54. 7
      selfdrive/car/mazda/interface.py
  55. 4
      selfdrive/car/mazda/values.py
  56. 3
      selfdrive/car/mock/carcontroller.py
  57. 6
      selfdrive/car/mock/carstate.py
  58. 3
      selfdrive/car/mock/interface.py
  59. 8
      selfdrive/car/nissan/carcontroller.py
  60. 9
      selfdrive/car/nissan/carstate.py
  61. 4
      selfdrive/car/nissan/fingerprints.py
  62. 9
      selfdrive/car/nissan/interface.py
  63. 4
      selfdrive/car/nissan/values.py
  64. 499
      selfdrive/car/structs.py
  65. 3
      selfdrive/car/subaru/carcontroller.py
  66. 6
      selfdrive/car/subaru/carstate.py
  67. 4
      selfdrive/car/subaru/fingerprints.py
  68. 11
      selfdrive/car/subaru/interface.py
  69. 4
      selfdrive/car/subaru/subarucan.py
  70. 6
      selfdrive/car/subaru/values.py
  71. 26
      selfdrive/car/tests/test_car_interfaces.py
  72. 44
      selfdrive/car/tests/test_fw_fingerprint.py
  73. 31
      selfdrive/car/tests/test_models.py
  74. 10
      selfdrive/car/toyota/carcontroller.py
  75. 11
      selfdrive/car/toyota/carstate.py
  76. 4
      selfdrive/car/toyota/fingerprints.py
  77. 9
      selfdrive/car/toyota/interface.py
  78. 6
      selfdrive/car/toyota/radar_interface.py
  79. 12
      selfdrive/car/toyota/tests/print_platform_codes.py
  80. 11
      selfdrive/car/toyota/tests/test_toyota.py
  81. 4
      selfdrive/car/toyota/toyotacan.py
  82. 4
      selfdrive/car/toyota/values.py
  83. 12
      selfdrive/car/volkswagen/carcontroller.py
  84. 12
      selfdrive/car/volkswagen/carstate.py
  85. 4
      selfdrive/car/volkswagen/fingerprints.py
  86. 9
      selfdrive/car/volkswagen/interface.py
  87. 4
      selfdrive/car/volkswagen/tests/test_volkswagen.py
  88. 36
      selfdrive/car/volkswagen/values.py
  89. 2
      selfdrive/controls/lib/tests/test_latcontrol.py
  90. 3
      selfdrive/controls/lib/tests/test_vehicle_model.py
  91. 5
      selfdrive/controls/radard.py
  92. 15
      selfdrive/debug/format_fingerprints.py
  93. 3
      selfdrive/modeld/modeld.py
  94. 3
      selfdrive/modeld/tests/test_modeld.py
  95. 7
      selfdrive/test/process_replay/process_replay.py
  96. 2
      selfdrive/test/process_replay/ref_commit
  97. 2
      selfdrive/test/test_onroad.py
  98. 3
      system/hardware/tici/tests/test_power_draw.py

@ -1,6 +1,8 @@
[importlinter] [importlinter]
root_packages = root_packages =
openpilot openpilot
cereal
capnp
[importlinter:contract:1] [importlinter:contract:1]
name = Forbid imports from openpilot.selfdrive.car to openpilot.system name = Forbid imports from openpilot.selfdrive.car to openpilot.system
@ -8,6 +10,8 @@ type = forbidden
source_modules = source_modules =
openpilot.selfdrive.car openpilot.selfdrive.car
forbidden_modules = forbidden_modules =
cereal
capnp
openpilot.common openpilot.common
openpilot.selfdrive.controls openpilot.selfdrive.controls
openpilot.selfdrive.debug openpilot.selfdrive.debug
@ -44,5 +48,16 @@ ignore_imports =
openpilot.selfdrive.car.tests.test_car_interfaces -> openpilot.selfdrive.pandad openpilot.selfdrive.car.tests.test_car_interfaces -> openpilot.selfdrive.pandad
openpilot.selfdrive.car.tests.test_models -> openpilot.selfdrive.pandad openpilot.selfdrive.car.tests.test_models -> openpilot.selfdrive.pandad
openpilot.selfdrive.car.tests.test_car_interfaces -> openpilot.selfdrive.test.fuzzy_generation openpilot.selfdrive.car.tests.test_car_interfaces -> openpilot.selfdrive.test.fuzzy_generation
openpilot.selfdrive.car.tests.test_models -> capnp
openpilot.selfdrive.car.tests.test_car_interfaces -> cereal
openpilot.selfdrive.car.tests.test_car_interfaces -> cereal.messaging
openpilot.selfdrive.car.tests.test_car_interfaces -> openpilot.selfdrive.test.fuzzy_generation
openpilot.selfdrive.car.tests.test_models -> cereal
openpilot.selfdrive.car.tests.test_models -> cereal.messaging
openpilot.selfdrive.car.card -> cereal
openpilot.selfdrive.car.card -> cereal.messaging
openpilot.selfdrive.car.car_specific -> openpilot.selfdrive.controls.lib.events openpilot.selfdrive.car.car_specific -> openpilot.selfdrive.controls.lib.events
openpilot.selfdrive.car.car_specific -> cereal
openpilot.selfdrive.car.car_specific -> cereal.messaging
openpilot.selfdrive.car.card -> capnp
unmatched_ignore_imports_alerting = warn unmatched_ignore_imports_alerting = warn

@ -5,10 +5,8 @@ from dataclasses import dataclass
from enum import IntFlag, ReprEnum, EnumType from enum import IntFlag, ReprEnum, EnumType
from dataclasses import replace from dataclasses import replace
import capnp
from cereal import car
from panda.python.uds import SERVICE_TYPE from panda.python.uds import SERVICE_TYPE
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.can_definitions import CanData from openpilot.selfdrive.car.can_definitions import CanData
from openpilot.selfdrive.car.docs_definitions import CarDocs from openpilot.selfdrive.car.docs_definitions import CarDocs
from openpilot.selfdrive.car.common.numpy_fast import clip, interp from openpilot.selfdrive.car.common.numpy_fast import clip, interp
@ -23,7 +21,7 @@ DT_CTRL = 0.01 # car state and control loop timestep (s)
# kg of standard extra cargo to count for drive, gas, etc... # kg of standard extra cargo to count for drive, gas, etc...
STD_CARGO_KG = 136. STD_CARGO_KG = 136.
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
AngleRateLimit = namedtuple('AngleRateLimit', ['speed_bp', 'angle_v']) AngleRateLimit = namedtuple('AngleRateLimit', ['speed_bp', 'angle_v'])
@ -35,9 +33,9 @@ def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
return val_steady return val_steady
def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, capnp.lib.capnp._EnumModule], def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, structs.CarState.ButtonEvent.Type],
unpressed_btn: int = 0) -> list[capnp.lib.capnp._DynamicStructBuilder]: unpressed_btn: int = 0) -> list[structs.CarState.ButtonEvent]:
events: list[capnp.lib.capnp._DynamicStructBuilder] = [] events: list[structs.CarState.ButtonEvent] = []
if cur_btn == prev_btn: if cur_btn == prev_btn:
return events return events
@ -45,8 +43,8 @@ def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, ca
# Add events for button presses, multiple when a button switches without going to unpressed # Add events for button presses, multiple when a button switches without going to unpressed
for pressed, btn in ((False, prev_btn), (True, cur_btn)): for pressed, btn in ((False, prev_btn), (True, cur_btn)):
if btn != unpressed_btn: if btn != unpressed_btn:
events.append(car.CarState.ButtonEvent(pressed=pressed, events.append(structs.CarState.ButtonEvent(pressed=pressed,
type=buttons_dict.get(btn, ButtonType.unknown))) type=buttons_dict.get(btn, ButtonType.unknown)))
return events return events
@ -183,7 +181,7 @@ def rate_limit(new_value, last_value, dw_step, up_step):
def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float, def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float,
torque_params: car.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float: torque_params: structs.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float:
friction_interp = interp( friction_interp = interp(
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
[-friction_threshold, friction_threshold], [-friction_threshold, friction_threshold],
@ -203,8 +201,8 @@ def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False):
return CanData(addr, bytes(dat), bus) return CanData(addr, bytes(dat), bus)
def get_safety_config(safety_model, safety_param = None): def get_safety_config(safety_model: structs.CarParams.SafetyModel, safety_param: int = None) -> structs.CarParams.SafetyConfig:
ret = car.CarParams.SafetyConfig.new_message() ret = structs.CarParams.SafetyConfig()
ret.safetyModel = safety_model ret.safetyModel = safety_model
if safety_param is not None: if safety_param is not None:
ret.safetyParam = safety_param ret.safetyParam = safety_param

@ -1,3 +1,4 @@
import copy
import numpy as np import numpy as np
from numbers import Number from numbers import Number
@ -132,7 +133,7 @@ class CarController(CarControllerBase):
can_sends = [] can_sends = []
can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r)) can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r))
new_actuators = CC.actuators.as_builder() new_actuators = copy.copy(CC.actuators)
new_actuators.accel = torque_l new_actuators.accel = torque_l
new_actuators.steer = torque_r new_actuators.steer = torque_r
new_actuators.steerOutputCan = torque_r new_actuators.steerOutputCan = torque_r

@ -1,12 +1,12 @@
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.body.values import DBC from openpilot.selfdrive.car.body.values import DBC
class CarState(CarStateBase): class CarState(CarStateBase):
def update(self, cp, *_): def update(self, cp, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
ret.wheelSpeeds.fl = cp.vl['MOTORS_DATA']['SPEED_L'] ret.wheelSpeeds.fl = cp.vl['MOTORS_DATA']['SPEED_L']
ret.wheelSpeeds.fr = cp.vl['MOTORS_DATA']['SPEED_R'] ret.wheelSpeeds.fr = cp.vl['MOTORS_DATA']['SPEED_R']
@ -23,7 +23,7 @@ class CarState(CarStateBase):
ret.fuelGauge = cp.vl["BODY_DATA"]["BATT_PERCENTAGE"] / 100 ret.fuelGauge = cp.vl["BODY_DATA"]["BATT_PERCENTAGE"] / 100
# irrelevant for non-car # irrelevant for non-car
ret.gearShifter = car.CarState.GearShifter.drive ret.gearShifter = structs.CarState.GearShifter.drive
ret.cruiseState.enabled = True ret.cruiseState.enabled = True
ret.cruiseState.available = True ret.cruiseState.available = True

@ -1,8 +1,8 @@
# ruff: noqa: E501 # ruff: noqa: E501
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.body.values import CAR from openpilot.selfdrive.car.body.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
# debug ecu fw version is the git hash of the firmware # debug ecu fw version is the git hash of the firmware

@ -1,16 +1,15 @@
import math import math
from cereal import car from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.notCar = True ret.notCar = True
ret.carName = "body" ret.carName = "body"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.body)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.body)]
ret.minSteerSpeed = -math.inf ret.minSteerSpeed = -math.inf
ret.maxLateralAccel = math.inf # TODO: set to a reasonable value ret.maxLateralAccel = math.inf # TODO: set to a reasonable value
@ -21,6 +20,6 @@ class CarInterface(CarInterfaceBase):
ret.radarUnavailable = True ret.radarUnavailable = True
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = structs.CarParams.SteerControlType.angle
return ret return ret

@ -1,9 +1,9 @@
from cereal import car
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarDocs from openpilot.selfdrive.car.docs_definitions import CarDocs
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
SPEED_FROM_RPM = 0.008587 SPEED_FROM_RPM = 0.008587

@ -1,9 +1,9 @@
import os import os
import time import time
from cereal import car
from openpilot.selfdrive.car import carlog, gen_empty_fingerprint from openpilot.selfdrive.car import carlog, gen_empty_fingerprint
from openpilot.selfdrive.car.can_definitions import CanRecvCallable, CanSendCallable from openpilot.selfdrive.car.can_definitions import CanRecvCallable, CanSendCallable
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from openpilot.selfdrive.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car from openpilot.selfdrive.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car
from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
@ -82,7 +82,7 @@ def can_fingerprint(can_recv: CanRecvCallable) -> tuple[str | None, dict[int, di
# **** for use live only **** # **** for use live only ****
def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, num_pandas: int, def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, num_pandas: int,
cached_params: type[car.CarParams] | None) -> tuple[str | None, dict, str, list, int, bool]: cached_params: CarParams | None) -> tuple[str | None, dict, str, list[CarParams.CarFw], CarParams.FingerprintSource, bool]:
fixed_fingerprint = os.environ.get('FINGERPRINT', "") fixed_fingerprint = os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False) disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
@ -129,17 +129,17 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
car_fingerprint, finger = can_fingerprint(can_recv) car_fingerprint, finger = can_fingerprint(can_recv)
exact_match = True exact_match = True
source = car.CarParams.FingerprintSource.can source = CarParams.FingerprintSource.can
# If FW query returns exactly 1 candidate, use it # If FW query returns exactly 1 candidate, use it
if len(fw_candidates) == 1: if len(fw_candidates) == 1:
car_fingerprint = list(fw_candidates)[0] car_fingerprint = list(fw_candidates)[0]
source = car.CarParams.FingerprintSource.fw source = CarParams.FingerprintSource.fw
exact_match = exact_fw_match exact_match = exact_fw_match
if fixed_fingerprint: if fixed_fingerprint:
car_fingerprint = fixed_fingerprint car_fingerprint = fixed_fingerprint
source = car.CarParams.FingerprintSource.fixed source = CarParams.FingerprintSource.fixed
carlog.error({"event": "fingerprinted", "car_fingerprint": str(car_fingerprint), "source": source, "fuzzy": not exact_match, carlog.error({"event": "fingerprinted", "car_fingerprint": str(car_fingerprint), "source": source, "fuzzy": not exact_match,
"cached": cached, "fw_count": len(car_fw), "ecu_responses": list(ecu_rx_addrs), "vin_rx_addr": vin_rx_addr, "cached": cached, "fw_count": len(car_fw), "ecu_responses": list(ecu_rx_addrs), "vin_rx_addr": vin_rx_addr,
@ -148,13 +148,13 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
return car_fingerprint, finger, vin, car_fw, source, exact_match return car_fingerprint, finger, vin, car_fw, source, exact_match
def get_car_interface(CP): def get_car_interface(CP: CarParams):
CarInterface, CarController, CarState = interfaces[CP.carFingerprint] CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
return CarInterface(CP, CarController, CarState) return CarInterface(CP, CarController, CarState)
def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, experimental_long_allowed: bool, def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, experimental_long_allowed: bool,
num_pandas: int = 1, cached_params: type[car.CarParams] | None = None): num_pandas: int = 1, cached_params: CarParams | None = None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(can_recv, can_send, set_obd_multiplexing, num_pandas, cached_params) candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(can_recv, can_send, set_obd_multiplexing, num_pandas, cached_params)
if candidate is None: if candidate is None:
@ -162,7 +162,7 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
candidate = "MOCK" candidate = "MOCK"
CarInterface, _, _ = interfaces[candidate] CarInterface, _, _ = interfaces[candidate]
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False) CP: CarParams = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False)
CP.carVin = vin CP.carVin = vin
CP.carFw = car_fw CP.carFw = car_fw
CP.fingerprintSource = source CP.fingerprintSource = source

@ -1,16 +1,16 @@
from cereal import car from cereal import car
import cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.selfdrive.car import DT_CTRL from openpilot.selfdrive.car import DT_CTRL, structs
from openpilot.selfdrive.car.interfaces import MAX_CTRL_SPEED from openpilot.selfdrive.car.interfaces import MAX_CTRL_SPEED, CarStateBase, CarControllerBase
from openpilot.selfdrive.car.volkswagen.values import CarControllerParams as VWCarControllerParams from openpilot.selfdrive.car.volkswagen.values import CarControllerParams as VWCarControllerParams
from openpilot.selfdrive.car.hyundai.interface import ENABLE_BUTTONS as HYUNDAI_ENABLE_BUTTONS from openpilot.selfdrive.car.hyundai.interface import ENABLE_BUTTONS as HYUNDAI_ENABLE_BUTTONS
from openpilot.selfdrive.controls.lib.events import Events from openpilot.selfdrive.controls.lib.events import Events
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
GearShifter = car.CarState.GearShifter GearShifter = structs.CarState.GearShifter
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = structs.CarParams.NetworkLocation
# TODO: the goal is to abstract this file into the CarState struct and make events generic # TODO: the goal is to abstract this file into the CarState struct and make events generic
@ -29,7 +29,7 @@ class MockCarState:
class CarSpecificEvents: class CarSpecificEvents:
def __init__(self, CP: car.CarParams): def __init__(self, CP: structs.CarParams):
self.CP = CP self.CP = CP
self.steering_unpressed = 0 self.steering_unpressed = 0
@ -37,7 +37,7 @@ class CarSpecificEvents:
self.no_steer_warning = False self.no_steer_warning = False
self.silent_steer_warning = True self.silent_steer_warning = True
def update(self, CS, CS_prev, CC, CC_prev): def update(self, CS: CarStateBase, CS_prev: car.CarState, CC: CarControllerBase, CC_prev: car.CarControl):
if self.CP.carName in ('body', 'mock'): if self.CP.carName in ('body', 'mock'):
events = Events() events = Events()
@ -50,15 +50,15 @@ class CarSpecificEvents:
elif self.CP.carName == 'nissan': elif self.CP.carName == 'nissan':
events = self.create_common_events(CS.out, CS_prev, extra_gears=[GearShifter.brake]) events = self.create_common_events(CS.out, CS_prev, extra_gears=[GearShifter.brake])
if CS.lkas_enabled: if CS.lkas_enabled: # type: ignore[attr-defined]
events.add(EventName.invalidLkasSetting) events.add(EventName.invalidLkasSetting)
elif self.CP.carName == 'mazda': elif self.CP.carName == 'mazda':
events = self.create_common_events(CS.out, CS_prev) events = self.create_common_events(CS.out, CS_prev)
if CS.lkas_disabled: if CS.lkas_disabled: # type: ignore[attr-defined]
events.add(EventName.lkasDisabled) events.add(EventName.lkasDisabled)
elif CS.low_speed_alert: elif CS.low_speed_alert: # type: ignore[attr-defined]
events.add(EventName.belowSteerSpeed) events.add(EventName.belowSteerSpeed)
elif self.CP.carName == 'chrysler': elif self.CP.carName == 'chrysler':
@ -99,7 +99,7 @@ class CarSpecificEvents:
if self.CP.openpilotLongitudinalControl: if self.CP.openpilotLongitudinalControl:
if CS.out.cruiseState.standstill and not CS.out.brakePressed: if CS.out.cruiseState.standstill and not CS.out.brakePressed:
events.add(EventName.resumeRequired) events.add(EventName.resumeRequired)
if CS.low_speed_lockout: if CS.low_speed_lockout: # type: ignore[attr-defined]
events.add(EventName.lowSpeedLockout) events.add(EventName.lowSpeedLockout)
if CS.out.vEgo < self.CP.minEnableSpeed: if CS.out.vEgo < self.CP.minEnableSpeed:
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
@ -121,7 +121,7 @@ class CarSpecificEvents:
# Enabling at a standstill with brake is allowed # Enabling at a standstill with brake is allowed
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs # TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
below_min_enable_speed = CS.out.vEgo < self.CP.minEnableSpeed or CS.moving_backward below_min_enable_speed = CS.out.vEgo < self.CP.minEnableSpeed or CS.moving_backward # type: ignore[attr-defined]
if below_min_enable_speed and not (CS.out.standstill and CS.out.brake >= 20 and if below_min_enable_speed and not (CS.out.standstill and CS.out.brake >= 20 and
self.CP.networkLocation == NetworkLocation.fwdCamera): self.CP.networkLocation == NetworkLocation.fwdCamera):
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
@ -149,14 +149,14 @@ class CarSpecificEvents:
if CC_prev.enabled and CS.out.vEgo < self.CP.minEnableSpeed: if CC_prev.enabled and CS.out.vEgo < self.CP.minEnableSpeed:
events.add(EventName.speedTooLow) events.add(EventName.speedTooLow)
if CC.eps_timer_soft_disable_alert: if CC.eps_timer_soft_disable_alert: # type: ignore[attr-defined]
events.add(EventName.steerTimeLimit) events.add(EventName.steerTimeLimit)
elif self.CP.carName == 'hyundai': elif self.CP.carName == 'hyundai':
# On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state # On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state
# To avoid re-engaging when openpilot cancels, check user engagement intention via buttons # To avoid re-engaging when openpilot cancels, check user engagement intention via buttons
# Main button also can trigger an engagement on these cars # Main button also can trigger an engagement on these cars
allow_enable = any(btn in HYUNDAI_ENABLE_BUTTONS for btn in CS.cruise_buttons) or any(CS.main_buttons) allow_enable = any(btn in HYUNDAI_ENABLE_BUTTONS for btn in CS.cruise_buttons) or any(CS.main_buttons) # type: ignore[attr-defined]
events = self.create_common_events(CS.out, CS_prev, pcm_enable=self.CP.pcmCruise, allow_enable=allow_enable) events = self.create_common_events(CS.out, CS_prev, pcm_enable=self.CP.pcmCruise, allow_enable=allow_enable)
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
@ -172,8 +172,8 @@ class CarSpecificEvents:
return events return events
def create_common_events(self, CS, CS_prev, extra_gears=None, pcm_enable=True, allow_enable=True, def create_common_events(self, CS: structs.CarState, CS_prev: car.CarState, extra_gears=None, pcm_enable=True,
enable_buttons=(ButtonType.accelCruise, ButtonType.decelCruise)): allow_enable=True, enable_buttons=(ButtonType.accelCruise, ButtonType.decelCruise)):
events = Events() events = Events()
if CS.doorOpen: if CS.doorOpen:

@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import capnp
import os import os
import time import time
from typing import Any
import cereal.messaging as messaging import cereal.messaging as messaging
@ -13,7 +15,7 @@ from openpilot.common.realtime import config_realtime_process, Priority, Ratekee
from openpilot.common.swaglog import cloudlog, ForwardingHandler from openpilot.common.swaglog import cloudlog, ForwardingHandler
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
from openpilot.selfdrive.car import DT_CTRL, carlog from openpilot.selfdrive.car import DT_CTRL, carlog, structs
from openpilot.selfdrive.car.can_definitions import CanData, CanRecvCallable, CanSendCallable from openpilot.selfdrive.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
from openpilot.selfdrive.car.car_specific import CarSpecificEvents, MockCarState from openpilot.selfdrive.car.car_specific import CarSpecificEvents, MockCarState
from openpilot.selfdrive.car.fw_versions import ObdCallback from openpilot.selfdrive.car.fw_versions import ObdCallback
@ -22,6 +24,7 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.controls.lib.events import Events from openpilot.selfdrive.controls.lib.events import Events
REPLAY = "REPLAY" in os.environ REPLAY = "REPLAY" in os.environ
_FIELDS = '__dataclass_fields__' # copy of dataclasses._FIELDS
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -58,8 +61,74 @@ def can_comm_callbacks(logcan: messaging.SubSocket, sendcan: messaging.PubSocket
return can_recv, can_send return can_recv, can_send
def is_dataclass(obj):
"""Similar to dataclasses.is_dataclass without instance type check checking"""
return hasattr(obj, _FIELDS)
def asdictref(obj) -> dict[str, Any]:
"""
Similar to dataclasses.asdict without recursive type checking and copy.deepcopy
Note that the resulting dict will contain references to the original struct as a result
"""
if not is_dataclass(obj):
raise TypeError("asdictref() should be called on dataclass instances")
def _asdictref_inner(obj) -> dict[str, Any] | Any:
if is_dataclass(obj):
ret = {}
for field in getattr(obj, _FIELDS): # similar to dataclasses.fields()
ret[field] = _asdictref_inner(getattr(obj, field))
return ret
elif isinstance(obj, (tuple, list)):
return type(obj)(_asdictref_inner(v) for v in obj)
else:
return obj
return _asdictref_inner(obj)
def convert_to_capnp(struct: structs.CarParams | structs.CarState | structs.CarControl.Actuators) -> capnp.lib.capnp._DynamicStructBuilder:
struct_dict = asdictref(struct)
if isinstance(struct, structs.CarParams):
del struct_dict['lateralTuning']
struct_capnp = car.CarParams.new_message(**struct_dict)
# this is the only union, special handling
which = struct.lateralTuning.which()
struct_capnp.lateralTuning.init(which)
lateralTuning_dict = asdictref(getattr(struct.lateralTuning, which))
setattr(struct_capnp.lateralTuning, which, lateralTuning_dict)
elif isinstance(struct, structs.CarState):
struct_capnp = car.CarState.new_message(**struct_dict)
elif isinstance(struct, structs.CarControl.Actuators):
struct_capnp = car.CarControl.Actuators.new_message(**struct_dict)
else:
raise ValueError(f"Unsupported struct type: {type(struct)}")
return struct_capnp
def convert_carControl(struct: capnp.lib.capnp._DynamicStructReader) -> structs.CarControl:
# TODO: recursively handle any car struct as needed
def remove_deprecated(s: dict) -> dict:
return {k: v for k, v in s.items() if not k.endswith('DEPRECATED')}
struct_dict = struct.to_dict()
struct_dataclass = structs.CarControl(**remove_deprecated({k: v for k, v in struct_dict.items() if not isinstance(k, dict)}))
struct_dataclass.actuators = structs.CarControl.Actuators(**remove_deprecated(struct_dict.get('actuators', {})))
struct_dataclass.cruiseControl = structs.CarControl.CruiseControl(**remove_deprecated(struct_dict.get('cruiseControl', {})))
struct_dataclass.hudControl = structs.CarControl.HUDControl(**remove_deprecated(struct_dict.get('hudControl', {})))
return struct_dataclass
class Car: class Car:
CI: CarInterfaceBase CI: CarInterfaceBase
CP: structs.CarParams
CP_capnp: car.CarParams
def __init__(self, CI=None) -> None: def __init__(self, CI=None) -> None:
self.can_sock = messaging.sub_sock('can', timeout=20) self.can_sock = messaging.sub_sock('can', timeout=20)
@ -72,7 +141,7 @@ class Car:
self.CS_prev = car.CarState.new_message() self.CS_prev = car.CarState.new_message()
self.initialized_prev = False self.initialized_prev = False
self.last_actuators_output = car.CarControl.Actuators.new_message() self.last_actuators_output = structs.CarControl.Actuators()
self.params = Params() self.params = Params()
@ -93,7 +162,7 @@ class Car:
cached_params_raw = self.params.get("CarParamsCache") cached_params_raw = self.params.get("CarParamsCache")
if cached_params_raw is not None: if cached_params_raw is not None:
with car.CarParams.from_bytes(cached_params_raw) as _cached_params: with car.CarParams.from_bytes(cached_params_raw) as _cached_params:
cached_params = _cached_params cached_params = structs.CarParams(carName=_cached_params.carName, carFw=_cached_params.carFw, carVin=_cached_params.carVin)
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params) self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params)
self.CP = self.CI.CP self.CP = self.CI.CP
@ -115,8 +184,8 @@ class Car:
self.CP.passive = not controller_available or self.CP.dashcamOnly self.CP.passive = not controller_available or self.CP.dashcamOnly
if self.CP.passive: if self.CP.passive:
safety_config = car.CarParams.SafetyConfig.new_message() safety_config = structs.CarParams.SafetyConfig()
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput safety_config.safetyModel = structs.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config] self.CP.safetyConfigs = [safety_config]
# Write previous route's CarParams # Write previous route's CarParams
@ -125,7 +194,9 @@ class Car:
self.params.put("CarParamsPrevRoute", prev_cp) self.params.put("CarParamsPrevRoute", prev_cp)
# Write CarParams for controls and radard # Write CarParams for controls and radard
cp_bytes = self.CP.to_bytes() # convert to pycapnp representation for caching and logging
self.CP_capnp = convert_to_capnp(self.CP)
cp_bytes = self.CP_capnp.to_bytes()
self.params.put("CarParams", cp_bytes) self.params.put("CarParams", cp_bytes)
self.params.put_nonblocking("CarParamsCache", cp_bytes) self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes) self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
@ -143,7 +214,7 @@ class Car:
# Update carState from CAN # Update carState from CAN
can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
CS = self.CI.update(can_capnp_to_list(can_strs)) CS = convert_to_capnp(self.CI.update(can_capnp_to_list(can_strs)))
if self.CP.carName == 'mock': if self.CP.carName == 'mock':
CS = self.mock_carstate.update(CS) CS = self.mock_carstate.update(CS)
@ -189,13 +260,13 @@ class Car:
if self.sm.frame % int(50. / DT_CTRL) == 0: if self.sm.frame % int(50. / DT_CTRL) == 0:
cp_send = messaging.new_message('carParams') cp_send = messaging.new_message('carParams')
cp_send.valid = True cp_send.valid = True
cp_send.carParams = self.CP cp_send.carParams = self.CP_capnp
self.pm.send('carParams', cp_send) self.pm.send('carParams', cp_send)
# publish new carOutput # publish new carOutput
co_send = messaging.new_message('carOutput') co_send = messaging.new_message('carOutput')
co_send.valid = self.sm.all_checks(['carControl']) co_send.valid = self.sm.all_checks(['carControl'])
co_send.carOutput.actuatorsOutput = self.last_actuators_output co_send.carOutput.actuatorsOutput = convert_to_capnp(self.last_actuators_output)
self.pm.send('carOutput', co_send) self.pm.send('carOutput', co_send)
# kick off controlsd step while we actuate the latest carControl packet # kick off controlsd step while we actuate the latest carControl packet
@ -219,7 +290,7 @@ class Car:
if self.sm.all_alive(['carControl']): if self.sm.all_alive(['carControl']):
# send car controls over can # send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos) self.last_actuators_output, can_sends = self.CI.apply(convert_carControl(CC), now_nanos)
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid)) self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
self.CC_prev = CC self.CC_prev = CC

@ -1,3 +1,4 @@
import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, apply_meas_steer_torque_limits from openpilot.selfdrive.car import DT_CTRL, apply_meas_steer_torque_limits
from openpilot.selfdrive.car.chrysler import chryslercan from openpilot.selfdrive.car.chrysler import chryslercan
@ -76,7 +77,7 @@ class CarController(CarControllerBase):
self.frame += 1 self.frame += 1
new_actuators = CC.actuators.as_builder() new_actuators = copy.copy(CC.actuators)
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -1,12 +1,11 @@
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS from openpilot.selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase): class CarState(CarStateBase):
@ -26,9 +25,9 @@ class CarState(CarStateBase):
self.distance_button = 0 self.distance_button = 0
def update(self, cp, cp_cam, *_): def update(self, cp, cp_cam, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
prev_distance_button = self.distance_button prev_distance_button = self.distance_button
self.distance_button = cp.vl["CRUISE_BUTTONS"]["ACC_Distance_Dec"] self.distance_button = cp.vl["CRUISE_BUTTONS"]["ACC_Distance_Dec"]

@ -1,8 +1,8 @@
from cereal import car from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.chrysler.values import RAM_CARS from openpilot.selfdrive.car.chrysler.values import RAM_CARS
GearShifter = car.CarState.GearShifter GearShifter = structs.CarState.GearShifter
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
def create_lkas_hud(packer, CP, lkas_active, hud_alert, hud_count, car_model, auto_high_beam): def create_lkas_hud(packer, CP, lkas_active, hud_alert, hud_count, car_model, auto_high_beam):
# LKAS_HUD - Controls what lane-keeping icon is displayed # LKAS_HUD - Controls what lane-keeping icon is displayed

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.chrysler.values import CAR from openpilot.selfdrive.car.chrysler.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FW_VERSIONS = { FW_VERSIONS = {
CAR.CHRYSLER_PACIFICA_2018: { CAR.CHRYSLER_PACIFICA_2018: {

@ -1,14 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "chrysler" ret.carName = "chrysler"
ret.dashcamOnly = candidate in RAM_HD ret.dashcamOnly = candidate in RAM_HD
@ -18,7 +17,7 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
# safety config # safety config
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.chrysler)]
if candidate in RAM_HD: if candidate in RAM_HD:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_HD ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_HD
elif candidate in RAM_DT: elif candidate in RAM_DT:

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from cereal import car from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
from openpilot.selfdrive.car.chrysler.values import DBC from openpilot.selfdrive.car.chrysler.values import DBC
@ -53,7 +53,7 @@ class RadarInterface(RadarInterfaceBase):
if self.trigger_msg not in self.updated_messages: if self.trigger_msg not in self.updated_messages:
return None return None
ret = car.RadarData.new_message() ret = structs.RadarData()
errors = [] errors = []
if not self.rcp.can_valid: if not self.rcp.can_valid:
errors.append("canError") errors.append("canError")
@ -64,7 +64,7 @@ class RadarInterface(RadarInterfaceBase):
trackId = _address_to_track(ii) trackId = _address_to_track(ii)
if trackId not in self.pts: if trackId not in self.pts:
self.pts[trackId] = car.RadarData.RadarPoint.new_message() self.pts[trackId] = structs.RadarData.RadarPoint()
self.pts[trackId].trackId = trackId self.pts[trackId].trackId = trackId
self.pts[trackId].aRel = float('nan') self.pts[trackId].aRel = float('nan')
self.pts[trackId].yvRel = float('nan') self.pts[trackId].yvRel = float('nan')

@ -1,13 +1,13 @@
from enum import IntFlag from enum import IntFlag
from dataclasses import dataclass, field from dataclasses import dataclass, field
from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class ChryslerFlags(IntFlag): class ChryslerFlags(IntFlag):

@ -3,8 +3,8 @@ import jinja2
from enum import Enum from enum import Enum
from natsort import natsorted from natsort import natsorted
from cereal import car
from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarDocs, Column, CommonFootnote, PartType from openpilot.selfdrive.car.docs_definitions import CarDocs, Column, CommonFootnote, PartType
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
from openpilot.selfdrive.car.values import PLATFORMS from openpilot.selfdrive.car.values import PLATFORMS
@ -24,7 +24,7 @@ def get_all_car_docs() -> list[CarDocs]:
car_docs = platform.config.car_docs car_docs = platform.config.car_docs
# If available, uses experimental longitudinal limits for the docs # If available, uses experimental longitudinal limits for the docs
CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(), CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(),
car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True) car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)], experimental_long=True, docs=True)
if CP.dashcamOnly or not len(car_docs): if CP.dashcamOnly or not len(car_docs):
continue continue

@ -4,8 +4,8 @@ import copy
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from cereal import car
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.structs import CarParams
GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2 GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2
MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)" MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)"
@ -248,7 +248,7 @@ class CarDocs:
self.make, self.model, self.years = split_name(self.name) self.make, self.model, self.years = split_name(self.name)
self.year_list = get_year_list(self.years) self.year_list = get_year_list(self.years)
def init(self, CP: car.CarParams, all_footnotes: dict[Enum, int]): def init(self, CP: CarParams, all_footnotes: dict[Enum, int]):
self.car_name = CP.carName self.car_name = CP.carName
self.car_fingerprint = CP.carFingerprint self.car_fingerprint = CP.carFingerprint
@ -316,7 +316,7 @@ class CarDocs:
return self return self
def init_make(self, CP: car.CarParams): def init_make(self, CP: CarParams):
"""CarDocs subclasses can add make-specific logic for harness selection, footnotes, etc.""" """CarDocs subclasses can add make-specific logic for harness selection, footnotes, etc."""
def get_detail_sentence(self, CP): def get_detail_sentence(self, CP):

@ -1,13 +1,13 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_std_steer_angle_limits from openpilot.selfdrive.car import apply_std_steer_angle_limits, structs
from openpilot.selfdrive.car.ford import fordcan from openpilot.selfdrive.car.ford import fordcan
from openpilot.selfdrive.car.ford.values import CarControllerParams, FordFlags from openpilot.selfdrive.car.ford.values import CarControllerParams, FordFlags
from openpilot.selfdrive.car.common.numpy_fast import clip from openpilot.selfdrive.car.common.numpy_fast import clip
from openpilot.selfdrive.car.interfaces import CarControllerBase, V_CRUISE_MAX from openpilot.selfdrive.car.interfaces import CarControllerBase, V_CRUISE_MAX
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = structs.CarControl.Actuators.LongControlState
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw): def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw):
@ -111,7 +111,7 @@ class CarController(CarControllerBase):
self.steer_alert_last = steer_alert self.steer_alert_last = steer_alert
self.lead_distance_bars_last = hud_control.leadDistanceBars self.lead_distance_bars_last = hud_control.leadDistanceBars
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.curvature = self.apply_curvature_last new_actuators.curvature = self.apply_curvature_last
self.frame += 1 self.frame += 1

@ -1,15 +1,14 @@
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.ford.fordcan import CanBus from openpilot.selfdrive.car.ford.fordcan import CanBus
from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
GearShifter = car.CarState.GearShifter GearShifter = structs.CarState.GearShifter
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
class CarState(CarStateBase): class CarState(CarStateBase):
@ -21,8 +20,8 @@ class CarState(CarStateBase):
self.distance_button = 0 self.distance_button = 0
def update(self, cp, cp_cam, *_): def update(self, cp, cp_cam, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
# Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement # Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement
# The vehicle usually recovers out of this state within a minute of normal driving # The vehicle usually recovers out of this state within a minute of normal driving

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.ford.values import CAR from openpilot.selfdrive.car.ford.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FW_VERSIONS = { FW_VERSIONS = {
CAR.FORD_BRONCO_SPORT_MK1: { CAR.FORD_BRONCO_SPORT_MK1: {

@ -1,7 +1,6 @@
from cereal import car from openpilot.selfdrive.car import CanBusBase, structs
from openpilot.selfdrive.car import CanBusBase
HUDControl = car.CarControl.HUDControl HUDControl = structs.CarControl.HUDControl
class CanBus(CanBusBase): class CanBus(CanBusBase):

@ -1,29 +1,28 @@
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.ford.fordcan import CanBus from openpilot.selfdrive.car.ford.fordcan import CanBus
from openpilot.selfdrive.car.ford.values import Ecu, FordFlags from openpilot.selfdrive.car.ford.values import Ecu, FordFlags
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "ford" ret.carName = "ford"
ret.dashcamOnly = bool(ret.flags & FordFlags.CANFD) ret.dashcamOnly = bool(ret.flags & FordFlags.CANFD)
ret.radarUnavailable = True ret.radarUnavailable = True
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
CAN = CanBus(fingerprint=fingerprint) CAN = CanBus(fingerprint=fingerprint)
cfgs = [get_safety_config(car.CarParams.SafetyModel.ford)] cfgs = [get_safety_config(structs.CarParams.SafetyModel.ford)]
if CAN.main >= 4: if CAN.main >= 4:
cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput)) cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs ret.safetyConfigs = cfgs
ret.experimentalLongitudinalAvailable = True ret.experimentalLongitudinalAvailable = True

@ -1,6 +1,6 @@
from math import cos, sin from math import cos, sin
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.ford.fordcan import CanBus from openpilot.selfdrive.car.ford.fordcan import CanBus
from openpilot.selfdrive.car.ford.values import DBC, RADAR from openpilot.selfdrive.car.ford.values import DBC, RADAR
@ -58,7 +58,7 @@ class RadarInterface(RadarInterfaceBase):
if self.trigger_msg not in self.updated_messages: if self.trigger_msg not in self.updated_messages:
return None return None
ret = car.RadarData.new_message() ret = structs.RadarData()
errors = [] errors = []
if not self.rcp.can_valid: if not self.rcp.can_valid:
errors.append("canError") errors.append("canError")
@ -89,7 +89,7 @@ class RadarInterface(RadarInterfaceBase):
# radar point only valid if there have been enough valid measurements # radar point only valid if there have been enough valid measurements
if self.valid_cnt[ii] > 0: if self.valid_cnt[ii] > 0:
if ii not in self.pts: if ii not in self.pts:
self.pts[ii] = car.RadarData.RadarPoint.new_message() self.pts[ii] = structs.RadarData.RadarPoint()
self.pts[ii].trackId = self.track_id self.pts[ii].trackId = self.track_id
self.track_id += 1 self.track_id += 1
self.pts[ii].dRel = cpt['X_Rel'] # from front of car self.pts[ii].dRel = cpt['X_Rel'] # from front of car
@ -112,7 +112,7 @@ class RadarInterface(RadarInterfaceBase):
i = (ii - 1) * 4 + scanIndex i = (ii - 1) * 4 + scanIndex
if i not in self.pts: if i not in self.pts:
self.pts[i] = car.RadarData.RadarPoint.new_message() self.pts[i] = structs.RadarData.RadarPoint()
self.pts[i].trackId = self.track_id self.pts[i].trackId = self.track_id
self.pts[i].aRel = float('nan') self.pts[i].aRel = float('nan')
self.pts[i].yvRel = float('nan') self.pts[i].yvRel = float('nan')

@ -1,30 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import defaultdict from collections import defaultdict
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.ford.values import get_platform_codes from openpilot.selfdrive.car.ford.values import get_platform_codes
from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
if __name__ == "__main__": if __name__ == "__main__":
cars_for_code: defaultdict = defaultdict(lambda: defaultdict(set)) cars_for_code: defaultdict = defaultdict(lambda: defaultdict(set))
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
print(car_model) print(car_model)
for ecu in sorted(ecus, key=lambda x: int(x[0])): for ecu in sorted(ecus):
platform_codes = get_platform_codes(ecus[ecu]) platform_codes = get_platform_codes(ecus[ecu])
for code in platform_codes: for code in platform_codes:
cars_for_code[ecu][code].add(car_model) cars_for_code[ecu][code].add(car_model)
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
print(f' Codes: {sorted(platform_codes)}') print(f' Codes: {sorted(platform_codes)}')
print() print()
print('\nCar models vs. platform codes:') print('\nCar models vs. platform codes:')
for ecu, codes in cars_for_code.items(): for ecu, codes in cars_for_code.items():
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
for code, cars in codes.items(): for code, cars in codes.items():
print(f' {code!r}: {sorted(map(str, cars))}') print(f' {code!r}: {sorted(map(str, cars))}')

@ -1,16 +1,15 @@
import random import random
from collections.abc import Iterable from collections.abc import Iterable
import capnp
from hypothesis import settings, given, strategies as st from hypothesis import settings, given, strategies as st
from parameterized import parameterized from parameterized import parameterized
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fw_versions import build_fw_dict from openpilot.selfdrive.car.fw_versions import build_fw_dict
from openpilot.selfdrive.car.ford.values import CAR, FW_QUERY_CONFIG, FW_PATTERN, get_platform_codes from openpilot.selfdrive.car.ford.values import CAR, FW_QUERY_CONFIG, FW_PATTERN, get_platform_codes
from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_ADDRESSES = { ECU_ADDRESSES = {
@ -49,7 +48,7 @@ class TestFordFW:
assert subaddr is None, "Unexpected ECU subaddress" assert subaddr is None, "Unexpected ECU subaddress"
@parameterized.expand(FW_VERSIONS.items()) @parameterized.expand(FW_VERSIONS.items())
def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[capnp.lib.capnp._EnumModule, int, int | None], Iterable[bytes]]): def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[Ecu, int, int | None], Iterable[bytes]]):
for (ecu, addr, subaddr), fws in fw_versions.items(): for (ecu, addr, subaddr), fws in fw_versions.items():
assert ecu in ECU_PART_NUMBER, "Unexpected ECU" assert ecu in ECU_PART_NUMBER, "Unexpected ECU"
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch" assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
@ -93,10 +92,10 @@ class TestFordFW:
for ecu, fw_versions in fw_by_addr.items(): for ecu, fw_versions in fw_by_addr.items():
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
fw = random.choice(fw_versions) fw = random.choice(fw_versions)
car_fw.append({"ecu": ecu_name, "fwVersion": fw, "address": addr, car_fw.append(CarParams.CarFw(ecu=ecu_name, fwVersion=fw, address=addr,
"subAddress": 0 if sub_addr is None else sub_addr}) subAddress=0 if sub_addr is None else sub_addr))
CP = car.CarParams.new_message(carFw=car_fw) CP = CarParams(carFw=car_fw)
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS) matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
assert matches == {platform} assert matches == {platform}

@ -4,13 +4,13 @@ from dataclasses import dataclass, field, replace
from enum import Enum, IntFlag from enum import Enum, IntFlag
import panda.python.uds as uds 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 import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \ from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
Device Device
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class CarControllerParams: class CarControllerParams:
@ -65,7 +65,7 @@ class FordCarDocs(CarDocs):
hybrid: bool = False hybrid: bool = False
plug_in_hybrid: bool = False plug_in_hybrid: bool = False
def init_make(self, CP: car.CarParams): def init_make(self, CP: CarParams):
harness = CarHarness.ford_q4 if CP.flags & FordFlags.CANFD else CarHarness.ford_q3 harness = CarHarness.ford_q4 if CP.flags & FordFlags.CANFD else CarHarness.ford_q3
if CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_MAVERICK_MK1, CAR.FORD_F_150_MK14, CAR.FORD_F_150_LIGHTNING_MK1): if CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_MAVERICK_MK1, CAR.FORD_F_150_MK14, CAR.FORD_F_150_LIGHTNING_MK1):
self.car_parts = CarParts([Device.threex_angled_mount, harness]) self.car_parts = CarParts([Device.threex_angled_mount, harness])

@ -1,4 +1,3 @@
import capnp
import copy import copy
from dataclasses import dataclass, field from dataclasses import dataclass, field
import struct import struct
@ -6,9 +5,11 @@ from collections.abc import Callable
import panda.python.uds as uds import panda.python.uds as uds
from openpilot.selfdrive.car.structs import CarParams
AddrType = tuple[int, int | None] AddrType = tuple[int, int | None]
EcuAddrBusType = tuple[int, int | None, int] EcuAddrBusType = tuple[int, int | None, int]
EcuAddrSubAddr = tuple[int, int, int | None] EcuAddrSubAddr = tuple[CarParams.Ecu, int, int | None]
LiveFwVersions = dict[AddrType, set[bytes]] LiveFwVersions = dict[AddrType, set[bytes]]
OfflineFwVersions = dict[str, dict[EcuAddrSubAddr, list[bytes]]] OfflineFwVersions = dict[str, dict[EcuAddrSubAddr, list[bytes]]]
@ -77,7 +78,7 @@ class StdQueries:
class Request: class Request:
request: list[bytes] request: list[bytes]
response: list[bytes] response: list[bytes]
whitelist_ecus: list[int] = field(default_factory=list) whitelist_ecus: list[CarParams.Ecu] = field(default_factory=list)
rx_offset: int = 0x8 rx_offset: int = 0x8
bus: int = 1 bus: int = 1
# Whether this query should be run on the first auxiliary panda (CAN FD cars for example) # Whether this query should be run on the first auxiliary panda (CAN FD cars for example)
@ -93,9 +94,9 @@ class FwQueryConfig:
requests: list[Request] requests: list[Request]
# TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus # TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus
# Overrides and removes from essential ecus for specific models and ecus (exact matching) # Overrides and removes from essential ecus for specific models and ecus (exact matching)
non_essential_ecus: dict[capnp.lib.capnp._EnumModule, list[str]] = field(default_factory=dict) non_essential_ecus: dict[CarParams.Ecu, list[str]] = field(default_factory=dict)
# Ecus added for data collection, not to be fingerprinted on # Ecus added for data collection, not to be fingerprinted on
extra_ecus: list[tuple[capnp.lib.capnp._EnumModule, int, int | None]] = field(default_factory=list) extra_ecus: list[tuple[CarParams.Ecu, int, int | None]] = field(default_factory=list)
# Function a brand can implement to provide better fuzzy matching. Takes in FW versions and VIN, # Function a brand can implement to provide better fuzzy matching. Takes in FW versions and VIN,
# returns set of candidates. Only will match if one candidate is returned # returns set of candidates. Only will match if one candidate is returned
match_fw_to_car_fuzzy: Callable[[LiveFwVersions, str, OfflineFwVersions], set[str]] | None = None match_fw_to_car_fuzzy: Callable[[LiveFwVersions, str, OfflineFwVersions], set[str]] | None = None

@ -3,19 +3,18 @@ from collections.abc import Callable, Iterator
from typing import Protocol, TypeVar from typing import Protocol, TypeVar
from tqdm import tqdm from tqdm import tqdm
import capnp
import panda.python.uds as uds import panda.python.uds as uds
from cereal import car
from openpilot.selfdrive.car import carlog from openpilot.selfdrive.car import carlog
from openpilot.selfdrive.car.can_definitions import CanRecvCallable, CanSendCallable
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.can_definitions import CanRecvCallable, CanSendCallable
from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions
from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa] ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
FUZZY_EXCLUDE_ECUS = [Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps, Ecu.debug] FUZZY_EXCLUDE_ECUS = [Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps, Ecu.debug]
@ -39,7 +38,7 @@ def is_brand(brand: str, filter_brand: str | None) -> bool:
return filter_brand is None or brand == filter_brand return filter_brand is None or brand == filter_brand
def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], filter_brand: str = None) -> dict[AddrType, set[bytes]]: def build_fw_dict(fw_versions: list[CarParams.CarFw], filter_brand: str = None) -> dict[AddrType, set[bytes]]:
fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set) fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set)
for fw in fw_versions: for fw in fw_versions:
if is_brand(fw.brand, filter_brand) and not fw.logging: if is_brand(fw.brand, filter_brand) and not fw.logging:
@ -144,8 +143,8 @@ def match_fw_to_car_exact(live_fw_versions: LiveFwVersions, match_brand: str = N
return set(candidates.keys()) - invalid return set(candidates.keys()) - invalid
def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], vin: str, def match_fw_to_car(fw_versions: list[CarParams.CarFw], vin: str, allow_exact: bool = True,
allow_exact: bool = True, allow_fuzzy: bool = True, log: bool = True) -> tuple[bool, set[str]]: allow_fuzzy: bool = True, log: bool = True) -> tuple[bool, set[str]]:
# Try exact matching first # Try exact matching first
exact_matches: list[tuple[bool, MatchFwToCar]] = [] exact_matches: list[tuple[bool, MatchFwToCar]] = []
if allow_exact: if allow_exact:
@ -229,7 +228,7 @@ def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, set[Ad
def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, vin: str, def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, vin: str,
ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1, debug: bool = False, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1, debug: bool = False,
progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]: progress: bool = False) -> list[CarParams.CarFw]:
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" """Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
all_car_fw = [] all_car_fw = []
@ -254,7 +253,7 @@ def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable
def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, query_brand: str = None, def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, query_brand: str = None,
extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, debug: bool = False, extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, debug: bool = False,
progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]: progress: bool = False) -> list[CarParams.CarFw]:
versions = VERSIONS.copy() versions = VERSIONS.copy()
if query_brand is not None: if query_brand is not None:
@ -306,7 +305,7 @@ def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_ob
if query_addrs: if query_addrs:
query = IsoTpParallelQuery(can_send, can_recv, r.bus, query_addrs, r.request, r.response, r.rx_offset, debug=debug) query = IsoTpParallelQuery(can_send, can_recv, r.bus, query_addrs, r.request, r.response, r.rx_offset, debug=debug)
for (tx_addr, sub_addr), version in query.get_data(timeout).items(): for (tx_addr, sub_addr), version in query.get_data(timeout).items():
f = car.CarParams.CarFw.new_message() f = CarParams.CarFw()
f.ecu = ecu_types.get((brand, tx_addr, sub_addr), Ecu.unknown) f.ecu = ecu_types.get((brand, tx_addr, sub_addr), Ecu.unknown)
f.fwVersion = version f.fwVersion = version

@ -1,15 +1,15 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits, structs
from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.gm import gmcan from openpilot.selfdrive.car.gm import gmcan
from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons
from openpilot.selfdrive.car.common.numpy_fast import interp from openpilot.selfdrive.car.common.numpy_fast import interp
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = structs.CarParams.NetworkLocation
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = structs.CarControl.Actuators.LongControlState
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s # Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
CAMERA_CANCEL_DELAY_FRAMES = 10 CAMERA_CANCEL_DELAY_FRAMES = 10
@ -153,7 +153,7 @@ class CarController(CarControllerBase):
if self.frame % 10 == 0: if self.frame % 10 == 0:
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status)) can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last
new_actuators.gas = self.apply_gas new_actuators.gas = self.apply_gas

@ -1,16 +1,15 @@
import copy import copy
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.numpy_fast import mean from openpilot.selfdrive.car.common.numpy_fast import mean
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, CruiseButtons, STEER_THRESHOLD from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, CruiseButtons, STEER_THRESHOLD
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = structs.CarParams.NetworkLocation
STANDSTILL_THRESHOLD = 10 * 0.0311 * CV.KPH_TO_MS STANDSTILL_THRESHOLD = 10 * 0.0311 * CV.KPH_TO_MS
@ -34,8 +33,8 @@ class CarState(CarStateBase):
self.distance_button = 0 self.distance_button = 0
def update(self, pt_cp, cam_cp, _, __, loopback_cp): def update(self, pt_cp, cam_cp, _, __, loopback_cp) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
prev_cruise_buttons = self.cruise_buttons prev_cruise_buttons = self.cruise_buttons
prev_distance_button = self.distance_button prev_distance_button = self.distance_button

@ -1,18 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
from cereal import car
from math import fabs, exp from math import fabs, exp
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config, get_friction from openpilot.selfdrive.car import get_safety_config, get_friction, structs
from openpilot.selfdrive.car.common.basedir import BASEDIR from openpilot.selfdrive.car.common.basedir import BASEDIR
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
from openpilot.selfdrive.car.gm.values import CAR, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus from openpilot.selfdrive.car.gm.values import CAR, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = structs.CarParams.NetworkLocation
NON_LINEAR_TORQUE_PARAMS = { NON_LINEAR_TORQUE_PARAMS = {
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178], CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
@ -41,8 +40,8 @@ class CarInterface(CarInterfaceBase):
else: else:
return CarInterfaceBase.get_steer_feedforward_default return CarInterfaceBase.get_steer_feedforward_default
def torque_from_lateral_accel_siglin(self, latcontrol_inputs: LatControlInputs, torque_params: car.CarParams.LateralTorqueTuning, lateral_accel_error: float, def torque_from_lateral_accel_siglin(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float: lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation) friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
def sig(val): def sig(val):
@ -57,14 +56,14 @@ class CarInterface(CarInterfaceBase):
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1) # An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
# This has big effect on the stability about 0 (noise when going straight) # This has big effect on the stability about 0 (noise when going straight)
# ToDo: To generalize to other GMs, explore tanh function as the nonlinear # ToDo: To generalize to other GMs, explore tanh function as the nonlinear
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint) non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint) # type: ignore[call-overload]
assert non_linear_torque_params, "The params are not defined" assert non_linear_torque_params, "The params are not defined"
a, b, c, _ = non_linear_torque_params a, b, c, _ = non_linear_torque_params
steer_torque = (sig(latcontrol_inputs.lateral_acceleration * a) * b) + (latcontrol_inputs.lateral_acceleration * c) steer_torque = (sig(latcontrol_inputs.lateral_acceleration * a) * b) + (latcontrol_inputs.lateral_acceleration * c)
return float(steer_torque) + friction return float(steer_torque) + friction
def torque_from_lateral_accel_neural(self, latcontrol_inputs: LatControlInputs, torque_params: car.CarParams.LateralTorqueTuning, lateral_accel_error: float, def torque_from_lateral_accel_neural(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float: lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation) friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
inputs = list(latcontrol_inputs) inputs = list(latcontrol_inputs)
if gravity_adjusted: if gravity_adjusted:
@ -81,9 +80,9 @@ class CarInterface(CarInterfaceBase):
return self.torque_from_lateral_accel_linear return self.torque_from_lateral_accel_linear
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "gm" ret.carName = "gm"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math import math
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.gm.values import DBC, CanBus from openpilot.selfdrive.car.gm.values import DBC, CanBus
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
@ -52,7 +52,7 @@ class RadarInterface(RadarInterfaceBase):
if self.trigger_msg not in self.updated_messages: if self.trigger_msg not in self.updated_messages:
return None return None
ret = car.RadarData.new_message() ret = structs.RadarData()
header = self.rcp.vl[RADAR_HEADER_MSG] header = self.rcp.vl[RADAR_HEADER_MSG]
fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \ fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \
header['FLRRYawRtPlsblityFlt'] or header['FLRRHWFltPrsntInt'] or \ header['FLRRYawRtPlsblityFlt'] or header['FLRRHWFltPrsntInt'] or \
@ -82,7 +82,7 @@ class RadarInterface(RadarInterfaceBase):
targetId = cpt['TrkObjectID'] targetId = cpt['TrkObjectID']
currentTargets.add(targetId) currentTargets.add(targetId)
if targetId not in self.pts: if targetId not in self.pts:
self.pts[targetId] = car.RadarData.RadarPoint.new_message() self.pts[targetId] = structs.RadarData.RadarPoint()
self.pts[targetId].trackId = targetId self.pts[targetId].trackId = targetId
distance = cpt['TrkRange'] distance = cpt['TrkRange']
self.pts[targetId].dRel = distance # from front of car self.pts[targetId].dRel = distance # from front of car

@ -1,11 +1,11 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from cereal import car
from openpilot.selfdrive.car import dbc_dict, PlatformConfig, DbcDict, Platforms, CarSpecs from openpilot.selfdrive.car import dbc_dict, PlatformConfig, DbcDict, Platforms, CarSpecs
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class CarControllerParams: class CarControllerParams:
@ -63,8 +63,8 @@ class CarControllerParams:
class GMCarDocs(CarDocs): class GMCarDocs(CarDocs):
package: str = "Adaptive Cruise Control (ACC)" package: str = "Adaptive Cruise Control (ACC)"
def init_make(self, CP: car.CarParams): def init_make(self, CP: CarParams):
if CP.networkLocation == car.CarParams.NetworkLocation.fwdCamera: if CP.networkLocation == CarParams.NetworkLocation.fwdCamera:
self.car_parts = CarParts.common([CarHarness.gm]) self.car_parts = CarParts.common([CarHarness.gm])
else: else:
self.car_parts = CarParts.common([CarHarness.obd_ii]) self.car_parts = CarParts.common([CarHarness.obd_ii])

@ -1,15 +1,15 @@
import copy
from collections import namedtuple from collections import namedtuple
from cereal import car
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, rate_limit, make_tester_present_msg from openpilot.selfdrive.car import DT_CTRL, rate_limit, make_tester_present_msg, structs
from openpilot.selfdrive.car.common.numpy_fast import clip, interp from openpilot.selfdrive.car.common.numpy_fast import clip, interp
from openpilot.selfdrive.car.honda import hondacan from openpilot.selfdrive.car.honda import hondacan
from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = structs.CarControl.Actuators.LongControlState
def compute_gb_honda_bosch(accel, speed): def compute_gb_honda_bosch(accel, speed):
@ -83,11 +83,11 @@ def process_hud_alert(hud_alert):
# priority is: FCW, steer required, all others # priority is: FCW, steer required, all others
if hud_alert == VisualAlert.fcw: if hud_alert == VisualAlert.fcw:
fcw_display = VISUAL_HUD[hud_alert.raw] fcw_display = VISUAL_HUD[hud_alert]
elif hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw): elif hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw):
steer_required = VISUAL_HUD[hud_alert.raw] steer_required = VISUAL_HUD[hud_alert]
else: else:
acc_alert = VISUAL_HUD[hud_alert.raw] acc_alert = VISUAL_HUD[hud_alert]
return fcw_display, steer_required, acc_alert return fcw_display, steer_required, acc_alert
@ -242,7 +242,7 @@ class CarController(CarControllerBase):
self.speed = pcm_speed self.speed = pcm_speed
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.speed = self.speed new_actuators.speed = self.speed
new_actuators.accel = self.accel new_actuators.accel = self.accel
new_actuators.gas = self.gas new_actuators.gas = self.gas

@ -1,9 +1,8 @@
from collections import defaultdict from collections import defaultdict
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.numpy_fast import interp from openpilot.selfdrive.car.common.numpy_fast import interp
from openpilot.selfdrive.car.honda.hondacan import CanBus, get_cruise_speed_conversion from openpilot.selfdrive.car.honda.hondacan import CanBus, get_cruise_speed_conversion
@ -12,8 +11,8 @@ from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HOND
HondaFlags, CruiseButtons, CruiseSettings HondaFlags, CruiseButtons, CruiseSettings
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise, BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel} CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel}
@ -110,8 +109,8 @@ class CarState(CarStateBase):
# However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX) # However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX)
self.dash_speed_seen = False self.dash_speed_seen = False
def update(self, cp, cp_cam, _, cp_body, __): def update(self, cp, cp_cam, _, cp_body, __) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
# car params # car params
v_weight_v = [0., 1.] # don't trust smooth speed at low values to avoid premature zero snapping v_weight_v = [0., 1.] # don't trust smooth speed at low values to avoid premature zero snapping
@ -198,7 +197,7 @@ class CarState(CarStateBase):
ret.steeringTorque = cp.vl["STEER_STATUS"]["STEER_TORQUE_SENSOR"] ret.steeringTorque = cp.vl["STEER_STATUS"]["STEER_TORQUE_SENSOR"]
ret.steeringTorqueEps = cp.vl["STEER_MOTOR_TORQUE"]["MOTOR_TORQUE"] ret.steeringTorqueEps = cp.vl["STEER_MOTOR_TORQUE"]["MOTOR_TORQUE"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD.get(self.CP.carFingerprint, 1200) ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD.get(self.CP.carFingerprint, 1200) # type: ignore[call-overload]
if self.CP.carFingerprint in HONDA_BOSCH: if self.CP.carFingerprint in HONDA_BOSCH:
# The PCM always manages its own cruise control state, but doesn't publish it # The PCM always manages its own cruise control state, but doesn't publish it

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.honda.values import CAR from openpilot.selfdrive.car.honda.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
# Modified FW can be identified by the second dash being replaced by a comma # Modified FW can be identified by the second dash being replaced by a comma
# For example: `b'39990-TVA,A150\x00\x00'` # For example: `b'39990-TVA,A150\x00\x00'`

@ -1,16 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.numpy_fast import interp from openpilot.selfdrive.car.common.numpy_fast import interp
from openpilot.selfdrive.car.honda.hondacan import CanBus from openpilot.selfdrive.car.honda.hondacan import CanBus
from openpilot.selfdrive.car.honda.values import CarControllerParams, HondaFlags, CAR, HONDA_BOSCH, \ from openpilot.selfdrive.car.honda.values import CarControllerParams, HondaFlags, CAR, HONDA_BOSCH, \
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.disable_ecu import disable_ecu
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -26,13 +25,13 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.NIDEC_ACCEL_MIN, interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS) return CarControllerParams.NIDEC_ACCEL_MIN, interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "honda" ret.carName = "honda"
CAN = CanBus(ret, fingerprint) CAN = CanBus(ret, fingerprint)
if candidate in HONDA_BOSCH: if candidate in HONDA_BOSCH:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaBosch)]
ret.radarUnavailable = True ret.radarUnavailable = True
# Disable the radar and let openpilot control longitudinal # Disable the radar and let openpilot control longitudinal
# WARNING: THIS DISABLES AEB! # WARNING: THIS DISABLES AEB!
@ -41,7 +40,7 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = experimental_long ret.openpilotLongitudinalControl = experimental_long
ret.pcmCruise = not ret.openpilotLongitudinalControl ret.pcmCruise = not ret.openpilotLongitudinalControl
else: else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaNidec)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaNidec)]
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
ret.pcmCruise = True ret.pcmCruise = True

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
from openpilot.selfdrive.car.honda.values import DBC from openpilot.selfdrive.car.honda.values import DBC
@ -47,7 +47,7 @@ class RadarInterface(RadarInterfaceBase):
return rr return rr
def _update(self, updated_messages): def _update(self, updated_messages):
ret = car.RadarData.new_message() ret = structs.RadarData()
for ii in sorted(updated_messages): for ii in sorted(updated_messages):
cpt = self.rcp.vl[ii] cpt = self.rcp.vl[ii]
@ -57,7 +57,7 @@ class RadarInterface(RadarInterfaceBase):
self.radar_wrong_config = cpt['RADAR_STATE'] == 0x69 self.radar_wrong_config = cpt['RADAR_STATE'] == 0x69
elif cpt['LONG_DIST'] < 255: elif cpt['LONG_DIST'] < 255:
if ii not in self.pts or cpt['NEW_TRACK']: if ii not in self.pts or cpt['NEW_TRACK']:
self.pts[ii] = car.RadarData.RadarPoint.new_message() self.pts[ii] = structs.RadarData.RadarPoint()
self.pts[ii].trackId = self.track_id self.pts[ii].trackId = self.track_id
self.track_id += 1 self.track_id += 1
self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car

@ -1,15 +1,14 @@
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum, IntFlag from enum import Enum, IntFlag
from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = car.CarParams.Ecu Ecu = structs.CarParams.Ecu
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
class CarControllerParams: class CarControllerParams:
@ -90,7 +89,7 @@ VISUAL_HUD = {
class HondaCarDocs(CarDocs): class HondaCarDocs(CarDocs):
package: str = "Honda Sensing" package: str = "Honda Sensing"
def init_make(self, CP: car.CarParams): def init_make(self, CP: structs.CarParams):
if CP.flags & HondaFlags.BOSCH: if CP.flags & HondaFlags.BOSCH:
self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.flags & HondaFlags.BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a]) self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.flags & HondaFlags.BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a])
else: else:

@ -1,6 +1,6 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.numpy_fast import clip from openpilot.selfdrive.car.common.numpy_fast import clip
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
@ -9,8 +9,8 @@ from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = structs.CarControl.Actuators.LongControlState
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second # EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault # All slightly below EPS thresholds to avoid fault
@ -162,7 +162,7 @@ class CarController(CarControllerBase):
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer)) can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steer = apply_steer / self.params.STEER_MAX new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer
new_actuators.accel = accel new_actuators.accel = accel
@ -170,7 +170,7 @@ class CarController(CarControllerBase):
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends
def create_button_messages(self, CC: car.CarControl, CS: CarState, use_clu11: bool): def create_button_messages(self, CC: structs.CarControl, CS: CarState, use_clu11: bool):
can_sends = [] can_sends = []
if use_clu11: if use_clu11:
if CC.cruiseControl.cancel: if CC.cruiseControl.cancel:

@ -2,17 +2,16 @@ from collections import deque
import copy import copy
import math import math
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \ from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, Buttons, CarControllerParams CANFD_CAR, Buttons, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
PREV_BUTTON_SAMPLES = 8 PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames CLUSTER_SAMPLE_RATE = 20 # frames
@ -58,11 +57,11 @@ class CarState(CarStateBase):
self.params = CarControllerParams(CP) self.params = CarControllerParams(CP)
def update(self, cp, cp_cam, *_): def update(self, cp, cp_cam, *_) -> structs.CarState:
if self.CP.carFingerprint in CANFD_CAR: if self.CP.carFingerprint in CANFD_CAR:
return self.update_canfd(cp, cp_cam) return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message() ret = structs.CarState()
cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0 self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
@ -177,8 +176,8 @@ class CarState(CarStateBase):
return ret return ret
def update_canfd(self, cp, cp_cam): def update_canfd(self, cp, cp_cam) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1 self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.hyundai.values import CAR from openpilot.selfdrive.car.hyundai.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
# The existence of SCC or RDR in the fwdRadar FW usually determines the radar's function, # The existence of SCC or RDR in the fwdRadar FW usually determines the radar's function,
# i.e. if it sends the SCC messages or if another ECU like the camera or ADAS Driving ECU does # i.e. if it sends the SCC messages or if another ECU like the camera or ADAS Driving ECU does

@ -1,6 +1,5 @@
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \ from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \ CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
@ -9,14 +8,14 @@ from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.disable_ecu import disable_ecu
Ecu = car.CarParams.Ecu Ecu = structs.CarParams.Ecu
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL) ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "hyundai" ret.carName = "hyundai"
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
@ -97,9 +96,9 @@ class CarInterface(CarInterfaceBase):
# *** panda safety config *** # *** panda safety config ***
if candidate in CANFD_CAR: if candidate in CANFD_CAR:
cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd), ] cfgs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiCanfd), ]
if CAN.ECAN >= 4: if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput)) cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs ret.safetyConfigs = cfgs
if ret.flags & HyundaiFlags.CANFD_HDA2: if ret.flags & HyundaiFlags.CANFD_HDA2:
@ -113,9 +112,9 @@ class CarInterface(CarInterfaceBase):
else: else:
if candidate in LEGACY_SAFETY_MODE_CAR: if candidate in LEGACY_SAFETY_MODE_CAR:
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiLegacy)]
else: else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundai, 0)]
if candidate in CAMERA_SCC_CAR: if candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC

@ -1,7 +1,7 @@
import math import math
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
from openpilot.selfdrive.car.hyundai.values import DBC from openpilot.selfdrive.car.hyundai.values import DBC
@ -44,7 +44,7 @@ class RadarInterface(RadarInterfaceBase):
return rr return rr
def _update(self, updated_messages): def _update(self, updated_messages):
ret = car.RadarData.new_message() ret = structs.RadarData()
if self.rcp is None: if self.rcp is None:
return ret return ret
@ -58,7 +58,7 @@ class RadarInterface(RadarInterfaceBase):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"] msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
if addr not in self.pts: if addr not in self.pts:
self.pts[addr] = car.RadarData.RadarPoint.new_message() self.pts[addr] = structs.RadarData.RadarPoint()
self.pts[addr].trackId = self.track_id self.pts[addr].trackId = self.track_id
self.track_id += 1 self.track_id += 1

@ -1,22 +1,21 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.hyundai.values import PLATFORM_CODE_ECUS, get_platform_codes from openpilot.selfdrive.car.hyundai.values import PLATFORM_CODE_ECUS, get_platform_codes
from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
if __name__ == "__main__": if __name__ == "__main__":
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
print() print()
print(car_model) print(car_model)
for ecu in sorted(ecus, key=lambda x: int(x[0])): for ecu in sorted(ecus):
if ecu[0] not in PLATFORM_CODE_ECUS: if ecu[0] not in PLATFORM_CODE_ECUS:
continue continue
platform_codes = get_platform_codes(ecus[ecu]) platform_codes = get_platform_codes(ecus[ecu])
codes = {code for code, _ in platform_codes} codes = {code for code, _ in platform_codes}
dates = {date for _, date in platform_codes if date is not None} dates = {date for _, date in platform_codes if date is not None}
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
print(f' Codes: {codes}') print(f' Codes: {codes}')
print(f' Dates: {dates}') print(f' Dates: {dates}')

@ -2,8 +2,8 @@ from hypothesis import settings, given, strategies as st
import pytest import pytest
from cereal import car
from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fw_versions import build_fw_dict from openpilot.selfdrive.car.fw_versions import build_fw_dict
from openpilot.selfdrive.car.hyundai.interface import CarInterface from openpilot.selfdrive.car.hyundai.interface import CarInterface
from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
@ -13,8 +13,7 @@ from openpilot.selfdrive.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CA
HyundaiFlags, get_platform_codes HyundaiFlags, get_platform_codes
from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
# Some platforms have date codes in a different format we don't yet parse (or are missing). # Some platforms have date codes in a different format we don't yet parse (or are missing).
# For now, assert list of expected missing date cars # For now, assert list of expected missing date cars
@ -46,7 +45,7 @@ class TestHyundaiFingerprint:
def test_feature_detection(self): def test_feature_detection(self):
# HDA2 # HDA2
for has_adas in (True, False): for has_adas in (True, False):
car_fw = [car.CarParams.CarFw(ecu=Ecu.adas if has_adas else Ecu.fwdCamera)] car_fw = [CarParams.CarFw(ecu=Ecu.adas if has_adas else Ecu.fwdCamera)]
CP = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), car_fw, False, False) CP = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), car_fw, False, False)
assert bool(CP.flags & HyundaiFlags.CANFD_HDA2) == has_adas assert bool(CP.flags & HyundaiFlags.CANFD_HDA2) == has_adas
@ -76,7 +75,7 @@ class TestHyundaiFingerprint:
for car_model in CANFD_CAR: for car_model in CANFD_CAR:
ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()} ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()}
ecus_not_in_whitelist = ecus - CANFD_EXPECTED_ECUS ecus_not_in_whitelist = ecus - CANFD_EXPECTED_ECUS
ecu_strings = ", ".join([f"Ecu.{ECU_NAME[ecu]}" for ecu in ecus_not_in_whitelist]) ecu_strings = ", ".join([f"Ecu.{ecu}" for ecu in ecus_not_in_whitelist])
assert len(ecus_not_in_whitelist) == 0, \ assert len(ecus_not_in_whitelist) == 0, \
f"{car_model}: Car model has unexpected ECUs: {ecu_strings}" f"{car_model}: Car model has unexpected ECUs: {ecu_strings}"
@ -223,10 +222,10 @@ class TestHyundaiFingerprint:
for ecu, fw_versions in fw_by_addr.items(): for ecu, fw_versions in fw_by_addr.items():
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
for fw in fw_versions: for fw in fw_versions:
car_fw.append({"ecu": ecu_name, "fwVersion": fw, "address": addr, car_fw.append(CarParams.CarFw(ecu=ecu_name, fwVersion=fw, address=addr,
"subAddress": 0 if sub_addr is None else sub_addr}) subAddress=0 if sub_addr is None else sub_addr))
CP = car.CarParams.new_message(carFw=car_fw) CP = CarParams(carFw=car_fw)
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS) matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
if len(matches) == 1: if len(matches) == 1:
assert list(matches)[0] == platform assert list(matches)[0] == platform

@ -2,14 +2,14 @@ import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, IntFlag from enum import Enum, IntFlag
from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class CarControllerParams: class CarControllerParams:
@ -107,7 +107,7 @@ class Footnote(Enum):
class HyundaiCarDocs(CarDocs): class HyundaiCarDocs(CarDocs):
package: str = "Smart Cruise Control (SCC)" package: str = "Smart Cruise Control (SCC)"
def init_make(self, CP: car.CarParams): def init_make(self, CP: CarParams):
if CP.flags & HyundaiFlags.CANFD: if CP.flags & HyundaiFlags.CANFD:
self.footnotes.insert(0, Footnote.CANFD) self.footnotes.insert(0, Footnote.CANFD)

@ -8,8 +8,8 @@ from typing import Any, NamedTuple
from collections.abc import Callable from collections.abc import Callable
from functools import cache from functools import cache
from cereal import car
from openpilot.selfdrive.car import DT_CTRL, apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_friction, STD_CARGO_KG from openpilot.selfdrive.car import DT_CTRL, apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_friction, STD_CARGO_KG
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.can_definitions import CanData, CanRecvCallable, CanSendCallable from openpilot.selfdrive.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
from openpilot.selfdrive.car.common.basedir import BASEDIR from openpilot.selfdrive.car.common.basedir import BASEDIR
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
@ -17,7 +17,7 @@ from openpilot.selfdrive.car.common.simple_kalman import KF1D, get_kalman_gain
from openpilot.selfdrive.car.common.numpy_fast import clip from openpilot.selfdrive.car.common.numpy_fast import clip
from openpilot.selfdrive.car.values import PLATFORMS from openpilot.selfdrive.car.values import PLATFORMS
GearShifter = car.CarState.GearShifter GearShifter = structs.CarState.GearShifter
V_CRUISE_MAX = 145 V_CRUISE_MAX = 145
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
@ -29,7 +29,7 @@ TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'torque_data/params.toml')
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'torque_data/override.toml') TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'torque_data/override.toml')
TORQUE_SUBSTITUTE_PATH = os.path.join(BASEDIR, 'torque_data/substitute.toml') TORQUE_SUBSTITUTE_PATH = os.path.join(BASEDIR, 'torque_data/substitute.toml')
GEAR_SHIFTER_MAP: dict[str, car.CarState.GearShifter] = { GEAR_SHIFTER_MAP: dict[str, GearShifter] = {
'P': GearShifter.park, 'PARK': GearShifter.park, 'P': GearShifter.park, 'PARK': GearShifter.park,
'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse, 'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse,
'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral, 'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral,
@ -49,7 +49,7 @@ class LatControlInputs(NamedTuple):
aego: float aego: float
TorqueFromLateralAccelCallbackType = Callable[[LatControlInputs, car.CarParams.LateralTorqueTuning, float, float, bool, bool], float] TorqueFromLateralAccelCallbackType = Callable[[LatControlInputs, structs.CarParams.LateralTorqueTuning, float, float, bool, bool], float]
@cache @cache
@ -84,7 +84,7 @@ def get_torque_params():
# generic car and radar interfaces # generic car and radar interfaces
class CarInterfaceBase(ABC): class CarInterfaceBase(ABC):
def __init__(self, CP: car.CarParams, CarController, CarState): def __init__(self, CP: structs.CarParams, CarController, CarState):
self.CP = CP self.CP = CP
self.frame = 0 self.frame = 0
@ -101,7 +101,7 @@ class CarInterfaceBase(ABC):
dbc_name = "" if self.cp is None else self.cp.dbc_name dbc_name = "" if self.cp is None else self.cp.dbc_name
self.CC: CarControllerBase = CarController(dbc_name, CP) self.CC: CarControllerBase = CarController(dbc_name, CP)
def apply(self, c: car.CarControl, now_nanos: int) -> tuple[car.CarControl.Actuators, list[CanData]]: def apply(self, c: structs.CarControl, now_nanos: int) -> tuple[structs.CarControl.Actuators, list[CanData]]:
return self.CC.update(c, self.CS, now_nanos) return self.CC.update(c, self.CS, now_nanos)
@staticmethod @staticmethod
@ -109,14 +109,15 @@ class CarInterfaceBase(ABC):
return ACCEL_MIN, ACCEL_MAX return ACCEL_MIN, ACCEL_MAX
@classmethod @classmethod
def get_non_essential_params(cls, candidate: str) -> car.CarParams: def get_non_essential_params(cls, candidate: str) -> structs.CarParams:
""" """
Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints. Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints.
""" """
return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, False) return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, False)
@classmethod @classmethod
def get_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): def get_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw],
experimental_long: bool, docs: bool) -> structs.CarParams:
ret = CarInterfaceBase.get_std_params(candidate) ret = CarInterfaceBase.get_std_params(candidate)
platform = PLATFORMS[candidate] platform = PLATFORMS[candidate]
@ -143,12 +144,12 @@ class CarInterfaceBase(ABC):
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def _get_params(ret: car.CarParams, candidate, fingerprint: dict[int, dict[int, int]], def _get_params(ret: structs.CarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool) -> car.CarParams: car_fw: list[structs.CarParams.CarFw], experimental_long: bool, docs: bool) -> structs.CarParams:
raise NotImplementedError raise NotImplementedError
@staticmethod @staticmethod
def init(CP: car.CarParams, can_recv: CanRecvCallable, can_send: CanSendCallable): def init(CP: structs.CarParams, can_recv: CanRecvCallable, can_send: CanSendCallable):
pass pass
@staticmethod @staticmethod
@ -159,7 +160,7 @@ class CarInterfaceBase(ABC):
def get_steer_feedforward_function(self): def get_steer_feedforward_function(self):
return self.get_steer_feedforward_default return self.get_steer_feedforward_default
def torque_from_lateral_accel_linear(self, latcontrol_inputs: LatControlInputs, torque_params: car.CarParams.LateralTorqueTuning, def torque_from_lateral_accel_linear(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float: lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation) friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
@ -170,8 +171,8 @@ class CarInterfaceBase(ABC):
# returns a set of default params to avoid repetition in car specific params # returns a set of default params to avoid repetition in car specific params
@staticmethod @staticmethod
def get_std_params(candidate): def get_std_params(candidate: str) -> structs.CarParams:
ret = car.CarParams.new_message() ret = structs.CarParams()
ret.carFingerprint = candidate ret.carFingerprint = candidate
# Car docs fields # Car docs fields
@ -180,7 +181,7 @@ class CarInterfaceBase(ABC):
# standard ALC params # standard ALC params
ret.tireStiffnessFactor = 1.0 ret.tireStiffnessFactor = 1.0
ret.steerControlType = car.CarParams.SteerControlType.torque ret.steerControlType = structs.CarParams.SteerControlType.torque
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
ret.wheelSpeedFactor = 1.0 ret.wheelSpeedFactor = 1.0
@ -204,7 +205,7 @@ class CarInterfaceBase(ABC):
return ret return ret
@staticmethod @staticmethod
def configure_torque_tune(candidate: str, tune: car.CarParams.LateralTuning, steering_angle_deadzone_deg: float = 0.0, use_steering_angle: bool = True): def configure_torque_tune(candidate: str, tune: structs.CarParams.LateralTuning, steering_angle_deadzone_deg: float = 0.0, use_steering_angle: bool = True):
params = get_torque_params()[candidate] params = get_torque_params()[candidate]
tune.init('torque') tune.init('torque')
@ -217,10 +218,10 @@ class CarInterfaceBase(ABC):
tune.torque.latAccelOffset = 0.0 tune.torque.latAccelOffset = 0.0
tune.torque.steeringAngleDeadzoneDeg = steering_angle_deadzone_deg tune.torque.steeringAngleDeadzoneDeg = steering_angle_deadzone_deg
def _update(self) -> car.CarState: def _update(self) -> structs.CarState:
return self.CS.update(*self.can_parsers) return self.CS.update(*self.can_parsers)
def update(self, can_packets: list[tuple[int, list[CanData]]]) -> car.CarState: def update(self, can_packets: list[tuple[int, list[CanData]]]) -> structs.CarState:
# parse can # parse can
for cp in self.can_parsers: for cp in self.can_parsers:
if cp is not None: if cp is not None:
@ -245,33 +246,33 @@ class CarInterfaceBase(ABC):
if ret.cruiseState.speedCluster == 0: if ret.cruiseState.speedCluster == 0:
ret.cruiseState.speedCluster = ret.cruiseState.speed ret.cruiseState.speedCluster = ret.cruiseState.speed
# copy back for next iteration # save for next iteration
self.CS.out = ret.as_reader() self.CS.out = ret
return ret return ret
class RadarInterfaceBase(ABC): class RadarInterfaceBase(ABC):
def __init__(self, CP: car.CarParams): def __init__(self, CP: structs.CarParams):
self.CP = CP self.CP = CP
self.rcp = None self.rcp = None
self.pts: dict[int, car.RadarData.RadarPoint] = {} self.pts: dict[int, structs.RadarData.RadarPoint] = {}
self.delay = 0 self.delay = 0
self.radar_ts = CP.radarTimeStep self.radar_ts = CP.radarTimeStep
self.frame = 0 self.frame = 0
def update(self, can_strings): def update(self, can_strings) -> structs.RadarData | None:
self.frame += 1 self.frame += 1
if (self.frame % int(100 * self.radar_ts)) == 0: if (self.frame % int(100 * self.radar_ts)) == 0:
return car.RadarData.new_message() return structs.RadarData()
return None return None
class CarStateBase(ABC): class CarStateBase(ABC):
def __init__(self, CP: car.CarParams): def __init__(self, CP: structs.CarParams):
self.CP = CP self.CP = CP
self.car_fingerprint = CP.carFingerprint self.car_fingerprint = CP.carFingerprint
self.out = car.CarState.new_message() self.out = structs.CarState()
self.cruise_buttons = 0 self.cruise_buttons = 0
self.left_blinker_cnt = 0 self.left_blinker_cnt = 0
@ -291,7 +292,7 @@ class CarStateBase(ABC):
self.v_ego_kf = KF1D(x0=x0, A=A, C=C[0], K=K) self.v_ego_kf = KF1D(x0=x0, A=A, C=C[0], K=K)
@abstractmethod @abstractmethod
def update(self, cp, cp_cam, cp_adas, cp_body, cp_loopback) -> car.CarState: def update(self, cp, cp_cam, cp_adas, cp_body, cp_loopback) -> structs.CarState:
pass pass
def update_speed_kf(self, v_ego_raw): def update_speed_kf(self, v_ego_raw):
@ -304,7 +305,7 @@ class CarStateBase(ABC):
def get_wheel_speeds(self, fl, fr, rl, rr, unit=CV.KPH_TO_MS): def get_wheel_speeds(self, fl, fr, rl, rr, unit=CV.KPH_TO_MS):
factor = unit * self.CP.wheelSpeedFactor factor = unit * self.CP.wheelSpeedFactor
wheelSpeeds = car.CarState.WheelSpeeds.new_message() wheelSpeeds = structs.CarState.WheelSpeeds()
wheelSpeeds.fl = fl * factor wheelSpeeds.fl = fl * factor
wheelSpeeds.fr = fr * factor wheelSpeeds.fr = fr * factor
wheelSpeeds.rl = rl * factor wheelSpeeds.rl = rl * factor
@ -349,7 +350,7 @@ class CarStateBase(ABC):
return bool(left_blinker_stalk or self.left_blinker_cnt > 0), bool(right_blinker_stalk or self.right_blinker_cnt > 0) return bool(left_blinker_stalk or self.left_blinker_cnt > 0), bool(right_blinker_stalk or self.right_blinker_cnt > 0)
@staticmethod @staticmethod
def parse_gear_shifter(gear: str | None) -> car.CarState.GearShifter: def parse_gear_shifter(gear: str | None) -> GearShifter:
if gear is None: if gear is None:
return GearShifter.unknown return GearShifter.unknown
return GEAR_SHIFTER_MAP.get(gear.upper(), GearShifter.unknown) return GEAR_SHIFTER_MAP.get(gear.upper(), GearShifter.unknown)
@ -376,12 +377,12 @@ class CarStateBase(ABC):
class CarControllerBase(ABC): class CarControllerBase(ABC):
def __init__(self, dbc_name: str, CP: car.CarParams): def __init__(self, dbc_name: str, CP: structs.CarParams):
self.CP = CP self.CP = CP
self.frame = 0 self.frame = 0
@abstractmethod @abstractmethod
def update(self, CC: car.CarControl, CS: CarStateBase, now_nanos: int) -> tuple[car.CarControl.Actuators, list[CanData]]: def update(self, CC: structs.CarControl, CS: CarStateBase, now_nanos: int) -> tuple[structs.CarControl.Actuators, list[CanData]]:
pass pass

@ -1,11 +1,11 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits from openpilot.selfdrive.car import apply_driver_steer_torque_limits, structs
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.mazda import mazdacan from openpilot.selfdrive.car.mazda import mazdacan
from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
class CarController(CarControllerBase): class CarController(CarControllerBase):
@ -57,7 +57,7 @@ class CarController(CarControllerBase):
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP, can_sends.append(mazdacan.create_steering_control(self.packer, self.CP,
self.frame, apply_steer, CS.cam_lkas)) self.frame, apply_steer, CS.cam_lkas))
new_actuators = CC.actuators.as_builder() new_actuators = copy.copy(CC.actuators)
new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer

@ -1,12 +1,11 @@
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase): class CarState(CarStateBase):
@ -24,9 +23,9 @@ class CarState(CarStateBase):
self.distance_button = 0 self.distance_button = 0
def update(self, cp, cp_cam, *_): def update(self, cp, cp_cam, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
prev_distance_button = self.distance_button prev_distance_button = self.distance_button
self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"] self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"]

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.mazda.values import CAR from openpilot.selfdrive.car.mazda.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FW_VERSIONS = { FW_VERSIONS = {
CAR.MAZDA_CX5_2022: { CAR.MAZDA_CX5_2022: {

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.mazda.values import CAR, LKAS_LIMITS from openpilot.selfdrive.car.mazda.values import CAR, LKAS_LIMITS
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
@ -10,9 +9,9 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "mazda" ret.carName = "mazda"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.mazda)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.mazda)]
ret.radarUnavailable = True ret.radarUnavailable = True
ret.dashcamOnly = candidate not in (CAR.MAZDA_CX5_2022, CAR.MAZDA_CX9_2021) ret.dashcamOnly = candidate not in (CAR.MAZDA_CX5_2022, CAR.MAZDA_CX9_2021)

@ -1,13 +1,13 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import IntFlag from enum import IntFlag
from cereal import car
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
# Steer torque limits # Steer torque limits

@ -1,5 +1,6 @@
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
class CarController(CarControllerBase): class CarController(CarControllerBase):
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
return CC.actuators.as_builder(), [] return CC.actuators, []

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
class CarState(CarStateBase): class CarState(CarStateBase):
def update(self, *_) -> car.CarState: def update(self, *_) -> structs.CarState:
return car.CarState.new_message() return structs.CarState()

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
@ -6,7 +7,7 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "mock" ret.carName = "mock"
ret.mass = 1700. ret.mass = 1700.
ret.wheelbase = 2.70 ret.wheelbase = 2.70

@ -1,11 +1,11 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_std_steer_angle_limits from openpilot.selfdrive.car import apply_std_steer_angle_limits, structs
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.nissan import nissancan from openpilot.selfdrive.car.nissan import nissancan
from openpilot.selfdrive.car.nissan.values import CAR, CarControllerParams from openpilot.selfdrive.car.nissan.values import CAR, CarControllerParams
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
class CarController(CarControllerBase): class CarController(CarControllerBase):
@ -74,7 +74,7 @@ class CarController(CarControllerBase):
self.packer, CS.lkas_hud_info_msg, steer_hud_alert self.packer, CS.lkas_hud_info_msg, steer_hud_alert
)) ))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steeringAngleDeg = apply_angle new_actuators.steeringAngleDeg = apply_angle
self.frame += 1 self.frame += 1

@ -1,14 +1,13 @@
import copy import copy
from collections import deque from collections import deque
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import create_button_events from openpilot.selfdrive.car import create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.nissan.values import CAR, DBC, CarControllerParams from openpilot.selfdrive.car.nissan.values import CAR, DBC, CarControllerParams
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
TORQUE_SAMPLES = 12 TORQUE_SAMPLES = 12
@ -26,8 +25,8 @@ class CarState(CarStateBase):
self.distance_button = 0 self.distance_button = 0
def update(self, cp, cp_cam, cp_adas, *_): def update(self, cp, cp_cam, cp_adas, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
prev_distance_button = self.distance_button prev_distance_button = self.distance_button
self.distance_button = cp.vl["CRUISE_THROTTLE"]["FOLLOW_DISTANCE_BUTTON"] self.distance_button = cp.vl["CRUISE_THROTTLE"]["FOLLOW_DISTANCE_BUTTON"]

@ -1,8 +1,8 @@
# ruff: noqa: E501 # ruff: noqa: E501
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.nissan.values import CAR from openpilot.selfdrive.car.nissan.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FINGERPRINTS = { FINGERPRINTS = {
CAR.NISSAN_XTRAIL: [{ CAR.NISSAN_XTRAIL: [{

@ -1,6 +1,5 @@
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.nissan.values import CAR from openpilot.selfdrive.car.nissan.values import CAR
@ -8,16 +7,16 @@ from openpilot.selfdrive.car.nissan.values import CAR
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "nissan" ret.carName = "nissan"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.nissan)]
ret.autoResumeSng = False ret.autoResumeSng = False
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.radarUnavailable = True ret.radarUnavailable = True
if candidate == CAR.NISSAN_ALTIMA: if candidate == CAR.NISSAN_ALTIMA:

@ -1,12 +1,12 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarDocs, CarHarness, CarParts from openpilot.selfdrive.car.docs_definitions import CarDocs, CarHarness, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class CarControllerParams: class CarControllerParams:

@ -0,0 +1,499 @@
from dataclasses import dataclass as _dataclass, field, is_dataclass
from enum import Enum, StrEnum as _StrEnum, auto
from typing import dataclass_transform, get_origin
AUTO_OBJ = object()
def auto_field():
return AUTO_OBJ
@dataclass_transform()
def auto_dataclass(cls=None, /, **kwargs):
cls_annotations = cls.__dict__.get('__annotations__', {})
for name, typ in cls_annotations.items():
current_value = getattr(cls, name)
if current_value is AUTO_OBJ:
origin_typ = get_origin(typ) or typ
if isinstance(origin_typ, str):
raise TypeError(f"Forward references are not supported for auto_field: '{origin_typ}'. Use a default_factory with lambda instead.")
elif origin_typ in (int, float, str, bytes, list, tuple, bool) or is_dataclass(origin_typ):
setattr(cls, name, field(default_factory=origin_typ))
elif issubclass(origin_typ, Enum): # first enum is the default
setattr(cls, name, field(default=next(iter(origin_typ))))
else:
raise TypeError(f"Unsupported type for auto_field: {origin_typ}")
# TODO: use slots, this prevents accidentally setting attributes that don't exist
return _dataclass(cls, **kwargs)
class StrEnum(_StrEnum):
@staticmethod
def _generate_next_value_(name, *args):
# auto() defaults to name.lower()
return name
@auto_dataclass
class CarState:
# CAN health
canValid: bool = auto_field() # invalid counter/checksums
canTimeout: bool = auto_field() # CAN bus dropped out
canErrorCounter: int = auto_field()
# car speed
vEgo: float = auto_field() # best estimate of speed
aEgo: float = auto_field() # best estimate of acceleration
vEgoRaw: float = auto_field() # unfiltered speed from CAN sensors
vEgoCluster: float = auto_field() # best estimate of speed shown on car's instrument cluster, used for UI
yawRate: float = auto_field() # best estimate of yaw rate
standstill: bool = auto_field()
wheelSpeeds: 'CarState.WheelSpeeds' = field(default_factory=lambda: CarState.WheelSpeeds())
# gas pedal, 0.0-1.0
gas: float = auto_field() # this is user pedal only
gasPressed: bool = auto_field() # this is user pedal only
engineRpm: float = auto_field()
# brake pedal, 0.0-1.0
brake: float = auto_field() # this is user pedal only
brakePressed: bool = auto_field() # this is user pedal only
regenBraking: bool = auto_field() # this is user pedal only
parkingBrake: bool = auto_field()
brakeHoldActive: bool = auto_field()
# steering wheel
steeringAngleDeg: float = auto_field()
steeringAngleOffsetDeg: float = auto_field() # Offset betweens sensors in case there multiple
steeringRateDeg: float = auto_field()
steeringTorque: float = auto_field() # TODO: standardize units
steeringTorqueEps: float = auto_field() # TODO: standardize units
steeringPressed: bool = auto_field() # if the user is using the steering wheel
steerFaultTemporary: bool = auto_field() # temporary EPS fault
steerFaultPermanent: bool = auto_field() # permanent EPS fault
stockAeb: bool = auto_field()
stockFcw: bool = auto_field()
espDisabled: bool = auto_field()
accFaulted: bool = auto_field()
carFaultedNonCritical: bool = auto_field() # some ECU is faulted, but car remains controllable
espActive: bool = auto_field()
vehicleSensorsInvalid: bool = auto_field() # invalid steering angle readings, etc.
# cruise state
cruiseState: 'CarState.CruiseState' = field(default_factory=lambda: CarState.CruiseState())
# gear
gearShifter: 'CarState.GearShifter' = field(default_factory=lambda: CarState.GearShifter.unknown)
# button presses
buttonEvents: list['CarState.ButtonEvent'] = auto_field()
leftBlinker: bool = auto_field()
rightBlinker: bool = auto_field()
genericToggle: bool = auto_field()
# lock info
doorOpen: bool = auto_field()
seatbeltUnlatched: bool = auto_field()
# clutch (manual transmission only)
clutchPressed: bool = auto_field()
# blindspot sensors
leftBlindspot: bool = auto_field() # Is there something blocking the left lane change
rightBlindspot: bool = auto_field() # Is there something blocking the right lane change
fuelGauge: float = auto_field() # battery or fuel tank level from 0.0 to 1.0
charging: bool = auto_field()
# process meta
cumLagMs: float = auto_field()
@auto_dataclass
class WheelSpeeds:
# optional wheel speeds
fl: float = auto_field()
fr: float = auto_field()
rl: float = auto_field()
rr: float = auto_field()
@auto_dataclass
class CruiseState:
enabled: bool = auto_field()
speed: float = auto_field()
speedCluster: float = auto_field() # Set speed as shown on instrument cluster
available: bool = auto_field()
speedOffset: float = auto_field()
standstill: bool = auto_field()
nonAdaptive: bool = auto_field()
class GearShifter(StrEnum):
unknown = auto()
park = auto()
drive = auto()
neutral = auto()
reverse = auto()
sport = auto()
low = auto()
brake = auto()
eco = auto()
manumatic = auto()
# send on change
@auto_dataclass
class ButtonEvent:
pressed: bool = auto_field()
type: 'CarState.ButtonEvent.Type' = field(default_factory=lambda: CarState.ButtonEvent.Type.unknown)
class Type(StrEnum):
unknown = auto()
leftBlinker = auto()
rightBlinker = auto()
accelCruise = auto()
decelCruise = auto()
cancel = auto()
altButton1 = auto()
altButton2 = auto()
altButton3 = auto()
setCruise = auto()
resumeCruise = auto()
gapAdjustCruise = auto()
@auto_dataclass
class RadarData:
errors: list['Error'] = auto_field()
points: list['RadarPoint'] = auto_field()
class Error(StrEnum):
canError = auto()
fault = auto()
wrongConfig = auto()
@auto_dataclass
class RadarPoint:
trackId: int = auto_field() # no trackId reuse
# these 3 are the minimum required
dRel: float = auto_field() # m from the front bumper of the car
yRel: float = auto_field() # m
vRel: float = auto_field() # m/s
# these are optional and valid if they are not NaN
aRel: float = auto_field() # m/s^2
yvRel: float = auto_field() # m/s
# some radars flag measurements VS estimates
measured: bool = auto_field()
@auto_dataclass
class CarControl:
# must be true for any actuator commands to work
enabled: bool = auto_field()
latActive: bool = auto_field()
longActive: bool = auto_field()
# Actuator commands as computed by controlsd
actuators: 'CarControl.Actuators' = field(default_factory=lambda: CarControl.Actuators())
leftBlinker: bool = auto_field()
rightBlinker: bool = auto_field()
orientationNED: list[float] = auto_field()
angularVelocity: list[float] = auto_field()
cruiseControl: 'CarControl.CruiseControl' = field(default_factory=lambda: CarControl.CruiseControl())
hudControl: 'CarControl.HUDControl' = field(default_factory=lambda: CarControl.HUDControl())
@auto_dataclass
class Actuators:
# range from 0.0 - 1.0
gas: float = auto_field()
brake: float = auto_field()
# range from -1.0 - 1.0
steer: float = auto_field()
# value sent over can to the car
steerOutputCan: float = auto_field()
steeringAngleDeg: float = auto_field()
curvature: float = auto_field()
speed: float = auto_field() # m/s
accel: float = auto_field() # m/s^2
longControlState: 'CarControl.Actuators.LongControlState' = field(default_factory=lambda: CarControl.Actuators.LongControlState.off)
class LongControlState(StrEnum):
off = auto()
pid = auto()
stopping = auto()
starting = auto()
@auto_dataclass
class CruiseControl:
cancel: bool = auto_field()
resume: bool = auto_field()
override: bool = auto_field()
@auto_dataclass
class HUDControl:
speedVisible: bool = auto_field()
setSpeed: float = auto_field()
lanesVisible: bool = auto_field()
leadVisible: bool = auto_field()
visualAlert: 'CarControl.HUDControl.VisualAlert' = field(default_factory=lambda: CarControl.HUDControl.VisualAlert.none)
audibleAlert: 'CarControl.HUDControl.AudibleAlert' = field(default_factory=lambda: CarControl.HUDControl.AudibleAlert.none)
rightLaneVisible: bool = auto_field()
leftLaneVisible: bool = auto_field()
rightLaneDepart: bool = auto_field()
leftLaneDepart: bool = auto_field()
leadDistanceBars: int = auto_field() # 1-3: 1 is closest, 3 is farthest. some ports may utilize 2-4 bars instead
class VisualAlert(StrEnum):
# these are the choices from the Honda
# map as good as you can for your car
none = auto()
fcw = auto()
steerRequired = auto()
brakePressed = auto()
wrongGear = auto()
seatbeltUnbuckled = auto()
speedTooHigh = auto()
ldw = auto()
class AudibleAlert(StrEnum):
none = auto()
engage = auto()
disengage = auto()
refuse = auto()
warningSoft = auto()
warningImmediate = auto()
prompt = auto()
promptRepeat = auto()
promptDistracted = auto()
@auto_dataclass
class CarParams:
carName: str = auto_field()
carFingerprint: str = auto_field()
fuzzyFingerprint: bool = auto_field()
notCar: bool = auto_field() # flag for non-car robotics platforms
pcmCruise: bool = auto_field() # is openpilot's state tied to the PCM's cruise state?
enableDsu: bool = auto_field() # driving support unit
enableBsm: bool = auto_field() # blind spot monitoring
flags: int = auto_field() # flags for car specific quirks
experimentalLongitudinalAvailable: bool = auto_field()
minEnableSpeed: float = auto_field()
minSteerSpeed: float = auto_field()
safetyConfigs: list['CarParams.SafetyConfig'] = auto_field()
alternativeExperience: int = auto_field() # panda flag for features like no disengage on gas
maxLateralAccel: float = auto_field()
autoResumeSng: bool = auto_field() # describes whether car can resume from a stop automatically
mass: float = auto_field() # [kg] curb weight: all fluids no cargo
wheelbase: float = auto_field() # [m] distance from rear axle to front axle
centerToFront: float = auto_field() # [m] distance from center of mass to front axle
steerRatio: float = auto_field() # [] ratio of steering wheel angle to front wheel angle
steerRatioRear: float = auto_field() # [] ratio of steering wheel angle to rear wheel angle (usually 0)
rotationalInertia: float = auto_field() # [kg*m2] body rotational inertia
tireStiffnessFactor: float = auto_field() # scaling factor used in calculating tireStiffness[Front,Rear]
tireStiffnessFront: float = auto_field() # [N/rad] front tire coeff of stiff
tireStiffnessRear: float = auto_field() # [N/rad] rear tire coeff of stiff
longitudinalTuning: 'CarParams.LongitudinalPIDTuning' = field(default_factory=lambda: CarParams.LongitudinalPIDTuning())
lateralParams: 'CarParams.LateralParams' = field(default_factory=lambda: CarParams.LateralParams())
lateralTuning: 'CarParams.LateralTuning' = field(default_factory=lambda: CarParams.LateralTuning())
@auto_dataclass
class LateralTuning:
def init(self, which: str):
self.which = CarParams.LateralTuning.Which(which)
class Which(StrEnum):
pid = auto()
torque = auto()
def __call__(self):
return self.value
which: 'CarParams.LateralTuning.Which' = field(default_factory=lambda: CarParams.LateralTuning.Which.pid)
pid: 'CarParams.LateralPIDTuning' = field(default_factory=lambda: CarParams.LateralPIDTuning())
torque: 'CarParams.LateralTorqueTuning' = field(default_factory=lambda: CarParams.LateralTorqueTuning())
@auto_dataclass
class SafetyConfig:
safetyModel: 'CarParams.SafetyModel' = field(default_factory=lambda: CarParams.SafetyModel.silent)
safetyParam: int = auto_field()
@auto_dataclass
class LateralParams:
torqueBP: list[int] = auto_field()
torqueV: list[int] = auto_field()
@auto_dataclass
class LateralPIDTuning:
kpBP: list[float] = auto_field()
kpV: list[float] = auto_field()
kiBP: list[float] = auto_field()
kiV: list[float] = auto_field()
kf: float = auto_field()
@auto_dataclass
class LateralTorqueTuning:
useSteeringAngle: bool = auto_field()
kp: float = auto_field()
ki: float = auto_field()
friction: float = auto_field()
kf: float = auto_field()
steeringAngleDeadzoneDeg: float = auto_field()
latAccelFactor: float = auto_field()
latAccelOffset: float = auto_field()
steerLimitAlert: bool = auto_field()
steerLimitTimer: float = auto_field() # time before steerLimitAlert is issued
vEgoStopping: float = auto_field() # Speed at which the car goes into stopping state
vEgoStarting: float = auto_field() # Speed at which the car goes into starting state
stoppingControl: bool = auto_field() # Does the car allow full control even at lows speeds when stopping
steerControlType: 'CarParams.SteerControlType' = field(default_factory=lambda: CarParams.SteerControlType.torque)
radarUnavailable: bool = auto_field() # True when radar objects aren't visible on CAN or aren't parsed out
stopAccel: float = auto_field() # Required acceleration to keep vehicle stationary
stoppingDecelRate: float = auto_field() # m/s^2/s while trying to stop
startAccel: float = auto_field() # Required acceleration to get car moving
startingState: bool = auto_field() # Does this car make use of special starting state
steerActuatorDelay: float = auto_field() # Steering wheel actuator delay in seconds
longitudinalActuatorDelay: float = auto_field() # Gas/Brake actuator delay in seconds
openpilotLongitudinalControl: bool = auto_field() # is openpilot doing the longitudinal control?
carVin: str = auto_field() # VIN number queried during fingerprinting
dashcamOnly: bool = auto_field()
passive: bool = auto_field() # is openpilot in control?
transmissionType: 'CarParams.TransmissionType' = field(default_factory=lambda: CarParams.TransmissionType.unknown)
carFw: list['CarParams.CarFw'] = auto_field()
radarTimeStep: float = 0.05 # time delta between radar updates, 20Hz is very standard
fingerprintSource: 'CarParams.FingerprintSource' = field(default_factory=lambda: CarParams.FingerprintSource.can)
# Where Panda/C2 is integrated into the car's CAN network
networkLocation: 'CarParams.NetworkLocation' = field(default_factory=lambda: CarParams.NetworkLocation.fwdCamera)
wheelSpeedFactor: float = auto_field() # Multiplier on wheels speeds to computer actual speeds
@auto_dataclass
class LongitudinalPIDTuning:
kpBP: list[float] = auto_field()
kpV: list[float] = auto_field()
kiBP: list[float] = auto_field()
kiV: list[float] = auto_field()
kf: float = auto_field()
class SafetyModel(StrEnum):
silent = auto()
hondaNidec = auto()
toyota = auto()
elm327 = auto()
gm = auto()
hondaBoschGiraffe = auto()
ford = auto()
cadillac = auto()
hyundai = auto()
chrysler = auto()
tesla = auto()
subaru = auto()
gmPassive = auto()
mazda = auto()
nissan = auto()
volkswagen = auto()
toyotaIpas = auto()
allOutput = auto()
gmAscm = auto()
noOutput = auto() # like silent but without silent CAN TXs
hondaBosch = auto()
volkswagenPq = auto()
subaruPreglobal = auto() # pre-Global platform
hyundaiLegacy = auto()
hyundaiCommunity = auto()
volkswagenMlb = auto()
hongqi = auto()
body = auto()
hyundaiCanfd = auto()
volkswagenMqbEvo = auto()
chryslerCusw = auto()
psa = auto()
class SteerControlType(StrEnum):
torque = auto()
angle = auto()
class TransmissionType(StrEnum):
unknown = auto()
automatic = auto() # Traditional auto, including DSG
manual = auto() # True "stick shift" only
direct = auto() # Electric vehicle or other direct drive
cvt = auto()
@auto_dataclass
class CarFw:
ecu: 'CarParams.Ecu' = field(default_factory=lambda: CarParams.Ecu.unknown)
fwVersion: bytes = auto_field()
address: int = auto_field()
subAddress: int = auto_field()
responseAddress: int = auto_field()
request: list[bytes] = auto_field()
brand: str = auto_field()
bus: int = auto_field()
logging: bool = auto_field()
obdMultiplexing: bool = auto_field()
class Ecu(StrEnum):
eps = auto()
abs = auto()
fwdRadar = auto()
fwdCamera = auto()
engine = auto()
unknown = auto()
transmission = auto() # Transmission Control Module
hybrid = auto() # hybrid control unit, e.g. Chrysler's HCP, Honda's IMA Control Unit, Toyota's hybrid control computer
srs = auto() # airbag
gateway = auto() # can gateway
hud = auto() # heads up display
combinationMeter = auto() # instrument cluster
electricBrakeBooster = auto()
shiftByWire = auto()
adas = auto()
cornerRadar = auto()
hvac = auto()
parkingAdas = auto() # parking assist system ECU, e.g. Toyota's IPAS, Hyundai's RSPA, etc.
epb = auto() # electronic parking brake
telematics = auto()
body = auto() # body control module
# Toyota only
dsu = auto()
# Honda only
vsa = auto() # Vehicle Stability Assist
programmedFuelInjection = auto()
debug = auto()
class FingerprintSource(StrEnum):
can = auto()
fw = auto()
fixed = auto()
class NetworkLocation(StrEnum):
fwdCamera = auto() # Standard/default integration at LKAS camera
gateway = auto() # Integration at vehicle's CAN gateway

@ -1,3 +1,4 @@
import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg
from openpilot.selfdrive.car.common.numpy_fast import clip, interp from openpilot.selfdrive.car.common.numpy_fast import clip, interp
@ -135,7 +136,7 @@ class CarController(CarControllerBase):
if self.frame % 2 == 0: if self.frame % 2 == 0:
can_sends.append(subarucan.create_es_static_2(self.packer)) can_sends.append(subarucan.create_es_static_2(self.packer))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -1,7 +1,7 @@
import copy import copy
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.subaru.values import DBC, CanBus, SubaruFlags from openpilot.selfdrive.car.subaru.values import DBC, CanBus, SubaruFlags
@ -16,8 +16,8 @@ class CarState(CarStateBase):
self.angle_rate_calulator = CanSignalRateCalculator(50) self.angle_rate_calulator = CanSignalRateCalculator(50)
def update(self, cp, cp_cam, _, cp_body, __): def update(self, cp, cp_cam, _, cp_body, __) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
throttle_msg = cp.vl["Throttle"] if not (self.CP.flags & SubaruFlags.HYBRID) else cp_body.vl["Throttle_Hybrid"] throttle_msg = cp.vl["Throttle"] if not (self.CP.flags & SubaruFlags.HYBRID) else cp_body.vl["Throttle_Hybrid"]
ret.gas = throttle_msg["Throttle_Pedal"] / 255. ret.gas = throttle_msg["Throttle_Pedal"] / 255.

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.subaru.values import CAR from openpilot.selfdrive.car.subaru.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FW_VERSIONS = { FW_VERSIONS = {
CAR.SUBARU_ASCENT: { CAR.SUBARU_ASCENT: {

@ -1,6 +1,5 @@
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags
@ -9,7 +8,7 @@ from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFla
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "subaru" ret.carName = "subaru"
ret.radarUnavailable = True ret.radarUnavailable = True
# for HYBRID CARS to be upstreamed, we need: # for HYBRID CARS to be upstreamed, we need:
@ -25,10 +24,10 @@ class CarInterface(CarInterfaceBase):
if ret.flags & SubaruFlags.PREGLOBAL: if ret.flags & SubaruFlags.PREGLOBAL:
ret.enableBsm = 0x25c in fingerprint[0] ret.enableBsm = 0x25c in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.subaruPreglobal)]
else: else:
ret.enableBsm = 0x228 in fingerprint[0] ret.enableBsm = 0x228 in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.subaru)]
if ret.flags & SubaruFlags.GLOBAL_GEN2: if ret.flags & SubaruFlags.GLOBAL_GEN2:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2 ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
@ -36,7 +35,7 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
if ret.flags & SubaruFlags.LKAS_ANGLE: if ret.flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = structs.CarParams.SteerControlType.angle
else: else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.subaru.values import CanBus from openpilot.selfdrive.car.subaru.values import CanBus
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
def create_steering_control(packer, apply_steer, steer_req): def create_steering_control(packer, apply_steer, steer_req):

@ -1,13 +1,13 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, IntFlag from enum import Enum, IntFlag
from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Tool, Column from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Tool, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
class CarControllerParams: class CarControllerParams:
@ -93,7 +93,7 @@ class SubaruCarDocs(CarDocs):
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a])) car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
footnotes: list[Enum] = field(default_factory=lambda: [Footnote.GLOBAL]) footnotes: list[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
def init_make(self, CP: car.CarParams): def init_make(self, CP: CarParams):
self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool]) self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool])
if CP.experimentalLongitudinalAvailable: if CP.experimentalLongitudinalAvailable:

@ -7,7 +7,9 @@ from parameterized import parameterized
from cereal import car, messaging from cereal import car, messaging
from openpilot.selfdrive.car import DT_CTRL, gen_empty_fingerprint from openpilot.selfdrive.car import DT_CTRL, gen_empty_fingerprint
from openpilot.selfdrive.car.card import convert_carControl, convert_to_capnp
from openpilot.selfdrive.car.car_helpers import interfaces from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fingerprints import all_known_cars from openpilot.selfdrive.car.fingerprints import all_known_cars
from openpilot.selfdrive.car.fw_versions import FW_VERSIONS, FW_QUERY_CONFIGS from openpilot.selfdrive.car.fw_versions import FW_VERSIONS, FW_QUERY_CONFIGS
from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
@ -43,8 +45,8 @@ def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
}) })
params: dict = draw(params_strategy) params: dict = draw(params_strategy)
params['car_fw'] = [car.CarParams.CarFw(ecu=fw[0], address=fw[1], subAddress=fw[2] or 0, params['car_fw'] = [CarParams.CarFw(ecu=fw[0], address=fw[1], subAddress=fw[2] or 0,
request=draw(st.sampled_from(sorted(ALL_REQUESTS)))) request=draw(st.sampled_from(sorted(ALL_REQUESTS))))
for fw in params['car_fw']] for fw in params['car_fw']]
return params return params
@ -63,7 +65,6 @@ class TestCarInterfaces:
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'], car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False) experimental_long=args['experimental_long'], docs=False)
car_params = car_params.as_reader()
car_interface = CarInterface(car_params, CarController, CarState) car_interface = CarInterface(car_params, CarController, CarState)
assert car_params assert car_params
assert car_interface assert car_interface
@ -79,7 +80,7 @@ class TestCarInterfaces:
assert len(car_params.longitudinalTuning.kiV) == len(car_params.longitudinalTuning.kiBP) assert len(car_params.longitudinalTuning.kiV) == len(car_params.longitudinalTuning.kiBP)
# Lateral sanity checks # Lateral sanity checks
if car_params.steerControlType != car.CarParams.SteerControlType.angle: if car_params.steerControlType != CarParams.SteerControlType.angle:
tune = car_params.lateralTuning tune = car_params.lateralTuning
if tune.which() == 'pid': if tune.which() == 'pid':
if car_name != MOCK.MOCK: if car_name != MOCK.MOCK:
@ -95,28 +96,31 @@ class TestCarInterfaces:
# Run car interface # Run car interface
now_nanos = 0 now_nanos = 0
CC = car.CarControl.new_message(**cc_msg) CC = car.CarControl.new_message(**cc_msg)
CC = convert_carControl(CC.as_reader())
for _ in range(10): for _ in range(10):
car_interface.update([]) car_interface.update([])
car_interface.apply(CC.as_reader(), now_nanos) car_interface.apply(CC, now_nanos)
now_nanos += DT_CTRL * 1e9 # 10 ms now_nanos += DT_CTRL * 1e9 # 10 ms
CC = car.CarControl.new_message(**cc_msg) CC = car.CarControl.new_message(**cc_msg)
CC.enabled = True CC.enabled = True
CC = convert_carControl(CC.as_reader())
for _ in range(10): for _ in range(10):
car_interface.update([]) car_interface.update([])
car_interface.apply(CC.as_reader(), now_nanos) car_interface.apply(CC, now_nanos)
now_nanos += DT_CTRL * 1e9 # 10ms now_nanos += DT_CTRL * 1e9 # 10ms
# Test controller initialization # Test controller initialization
# TODO: wait until card refactor is merged to run controller a few times, # TODO: wait until card refactor is merged to run controller a few times,
# hypothesis also slows down significantly with just one more message draw # hypothesis also slows down significantly with just one more message draw
LongControl(car_params) car_params_capnp = convert_to_capnp(car_params).as_reader()
if car_params.steerControlType == car.CarParams.SteerControlType.angle: LongControl(car_params_capnp)
LatControlAngle(car_params, car_interface) if car_params.steerControlType == CarParams.SteerControlType.angle:
LatControlAngle(car_params_capnp, car_interface)
elif car_params.lateralTuning.which() == 'pid': elif car_params.lateralTuning.which() == 'pid':
LatControlPID(car_params, car_interface) LatControlPID(car_params_capnp, car_interface)
elif car_params.lateralTuning.which() == 'torque': elif car_params.lateralTuning.which() == 'torque':
LatControlTorque(car_params, car_interface) LatControlTorque(car_params_capnp, car_interface)
# Test radar interface # Test radar interface
RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface

@ -4,18 +4,16 @@ import time
from collections import defaultdict from collections import defaultdict
from parameterized import parameterized from parameterized import parameterized
from cereal import car
from openpilot.selfdrive.car.can_definitions import CanData from openpilot.selfdrive.car.can_definitions import CanData
from openpilot.selfdrive.car.car_helpers import interfaces from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.fw_versions import ESSENTIAL_ECUS, FW_QUERY_CONFIGS, FUZZY_EXCLUDE_ECUS, VERSIONS, build_fw_dict, \ from openpilot.selfdrive.car.fw_versions import ESSENTIAL_ECUS, FW_QUERY_CONFIGS, FUZZY_EXCLUDE_ECUS, VERSIONS, build_fw_dict, \
match_fw_to_car, get_brand_ecu_matches, get_fw_versions, get_fw_versions_ordered, get_present_ecus match_fw_to_car, get_brand_ecu_matches, get_fw_versions, get_fw_versions_ordered, get_present_ecus
from openpilot.selfdrive.car.vin import get_vin from openpilot.selfdrive.car.vin import get_vin
CarFw = car.CarParams.CarFw CarFw = CarParams.CarFw
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
class TestFwFingerprint: class TestFwFingerprint:
@ -27,7 +25,7 @@ class TestFwFingerprint:
@parameterized.expand([(b, c, e[c], n) for b, e in VERSIONS.items() for c in e for n in (True, False)]) @parameterized.expand([(b, c, e[c], n) for b, e in VERSIONS.items() for c in e for n in (True, False)])
def test_exact_match(self, brand, car_model, ecus, test_non_essential): def test_exact_match(self, brand, car_model, ecus, test_non_essential):
config = FW_QUERY_CONFIGS[brand] config = FW_QUERY_CONFIGS[brand]
CP = car.CarParams.new_message() CP = CarParams()
for _ in range(100): for _ in range(100):
fw = [] fw = []
for ecu, fw_versions in ecus.items(): for ecu, fw_versions in ecus.items():
@ -37,8 +35,8 @@ class TestFwFingerprint:
continue continue
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand, fw.append(CarFw(ecu=ecu_name, fwVersion=random.choice(fw_versions), brand=brand,
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr}) address=addr, subAddress=0 if sub_addr is None else sub_addr))
CP.carFw = fw CP.carFw = fw
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_fuzzy=False) _, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_fuzzy=False)
if not test_non_essential: if not test_non_essential:
@ -55,13 +53,13 @@ class TestFwFingerprint:
if config.match_fw_to_car_fuzzy is None: if config.match_fw_to_car_fuzzy is None:
pytest.skip("Brand does not implement custom fuzzy fingerprinting function") pytest.skip("Brand does not implement custom fuzzy fingerprinting function")
CP = car.CarParams.new_message() CP = CarParams()
for _ in range(5): for _ in range(5):
fw = [] fw = []
for ecu, fw_versions in ecus.items(): for ecu, fw_versions in ecus.items():
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
fw.append({"ecu": ecu_name, "fwVersion": random.choice(fw_versions), 'brand': brand, fw.append(CarFw(ecu=ecu_name, fwVersion=random.choice(fw_versions), brand=brand,
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr}) address=addr, subAddress=0 if sub_addr is None else sub_addr))
CP.carFw = fw CP.carFw = fw
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False) _, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False)
brand_matches = config.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, VERSIONS[brand]) brand_matches = config.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, VERSIONS[brand])
@ -82,13 +80,13 @@ class TestFwFingerprint:
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
for _ in range(5): for _ in range(5):
# Add multiple FW versions to simulate ECU returning to multiple queries in a brand # Add multiple FW versions to simulate ECU returning to multiple queries in a brand
fw.append({"ecu": ecu_name, "fwVersion": random.choice(ecus[ecu]), 'brand': brand, fw.append(CarFw(ecu=ecu_name, fwVersion=random.choice(ecus[ecu]), brand=brand,
"address": addr, "subAddress": 0 if sub_addr is None else sub_addr}) address=addr, subAddress=0 if sub_addr is None else sub_addr))
CP = car.CarParams.new_message(carFw=fw) CP = CarParams(carFw=fw)
_, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False) _, matches = match_fw_to_car(CP.carFw, CP.carVin, allow_exact=False, log=False)
# Assert no match if there are not enough unique ECUs # Assert no match if there are not enough unique ECUs
unique_ecus = {(f['address'], f['subAddress']) for f in fw} unique_ecus = {(f.address, f.subAddress) for f in fw}
if len(unique_ecus) < 2: if len(unique_ecus) < 2:
assert len(matches) == 0, car_model assert len(matches) == 0, car_model
# There won't always be a match due to shared FW, but if there is it should be correct # There won't always be a match due to shared FW, but if there is it should be correct
@ -99,10 +97,10 @@ class TestFwFingerprint:
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
with subtests.test(car_model=car_model.value): with subtests.test(car_model=car_model.value):
for ecu, ecu_fw in ecus.items(): for ecu, ecu_fw in ecus.items():
with subtests.test(ecu): with subtests.test((ecu[0].value, ecu[1], ecu[2])):
duplicates = {fw for fw in ecu_fw if ecu_fw.count(fw) > 1} duplicates = {fw for fw in ecu_fw if ecu_fw.count(fw) > 1}
assert not len(duplicates), f'{car_model}: Duplicate FW versions: Ecu.{ECU_NAME[ecu[0]]}, {duplicates}' assert not len(duplicates), f'{car_model}: Duplicate FW versions: Ecu.{ecu[0]}, {duplicates}'
assert len(ecu_fw) > 0, f'{car_model}: No FW versions: Ecu.{ECU_NAME[ecu[0]]}' assert len(ecu_fw) > 0, f'{car_model}: No FW versions: Ecu.{ecu[0]}'
def test_all_addrs_map_to_one_ecu(self): def test_all_addrs_map_to_one_ecu(self):
for brand, cars in VERSIONS.items(): for brand, cars in VERSIONS.items():
@ -111,7 +109,7 @@ class TestFwFingerprint:
for ecu_type, addr, sub_addr in ecus.keys(): for ecu_type, addr, sub_addr in ecus.keys():
addr_to_ecu[(addr, sub_addr)].add(ecu_type) addr_to_ecu[(addr, sub_addr)].add(ecu_type)
ecus_for_addr = addr_to_ecu[(addr, sub_addr)] ecus_for_addr = addr_to_ecu[(addr, sub_addr)]
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_for_addr]) ecu_strings = ", ".join([f'Ecu.{ecu}' for ecu in ecus_for_addr])
assert len(ecus_for_addr) <= 1, f"{brand} has multiple ECUs that map to one address: {ecu_strings} -> ({hex(addr)}, {sub_addr})" assert len(ecus_for_addr) <= 1, f"{brand} has multiple ECUs that map to one address: {ecu_strings} -> ({hex(addr)}, {sub_addr})"
def test_data_collection_ecus(self, subtests): def test_data_collection_ecus(self, subtests):
@ -129,13 +127,13 @@ class TestFwFingerprint:
CP = interfaces[car_model][0].get_non_essential_params(car_model) CP = interfaces[car_model][0].get_non_essential_params(car_model)
if CP.carName == 'subaru': if CP.carName == 'subaru':
for ecu in ecus.keys(): for ecu in ecus.keys():
assert ecu[1] not in blacklisted_addrs, f'{car_model}: Blacklisted ecu: (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])})' assert ecu[1] not in blacklisted_addrs, f'{car_model}: Blacklisted ecu: (Ecu.{ecu[0]}, {hex(ecu[1])})'
elif CP.carName == "chrysler": elif CP.carName == "chrysler":
# Some HD trucks have a combined TCM and ECM # Some HD trucks have a combined TCM and ECM
if CP.carFingerprint.startswith("RAM_HD"): if CP.carFingerprint.startswith("RAM_HD"):
for ecu in ecus.keys(): for ecu in ecus.keys():
assert ecu[0] != Ecu.transmission, f"{car_model}: Blacklisted ecu: (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])})" assert ecu[0] != Ecu.transmission, f"{car_model}: Blacklisted ecu: (Ecu.{ecu[0]}, {hex(ecu[1])})"
def test_non_essential_ecus(self, subtests): def test_non_essential_ecus(self, subtests):
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():
@ -143,7 +141,7 @@ class TestFwFingerprint:
# These ECUs are already not in ESSENTIAL_ECUS which the fingerprint functions give a pass if missing # These ECUs are already not in ESSENTIAL_ECUS which the fingerprint functions give a pass if missing
unnecessary_non_essential_ecus = set(config.non_essential_ecus) - set(ESSENTIAL_ECUS) unnecessary_non_essential_ecus = set(config.non_essential_ecus) - set(ESSENTIAL_ECUS)
assert unnecessary_non_essential_ecus == set(), "Declaring non-essential ECUs non-essential is not required: " + \ assert unnecessary_non_essential_ecus == set(), "Declaring non-essential ECUs non-essential is not required: " + \
f"{', '.join([f'Ecu.{ECU_NAME[ecu]}' for ecu in unnecessary_non_essential_ecus])}" f"{', '.join([f'Ecu.{ecu}' for ecu in unnecessary_non_essential_ecus])}"
def test_missing_versions_and_configs(self, subtests): def test_missing_versions_and_configs(self, subtests):
brand_versions = set(VERSIONS.keys()) brand_versions = set(VERSIONS.keys())
@ -172,7 +170,7 @@ class TestFwFingerprint:
# each ecu in brand's fw versions + extra ecus needs to be whitelisted at least once # each ecu in brand's fw versions + extra ecus needs to be whitelisted at least once
ecus_not_whitelisted = brand_ecus - whitelisted_ecus ecus_not_whitelisted = brand_ecus - whitelisted_ecus
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_whitelisted]) ecu_strings = ", ".join([f'Ecu.{ecu}' for ecu in ecus_not_whitelisted])
assert not (len(whitelisted_ecus) and len(ecus_not_whitelisted)), \ assert not (len(whitelisted_ecus) and len(ecus_not_whitelisted)), \
f'{brand.title()}: ECUs not in any FW query whitelists: {ecu_strings}' f'{brand.title()}: ECUs not in any FW query whitelists: {ecu_strings}'

@ -1,4 +1,6 @@
import capnp import capnp
import copy
import dataclasses
import os import os
import importlib import importlib
import pytest import pytest
@ -13,12 +15,13 @@ from cereal import messaging, log, car
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.car import DT_CTRL, gen_empty_fingerprint from openpilot.selfdrive.car import DT_CTRL, gen_empty_fingerprint
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.fingerprints import all_known_cars, MIGRATION from openpilot.selfdrive.car.fingerprints import all_known_cars, MIGRATION
from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces
from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags
from openpilot.selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute from openpilot.selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute
from openpilot.selfdrive.car.values import Platform from openpilot.selfdrive.car.values import Platform
from openpilot.selfdrive.car.card import Car from openpilot.selfdrive.car.card import Car, convert_to_capnp
from openpilot.selfdrive.pandad import can_capnp_to_list from openpilot.selfdrive.pandad import can_capnp_to_list
from openpilot.selfdrive.test.helpers import read_segment_list from openpilot.selfdrive.test.helpers import read_segment_list
from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT
@ -181,7 +184,7 @@ class TestCarModelBase(unittest.TestCase):
del cls.can_msgs del cls.can_msgs
def setUp(self): def setUp(self):
self.CI = self.CarInterface(self.CP.copy(), self.CarController, self.CarState) self.CI = self.CarInterface(copy.deepcopy(self.CP), self.CarController, self.CarState)
assert self.CI assert self.CI
Params().put_bool("OpenpilotEnabledToggle", self.openpilot_enabled) Params().put_bool("OpenpilotEnabledToggle", self.openpilot_enabled)
@ -189,7 +192,7 @@ class TestCarModelBase(unittest.TestCase):
# TODO: check safetyModel is in release panda build # TODO: check safetyModel is in release panda build
self.safety = libpanda_py.libpanda self.safety = libpanda_py.libpanda
cfg = self.CP.safetyConfigs[-1] cfg = car.CarParams.SafetyConfig(**dataclasses.asdict(self.CP.safetyConfigs[-1]))
set_status = self.safety.set_safety_hooks(cfg.safetyModel.raw, cfg.safetyParam) set_status = self.safety.set_safety_hooks(cfg.safetyModel.raw, cfg.safetyParam)
self.assertEqual(0, set_status, f"failed to set safetyModel {cfg}") self.assertEqual(0, set_status, f"failed to set safetyModel {cfg}")
self.safety.init_tests() self.safety.init_tests()
@ -201,7 +204,7 @@ class TestCarModelBase(unittest.TestCase):
# make sure car params are within a valid range # make sure car params are within a valid range
self.assertGreater(self.CP.mass, 1) self.assertGreater(self.CP.mass, 1)
if self.CP.steerControlType != car.CarParams.SteerControlType.angle: if self.CP.steerControlType != structs.CarParams.SteerControlType.angle:
tuning = self.CP.lateralTuning.which() tuning = self.CP.lateralTuning.which()
if tuning == 'pid': if tuning == 'pid':
self.assertTrue(len(self.CP.lateralTuning.pid.kpV)) self.assertTrue(len(self.CP.lateralTuning.pid.kpV))
@ -214,7 +217,7 @@ class TestCarModelBase(unittest.TestCase):
# TODO: also check for checksum violations from can parser # TODO: also check for checksum violations from can parser
can_invalid_cnt = 0 can_invalid_cnt = 0
can_valid = False can_valid = False
CC = car.CarControl.new_message().as_reader() CC = structs.CarControl()
for i, msg in enumerate(self.can_msgs): for i, msg in enumerate(self.can_msgs):
CS = self.CI.update(can_capnp_to_list((msg.as_builder().to_bytes(),))) CS = self.CI.update(can_capnp_to_list((msg.as_builder().to_bytes(),)))
@ -238,9 +241,9 @@ class TestCarModelBase(unittest.TestCase):
# start parsing CAN messages after we've left ELM mode and can expect CAN traffic # start parsing CAN messages after we've left ELM mode and can expect CAN traffic
error_cnt = 0 error_cnt = 0
for i, msg in enumerate(self.can_msgs[self.elm_frame:]): for i, msg in enumerate(self.can_msgs[self.elm_frame:]):
rr = RI.update(can_capnp_to_list((msg.as_builder().to_bytes(),))) rr: structs.RadarData | None = RI.update(can_capnp_to_list((msg.as_builder().to_bytes(),)))
if rr is not None and i > 50: if rr is not None and i > 50:
error_cnt += car.RadarData.Error.canError in rr.errors error_cnt += structs.RadarData.Error.canError in rr.errors
self.assertEqual(error_cnt, 0) self.assertEqual(error_cnt, 0)
def test_panda_safety_rx_checks(self): def test_panda_safety_rx_checks(self):
@ -306,18 +309,18 @@ class TestCarModelBase(unittest.TestCase):
self.assertGreater(msgs_sent, 50) self.assertGreater(msgs_sent, 50)
# Make sure we can send all messages while inactive # Make sure we can send all messages while inactive
CC = car.CarControl.new_message() CC = structs.CarControl()
test_car_controller(CC.as_reader()) test_car_controller(CC)
# Test cancel + general messages (controls_allowed=False & cruise_engaged=True) # Test cancel + general messages (controls_allowed=False & cruise_engaged=True)
self.safety.set_cruise_engaged_prev(True) self.safety.set_cruise_engaged_prev(True)
CC = car.CarControl.new_message(cruiseControl={'cancel': True}) CC = structs.CarControl(cruiseControl=structs.CarControl.CruiseControl(cancel=True))
test_car_controller(CC.as_reader()) test_car_controller(CC)
# Test resume + general messages (controls_allowed=True & cruise_engaged=True) # Test resume + general messages (controls_allowed=True & cruise_engaged=True)
self.safety.set_controls_allowed(True) self.safety.set_controls_allowed(True)
CC = car.CarControl.new_message(cruiseControl={'resume': True}) CC = structs.CarControl(cruiseControl=structs.CarControl.CruiseControl(resume=True))
test_car_controller(CC.as_reader()) test_car_controller(CC)
# Skip stdout/stderr capture with pytest, causes elevated memory usage # Skip stdout/stderr capture with pytest, causes elevated memory usage
@pytest.mark.nocapture @pytest.mark.nocapture
@ -403,7 +406,7 @@ class TestCarModelBase(unittest.TestCase):
checks = defaultdict(int) checks = defaultdict(int)
card = Car(CI=self.CI) card = Car(CI=self.CI)
for idx, can in enumerate(self.can_msgs): for idx, can in enumerate(self.can_msgs):
CS = self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))) CS = convert_to_capnp(self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))))
for msg in filter(lambda m: m.src in range(64), can.can): for msg in filter(lambda m: m.src in range(64), can.can):
to_send = libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat) to_send = libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
ret = self.safety.safety_rx_hook(to_send) ret = self.safety.safety_rx_hook(to_send)

@ -1,5 +1,5 @@
from cereal import car import copy
from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, make_tester_present_msg from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, make_tester_present_msg, structs
from openpilot.selfdrive.car.can_definitions import CanData from openpilot.selfdrive.car.can_definitions import CanData
from openpilot.selfdrive.car.common.numpy_fast import clip from openpilot.selfdrive.car.common.numpy_fast import clip
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
@ -9,8 +9,8 @@ from openpilot.selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_
UNSUPPORTED_DSU_CAR UNSUPPORTED_DSU_CAR
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
SteerControlType = car.CarParams.SteerControlType SteerControlType = structs.CarParams.SteerControlType
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
# LKA limits # LKA limits
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long # EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
@ -168,7 +168,7 @@ class CarController(CarControllerBase):
if self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value: if self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
can_sends.append(make_tester_present_msg(0x750, 0, 0xF)) can_sends.append(make_tester_present_msg(0x750, 0, 0xF))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steer = apply_steer / self.params.STEER_MAX new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer
new_actuators.steeringAngleDeg = self.last_angle new_actuators.steeringAngleDeg = self.last_angle

@ -1,9 +1,8 @@
import copy import copy
from cereal import car
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import DT_CTRL, create_button_events from openpilot.selfdrive.car import DT_CTRL, create_button_events, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.filter_simple import FirstOrderFilter from openpilot.selfdrive.car.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.car.common.numpy_fast import mean from openpilot.selfdrive.car.common.numpy_fast import mean
@ -11,8 +10,8 @@ from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, \ from openpilot.selfdrive.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, \
TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR
ButtonType = car.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
SteerControlType = car.CarParams.SteerControlType SteerControlType = structs.CarParams.SteerControlType
# These steering fault definitions seem to be common across LKA (torque) and LTA (angle): # These steering fault definitions seem to be common across LKA (torque) and LTA (angle):
# - high steer rate fault: goes to 21 or 25 for 1 frame, then 9 for 2 seconds # - high steer rate fault: goes to 21 or 25 for 1 frame, then 9 for 2 seconds
@ -49,8 +48,8 @@ class CarState(CarStateBase):
self.acc_type = 1 self.acc_type = 1
self.lkas_hud = {} self.lkas_hud = {}
def update(self, cp, cp_cam, *_): def update(self, cp, cp_cam, *_) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
ret.doorOpen = any([cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_FL"], cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_FR"], ret.doorOpen = any([cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_FL"], cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_FR"],
cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_RL"], cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_RR"]]) cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_RL"], cp.vl["BODY_CONTROL_STATE"]["DOOR_OPEN_RR"]])

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.toyota.values import CAR from openpilot.selfdrive.car.toyota.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
FW_VERSIONS = { FW_VERSIONS = {
CAR.TOYOTA_AVALON: { CAR.TOYOTA_AVALON: {

@ -1,13 +1,12 @@
from cereal import car
from panda import Panda from panda import Panda
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import structs, get_safety_config
from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \ from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
SteerControlType = car.CarParams.SteerControlType SteerControlType = structs.CarParams.SteerControlType
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -16,9 +15,9 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "toyota" ret.carName = "toyota"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate] ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
# BRAKE_MODULE is on a different address for these cars # BRAKE_MODULE is on a different address for these cars

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from cereal import car from openpilot.selfdrive.car.structs import RadarData
from openpilot.selfdrive.car.toyota.values import DBC, TSS2_CAR from openpilot.selfdrive.car.toyota.values import DBC, TSS2_CAR
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
@ -54,7 +54,7 @@ class RadarInterface(RadarInterfaceBase):
return rr return rr
def _update(self, updated_messages): def _update(self, updated_messages):
ret = car.RadarData.new_message() ret = RadarData()
errors = [] errors = []
if not self.rcp.can_valid: if not self.rcp.can_valid:
errors.append("canError") errors.append("canError")
@ -77,7 +77,7 @@ class RadarInterface(RadarInterfaceBase):
# radar point only valid if it's a valid measurement and score is above 50 # radar point only valid if it's a valid measurement and score is above 50
if cpt['VALID'] or (score > 50 and cpt['LONG_DIST'] < 255 and self.valid_cnt[ii] > 0): if cpt['VALID'] or (score > 50 and cpt['LONG_DIST'] < 255 and self.valid_cnt[ii] > 0):
if ii not in self.pts or cpt['NEW_TRACK']: if ii not in self.pts or cpt['NEW_TRACK']:
self.pts[ii] = car.RadarData.RadarPoint.new_message() self.pts[ii] = RadarData.RadarPoint()
self.pts[ii].trackId = self.track_id self.pts[ii].trackId = self.track_id
self.track_id += 1 self.track_id += 1
self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car

@ -1,19 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import defaultdict from collections import defaultdict
from cereal import car
from openpilot.selfdrive.car.toyota.values import PLATFORM_CODE_ECUS, get_platform_codes from openpilot.selfdrive.car.toyota.values import PLATFORM_CODE_ECUS, get_platform_codes
from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
if __name__ == "__main__": if __name__ == "__main__":
parts_for_ecu: dict = defaultdict(set) parts_for_ecu: dict = defaultdict(set)
cars_for_code: dict = defaultdict(lambda: defaultdict(set)) cars_for_code: dict = defaultdict(lambda: defaultdict(set))
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
print() print()
print(car_model) print(car_model)
for ecu in sorted(ecus, key=lambda x: int(x[0])): for ecu in sorted(ecus):
if ecu[0] not in PLATFORM_CODE_ECUS: if ecu[0] not in PLATFORM_CODE_ECUS:
continue continue
@ -21,15 +17,15 @@ if __name__ == "__main__":
parts_for_ecu[ecu] |= {code.split(b'-')[0] for code in platform_codes if code.count(b'-') > 1} parts_for_ecu[ecu] |= {code.split(b'-')[0] for code in platform_codes if code.count(b'-') > 1}
for code in platform_codes: for code in platform_codes:
cars_for_code[ecu][b'-'.join(code.split(b'-')[:2])] |= {car_model} cars_for_code[ecu][b'-'.join(code.split(b'-')[:2])] |= {car_model}
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
print(f' Codes: {platform_codes}') print(f' Codes: {platform_codes}')
print('\nECU parts:') print('\nECU parts:')
for ecu, parts in parts_for_ecu.items(): for ecu, parts in parts_for_ecu.items():
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}): {parts}') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}): {parts}')
print('\nCar models vs. platform codes (no major versions):') print('\nCar models vs. platform codes (no major versions):')
for ecu, codes in cars_for_code.items(): for ecu, codes in cars_for_code.items():
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):') print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
for code, cars in codes.items(): for code, cars in codes.items():
print(f' {code!r}: {sorted(cars)}') print(f' {code!r}: {sorted(cars)}')

@ -1,14 +1,13 @@
from hypothesis import given, settings, strategies as st from hypothesis import given, settings, strategies as st
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.fw_versions import build_fw_dict from openpilot.selfdrive.car.fw_versions import build_fw_dict
from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, \ from openpilot.selfdrive.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, \
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \ FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
get_platform_codes get_platform_codes
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
def check_fw_version(fw_version: bytes) -> bool: def check_fw_version(fw_version: bytes) -> bool:
@ -153,10 +152,10 @@ class TestToyotaFingerprint:
for ecu, fw_versions in fw_by_addr.items(): for ecu, fw_versions in fw_by_addr.items():
ecu_name, addr, sub_addr = ecu ecu_name, addr, sub_addr = ecu
for fw in fw_versions: for fw in fw_versions:
car_fw.append({"ecu": ecu_name, "fwVersion": fw, "address": addr, car_fw.append(CarParams.CarFw(ecu=ecu_name, fwVersion=fw, address=addr,
"subAddress": 0 if sub_addr is None else sub_addr}) subAddress=0 if sub_addr is None else sub_addr))
CP = car.CarParams.new_message(carFw=car_fw) CP = CarParams(carFw=car_fw)
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS) matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
if len(matches) == 1: if len(matches) == 1:
assert list(matches)[0] == platform assert list(matches)[0] == platform

@ -1,6 +1,6 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
SteerControlType = car.CarParams.SteerControlType SteerControlType = CarParams.SteerControlType
def create_steer_command(packer, steer, steer_req): def create_steer_command(packer, steer, steer_req):

@ -3,13 +3,13 @@ from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, IntFlag from enum import Enum, IntFlag
from cereal import car
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, AngleRateLimit, dbc_dict from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, AngleRateLimit, dbc_dict
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
MIN_ACC_SPEED = 19. * CV.MPH_TO_MS MIN_ACC_SPEED = 19. * CV.MPH_TO_MS
PEDAL_TRANSITION = 10. * CV.MPH_TO_MS PEDAL_TRANSITION = 10. * CV.MPH_TO_MS

@ -1,14 +1,14 @@
from cereal import car import copy
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits from openpilot.selfdrive.car import DT_CTRL, apply_driver_steer_torque_limits, structs
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.common.numpy_fast import clip from openpilot.selfdrive.car.common.numpy_fast import clip
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.volkswagen import mqbcan, pqcan from openpilot.selfdrive.car.volkswagen import mqbcan, pqcan
from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = structs.CarControl.Actuators.LongControlState
class CarController(CarControllerBase): class CarController(CarControllerBase):
@ -17,7 +17,7 @@ class CarController(CarControllerBase):
self.CCP = CarControllerParams(CP) self.CCP = CarControllerParams(CP)
self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan
self.packer_pt = CANPacker(dbc_name) self.packer_pt = CANPacker(dbc_name)
self.ext_bus = CANBUS.pt if CP.networkLocation == car.CarParams.NetworkLocation.fwdCamera else CANBUS.cam self.ext_bus = CANBUS.pt if CP.networkLocation == structs.CarParams.NetworkLocation.fwdCamera else CANBUS.cam
self.apply_steer_last = 0 self.apply_steer_last = 0
self.gra_acc_counter_last = None self.gra_acc_counter_last = None
@ -110,7 +110,7 @@ class CarController(CarControllerBase):
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values, can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume)) cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
new_actuators = actuators.as_builder() new_actuators = copy.copy(actuators)
new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -1,6 +1,6 @@
import numpy as np import numpy as np
from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \
@ -24,7 +24,7 @@ class CarState(CarStateBase):
for button in buttons: for button in buttons:
state = pt_cp.vl[button.can_addr][button.can_msg] in button.values state = pt_cp.vl[button.can_addr][button.can_msg] in button.values
if self.button_states[button.event_type] != state: if self.button_states[button.event_type] != state:
event = car.CarState.ButtonEvent.new_message() event = structs.CarState.ButtonEvent()
event.type = button.event_type event.type = button.event_type
event.pressed = state event.pressed = state
button_events.append(event) button_events.append(event)
@ -32,14 +32,14 @@ class CarState(CarStateBase):
return button_events return button_events
def update(self, pt_cp, cam_cp, *_): def update(self, pt_cp, cam_cp, *_) -> structs.CarState:
ext_cp = pt_cp if self.CP.networkLocation == NetworkLocation.fwdCamera else cam_cp ext_cp = pt_cp if self.CP.networkLocation == NetworkLocation.fwdCamera else cam_cp
if self.CP.flags & VolkswagenFlags.PQ: if self.CP.flags & VolkswagenFlags.PQ:
return self.update_pq(pt_cp, cam_cp, ext_cp) return self.update_pq(pt_cp, cam_cp, ext_cp)
ret = car.CarState.new_message() ret = structs.CarState()
# Update vehicle speed and acceleration from ABS wheel speeds. # Update vehicle speed and acceleration from ABS wheel speeds.
ret.wheelSpeeds = self.get_wheel_speeds( ret.wheelSpeeds = self.get_wheel_speeds(
pt_cp.vl["ESP_19"]["ESP_VL_Radgeschw_02"], pt_cp.vl["ESP_19"]["ESP_VL_Radgeschw_02"],
@ -158,8 +158,8 @@ class CarState(CarStateBase):
self.frame += 1 self.frame += 1
return ret return ret
def update_pq(self, pt_cp, cam_cp, ext_cp): def update_pq(self, pt_cp, cam_cp, ext_cp) -> structs.CarState:
ret = car.CarState.new_message() ret = structs.CarState()
# Update vehicle speed and acceleration from ABS wheel speeds. # Update vehicle speed and acceleration from ABS wheel speeds.
ret.wheelSpeeds = self.get_wheel_speeds( ret.wheelSpeeds = self.get_wheel_speeds(
pt_cp.vl["Bremse_3"]["Radgeschw__VL_4_1"], pt_cp.vl["Bremse_3"]["Radgeschw__VL_4_1"],

@ -1,7 +1,7 @@
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.volkswagen.values import CAR from openpilot.selfdrive.car.volkswagen.values import CAR
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
# TODO: Sharan Mk2 EPS and DQ250 auto trans both require KWP2000 support for fingerprinting # TODO: Sharan Mk2 EPS and DQ250 auto trans both require KWP2000 support for fingerprinting

@ -1,19 +1,18 @@
from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config, structs
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.volkswagen.values import CAR, NetworkLocation, TransmissionType, VolkswagenFlags from openpilot.selfdrive.car.volkswagen.values import CAR, NetworkLocation, TransmissionType, VolkswagenFlags
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs): def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
ret.carName = "volkswagen" ret.carName = "volkswagen"
ret.radarUnavailable = True ret.radarUnavailable = True
if ret.flags & VolkswagenFlags.PQ: if ret.flags & VolkswagenFlags.PQ:
# Set global PQ35/PQ46/NMS parameters # Set global PQ35/PQ46/NMS parameters
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagenPq)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)]
ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1 ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
if 0x440 in fingerprint[0] or docs: # Getriebe_1 if 0x440 in fingerprint[0] or docs: # Getriebe_1
@ -36,7 +35,7 @@ class CarInterface(CarInterfaceBase):
else: else:
# Set global MQB parameters # Set global MQB parameters
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagen)] ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagen)]
ret.enableBsm = 0x30F in fingerprint[0] # SWA_01 ret.enableBsm = 0x30F in fingerprint[0] # SWA_01
if 0xAD in fingerprint[0] or docs: # Getriebe_11 if 0xAD in fingerprint[0] or docs: # Getriebe_11

@ -1,11 +1,11 @@
import random import random
import re import re
from cereal import car from openpilot.selfdrive.car.structs import CarParams
from openpilot.selfdrive.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI from openpilot.selfdrive.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
from openpilot.selfdrive.car.volkswagen.fingerprints import FW_VERSIONS from openpilot.selfdrive.car.volkswagen.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
CHASSIS_CODE_PATTERN = re.compile('[A-Z0-9]{2}') CHASSIS_CODE_PATTERN = re.compile('[A-Z0-9]{2}')
# TODO: determine the unknown groups # TODO: determine the unknown groups

@ -2,19 +2,19 @@ from collections import defaultdict, namedtuple
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, IntFlag, StrEnum from enum import Enum, IntFlag, StrEnum
from cereal import car
from panda.python import uds from panda.python import uds
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car import dbc_dict, CarSpecs, DbcDict, PlatformConfig, Platforms from openpilot.selfdrive.car import dbc_dict, CarSpecs, DbcDict, PlatformConfig, Platforms
from openpilot.selfdrive.car.conversions import Conversions as CV from openpilot.selfdrive.car.conversions import Conversions as CV
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \ from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
Device Device
from openpilot.selfdrive.car.fw_query_definitions import EcuAddrSubAddr, FwQueryConfig, Request, p16 from openpilot.selfdrive.car.fw_query_definitions import EcuAddrSubAddr, FwQueryConfig, Request, p16
Ecu = car.CarParams.Ecu Ecu = structs.CarParams.Ecu
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = structs.CarParams.NetworkLocation
TransmissionType = car.CarParams.TransmissionType TransmissionType = structs.CarParams.TransmissionType
GearShifter = car.CarState.GearShifter GearShifter = structs.CarState.GearShifter
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
@ -54,12 +54,12 @@ class CarControllerParams:
self.hca_status_values = can_define.dv["Lenkhilfe_2"]["LH2_Sta_HCA"] self.hca_status_values = can_define.dv["Lenkhilfe_2"]["LH2_Sta_HCA"]
self.BUTTONS = [ self.BUTTONS = [
Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_Neu", "GRA_Neu_Setzen", [1]), Button(structs.CarState.ButtonEvent.Type.setCruise, "GRA_Neu", "GRA_Neu_Setzen", [1]),
Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_Neu", "GRA_Recall", [1]), Button(structs.CarState.ButtonEvent.Type.resumeCruise, "GRA_Neu", "GRA_Recall", [1]),
Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_Neu", "GRA_Up_kurz", [1]), Button(structs.CarState.ButtonEvent.Type.accelCruise, "GRA_Neu", "GRA_Up_kurz", [1]),
Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_Neu", "GRA_Down_kurz", [1]), Button(structs.CarState.ButtonEvent.Type.decelCruise, "GRA_Neu", "GRA_Down_kurz", [1]),
Button(car.CarState.ButtonEvent.Type.cancel, "GRA_Neu", "GRA_Abbrechen", [1]), Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_Neu", "GRA_Abbrechen", [1]),
Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_Neu", "GRA_Zeitluecke", [1]), Button(structs.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_Neu", "GRA_Zeitluecke", [1]),
] ]
self.LDW_MESSAGES = { self.LDW_MESSAGES = {
@ -85,12 +85,12 @@ class CarControllerParams:
self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"] self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"]
self.BUTTONS = [ self.BUTTONS = [
Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]), Button(structs.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]),
Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]), Button(structs.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]),
Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]), Button(structs.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]),
Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]), Button(structs.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]),
Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]), Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]),
Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]), Button(structs.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]),
] ]
self.LDW_MESSAGES = { self.LDW_MESSAGES = {
@ -190,7 +190,7 @@ class VWCarDocs(CarDocs):
package: str = "Adaptive Cruise Control (ACC) & Lane Assist" package: str = "Adaptive Cruise Control (ACC) & Lane Assist"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.vw_j533])) car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.vw_j533]))
def init_make(self, CP: car.CarParams): def init_make(self, CP: structs.CarParams):
self.footnotes.append(Footnote.VW_EXP_LONG) self.footnotes.append(Footnote.VW_EXP_LONG)
if "SKODA" in CP.carFingerprint: if "SKODA" in CP.carFingerprint:
self.footnotes.append(Footnote.SKODA_HEATED_WINDSHIELD) self.footnotes.append(Footnote.SKODA_HEATED_WINDSHIELD)

@ -1,6 +1,7 @@
from parameterized import parameterized from parameterized import parameterized
from cereal import car, log from cereal import car, log
from openpilot.selfdrive.car.card import convert_to_capnp
from openpilot.selfdrive.car.car_helpers import interfaces from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.honda.values import CAR as HONDA from openpilot.selfdrive.car.honda.values import CAR as HONDA
from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA
@ -20,6 +21,7 @@ class TestLatControl:
CarInterface, CarController, CarState = interfaces[car_name] CarInterface, CarController, CarState = interfaces[car_name]
CP = CarInterface.get_non_essential_params(car_name) CP = CarInterface.get_non_essential_params(car_name)
CI = CarInterface(CP, CarController, CarState) CI = CarInterface(CP, CarController, CarState)
CP = convert_to_capnp(CP)
VM = VehicleModel(CP) VM = VehicleModel(CP)
controller = controller(CP.as_reader(), CI) controller = controller(CP.as_reader(), CI)

@ -3,6 +3,7 @@ import math
import numpy as np import numpy as np
from openpilot.selfdrive.car.card import convert_to_capnp
from openpilot.selfdrive.car.honda.interface import CarInterface from openpilot.selfdrive.car.honda.interface import CarInterface
from openpilot.selfdrive.car.honda.values import CAR from openpilot.selfdrive.car.honda.values import CAR
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, create_dyn_state_matrices from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, create_dyn_state_matrices
@ -11,7 +12,7 @@ from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_
class TestVehicleModel: class TestVehicleModel:
def setup_method(self): def setup_method(self):
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC) CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
self.VM = VehicleModel(CP) self.VM = VehicleModel(convert_to_capnp(CP))
def test_round_trip_yaw_rate(self): def test_round_trip_yaw_rate(self):
# TODO: fix VM to work at zero speed # TODO: fix VM to work at zero speed

@ -11,6 +11,7 @@ from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL, Ratekeeper, Priority, config_realtime_process from openpilot.common.realtime import DT_CTRL, Ratekeeper, Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.common.simple_kalman import KF1D from openpilot.common.simple_kalman import KF1D
from openpilot.selfdrive.car import structs
from openpilot.selfdrive.pandad import can_capnp_to_list from openpilot.selfdrive.pandad import can_capnp_to_list
@ -208,7 +209,7 @@ class RadarD:
self.ready = False self.ready = False
def update(self, sm: messaging.SubMaster, rr: car.RadarData): def update(self, sm: messaging.SubMaster, rr: structs.RadarData):
self.ready = sm.seen['modelV2'] self.ready = sm.seen['modelV2']
self.current_time = 1e-9*max(sm.logMonoTime.values()) self.current_time = 1e-9*max(sm.logMonoTime.values())
@ -301,7 +302,7 @@ def main() -> None:
while 1: while 1:
can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True) can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)
rr: car.RadarData | None = RI.update(can_capnp_to_list(can_strings)) rr: structs.RadarData | None = RI.update(can_capnp_to_list(can_strings))
sm.update(0) sm.update(0)
if rr is None: if rr is None:
continue continue

@ -2,28 +2,24 @@
import jinja2 import jinja2
import os import os
from cereal import car
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
Ecu = car.CarParams.Ecu
CARS = get_interface_attr('CAR') CARS = get_interface_attr('CAR')
FW_VERSIONS = get_interface_attr('FW_VERSIONS') FW_VERSIONS = get_interface_attr('FW_VERSIONS')
FINGERPRINTS = get_interface_attr('FINGERPRINTS') FINGERPRINTS = get_interface_attr('FINGERPRINTS')
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
FINGERPRINTS_PY_TEMPLATE = jinja2.Template(""" FINGERPRINTS_PY_TEMPLATE = jinja2.Template("""
{%- if FINGERPRINTS[brand] %} {%- if FINGERPRINTS[brand] %}
# ruff: noqa: E501 # ruff: noqa: E501
{% endif %} {% endif %}
{% if FW_VERSIONS[brand] %} {% if FW_VERSIONS[brand] %}
from cereal import car from openpilot.selfdrive.car.structs import CarParams
{% endif %} {% endif %}
from openpilot.selfdrive.car.{{brand}}.values import CAR from openpilot.selfdrive.car.{{brand}}.values import CAR
{% if FW_VERSIONS[brand] %} {% if FW_VERSIONS[brand] %}
Ecu = car.CarParams.Ecu Ecu = CarParams.Ecu
{% endif %} {% endif %}
{% if comments +%} {% if comments +%}
{{ comments | join() }} {{ comments | join() }}
@ -49,7 +45,7 @@ FW_VERSIONS{% if not FW_VERSIONS[brand] %}: dict[str, dict[tuple, list[bytes]]]{
{% for car, _ in FW_VERSIONS[brand].items() %} {% for car, _ in FW_VERSIONS[brand].items() %}
CAR.{{car.name}}: { CAR.{{car.name}}: {
{% for key, fw_versions in FW_VERSIONS[brand][car].items() %} {% for key, fw_versions in FW_VERSIONS[brand][car].items() %}
(Ecu.{{ECU_NAME[key[0]]}}, 0x{{"%0x" | format(key[1] | int)}}, \ (Ecu.{{key[0]}}, 0x{{"%0x" | format(key[1] | int)}}, \
{% if key[2] %}0x{{"%0x" | format(key[2] | int)}}{% else %}{{key[2]}}{% endif %}): [ {% if key[2] %}0x{{"%0x" | format(key[2] | int)}}{% else %}{{key[2]}}{% endif %}): [
{% for fw_version in (fw_versions + extra_fw_versions.get(car, {}).get(key, [])) | unique | sort %} {% for fw_version in (fw_versions + extra_fw_versions.get(car, {}).get(key, [])) | unique | sort %}
{{fw_version}}, {{fw_version}},
@ -71,9 +67,8 @@ def format_brand_fw_versions(brand, extra_fw_versions: None | dict[str, dict[tup
comments = [line for line in f.readlines() if line.startswith("#") and "noqa" not in line] comments = [line for line in f.readlines() if line.startswith("#") and "noqa" not in line]
with open(fingerprints_file, "w") as f: with open(fingerprints_file, "w") as f:
f.write(FINGERPRINTS_PY_TEMPLATE.render(brand=brand, comments=comments, ECU_NAME=ECU_NAME, f.write(FINGERPRINTS_PY_TEMPLATE.render(brand=brand, comments=comments, FINGERPRINTS=FINGERPRINTS,
FINGERPRINTS=FINGERPRINTS, FW_VERSIONS=FW_VERSIONS, FW_VERSIONS=FW_VERSIONS, extra_fw_versions=extra_fw_versions))
extra_fw_versions=extra_fw_versions))
if __name__ == "__main__": if __name__ == "__main__":

@ -16,6 +16,7 @@ from openpilot.common.realtime import config_realtime_process
from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry from openpilot.system import sentry
from openpilot.selfdrive.car.card import convert_to_capnp
from openpilot.selfdrive.car.car_helpers import get_demo_car_params from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime
@ -170,7 +171,7 @@ def main(demo=False):
if demo: if demo:
CP = get_demo_car_params() CP = convert_to_capnp(get_demo_car_params())
else: else:
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)

@ -6,6 +6,7 @@ from msgq.visionipc import VisionIpcServer, VisionStreamType
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.realtime import DT_MDL from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.car.card import convert_to_capnp
from openpilot.selfdrive.car.car_helpers import get_demo_car_params from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.system.manager.process_config import managed_processes from openpilot.system.manager.process_config import managed_processes
from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state
@ -23,7 +24,7 @@ class TestModeld:
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_DRIVER, 40, False, CAM.width, CAM.height) self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_DRIVER, 40, False, CAM.width, CAM.height)
self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_WIDE_ROAD, 40, False, CAM.width, CAM.height) self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_WIDE_ROAD, 40, False, CAM.width, CAM.height)
self.vipc_server.start_listener() self.vipc_server.start_listener()
Params().put("CarParams", get_demo_car_params().to_bytes()) Params().put("CarParams", convert_to_capnp(get_demo_car_params()).to_bytes())
self.sm = messaging.SubMaster(['modelV2', 'cameraOdometry']) self.sm = messaging.SubMaster(['modelV2', 'cameraOdometry'])
self.pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'liveCalibration']) self.pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'liveCalibration'])

@ -22,8 +22,9 @@ from openpilot.common.prefix import OpenpilotPrefix
from openpilot.common.timeout import Timeout from openpilot.common.timeout import Timeout
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from panda.python import ALTERNATIVE_EXPERIENCE from panda.python import ALTERNATIVE_EXPERIENCE
from openpilot.selfdrive.car.card import can_comm_callbacks from openpilot.selfdrive.car.card import can_comm_callbacks, convert_to_capnp
from openpilot.selfdrive.car.car_helpers import get_car, interfaces from openpilot.selfdrive.car.car_helpers import get_car, interfaces
from openpilot.selfdrive.car import structs
from openpilot.system.manager.process_config import managed_processes from openpilot.system.manager.process_config import managed_processes
from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams
from openpilot.selfdrive.test.process_replay.migration import migrate_all from openpilot.selfdrive.test.process_replay.migration import migrate_all
@ -362,14 +363,14 @@ def get_car_params_callback(rc, pm, msgs, fingerprint):
cached_params = None cached_params = None
if has_cached_cp: if has_cached_cp:
with car.CarParams.from_bytes(cached_params_raw) as _cached_params: with car.CarParams.from_bytes(cached_params_raw) as _cached_params:
cached_params = _cached_params cached_params = structs.CarParams(carName=_cached_params.carName, carFw=_cached_params.carFw, carVin=_cached_params.carVin)
CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("ExperimentalLongitudinalEnabled"), cached_params=cached_params).CP CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("ExperimentalLongitudinalEnabled"), cached_params=cached_params).CP
if not params.get_bool("DisengageOnAccelerator"): if not params.get_bool("DisengageOnAccelerator"):
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
params.put("CarParams", CP.to_bytes()) params.put("CarParams", convert_to_capnp(CP).to_bytes())
def controlsd_rcv_callback(msg, cfg, frame): def controlsd_rcv_callback(msg, cfg, frame):

@ -1 +1 @@
d768496a1a85bfe5b74c99a79203affdf9a0a065 fa4965a27dee4449ad8b255f9f7674d69327b6f7

@ -36,7 +36,7 @@ MAX_TOTAL_CPU = 250. # total for all 8 cores
PROCS = { PROCS = {
# Baseline CPU usage by process # Baseline CPU usage by process
"selfdrive.controls.controlsd": 32.0, "selfdrive.controls.controlsd": 32.0,
"selfdrive.car.card": 22.0, "selfdrive.car.card": 26.0,
"./loggerd": 14.0, "./loggerd": 14.0,
"./encoderd": 17.0, "./encoderd": 17.0,
"./camerad": 14.5, "./camerad": 14.5,

@ -9,6 +9,7 @@ import cereal.messaging as messaging
from cereal.services import SERVICE_LIST from cereal.services import SERVICE_LIST
from openpilot.common.mock import mock_messages from openpilot.common.mock import mock_messages
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.car.card import convert_to_capnp
from openpilot.selfdrive.car.car_helpers import get_demo_car_params from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.system.hardware.tici.power_monitor import get_power from openpilot.system.hardware.tici.power_monitor import get_power
from openpilot.system.manager.process_config import managed_processes from openpilot.system.manager.process_config import managed_processes
@ -42,7 +43,7 @@ PROCS = [
class TestPowerDraw: class TestPowerDraw:
def setup_method(self): def setup_method(self):
Params().put("CarParams", get_demo_car_params().to_bytes()) Params().put("CarParams", convert_to_capnp(get_demo_car_params()).to_bytes())
# wait a bit for power save to disable # wait a bit for power save to disable
time.sleep(5) time.sleep(5)

Loading…
Cancel
Save