Merge remote-tracking branch 'upstream/master' into toyota-pc

pull/31607/head
Shane Smiskol 1 year ago
commit b592eb777e
  1. 22
      .github/workflows/auto_pr_review.yaml
  2. 2
      .github/workflows/tools_tests.yaml
  3. 17
      SConstruct
  4. 2
      cereal
  5. 1
      common/params.cc
  6. 11
      common/utils.py
  7. 2
      docs/CONTRIBUTING.md
  8. 2
      opendbc
  9. 2
      panda
  10. 1
      pyproject.toml
  11. 2
      release/files_common
  12. 7
      selfdrive/SConscript
  13. 8
      selfdrive/car/__init__.py
  14. 21
      selfdrive/car/body/carcontroller.py
  15. 3
      selfdrive/car/chrysler/carcontroller.py
  16. 15
      selfdrive/car/chrysler/interface.py
  17. 131
      selfdrive/car/chrysler/values.py
  18. 3
      selfdrive/car/docs_definitions.py
  19. 3
      selfdrive/car/ford/carcontroller.py
  20. 1
      selfdrive/car/ford/tests/test_ford.py
  21. 50
      selfdrive/car/ford/values.py
  22. 3
      selfdrive/car/gm/carcontroller.py
  23. 15
      selfdrive/car/gm/values.py
  24. 18
      selfdrive/car/honda/carcontroller.py
  25. 12
      selfdrive/car/honda/carstate.py
  26. 85
      selfdrive/car/honda/fingerprints.py
  27. 63
      selfdrive/car/honda/hondacan.py
  28. 16
      selfdrive/car/honda/interface.py
  29. 23
      selfdrive/car/honda/values.py
  30. 3
      selfdrive/car/hyundai/carcontroller.py
  31. 12
      selfdrive/car/interfaces.py
  32. 3
      selfdrive/car/mazda/carcontroller.py
  33. 3
      selfdrive/car/nissan/carcontroller.py
  34. 14
      selfdrive/car/nissan/interface.py
  35. 68
      selfdrive/car/nissan/values.py
  36. 16
      selfdrive/car/subaru/carcontroller.py
  37. 54
      selfdrive/car/subaru/carstate.py
  38. 23
      selfdrive/car/subaru/interface.py
  39. 42
      selfdrive/car/subaru/values.py
  40. 3
      selfdrive/car/tesla/carcontroller.py
  41. 4
      selfdrive/car/tests/routes.py
  42. 8
      selfdrive/car/tests/test_fw_fingerprint.py
  43. 4
      selfdrive/car/tests/test_models_segs.txt
  44. 3
      selfdrive/car/torque_data/params.toml
  45. 3
      selfdrive/car/toyota/carcontroller.py
  46. 3
      selfdrive/car/toyota/fingerprints.py
  47. 3
      selfdrive/car/volkswagen/carcontroller.py
  48. 5
      selfdrive/car/volkswagen/values.py
  49. 3
      selfdrive/controls/lib/lateral_mpc_lib/SConscript
  50. 4
      selfdrive/controls/lib/longitudinal_mpc_lib/SConscript
  51. 12
      selfdrive/locationd/locationd.cc
  52. 2
      selfdrive/test/process_replay/process_replay.py
  53. 2
      selfdrive/test/process_replay/ref_commit
  54. 2
      selfdrive/test/test_onroad.py
  55. 122
      selfdrive/ui/qt/setup/setup.cc
  56. 1
      selfdrive/ui/qt/setup/setup.h
  57. 12
      selfdrive/ui/translations/main_ar.ts
  58. 12
      selfdrive/ui/translations/main_de.ts
  59. 12
      selfdrive/ui/translations/main_fr.ts
  60. 12
      selfdrive/ui/translations/main_ja.ts
  61. 12
      selfdrive/ui/translations/main_ko.ts
  62. 14
      selfdrive/ui/translations/main_pt-BR.ts
  63. 12
      selfdrive/ui/translations/main_th.ts
  64. 12
      selfdrive/ui/translations/main_tr.ts
  65. 12
      selfdrive/ui/translations/main_zh-CHS.ts
  66. 12
      selfdrive/ui/translations/main_zh-CHT.ts
  67. 2
      system/hardware/tici/tests/test_hardware.py
  68. 3
      system/hardware/tici/tests/test_power_draw.py
  69. 2
      system/ubloxd/tests/print_gps_stats.py
  70. 2
      system/ubloxd/ublox_msg.cc
  71. 2
      tools/lib/tests/test_comma_car_segments.py
  72. 2
      tools/plotjuggler/layouts/gps_vs_llk.xml
  73. 2
      tools/plotjuggler/layouts/ublox-debug.xml
  74. 2
      tools/sim/lib/simulated_sensors.py

@ -34,6 +34,26 @@ jobs:
already-exists-action: close_this already-exists-action: close_this
already-exists-comment: "Your PR should be made against the `master` branch" already-exists-comment: "Your PR should be made against the `master` branch"
comment:
runs-on: ubuntu-latest
steps:
- name: comment
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
with:
message: |
<!-- _(run_id **${{ github.run_id }}**)_ -->
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
* Convert your PR to a draft unless it's ready to review
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
* Before marking as "ready for review", ensure:
* the goal is clearly stated in the description
* all the tests are passing
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
* include a route or your device' dongle ID if relevant
comment_tag: run_id
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
check-pr-template: check-pr-template:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@ -41,7 +61,7 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
actions: read actions: read
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot' if: false && github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
steps: steps:
- uses: actions/github-script@v7 - uses: actions/github-script@v7
with: with:

@ -86,7 +86,7 @@ jobs:
notebooks: notebooks:
name: notebooks name: notebooks
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
if: github.repository == 'commaai/openpilot' if: false && github.repository == 'commaai/openpilot'
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

@ -96,8 +96,6 @@ lenv = {
rpath = lenv["LD_LIBRARY_PATH"].copy() rpath = lenv["LD_LIBRARY_PATH"].copy()
if arch == "larch64": if arch == "larch64":
lenv["LD_LIBRARY_PATH"] += ['/data/data/com.termux/files/usr/lib']
cpppath = [ cpppath = [
"#third_party/opencl/include", "#third_party/opencl/include",
] ]
@ -360,13 +358,6 @@ Export('common', 'gpucommon')
# Build cereal and messaging # Build cereal and messaging
SConscript(['cereal/SConscript']) SConscript(['cereal/SConscript'])
cereal = [File('#cereal/libcereal.a')]
messaging = [File('#cereal/libmessaging.a')]
visionipc = [File('#cereal/libvisionipc.a')]
messaging_python = [File('#cereal/messaging/messaging_pyx.so')]
Export('cereal', 'messaging', 'messaging_python', 'visionipc')
# Build other submodules # Build other submodules
SConscript([ SConscript([
'body/board/SConscript', 'body/board/SConscript',
@ -393,13 +384,7 @@ if arch != "Darwin":
# Build openpilot # Build openpilot
SConscript(['third_party/SConscript']) SConscript(['third_party/SConscript'])
SConscript(['selfdrive/boardd/SConscript']) SConscript(['selfdrive/SConscript'])
SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript'])
SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript'])
SConscript(['selfdrive/locationd/SConscript'])
SConscript(['selfdrive/navd/SConscript'])
SConscript(['selfdrive/modeld/SConscript'])
SConscript(['selfdrive/ui/SConscript'])
if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'): if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'):
SConscript(['tools/replay/SConscript']) SConscript(['tools/replay/SConscript'])

@ -1 +1 @@
Subproject commit a4255106b7255e00ae04162f7aa14aa3cae339c3 Subproject commit bfbb0cab83e3ad49d85ad1a34ee1241bf1ff782f

@ -207,7 +207,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UpdaterLastFetchTime", PERSISTENT}, {"UpdaterLastFetchTime", PERSISTENT},
{"Version", PERSISTENT}, {"Version", PERSISTENT},
{"VisionRadarToggle", PERSISTENT}, {"VisionRadarToggle", PERSISTENT},
{"WheeledBody", PERSISTENT},
}; };
} // namespace } // namespace

@ -0,0 +1,11 @@
class Freezable:
_frozen: bool = False
def freeze(self):
if not self._frozen:
self._frozen = True
def __setattr__(self, *args, **kwargs):
if self._frozen:
raise Exception("cannot modify frozen object")
super().__setattr__(*args, **kwargs)

@ -62,4 +62,4 @@ A good pull request has all of the following:
* Consider opting into driver camera uploads to improve the driver monitoring model. * Consider opting into driver camera uploads to improve the driver monitoring model.
* Connect your device to Wi-Fi regularly, so that we can pull data for training better driving models. * Connect your device to Wi-Fi regularly, so that we can pull data for training better driving models.
* Run the `nightly` branch and report issues. This branch is like `master` but it's built just like a release. * Run the `nightly` branch and report issues. This branch is like `master` but it's built just like a release.
* Annotate images in the [comma10k dateset](https://github.com/commaai/comma10k). * Annotate images in the [comma10k dataset](https://github.com/commaai/comma10k).

@ -1 +1 @@
Subproject commit 0ac21652f2e643e29aa471ad6b238bf74b22e356 Subproject commit 5f096db742b0c5dcd19976afdb07d5dd098f4b07

@ -1 +1 @@
Subproject commit 0c7d5f11d7187904022ea49b6a76b54d7b280345 Subproject commit ea156f7c628a371bea9a15a29f9068d5392534ba

@ -190,6 +190,7 @@ lint.flake8-implicit-str-concat.allow-multiline=false
"system".msg = "Use openpilot.system" "system".msg = "Use openpilot.system"
"third_party".msg = "Use openpilot.third_party" "third_party".msg = "Use openpilot.third_party"
"tools".msg = "Use openpilot.tools" "tools".msg = "Use openpilot.tools"
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
[tool.coverage.run] [tool.coverage.run]
concurrency = ["multiprocessing", "thread"] concurrency = ["multiprocessing", "thread"]

@ -60,6 +60,8 @@ system/logmessaged.py
system/micd.py system/micd.py
system/version.py system/version.py
selfdrive/SConscript
selfdrive/athena/__init__.py selfdrive/athena/__init__.py
selfdrive/athena/athenad.py selfdrive/athena/athenad.py
selfdrive/athena/manage_athenad.py selfdrive/athena/manage_athenad.py

@ -0,0 +1,7 @@
SConscript(['boardd/SConscript'])
SConscript(['controls/lib/lateral_mpc_lib/SConscript'])
SConscript(['controls/lib/longitudinal_mpc_lib/SConscript'])
SConscript(['locationd/SConscript'])
SConscript(['navd/SConscript'])
SConscript(['modeld/SConscript'])
SConscript(['ui/SConscript'])

@ -1,12 +1,14 @@
# functions common among cars # functions common among cars
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import ReprEnum from enum import IntFlag, ReprEnum
from dataclasses import replace
import capnp import capnp
from cereal import car from cereal import car
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.common.utils import Freezable
from openpilot.selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.docs_definitions import CarInfo
@ -292,3 +294,7 @@ class Platforms(str, ReprEnum):
@classmethod @classmethod
def create_carinfo_map(cls) -> dict[str, CarInfos]: def create_carinfo_map(cls) -> dict[str, CarInfos]:
return {p: p.config.car_info for p in cls} return {p: p.config.car_info for p in cls}
@classmethod
def with_flags(cls, flags: IntFlag) -> set['Platforms']:
return {p for p in cls if p.config.flags & flags}

@ -1,10 +1,10 @@
import numpy as np import numpy as np
from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car.body import bodycan from openpilot.selfdrive.car.body import bodycan
from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.controls.lib.pid import PIDController
@ -15,23 +15,18 @@ MAX_POS_INTEGRATOR = 0.2 # meters
MAX_TURN_INTEGRATOR = 0.1 # meters MAX_TURN_INTEGRATOR = 0.1 # meters
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.frame = 0 self.frame = 0
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
# Speed, balance and turn PIDs # PIDs
self.speed_pid = PIDController(0.115, k_i=0.23, rate=1/DT_CTRL)
self.balance_pid = PIDController(1300, k_i=0, k_d=280, rate=1/DT_CTRL)
self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.torque_r_filtered = 0. self.torque_r_filtered = 0.
self.torque_l_filtered = 0. self.torque_l_filtered = 0.
params = Params()
self.wheeled_body = params.get("WheeledBody")
@staticmethod @staticmethod
def deadband_filter(torque, deadband): def deadband_filter(torque, deadband):
if torque > 0: if torque > 0:
@ -55,16 +50,6 @@ class CarController:
speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2. speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2.
speed_error = speed_desired - speed_measured speed_error = speed_desired - speed_measured
if self.wheeled_body is None:
freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or
(speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR))
angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator)
# Clip angle error, this is enough to get up from stands
angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR)
angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.)
torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate)
else:
torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False) torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False)
speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr) speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr)

@ -3,9 +3,10 @@ from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car import apply_meas_steer_torque_limits from openpilot.selfdrive.car import apply_meas_steer_torque_limits
from openpilot.selfdrive.car.chrysler import chryslercan from openpilot.selfdrive.car.chrysler import chryslercan
from openpilot.selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags from openpilot.selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
from openpilot.selfdrive.car.interfaces import CarControllerBase
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0

@ -24,7 +24,6 @@ class CarInterface(CarInterfaceBase):
elif candidate in RAM_DT: elif candidate in RAM_DT:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT
ret.minSteerSpeed = 3.8 # m/s
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate not in RAM_CARS: if candidate not in RAM_CARS:
# Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed. # Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed.
@ -35,10 +34,6 @@ class CarInterface(CarInterfaceBase):
# Chrysler # Chrysler
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO): if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO):
ret.mass = 2242.
ret.wheelbase = 3.089
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
ret.lateralTuning.init('pid') ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
@ -46,9 +41,6 @@ class CarInterface(CarInterfaceBase):
# Jeep # Jeep
elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019): elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019):
ret.mass = 1778
ret.wheelbase = 2.71
ret.steerRatio = 16.7
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.lateralTuning.init('pid') ret.lateralTuning.init('pid')
@ -60,19 +52,12 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.RAM_1500: elif candidate == CAR.RAM_1500:
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.wheelbase = 3.88 ret.wheelbase = 3.88
ret.steerRatio = 16.3
ret.mass = 2493.
ret.minSteerSpeed = 14.5
# Older EPS FW allow steer to zero # Older EPS FW allow steer to zero
if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw): if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw):
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
elif candidate == CAR.RAM_HD: elif candidate == CAR.RAM_HD:
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.wheelbase = 3.785
ret.steerRatio = 15.61
ret.mass = 3405.
ret.minSteerSpeed = 16
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False)
else: else:

@ -1,9 +1,9 @@
from enum import IntFlag, StrEnum from enum import IntFlag
from dataclasses import dataclass, field from dataclasses import dataclass, field
from cereal import car from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import dbc_dict from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
@ -13,25 +13,89 @@ Ecu = car.CarParams.Ecu
class ChryslerFlags(IntFlag): class ChryslerFlags(IntFlag):
HIGHER_MIN_STEERING_SPEED = 1 HIGHER_MIN_STEERING_SPEED = 1
@dataclass
class ChryslerCarInfo(CarInfo):
package: str = "Adaptive Cruise Control (ACC)"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
class CAR(StrEnum): @dataclass
class ChryslerPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None))
@dataclass(frozen=True)
class ChryslerCarSpecs(CarSpecs):
minSteerSpeed: float = 3.8 # m/s
class CAR(Platforms):
# Chrysler # Chrysler
PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" PACIFICA_2017_HYBRID = ChryslerPlatformConfig(
PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" "CHRYSLER PACIFICA HYBRID 2017",
PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"),
PACIFICA_2018 = "CHRYSLER PACIFICA 2018" specs=ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2),
PACIFICA_2020 = "CHRYSLER PACIFICA 2020" )
PACIFICA_2018_HYBRID = ChryslerPlatformConfig(
"CHRYSLER PACIFICA HYBRID 2018",
ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"),
specs=PACIFICA_2017_HYBRID.specs,
)
PACIFICA_2019_HYBRID = ChryslerPlatformConfig(
"CHRYSLER PACIFICA HYBRID 2019",
ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"),
specs=PACIFICA_2017_HYBRID.specs,
)
PACIFICA_2018 = ChryslerPlatformConfig(
"CHRYSLER PACIFICA 2018",
ChryslerCarInfo("Chrysler Pacifica 2017-18"),
specs=PACIFICA_2017_HYBRID.specs,
)
PACIFICA_2020 = ChryslerPlatformConfig(
"CHRYSLER PACIFICA 2020",
[
ChryslerCarInfo("Chrysler Pacifica 2019-20"),
ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"),
],
specs=PACIFICA_2017_HYBRID.specs,
)
# Dodge # Dodge
DODGE_DURANGO = "DODGE DURANGO 2021" DODGE_DURANGO = ChryslerPlatformConfig(
"DODGE DURANGO 2021",
ChryslerCarInfo("Dodge Durango 2020-21"),
specs=PACIFICA_2017_HYBRID.specs,
)
# Jeep # Jeep
JEEP_GRAND_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 Trailhawk
JEEP_GRAND_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk "JEEP GRAND CHEROKEE V6 2018",
ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
specs=ChryslerCarSpecs(mass=1778., wheelbase=2.71, steerRatio=16.7),
)
JEEP_GRAND_CHEROKEE_2019 = ChryslerPlatformConfig( # includes 2020 Trailhawk
"JEEP GRAND CHEROKEE 2019",
ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
specs=JEEP_GRAND_CHEROKEE.specs,
)
# Ram # Ram
RAM_1500 = "RAM 1500 5TH GEN" RAM_1500 = ChryslerPlatformConfig(
RAM_HD = "RAM HD 5TH GEN" "RAM 1500 5TH GEN",
ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])),
dbc_dict('chrysler_ram_dt_generated', None),
specs=ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5),
)
RAM_HD = ChryslerPlatformConfig(
"RAM HD 5TH GEN",
[
ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])),
],
dbc_dict('chrysler_ram_hd_generated', None),
specs=ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.),
)
class CarControllerParams: class CarControllerParams:
@ -59,32 +123,6 @@ RAM_HD = {CAR.RAM_HD, }
RAM_CARS = RAM_DT | RAM_HD RAM_CARS = RAM_DT | RAM_HD
@dataclass
class ChryslerCarInfo(CarInfo):
package: str = "Adaptive Cruise Control (ACC)"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
CAR_INFO: dict[str, ChryslerCarInfo | list[ChryslerCarInfo] | None] = {
CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"),
CAR.PACIFICA_2018_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"),
CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"),
CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"),
CAR.PACIFICA_2020: [
ChryslerCarInfo("Chrysler Pacifica 2019-20"),
ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"),
],
CAR.JEEP_GRAND_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
CAR.JEEP_GRAND_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
CAR.DODGE_DURANGO: ChryslerCarInfo("Dodge Durango 2020-21"),
CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])),
CAR.RAM_HD: [
ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])),
],
}
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf132) p16(0xf132)
CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
@ -124,16 +162,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = { DBC = CAR.create_dbc_map()
CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.DODGE_DURANGO: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.JEEP_GRAND_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.JEEP_GRAND_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None),
CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None),
}

@ -118,7 +118,8 @@ class CarHarness(EnumBase):
nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler]) nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler])
mazda = BaseCarHarness("Mazda connector") mazda = BaseCarHarness("Mazda connector")
ford_q3 = BaseCarHarness("Ford Q3 connector") ford_q3 = BaseCarHarness("Ford Q3 connector")
ford_q4 = BaseCarHarness("Ford Q4 connector") ford_q4 = BaseCarHarness("Ford Q4 connector", parts=[Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft, Cable.long_obdc_cable,
Cable.usbc_coupler])
class Device(EnumBase): class Device(EnumBase):

@ -4,6 +4,7 @@ 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
from openpilot.selfdrive.car.ford import fordcan from openpilot.selfdrive.car.ford import fordcan
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@ -22,7 +23,7 @@ def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_c
return clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX) return clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX)
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.VM = VM self.VM = VM

@ -19,6 +19,7 @@ ECU_ADDRESSES = {
Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA) Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA)
Ecu.engine: 0x7E0, # Powertrain Control Module (PCM) Ecu.engine: 0x7E0, # Powertrain Control Module (PCM)
Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM) Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM)
Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM)
} }

@ -1,11 +1,12 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
import panda.python.uds as uds
from cereal import car 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.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \
Device Device
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu
@ -57,13 +58,13 @@ class FordCarInfo(CarInfo):
def init_make(self, CP: car.CarParams): def init_make(self, CP: car.CarParams):
harness = CarHarness.ford_q4 if CP.carFingerprint in CANFD_CAR else CarHarness.ford_q3 harness = CarHarness.ford_q4 if CP.carFingerprint in CANFD_CAR else CarHarness.ford_q3
if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1, CAR.F_150_MK14): if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1, CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1):
self.car_parts = CarParts([Device.threex_angled_mount, harness]) self.car_parts = CarParts([Device.threex_angled_mount, harness])
else: else:
self.car_parts = CarParts([Device.threex, harness]) self.car_parts = CarParts([Device.threex, harness])
@dataclass(frozen=False) @dataclass
class FordPlatformConfig(PlatformConfig): class FordPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR))
@ -140,6 +141,33 @@ class CAR(Platforms):
CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1}
DATA_IDENTIFIER_FORD_ASBUILT = 0xDE00
ASBUILT_BLOCKS: list[tuple[int, list]] = [
(1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]),
(2, [Ecu.abs, Ecu.debug, Ecu.eps]),
(3, [Ecu.abs, Ecu.debug, Ecu.eps]),
(4, [Ecu.debug, Ecu.fwdCamera]),
(5, [Ecu.debug]),
(6, [Ecu.debug]),
(7, [Ecu.debug]),
(8, [Ecu.debug]),
(9, [Ecu.debug]),
(16, [Ecu.debug, Ecu.fwdCamera]),
(18, [Ecu.fwdCamera]),
(20, [Ecu.fwdCamera]),
(21, [Ecu.fwdCamera]),
]
def ford_asbuilt_block_request(block_id: int):
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
def ford_asbuilt_block_response(block_id: int):
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
FW_QUERY_CONFIG = FwQueryConfig( FW_QUERY_CONFIG = FwQueryConfig(
requests=[ requests=[
# CAN and CAN FD queries are combined. # CAN and CAN FD queries are combined.
@ -147,19 +175,29 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request( Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
logging=True, logging=True,
), ),
Request( Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
bus=0, bus=0,
auxiliary=True, auxiliary=True,
), ),
*[Request(
[StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)],
[StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)],
whitelist_ecus=ecus,
bus=0,
logging=True,
) for block_id, ecus in ASBUILT_BLOCKS],
], ],
extra_ecus=[ extra_ecus=[
# We are unlikely to get a response from the PCM from behind the gateway (Ecu.engine, 0x7e0, None), # Powertrain Control Module
(Ecu.engine, 0x7e0, None), # Note: We are unlikely to get a response from behind the gateway
(Ecu.shiftByWire, 0x732, None), (Ecu.shiftByWire, 0x732, None), # Gear Shift Module
(Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module
], ],
) )

@ -6,6 +6,7 @@ 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
from openpilot.selfdrive.car.gm import gmcan from openpilot.selfdrive.car.gm import gmcan
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.interfaces import CarControllerBase
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation NetworkLocation = car.CarParams.NetworkLocation
@ -17,7 +18,7 @@ CAMERA_CANCEL_DELAY_FRAMES = 10
MIN_STEER_MSG_INTERVAL_MS = 15 MIN_STEER_MSG_INTERVAL_MS = 15
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.start_time = 0. self.start_time = 0.

@ -79,7 +79,7 @@ class GMCarInfo(CarInfo):
self.footnotes.append(Footnote.OBD_II) self.footnotes.append(Footnote.OBD_II)
@dataclass(frozen=False) @dataclass
class GMPlatformConfig(PlatformConfig): class GMPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))
@ -189,22 +189,35 @@ class CanBus:
# In a Data Module, an identifier is a string used to recognize an object, # In a Data Module, an identifier is a string used to recognize an object,
# either by itself or together with the identifiers of parent objects. # either by itself or together with the identifiers of parent objects.
# Each returns a 4 byte hex representation of the decimal part number. `b"\x02\x8c\xf0'"` -> 42790951 # Each returns a 4 byte hex representation of the decimal part number. `b"\x02\x8c\xf0'"` -> 42790951
GM_BOOT_SOFTWARE_PART_NUMER_REQUEST = b'\x1a\xc0' # likely does not contain anything useful
GM_SOFTWARE_MODULE_1_REQUEST = b'\x1a\xc1' GM_SOFTWARE_MODULE_1_REQUEST = b'\x1a\xc1'
GM_SOFTWARE_MODULE_2_REQUEST = b'\x1a\xc2' GM_SOFTWARE_MODULE_2_REQUEST = b'\x1a\xc2'
GM_SOFTWARE_MODULE_3_REQUEST = b'\x1a\xc3' GM_SOFTWARE_MODULE_3_REQUEST = b'\x1a\xc3'
# Part number of XML data file that is used to configure ECU
GM_XML_DATA_FILE_PART_NUMBER = b'\x1a\x9c'
GM_XML_CONFIG_COMPAT_ID = b'\x1a\x9b' # used to know if XML file is compatible with the ECU software/hardware
# This DID is for identifying the part number that reflects the mix of hardware, # This DID is for identifying the part number that reflects the mix of hardware,
# software, and calibrations in the ECU when it first arrives at the vehicle assembly plant. # software, and calibrations in the ECU when it first arrives at the vehicle assembly plant.
# If there's an Alpha Code, it's associated with this part number and stored in the DID $DB. # If there's an Alpha Code, it's associated with this part number and stored in the DID $DB.
GM_END_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcb' GM_END_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcb'
GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdb'
GM_BASE_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcc' GM_BASE_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcc'
GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdc'
GM_FW_RESPONSE = b'\x5a' GM_FW_RESPONSE = b'\x5a'
GM_FW_REQUESTS = [ GM_FW_REQUESTS = [
GM_BOOT_SOFTWARE_PART_NUMER_REQUEST,
GM_SOFTWARE_MODULE_1_REQUEST, GM_SOFTWARE_MODULE_1_REQUEST,
GM_SOFTWARE_MODULE_2_REQUEST, GM_SOFTWARE_MODULE_2_REQUEST,
GM_SOFTWARE_MODULE_3_REQUEST, GM_SOFTWARE_MODULE_3_REQUEST,
GM_XML_DATA_FILE_PART_NUMBER,
GM_XML_CONFIG_COMPAT_ID,
GM_END_MODEL_PART_NUMBER_REQUEST, GM_END_MODEL_PART_NUMBER_REQUEST,
GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST,
GM_BASE_MODEL_PART_NUMBER_REQUEST, GM_BASE_MODEL_PART_NUMBER_REQUEST,
GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST,
] ]
GM_RX_OFFSET = 0x400 GM_RX_OFFSET = 0x400

@ -7,6 +7,7 @@ from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import create_gas_interceptor_command from openpilot.selfdrive.car import create_gas_interceptor_command
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.controls.lib.drive_helpers import rate_limit from openpilot.selfdrive.controls.lib.drive_helpers import rate_limit
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
@ -104,11 +105,12 @@ def rate_limit_steer(new_steer, last_steer):
return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA) return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA)
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.params = CarControllerParams(CP) self.params = CarControllerParams(CP)
self.CAN = hondacan.CanBus(CP)
self.frame = 0 self.frame = 0
self.braking = False self.braking = False
@ -167,7 +169,7 @@ class CarController:
can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1)) can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1))
# Send steering command. # Send steering command.
can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint, can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_steer, CC.latActive, self.CP.carFingerprint,
CS.CP.openpilotLongitudinalControl)) CS.CP.openpilotLongitudinalControl))
# wind brake from air resistance decel at high speed # wind brake from air resistance decel at high speed
@ -201,12 +203,12 @@ class CarController:
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message
can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CP.carFingerprint)) can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CAN, self.CP.carFingerprint))
# If using stock ACC, spam cancel command to kill gas when OP disengages. # If using stock ACC, spam cancel command to kill gas when OP disengages.
if pcm_cancel_cmd: if pcm_cancel_cmd:
can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.CANCEL, self.CP.carFingerprint)) can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume: elif CC.cruiseControl.resume:
can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.RES_ACCEL, self.CP.carFingerprint)) can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint))
else: else:
# Send gas and brake commands. # Send gas and brake commands.
@ -219,7 +221,7 @@ class CarController:
stopping = actuators.longControlState == LongCtrlState.stopping stopping = actuators.longControlState == LongCtrlState.stopping
self.stopping_counter = self.stopping_counter + 1 if stopping else 0 self.stopping_counter = self.stopping_counter + 1 if stopping else 0
can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas, can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
self.stopping_counter, self.CP.carFingerprint)) self.stopping_counter, self.CP.carFingerprint))
else: else:
apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0) apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0)
@ -227,7 +229,7 @@ class CarController:
pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts) pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts)
pcm_override = True pcm_override = True
can_sends.append(hondacan.create_brake_command(self.packer, apply_brake, pump_on, can_sends.append(hondacan.create_brake_command(self.packer, self.CAN, apply_brake, pump_on,
pcm_override, pcm_cancel_cmd, fcw_display, pcm_override, pcm_cancel_cmd, fcw_display,
self.CP.carFingerprint, CS.stock_brake)) self.CP.carFingerprint, CS.stock_brake))
self.apply_brake_last = apply_brake self.apply_brake_last = apply_brake
@ -250,7 +252,7 @@ class CarController:
if self.frame % 10 == 0: if self.frame % 10 == 0:
hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible, hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible,
hud_control.lanesVisible, fcw_display, acc_alert, steer_required) hud_control.lanesVisible, fcw_display, acc_alert, steer_required)
can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud)) can_sends.extend(hondacan.create_ui_commands(self.packer, self.CAN, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud))
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH: if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH:
self.speed = pcm_speed self.speed = pcm_speed

@ -5,7 +5,7 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import interp from openpilot.common.numpy_fast import interp
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.honda.hondacan import get_cruise_speed_conversion, get_pt_bus from openpilot.selfdrive.car.honda.hondacan import CanBus, get_cruise_speed_conversion
from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \ from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \ HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
HondaFlags HondaFlags
@ -64,7 +64,7 @@ def get_can_messages(CP, gearbox_msg):
messages.append(("CRUISE_PARAMS", 50)) messages.append(("CRUISE_PARAMS", 50))
# TODO: clean this up # TODO: clean this up
if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, if CP.carFingerprint in (CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT,
CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G):
pass pass
elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
@ -129,7 +129,7 @@ class CarState(CarStateBase):
# panda checks if the signal is non-zero # panda checks if the signal is non-zero
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5
# TODO: find a common signal across all cars # TODO: find a common signal across all cars
if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, if self.CP.carFingerprint in (CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT,
CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G):
ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"])
elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
@ -267,7 +267,7 @@ class CarState(CarStateBase):
def get_can_parser(self, CP): def get_can_parser(self, CP):
messages = get_can_messages(CP, self.gearbox_msg) messages = get_can_messages(CP, self.gearbox_msg)
return CANParser(DBC[CP.carFingerprint]["pt"], messages, get_pt_bus(CP.carFingerprint)) return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).pt)
@staticmethod @staticmethod
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
@ -287,7 +287,7 @@ class CarState(CarStateBase):
("BRAKE_COMMAND", 50), ("BRAKE_COMMAND", 50),
] ]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2) return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).camera)
@staticmethod @staticmethod
def get_body_can_parser(CP): def get_body_can_parser(CP):
@ -296,6 +296,6 @@ class CarState(CarStateBase):
("BSM_STATUS_LEFT", 3), ("BSM_STATUS_LEFT", 3),
("BSM_STATUS_RIGHT", 3), ("BSM_STATUS_RIGHT", 3),
] ]
bus_body = 0 # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port) bus_body = CanBus(CP).radar # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port)
return CANParser(DBC[CP.carFingerprint]["body"], messages, bus_body) return CANParser(DBC[CP.carFingerprint]["body"], messages, bus_body)
return None return None

@ -48,6 +48,7 @@ FW_VERSIONS = {
], ],
(Ecu.shiftByWire, 0x18da0bf1, None): [ (Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TVC-A910\x00\x00', b'54008-TVC-A910\x00\x00',
b'54008-TWA-A910\x00\x00',
], ],
(Ecu.transmission, 0x18da1ef1, None): [ (Ecu.transmission, 0x18da1ef1, None): [
b'28101-6A7-A220\x00\x00', b'28101-6A7-A220\x00\x00',
@ -89,6 +90,12 @@ FW_VERSIONS = {
b'57114-TVA-C530\x00\x00', b'57114-TVA-C530\x00\x00',
b'57114-TVA-E520\x00\x00', b'57114-TVA-E520\x00\x00',
b'57114-TVE-H250\x00\x00', b'57114-TVE-H250\x00\x00',
b'57114-TWA-A040\x00\x00',
b'57114-TWA-A050\x00\x00',
b'57114-TWA-A530\x00\x00',
b'57114-TWA-B520\x00\x00',
b'57114-TWA-C510\x00\x00',
b'57114-TWB-H030\x00\x00',
], ],
(Ecu.eps, 0x18da30f1, None): [ (Ecu.eps, 0x18da30f1, None): [
b'39990-TBX-H120\x00\x00', b'39990-TBX-H120\x00\x00',
@ -100,6 +107,7 @@ FW_VERSIONS = {
b'39990-TVA-X030\x00\x00', b'39990-TVA-X030\x00\x00',
b'39990-TVA-X040\x00\x00', b'39990-TVA-X040\x00\x00',
b'39990-TVE-H130\x00\x00', b'39990-TVE-H130\x00\x00',
b'39990-TWB-H120\x00\x00',
], ],
(Ecu.srs, 0x18da53f1, None): [ (Ecu.srs, 0x18da53f1, None): [
b'77959-TBX-H230\x00\x00', b'77959-TBX-H230\x00\x00',
@ -108,6 +116,9 @@ FW_VERSIONS = {
b'77959-TVA-H230\x00\x00', b'77959-TVA-H230\x00\x00',
b'77959-TVA-L420\x00\x00', b'77959-TVA-L420\x00\x00',
b'77959-TVA-X330\x00\x00', b'77959-TVA-X330\x00\x00',
b'77959-TWA-A440\x00\x00',
b'77959-TWA-L420\x00\x00',
b'77959-TWB-H220\x00\x00',
], ],
(Ecu.combinationMeter, 0x18da60f1, None): [ (Ecu.combinationMeter, 0x18da60f1, None): [
b'78109-TBX-H310\x00\x00', b'78109-TBX-H310\x00\x00',
@ -141,7 +152,19 @@ FW_VERSIONS = {
b'78109-TVC-M510\x00\x00', b'78109-TVC-M510\x00\x00',
b'78109-TVC-YF10\x00\x00', b'78109-TVC-YF10\x00\x00',
b'78109-TVE-H610\x00\x00', b'78109-TVE-H610\x00\x00',
b'78109-TWA-A010\x00\x00',
b'78109-TWA-A020\x00\x00',
b'78109-TWA-A030\x00\x00',
b'78109-TWA-A110\x00\x00',
b'78109-TWA-A120\x00\x00',
b'78109-TWA-A130\x00\x00',
b'78109-TWA-A210\x00\x00', b'78109-TWA-A210\x00\x00',
b'78109-TWA-A220\x00\x00',
b'78109-TWA-A230\x00\x00',
b'78109-TWA-A610\x00\x00',
b'78109-TWA-H210\x00\x00',
b'78109-TWA-L010\x00\x00',
b'78109-TWA-L210\x00\x00',
], ],
(Ecu.hud, 0x18da61f1, None): [ (Ecu.hud, 0x18da61f1, None): [
b'78209-TVA-A010\x00\x00', b'78209-TVA-A010\x00\x00',
@ -158,6 +181,9 @@ FW_VERSIONS = {
b'36802-TVE-H070\x00\x00', b'36802-TVE-H070\x00\x00',
b'36802-TWA-A070\x00\x00', b'36802-TWA-A070\x00\x00',
b'36802-TWA-A080\x00\x00', b'36802-TWA-A080\x00\x00',
b'36802-TWA-A210\x00\x00',
b'36802-TWA-A330\x00\x00',
b'36802-TWB-H060\x00\x00',
], ],
(Ecu.fwdCamera, 0x18dab5f1, None): [ (Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TBX-H130\x00\x00', b'36161-TBX-H130\x00\x00',
@ -166,72 +192,17 @@ FW_VERSIONS = {
b'36161-TVC-A330\x00\x00', b'36161-TVC-A330\x00\x00',
b'36161-TVE-H050\x00\x00', b'36161-TVE-H050\x00\x00',
b'36161-TWA-A070\x00\x00', b'36161-TWA-A070\x00\x00',
b'36161-TWA-A330\x00\x00',
b'36161-TWB-H040\x00\x00',
], ],
(Ecu.gateway, 0x18daeff1, None): [ (Ecu.gateway, 0x18daeff1, None): [
b'38897-TVA-A010\x00\x00', b'38897-TVA-A010\x00\x00',
b'38897-TVA-A020\x00\x00', b'38897-TVA-A020\x00\x00',
b'38897-TVA-A230\x00\x00', b'38897-TVA-A230\x00\x00',
b'38897-TVA-A240\x00\x00', b'38897-TVA-A240\x00\x00',
],
},
CAR.ACCORDH: {
(Ecu.gateway, 0x18daeff1, None): [
b'38897-TWA-A120\x00\x00', b'38897-TWA-A120\x00\x00',
b'38897-TWD-J020\x00\x00', b'38897-TWD-J020\x00\x00',
], ],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-TWA-A040\x00\x00',
b'57114-TWA-A050\x00\x00',
b'57114-TWA-A530\x00\x00',
b'57114-TWA-B520\x00\x00',
b'57114-TWA-C510\x00\x00',
b'57114-TWB-H030\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-TWA-A440\x00\x00',
b'77959-TWA-L420\x00\x00',
b'77959-TWB-H220\x00\x00',
],
(Ecu.combinationMeter, 0x18da60f1, None): [
b'78109-TWA-A010\x00\x00',
b'78109-TWA-A020\x00\x00',
b'78109-TWA-A030\x00\x00',
b'78109-TWA-A110\x00\x00',
b'78109-TWA-A120\x00\x00',
b'78109-TWA-A130\x00\x00',
b'78109-TWA-A210\x00\x00',
b'78109-TWA-A220\x00\x00',
b'78109-TWA-A230\x00\x00',
b'78109-TWA-A610\x00\x00',
b'78109-TWA-H210\x00\x00',
b'78109-TWA-L010\x00\x00',
b'78109-TWA-L210\x00\x00',
],
(Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TWA-A910\x00\x00',
],
(Ecu.hud, 0x18da61f1, None): [
b'78209-TVA-A010\x00\x00',
b'78209-TVA-A110\x00\x00',
],
(Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TWA-A070\x00\x00',
b'36161-TWA-A330\x00\x00',
b'36161-TWB-H040\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36802-TWA-A070\x00\x00',
b'36802-TWA-A080\x00\x00',
b'36802-TWA-A210\x00\x00',
b'36802-TWA-A330\x00\x00',
b'36802-TWB-H060\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TVA-A150\x00\x00',
b'39990-TVA-A160\x00\x00',
b'39990-TVA-A340\x00\x00',
b'39990-TWB-H120\x00\x00',
],
}, },
CAR.CIVIC: { CAR.CIVIC: {
(Ecu.programmedFuelInjection, 0x18da10f1, None): [ (Ecu.programmedFuelInjection, 0x18da10f1, None): [

@ -1,4 +1,5 @@
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import CanBusBase
from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams
# CAN bus layout with relay # CAN bus layout with relay
@ -8,15 +9,34 @@ from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_
# 3 = F-CAN A - OBDII port # 3 = F-CAN A - OBDII port
def get_pt_bus(car_fingerprint): class CanBus(CanBusBase):
return 1 if car_fingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) else 0 def __init__(self, CP=None, fingerprint=None) -> None:
# use fingerprint if specified
super().__init__(CP if fingerprint is None else None, fingerprint)
if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS):
self._pt, self._radar = self.offset + 1, self.offset
else:
self._pt, self._radar = self.offset, self.offset + 1
@property
def pt(self) -> int:
return self._pt
@property
def radar(self) -> int:
return self._radar
@property
def camera(self) -> int:
return self.offset + 2
def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False): def get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled=False):
no_radar = car_fingerprint in HONDA_BOSCH_RADARLESS no_radar = car_fingerprint in HONDA_BOSCH_RADARLESS
if radar_disabled or no_radar: if radar_disabled or no_radar:
# when radar is disabled, steering commands are sent directly to powertrain bus # when radar is disabled, steering commands are sent directly to powertrain bus
return get_pt_bus(car_fingerprint) return CAN.pt
# normally steering commands are sent to radar, which forwards them to powertrain bus # normally steering commands are sent to radar, which forwards them to powertrain bus
return 0 return 0
@ -26,7 +46,7 @@ def get_cruise_speed_conversion(car_fingerprint: str, is_metric: bool) -> float:
return CV.MPH_TO_MS if car_fingerprint in HONDA_BOSCH_RADARLESS and not is_metric else CV.KPH_TO_MS return CV.MPH_TO_MS if car_fingerprint in HONDA_BOSCH_RADARLESS and not is_metric else CV.KPH_TO_MS
def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake): def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake):
# TODO: do we loose pressure if we keep pump off for long? # TODO: do we loose pressure if we keep pump off for long?
brakelights = apply_brake > 0 brakelights = apply_brake > 0
brake_rq = apply_brake > 0 brake_rq = apply_brake > 0
@ -47,13 +67,11 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_
"AEB_REQ_2": 0, "AEB_REQ_2": 0,
"AEB_STATUS": 0, "AEB_STATUS": 0,
} }
bus = get_pt_bus(car_fingerprint) return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
return packer.make_can_msg("BRAKE_COMMAND", bus, values)
def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, car_fingerprint): def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
commands = [] commands = []
bus = get_pt_bus(car_fingerprint)
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0] min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
control_on = 5 if enabled else 0 control_on = 5 if enabled else 0
@ -90,37 +108,36 @@ def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, c
"SET_TO_75": 0x75, "SET_TO_75": 0x75,
"SET_TO_30": 0x30, "SET_TO_30": 0x30,
} }
commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values)) commands.append(packer.make_can_msg("ACC_CONTROL_ON", CAN.pt, acc_control_on_values))
commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values)) commands.append(packer.make_can_msg("ACC_CONTROL", CAN.pt, acc_control_values))
return commands return commands
def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, radar_disabled): def create_steering_control(packer, CAN, apply_steer, lkas_active, car_fingerprint, radar_disabled):
values = { values = {
"STEER_TORQUE": apply_steer if lkas_active else 0, "STEER_TORQUE": apply_steer if lkas_active else 0,
"STEER_TORQUE_REQUEST": lkas_active, "STEER_TORQUE_REQUEST": lkas_active,
} }
bus = get_lkas_cmd_bus(car_fingerprint, radar_disabled) bus = get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled)
return packer.make_can_msg("STEERING_CONTROL", bus, values) return packer.make_can_msg("STEERING_CONTROL", bus, values)
def create_bosch_supplemental_1(packer, car_fingerprint): def create_bosch_supplemental_1(packer, CAN, car_fingerprint):
# non-active params # non-active params
values = { values = {
"SET_ME_X04": 0x04, "SET_ME_X04": 0x04,
"SET_ME_X80": 0x80, "SET_ME_X80": 0x80,
"SET_ME_X10": 0x10, "SET_ME_X10": 0x10,
} }
bus = get_lkas_cmd_bus(car_fingerprint) bus = get_lkas_cmd_bus(CAN, car_fingerprint)
return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values) return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values)
def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud): def create_ui_commands(packer, CAN, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud):
commands = [] commands = []
bus_pt = get_pt_bus(CP.carFingerprint)
radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl
bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled) bus_lkas = get_lkas_cmd_bus(CAN, CP.carFingerprint, radar_disabled)
if CP.openpilotLongitudinalControl: if CP.openpilotLongitudinalControl:
acc_hud_values = { acc_hud_values = {
@ -144,7 +161,7 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
acc_hud_values['FCM_OFF_2'] = acc_hud['FCM_OFF_2'] acc_hud_values['FCM_OFF_2'] = acc_hud['FCM_OFF_2']
acc_hud_values['FCM_PROBLEM'] = acc_hud['FCM_PROBLEM'] acc_hud_values['FCM_PROBLEM'] = acc_hud['FCM_PROBLEM']
acc_hud_values['ICONS'] = acc_hud['ICONS'] acc_hud_values['ICONS'] = acc_hud['ICONS']
commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values)) commands.append(packer.make_can_msg("ACC_HUD", CAN.pt, acc_hud_values))
lkas_hud_values = { lkas_hud_values = {
'SET_ME_X41': 0x41, 'SET_ME_X41': 0x41,
@ -173,19 +190,19 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
'CMBS_OFF': 0x01, 'CMBS_OFF': 0x01,
'SET_TO_1': 0x01, 'SET_TO_1': 0x01,
} }
commands.append(packer.make_can_msg('RADAR_HUD', bus_pt, radar_hud_values)) commands.append(packer.make_can_msg('RADAR_HUD', CAN.pt, radar_hud_values))
if CP.carFingerprint == CAR.CIVIC_BOSCH: if CP.carFingerprint == CAR.CIVIC_BOSCH:
commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", bus_pt, {})) commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", CAN.pt, {}))
return commands return commands
def spam_buttons_command(packer, button_val, car_fingerprint): def spam_buttons_command(packer, CAN, button_val, car_fingerprint):
values = { values = {
'CRUISE_BUTTONS': button_val, 'CRUISE_BUTTONS': button_val,
'CRUISE_SETTING': 0, 'CRUISE_SETTING': 0,
} }
# send buttons to camera on radarless cars # send buttons to camera on radarless cars
bus = 2 if car_fingerprint in HONDA_BOSCH_RADARLESS else get_pt_bus(car_fingerprint) bus = CAN.camera if car_fingerprint in HONDA_BOSCH_RADARLESS else CAN.pt
return packer.make_can_msg("SCM_BUTTONS", bus, values) return packer.make_can_msg("SCM_BUTTONS", bus, values)

@ -3,7 +3,7 @@ from cereal import car
from panda import Panda from panda import Panda
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import interp from openpilot.common.numpy_fast import interp
from openpilot.selfdrive.car.honda.hondacan import get_pt_bus from openpilot.selfdrive.car.honda.hondacan import CanBus
from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \ from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \
HONDA_BOSCH_RADARLESS HONDA_BOSCH_RADARLESS
from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car import create_button_events, get_safety_config
@ -36,6 +36,8 @@ class CarInterface(CarInterfaceBase):
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "honda" ret.carName = "honda"
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(car.CarParams.SafetyModel.hondaBosch)]
ret.radarUnavailable = True ret.radarUnavailable = True
@ -47,20 +49,20 @@ class CarInterface(CarInterfaceBase):
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(car.CarParams.SafetyModel.hondaNidec)]
ret.enableGasInterceptor = 0x201 in fingerprint[0] ret.enableGasInterceptor = 0x201 in fingerprint[CAN.pt]
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
ret.pcmCruise = not ret.enableGasInterceptor ret.pcmCruise = not ret.enableGasInterceptor
if candidate == CAR.CRV_5G: if candidate == CAR.CRV_5G:
ret.enableBsm = 0x12f8bfa7 in fingerprint[0] ret.enableBsm = 0x12f8bfa7 in fingerprint[CAN.radar]
# Detect Bosch cars with new HUD msgs # Detect Bosch cars with new HUD msgs
if any(0x33DA in f for f in fingerprint.values()): if any(0x33DA in f for f in fingerprint.values()):
ret.flags |= HondaFlags.BOSCH_EXT_HUD.value ret.flags |= HondaFlags.BOSCH_EXT_HUD.value
# Accord 1.5T CVT has different gearbox message # Accord ICE 1.5T CVT has different gearbox message
if candidate == CAR.ACCORD and 0x191 in fingerprint[1]: if candidate == CAR.ACCORD and 0x191 in fingerprint[CAN.pt]:
ret.transmissionType = TransmissionType.cvt ret.transmissionType = TransmissionType.cvt
# Certain Hondas have an extra steering sensor at the bottom of the steering rack, # Certain Hondas have an extra steering sensor at the bottom of the steering rack,
@ -115,7 +117,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.ACCORD, CAR.ACCORDH): elif candidate == CAR.ACCORD:
ret.mass = 3279. * CV.LB_TO_KG ret.mass = 3279. * CV.LB_TO_KG
ret.wheelbase = 2.83 ret.wheelbase = 2.83
ret.centerToFront = ret.wheelbase * 0.39 ret.centerToFront = ret.wheelbase * 0.39
@ -276,7 +278,7 @@ class CarInterface(CarInterfaceBase):
raise ValueError(f"unsupported car {candidate}") raise ValueError(f"unsupported car {candidate}")
# These cars use alternate user brake msg (0x1BE) # These cars use alternate user brake msg (0x1BE)
if 0x1BE in fingerprint[get_pt_bus(candidate)] and candidate in HONDA_BOSCH: if 0x1BE in fingerprint[CAN.pt] and candidate in HONDA_BOSCH:
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE

@ -74,7 +74,6 @@ VISUAL_HUD = {
class CAR(StrEnum): class CAR(StrEnum):
ACCORD = "HONDA ACCORD 2018" ACCORD = "HONDA ACCORD 2018"
ACCORDH = "HONDA ACCORD HYBRID 2018"
CIVIC = "HONDA CIVIC 2016" CIVIC = "HONDA CIVIC 2016"
CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019"
CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019"
@ -119,8 +118,8 @@ CAR_INFO: dict[str, HondaCarInfo | list[HondaCarInfo] | None] = {
CAR.ACCORD: [ CAR.ACCORD: [
HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS), HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS),
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS), HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS),
HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
], ],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [ CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8",
@ -188,7 +187,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
[StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_REQUEST],
[StdQueries.UDS_VERSION_RESPONSE], [StdQueries.UDS_VERSION_RESPONSE],
bus=0, bus=0,
logging=True,
), ),
# Bosch PT bus # Bosch PT bus
Request( Request(
@ -201,12 +199,16 @@ FW_QUERY_CONFIG = FwQueryConfig(
# We lose these ECUs without the comma power on these cars. # We lose these ECUs without the comma power on these cars.
# Note that we still attempt to match with them when they are present # Note that we still attempt to match with them when they are present
non_essential_ecus={ non_essential_ecus={
Ecu.programmedFuelInjection: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.programmedFuelInjection: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.transmission: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.transmission: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.vsa: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.srs: [CAR.ACCORD],
Ecu.combinationMeter: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.eps: [CAR.ACCORD],
Ecu.gateway: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.vsa: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.electricBrakeBooster: [CAR.CIVIC_BOSCH, CAR.CRV_5G], Ecu.combinationMeter: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.gateway: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.electricBrakeBooster: [CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CRV_5G],
Ecu.shiftByWire: [CAR.ACCORD], # existence correlates with transmission type for ICE
Ecu.hud: [CAR.ACCORD], # existence correlates with trim level
}, },
extra_ecus=[ extra_ecus=[
# The only other ECU on PT bus accessible by camera on radarless Civic # The only other ECU on PT bus accessible by camera on radarless Civic
@ -217,7 +219,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
DBC = { DBC = {
CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None), CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACCORDH: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'), CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'),
CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None), CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None),
@ -250,6 +251,6 @@ STEER_THRESHOLD = {
HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY} HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY}
HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
CAR.PILOT, CAR.RIDGELINE} CAR.PILOT, CAR.RIDGELINE}
HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, HONDA_BOSCH = {CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G} CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G}
HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G} HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G}

@ -7,6 +7,7 @@ from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fau
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus 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
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@ -42,7 +43,7 @@ def process_hud_alert(enabled, fingerprint, hud_control):
return sys_warning, sys_state, left_lane_warning, right_lane_warning return sys_warning, sys_state, left_lane_warning, right_lane_warning
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.CAN = CanBus(CP) self.CAN = CanBus(CP)

@ -120,6 +120,7 @@ class CarInterfaceBase(ABC):
ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio
ret.minEnableSpeed = candidate.config.specs.minEnableSpeed ret.minEnableSpeed = candidate.config.specs.minEnableSpeed
ret.minSteerSpeed = candidate.config.specs.minSteerSpeed ret.minSteerSpeed = candidate.config.specs.minSteerSpeed
ret.flags |= int(candidate.config.flags)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs)
@ -135,7 +136,7 @@ class CarInterfaceBase(ABC):
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def _get_params(ret: car.CarParams, candidate: Platform, fingerprint: dict[int, dict[int, int]], def _get_params(ret: car.CarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool):
raise NotImplementedError raise NotImplementedError
@ -455,6 +456,15 @@ class CarStateBase(ABC):
return None return None
SendCan = tuple[int, int, bytes, int]
class CarControllerBase(ABC):
@abstractmethod
def update(self, CC, CS, now_nanos) -> tuple[car.CarControl.Actuators, list[SendCan]]:
pass
INTERFACE_ATTR_FILE = { INTERFACE_ATTR_FILE = {
"FINGERPRINTS": "fingerprints", "FINGERPRINTS": "fingerprints",
"FW_VERSIONS": "fingerprints", "FW_VERSIONS": "fingerprints",

@ -1,13 +1,14 @@
from cereal import car from cereal import car
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
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 = car.CarControl.HUDControl.VisualAlert
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0

@ -1,13 +1,14 @@
from cereal import car from cereal import car
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
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 = car.CarControl.HUDControl.VisualAlert
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.car_fingerprint = CP.carFingerprint self.car_fingerprint = CP.carFingerprint

@ -16,25 +16,13 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRatio = 17
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
ret.radarUnavailable = True ret.radarUnavailable = True
if candidate in (CAR.ROGUE, CAR.XTRAIL): if candidate == CAR.ALTIMA:
ret.mass = 1610
ret.wheelbase = 2.705
ret.centerToFront = ret.wheelbase * 0.44
elif candidate in (CAR.LEAF, CAR.LEAF_IC):
ret.mass = 1610
ret.wheelbase = 2.705
ret.centerToFront = ret.wheelbase * 0.44
elif candidate == CAR.ALTIMA:
# Altima has EPS on C-CAN unlike the others that have it on V-CAN # Altima has EPS on C-CAN unlike the others that have it on V-CAN
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_NISSAN_ALT_EPS_BUS ret.safetyConfigs[0].safetyParam |= Panda.FLAG_NISSAN_ALT_EPS_BUS
ret.mass = 1492
ret.wheelbase = 2.824
ret.centerToFront = ret.wheelbase * 0.44
return ret return ret

@ -1,9 +1,8 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import StrEnum
from cereal import car from cereal import car
from panda.python import uds from panda.python import uds
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts from openpilot.selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
@ -20,29 +19,52 @@ class CarControllerParams:
pass pass
class CAR(StrEnum):
XTRAIL = "NISSAN X-TRAIL 2017"
LEAF = "NISSAN LEAF 2018"
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
# Currently the only known difference between them is the inverted seatbelt signal.
LEAF_IC = "NISSAN LEAF 2018 Instrument Cluster"
ROGUE = "NISSAN ROGUE 2019"
ALTIMA = "NISSAN ALTIMA 2020"
@dataclass @dataclass
class NissanCarInfo(CarInfo): class NissanCarInfo(CarInfo):
package: str = "ProPILOT Assist" package: str = "ProPILOT Assist"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a])) car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a]))
CAR_INFO: dict[str, NissanCarInfo | list[NissanCarInfo] | None] = { @dataclass(frozen=True)
CAR.XTRAIL: NissanCarInfo("Nissan X-Trail 2017"), class NissanCarSpecs(CarSpecs):
CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), centerToFrontRatio: float = 0.44
CAR.LEAF_IC: None, # same platforms steerRatio: float = 17.
CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"),
CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])),
} @dataclass
class NissanPlaformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None))
class CAR(Platforms):
XTRAIL = NissanPlaformConfig(
"NISSAN X-TRAIL 2017",
NissanCarInfo("Nissan X-Trail 2017"),
specs=NissanCarSpecs(mass=1610, wheelbase=2.705)
)
LEAF = NissanPlaformConfig(
"NISSAN LEAF 2018",
NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"),
dbc_dict=dbc_dict('nissan_leaf_2018_generated', None),
specs=NissanCarSpecs(mass=1610, wheelbase=2.705)
)
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
# Currently the only known difference between them is the inverted seatbelt signal.
LEAF_IC = LEAF.override(platform_str="NISSAN LEAF 2018 Instrument Cluster", car_info=None)
ROGUE = NissanPlaformConfig(
"NISSAN ROGUE 2019",
NissanCarInfo("Nissan Rogue 2018-20"),
specs=NissanCarSpecs(mass=1610, wheelbase=2.705)
)
ALTIMA = NissanPlaformConfig(
"NISSAN ALTIMA 2020",
NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])),
specs=NissanCarSpecs(mass=1492, wheelbase=2.824)
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()
# Default diagnostic session # Default diagnostic session
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81]) NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81])
@ -88,11 +110,3 @@ FW_QUERY_CONFIG = FwQueryConfig(
), ),
]], ]],
) )
DBC = {
CAR.XTRAIL: dbc_dict('nissan_x_trail_2017_generated', None),
CAR.LEAF: dbc_dict('nissan_leaf_2018_generated', None),
CAR.LEAF_IC: dbc_dict('nissan_leaf_2018_generated', None),
CAR.ROGUE: dbc_dict('nissan_x_trail_2017_generated', None),
CAR.ALTIMA: dbc_dict('nissan_x_trail_2017_generated', None),
}

@ -1,9 +1,9 @@
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
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 from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.subaru import subarucan from openpilot.selfdrive.car.subaru import subarucan
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, STEER_RATE_LIMITED, \ from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags
CanBus, CarControllerParams, SubaruFlags
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and # FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and
# involves the total steering angle change rather than rate, but these limits work well for now # involves the total steering angle change rather than rate, but these limits work well for now
@ -11,7 +11,7 @@ MAX_STEER_RATE = 25 # deg/s
MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0
@ -42,12 +42,12 @@ class CarController:
if not CC.latActive: if not CC.latActive:
apply_steer = 0 apply_steer = 0
if self.CP.carFingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive)) can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive))
else: else:
apply_steer_req = CC.latActive apply_steer_req = CC.latActive
if self.CP.carFingerprint in STEER_RATE_LIMITED: if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED:
# Steering rate fault prevention # Steering rate fault prevention
self.steer_rate_counter, apply_steer_req = \ self.steer_rate_counter, apply_steer_req = \
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
@ -74,7 +74,7 @@ class CarController:
cruise_brake = CarControllerParams.BRAKE_MIN cruise_brake = CarControllerParams.BRAKE_MIN
# *** alerts and pcm cancel *** # *** alerts and pcm cancel ***
if self.CP.carFingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
if self.frame % 5 == 0: if self.frame % 5 == 0:
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep # 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
# disengage ACC when OP is disengaged # disengage ACC when OP is disengaged
@ -117,8 +117,8 @@ class CarController:
self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle)) self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle))
else: else:
if pcm_cancel_cmd: if pcm_cancel_cmd:
if self.CP.carFingerprint not in HYBRID_CARS: if not (self.CP.flags & SubaruFlags.HYBRID):
bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main bus = CanBus.alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else CanBus.main
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd)) can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd))
if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT: if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT:

@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, CanBus, SubaruFlags from openpilot.selfdrive.car.subaru.values import DBC, CanBus, SubaruFlags
from openpilot.selfdrive.car import CanSignalRateCalculator from openpilot.selfdrive.car import CanSignalRateCalculator
@ -19,17 +19,17 @@ class CarState(CarStateBase):
def update(self, cp, cp_cam, cp_body): def update(self, cp, cp_cam, cp_body):
ret = car.CarState.new_message() ret = car.CarState.new_message()
throttle_msg = cp.vl["Throttle"] if self.car_fingerprint not in HYBRID_CARS 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.
ret.gasPressed = ret.gas > 1e-5 ret.gasPressed = ret.gas > 1e-5
if self.car_fingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0 ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0
else: else:
cp_brakes = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp cp_brakes = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
ret.brakePressed = cp_brakes.vl["Brake_Status"]["Brake"] == 1 ret.brakePressed = cp_brakes.vl["Brake_Status"]["Brake"] == 1
cp_wheels = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp cp_wheels = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
ret.wheelSpeeds = self.get_wheel_speeds( ret.wheelSpeeds = self.get_wheel_speeds(
cp_wheels.vl["Wheel_Speeds"]["FL"], cp_wheels.vl["Wheel_Speeds"]["FL"],
cp_wheels.vl["Wheel_Speeds"]["FR"], cp_wheels.vl["Wheel_Speeds"]["FR"],
@ -48,24 +48,24 @@ class CarState(CarStateBase):
ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1) ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1)
ret.rightBlindspot = (cp.vl["BSD_RCTA"]["R_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["R_APPROACHING"] == 1) ret.rightBlindspot = (cp.vl["BSD_RCTA"]["R_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["R_APPROACHING"] == 1)
cp_transmission = cp_body if self.car_fingerprint in HYBRID_CARS else cp cp_transmission = cp_body if self.CP.flags & SubaruFlags.HYBRID else cp
can_gear = int(cp_transmission.vl["Transmission"]["Gear"]) can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
if self.car_fingerprint not in PREGLOBAL_CARS: if not (self.CP.flags & SubaruFlags.PREGLOBAL):
# ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it # ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"]) ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80 steer_threshold = 75 if self.CP.flags & SubaruFlags.PREGLOBAL else 80
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp cp_cruise = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
if self.car_fingerprint in HYBRID_CARS: if self.CP.flags & SubaruFlags.HYBRID:
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0 ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0 ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
else: else:
@ -73,8 +73,8 @@ class CarState(CarStateBase):
ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0 ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0
ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS
if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \ if (self.CP.flags & SubaruFlags.PREGLOBAL and cp.vl["Dash_State2"]["UNITS"] == 1) or \
(self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1): (not (self.CP.flags & SubaruFlags.PREGLOBAL) and cp.vl["Dashlights"]["UNITS"] == 1):
ret.cruiseState.speed *= CV.MPH_TO_KPH ret.cruiseState.speed *= CV.MPH_TO_KPH
ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1 ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1
@ -84,8 +84,8 @@ class CarState(CarStateBase):
cp.vl["BodyInfo"]["DOOR_OPEN_FL"]]) cp.vl["BodyInfo"]["DOOR_OPEN_FL"]])
ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1 ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1
cp_es_distance = cp_body if self.car_fingerprint in (GLOBAL_GEN2 | HYBRID_CARS) else cp_cam cp_es_distance = cp_body if self.CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID) else cp_cam
if self.car_fingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"] self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"] self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
else: else:
@ -96,12 +96,12 @@ class CarState(CarStateBase):
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2) (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"]) self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
cp_es_brake = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam cp_es_brake = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"]) self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"])
cp_es_status = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam cp_es_status = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
# TODO: Hybrid cars don't have ES_Distance, need a replacement # TODO: Hybrid cars don't have ES_Distance, need a replacement
if self.car_fingerprint not in HYBRID_CARS: if not (self.CP.flags & SubaruFlags.HYBRID):
# 8 is known AEB, there are a few other values related to AEB we ignore # 8 is known AEB, there are a few other values related to AEB we ignore
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \ ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0) (cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
@ -109,7 +109,7 @@ class CarState(CarStateBase):
self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"]) self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"]) self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
if self.car_fingerprint not in HYBRID_CARS: if not (self.CP.flags & SubaruFlags.HYBRID):
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"]) self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"]) self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
@ -125,7 +125,7 @@ class CarState(CarStateBase):
("Brake_Status", 50), ("Brake_Status", 50),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages.append(("CruiseControl", 20)) messages.append(("CruiseControl", 20))
return messages return messages
@ -136,7 +136,7 @@ class CarState(CarStateBase):
("ES_Brake", 20), ("ES_Brake", 20),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages += [ messages += [
("ES_Distance", 20), ("ES_Distance", 20),
("ES_Status", 20) ("ES_Status", 20)
@ -164,7 +164,7 @@ class CarState(CarStateBase):
("Brake_Pedal", 50), ("Brake_Pedal", 50),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages += [ messages += [
("Throttle", 100), ("Throttle", 100),
("Transmission", 100) ("Transmission", 100)
@ -173,8 +173,8 @@ class CarState(CarStateBase):
if CP.enableBsm: if CP.enableBsm:
messages.append(("BSD_RCTA", 17)) messages.append(("BSD_RCTA", 17))
if CP.carFingerprint not in PREGLOBAL_CARS: if not (CP.flags & SubaruFlags.PREGLOBAL):
if CP.carFingerprint not in GLOBAL_GEN2: if not (CP.flags & SubaruFlags.GLOBAL_GEN2):
messages += CarState.get_common_global_body_messages(CP) messages += CarState.get_common_global_body_messages(CP)
else: else:
messages += CarState.get_common_preglobal_body_messages() messages += CarState.get_common_preglobal_body_messages()
@ -183,7 +183,7 @@ class CarState(CarStateBase):
@staticmethod @staticmethod
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
if CP.carFingerprint in PREGLOBAL_CARS: if CP.flags & SubaruFlags.PREGLOBAL:
messages = [ messages = [
("ES_DashStatus", 20), ("ES_DashStatus", 20),
("ES_Distance", 20), ("ES_Distance", 20),
@ -194,7 +194,7 @@ class CarState(CarStateBase):
("ES_LKAS_State", 10), ("ES_LKAS_State", 10),
] ]
if CP.carFingerprint not in GLOBAL_GEN2: if not (CP.flags & SubaruFlags.GLOBAL_GEN2):
messages += CarState.get_common_global_es_messages(CP) messages += CarState.get_common_global_es_messages(CP)
if CP.flags & SubaruFlags.SEND_INFOTAINMENT: if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
@ -206,11 +206,11 @@ class CarState(CarStateBase):
def get_body_can_parser(CP): def get_body_can_parser(CP):
messages = [] messages = []
if CP.carFingerprint in GLOBAL_GEN2: if CP.flags & SubaruFlags.GLOBAL_GEN2:
messages += CarState.get_common_global_body_messages(CP) messages += CarState.get_common_global_body_messages(CP)
messages += CarState.get_common_global_es_messages(CP) messages += CarState.get_common_global_es_messages(CP)
if CP.carFingerprint in HYBRID_CARS: if CP.flags & SubaruFlags.HYBRID:
messages += [ messages += [
("Throttle_Hybrid", 40), ("Throttle_Hybrid", 40),
("Transmission", 100) ("Transmission", 100)

@ -1,42 +1,43 @@
from cereal import car 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
from openpilot.selfdrive.car.values import Platform
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, LKAS_ANGLE, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, SubaruFlags from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def _get_params(ret, candidate: Platform, fingerprint, car_fw, experimental_long, docs): def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs):
platform_flags = candidate.config.flags
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:
# - replacement for ES_Distance so we can cancel the cruise control # - replacement for ES_Distance so we can cancel the cruise control
# - to find the Cruise_Activated bit from the car # - to find the Cruise_Activated bit from the car
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) # - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc)
ret.dashcamOnly = candidate in (PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS) ret.dashcamOnly = bool(platform_flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
ret.autoResumeSng = False ret.autoResumeSng = False
# Detect infotainment message sent from the camera # Detect infotainment message sent from the camera
if candidate not in PREGLOBAL_CARS and 0x323 in fingerprint[2]: if not (platform_flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]:
ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
if candidate in PREGLOBAL_CARS: if platform_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(car.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(car.CarParams.SafetyModel.subaru)]
if candidate in GLOBAL_GEN2: if platform_flags & SubaruFlags.GLOBAL_GEN2:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2 ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
if candidate in LKAS_ANGLE: if platform_flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
else: else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@ -84,10 +85,12 @@ class CarInterface(CarInterfaceBase):
else: else:
raise ValueError(f"unknown car: {candidate}") raise ValueError(f"unknown car: {candidate}")
ret.experimentalLongitudinalAvailable = candidate not in (GLOBAL_GEN2 | PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS) LONG_UNAVAILABLE = SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL| SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID
ret.experimentalLongitudinalAvailable = not (platform_flags & LONG_UNAVAILABLE)
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
if candidate in GLOBAL_GEN2 and ret.openpilotLongitudinalControl: if platform_flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
if ret.openpilotLongitudinalControl: if ret.openpilotLongitudinalControl:

@ -19,7 +19,7 @@ class CarControllerParams:
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
self.STEER_DRIVER_FACTOR = 1 # from dbc self.STEER_DRIVER_FACTOR = 1 # from dbc
if CP.carFingerprint in GLOBAL_GEN2: if CP.flags & SubaruFlags.GLOBAL_GEN2:
self.STEER_MAX = 1000 self.STEER_MAX = 1000
self.STEER_DELTA_UP = 40 self.STEER_DELTA_UP = 40
self.STEER_DELTA_DOWN = 40 self.STEER_DELTA_DOWN = 40
@ -55,6 +55,14 @@ class CarControllerParams:
class SubaruFlags(IntFlag): class SubaruFlags(IntFlag):
SEND_INFOTAINMENT = 1 SEND_INFOTAINMENT = 1
DISABLE_EYESIGHT = 2 DISABLE_EYESIGHT = 2
GLOBAL_GEN2 = 4
# Cars that temporarily fault when steering angle rate is greater than some threshold.
# Appears to be all torque-based cars produced around 2019 - present
STEER_RATE_LIMITED = 8
PREGLOBAL = 16
HYBRID = 32
LKAS_ANGLE = 64
GLOBAL_ES_ADDR = 0x787 GLOBAL_ES_ADDR = 0x787
@ -89,10 +97,14 @@ class SubaruCarInfo(CarInfo):
self.footnotes.append(Footnote.EXP_LONG) self.footnotes.append(Footnote.EXP_LONG)
@dataclass(frozen=False) @dataclass
class SubaruPlatformConfig(PlatformConfig): class SubaruPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None))
def init(self):
if self.flags & SubaruFlags.HYBRID:
self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None)
class CAR(Platforms): class CAR(Platforms):
# Global platform # Global platform
@ -105,11 +117,13 @@ class CAR(Platforms):
"SUBARU OUTBACK 6TH GEN", "SUBARU OUTBACK 6TH GEN",
SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
) )
LEGACY = SubaruPlatformConfig( LEGACY = SubaruPlatformConfig(
"SUBARU LEGACY 7TH GEN", "SUBARU LEGACY 7TH GEN",
SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=OUTBACK.specs, specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
) )
IMPREZA = SubaruPlatformConfig( IMPREZA = SubaruPlatformConfig(
"SUBARU IMPREZA LIMITED 2019", "SUBARU IMPREZA LIMITED 2019",
@ -128,24 +142,26 @@ class CAR(Platforms):
SubaruCarInfo("Subaru XV 2020-21"), SubaruCarInfo("Subaru XV 2020-21"),
], ],
specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
) )
# TODO: is there an XV and Impreza too? # TODO: is there an XV and Impreza too?
CROSSTREK_HYBRID = SubaruPlatformConfig( CROSSTREK_HYBRID = SubaruPlatformConfig(
"SUBARU CROSSTREK HYBRID 2020", "SUBARU CROSSTREK HYBRID 2020",
SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])), SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
dbc_dict('subaru_global_2020_hybrid_generated', None),
specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.HYBRID,
) )
FORESTER = SubaruPlatformConfig( FORESTER = SubaruPlatformConfig(
"SUBARU FORESTER 2019", "SUBARU FORESTER 2019",
SubaruCarInfo("Subaru Forester 2019-21", "All"), SubaruCarInfo("Subaru Forester 2019-21", "All"),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
) )
FORESTER_HYBRID = SubaruPlatformConfig( FORESTER_HYBRID = SubaruPlatformConfig(
"SUBARU FORESTER HYBRID 2020", "SUBARU FORESTER HYBRID 2020",
SubaruCarInfo("Subaru Forester Hybrid 2020"), SubaruCarInfo("Subaru Forester Hybrid 2020"),
dbc_dict('subaru_global_2020_hybrid_generated', None),
specs=FORESTER.specs, specs=FORESTER.specs,
flags=SubaruFlags.HYBRID,
) )
# Pre-global # Pre-global
FORESTER_PREGLOBAL = SubaruPlatformConfig( FORESTER_PREGLOBAL = SubaruPlatformConfig(
@ -153,52 +169,50 @@ class CAR(Platforms):
SubaruCarInfo("Subaru Forester 2017-18"), SubaruCarInfo("Subaru Forester 2017-18"),
dbc_dict('subaru_forester_2017_generated', None), dbc_dict('subaru_forester_2017_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
flags=SubaruFlags.PREGLOBAL,
) )
LEGACY_PREGLOBAL = SubaruPlatformConfig( LEGACY_PREGLOBAL = SubaruPlatformConfig(
"SUBARU LEGACY 2015 - 2018", "SUBARU LEGACY 2015 - 2018",
SubaruCarInfo("Subaru Legacy 2015-18"), SubaruCarInfo("Subaru Legacy 2015-18"),
dbc_dict('subaru_outback_2015_generated', None), dbc_dict('subaru_outback_2015_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
flags=SubaruFlags.PREGLOBAL,
) )
OUTBACK_PREGLOBAL = SubaruPlatformConfig( OUTBACK_PREGLOBAL = SubaruPlatformConfig(
"SUBARU OUTBACK 2015 - 2017", "SUBARU OUTBACK 2015 - 2017",
SubaruCarInfo("Subaru Outback 2015-17"), SubaruCarInfo("Subaru Outback 2015-17"),
dbc_dict('subaru_outback_2015_generated', None), dbc_dict('subaru_outback_2015_generated', None),
specs=FORESTER_PREGLOBAL.specs, specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
) )
OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig( OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig(
"SUBARU OUTBACK 2018 - 2019", "SUBARU OUTBACK 2018 - 2019",
SubaruCarInfo("Subaru Outback 2018-19"), SubaruCarInfo("Subaru Outback 2018-19"),
dbc_dict('subaru_outback_2019_generated', None), dbc_dict('subaru_outback_2019_generated', None),
specs=FORESTER_PREGLOBAL.specs, specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
) )
# Angle LKAS # Angle LKAS
FORESTER_2022 = SubaruPlatformConfig( FORESTER_2022 = SubaruPlatformConfig(
"SUBARU FORESTER 2022", "SUBARU FORESTER 2022",
SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])), SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
specs=FORESTER.specs, specs=FORESTER.specs,
flags=SubaruFlags.LKAS_ANGLE,
) )
OUTBACK_2023 = SubaruPlatformConfig( OUTBACK_2023 = SubaruPlatformConfig(
"SUBARU OUTBACK 7TH GEN", "SUBARU OUTBACK 7TH GEN",
SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=OUTBACK.specs, specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE,
) )
ASCENT_2023 = SubaruPlatformConfig( ASCENT_2023 = SubaruPlatformConfig(
"SUBARU ASCENT 2023", "SUBARU ASCENT 2023",
SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=ASCENT.specs, specs=ASCENT.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE,
) )
LKAS_ANGLE = {CAR.FORESTER_2022, CAR.OUTBACK_2023, CAR.ASCENT_2023}
GLOBAL_GEN2 = {CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023, CAR.ASCENT_2023}
PREGLOBAL_CARS = {CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018}
HYBRID_CARS = {CAR.CROSSTREK_HYBRID, CAR.FORESTER_HYBRID}
# Cars that temporarily fault when steering angle rate is greater than some threshold.
# Appears to be all torque-based cars produced around 2019 - present
STEER_RATE_LIMITED = GLOBAL_GEN2 | {CAR.IMPREZA_2020, CAR.FORESTER}
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
@ -235,7 +249,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
# We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists # We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists
non_essential_ecus={ non_essential_ecus={
Ecu.eps: list(GLOBAL_GEN2), Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)),
} }
) )

@ -1,11 +1,12 @@
from openpilot.common.numpy_fast import clip from openpilot.common.numpy_fast import clip
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
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.tesla.teslacan import TeslaCAN from openpilot.selfdrive.car.tesla.teslacan import TeslaCAN
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, CarControllerParams from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, CarControllerParams
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.frame = 0 self.frame = 0

@ -82,8 +82,8 @@ routes = [
CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T
CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T
CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs
CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH), CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORD), # hybrid
CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORD), # hybrid, 2021 with new style HUD msgs
CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G), CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G),
CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY), CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY),
CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL), CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL),

@ -263,13 +263,13 @@ class TestFwFingerprintTiming(unittest.TestCase):
print(f'get_vin {name} case, query time={self.total_time / self.N} seconds') print(f'get_vin {name} case, query time={self.total_time / self.N} seconds')
def test_fw_query_timing(self): def test_fw_query_timing(self):
total_ref_time = {1: 6.5, 2: 7.4} total_ref_time = {1: 8.3, 2: 9.2}
brand_ref_times = { brand_ref_times = {
1: { 1: {
'gm': 0.5, 'gm': 1.0,
'body': 0.1, 'body': 0.1,
'chrysler': 0.3, 'chrysler': 0.3,
'ford': 0.2, 'ford': 1.5,
'honda': 0.55, 'honda': 0.55,
'hyundai': 1.05, 'hyundai': 1.05,
'mazda': 0.1, 'mazda': 0.1,
@ -280,7 +280,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
'volkswagen': 0.65, 'volkswagen': 0.65,
}, },
2: { 2: {
'ford': 0.3, 'ford': 1.6,
'hyundai': 1.85, 'hyundai': 1.85,
} }
} }

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:cadad21e1230729c70cffb4c46dd0a5dda4eec1a262b1bcd9f1b6b98265c20b5 oid sha256:0810a361ec5b5f5f9a2ee73b89ffb2df62ef40e8feff7e97ecb62f80fa53f6f5
size 125104 size 124950

@ -11,8 +11,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CHRYSLER PACIFICA HYBRID 2018" = [2.08887, 1.2943025830995154, 0.114818] "CHRYSLER PACIFICA HYBRID 2018" = [2.08887, 1.2943025830995154, 0.114818]
"CHRYSLER PACIFICA HYBRID 2019" = [1.90120, 1.1958788168371808, 0.131520] "CHRYSLER PACIFICA HYBRID 2019" = [1.90120, 1.1958788168371808, 0.131520]
"GENESIS G70 2018" = [3.8520195946707947, 2.354697063349854, 0.06830285485626221] "GENESIS G70 2018" = [3.8520195946707947, 2.354697063349854, 0.06830285485626221]
"HONDA ACCORD 2018" = [1.7135052593468778, 0.3461280068322071, 0.21579936052863807] "HONDA ACCORD 2018" = [1.6893333799149202, 0.3246749081720698, 0.2120497022936265]
"HONDA ACCORD HYBRID 2018" = [1.6651615004829625, 0.30322180951193245, 0.2083000440586149]
"HONDA CIVIC (BOSCH) 2019" = [1.691708637466905, 0.40132900729454185, 0.25460295304024094] "HONDA CIVIC (BOSCH) 2019" = [1.691708637466905, 0.40132900729454185, 0.25460295304024094]
"HONDA CIVIC 2016" = [1.6528895627785531, 0.4018518740819229, 0.25458812851328544] "HONDA CIVIC 2016" = [1.6528895627785531, 0.4018518740819229, 0.25458812851328544]
"HONDA CR-V 2016" = [0.7667141440182675, 0.5927571534745969, 0.40909087636157127] "HONDA CR-V 2016" = [0.7667141440182675, 0.5927571534745969, 0.40909087636157127]

@ -2,6 +2,7 @@ from cereal import car
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, \ from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, \
create_gas_interceptor_command, make_can_msg create_gas_interceptor_command, make_can_msg
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.toyota import toyotacan from openpilot.selfdrive.car.toyota import toyotacan
from openpilot.selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \ from openpilot.selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \
MIN_ACC_SPEED, PEDAL_TRANSITION, CarControllerParams, ToyotaFlags, \ MIN_ACC_SPEED, PEDAL_TRANSITION, CarControllerParams, ToyotaFlags, \
@ -25,7 +26,7 @@ MAX_LTA_ANGLE = 94.9461 # deg
MAX_LTA_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes MAX_LTA_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.params = CarControllerParams(self.CP) self.params = CarControllerParams(self.CP)

@ -573,6 +573,7 @@ FW_VERSIONS = {
b'\x018821F6201400\x00\x00\x00\x00', b'\x018821F6201400\x00\x00\x00\x00',
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F12010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
@ -843,6 +844,7 @@ FW_VERSIONS = {
b'8965B47023\x00\x00\x00\x00\x00\x00', b'8965B47023\x00\x00\x00\x00\x00\x00',
b'8965B47050\x00\x00\x00\x00\x00\x00', b'8965B47050\x00\x00\x00\x00\x00\x00',
b'8965B47060\x00\x00\x00\x00\x00\x00', b'8965B47060\x00\x00\x00\x00\x00\x00',
b'8965B47070\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.abs, 0x7b0, None): [ (Ecu.abs, 0x7b0, None): [
b'F152647290\x00\x00\x00\x00\x00\x00', b'F152647290\x00\x00\x00\x00\x00\x00',
@ -1024,6 +1026,7 @@ FW_VERSIONS = {
b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A13201\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',

@ -4,6 +4,7 @@ from openpilot.common.numpy_fast import clip
from openpilot.common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car import apply_driver_steer_torque_limits from openpilot.selfdrive.car import apply_driver_steer_torque_limits
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, PQ_CARS, CarControllerParams, VolkswagenFlags from openpilot.selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams, VolkswagenFlags
@ -11,7 +12,7 @@ VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
class CarController: class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.CCP = CarControllerParams(CP) self.CCP = CarControllerParams(CP)

@ -112,11 +112,11 @@ class CANBUS:
class VolkswagenFlags(IntFlag): class VolkswagenFlags(IntFlag):
STOCK_HCA_PRESENT = 1 STOCK_HCA_PRESENT = 1
@dataclass(frozen=False) @dataclass
class VolkswagenMQBPlatformConfig(PlatformConfig): class VolkswagenMQBPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None))
@dataclass(frozen=False) @dataclass
class VolkswagenPQPlatformConfig(PlatformConfig): class VolkswagenPQPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None))
@ -393,6 +393,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
obd_multiplexing=obd_multiplexing, obd_multiplexing=obd_multiplexing,
), ),
]], ]],
extra_ecus=[(Ecu.fwdCamera, 0x74f, None)],
) )
CAR_INFO = CAR.create_carinfo_map() CAR_INFO = CAR.create_carinfo_map()

@ -1,4 +1,4 @@
Import('env', 'envCython', 'arch') Import('env', 'envCython', 'arch', 'messaging_python', 'common_python', 'opendbc_python')
gen = "c_generated_code" gen = "c_generated_code"
@ -60,6 +60,7 @@ lenv.Clean(generated_files, Dir(gen))
generated_lat = lenv.Command(generated_files, generated_lat = lenv.Command(generated_files,
source_list, source_list,
f"cd {Dir('.').abspath} && python3 lat_mpc.py") f"cd {Dir('.').abspath} && python3 lat_mpc.py")
lenv.Depends(generated_lat, [messaging_python, common_python, opendbc_python])
lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES")

@ -1,4 +1,4 @@
Import('env', 'envCython', 'arch', 'messaging_python', 'common_python') Import('env', 'envCython', 'arch', 'messaging_python', 'common_python', 'opendbc_python')
gen = "c_generated_code" gen = "c_generated_code"
@ -66,7 +66,7 @@ lenv.Clean(generated_files, Dir(gen))
generated_long = lenv.Command(generated_files, generated_long = lenv.Command(generated_files,
source_list, source_list,
f"cd {Dir('.').abspath} && python3 long_mpc.py") f"cd {Dir('.').abspath} && python3 long_mpc.py")
lenv.Depends(generated_long, [messaging_python, common_python]) lenv.Depends(generated_long, [messaging_python, common_python, opendbc_python])
lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES")

@ -310,7 +310,7 @@ void Localizer::input_fake_gps_observations(double current_time) {
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) { void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) {
// ignore the message if the fix is invalid // ignore the message if the fix is invalid
bool gps_invalid_flag = (log.getFlags() % 2 == 0); bool gps_invalid_flag = (log.getFlags() % 2 == 0);
bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY); bool gps_unreasonable = (Vector2d(log.getHorizontalAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY);
bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0)); bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0));
bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK)); bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK));
bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK); bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK);
@ -331,7 +331,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector();
VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos;
float ecef_pos_std = std::sqrt(this->gps_variance_factor * std::pow(log.getAccuracy(), 2) + this->gps_vertical_variance_factor * std::pow(log.getVerticalAccuracy(), 2)); float ecef_pos_std = std::sqrt(this->gps_variance_factor * std::pow(log.getHorizontalAccuracy(), 2) + this->gps_vertical_variance_factor * std::pow(log.getVerticalAccuracy(), 2));
MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(this->gps_std_factor * ecef_pos_std, 2)).asDiagonal(); MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(this->gps_std_factor * ecef_pos_std, 2)).asDiagonal();
MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(this->gps_std_factor * log.getSpeedAccuracy(), 2)).asDiagonal(); MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(this->gps_std_factor * log.getSpeedAccuracy(), 2)).asDiagonal();
@ -691,10 +691,9 @@ int Localizer::locationd_thread() {
this->configure_gnss_source(source); this->configure_gnss_source(source);
const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration",
"carState", "carParams", "accelerometer", "gyroscope"}; "carState", "accelerometer", "gyroscope"};
// TODO: remove carParams once we're always sending at 100Hz SubMaster sm(service_list, {}, nullptr, {gps_location_socket});
SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"});
PubMaster pm({"liveLocationKalman"}); PubMaster pm({"liveLocationKalman"});
uint64_t cnt = 0; uint64_t cnt = 0;
@ -718,8 +717,7 @@ int Localizer::locationd_thread() {
filterInitialized = sm.allAliveAndValid(); filterInitialized = sm.allAliveAndValid();
} }
// 100Hz publish for notcars, 20Hz for cars const char* trigger_msg = "cameraOdometry";
const char* trigger_msg = sm["carParams"].getCarParams().getNotCar() ? "accelerometer" : "cameraOdometry";
if (sm.updated(trigger_msg)) { if (sm.updated(trigger_msg)) {
bool inputsOK = sm.allValid() && this->are_inputs_ok(); bool inputsOK = sm.allValid() && this->are_inputs_ok();
bool gpsOK = this->is_gps_ok(); bool gpsOK = this->is_gps_ok();

@ -512,7 +512,7 @@ CONFIGS = [
proc_name="locationd", proc_name="locationd",
pubs=[ pubs=[
"cameraOdometry", "accelerometer", "gyroscope", "gpsLocationExternal", "cameraOdometry", "accelerometer", "gyroscope", "gpsLocationExternal",
"liveCalibration", "carState", "carParams", "gpsLocation" "liveCalibration", "carState", "gpsLocation"
], ],
subs=["liveLocationKalman"], subs=["liveLocationKalman"],
ignore=["logMonoTime"], ignore=["logMonoTime"],

@ -1 +1 @@
d0cdea7eb15f3cac8a921f7ace3eaa6baebb4fd5 b9d29ac9402cfc04bf3e48867415efa70c144029

@ -424,4 +424,4 @@ class TestOnroad(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
pytest.main() unittest.main()

@ -20,7 +20,7 @@
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
const std::string USER_AGENT = "AGNOSSetup-"; const std::string USER_AGENT = "AGNOSSetup-";
const QString TEST_URL = "https://openpilot.comma.ai"; const QString OPENPILOT_URL = "https://openpilot.comma.ai";
bool is_elf(char *fname) { bool is_elf(char *fname) {
FILE *fp = fopen(fname, "rb"); FILE *fp = fopen(fname, "rb");
@ -201,20 +201,7 @@ QWidget * Setup::network_setup() {
QPushButton *cont = new QPushButton(); QPushButton *cont = new QPushButton();
cont->setObjectName("navBtn"); cont->setObjectName("navBtn");
cont->setProperty("primary", true); cont->setProperty("primary", true);
QObject::connect(cont, &QPushButton::clicked, [=]() { QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
auto w = currentWidget();
QTimer::singleShot(0, [=]() {
setCurrentWidget(downloading_widget);
});
QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
if (!url.isEmpty()) {
QTimer::singleShot(1000, this, [=]() {
download(url);
});
} else {
setCurrentWidget(w);
}
});
blayout->addWidget(cont); blayout->addWidget(cont);
// setup timer for testing internet connection // setup timer for testing internet connection
@ -229,11 +216,11 @@ QWidget * Setup::network_setup() {
} }
repaint(); repaint();
}); });
request->sendRequest(TEST_URL); request->sendRequest(OPENPILOT_URL);
QTimer *timer = new QTimer(this); QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, [=]() { QObject::connect(timer, &QTimer::timeout, [=]() {
if (!request->active() && cont->isVisible()) { if (!request->active() && cont->isVisible()) {
request->sendRequest(TEST_URL); request->sendRequest(OPENPILOT_URL);
} }
}); });
timer->start(1000); timer->start(1000);
@ -241,6 +228,106 @@ QWidget * Setup::network_setup() {
return widget; return widget;
} }
QWidget * radio_button(QString title, QButtonGroup *group) {
QPushButton *btn = new QPushButton(title);
btn->setCheckable(true);
group->addButton(btn);
btn->setStyleSheet(R"(
QPushButton {
height: 230;
padding-left: 100px;
padding-right: 100px;
text-align: left;
font-size: 80px;
font-weight: 400;
border-radius: 10px;
background-color: #4F4F4F;
}
QPushButton:checked {
background-color: #465BEA;
}
)");
// checkmark icon
QPixmap pix(":/img_circled_check.svg");
btn->setIcon(pix);
btn->setIconSize(QSize(0, 0));
btn->setLayoutDirection(Qt::RightToLeft);
QObject::connect(btn, &QPushButton::toggled, [=](bool checked) {
btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0));
});
return btn;
}
QWidget * Setup::software_selection() {
QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setContentsMargins(55, 50, 55, 50);
main_layout->setSpacing(0);
// title
QLabel *title = new QLabel(tr("Choose Software to Install"));
title->setStyleSheet("font-size: 90px; font-weight: 500;");
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addSpacing(50);
// openpilot + custom radio buttons
QButtonGroup *group = new QButtonGroup(widget);
group->setExclusive(true);
QWidget *openpilot = radio_button(tr("openpilot"), group);
main_layout->addWidget(openpilot);
main_layout->addSpacing(30);
QWidget *custom = radio_button(tr("Custom Software"), group);
main_layout->addWidget(custom);
main_layout->addStretch();
// back + continue buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *back = new QPushButton(tr("Back"));
back->setObjectName("navBtn");
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
blayout->addWidget(back);
QPushButton *cont = new QPushButton(tr("Continue"));
cont->setObjectName("navBtn");
cont->setEnabled(false);
cont->setProperty("primary", true);
blayout->addWidget(cont);
QObject::connect(cont, &QPushButton::clicked, [=]() {
auto w = currentWidget();
QTimer::singleShot(0, [=]() {
setCurrentWidget(downloading_widget);
});
QString url = OPENPILOT_URL;
if (group->checkedButton() != openpilot) {
url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
}
if (!url.isEmpty()) {
QTimer::singleShot(1000, this, [=]() {
download(url);
});
} else {
setCurrentWidget(w);
}
});
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
btn->setChecked(true);
cont->setEnabled(true);
});
return widget;
}
QWidget * Setup::downloading() { QWidget * Setup::downloading() {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
@ -326,6 +413,7 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
addWidget(getting_started()); addWidget(getting_started());
addWidget(network_setup()); addWidget(network_setup());
addWidget(software_selection());
downloading_widget = downloading(); downloading_widget = downloading();
addWidget(downloading_widget); addWidget(downloading_widget);

@ -17,6 +17,7 @@ private:
QWidget *low_voltage(); QWidget *low_voltage();
QWidget *getting_started(); QWidget *getting_started();
QWidget *network_setup(); QWidget *network_setup();
QWidget *software_selection();
QWidget *downloading(); QWidget *downloading();
QWidget *download_failed(QLabel *url, QLabel *body); QWidget *download_failed(QLabel *url, QLabel *body);

@ -762,6 +762,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation>اختر لغة</translation> <translation>اختر لغة</translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -744,6 +744,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation>Sprache wählen</translation> <translation>Sprache wählen</translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -746,6 +746,18 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Select a language</source> <source>Select a language</source>
<translation>Choisir une langue</translation> <translation>Choisir une langue</translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -740,6 +740,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation> </translation> <translation> </translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -638,7 +638,7 @@ Isso pode levar até um minuto.</translation>
</message> </message>
<message> <message>
<source>System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot.</source> <source>System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot.</source>
<translation type="unfinished">Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização.</translation> <translation>Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -746,6 +746,18 @@ Isso pode levar até um minuto.</translation>
<source>Select a language</source> <source>Select a language</source>
<translation>Selecione o Idioma</translation> <translation>Selecione o Idioma</translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation>Escolha o Software a ser Instalado</translation>
</message>
<message>
<source>openpilot</source>
<translation>openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation>Software Customizado</translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -740,6 +740,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation>Dil seçin</translation> <translation>Dil seçin</translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -25,4 +25,4 @@ class TestHardware(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
pytest.main() unittest.main()

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import defaultdict, deque from collections import defaultdict, deque
import sys
import pytest import pytest
import unittest import unittest
import time import time
@ -132,4 +131,4 @@ class TestPowerDraw(unittest.TestCase):
if __name__ == "__main__": if __name__ == "__main__":
pytest.main(sys.argv) unittest.main()

@ -13,7 +13,7 @@ if __name__ == "__main__":
cnos = [] cnos = []
for m in ug.measurementReport.measurements: for m in ug.measurementReport.measurements:
cnos.append(m.cno) cnos.append(m.cno)
print("Sats: %d Accuracy: %.2f m cnos" % (ug.measurementReport.numMeas, gle.accuracy), sorted(cnos)) print("Sats: %d Accuracy: %.2f m cnos" % (ug.measurementReport.numMeas, gle.horizontalAccuracy), sorted(cnos))
except Exception: except Exception:
pass pass
sm.update() sm.update()

@ -132,7 +132,7 @@ kj::Array<capnp::word> UbloxMsgParser::gen_nav_pvt(ubx_t::nav_pvt_t *msg) {
gpsLoc.setAltitude(msg->height() * 1e-03); gpsLoc.setAltitude(msg->height() * 1e-03);
gpsLoc.setSpeed(msg->g_speed() * 1e-03); gpsLoc.setSpeed(msg->g_speed() * 1e-03);
gpsLoc.setBearingDeg(msg->head_mot() * 1e-5); gpsLoc.setBearingDeg(msg->head_mot() * 1e-5);
gpsLoc.setAccuracy(msg->h_acc() * 1e-03); gpsLoc.setHorizontalAccuracy(msg->h_acc() * 1e-03);
std::tm timeinfo = std::tm(); std::tm timeinfo = std::tm();
timeinfo.tm_year = msg->year() - 1900; timeinfo.tm_year = msg->year() - 1900;
timeinfo.tm_mon = msg->month() - 1; timeinfo.tm_mon = msg->month() - 1;

@ -1,3 +1,4 @@
import pytest
import unittest import unittest
import requests import requests
@ -6,6 +7,7 @@ from openpilot.tools.lib.logreader import LogReader
from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.route import SegmentRange
@pytest.mark.skip(reason="huggingface is flaky, run this test manually to check for issues")
class TestCommaCarSegments(unittest.TestCase): class TestCommaCarSegments(unittest.TestCase):
def test_database(self): def test_database(self):
database = get_comma_car_segments_database() database = get_comma_car_segments_database()

@ -32,7 +32,7 @@
<plot mode="TimeSeries" style="Lines" flip_y="false" flip_x="false"> <plot mode="TimeSeries" style="Lines" flip_y="false" flip_x="false">
<range bottom="-0.449385" right="196.811937" left="76.646983" top="7.160833"/> <range bottom="-0.449385" right="196.811937" left="76.646983" top="7.160833"/>
<limitY/> <limitY/>
<curve name="/gpsLocationExternal/accuracy" color="#ff7f0e"/> <curve name="/gpsLocationExternal/horizontalAccuracy" color="#ff7f0e"/>
<curve name="/gpsLocationExternal/verticalAccuracy" color="#f14cc1"/> <curve name="/gpsLocationExternal/verticalAccuracy" color="#f14cc1"/>
<curve name="/gpsLocationExternal/speedAccuracy" color="#9467bd"/> <curve name="/gpsLocationExternal/speedAccuracy" color="#9467bd"/>
</plot> </plot>

@ -8,7 +8,7 @@
<plot style="Lines" flip_x="false" flip_y="false" mode="TimeSeries"> <plot style="Lines" flip_x="false" flip_y="false" mode="TimeSeries">
<range right="134.825489" top="4402341.574525" bottom="-107369.555525" left="0.000000"/> <range right="134.825489" top="4402341.574525" bottom="-107369.555525" left="0.000000"/>
<limitY/> <limitY/>
<curve name="/gpsLocationExternal/accuracy" color="#1f77b4"/> <curve name="/gpsLocationExternal/horizontalAccuracy" color="#1f77b4"/>
</plot> </plot>
</DockArea> </DockArea>
<DockArea name="..."> <DockArea name="...">

@ -55,7 +55,7 @@ class SimulatedSensors:
dat.gpsLocationExternal = { dat.gpsLocationExternal = {
"unixTimestampMillis": int(time.time() * 1000), "unixTimestampMillis": int(time.time() * 1000),
"flags": 1, # valid fix "flags": 1, # valid fix
"accuracy": 1.0, "horizontalAccuracy": 1.0,
"verticalAccuracy": 1.0, "verticalAccuracy": 1.0,
"speedAccuracy": 0.1, "speedAccuracy": 0.1,
"bearingAccuracyDeg": 0.1, "bearingAccuracyDeg": 0.1,

Loading…
Cancel
Save