Merge remote-tracking branch 'commaai/master' into ford-platform-codes

pull/31124/head
Cameron Clough 1 year ago
commit a4de571fd9
  1. 2
      .devcontainer/Dockerfile
  2. 6
      .devcontainer/devcontainer.json
  3. 5
      RELEASES.md
  4. 5
      SConstruct
  5. 2
      cereal
  6. 1
      common/params.cc
  7. 8
      docs/CARS.md
  8. 2
      opendbc
  9. 2
      panda
  10. 4
      pyproject.toml
  11. 2
      rednose_repo
  12. 7
      release/files_common
  13. 4
      scripts/cell.sh
  14. 2
      scripts/stop_updater.sh
  15. 13
      selfdrive/boardd/panda.cc
  16. 1
      selfdrive/car/CARS_template.md
  17. 39
      selfdrive/car/__init__.py
  18. 4
      selfdrive/car/body/interface.py
  19. 4
      selfdrive/car/body/values.py
  20. 24
      selfdrive/car/car_helpers.py
  21. 142
      selfdrive/car/card.py
  22. 3
      selfdrive/car/chrysler/fingerprints.py
  23. 24
      selfdrive/car/chrysler/values.py
  24. 8
      selfdrive/car/docs.py
  25. 6
      selfdrive/car/ford/carcontroller.py
  26. 21
      selfdrive/car/ford/carstate.py
  27. 11
      selfdrive/car/ford/interface.py
  28. 47
      selfdrive/car/ford/values.py
  29. 5
      selfdrive/car/fw_query_definitions.py
  30. 54
      selfdrive/car/fw_versions.py
  31. 7
      selfdrive/car/gm/interface.py
  32. 34
      selfdrive/car/gm/values.py
  33. 1
      selfdrive/car/honda/fingerprints.py
  34. 89
      selfdrive/car/honda/interface.py
  35. 310
      selfdrive/car/honda/values.py
  36. 6
      selfdrive/car/hyundai/carcontroller.py
  37. 63
      selfdrive/car/hyundai/fingerprints.py
  38. 28
      selfdrive/car/hyundai/hyundaican.py
  39. 191
      selfdrive/car/hyundai/interface.py
  40. 17
      selfdrive/car/hyundai/tests/test_hyundai.py
  41. 881
      selfdrive/car/hyundai/values.py
  42. 18
      selfdrive/car/interfaces.py
  43. 6
      selfdrive/car/mazda/carcontroller.py
  44. 6
      selfdrive/car/mazda/carstate.py
  45. 18
      selfdrive/car/mazda/interface.py
  46. 10
      selfdrive/car/mazda/mazdacan.py
  47. 83
      selfdrive/car/mazda/values.py
  48. 18
      selfdrive/car/mock/values.py
  49. 11
      selfdrive/car/nissan/values.py
  50. 23
      selfdrive/car/subaru/interface.py
  51. 60
      selfdrive/car/subaru/values.py
  52. 30
      selfdrive/car/tesla/carstate.py
  53. 11
      selfdrive/car/tesla/fingerprints.py
  54. 17
      selfdrive/car/tesla/interface.py
  55. 62
      selfdrive/car/tesla/radar_interface.py
  56. 49
      selfdrive/car/tesla/values.py
  57. 2
      selfdrive/car/tests/routes.py
  58. 2
      selfdrive/car/tests/test_car_interfaces.py
  59. 7
      selfdrive/car/tests/test_docs.py
  60. 5
      selfdrive/car/tests/test_fw_fingerprint.py
  61. 6
      selfdrive/car/tests/test_models.py
  62. 23
      selfdrive/car/tests/test_platform_configs.py
  63. 2
      selfdrive/car/torque_data/override.toml
  64. 16
      selfdrive/car/toyota/carstate.py
  65. 7
      selfdrive/car/toyota/fingerprints.py
  66. 161
      selfdrive/car/toyota/interface.py
  67. 526
      selfdrive/car/toyota/values.py
  68. 13
      selfdrive/car/values.py
  69. 4
      selfdrive/car/volkswagen/carcontroller.py
  70. 36
      selfdrive/car/volkswagen/carstate.py
  71. 13
      selfdrive/car/volkswagen/fingerprints.py
  72. 13
      selfdrive/car/volkswagen/interface.py
  73. 77
      selfdrive/car/volkswagen/values.py
  74. 152
      selfdrive/controls/controlsd.py
  75. 5
      selfdrive/controls/tests/test_state_machine.py
  76. 3
      selfdrive/debug/hyundai_enable_radar_points.py
  77. 1
      selfdrive/debug/run_process_on_route.py
  78. 8
      selfdrive/locationd/torqued.py
  79. 2
      selfdrive/manager/process_config.py
  80. 2
      selfdrive/modeld/models/supercombo.onnx
  81. 40
      selfdrive/test/helpers.py
  82. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  83. 2
      selfdrive/test/process_replay/ref_commit
  84. 3
      selfdrive/test/process_replay/test_processes.py
  85. 2
      selfdrive/test/test_onroad.py
  86. 4
      selfdrive/ui/qt/offroad/software_settings.cc
  87. 77
      selfdrive/ui/ui.py
  88. 172
      selfdrive/updated/tests/test_updated.py
  89. 0
      selfdrive/updated/updated.py
  90. 6
      system/camerad/cameras/camera_common.cc
  91. 1
      system/hardware/tici/amplifier.py
  92. 24
      system/hardware/tici/hardware.py
  93. 7
      system/loggerd/encoder/v4l_encoder.cc
  94. 11
      system/loggerd/logger.cc
  95. 1
      system/loggerd/logger.h
  96. 4
      system/loggerd/tests/loggerd_tests_common.py
  97. 14
      system/webrtc/tests/test_webrtcd.py
  98. 41
      system/webrtc/webrtcd.py
  99. 2
      teleoprtc_repo
  100. 6
      tools/cabana/dbc/generate_dbc_json.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,6 +1,6 @@
FROM ghcr.io/commaai/openpilot-base:latest
RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa libegl1-mesa
RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa libegl1-mesa gdb
RUN pip install ipython jupyter jupyterlab
RUN cd /tmp && \

@ -18,7 +18,6 @@
"--volume=${localWorkspaceFolder}/.devcontainer/.host/.Xauthority:/home/batman/.Xauthority",
"--volume=${localEnv:HOME}/.comma:/home/batman/.comma",
"--volume=/tmp/comma_download_cache:/tmp/comma_download_cache",
"--volume=/tmp/devcontainer_scons_cache:/tmp/scons_cache",
"--shm-size=1G",
"--add-host=host.docker.internal:host-gateway", // required to use host.docker.internal on linux
"--publish=0.0.0.0:8070-8079:8070-8079" // body ZMQ services
@ -43,5 +42,8 @@
"lharri73.dbc"
]
}
}
},
"mounts": [
"type=volume,source=scons_cache,target=/tmp/scons_cache"
]
}

@ -1,7 +1,7 @@
Version 0.9.7 (2024-XX-XX)
========================
* New driving model
* Support for many hybrid Ford models
* Support for hybrid variants of supported Ford models
Version 0.9.6 (2024-02-27)
========================
@ -11,6 +11,9 @@ Version 0.9.6 (2024-02-27)
* Directly outputs curvature for lateral control
* New driver monitoring model
* Trained on larger dataset
* Model path UI
* Shows where driving model wants to be
* Shows what model is seeing more clearly, but more jittery
* AGNOS 9
* comma body streaming and controls over WebRTC
* Improved fuzzy fingerprinting for many makes and models

@ -386,9 +386,10 @@ SConscript(['third_party/SConscript'])
SConscript(['selfdrive/SConscript'])
if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'):
if Dir('#tools/cabana/').exists() and GetOption('extras'):
SConscript(['tools/replay/SConscript'])
SConscript(['tools/cabana/SConscript'])
if arch != "larch64":
SConscript(['tools/cabana/SConscript'])
external_sconscript = GetOption('external_sconscript')
if external_sconscript:

@ -1 +1 @@
Subproject commit bfbb0cab83e3ad49d85ad1a34ee1241bf1ff782f
Subproject commit c5c2a60f1aa796e7de464015349db3c336b79220

@ -188,6 +188,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RecordFront", PERSISTENT},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"RouteCount", PERSISTENT},
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"SshEnabled", PERSISTENT},
{"TermsVersion", PERSISTENT},

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 288 Supported Cars
# 289 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -143,6 +143,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro EV 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro EV 2022">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2018|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2018">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2021">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2022">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
@ -239,9 +240,9 @@ A supported vehicle is one that just works when you install a comma device. All
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Corolla Hatchback 2019-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Corolla Hybrid 2020-22">Buy Here</a></sub></details>||
|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Corolla Hybrid (Non-US only) 2020-23">Buy Here</a></sub></details>||
|Toyota|Highlander 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|Highlander 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander 2020-23">Buy Here</a></sub></details>||
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander Hybrid 2017-19">Buy Here</a></sub></details>||
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander Hybrid 2017-19">Buy Here</a></sub></details>||
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Highlander Hybrid 2020-23">Buy Here</a></sub></details>||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Mirai 2021">Buy Here</a></sub></details>||
|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Prius 2016">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@ -352,6 +353,7 @@ openpilot does not yet support these Toyota models due to a new message authenti
* Toyota Venza 2021+
* Toyota Sequoia 2023+
* Toyota Tundra 2022+
* Toyota Highlander 2024+
* Toyota Corolla Cross 2022+ (only US model)
* Lexus NX 2022+
* Toyota bZ4x 2023+

@ -1 +1 @@
Subproject commit 5f096db742b0c5dcd19976afdb07d5dd098f4b07
Subproject commit ff1f1ff335261c469635c57c81817afd04663eab

@ -1 +1 @@
Subproject commit ea156f7c628a371bea9a15a29f9068d5392534ba
Subproject commit 41e9610ff841e4cf62051c6df09c1870f5d12477

@ -21,6 +21,7 @@ testpaths = [
"selfdrive/thermald",
"selfdrive/test/longitudinal_maneuvers",
"selfdrive/test/process_replay/test_fuzzy.py",
"selfdrive/updated",
"system/camerad",
"system/hardware/tici",
"system/loggerd",
@ -170,6 +171,7 @@ build-backend = "poetry.core.masonry.api"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff]
indent-width = 2
lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"]
lint.ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"]
line-length = 160
@ -194,3 +196,5 @@ lint.flake8-implicit-str-concat.allow-multiline=false
[tool.coverage.run]
concurrency = ["multiprocessing", "thread"]
[tool.ruff.format]
quote-style = "preserve"

@ -1 +1 @@
Subproject commit c5762e8bc6f7338c7a30d2cd1cba8cc64e81ba19
Subproject commit 1dc61a60e684b4bc8c591a8bce7e24e02aa8f400

@ -53,9 +53,10 @@ tools/replay/*.h
selfdrive/__init__.py
selfdrive/sentry.py
selfdrive/tombstoned.py
selfdrive/updated.py
selfdrive/statsd.py
selfdrive/updated/*
system/logmessaged.py
system/micd.py
system/version.py
@ -86,6 +87,7 @@ selfdrive/boardd/pandad.py
selfdrive/boardd/tests/test_boardd_loopback.py
selfdrive/car/__init__.py
selfdrive/car/card.py
selfdrive/car/docs_definitions.py
selfdrive/car/car_helpers.py
selfdrive/car/fingerprints.py
@ -540,7 +542,8 @@ opendbc/vw_golf_mk4.dbc
opendbc/vw_mqb_2010.dbc
opendbc/tesla_can.dbc
opendbc/tesla_radar.dbc
opendbc/tesla_radar_bosch_generated.dbc
opendbc/tesla_radar_continental_generated.dbc
opendbc/tesla_powertrain.dbc
tinygrad_repo/openpilot/compile2.py

@ -1,3 +1,3 @@
#!/usr/bin/bash
nmcli connection modify --temporary lte ipv4.route-metric 1 ipv6.route-metric 1
nmcli con up lte
nmcli connection modify --temporary esim ipv4.route-metric 1 ipv6.route-metric 1
nmcli con up esim

@ -1,7 +1,7 @@
#!/usr/bin/env sh
# Stop updater
pkill -2 -f selfdrive.updated
pkill -2 -f selfdrive.updated.updated
# Remove pending update
rm -f /data/safe_staging/finalized/.overlay_consistent

@ -284,6 +284,13 @@ bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector<can_fra
break;
}
if (calculate_checksum(&data[pos], sizeof(can_header) + data_len) != 0) {
// TODO: also reset CAN comms?
LOGE("Panda CAN checksum failed");
size = 0;
return false;
}
can_frame &canData = out_vec.emplace_back();
canData.busTime = 0;
canData.address = header.addr;
@ -295,12 +302,6 @@ bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector<can_fra
canData.src += CAN_RETURNED_BUS_OFFSET;
}
if (calculate_checksum(&data[pos], sizeof(can_header) + data_len) != 0) {
LOGE("Panda CAN checksum failed");
size = 0;
return false;
}
canData.dat.assign((char *)&data[pos + sizeof(can_header)], data_len);
pos += sizeof(can_header) + data_len;

@ -65,6 +65,7 @@ openpilot does not yet support these Toyota models due to a new message authenti
* Toyota Venza 2021+
* Toyota Sequoia 2023+
* Toyota Tundra 2022+
* Toyota Highlander 2024+
* Toyota Corolla Cross 2022+ (only US model)
* Lexus NX 2022+
* Toyota bZ4x 2023+

@ -1,5 +1,5 @@
# functions common among cars
from collections import namedtuple
from collections import defaultdict, namedtuple
from dataclasses import dataclass
from enum import IntFlag, ReprEnum
from dataclasses import replace
@ -245,27 +245,32 @@ class CanSignalRateCalculator:
return self.rate
CarInfos = CarInfo | list[CarInfo]
CarInfos = CarInfo | list[CarInfo] | None
@dataclass(frozen=True, kw_only=True)
class CarSpecs:
mass: float # kg, curb weight
wheelbase: float
wheelbase: float # meters
steerRatio: float
centerToFrontRatio: float = 0.5
minSteerSpeed: float = 0.0
minEnableSpeed: float = -1.0
minSteerSpeed: float = 0.0 # m/s
minEnableSpeed: float = -1.0 # m/s
tireStiffnessFactor: float = 1.0
def override(self, **kwargs):
return replace(self, **kwargs)
@dataclass(order=True)
class PlatformConfig(Freezable):
platform_str: str
car_info: CarInfos
specs: CarSpecs
dbc_dict: DbcDict
flags: int = 0
specs: CarSpecs | None = None
flags: int = 0
def __hash__(self) -> int:
return hash(self.platform_str)
@ -294,10 +299,22 @@ class Platforms(str, ReprEnum):
def create_dbc_map(cls) -> dict[str, DbcDict]:
return {p: p.config.dbc_dict for p in cls}
@classmethod
def create_carinfo_map(cls) -> dict[str, CarInfos]:
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}
@classmethod
def without_flags(cls, flags: IntFlag) -> set['Platforms']:
return {p for p in cls if not (p.config.flags & flags)}
@classmethod
def print_debug(cls, flags):
platforms_with_flag = defaultdict(list)
for flag in flags:
for platform in cls:
if platform.config.flags & flag:
assert flag.name is not None
platforms_with_flag[flag.name].append(platform)
for flag, platforms in platforms_with_flag.items():
print(f"{flag:32s}: {', '.join(p.name for p in platforms)}")

@ -14,14 +14,10 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = -math.inf
ret.maxLateralAccel = math.inf # TODO: set to a reasonable value
ret.steerRatio = 0.5
ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.
ret.mass = 9
ret.wheelbase = 0.406
ret.wheelSpeedFactor = SPEED_FROM_RPM
ret.centerToFront = ret.wheelbase * 0.44
ret.radarUnavailable = True
ret.openpilotLongitudinalControl = True

@ -1,5 +1,5 @@
from cereal import car
from openpilot.selfdrive.car import PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarInfo
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
@ -23,6 +23,7 @@ class CAR(Platforms):
BODY = PlatformConfig(
"COMMA BODY",
CarInfo("comma body", package="All"),
CarSpecs(mass=9, wheelbase=0.406, steerRatio=0.5, centerToFrontRatio=0.44),
dbc_dict('comma_body', None),
)
@ -37,5 +38,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()

@ -11,6 +11,7 @@ from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.common.swaglog import cloudlog
import cereal.messaging as messaging
from openpilot.selfdrive.car import gen_empty_fingerprint
@ -191,11 +192,16 @@ def fingerprint(logcan, sendcan, num_pandas):
fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus,
fingerprints=repr(finger), fw_query_time=fw_query_time, error=True)
car_platform = PLATFORMS.get(car_fingerprint, car_fingerprint)
car_platform = PLATFORMS.get(car_fingerprint, MOCK.MOCK)
return car_platform, finger, vin, car_fw, source, exact_match
def get_car_interface(CP):
CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
return CarInterface(CP, CarController, CarState)
def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
@ -203,23 +209,23 @@ def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1):
cloudlog.event("car doesn't match any fingerprints", fingerprints=repr(fingerprints), error=True)
candidate = "mock"
CarInterface, CarController, CarState = interfaces[candidate]
CarInterface, _, _ = interfaces[candidate]
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False)
CP.carVin = vin
CP.carFw = car_fw
CP.fingerprintSource = source
CP.fuzzyFingerprint = not exact_match
return CarInterface(CP, CarController, CarState), CP
return get_car_interface(CP), CP
def write_car_param(fingerprint="mock"):
def write_car_param(platform=MOCK.MOCK):
params = Params()
CarInterface, _, _ = interfaces[fingerprint]
CP = CarInterface.get_non_essential_params(fingerprint)
CarInterface, _, _ = interfaces[platform]
CP = CarInterface.get_non_essential_params(platform)
params.put("CarParams", CP.to_bytes())
def get_demo_car_params():
fingerprint="mock"
CarInterface, _, _ = interfaces[fingerprint]
CP = CarInterface.get_non_essential_params(fingerprint)
platform = MOCK.MOCK
CarInterface, _, _ = interfaces[platform]
CP = CarInterface.get_non_essential_params(platform)
return CP

@ -0,0 +1,142 @@
#!/usr/bin/env python3
import os
import time
import cereal.messaging as messaging
from cereal import car
from panda import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
REPLAY = "REPLAY" in os.environ
class CarD:
CI: CarInterfaceBase
CS: car.CarState
def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput'])
self.can_rcv_timeout_counter = 0 # conseuctive timeout count
self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count
self.CC_prev = car.CarControl.new_message()
self.last_actuators = None
self.params = Params()
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
get_one_can(self.can_sock)
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled")
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
else:
self.CI, self.CP = CI, CI.CP
# set alternative experiences from parameters
disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
self.CP.alternativeExperience = 0
if not disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
car_recognized = self.CP.carName != 'mock'
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly
if self.CP.passive:
safety_config = car.CarParams.SafetyConfig.new_message()
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config]
# Write previous route's CarParams
prev_cp = self.params.get("CarParamsPersistent")
if prev_cp is not None:
self.params.put("CarParamsPrevRoute", prev_cp)
# Write CarParams for radard
cp_bytes = self.CP.to_bytes()
self.params.put("CarParams", cp_bytes)
self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
def initialize(self):
"""Initialize CarInterface, once controls are ready"""
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
def state_update(self):
"""carState update loop, driven by can"""
# Update carState from CAN
can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
self.CS = self.CI.update(self.CC_prev, can_strs)
self.sm.update(0)
can_rcv_valid = len(can_strs) > 0
# Check for CAN timeout
if not can_rcv_valid:
self.can_rcv_timeout_counter += 1
self.can_rcv_cum_timeout_counter += 1
else:
self.can_rcv_timeout_counter = 0
self.can_rcv_timeout = self.can_rcv_timeout_counter >= 5
if can_rcv_valid and REPLAY:
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
self.state_publish()
return self.CS
def state_publish(self):
"""carState and carParams publish loop"""
# carState
cs_send = messaging.new_message('carState')
cs_send.valid = self.CS.canValid
cs_send.carState = self.CS
self.pm.send('carState', cs_send)
# carParams - logged every 50 seconds (> 1 per segment)
if (self.sm.frame % int(50. / DT_CTRL) == 0):
cp_send = messaging.new_message('carParams')
cp_send.valid = True
cp_send.carParams = self.CP
self.pm.send('carParams', cp_send)
# publish new carOutput
co_send = messaging.new_message('carOutput')
co_send.valid = True
if self.last_actuators is not None:
co_send.carOutput.actuatorsOutput = self.last_actuators
self.pm.send('carOutput', co_send)
def controls_update(self, CC: car.CarControl):
"""control update loop, driven by carControl"""
# send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators, can_sends = self.CI.apply(CC, now_nanos)
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid))
self.CC_prev = CC

@ -38,6 +38,7 @@ FW_VERSIONS = {
b'68227902AF',
b'68227902AG',
b'68227902AH',
b'68227905AG',
b'68360252AC',
],
(Ecu.srs, 0x744, None): [
@ -71,6 +72,7 @@ FW_VERSIONS = {
b'68340762AD ',
b'68340764AD ',
b'68352652AE ',
b'68352654AE ',
b'68366851AH ',
b'68366853AE ',
b'68372861AF ',
@ -304,6 +306,7 @@ FW_VERSIONS = {
b'68402708AB',
b'68402971AD',
b'68454144AD',
b'68454145AB',
b'68454152AB',
b'68454156AB',
b'68516650AB',

@ -11,6 +11,7 @@ Ecu = car.CarParams.Ecu
class ChryslerFlags(IntFlag):
# Detected flags
HIGHER_MIN_STEERING_SPEED = 1
@dataclass
@ -21,7 +22,7 @@ class ChryslerCarInfo(CarInfo):
@dataclass
class ChryslerPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None))
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'))
@dataclass(frozen=True)
@ -34,22 +35,22 @@ class CAR(Platforms):
PACIFICA_2017_HYBRID = ChryslerPlatformConfig(
"CHRYSLER PACIFICA HYBRID 2017",
ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"),
specs=ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2),
ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2),
)
PACIFICA_2018_HYBRID = ChryslerPlatformConfig(
"CHRYSLER PACIFICA HYBRID 2018",
ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"),
specs=PACIFICA_2017_HYBRID.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_2017_HYBRID.specs,
)
PACIFICA_2018 = ChryslerPlatformConfig(
"CHRYSLER PACIFICA 2018",
ChryslerCarInfo("Chrysler Pacifica 2017-18"),
specs=PACIFICA_2017_HYBRID.specs,
PACIFICA_2017_HYBRID.specs,
)
PACIFICA_2020 = ChryslerPlatformConfig(
"CHRYSLER PACIFICA 2020",
@ -57,35 +58,35 @@ class CAR(Platforms):
ChryslerCarInfo("Chrysler Pacifica 2019-20"),
ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"),
],
specs=PACIFICA_2017_HYBRID.specs,
PACIFICA_2017_HYBRID.specs,
)
# Dodge
DODGE_DURANGO = ChryslerPlatformConfig(
"DODGE DURANGO 2021",
ChryslerCarInfo("Dodge Durango 2020-21"),
specs=PACIFICA_2017_HYBRID.specs,
PACIFICA_2017_HYBRID.specs,
)
# Jeep
JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 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),
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,
JEEP_GRAND_CHEROKEE.specs,
)
# Ram
RAM_1500 = ChryslerPlatformConfig(
"RAM 1500 5TH GEN",
ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])),
ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5),
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",
@ -93,8 +94,8 @@ class CAR(Platforms):
ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])),
],
ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.),
dbc_dict('chrysler_ram_hd_generated', None),
specs=ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.),
)
@ -162,5 +163,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()

@ -11,6 +11,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
from openpilot.selfdrive.car.values import PLATFORMS
def get_all_footnotes() -> dict[Enum, int]:
@ -27,9 +28,10 @@ CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md")
def get_all_car_info() -> list[CarInfo]:
all_car_info: list[CarInfo] = []
footnotes = get_all_footnotes()
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
for model, platform in PLATFORMS.items():
car_info = platform.config.car_info
# If available, uses experimental longitudinal limits for the docs
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(),
CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(),
car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True)
if CP.dashcamOnly or car_info is None:
@ -37,7 +39,7 @@ def get_all_car_info() -> list[CarInfo]:
# A platform can include multiple car models
if not isinstance(car_info, list):
car_info = (car_info,)
car_info = [car_info,]
for _car_info in car_info:
if not hasattr(_car_info, "row"):

@ -1,9 +1,9 @@
from cereal import car
from openpilot.common.numpy_fast import clip
from opendbc.can.packer import CANPacker
from openpilot.common.numpy_fast import clip
from openpilot.selfdrive.car import apply_std_steer_angle_limits
from openpilot.selfdrive.car.ford import fordcan
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams
from openpilot.selfdrive.car.ford.values import CarControllerParams, FordFlags
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
@ -70,7 +70,7 @@ class CarController(CarControllerBase):
self.apply_curvature_last = apply_curvature
if self.CP.carFingerprint in CANFD_CAR:
if self.CP.flags & FordFlags.CANFD:
# TODO: extended mode
mode = 1 if CC.latActive else 0
counter = (self.frame // CarControllerParams.STEER_STEP) % 0xF

@ -1,10 +1,10 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.ford.fordcan import CanBus
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC
from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags
from openpilot.selfdrive.car.interfaces import CarStateBase
GearShifter = car.CarState.GearShifter
TransmissionType = car.CarParams.TransmissionType
@ -19,6 +19,9 @@ class CarState(CarStateBase):
self.vehicle_sensors_valid = False
self.prev_distance_button = 0
self.distance_button = 0
def update(self, cp, cp_cam):
ret = car.CarState.new_message()
@ -49,7 +52,7 @@ class CarState(CarStateBase):
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
ret.espDisabled = cp.vl["Cluster_Info1_FD1"]["DrvSlipCtlMde_D_Rq"] != 0 # 0 is default mode
if self.CP.carFingerprint in CANFD_CAR:
if self.CP.flags & FordFlags.CANFD:
# this signal is always 0 on non-CAN FD cars
ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3)
@ -83,6 +86,8 @@ class CarState(CarStateBase):
ret.rightBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 2
# TODO: block this going to the camera otherwise it will enable stock TJA
ret.genericToggle = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
self.prev_distance_button = self.distance_button
self.distance_button = cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"]
# lock info
ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"],
@ -91,7 +96,7 @@ class CarState(CarStateBase):
# blindspot sensors
if self.CP.enableBsm:
cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CAR else cp
cp_bsm = cp_cam if self.CP.flags & FordFlags.CANFD else cp
ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
@ -122,7 +127,7 @@ class CarState(CarStateBase):
("RCMStatusMessage2_FD1", 10),
]
if CP.carFingerprint in CANFD_CAR:
if CP.flags & FordFlags.CANFD:
messages += [
("Lane_Assist_Data3_FD1", 33),
]
@ -137,7 +142,7 @@ class CarState(CarStateBase):
("BCM_Lamp_Stat_FD1", 1),
]
if CP.enableBsm and CP.carFingerprint not in CANFD_CAR:
if CP.enableBsm and not (CP.flags & FordFlags.CANFD):
messages += [
("Side_Detect_L_Stat", 5),
("Side_Detect_R_Stat", 5),
@ -155,7 +160,7 @@ class CarState(CarStateBase):
("IPMA_Data", 1),
]
if CP.enableBsm and CP.carFingerprint in CANFD_CAR:
if CP.enableBsm and CP.flags & FordFlags.CANFD:
messages += [
("Side_Detect_L_Stat", 5),
("Side_Detect_R_Stat", 5),

@ -1,11 +1,12 @@
from cereal import car
from panda import Panda
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.ford.fordcan import CanBus
from openpilot.selfdrive.car.ford.values import CANFD_CAR, Ecu
from openpilot.selfdrive.car.ford.values import Ecu, FordFlags
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type
TransmissionType = car.CarParams.TransmissionType
GearShifter = car.CarState.GearShifter
@ -14,7 +15,7 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "ford"
ret.dashcamOnly = candidate in CANFD_CAR
ret.dashcamOnly = bool(ret.flags & FordFlags.CANFD)
ret.radarUnavailable = True
ret.steerControlType = car.CarParams.SteerControlType.angle
@ -36,7 +37,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_LONG_CONTROL
ret.openpilotLongitudinalControl = True
if candidate in CANFD_CAR:
if ret.flags & FordFlags.CANFD:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_CANFD
# Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1
@ -61,6 +62,8 @@ class CarInterface(CarInterfaceBase):
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam)
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
events = self.create_common_events(ret, extra_gears=[GearShifter.manumatic])
if not self.CS.vehicle_sensors_valid:
events.add(car.CarEvent.EventName.vehicleSensorsInvalid)

@ -1,7 +1,7 @@
import re
from collections import defaultdict
from dataclasses import dataclass, field
from enum import Enum
from enum import Enum, IntFlag
import panda.python.uds as uds
from cereal import car
@ -41,6 +41,11 @@ class CarControllerParams:
pass
class FordFlags(IntFlag):
# Static flags
CANFD = 1
class RADAR:
DELPHI_ESR = 'ford_fusion_2018_adas'
DELPHI_MRR = 'FORD_CADS'
@ -59,7 +64,7 @@ class FordCarInfo(CarInfo):
package: str = "Co-Pilot360 Assist+"
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.flags & FordFlags.CANFD else CarHarness.ford_q3
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])
else:
@ -71,11 +76,20 @@ class FordPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR))
@dataclass
class FordCANFDPlatformConfig(FordPlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', None))
def init(self):
super().init()
self.flags |= FordFlags.CANFD
class CAR(Platforms):
BRONCO_SPORT_MK1 = FordPlatformConfig(
"FORD BRONCO SPORT 1ST GEN",
FordCarInfo("Ford Bronco Sport 2021-22"),
specs=CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7),
CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7),
)
ESCAPE_MK4 = FordPlatformConfig(
"FORD ESCAPE 4TH GEN",
@ -87,7 +101,7 @@ class CAR(Platforms):
FordCarInfo("Ford Kuga Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"),
FordCarInfo("Ford Kuga Plug-in Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"),
],
specs=CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7),
CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7),
)
EXPLORER_MK6 = FordPlatformConfig(
"FORD EXPLORER 6TH GEN",
@ -97,22 +111,20 @@ class CAR(Platforms):
FordCarInfo("Lincoln Aviator 2020-23", "Co-Pilot360 Plus"),
FordCarInfo("Lincoln Aviator Plug-in Hybrid 2020-23", "Co-Pilot360 Plus"), # Grand Touring only
],
specs=CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8),
CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8),
)
F_150_MK14 = FordPlatformConfig(
F_150_MK14 = FordCANFDPlatformConfig(
"FORD F-150 14TH GEN",
[
FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"),
FordCarInfo("Ford F-150 Hybrid 2023", "Co-Pilot360 Active 2.0"),
],
dbc_dict=dbc_dict('ford_lincoln_base_pt', None),
specs=CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0),
CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0),
)
F_150_LIGHTNING_MK1 = FordPlatformConfig(
F_150_LIGHTNING_MK1 = FordCANFDPlatformConfig(
"FORD F-150 LIGHTNING 1ST GEN",
FordCarInfo("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 2.0"),
dbc_dict=dbc_dict('ford_lincoln_base_pt', None),
specs=CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9),
CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9),
)
FOCUS_MK4 = FordPlatformConfig(
"FORD FOCUS 4TH GEN",
@ -120,7 +132,7 @@ class CAR(Platforms):
FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]),
FordCarInfo("Ford Focus Hybrid 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), # mHEV only
],
specs=CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0),
CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0),
)
MAVERICK_MK1 = FordPlatformConfig(
"FORD MAVERICK 1ST GEN",
@ -130,19 +142,15 @@ class CAR(Platforms):
FordCarInfo("Ford Maverick 2023", "Co-Pilot360 Assist"),
FordCarInfo("Ford Maverick Hybrid 2023", "Co-Pilot360 Assist"),
],
specs=CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0),
CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0),
)
MUSTANG_MACH_E_MK1 = FordPlatformConfig(
MUSTANG_MACH_E_MK1 = FordCANFDPlatformConfig(
"FORD MUSTANG MACH-E 1ST GEN",
FordCarInfo("Ford Mustang Mach-E 2021-23", "Co-Pilot360 Active 2.0"),
dbc_dict=dbc_dict('ford_lincoln_base_pt', None),
specs=CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio
CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio
)
CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1}
# FW response contains a combined software and part number
# A-Z except no I, O or W
# e.g. NZ6A-14C204-AAA
@ -300,5 +308,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()

@ -47,6 +47,11 @@ class StdQueries:
MANUFACTURER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
SUPPLIER_SOFTWARE_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER)
SUPPLIER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER)
UDS_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
UDS_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \

@ -1,19 +1,20 @@
#!/usr/bin/env python3
from collections import defaultdict
from typing import Any, TypeVar
from collections.abc import Iterator
from typing import Any, Protocol, TypeVar
from tqdm import tqdm
import capnp
import panda.python.uds as uds
from cereal import car
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs
from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig
from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions
from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from openpilot.common.swaglog import cloudlog
Ecu = car.CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
@ -38,8 +39,7 @@ def is_brand(brand: str, filter_brand: str | None) -> bool:
return filter_brand is None or brand == filter_brand
def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder],
filter_brand: str = None) -> dict[AddrType, set[bytes]]:
def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], filter_brand: str = None) -> dict[AddrType, set[bytes]]:
fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set)
for fw in fw_versions:
if is_brand(fw.brand, filter_brand) and not fw.logging:
@ -48,7 +48,12 @@ def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder],
return dict(fw_versions_dict)
def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=None):
class MatchFwToCar(Protocol):
def __call__(self, live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True) -> set[str]:
...
def match_fw_to_car_fuzzy(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, exclude: str = None) -> set[str]:
"""Do a fuzzy FW match. This function will return a match, and the number of firmware version
that were matched uniquely to that specific car. If multiple ECUs uniquely match to different cars
the match is rejected."""
@ -73,7 +78,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=
all_fw_versions[(addr[1], addr[2], f)].append(candidate)
matched_ecus = set()
candidate = None
match: str | None = None
for addr, versions in live_fw_versions.items():
ecu_key = (addr[0], addr[1])
for version in versions:
@ -82,23 +87,23 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=
if len(candidates) == 1:
matched_ecus.add(ecu_key)
if candidate is None:
candidate = candidates[0]
if match is None:
match = candidates[0]
# We uniquely matched two different cars. No fuzzy match possible
elif candidate != candidates[0]:
elif match != candidates[0]:
return set()
# Note that it is possible to match to a candidate without all its ECUs being present
# if there are enough matches. FIXME: parameterize this or require all ECUs to exist like exact matching
if len(matched_ecus) >= 2:
if match and len(matched_ecus) >= 2:
if log:
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {len(matched_ecus)} matching ECUs")
return {candidate}
cloudlog.error(f"Fingerprinted {match} using fuzzy match. {len(matched_ecus)} matching ECUs")
return {match}
else:
return set()
def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw_versions=None) -> set[str]:
def match_fw_to_car_exact(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, extra_fw_versions: dict = None) -> set[str]:
"""Do an exact FW match. Returns all cars that match the given
FW versions for a list of "essential" ECUs. If an ECU is not considered
essential the FW version can be missing to get a fingerprint, but if it's present it
@ -139,9 +144,10 @@ def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw
return set(candidates.keys()) - invalid
def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], allow_exact: bool = True, allow_fuzzy: bool = True,
log: bool = True) -> tuple[bool, set[str]]:
# Try exact matching first
exact_matches = []
exact_matches: list[tuple[bool, MatchFwToCar]] = []
if allow_exact:
exact_matches = [(True, match_fw_to_car_exact)]
if allow_fuzzy:
@ -149,7 +155,7 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
for exact_match, match_func in exact_matches:
# For each brand, attempt to fingerprint using all FW returned from its queries
matches = set()
matches: set[str] = set()
for brand in VERSIONS.keys():
fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand)
matches |= match_func(fw_versions_dict, match_brand=brand, log=log)
@ -165,12 +171,12 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
return True, set()
def get_present_ecus(logcan, sendcan, num_pandas=1) -> set[EcuAddrBusType]:
def get_present_ecus(logcan, sendcan, num_pandas: int = 1) -> set[EcuAddrBusType]:
params = Params()
# queries are split by OBD multiplexing mode
queries: dict[bool, list[list[EcuAddrBusType]]] = {True: [], False: []}
parallel_queries: dict[bool, list[EcuAddrBusType]] = {True: [], False: []}
responses = set()
responses: set[EcuAddrBusType] = set()
for brand, config, r in REQUESTS:
# Skip query if no panda available
@ -231,8 +237,8 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool):
cloudlog.warning("OBD multiplexing set successfully")
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \
list[capnp.lib.capnp._DynamicStructBuilder]:
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1,
debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]:
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
all_car_fw = []
@ -254,8 +260,8 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
return all_car_fw
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \
list[capnp.lib.capnp._DynamicStructBuilder]:
def get_fw_versions(logcan, sendcan, query_brand: str = None, extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1,
debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]:
versions = VERSIONS.copy()
params = Params()

@ -145,15 +145,12 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]]
ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
if candidate == CAR.VOLT:
ret.tireStiffnessFactor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters
ret.lateralTuning.pid.kpBP = [0., 40.]
ret.lateralTuning.pid.kpV = [0., 0.17]
ret.lateralTuning.pid.kiBP = [0.]
@ -175,7 +172,6 @@ class CarInterface(CarInterfaceBase):
elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019):
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.tireStiffnessFactor = 1.0
if candidate == CAR.ESCALADE_ESV:
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]]
@ -186,12 +182,10 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BOLT_EUV:
ret.tireStiffnessFactor = 1.0
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.SILVERADO:
ret.tireStiffnessFactor = 1.0
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
# with foot on brake to allow engagement, but this platform only has that check in the camera.
# TODO: check if this is split by EV/ICE with more platforms in the future
@ -203,7 +197,6 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.TRAILBLAZER:
ret.tireStiffnessFactor = 1.0
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)

@ -79,6 +79,11 @@ class GMCarInfo(CarInfo):
self.footnotes.append(Footnote.OBD_II)
@dataclass(frozen=True, kw_only=True)
class GMCarSpecs(CarSpecs):
tireStiffnessFactor: float = 0.444 # not optimized yet
@dataclass
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'))
@ -88,52 +93,52 @@ class CAR(Platforms):
HOLDEN_ASTRA = GMPlatformConfig(
"HOLDEN ASTRA RS-V BK 2017",
GMCarInfo("Holden Astra 2017"),
specs=CarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4),
GMCarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4),
)
VOLT = GMPlatformConfig(
"CHEVROLET VOLT PREMIER 2017",
GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"),
specs=CarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45),
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469),
)
CADILLAC_ATS = GMPlatformConfig(
"CADILLAC ATS Premium Performance 2018",
GMCarInfo("Cadillac ATS Premium Performance 2018"),
specs=CarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3),
GMCarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3),
)
MALIBU = GMPlatformConfig(
"CHEVROLET MALIBU PREMIER 2017",
GMCarInfo("Chevrolet Malibu Premier 2017"),
specs=CarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
)
ACADIA = GMPlatformConfig(
"GMC ACADIA DENALI 2018",
GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
specs=CarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
)
BUICK_LACROSSE = GMPlatformConfig(
"BUICK LACROSSE 2017",
GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"),
specs=CarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
)
BUICK_REGAL = GMPlatformConfig(
"BUICK REGAL ESSENCE 2018",
GMCarInfo("Buick Regal Essence 2018"),
specs=CarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
)
ESCALADE = GMPlatformConfig(
"CADILLAC ESCALADE 2017",
GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"),
specs=CarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3),
GMCarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3),
)
ESCALADE_ESV = GMPlatformConfig(
"CADILLAC ESCALADE ESV 2016",
GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
specs=CarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3),
GMCarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3, tireStiffnessFactor=1.0),
)
ESCALADE_ESV_2019 = GMPlatformConfig(
"CADILLAC ESCALADE ESV 2019",
GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"),
specs=ESCALADE_ESV.specs,
ESCALADE_ESV.specs,
)
BOLT_EUV = GMPlatformConfig(
"CHEVROLET BOLT EUV 2022",
@ -141,7 +146,7 @@ class CAR(Platforms):
GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
],
specs=CarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4),
GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
)
SILVERADO = GMPlatformConfig(
"CHEVROLET SILVERADO 1500 2020",
@ -149,17 +154,17 @@ class CAR(Platforms):
GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"),
],
specs=CarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3),
GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
)
EQUINOX = GMPlatformConfig(
"CHEVROLET EQUINOX 2019",
GMCarInfo("Chevrolet Equinox 2019-22"),
specs=CarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4),
GMCarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4),
)
TRAILBLAZER = GMPlatformConfig(
"CHEVROLET TRAILBLAZER 2021",
GMCarInfo("Chevrolet Trailblazer 2021-22"),
specs=CarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4),
GMCarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
)
@ -242,5 +247,4 @@ CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER}
STEER_THRESHOLD = 1.0
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()

@ -805,6 +805,7 @@ FW_VERSIONS = {
b'37805-5MR-3250\x00\x00',
b'37805-5MR-4070\x00\x00',
b'37805-5MR-4080\x00\x00',
b'37805-5MR-4170\x00\x00',
b'37805-5MR-4180\x00\x00',
b'37805-5MR-A240\x00\x00',
b'37805-5MR-A250\x00\x00',

@ -92,10 +92,6 @@ class CarInterface(CarInterfaceBase):
eps_modified = True
if candidate == CAR.CIVIC:
ret.mass = 1326.
ret.wheelbase = 2.70
ret.centerToFront = ret.wheelbase * 0.4
ret.steerRatio = 15.38 # 10.93 is end-to-end spec
if eps_modified:
# stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE
# stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680
@ -110,20 +106,11 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022):
ret.mass = 1326.
ret.wheelbase = 2.70
ret.centerToFront = ret.wheelbase * 0.4
ret.steerRatio = 15.38 # 10.93 is end-to-end spec
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]]
elif candidate == CAR.ACCORD:
ret.mass = 3279. * CV.LB_TO_KG
ret.wheelbase = 2.83
ret.centerToFront = ret.wheelbase * 0.39
ret.steerRatio = 16.33 # 11.82 is spec end-to-end
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.8467
if eps_modified:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
@ -131,29 +118,15 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.ACURA_ILX:
ret.mass = 3095. * CV.LB_TO_KG
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.37
ret.steerRatio = 18.61 # 15.3 is spec end-to-end
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.72
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.CRV, CAR.CRV_EU):
ret.mass = 3572. * CV.LB_TO_KG
ret.wheelbase = 2.62
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 16.89 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.444
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_5G:
ret.mass = 3410. * CV.LB_TO_KG
ret.wheelbase = 2.66
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 16.0 # 12.3 is spec end-to-end
if eps_modified:
# stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F
# stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400
@ -163,45 +136,23 @@ class CarInterface(CarInterfaceBase):
else:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
ret.tireStiffnessFactor = 0.677
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_HYBRID:
ret.mass = 1667. # mean of 4 models in kg
ret.wheelbase = 2.66
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 16.0 # 12.3 is spec end-to-end
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.677
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.FIT:
ret.mass = 2644. * CV.LB_TO_KG
ret.wheelbase = 2.53
ret.centerToFront = ret.wheelbase * 0.39
ret.steerRatio = 13.06
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.75
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
elif candidate == CAR.FREED:
ret.mass = 3086. * CV.LB_TO_KG
ret.wheelbase = 2.74
# the remaining parameters were copied from FIT
ret.centerToFront = ret.wheelbase * 0.39
ret.steerRatio = 13.06
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
ret.tireStiffnessFactor = 0.75
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
elif candidate in (CAR.HRV, CAR.HRV_3G):
ret.mass = 3125 * CV.LB_TO_KG
ret.wheelbase = 2.61
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 15.2
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
ret.tireStiffnessFactor = 0.5
if candidate == CAR.HRV:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.025]]
ret.wheelSpeedFactor = 1.025
@ -209,29 +160,14 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] # TODO: can probably use some tuning
elif candidate == CAR.ACURA_RDX:
ret.mass = 3935. * CV.LB_TO_KG
ret.wheelbase = 2.68
ret.centerToFront = ret.wheelbase * 0.38
ret.steerRatio = 15.0 # as spec
ret.tireStiffnessFactor = 0.444
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate == CAR.ACURA_RDX_3G:
ret.mass = 4068. * CV.LB_TO_KG
ret.wheelbase = 2.75
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 11.95 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
ret.tireStiffnessFactor = 0.677
elif candidate in (CAR.ODYSSEY, CAR.ODYSSEY_CHN):
ret.mass = 1900.
ret.wheelbase = 3.00
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 14.35 # as spec
ret.tireStiffnessFactor = 0.82
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
if candidate == CAR.ODYSSEY_CHN:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]] # TODO: determine if there is a dead zone at the top end
@ -239,47 +175,30 @@ 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
elif candidate == CAR.PILOT:
ret.mass = 4278. * CV.LB_TO_KG # average weight
ret.wheelbase = 2.86
ret.centerToFront = ret.wheelbase * 0.428
ret.steerRatio = 16.0 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.444
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.RIDGELINE:
ret.mass = 4515. * CV.LB_TO_KG
ret.wheelbase = 3.18
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 15.59 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.444
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.INSIGHT:
ret.mass = 2987. * CV.LB_TO_KG
ret.wheelbase = 2.7
ret.centerToFront = ret.wheelbase * 0.39
ret.steerRatio = 15.0 # 12.58 is spec end-to-end
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.82
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.HONDA_E:
ret.mass = 3338.8 * CV.LB_TO_KG
ret.wheelbase = 2.5
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 16.71
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.tireStiffnessFactor = 0.82
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning
else:
raise ValueError(f"unsupported car {candidate}")
# These cars use alternate user brake msg (0x1BE)
if 0x1BE in fingerprint[CAN.pt] and candidate in HONDA_BOSCH:
# TODO: Only detect feature for Accord/Accord Hybrid, not all Bosch DBCs have BRAKE_MODULE
if 0x1BE in fingerprint[CAN.pt] and candidate == CAR.ACCORD:
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
if ret.flags & HondaFlags.BOSCH_ALT_BRAKE:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE
# These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)

@ -1,10 +1,10 @@
from dataclasses import dataclass
from enum import Enum, IntFlag, StrEnum
from enum import Enum, IntFlag
from cereal import car
from openpilot.common.conversions import Conversions as CV
from panda.python import uds
from openpilot.selfdrive.car import dbc_dict
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
@ -46,10 +46,19 @@ class CarControllerParams:
class HondaFlags(IntFlag):
# Detected flags
# Bosch models with alternate set of LKAS_HUD messages
BOSCH_EXT_HUD = 1
BOSCH_ALT_BRAKE = 2
# Static flags
BOSCH = 4
BOSCH_RADARLESS = 8
NIDEC = 16
NIDEC_ALT_PCM_ACCEL = 32
NIDEC_ALT_SCM_MESSAGES = 64
# Car button codes
class CruiseButtons:
@ -72,86 +81,202 @@ VISUAL_HUD = {
}
class CAR(StrEnum):
ACCORD = "HONDA ACCORD 2018"
CIVIC = "HONDA CIVIC 2016"
CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019"
CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019"
CIVIC_2022 = "HONDA CIVIC 2022"
ACURA_ILX = "ACURA ILX 2016"
CRV = "HONDA CR-V 2016"
CRV_5G = "HONDA CR-V 2017"
CRV_EU = "HONDA CR-V EU 2016"
CRV_HYBRID = "HONDA CR-V HYBRID 2019"
FIT = "HONDA FIT 2018"
FREED = "HONDA FREED 2020"
HRV = "HONDA HRV 2019"
HRV_3G = "HONDA HR-V 2023"
ODYSSEY = "HONDA ODYSSEY 2018"
ODYSSEY_CHN = "HONDA ODYSSEY CHN 2019"
ACURA_RDX = "ACURA RDX 2018"
ACURA_RDX_3G = "ACURA RDX 2020"
PILOT = "HONDA PILOT 2017"
RIDGELINE = "HONDA RIDGELINE 2017"
INSIGHT = "HONDA INSIGHT 2019"
HONDA_E = "HONDA E 2020"
class Footnote(Enum):
CIVIC_DIESEL = CarFootnote(
"2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.",
Column.FSR_STEERING)
@dataclass
class HondaCarInfo(CarInfo):
package: str = "Honda Sensing"
def init_make(self, CP: car.CarParams):
if CP.carFingerprint in HONDA_BOSCH:
self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.carFingerprint in HONDA_BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a])
if CP.flags & HondaFlags.BOSCH:
self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.flags & HondaFlags.BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a])
else:
self.car_parts = CarParts.common([CarHarness.nidec])
CAR_INFO: dict[str, HondaCarInfo | list[HondaCarInfo] | None] = {
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 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.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8",
footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS),
HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS),
],
CAR.CIVIC_BOSCH_DIESEL: None, # same platform
CAR.CIVIC_2022: [
HondaCarInfo("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
HondaCarInfo("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
],
CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS),
CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring
CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-20", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.HRV_3G: HondaCarInfo("Honda HR-V 2023", "All"),
CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20"),
CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey
CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.PILOT: [
HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS),
],
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS),
}
class Footnote(Enum):
CIVIC_DIESEL = CarFootnote(
"2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.",
Column.FSR_STEERING)
class HondaBoschPlatformConfig(PlatformConfig):
def init(self):
self.flags |= HondaFlags.BOSCH
class HondaNidecPlatformConfig(PlatformConfig):
def init(self):
self.flags |= HondaFlags.NIDEC
class CAR(Platforms):
# Bosch Cars
ACCORD = HondaBoschPlatformConfig(
"HONDA ACCORD 2018",
[
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 Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
],
# steerRatio: 11.82 is spec end-to-end
CarSpecs(mass=3279 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=16.33, centerToFrontRatio=0.39, tireStiffnessFactor=0.8467),
dbc_dict('honda_accord_2018_can_generated', None),
)
CIVIC_BOSCH = HondaBoschPlatformConfig(
"HONDA CIVIC (BOSCH) 2019",
[
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8",
footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS),
HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS),
],
CarSpecs(mass=1326, wheelbase=2.7, steerRatio=15.38, centerToFrontRatio=0.4), # steerRatio: 10.93 is end-to-end spec
dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None),
)
CIVIC_BOSCH_DIESEL = HondaBoschPlatformConfig(
"HONDA CIVIC SEDAN 1.6 DIESEL 2019",
None, # don't show in docs
CIVIC_BOSCH.specs,
dbc_dict('honda_accord_2018_can_generated', None),
)
CIVIC_2022 = HondaBoschPlatformConfig(
"HONDA CIVIC 2022",
[
HondaCarInfo("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
HondaCarInfo("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
],
CIVIC_BOSCH.specs,
dbc_dict('honda_civic_ex_2022_can_generated', None),
flags=HondaFlags.BOSCH_RADARLESS,
)
CRV_5G = HondaBoschPlatformConfig(
"HONDA CR-V 2017",
HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS),
# steerRatio: 12.3 is spec end-to-end
CarSpecs(mass=3410 * CV.LB_TO_KG, wheelbase=2.66, steerRatio=16.0, centerToFrontRatio=0.41, tireStiffnessFactor=0.677),
dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'),
flags=HondaFlags.BOSCH_ALT_BRAKE,
)
CRV_HYBRID = HondaBoschPlatformConfig(
"HONDA CR-V HYBRID 2019",
HondaCarInfo("Honda CR-V Hybrid 2017-20", min_steer_speed=12. * CV.MPH_TO_MS),
# mass: mean of 4 models in kg, steerRatio: 12.3 is spec end-to-end
CarSpecs(mass=1667, wheelbase=2.66, steerRatio=16, centerToFrontRatio=0.41, tireStiffnessFactor=0.677),
dbc_dict('honda_accord_2018_can_generated', None),
)
HRV_3G = HondaBoschPlatformConfig(
"HONDA HR-V 2023",
HondaCarInfo("Honda HR-V 2023", "All"),
CarSpecs(mass=3125 * CV.LB_TO_KG, wheelbase=2.61, steerRatio=15.2, centerToFrontRatio=0.41, tireStiffnessFactor=0.5),
dbc_dict('honda_civic_ex_2022_can_generated', None),
flags=HondaFlags.BOSCH_RADARLESS | HondaFlags.BOSCH_ALT_BRAKE,
)
ACURA_RDX_3G = HondaBoschPlatformConfig(
"ACURA RDX 2020",
HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CarSpecs(mass=4068 * CV.LB_TO_KG, wheelbase=2.75, steerRatio=11.95, centerToFrontRatio=0.41, tireStiffnessFactor=0.677), # as spec
dbc_dict('acura_rdx_2020_can_generated', None),
flags=HondaFlags.BOSCH_ALT_BRAKE,
)
INSIGHT = HondaBoschPlatformConfig(
"HONDA INSIGHT 2019",
HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.0, centerToFrontRatio=0.39, tireStiffnessFactor=0.82), # as spec
dbc_dict('honda_insight_ex_2019_can_generated', None),
)
HONDA_E = HondaBoschPlatformConfig(
"HONDA E 2020",
HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
dbc_dict('acura_rdx_2020_can_generated', None),
)
# Nidec Cars
ACURA_ILX = HondaNidecPlatformConfig(
"ACURA ILX 2016",
HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS),
CarSpecs(mass=3095 * CV.LB_TO_KG, wheelbase=2.67, steerRatio=18.61, centerToFrontRatio=0.37, tireStiffnessFactor=0.72), # 15.3 is spec end-to-end
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
CRV = HondaNidecPlatformConfig(
"HONDA CR-V 2016",
HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS),
CarSpecs(mass=3572 * CV.LB_TO_KG, wheelbase=2.62, steerRatio=16.89, centerToFrontRatio=0.41, tireStiffnessFactor=0.444), # as spec
dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
CRV_EU = HondaNidecPlatformConfig(
"HONDA CR-V EU 2016",
None, # Euro version of CRV Touring, don't show in docs
CRV.specs,
dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
FIT = HondaNidecPlatformConfig(
"HONDA FIT 2018",
HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS),
CarSpecs(mass=2644 * CV.LB_TO_KG, wheelbase=2.53, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75),
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
FREED = HondaNidecPlatformConfig(
"HONDA FREED 2020",
HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS),
CarSpecs(mass=3086. * CV.LB_TO_KG, wheelbase=2.74, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75), # mostly copied from FIT
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
HRV = HondaNidecPlatformConfig(
"HONDA HRV 2019",
HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS),
HRV_3G.specs,
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ODYSSEY = HondaNidecPlatformConfig(
"HONDA ODYSSEY 2018",
HondaCarInfo("Honda Odyssey 2018-20"),
CarSpecs(mass=1900, wheelbase=3.0, steerRatio=14.35, centerToFrontRatio=0.41, tireStiffnessFactor=0.82),
dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL,
)
ODYSSEY_CHN = HondaNidecPlatformConfig(
"HONDA ODYSSEY CHN 2019",
None, # Chinese version of Odyssey, don't show in docs
ODYSSEY.specs,
dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ACURA_RDX = HondaNidecPlatformConfig(
"ACURA RDX 2018",
HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS),
CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec
dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
PILOT = HondaNidecPlatformConfig(
"HONDA PILOT 2017",
[
HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS),
],
CarSpecs(mass=4278 * CV.LB_TO_KG, wheelbase=2.86, centerToFrontRatio=0.428, steerRatio=16.0, tireStiffnessFactor=0.444), # as spec
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
RIDGELINE = HondaNidecPlatformConfig(
"HONDA RIDGELINE 2017",
HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS),
CarSpecs(mass=4515 * CV.LB_TO_KG, wheelbase=3.18, centerToFrontRatio=0.41, steerRatio=15.59, tireStiffnessFactor=0.444), # as spec
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
CIVIC = HondaNidecPlatformConfig(
"HONDA CIVIC 2016",
HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"),
CarSpecs(mass=1326, wheelbase=2.70, centerToFrontRatio=0.4, steerRatio=15.38), # 10.93 is end-to-end spec
dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
)
HONDA_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xF112)
@ -216,41 +341,16 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
)
DBC = {
CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None),
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_3G: dbc_dict('acura_rdx_2020_can_generated', None),
CAR.CIVIC: dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.CIVIC_BOSCH: dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None),
CAR.CIVIC_BOSCH_DIESEL: dbc_dict('honda_accord_2018_can_generated', None),
CAR.CRV: dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.CRV_5G: dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'),
CAR.CRV_EU: dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.CRV_HYBRID: dbc_dict('honda_accord_2018_can_generated', None),
CAR.FIT: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
CAR.FREED: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
CAR.HRV: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
CAR.HRV_3G: dbc_dict('honda_civic_ex_2022_can_generated', None),
CAR.ODYSSEY: dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'),
CAR.ODYSSEY_CHN: dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'),
CAR.PILOT: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None),
CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None),
CAR.CIVIC_2022: dbc_dict('honda_civic_ex_2022_can_generated', None),
}
STEER_THRESHOLD = {
# default is 1200, overrides go here
CAR.ACURA_RDX: 400,
CAR.CRV_EU: 400,
}
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,
CAR.PILOT, CAR.RIDGELINE}
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}
HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G}
HONDA_NIDEC_ALT_PCM_ACCEL = CAR.with_flags(HondaFlags.NIDEC_ALT_PCM_ACCEL)
HONDA_NIDEC_ALT_SCM_MESSAGES = CAR.with_flags(HondaFlags.NIDEC_ALT_SCM_MESSAGES)
HONDA_BOSCH = CAR.with_flags(HondaFlags.BOSCH)
HONDA_BOSCH_RADARLESS = CAR.with_flags(HondaFlags.BOSCH_RADARLESS)
DBC = CAR.create_dbc_map()

@ -135,7 +135,7 @@ class CarController(CarControllerBase):
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
@ -175,12 +175,12 @@ class CarController(CarControllerBase):
can_sends = []
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint))
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:

@ -5,21 +5,6 @@ from openpilot.selfdrive.car.hyundai.values import CAR
Ecu = car.CarParams.Ecu
FINGERPRINTS = {
CAR.HYUNDAI_GENESIS: [{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4
},
{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1378: 4, 1379: 8, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4
},
{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 912: 7, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1268: 8, 1280: 1, 1281: 3, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1437: 8, 1456: 4
},
{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1378: 4, 1379: 8, 1384: 5, 1407: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4
},
{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4
}],
CAR.SANTA_FE: [{
67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1379: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8
},
@ -32,33 +17,15 @@ FINGERPRINTS = {
CAR.SONATA: [{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 546: 8, 549: 8, 550: 8, 576: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 8, 865: 8, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 908: 8, 909: 8, 912: 7, 913: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1089: 5, 1096: 8, 1107: 5, 1108: 8, 1114: 8, 1136: 8, 1145: 8, 1151: 8, 1155: 8, 1156: 8, 1157: 4, 1162: 8, 1164: 8, 1168: 8, 1170: 8, 1173: 8, 1180: 8, 1183: 8, 1184: 8, 1186: 2, 1191: 2, 1193: 8, 1210: 8, 1225: 8, 1227: 8, 1265: 4, 1268: 8, 1280: 8, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1330: 8, 1339: 8, 1342: 6, 1343: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1379: 8, 1384: 8, 1394: 8, 1407: 8, 1419: 8, 1427: 6, 1446: 8, 1456: 4, 1460: 8, 1470: 8, 1485: 8, 1504: 3, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8, 2015: 8
}],
CAR.SONATA_LF: [{
66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 447: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 832: 8, 884: 8, 897: 8, 899: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1151: 6, 1168: 7, 1170: 8, 1253: 8, 1254: 8, 1255: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1342: 6, 1345: 8, 1348: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1365: 8, 1366: 8, 1367: 8, 1369: 8, 1397: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1470: 8, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2008: 8, 2009: 8, 2012: 8, 2013: 8, 2014: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
}],
CAR.KIA_SORENTO: [{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1384: 8, 1407: 8, 1411: 8, 1419: 8, 1425: 2, 1427: 6, 1444: 8, 1456: 4, 1470: 8, 1489: 1
}],
CAR.KIA_STINGER: [{
67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 576: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 4, 1379: 8, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1456: 4, 1470: 8
}],
CAR.GENESIS_G80: [{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1024: 2, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1191: 2, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1456: 4, 1470: 8
},
{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 546: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1157: 4, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1437: 8, 1456: 4, 1470: 8
},
{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1157: 4, 1162: 8, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1193: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4, 1470: 8
}],
CAR.GENESIS_G90: [{
67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1162: 4, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1456: 4, 1470: 8, 1988: 8, 2000: 8, 2003: 8, 2004: 8, 2005: 8, 2008: 8, 2011: 8, 2012: 8, 2013: 8
}],
CAR.IONIQ_EV_2020: [{
127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 524: 8, 544: 7, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1164: 8, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1379: 8, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1456: 4, 1470: 8, 1473: 8, 1507: 8, 1535: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8, 2013: 8
}],
CAR.IONIQ: [{
68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 524: 8, 544: 8, 576: 8, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 6, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1164: 8, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1379: 8, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1448: 8, 1456: 4, 1470: 8, 1473: 8, 1476: 8, 1507: 8, 1535: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8, 2013: 8
}],
CAR.KONA_EV: [{
127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 544: 8, 549: 8, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 8, 1151: 6, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1307: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1378: 4, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1456: 4, 1470: 8, 1473: 8, 1507: 8, 1535: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8, 1157: 4, 1193: 8, 1379: 8, 1988: 8, 1996: 8
}],
@ -74,9 +41,6 @@ FINGERPRINTS = {
{
68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 544: 8, 576: 8, 593: 8, 688: 5, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 909: 8, 912: 7, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 6, 1151: 6, 1168: 7, 1173: 8, 1180: 8, 1186: 2, 1191: 2, 1265: 4, 1268: 8, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1371: 8, 1407: 8, 1419: 8, 1420: 8, 1425: 2, 1427: 6, 1429: 8, 1430: 8, 1448: 8, 1456: 4, 1470: 8, 1476: 8, 1535: 8
}],
CAR.PALISADE: [{
67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 546: 8, 547: 8, 548: 8, 549: 8, 576: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 913: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1123: 8, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1180: 8, 1186: 2, 1191: 2, 1193: 8, 1210: 8, 1225: 8, 1227: 8, 1265: 4, 1280: 8, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1384: 8, 1407: 8, 1419: 8, 1427: 6, 1456: 4, 1470: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8
}],
}
FW_VERSIONS = {
@ -100,15 +64,18 @@ FW_VERSIONS = {
CAR.AZERA_HEV_6TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.00 99211-G8000 180903',
b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.01 99211-G8000 181109',
b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.02 99211-G8100 191029',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00IG MDPS C 1.00 1.00 56310M9600\x00 4IHSC100',
b'\xf1\x00IG MDPS C 1.00 1.01 56310M9350\x00 4IH8C101',
b'\xf1\x00IG MDPS C 1.00 1.02 56310M9350\x00 4IH8C102',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00IGhe SCC FHCUP 1.00 1.00 99110-M9100 ',
b'\xf1\x00IGhe SCC FHCUP 1.00 1.01 99110-M9000 ',
b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T7N0_C2\x00\x006T7Q2051\x00\x00TIG2H24KA2\x12@\x11\xb7',
@ -413,6 +380,7 @@ FW_VERSIONS = {
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24NL1\xb0\x9f\xee\xf5',
b'\xf1\x006T6H0_C2\x00\x006T6B7051\x00\x00TLF0G24SL4;\x08\x12i',
b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2\x00\x00\x00\x00',
b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm',
b'\xf1\x87LAJSG49645724HF0\x87x\x87\x88\x87www\x88\x99\xa8\x89\x88\x99\xa8\x89\x88\x99\xa8\x89S_\xfb\xff\x87f\x7f\xff^2\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm',
@ -724,6 +692,7 @@ FW_VERSIONS = {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00LX ESC \x01 103\x19\t\x10 58910-S8360',
b'\xf1\x00LX ESC \x01 1031\t\x10 58910-S8360',
b'\xf1\x00LX ESC \x01 104 \x10\x16 58910-S8360',
b'\xf1\x00LX ESC \x0b 101\x19\x03\x17 58910-S8330',
b'\xf1\x00LX ESC \x0b 102\x19\x05\x07 58910-S8330',
b'\xf1\x00LX ESC \x0b 103\x19\t\x07 58910-S8330',
@ -745,10 +714,12 @@ FW_VERSIONS = {
b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8000 4LXDC103',
b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8020 4LXDC103',
b'\xf1\x00LX2 MDPS C 1.00 1.04 56310-S8020 4LXDC104',
b'\xf1\x00LX2 MDPS R 1.00 1.02 56370-S8300 9318',
b'\xf1\x00ON MDPS C 1.00 1.00 56340-S9000 8B13',
b'\xf1\x00ON MDPS C 1.00 1.01 56340-S9000 9201',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00LX2 MFC AT KOR LHD 1.00 1.08 99211-S8100 200903',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.00 99211-S8110 210226',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.03 99211-S8100 190125',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.05 99211-S8100 190909',
@ -762,6 +733,7 @@ FW_VERSIONS = {
b'\xf1\x00bcsh8p54 U872\x00\x00\x00\x00\x00\x00TON4G38NB1\x96z28',
b'\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX4G38NB3X\xa8\xc08',
b'\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00TON4G38NB2[v\\\xb6',
b'\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX2G38NB5X\xfa\xe88',
b'\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00TON2G38NB5j\x94.\xde',
b'\xf1\x87LBLUFN591307KF25vgvw\x97wwwy\x99\xa7\x99\x99\xaa\xa9\x9af\x88\x96h\x95o\xf7\xff\x99f/\xff\xe4c\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB2\xd7\xc1/\xd1',
b"\xf1\x87LBLUFN622950KF36\xa8\x88\x88\x88\x87w\x87xh\x99\x96\x89\x88\x99\x98\x89\x88\x99\x98\x89\x87o\xf6\xff\x98\x88o\xffx'\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB3\xd1\xc3\xf8\xa8",
@ -890,6 +862,7 @@ FW_VERSIONS = {
CAR.GENESIS_G80: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DH__ SCC F-CUP 1.00 1.01 96400-B1120 ',
b'\xf1\x00DH__ SCC F-CUP 1.00 1.02 96400-B1120 ',
b'\xf1\x00DH__ SCC FHCUP 1.00 1.01 96400-B1110 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
@ -897,10 +870,12 @@ FW_VERSIONS = {
b'\xf1\x00DH LKAS AT USA LHD 1.01 1.01 95895-B1500 161014',
b'\xf1\x00DH LKAS AT USA LHD 1.01 1.02 95895-B1500 170810',
b'\xf1\x00DH LKAS AT USA LHD 1.01 1.03 95895-B1500 180713',
b'\xf1\x00DH LKAS AT USA LHD 1.01 1.04 95895-B1500 181213',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G33KH2\xae\xde\xd5!',
b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G38NH2j\x9dA\x1c',
b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G38NH3\xaf\x1a7\xe2',
b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0T33NH3\x97\xe6\xbc\xb8',
b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00TDH0G38NH3:-\xa9n',
b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SDH0T33NH4\xd7O\x9e\xc9',
@ -1089,6 +1064,7 @@ FW_VERSIONS = {
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00OS MDPS C 1.00 1.03 56310/K4550 4OEDC103',
b'\xf1\x00OS MDPS C 1.00 1.04 56310-XX000 4OEDC104',
b'\xf1\x00OS MDPS C 1.00 1.04 56310K4000\x00 4OEDC104',
b'\xf1\x00OS MDPS C 1.00 1.04 56310K4050\x00 4OEDC104',
],
@ -1176,11 +1152,13 @@ FW_VERSIONS = {
},
CAR.KIA_NIRO_PHEV: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6D0051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6F6051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006U3H0_C2\x00\x006U3G0051\x00\x00HDE0G16NS2\x00\x00\x00\x00',
b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PDE0G16NL2&[\xc3\x01',
b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x00\x00\x00\x00',
b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x13\xcd\x88\x92',
@ -1192,6 +1170,7 @@ FW_VERSIONS = {
b'\xf1\x00DE MDPS C 1.00 1.09 56310G5301\x00 4DEHC109',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DEH MFC AT USA LHD 1.00 1.00 95740-G5010 170117',
b'\xf1\x00DEP MFC AT USA LHD 1.00 1.00 95740-G5010 170117',
b'\xf1\x00DEP MFC AT USA LHD 1.00 1.01 95740-G5010 170424',
b'\xf1\x00DEP MFC AT USA LHD 1.00 1.05 99211-G5000 190826',
@ -1476,11 +1455,13 @@ FW_VERSIONS = {
CAR.SONATA_HYBRID: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
b'\xf1\x00DNhe SCC FHCUP 1.00 1.00 99110-L5000 ',
b'\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
b'\xf1\x8799110L5000\xf1\x00DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
b'\xf1\x8799110L5000\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L5000 4DNHC101',
b'\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104',
b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102',
b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.03 56310-L5450 4DNHC103',
@ -1488,12 +1469,14 @@ FW_VERSIONS = {
b'\xf1\x8756310L5450\x00\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8HMFC AT KOR LHD 1.00 1.03 99211-L1000 190705',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.04 99211-L1000 191016',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.05 99211-L1000 201109',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.06 99211-L1000 210325',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.07 99211-L1000 211223',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00PSBG2314 E07\x00\x00\x00\x00\x00\x00\x00TDN2H20KA5\xba\x82\xc7\xc3',
b'\xf1\x00PSBG2323 E09\x00\x00\x00\x00\x00\x00\x00TDN2H20SA5\x97R\x88\x9e',
b'\xf1\x00PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TDN2H20SA6N\xc2\xeeW',
b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TDN2H20SA7\x1a3\xf9\xab',
@ -1503,6 +1486,7 @@ FW_VERSIONS = {
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x87391062J002',
b'\xf1\x87391162J011',
b'\xf1\x87391162J012',
b'\xf1\x87391162J013',
b'\xf1\x87391162J014',
@ -1510,15 +1494,18 @@ FW_VERSIONS = {
},
CAR.KIA_SORENTO: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00UMP LKAS AT USA LHD 1.00 1.00 95740-C6550 d00',
b'\xf1\x00UMP LKAS AT USA LHD 1.01 1.01 95740-C6550 d01',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00UM ESC \x02 12 \x18\x05\x05 58910-C6300',
b'\xf1\x00UM ESC \x0c 12 \x18\x05\x06 58910-C6330',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00UM__ SCC F-CUP 1.00 1.00 96400-C6500 ',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00bcsh8p54 U834\x00\x00\x00\x00\x00\x00TUM2G33NL7K\xae\xdd\x1d',
b'\xf1\x87LDKUAA0348164HE3\x87www\x87www\x88\x88\xa8\x88w\x88\x97xw\x88\x97x\x86o\xf8\xff\x87f\x7f\xff\x15\xe0\xf1\x81U811\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U811\x00\x00\x00\x00\x00\x00TUM4G33NL3V|DG',
],
(Ecu.engine, 0x7e0, None): [
@ -1546,7 +1533,9 @@ FW_VERSIONS = {
b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI010 230110',
b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719',
@ -1564,12 +1553,14 @@ FW_VERSIONS = {
b'\xf1\x00CE__ RDR ----- 1.00 1.01 99110-KL000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CE MFC AT CAN LHD 1.00 1.04 99211-KL000 221213',
b'\xf1\x00CE MFC AT EUR LHD 1.00 1.03 99211-KL000 221011',
b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213',
],
},
CAR.TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.01 99211-N9100 14A',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9220 14K',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 2.02 99211-N9000 14E',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9210 14G',

@ -1,9 +1,9 @@
import crcmod
from openpilot.selfdrive.car.hyundai.values import CAR, CHECKSUM, CAMERA_SCC_CAR
from openpilot.selfdrive.car.hyundai.values import CAR, HyundaiFlags
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
def create_lkas11(packer, frame, CP, apply_steer, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart):
@ -33,12 +33,12 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
values["CF_Lkas_MsgCount"] = frame % 0x10
if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022,
CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022,
CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN):
if CP.carFingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022,
CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022,
CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
@ -57,7 +57,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
# Likely cars lacking the ability to show individual lane lines in the dash
elif car_fingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
elif CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
# SysWarning 4 = keep hands on wheel + beep
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
@ -72,18 +72,18 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
values["CF_Lkas_LdwsActivemode"] = 0
values["CF_Lkas_FcwOpt_USM"] = 0
elif car_fingerprint == CAR.HYUNDAI_GENESIS:
elif CP.carFingerprint == CAR.HYUNDAI_GENESIS:
# This field is actually LdwsActivemode
# Genesis and Optima fault when forwarding while engaged
values["CF_Lkas_LdwsActivemode"] = 2
dat = packer.make_can_msg("LKAS11", 0, values)[2]
if car_fingerprint in CHECKSUM["crc8"]:
if CP.flags & HyundaiFlags.CHECKSUM_CRC8:
# CRC Checksum as seen on 2019 Hyundai Santa Fe
dat = dat[:6] + dat[7:8]
checksum = hyundai_checksum(dat)
elif car_fingerprint in CHECKSUM["6B"]:
elif CP.flags & HyundaiFlags.CHECKSUM_6B:
# Checksum of first 6 Bytes, as seen on 2018 Kia Sorento
checksum = sum(dat[:6]) % 256
else:
@ -95,7 +95,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
return packer.make_can_msg("LKAS11", 0, values)
def create_clu11(packer, frame, clu11, button, car_fingerprint):
def create_clu11(packer, frame, clu11, button, CP):
values = {s: clu11[s] for s in [
"CF_Clu_CruiseSwState",
"CF_Clu_CruiseSwMain",
@ -113,7 +113,7 @@ def create_clu11(packer, frame, clu11, button, car_fingerprint):
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = frame % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if car_fingerprint in CAMERA_SCC_CAR else 0
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
return packer.make_can_msg("CLU11", bus, values)

@ -1,6 +1,5 @@
from cereal import car
from panda import Panda
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
@ -76,196 +75,6 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN):
ret.mass = 1600. if candidate == CAR.AZERA_6TH_GEN else 1675. # ICE is ~average of 2.5L and 3.5L
ret.wheelbase = 2.885
ret.steerRatio = 14.5
elif candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022):
ret.mass = 3982. * CV.LB_TO_KG
ret.wheelbase = 2.766
# Values from optimizer
ret.steerRatio = 16.55 # 13.8 is spec end-to-end
ret.tireStiffnessFactor = 0.82
elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID):
ret.mass = 1513.
ret.wheelbase = 2.84
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.SONATA_LF:
ret.mass = 1536.
ret.wheelbase = 2.804
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
elif candidate == CAR.PALISADE:
ret.mass = 1999.
ret.wheelbase = 2.90
ret.steerRatio = 15.6 * 1.15
ret.tireStiffnessFactor = 0.63
elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30):
ret.mass = 1275.
ret.wheelbase = 2.7
ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535
ret.tireStiffnessFactor = 0.385 # stiffnessFactor settled on 1.0081302973865127
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.ELANTRA_2021:
ret.mass = 2800. * CV.LB_TO_KG
ret.wheelbase = 2.72
ret.steerRatio = 12.9
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.ELANTRA_HEV_2021:
ret.mass = 3017. * CV.LB_TO_KG
ret.wheelbase = 2.72
ret.steerRatio = 12.9
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.HYUNDAI_GENESIS:
ret.mass = 2060.
ret.wheelbase = 3.01
ret.steerRatio = 16.5
ret.minSteerSpeed = 60 * CV.KPH_TO_MS
elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN):
ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743., CAR.KONA_EV_2ND_GEN: 1740.}.get(candidate, 1275.)
ret.wheelbase = {CAR.KONA_EV_2ND_GEN: 2.66, }.get(candidate, 2.6)
ret.steerRatio = {CAR.KONA_EV_2ND_GEN: 13.6, }.get(candidate, 13.42) # Spec
ret.tireStiffnessFactor = 0.385
elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019, CAR.IONIQ_HEV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV):
ret.mass = 1490. # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx
ret.wheelbase = 2.7
ret.steerRatio = 13.73 # Spec
ret.tireStiffnessFactor = 0.385
if candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019):
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate in (CAR.IONIQ_5, CAR.IONIQ_6):
ret.mass = 1948
ret.wheelbase = 2.97
ret.steerRatio = 14.26
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.VELOSTER:
ret.mass = 2917. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75 * 1.15
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.TUCSON:
ret.mass = 3520. * CV.LB_TO_KG
ret.wheelbase = 2.67
ret.steerRatio = 14.00 * 1.15
ret.tireStiffnessFactor = 0.385
elif candidate == CAR.TUCSON_4TH_GEN:
ret.mass = 1630. # average
ret.wheelbase = 2.756
ret.steerRatio = 16.
ret.tireStiffnessFactor = 0.385
elif candidate == CAR.SANTA_CRUZ_1ST_GEN:
ret.mass = 1870. # weight from Limited trim - the only supported trim
ret.wheelbase = 3.000
# steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
ret.steerRatio = 14.2
elif candidate == CAR.CUSTIN_1ST_GEN:
ret.mass = 1690. # from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0
ret.wheelbase = 3.055
ret.steerRatio = 17.0 # from learner
elif candidate == CAR.STARIA_4TH_GEN:
ret.mass = 2205.
ret.wheelbase = 3.273
ret.steerRatio = 11.94 # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf
# Kia
elif candidate == CAR.KIA_SORENTO:
ret.mass = 1985.
ret.wheelbase = 2.78
ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable
elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_PHEV_2022):
ret.mass = 3543. * CV.LB_TO_KG # average of all the cars
ret.wheelbase = 2.7
ret.steerRatio = 13.6 # average of all the cars
ret.tireStiffnessFactor = 0.385
if candidate == CAR.KIA_NIRO_PHEV:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.KIA_SELTOS:
ret.mass = 1337.
ret.wheelbase = 2.63
ret.steerRatio = 14.56
elif candidate == CAR.KIA_SPORTAGE_5TH_GEN:
ret.mass = 1725. # weight from SX and above trims, average of FWD and AWD versions
ret.wheelbase = 2.756
ret.steerRatio = 13.6 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
elif candidate in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL):
ret.mass = 3558. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
if candidate == CAR.KIA_OPTIMA_G4:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate in (CAR.KIA_STINGER, CAR.KIA_STINGER_2022):
ret.mass = 1825.
ret.wheelbase = 2.78
ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable
elif candidate == CAR.KIA_FORTE:
ret.mass = 2878. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.KIA_CEED:
ret.mass = 1450.
ret.wheelbase = 2.65
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
elif candidate in (CAR.KIA_K5_2021, CAR.KIA_K5_HEV_2020):
ret.mass = 3381. * CV.LB_TO_KG
ret.wheelbase = 2.85
ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims)
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.KIA_EV6:
ret.mass = 2055
ret.wheelbase = 2.9
ret.steerRatio = 16.
ret.tireStiffnessFactor = 0.65
elif candidate in (CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN):
ret.wheelbase = 2.81
ret.steerRatio = 13.5 # average of the platforms
if candidate == CAR.KIA_SORENTO_4TH_GEN:
ret.mass = 3957 * CV.LB_TO_KG
else:
ret.mass = 4396 * CV.LB_TO_KG
elif candidate == CAR.KIA_CARNIVAL_4TH_GEN:
ret.mass = 2087.
ret.wheelbase = 3.09
ret.steerRatio = 14.23
elif candidate == CAR.KIA_K8_HEV_1ST_GEN:
ret.mass = 1630. # https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid
ret.wheelbase = 2.895
ret.steerRatio = 13.27 # guesstimate from K5 platform
# Genesis
elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN:
ret.mass = 2205
ret.wheelbase = 2.9
# https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1.
ret.steerRatio = 12.6
elif candidate == CAR.GENESIS_G70:
ret.steerActuatorDelay = 0.1
ret.mass = 1640.0
ret.wheelbase = 2.84
ret.steerRatio = 13.56
elif candidate == CAR.GENESIS_G70_2020:
ret.mass = 3673.0 * CV.LB_TO_KG
ret.wheelbase = 2.83
ret.steerRatio = 12.9
elif candidate == CAR.GENESIS_GV70_1ST_GEN:
ret.mass = 1950.
ret.wheelbase = 2.87
ret.steerRatio = 14.6
elif candidate == CAR.GENESIS_G80:
ret.mass = 2060.
ret.wheelbase = 3.01
ret.steerRatio = 16.5
elif candidate == CAR.GENESIS_G90:
ret.mass = 2200.
ret.wheelbase = 3.15
ret.steerRatio = 12.069
elif candidate == CAR.GENESIS_GV80:
ret.mass = 2258.
ret.wheelbase = 2.95
ret.steerRatio = 14.14
# *** longitudinal control ***
if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1]

@ -98,6 +98,23 @@ class TestHyundaiFingerprint(unittest.TestCase):
fws = data.draw(fw_strategy)
get_platform_codes(fws)
def test_expected_platform_codes(self):
# Ensures we don't accidentally add multiple platform codes for a car unless it is intentional
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
for ecu, fws in ecus.items():
if ecu[0] not in PLATFORM_CODE_ECUS:
continue
# Third and fourth character are usually EV/hybrid identifiers
codes = {code.split(b"-")[0][:2] for code, _ in get_platform_codes(fws)}
if car_model == CAR.PALISADE:
self.assertEqual(codes, {b"LX", b"ON"}, f"Car has unexpected platform codes: {car_model} {codes}")
elif car_model == CAR.KONA_EV and ecu[0] == Ecu.fwdCamera:
self.assertEqual(codes, {b"OE", b"OS"}, f"Car has unexpected platform codes: {car_model} {codes}")
else:
self.assertEqual(len(codes), 1, f"Car has multiple platform codes: {car_model} {codes}")
# Tests for platform codes, part numbers, and FW dates which Hyundai will use to fuzzy
# fingerprint in the absence of full FW matches:
def test_platform_code_ecus_available(self):

@ -1,11 +1,11 @@
import re
from dataclasses import dataclass
from enum import Enum, IntFlag, StrEnum
from dataclasses import dataclass, field
from enum import Enum, IntFlag
from cereal import car
from panda.python import uds
from openpilot.common.conversions import Conversions as CV
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 CarFootnote, CarHarness, CarInfo, CarParts, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
@ -52,92 +52,48 @@ class CarControllerParams:
class HyundaiFlags(IntFlag):
# Dynamic Flags
CANFD_HDA2 = 1
CANFD_ALT_BUTTONS = 2
CANFD_ALT_GEARS = 4
CANFD_CAMERA_SCC = 8
CANFD_ALT_GEARS = 2 ** 2
CANFD_CAMERA_SCC = 2 ** 3
ALT_LIMITS = 16
ENABLE_BLINKERS = 32
CANFD_ALT_GEARS_2 = 64
SEND_LFA = 128
USE_FCA = 256
CANFD_HDA2_ALT_STEERING = 512
HYBRID = 1024
EV = 2048
ALT_LIMITS = 2 ** 4
ENABLE_BLINKERS = 2 ** 5
CANFD_ALT_GEARS_2 = 2 ** 6
SEND_LFA = 2 ** 7
USE_FCA = 2 ** 8
CANFD_HDA2_ALT_STEERING = 2 ** 9
# these cars use a different gas signal
HYBRID = 2 ** 10
EV = 2 ** 11
class CAR(StrEnum):
# Hyundai
AZERA_6TH_GEN = "HYUNDAI AZERA 6TH GEN"
AZERA_HEV_6TH_GEN = "HYUNDAI AZERA HYBRID 6TH GEN"
ELANTRA = "HYUNDAI ELANTRA 2017"
ELANTRA_GT_I30 = "HYUNDAI I30 N LINE 2019 & GT 2018 DCT"
ELANTRA_2021 = "HYUNDAI ELANTRA 2021"
ELANTRA_HEV_2021 = "HYUNDAI ELANTRA HYBRID 2021"
HYUNDAI_GENESIS = "HYUNDAI GENESIS 2015-2016"
IONIQ = "HYUNDAI IONIQ HYBRID 2017-2019"
IONIQ_HEV_2022 = "HYUNDAI IONIQ HYBRID 2020-2022"
IONIQ_EV_LTD = "HYUNDAI IONIQ ELECTRIC LIMITED 2019"
IONIQ_EV_2020 = "HYUNDAI IONIQ ELECTRIC 2020"
IONIQ_PHEV_2019 = "HYUNDAI IONIQ PLUG-IN HYBRID 2019"
IONIQ_PHEV = "HYUNDAI IONIQ PHEV 2020"
KONA = "HYUNDAI KONA 2020"
KONA_EV = "HYUNDAI KONA ELECTRIC 2019"
KONA_EV_2022 = "HYUNDAI KONA ELECTRIC 2022"
KONA_EV_2ND_GEN = "HYUNDAI KONA ELECTRIC 2ND GEN"
KONA_HEV = "HYUNDAI KONA HYBRID 2020"
SANTA_FE = "HYUNDAI SANTA FE 2019"
SANTA_FE_2022 = "HYUNDAI SANTA FE 2022"
SANTA_FE_HEV_2022 = "HYUNDAI SANTA FE HYBRID 2022"
SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022"
SONATA = "HYUNDAI SONATA 2020"
SONATA_LF = "HYUNDAI SONATA 2019"
STARIA_4TH_GEN = "HYUNDAI STARIA 4TH GEN"
TUCSON = "HYUNDAI TUCSON 2019"
PALISADE = "HYUNDAI PALISADE 2020"
VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
IONIQ_5 = "HYUNDAI IONIQ 5 2022"
IONIQ_6 = "HYUNDAI IONIQ 6 2023"
TUCSON_4TH_GEN = "HYUNDAI TUCSON 4TH GEN"
SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN"
CUSTIN_1ST_GEN = "HYUNDAI CUSTIN 1ST GEN"
# Static flags
# Kia
KIA_FORTE = "KIA FORTE E 2018 & GT 2021"
KIA_K5_2021 = "KIA K5 2021"
KIA_K5_HEV_2020 = "KIA K5 HYBRID 2020"
KIA_K8_HEV_1ST_GEN = "KIA K8 HYBRID 1ST GEN"
KIA_NIRO_EV = "KIA NIRO EV 2020"
KIA_NIRO_EV_2ND_GEN = "KIA NIRO EV 2ND GEN"
KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019"
KIA_NIRO_PHEV_2022 = "KIA NIRO PLUG-IN HYBRID 2022"
KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021"
KIA_NIRO_HEV_2ND_GEN = "KIA NIRO HYBRID 2ND GEN"
KIA_OPTIMA_G4 = "KIA OPTIMA 4TH GEN"
KIA_OPTIMA_G4_FL = "KIA OPTIMA 4TH GEN FACELIFT"
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
KIA_OPTIMA_H_G4_FL = "KIA OPTIMA HYBRID 4TH GEN FACELIFT"
KIA_SELTOS = "KIA SELTOS 2021"
KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN"
KIA_SORENTO = "KIA SORENTO GT LINE 2018"
KIA_SORENTO_4TH_GEN = "KIA SORENTO 4TH GEN"
KIA_SORENTO_HEV_4TH_GEN = "KIA SORENTO HYBRID 4TH GEN"
KIA_STINGER = "KIA STINGER GT2 2018"
KIA_STINGER_2022 = "KIA STINGER 2022"
KIA_CEED = "KIA CEED INTRO ED 2019"
KIA_EV6 = "KIA EV6 2022"
KIA_CARNIVAL_4TH_GEN = "KIA CARNIVAL 4TH GEN"
# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points.
# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py
MANDO_RADAR = 2 ** 12
CANFD = 2 ** 13
# Genesis
GENESIS_GV60_EV_1ST_GEN = "GENESIS GV60 ELECTRIC 1ST GEN"
GENESIS_G70 = "GENESIS G70 2018"
GENESIS_G70_2020 = "GENESIS G70 2020"
GENESIS_GV70_1ST_GEN = "GENESIS GV70 1ST GEN"
GENESIS_G80 = "GENESIS G80 2017"
GENESIS_G90 = "GENESIS G90 2017"
GENESIS_GV80 = "GENESIS GV80 2023"
# The radar does SCC on these cars when HDA I, rather than the camera
RADAR_SCC = 2 ** 14
CAMERA_SCC = 2 ** 15
CHECKSUM_CRC8 = 2 ** 16
CHECKSUM_6B = 2 ** 17
# these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY = 2 ** 18
# these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc.
UNSUPPORTED_LONGITUDINAL = 2 ** 19
CANFD_NO_RADAR_DISABLE = 2 ** 20
CLUSTER_GEARS = 2 ** 21
TCU_GEARS = 2 ** 22
MIN_STEER_32_MPH = 2 ** 23
class Footnote(Enum):
@ -152,160 +108,498 @@ class HyundaiCarInfo(CarInfo):
package: str = "Smart Cruise Control (SCC)"
def init_make(self, CP: car.CarParams):
if CP.carFingerprint in CANFD_CAR:
if CP.flags & HyundaiFlags.CANFD:
self.footnotes.insert(0, Footnote.CANFD)
CAR_INFO: dict[str, HyundaiCarInfo | list[HyundaiCarInfo] | None] = {
CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.AZERA_HEV_6TH_GEN: [
HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CAR.ELANTRA: [
# TODO: 2017-18 could be Hyundai G
HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])),
],
CAR.ELANTRA_GT_I30: [
HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c",
car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.HYUNDAI_GENESIS: [
# TODO: check 2015 packages
HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
],
CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness
CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_EV_2020: HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.IONIQ_PHEV_2019: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])),
CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o])),
CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages
# TODO: this is the 2024 US MY, not yet released
CAR.KONA_EV_2ND_GEN: HyundaiCarInfo("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r])),
CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s",
car_parts=CarParts.common([CarHarness.hyundai_d])),
CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw",
car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.STARIA_4TH_GEN: HyundaiCarInfo("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.TUCSON: [
HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])),
],
CAR.PALISADE: [
HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.IONIQ_5: [
HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CAR.IONIQ_6: [
@dataclass
class HyundaiPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_kia_generic", None))
def init(self):
if self.flags & HyundaiFlags.MANDO_RADAR:
self.dbc_dict = dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated')
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_canfd", None))
def init(self):
self.flags |= HyundaiFlags.CANFD
class CAR(Platforms):
# Hyundai
AZERA_6TH_GEN = HyundaiPlatformConfig(
"HYUNDAI AZERA 6TH GEN",
HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=1600, wheelbase=2.885, steerRatio=14.5),
)
AZERA_HEV_6TH_GEN = HyundaiPlatformConfig(
"HYUNDAI AZERA HYBRID 6TH GEN",
[
HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5),
flags=HyundaiFlags.HYBRID,
)
ELANTRA = HyundaiPlatformConfig(
"HYUNDAI ELANTRA 2017",
[
# TODO: 2017-18 could be Hyundai G
HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])),
],
# steerRatio: 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535, stiffnessFactor settled on 1.0081302973865127
CarSpecs(mass=1275, wheelbase=2.7, steerRatio=15.4, tireStiffnessFactor=0.385),
flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.MIN_STEER_32_MPH,
)
ELANTRA_GT_I30 = HyundaiPlatformConfig(
"HYUNDAI I30 N LINE 2019 & GT 2018 DCT",
[
HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
ELANTRA.specs,
flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.MIN_STEER_32_MPH,
)
ELANTRA_2021 = HyundaiPlatformConfig(
"HYUNDAI ELANTRA 2021",
HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=2800 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CHECKSUM_CRC8,
)
ELANTRA_HEV_2021 = HyundaiPlatformConfig(
"HYUNDAI ELANTRA HYBRID 2021",
HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c",
car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=3017 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_GENESIS = HyundaiPlatformConfig(
"HYUNDAI GENESIS 2015-2016",
[
# TODO: check 2015 packages
HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
],
CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5, minSteerSpeed=60 * CV.KPH_TO_MS),
flags=HyundaiFlags.CHECKSUM_6B | HyundaiFlags.LEGACY,
)
IONIQ = HyundaiPlatformConfig(
"HYUNDAI IONIQ HYBRID 2017-2019",
HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH,
)
IONIQ_HEV_2022 = HyundaiPlatformConfig(
"HYUNDAI IONIQ HYBRID 2020-2022",
HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness,
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
IONIQ_EV_LTD = HyundaiPlatformConfig(
"HYUNDAI IONIQ ELECTRIC LIMITED 2019",
HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV | HyundaiFlags.LEGACY | HyundaiFlags.MIN_STEER_32_MPH,
)
IONIQ_EV_2020 = HyundaiPlatformConfig(
"HYUNDAI IONIQ ELECTRIC 2020",
HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV,
)
IONIQ_PHEV_2019 = HyundaiPlatformConfig(
"HYUNDAI IONIQ PLUG-IN HYBRID 2019",
HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH,
)
IONIQ_PHEV = HyundaiPlatformConfig(
"HYUNDAI IONIQ PHEV 2020",
HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID,
)
KONA = HyundaiPlatformConfig(
"HYUNDAI KONA 2020",
HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])),
CarSpecs(mass=1275, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CLUSTER_GEARS,
)
KONA_EV = HyundaiPlatformConfig(
"HYUNDAI KONA ELECTRIC 2019",
HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
CarSpecs(mass=1685, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV,
)
KONA_EV_2022 = HyundaiPlatformConfig(
"HYUNDAI KONA ELECTRIC 2022",
HyundaiCarInfo("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o])),
CarSpecs(mass=1743, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV,
)
KONA_EV_2ND_GEN = HyundaiCanFDPlatformConfig(
"HYUNDAI KONA ELECTRIC 2ND GEN",
HyundaiCarInfo("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r])),
CarSpecs(mass=1740, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
)
KONA_HEV = HyundaiPlatformConfig(
"HYUNDAI KONA HYBRID 2020",
HyundaiCarInfo("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages,
CarSpecs(mass=1425, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID,
)
SANTA_FE = HyundaiPlatformConfig(
"HYUNDAI SANTA FE 2019",
HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s",
car_parts=CarParts.common([CarHarness.hyundai_d])),
CarSpecs(mass=3982 * CV.LB_TO_KG, wheelbase=2.766, steerRatio=16.55, tireStiffnessFactor=0.82),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
SANTA_FE_2022 = HyundaiPlatformConfig(
"HYUNDAI SANTA FE 2022",
HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l])),
SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
)
SANTA_FE_HEV_2022 = HyundaiPlatformConfig(
"HYUNDAI SANTA FE HYBRID 2022",
HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
SANTA_FE_PHEV_2022 = HyundaiPlatformConfig(
"HYUNDAI SANTA FE PlUG-IN HYBRID 2022",
HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
SONATA = HyundaiPlatformConfig(
"HYUNDAI SONATA 2020",
HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw",
car_parts=CarParts.common([CarHarness.hyundai_a])),
CarSpecs(mass=1513, wheelbase=2.84, steerRatio=13.27 * 1.15, tireStiffnessFactor=0.65), # 15% higher at the center seems reasonable
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
SONATA_LF = HyundaiPlatformConfig(
"HYUNDAI SONATA 2019",
HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
CarSpecs(mass=1536, wheelbase=2.804, steerRatio=13.27 * 1.15), # 15% higher at the center seems reasonable
flags=HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.TCU_GEARS,
)
STARIA_4TH_GEN = HyundaiCanFDPlatformConfig(
"HYUNDAI STARIA 4TH GEN",
HyundaiCarInfo("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=2205, wheelbase=3.273, steerRatio=11.94), # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf
)
TUCSON = HyundaiPlatformConfig(
"HYUNDAI TUCSON 2019",
[
HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])),
],
CarSpecs(mass=3520 * CV.LB_TO_KG, wheelbase=2.67, steerRatio=16.1, tireStiffnessFactor=0.385),
flags=HyundaiFlags.TCU_GEARS,
)
PALISADE = HyundaiPlatformConfig(
"HYUNDAI PALISADE 2020",
[
HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
VELOSTER = HyundaiPlatformConfig(
"HYUNDAI VELOSTER 2019",
HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])),
CarSpecs(mass=2917 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75 * 1.15, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY | HyundaiFlags.TCU_GEARS,
)
SONATA_HYBRID = HyundaiPlatformConfig(
"HYUNDAI SONATA HYBRID 2021",
HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
SONATA.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
IONIQ_5 = HyundaiCanFDPlatformConfig(
"HYUNDAI IONIQ 5 2022",
[
HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
IONIQ_6 = HyundaiCanFDPlatformConfig(
"HYUNDAI IONIQ 6 2023",
HyundaiCarInfo("Hyundai Ioniq 6 (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])),
],
CAR.TUCSON_4TH_GEN: [
HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Hyundai Tucson Hybrid 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.CUSTIN_1ST_GEN: HyundaiCarInfo("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
IONIQ_5.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
)
TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
"HYUNDAI TUCSON 4TH GEN",
[
HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Hyundai Tucson Hybrid 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=16, tireStiffnessFactor=0.385),
)
SANTA_CRUZ_1ST_GEN = HyundaiCanFDPlatformConfig(
"HYUNDAI SANTA CRUZ 1ST GEN",
HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])),
# weight from Limited trim - the only supported trim, steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
CarSpecs(mass=1870, wheelbase=3, steerRatio=14.2),
)
CUSTIN_1ST_GEN = HyundaiPlatformConfig(
"HYUNDAI CUSTIN 1ST GEN",
HyundaiCarInfo("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=1690, wheelbase=3.055, steerRatio=17), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
flags=HyundaiFlags.CHECKSUM_CRC8,
)
# Kia
CAR.KIA_FORTE: [
HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K8_HEV_1ST_GEN: HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
CAR.KIA_NIRO_EV: [
HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])),
HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CAR.KIA_NIRO_EV_2ND_GEN: HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_NIRO_PHEV: [
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
],
CAR.KIA_NIRO_PHEV_2022: [
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
CAR.KIA_NIRO_HEV_2021: [
HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control",
car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018
CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])),
KIA_FORTE = HyundaiPlatformConfig(
"KIA FORTE E 2018 & GT 2021",
[
HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CarSpecs(mass=2878 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5)
)
KIA_K5_2021 = HyundaiPlatformConfig(
"KIA K5 2021",
HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
CarSpecs(mass=3381 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5), # 2021 Kia K5 Steering Ratio (all trims)
flags=HyundaiFlags.CHECKSUM_CRC8,
)
KIA_K5_HEV_2020 = HyundaiPlatformConfig(
"KIA K5 HYBRID 2020",
HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])),
KIA_K5_2021.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
KIA_K8_HEV_1ST_GEN = HyundaiCanFDPlatformConfig(
"KIA K8 HYBRID 1ST GEN",
HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
# mass: https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid, steerRatio: guesstimate from K5 platform
CarSpecs(mass=1630, wheelbase=2.895, steerRatio=13.27)
)
KIA_NIRO_EV = HyundaiPlatformConfig(
"KIA NIRO EV 2020",
[
HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])),
HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CarSpecs(mass=3543 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=13.6, tireStiffnessFactor=0.385), # average of all the cars
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV,
)
KIA_NIRO_EV_2ND_GEN = HyundaiCanFDPlatformConfig(
"KIA NIRO EV 2ND GEN",
HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
KIA_NIRO_EV.specs,
flags=HyundaiFlags.EV,
)
KIA_NIRO_PHEV = HyundaiPlatformConfig(
"KIA NIRO HYBRID 2019",
[
HyundaiCarInfo("Kia Niro Hybrid 2018", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
],
KIA_NIRO_EV.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.HYBRID | HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.MIN_STEER_32_MPH,
)
KIA_NIRO_PHEV_2022 = HyundaiPlatformConfig(
"KIA NIRO PLUG-IN HYBRID 2022",
[
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
KIA_NIRO_EV.specs,
flags=HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR,
)
KIA_NIRO_HEV_2021 = HyundaiPlatformConfig(
"KIA NIRO HYBRID 2021",
[
HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
KIA_NIRO_EV.specs,
flags=HyundaiFlags.HYBRID,
)
KIA_NIRO_HEV_2ND_GEN = HyundaiCanFDPlatformConfig(
"KIA NIRO HYBRID 2ND GEN",
HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])),
KIA_NIRO_EV.specs,
)
KIA_OPTIMA_G4 = HyundaiPlatformConfig(
"KIA OPTIMA 4TH GEN",
HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control",
car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018
CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY | HyundaiFlags.TCU_GEARS | HyundaiFlags.MIN_STEER_32_MPH,
)
KIA_OPTIMA_G4_FL = HyundaiPlatformConfig(
"KIA OPTIMA 4TH GEN FACELIFT",
HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])),
CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.TCU_GEARS,
)
# TODO: may support adjacent years. may have a non-zero minimum steering speed
CAR.KIA_OPTIMA_H: HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.KIA_OPTIMA_H_G4_FL: HyundaiCarInfo("Kia Optima Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SPORTAGE_5TH_GEN: [
HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CAR.KIA_SORENTO: [
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control & LKAS", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8",
car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.KIA_SORENTO_HEV_4TH_GEN: [
HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
],
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0",
car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.KIA_EV6: [
HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CAR.KIA_CARNIVAL_4TH_GEN: [
HyundaiCarInfo("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k]))
],
KIA_OPTIMA_H = HyundaiPlatformConfig(
"KIA OPTIMA HYBRID 2017 & SPORTS 2019",
HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
KIA_OPTIMA_H_G4_FL = HyundaiPlatformConfig(
"KIA OPTIMA HYBRID 4TH GEN FACELIFT",
HyundaiCarInfo("Kia Optima Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_h])),
CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.HYBRID | HyundaiFlags.UNSUPPORTED_LONGITUDINAL,
)
KIA_SELTOS = HyundaiPlatformConfig(
"KIA SELTOS 2021",
HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])),
CarSpecs(mass=1337, wheelbase=2.63, steerRatio=14.56),
flags=HyundaiFlags.CHECKSUM_CRC8,
)
KIA_SPORTAGE_5TH_GEN = HyundaiCanFDPlatformConfig(
"KIA SPORTAGE 5TH GEN",
[
HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
# weight from SX and above trims, average of FWD and AWD version, steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
CarSpecs(mass=1725, wheelbase=2.756, steerRatio=13.6),
)
KIA_SORENTO = HyundaiPlatformConfig(
"KIA SORENTO GT LINE 2018",
[
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control & LKAS", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8",
car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CarSpecs(mass=1985, wheelbase=2.78, steerRatio=14.4 * 1.1), # 10% higher at the center seems reasonable
flags=HyundaiFlags.CHECKSUM_6B | HyundaiFlags.UNSUPPORTED_LONGITUDINAL,
)
KIA_SORENTO_4TH_GEN = HyundaiCanFDPlatformConfig(
"KIA SORENTO 4TH GEN",
HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])),
CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_SORENTO_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
"KIA SORENTO HYBRID 4TH GEN",
[
HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
],
CarSpecs(mass=4395 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_STINGER = HyundaiPlatformConfig(
"KIA STINGER GT2 2018",
HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0",
car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=1825, wheelbase=2.78, steerRatio=14.4 * 1.15) # 15% higher at the center seems reasonable
)
KIA_STINGER_2022 = HyundaiPlatformConfig(
"KIA STINGER 2022",
HyundaiCarInfo("Kia Stinger 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
KIA_STINGER.specs,
)
KIA_CEED = HyundaiPlatformConfig(
"KIA CEED INTRO ED 2019",
HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])),
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY,
)
KIA_EV6 = HyundaiCanFDPlatformConfig(
"KIA EV6 2022",
[
HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
"KIA CARNIVAL 4TH GEN",
[
HyundaiCarInfo("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k]))
],
CarSpecs(mass=2087, wheelbase=3.09, steerRatio=14.23),
flags=HyundaiFlags.RADAR_SCC,
)
# Genesis
CAR.GENESIS_GV60_EV_1ST_GEN: [
HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.GENESIS_GV70_1ST_GEN: [
HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.GENESIS_GV80: HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
}
GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig(
"GENESIS GV60 ELECTRIC 1ST GEN",
[
HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=2205, wheelbase=2.9, steerRatio=12.6), # steerRatio: https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1.
flags=HyundaiFlags.EV,
)
GENESIS_G70 = HyundaiPlatformConfig(
"GENESIS G70 2018",
HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CarSpecs(mass=1640, wheelbase=2.84, steerRatio=13.56),
flags=HyundaiFlags.LEGACY,
)
GENESIS_G70_2020 = HyundaiPlatformConfig(
"GENESIS G70 2020",
HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CarSpecs(mass=3673 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=12.9),
flags=HyundaiFlags.MANDO_RADAR,
)
GENESIS_GV70_1ST_GEN = HyundaiCanFDPlatformConfig(
"GENESIS GV70 1ST GEN",
[
HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
CarSpecs(mass=1950, wheelbase=2.87, steerRatio=14.6),
flags=HyundaiFlags.RADAR_SCC,
)
GENESIS_G80 = HyundaiPlatformConfig(
"GENESIS G80 2017",
HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5),
flags=HyundaiFlags.LEGACY,
)
GENESIS_G90 = HyundaiPlatformConfig(
"GENESIS G90 2017",
HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
CarSpecs(mass=2200, wheelbase=3.15, steerRatio=12.069),
)
GENESIS_GV80 = HyundaiCanFDPlatformConfig(
"GENESIS GV80 2023",
HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
CarSpecs(mass=2258, wheelbase=2.95, steerRatio=14.14),
flags=HyundaiFlags.RADAR_SCC,
)
class Buttons:
NONE = 0
@ -438,7 +732,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac],
whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac, Ecu.eps],
bus=0,
auxiliary=True,
),
@ -508,6 +802,13 @@ FW_QUERY_CONFIG = FwQueryConfig(
obd_multiplexing=False,
),
],
# We lose these ECUs without the comma power on these cars.
# Note that we still attempt to match with them when they are present
non_essential_ecus={
Ecu.transmission: [CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.PALISADE, CAR.SONATA],
Ecu.engine: [CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.PALISADE, CAR.SONATA],
Ecu.abs: [CAR.PALISADE, CAR.SONATA],
},
extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms
(Ecu.parkingAdas, 0x7b1, None), # ADAS Parking ECU (may exist on all platforms)
@ -518,118 +819,36 @@ FW_QUERY_CONFIG = FwQueryConfig(
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
)
CHECKSUM = {
"crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021,
CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022,
CAR.KIA_K5_HEV_2020, CAR.CUSTIN_1ST_GEN],
"6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS],
"crc8": CAR.with_flags(HyundaiFlags.CHECKSUM_CRC8),
"6B": CAR.with_flags(HyundaiFlags.CHECKSUM_6B),
}
CAN_GEARS = {
# which message has the gear. hybrid and EV use ELECT_GEAR
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
"use_cluster_gears": CAR.with_flags(HyundaiFlags.CLUSTER_GEARS),
"use_tcu_gears": CAR.with_flags(HyundaiFlags.TCU_GEARS),
}
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN,
CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN,
CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KONA_EV_2ND_GEN, CAR.KIA_K8_HEV_1ST_GEN,
CAR.STARIA_4TH_GEN}
# The radar does SCC on these cars when HDA I, rather than the camera
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN}
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC)
# These CAN FD cars do not accept communication control to disable the ADAS ECU,
# responds with 0x7F2822 - 'conditions not correct'
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = {CAR.IONIQ_6, CAR.KONA_EV_2ND_GEN}
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE)
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
# these cars use a different gas signal
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ,
CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KIA_K5_HEV_2020,
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.AZERA_HEV_6TH_GEN, CAR.KIA_NIRO_PHEV_2022}
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022,
CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KONA_EV_2ND_GEN}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.KIA_OPTIMA_G4,
CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022,
CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30}
# these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc.
UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4_FL,
CAR.KIA_OPTIMA_H_G4_FL}
# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points.
# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py
DBC = {
CAR.AZERA_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.AZERA_HEV_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_GT_I30: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_2021: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G70: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G70_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.GENESIS_G80: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G90: dbc_dict('hyundai_kia_generic', None),
CAR.HYUNDAI_GENESIS: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_PHEV_2019: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_PHEV: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_EV_2020: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_EV_LTD: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.IONIQ: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_HEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_HEV_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4_FL: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H_G4_FL: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_STINGER_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KONA: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_HEV: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.SANTA_FE_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE_HEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.TUCSON: dbc_dict('hyundai_kia_generic', None),
CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_EV6: dbc_dict('hyundai_canfd', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.TUCSON_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_6: dbc_dict('hyundai_canfd', None),
CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV60_EV_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_NIRO_HEV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_NIRO_EV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV80: dbc_dict('hyundai_canfd', None),
CAR.KIA_CARNIVAL_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_HEV_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KONA_EV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_K8_HEV_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.CUSTIN_1ST_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_NIRO_PHEV_2022: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.STARIA_4TH_GEN: dbc_dict('hyundai_canfd', None),
}
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)
HYBRID_CAR = CAR.with_flags(HyundaiFlags.HYBRID)
EV_CAR = CAR.with_flags(HyundaiFlags.EV)
LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY)
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
DBC = CAR.create_dbc_map()
if __name__ == "__main__":
CAR.print_debug(HyundaiFlags)

@ -112,15 +112,14 @@ class CarInterfaceBase(ABC):
def get_params(cls, candidate: Platform, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool):
ret = CarInterfaceBase.get_std_params(candidate)
if hasattr(candidate, "config"):
if candidate.config.specs is not None:
ret.mass = candidate.config.specs.mass
ret.wheelbase = candidate.config.specs.wheelbase
ret.steerRatio = candidate.config.specs.steerRatio
ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio
ret.minEnableSpeed = candidate.config.specs.minEnableSpeed
ret.minSteerSpeed = candidate.config.specs.minSteerSpeed
ret.flags |= int(candidate.config.flags)
ret.mass = candidate.config.specs.mass
ret.wheelbase = candidate.config.specs.wheelbase
ret.steerRatio = candidate.config.specs.steerRatio
ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio
ret.minEnableSpeed = candidate.config.specs.minEnableSpeed
ret.minSteerSpeed = candidate.config.specs.minSteerSpeed
ret.tireStiffnessFactor = candidate.config.specs.tireStiffnessFactor
ret.flags |= int(candidate.config.flags)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs)
@ -335,7 +334,6 @@ class RadarInterfaceBase(ABC):
self.delay = 0
self.radar_ts = CP.radarTimeStep
self.frame = 0
self.no_radar_sleep = 'NO_RADAR_SLEEP' in os.environ
def update(self, can_strings):
self.frame += 1

@ -36,13 +36,13 @@ class CarController(CarControllerBase):
if self.frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7):
# Cancel Stock ACC if it's enabled while OP is disengaged
# Send at a rate of 10hz until we sync with stock ACC state
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL))
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.CANCEL))
else:
self.brake_counter = 0
if CC.cruiseControl.resume and self.frame % 5 == 0:
# Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds
# Send Resume button when planner wants car to move
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME))
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.RESUME))
self.apply_steer_last = apply_steer
@ -55,7 +55,7 @@ class CarController(CarControllerBase):
can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required))
# send steering command
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint,
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP,
self.frame, apply_steer, CS.cam_lkas))
new_actuators = CC.actuators.copy()

@ -3,7 +3,7 @@ from openpilot.common.conversions import Conversions as CV
from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, GEN1
from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags
class CarState(CarStateBase):
def __init__(self, CP):
@ -116,7 +116,7 @@ class CarState(CarStateBase):
("WHEEL_SPEEDS", 100),
]
if CP.carFingerprint in GEN1:
if CP.flags & MazdaFlags.GEN1:
messages += [
("ENGINE_DATA", 100),
("CRZ_CTRL", 50),
@ -136,7 +136,7 @@ class CarState(CarStateBase):
def get_cam_can_parser(CP):
messages = []
if CP.carFingerprint in GEN1:
if CP.flags & MazdaFlags.GEN1:
messages += [
# sig_address, frequency
("CAM_LANEINFO", 2),

@ -20,27 +20,9 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1
ret.steerLimitTimer = 0.8
ret.tireStiffnessFactor = 0.70 # not optimized yet
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.CX5, CAR.CX5_2022):
ret.mass = 3655 * CV.LB_TO_KG
ret.wheelbase = 2.7
ret.steerRatio = 15.5
elif candidate in (CAR.CX9, CAR.CX9_2021):
ret.mass = 4217 * CV.LB_TO_KG
ret.wheelbase = 3.1
ret.steerRatio = 17.6
elif candidate == CAR.MAZDA3:
ret.mass = 2875 * CV.LB_TO_KG
ret.wheelbase = 2.7
ret.steerRatio = 14.0
elif candidate == CAR.MAZDA6:
ret.mass = 3443 * CV.LB_TO_KG
ret.wheelbase = 2.83
ret.steerRatio = 15.5
if candidate not in (CAR.CX5_2022, ):
ret.minSteerSpeed = LKAS_LIMITS.DISABLE_SPEED * CV.KPH_TO_MS

@ -1,7 +1,7 @@
from openpilot.selfdrive.car.mazda.values import GEN1, Buttons
from openpilot.selfdrive.car.mazda.values import Buttons, MazdaFlags
def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas):
def create_steering_control(packer, CP, frame, apply_steer, lkas):
tmp = apply_steer + 2048
@ -45,7 +45,7 @@ def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas):
csum = csum % 256
values = {}
if car_fingerprint in GEN1:
if CP.flags & MazdaFlags.GEN1:
values = {
"LKAS_REQUEST": apply_steer,
"CTR": ctr,
@ -88,12 +88,12 @@ def create_alert_command(packer, cam_msg: dict, ldw: bool, steer_required: bool)
return packer.make_can_msg("CAM_LANEINFO", 0, values)
def create_button_cmd(packer, car_fingerprint, counter, button):
def create_button_cmd(packer, CP, counter, button):
can = int(button == Buttons.CANCEL)
res = int(button == Buttons.RESUME)
if car_fingerprint in GEN1:
if CP.flags & MazdaFlags.GEN1:
values = {
"CAN_OFF": can,
"CAN_OFF_INV": (can + 1) % 2,

@ -1,8 +1,9 @@
from dataclasses import dataclass, field
from enum import StrEnum
from enum import IntFlag
from cereal import car
from openpilot.selfdrive.car import dbc_dict
from openpilot.common.conversions import Conversions as CV
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.fw_query_definitions import FwQueryConfig, Request, StdQueries
@ -25,29 +26,60 @@ class CarControllerParams:
pass
class CAR(StrEnum):
CX5 = "MAZDA CX-5"
CX9 = "MAZDA CX-9"
MAZDA3 = "MAZDA 3"
MAZDA6 = "MAZDA 6"
CX9_2021 = "MAZDA CX-9 2021"
CX5_2022 = "MAZDA CX-5 2022"
@dataclass
class MazdaCarInfo(CarInfo):
package: str = "All"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.mazda]))
CAR_INFO: dict[str, MazdaCarInfo | list[MazdaCarInfo]] = {
CAR.CX5: MazdaCarInfo("Mazda CX-5 2017-21"),
CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"),
CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"),
CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"),
CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4"),
CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022-24"),
}
@dataclass(frozen=True, kw_only=True)
class MazdaCarSpecs(CarSpecs):
tireStiffnessFactor: float = 0.7 # not optimized yet
class MazdaFlags(IntFlag):
# Static flags
# Gen 1 hardware: same CAN messages and same camera
GEN1 = 1
@dataclass
class MazdaPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('mazda_2017', None))
flags: int = MazdaFlags.GEN1
class CAR(Platforms):
CX5 = MazdaPlatformConfig(
"MAZDA CX-5",
MazdaCarInfo("Mazda CX-5 2017-21"),
MazdaCarSpecs(mass=3655 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.5)
)
CX9 = MazdaPlatformConfig(
"MAZDA CX-9",
MazdaCarInfo("Mazda CX-9 2016-20"),
MazdaCarSpecs(mass=4217 * CV.LB_TO_KG, wheelbase=3.1, steerRatio=17.6)
)
MAZDA3 = MazdaPlatformConfig(
"MAZDA 3",
MazdaCarInfo("Mazda 3 2017-18"),
MazdaCarSpecs(mass=2875 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=14.0)
)
MAZDA6 = MazdaPlatformConfig(
"MAZDA 6",
MazdaCarInfo("Mazda 6 2017-20"),
MazdaCarSpecs(mass=3443 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=15.5)
)
CX9_2021 = MazdaPlatformConfig(
"MAZDA CX-9 2021",
MazdaCarInfo("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4"),
CX9.specs
)
CX5_2022 = MazdaPlatformConfig(
"MAZDA CX-5 2022",
MazdaCarInfo("Mazda CX-5 2022-24"),
CX5.specs,
)
class LKAS_LIMITS:
@ -75,15 +107,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
)
DBC = {
CAR.CX5: dbc_dict('mazda_2017', None),
CAR.CX9: dbc_dict('mazda_2017', None),
CAR.MAZDA3: dbc_dict('mazda_2017', None),
CAR.MAZDA6: dbc_dict('mazda_2017', None),
CAR.CX9_2021: dbc_dict('mazda_2017', None),
CAR.CX5_2022: dbc_dict('mazda_2017', None),
}
# Gen 1 hardware: same CAN messages and same camera
GEN1 = {CAR.CX5, CAR.CX9, CAR.CX9_2021, CAR.MAZDA3, CAR.MAZDA6, CAR.CX5_2022}
DBC = CAR.create_dbc_map()

@ -1,12 +1,10 @@
from enum import StrEnum
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms
from openpilot.selfdrive.car.docs_definitions import CarInfo
class CAR(StrEnum):
MOCK = 'mock'
CAR_INFO: dict[str, CarInfo | list[CarInfo] | None] = {
CAR.MOCK: None,
}
class CAR(Platforms):
MOCK = PlatformConfig(
'mock',
None,
CarSpecs(mass=1700, wheelbase=2.7, steerRatio=13),
{}
)

@ -40,13 +40,13 @@ class CAR(Platforms):
XTRAIL = NissanPlaformConfig(
"NISSAN X-TRAIL 2017",
NissanCarInfo("Nissan X-Trail 2017"),
specs=NissanCarSpecs(mass=1610, wheelbase=2.705)
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)
NissanCarSpecs(mass=1610, wheelbase=2.705),
dbc_dict('nissan_leaf_2018_generated', None),
)
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
# Currently the only known difference between them is the inverted seatbelt signal.
@ -54,16 +54,15 @@ class CAR(Platforms):
ROGUE = NissanPlaformConfig(
"NISSAN ROGUE 2019",
NissanCarInfo("Nissan Rogue 2018-20"),
specs=NissanCarSpecs(mass=1610, wheelbase=2.705)
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)
NissanCarSpecs(mass=1492, wheelbase=2.824)
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()
# Default diagnostic session

@ -10,47 +10,45 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs):
platform_flags = candidate.config.flags
ret.carName = "subaru"
ret.radarUnavailable = True
# for HYBRID CARS to be upstreamed, we need:
# - replacement for ES_Distance so we can cancel the cruise control
# - to find the Cruise_Activated bit from the car
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc)
ret.dashcamOnly = bool(platform_flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
ret.autoResumeSng = False
# Detect infotainment message sent from the camera
if not (platform_flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]:
if not (ret.flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]:
ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
if platform_flags & SubaruFlags.PREGLOBAL:
if ret.flags & SubaruFlags.PREGLOBAL:
ret.enableBsm = 0x25c in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)]
else:
ret.enableBsm = 0x228 in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)]
if platform_flags & SubaruFlags.GLOBAL_GEN2:
if ret.flags & SubaruFlags.GLOBAL_GEN2:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1
if platform_flags & SubaruFlags.LKAS_ANGLE:
if ret.flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = car.CarParams.SteerControlType.angle
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.ASCENT, CAR.ASCENT_2023):
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00003
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]]
elif candidate == CAR.IMPREZA:
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00005
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
@ -85,12 +83,11 @@ class CarInterface(CarInterfaceBase):
else:
raise ValueError(f"unknown car: {candidate}")
LONG_UNAVAILABLE = SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL| SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID
ret.experimentalLongitudinalAvailable = not (platform_flags & LONG_UNAVAILABLE)
ret.experimentalLongitudinalAvailable = not (ret.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL |
SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
if platform_flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
if ret.flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
if ret.openpilotLongitudinalControl:

@ -53,8 +53,11 @@ class CarControllerParams:
class SubaruFlags(IntFlag):
# Detected flags
SEND_INFOTAINMENT = 1
DISABLE_EYESIGHT = 2
# Static flags
GLOBAL_GEN2 = 4
# Cars that temporarily fault when steering angle rate is greater than some threshold.
@ -106,24 +109,31 @@ class SubaruPlatformConfig(PlatformConfig):
self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None)
@dataclass
class SubaruGen2PlatformConfig(SubaruPlatformConfig):
def init(self):
super().init()
self.flags |= SubaruFlags.GLOBAL_GEN2
if not (self.flags & SubaruFlags.LKAS_ANGLE):
self.flags |= SubaruFlags.STEER_RATE_LIMITED
class CAR(Platforms):
# Global platform
ASCENT = SubaruPlatformConfig(
"SUBARU ASCENT LIMITED 2019",
SubaruCarInfo("Subaru Ascent 2019-21", "All"),
specs=CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5),
CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5),
)
OUTBACK = SubaruPlatformConfig(
OUTBACK = SubaruGen2PlatformConfig(
"SUBARU OUTBACK 6TH GEN",
SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
)
LEGACY = SubaruPlatformConfig(
LEGACY = SubaruGen2PlatformConfig(
"SUBARU LEGACY 7TH GEN",
SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
OUTBACK.specs,
)
IMPREZA = SubaruPlatformConfig(
"SUBARU IMPREZA LIMITED 2019",
@ -132,7 +142,7 @@ class CAR(Platforms):
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
],
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15),
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15),
)
IMPREZA_2020 = SubaruPlatformConfig(
"SUBARU IMPREZA SPORT 2020",
@ -141,75 +151,75 @@ class CAR(Platforms):
SubaruCarInfo("Subaru Crosstrek 2020-23"),
SubaruCarInfo("Subaru XV 2020-21"),
],
specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
)
# TODO: is there an XV and Impreza too?
CROSSTREK_HYBRID = SubaruPlatformConfig(
"SUBARU CROSSTREK HYBRID 2020",
SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.HYBRID,
)
FORESTER = SubaruPlatformConfig(
"SUBARU FORESTER 2019",
SubaruCarInfo("Subaru Forester 2019-21", "All"),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
)
FORESTER_HYBRID = SubaruPlatformConfig(
"SUBARU FORESTER HYBRID 2020",
SubaruCarInfo("Subaru Forester Hybrid 2020"),
specs=FORESTER.specs,
FORESTER.specs,
flags=SubaruFlags.HYBRID,
)
# Pre-global
FORESTER_PREGLOBAL = SubaruPlatformConfig(
"SUBARU FORESTER 2017 - 2018",
SubaruCarInfo("Subaru Forester 2017-18"),
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
dbc_dict('subaru_forester_2017_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
flags=SubaruFlags.PREGLOBAL,
)
LEGACY_PREGLOBAL = SubaruPlatformConfig(
"SUBARU LEGACY 2015 - 2018",
SubaruCarInfo("Subaru Legacy 2015-18"),
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
dbc_dict('subaru_outback_2015_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
flags=SubaruFlags.PREGLOBAL,
)
OUTBACK_PREGLOBAL = SubaruPlatformConfig(
"SUBARU OUTBACK 2015 - 2017",
SubaruCarInfo("Subaru Outback 2015-17"),
FORESTER_PREGLOBAL.specs,
dbc_dict('subaru_outback_2015_generated', None),
specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
)
OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig(
"SUBARU OUTBACK 2018 - 2019",
SubaruCarInfo("Subaru Outback 2018-19"),
FORESTER_PREGLOBAL.specs,
dbc_dict('subaru_outback_2019_generated', None),
specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
)
# Angle LKAS
FORESTER_2022 = SubaruPlatformConfig(
"SUBARU FORESTER 2022",
SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
specs=FORESTER.specs,
FORESTER.specs,
flags=SubaruFlags.LKAS_ANGLE,
)
OUTBACK_2023 = SubaruPlatformConfig(
OUTBACK_2023 = SubaruGen2PlatformConfig(
"SUBARU OUTBACK 7TH GEN",
SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE,
OUTBACK.specs,
flags=SubaruFlags.LKAS_ANGLE,
)
ASCENT_2023 = SubaruPlatformConfig(
ASCENT_2023 = SubaruGen2PlatformConfig(
"SUBARU ASCENT 2023",
SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=ASCENT.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE,
ASCENT.specs,
flags=SubaruFlags.LKAS_ANGLE,
)
@ -253,5 +263,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
}
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()
if __name__ == "__main__":
CAR.print_debug(SubaruFlags)

@ -2,7 +2,7 @@ import copy
from collections import deque
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS
from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS
from openpilot.selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
@ -37,13 +37,15 @@ class CarState(CarStateBase):
ret.brakePressed = bool(cp.vl["BrakeMessage"]["driverBrakeStatus"] != 1)
# Steering wheel
self.hands_on_level = cp.vl["EPAS_sysStatus"]["EPAS_handsOnLevel"]
self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacErrorCode"]), None)
steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacStatus"]), None)
epas_status = cp_cam.vl["EPAS3P_sysStatus"] if self.CP.carFingerprint == CAR.MODELS_RAVEN else cp.vl["EPAS_sysStatus"]
ret.steeringAngleDeg = -cp.vl["EPAS_sysStatus"]["EPAS_internalSAS"]
self.hands_on_level = epas_status["EPAS_handsOnLevel"]
self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(epas_status["EPAS_eacErrorCode"]), None)
steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(epas_status["EPAS_eacStatus"]), None)
ret.steeringAngleDeg = -epas_status["EPAS_internalSAS"]
ret.steeringRateDeg = -cp.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"] # This is from a different angle sensor, and at different rate
ret.steeringTorque = -cp.vl["EPAS_sysStatus"]["EPAS_torsionBarTorque"]
ret.steeringTorque = -epas_status["EPAS_torsionBarTorque"]
ret.steeringPressed = (self.hands_on_level > 0)
ret.steerFaultPermanent = steer_status == "EAC_FAULT"
ret.steerFaultTemporary = (self.steer_warning not in ("EAC_ERROR_IDLE", "EAC_ERROR_HANDS_ON"))
@ -85,7 +87,10 @@ class CarState(CarStateBase):
ret.rightBlinker = (cp.vl["GTW_carState"]["BC_indicatorRStatus"] == 1)
# Seatbelt
ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1)
if self.CP.carFingerprint == CAR.MODELS_RAVEN:
ret.seatbeltUnlatched = (cp.vl["DriverSeat"]["buckleStatus"] != 1)
else:
ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1)
# TODO: blindspot
@ -111,9 +116,14 @@ class CarState(CarStateBase):
("DI_state", 10),
("STW_ACTN_RQ", 10),
("GTW_carState", 10),
("SDM1", 10),
("BrakeMessage", 50),
]
if CP.carFingerprint == CAR.MODELS_RAVEN:
messages.append(("DriverSeat", 20))
else:
messages.append(("SDM1", 10))
return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.chassis)
@staticmethod
@ -122,4 +132,8 @@ class CarState(CarStateBase):
# sig_address, frequency
("DAS_control", 40),
]
if CP.carFingerprint == CAR.MODELS_RAVEN:
messages.append(("EPAS3P_sysStatus", 100))
return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.autopilot_chassis)

@ -25,4 +25,15 @@ FW_VERSIONS = {
b'\x10#\x01',
],
},
CAR.MODELS_RAVEN: {
(Ecu.electricBrakeBooster, 0x64d, None): [
b'1037123-00-A',
],
(Ecu.fwdRadar, 0x671, None): [
b'\x01\x00\x99\x02\x01\x00\x10\x00\x00AP8.3.03\x00\x10',
],
(Ecu.eps, 0x730, None): [
b'SX_0.0.0 (99),SR013.7',
],
},
}

@ -28,27 +28,20 @@ class CarInterface(CarInterfaceBase):
# Check if we have messages on an auxiliary panda, and that 0x2bf (DAS_control) is present on the AP powertrain bus
# If so, we assume that it is connected to the longitudinal harness.
flags = (Panda.FLAG_TESLA_RAVEN if candidate == CAR.MODELS_RAVEN else 0)
if (CANBUS.autopilot_powertrain in fingerprint.keys()) and (0x2bf in fingerprint[CANBUS.autopilot_powertrain].keys()):
ret.openpilotLongitudinalControl = True
flags |= Panda.FLAG_TESLA_LONG_CONTROL
ret.safetyConfigs = [
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL),
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN),
get_safety_config(car.CarParams.SafetyModel.tesla, flags),
get_safety_config(car.CarParams.SafetyModel.tesla, flags | Panda.FLAG_TESLA_POWERTRAIN),
]
else:
ret.openpilotLongitudinalControl = False
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, 0)]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, flags)]
ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.25
if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS):
ret.mass = 2100.
ret.wheelbase = 2.959
ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 15.0
else:
raise ValueError(f"Unsupported car: {candidate}")
return ret
def _update(self, c):

@ -1,38 +1,33 @@
#!/usr/bin/env python3
from cereal import car
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS
from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
RADAR_MSGS_A = list(range(0x310, 0x36E, 3))
RADAR_MSGS_B = list(range(0x311, 0x36F, 3))
NUM_POINTS = len(RADAR_MSGS_A)
def get_radar_can_parser(CP):
# Status messages
messages = [
('TeslaRadarSguInfo', 10),
]
# Radar tracks. There are also raw point clouds available,
# we don't use those.
for i in range(NUM_POINTS):
msg_id_a = RADAR_MSGS_A[i]
msg_id_b = RADAR_MSGS_B[i]
messages.extend([
(msg_id_a, 8),
(msg_id_b, 8),
])
return CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.rcp = get_radar_can_parser(CP)
self.CP = CP
if CP.carFingerprint == CAR.MODELS_RAVEN:
messages = [('RadarStatus', 16)]
self.num_points = 40
self.trigger_msg = 1119
else:
messages = [('TeslaRadarSguInfo', 10)]
self.num_points = 32
self.trigger_msg = 878
for i in range(self.num_points):
messages.extend([
(f'RadarPoint{i}_A', 16),
(f'RadarPoint{i}_B', 16),
])
self.rcp = CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar)
self.updated_messages = set()
self.track_id = 0
self.trigger_msg = RADAR_MSGS_B[-1]
def update(self, can_strings):
if self.rcp is None:
@ -48,17 +43,24 @@ class RadarInterface(RadarInterfaceBase):
# Errors
errors = []
sgu_info = self.rcp.vl['TeslaRadarSguInfo']
if not self.rcp.can_valid:
errors.append('canError')
if sgu_info['RADC_HWFail'] or sgu_info['RADC_SGUFail'] or sgu_info['RADC_SensorDirty']:
errors.append('fault')
if self.CP.carFingerprint == CAR.MODELS_RAVEN:
radar_status = self.rcp.vl['RadarStatus']
if radar_status['sensorBlocked'] or radar_status['shortTermUnavailable'] or radar_status['vehDynamicsError']:
errors.append('fault')
else:
radar_status = self.rcp.vl['TeslaRadarSguInfo']
if radar_status['RADC_HWFail'] or radar_status['RADC_SGUFail'] or radar_status['RADC_SensorDirty']:
errors.append('fault')
ret.errors = errors
# Radar tracks
for i in range(NUM_POINTS):
msg_a = self.rcp.vl[RADAR_MSGS_A[i]]
msg_b = self.rcp.vl[RADAR_MSGS_B[i]]
for i in range(self.num_points):
msg_a = self.rcp.vl[f'RadarPoint{i}_A']
msg_b = self.rcp.vl[f'RadarPoint{i}_B']
# Make sure msg A and B are together
if msg_a['Index'] != msg_b['Index2']:

@ -1,8 +1,7 @@
from collections import namedtuple
from enum import StrEnum
from cereal import car
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, PlatformConfig, Platforms, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarInfo
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
@ -10,22 +9,25 @@ Ecu = car.CarParams.Ecu
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
class CAR(StrEnum):
AP1_MODELS = 'TESLA AP1 MODEL S'
AP2_MODELS = 'TESLA AP2 MODEL S'
CAR_INFO: dict[str, CarInfo | list[CarInfo]] = {
CAR.AP1_MODELS: CarInfo("Tesla AP1 Model S", "All"),
CAR.AP2_MODELS: CarInfo("Tesla AP2 Model S", "All"),
}
DBC = {
CAR.AP2_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
CAR.AP1_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
}
class CAR(Platforms):
AP1_MODELS = PlatformConfig(
'TESLA AP1 MODEL S',
CarInfo("Tesla AP1 Model S", "All"),
CarSpecs(mass=2100., wheelbase=2.959, steerRatio=15.0),
dbc_dict('tesla_powertrain', 'tesla_radar_bosch_generated', chassis_dbc='tesla_can')
)
AP2_MODELS = PlatformConfig(
'TESLA AP2 MODEL S',
CarInfo("Tesla AP2 Model S", "All"),
AP1_MODELS.specs,
AP1_MODELS.dbc_dict
)
MODELS_RAVEN = PlatformConfig(
'TESLA MODEL S RAVEN',
CarInfo("Tesla Model S Raven", "All"),
AP1_MODELS.specs,
dbc_dict('tesla_powertrain', 'tesla_radar_continental_generated', chassis_dbc='tesla_can')
)
FW_QUERY_CONFIG = FwQueryConfig(
requests=[
@ -36,6 +38,13 @@ FW_QUERY_CONFIG = FwQueryConfig(
rx_offset=0x08,
bus=0,
),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.SUPPLIER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.SUPPLIER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.eps],
rx_offset=0x08,
bus=0,
),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.UDS_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.UDS_VERSION_RESPONSE],
@ -46,7 +55,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
]
)
class CANBUS:
# Lateral harness
chassis = 0
@ -88,3 +96,6 @@ class CarControllerParams:
def __init__(self, CP):
pass
DBC = CAR.create_dbc_map()

@ -229,6 +229,7 @@ routes = [
CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1),
CarTestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1),
CarTestRoute("ffcd23abbbd02219|2024-02-28--14-59-38", VOLKSWAGEN.CADDY_MK3),
CarTestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7), # Stock ACC
CarTestRoute("3cfdec54aa035f3f|2022-10-13--14-58-58", VOLKSWAGEN.GOLF_MK7), # openpilot longitudinal
CarTestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7),
@ -288,6 +289,7 @@ routes = [
CarTestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS),
CarTestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS),
CarTestRoute("66c1699b7697267d/2024-03-03--13-09-53", TESLA.MODELS_RAVEN),
# Segments that test specific issues
# Controls mismatch due to interceptor threshold

@ -22,7 +22,7 @@ from openpilot.selfdrive.test.fuzzy_generation import DrawType, FuzzyGenerator
ALL_ECUS = list({ecu for ecus in FW_VERSIONS.values() for ecu in ecus.keys()})
MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '20'))
MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '40'))
def get_fuzzy_car_interface_args(draw: DrawType) -> dict:

@ -5,10 +5,11 @@ import re
import unittest
from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info
from openpilot.selfdrive.car.docs_definitions import Cable, Column, PartType, Star
from openpilot.selfdrive.car.honda.values import CAR as HONDA
from openpilot.selfdrive.car.values import PLATFORMS
from openpilot.selfdrive.debug.dump_car_info import dump_car_info
from openpilot.selfdrive.debug.print_docs_diff import print_car_info_diff
@ -42,10 +43,10 @@ class TestCarDocs(unittest.TestCase):
make_model_years[make_model].append(year)
def test_missing_car_info(self):
all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys()
all_car_info_platforms = [name for name, config in PLATFORMS.items()]
for platform in sorted(interfaces.keys()):
with self.subTest(platform=platform):
self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't exist in CarInfo")
self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't have a CarInfo entry")
def test_naming_conventions(self):
# Asserts market-standard car naming conventions by brand

@ -263,7 +263,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
print(f'get_vin {name} case, query time={self.total_time / self.N} seconds')
def test_fw_query_timing(self):
total_ref_time = {1: 8.3, 2: 9.2}
total_ref_time = {1: 8.4, 2: 9.3}
brand_ref_times = {
1: {
'gm': 1.0,
@ -275,13 +275,14 @@ class TestFwFingerprintTiming(unittest.TestCase):
'mazda': 0.1,
'nissan': 0.8,
'subaru': 0.45,
'tesla': 0.2,
'tesla': 0.3,
'toyota': 1.6,
'volkswagen': 0.65,
},
2: {
'ford': 1.6,
'hyundai': 1.85,
'tesla': 0.3,
}
}

@ -17,7 +17,7 @@ from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.fingerprints import all_known_cars
from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces
from openpilot.selfdrive.car.honda.values import CAR as HONDA, HONDA_BOSCH
from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags
from openpilot.selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute
from openpilot.selfdrive.controls.controlsd import Controls
from openpilot.selfdrive.test.helpers import read_segment_list
@ -381,7 +381,7 @@ class TestCarModelBase(unittest.TestCase):
if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving:
self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving())
if not (self.CP.carName == "honda" and self.CP.carFingerprint not in HONDA_BOSCH):
if not (self.CP.carName == "honda" and not (self.CP.flags & HondaFlags.BOSCH)):
if self.safety.get_cruise_engaged_prev() != prev_panda_cruise_engaged:
self.assertEqual(CS.cruiseState.enabled, self.safety.get_cruise_engaged_prev())
@ -442,7 +442,7 @@ class TestCarModelBase(unittest.TestCase):
# On most pcmCruise cars, openpilot's state is always tied to the PCM's cruise state.
# On Honda Nidec, we always engage on the rising edge of the PCM cruise state, but
# openpilot brakes to zero even if the min ACC speed is non-zero (i.e. the PCM disengages).
if self.CP.carName == "honda" and self.CP.carFingerprint not in HONDA_BOSCH:
if self.CP.carName == "honda" and not (self.CP.flags & HondaFlags.BOSCH):
# only the rising edges are expected to match
if CS.cruiseState.enabled and not CS_prev.cruiseState.enabled:
checks['controlsAllowed'] += not self.safety.get_controls_allowed()

@ -0,0 +1,23 @@
#!/usr/bin/env python3
import unittest
from openpilot.selfdrive.car.values import PLATFORMS
class TestPlatformConfigs(unittest.TestCase):
def test_configs(self):
for platform in PLATFORMS.values():
with self.subTest(platform=str(platform)):
self.assertTrue(platform.config._frozen)
if platform != "mock":
self.assertIn("pt", platform.config.dbc_dict)
self.assertTrue(len(platform.config.platform_str) > 0)
self.assertIsNotNone(platform.config.specs)
if __name__ == "__main__":
unittest.main()

@ -18,6 +18,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
# Tesla has high torque
"TESLA AP1 MODEL S" = [nan, 2.5, nan]
"TESLA AP2 MODEL S" = [nan, 2.5, nan]
"TESLA MODEL S RAVEN" = [nan, 2.5, nan]
# Guess
"FORD BRONCO SPORT 1ST GEN" = [nan, 1.5, nan]
@ -43,6 +44,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CHEVROLET SILVERADO 1500 2020" = [1.9, 1.9, 0.112]
"CHEVROLET TRAILBLAZER 2021" = [1.33, 1.9, 0.16]
"CHEVROLET EQUINOX 2019" = [2.5, 2.5, 0.05]
"VOLKSWAGEN CADDY 3RD GEN" = [1.2, 1.2, 0.1]
"VOLKSWAGEN PASSAT NMS" = [2.5, 2.5, 0.1]
"VOLKSWAGEN SHARAN 2ND GEN" = [2.5, 2.5, 0.1]
"HYUNDAI SANTA CRUZ 1ST GEN" = [2.7, 2.7, 0.1]

@ -40,6 +40,9 @@ class CarState(CarStateBase):
self.accurate_steer_angle_seen = False
self.angle_offset = FirstOrderFilter(None, 60.0, DT_CTRL, initialized=False)
self.prev_distance_button = 0
self.distance_button = 0
self.low_speed_lockout = False
self.acc_type = 1
self.lkas_hud = {}
@ -163,6 +166,14 @@ class CarState(CarStateBase):
if self.CP.carFingerprint != CAR.PRIUS_V:
self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"])
# distance button is wired to the ACC module (camera or radar)
self.prev_distance_button = self.distance_button
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR):
self.distance_button = cp_acc.vl["ACC_CONTROL"]["DISTANCE"]
elif self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER:
self.distance_button = cp.vl["SDSU"]["FD_BUTTON"]
return ret
@staticmethod
@ -213,6 +224,11 @@ class CarState(CarStateBase):
("PRE_COLLISION", 33),
]
if CP.flags & ToyotaFlags.SMART_DSU and not CP.flags & ToyotaFlags.RADAR_CAN_FILTER:
messages += [
("SDSU", 100),
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
@staticmethod

@ -772,16 +772,22 @@ FW_VERSIONS = {
b'\x018966353S1000\x00\x00\x00\x00',
b'\x018966353S2000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x02353U0000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F15265337200\x00\x00\x00\x00',
b'\x01F15265342000\x00\x00\x00\x00',
b'\x01F15265343000\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B53450\x00\x00\x00\x00\x00\x00',
b'8965B53800\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F5303300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
@ -1266,6 +1272,7 @@ FW_VERSIONS = {
},
CAR.LEXUS_ES: {
(Ecu.engine, 0x7e0, None): [
b'\x02333M4100\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02333M4200\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02333R0000\x00\x00\x00\x00\x00\x00\x00\x00A0C01000\x00\x00\x00\x00\x00\x00\x00\x00',
],

@ -1,13 +1,13 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from panda import Panda
from panda.python import uds
from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
SteerControlType = car.CarParams.SteerControlType
@ -44,82 +44,37 @@ class CarInterface(CarInterfaceBase):
stop_and_go = candidate in TSS2_CAR
# Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it
# 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs
if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR):
ret.flags |= ToyotaFlags.SMART_DSU.value
if 0x2AA in fingerprint[0] and candidate in NO_DSU_CAR:
ret.flags |= ToyotaFlags.RADAR_CAN_FILTER.value
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \
and not (ret.flags & ToyotaFlags.SMART_DSU)
if candidate == CAR.PRIUS:
stop_and_go = True
ret.wheelbase = 2.70
ret.steerRatio = 15.74 # unknown end-to-end spec
ret.tireStiffnessFactor = 0.6371 # hand-tune
ret.mass = 3045. * CV.LB_TO_KG
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2)
elif candidate == CAR.PRIUS_V:
stop_and_go = True
ret.wheelbase = 2.78
ret.steerRatio = 17.4
ret.tireStiffnessFactor = 0.5533
ret.mass = 3340. * CV.LB_TO_KG
elif candidate in (CAR.RAV4, CAR.RAV4H):
stop_and_go = True if (candidate in CAR.RAV4H) else False
ret.wheelbase = 2.65
ret.steerRatio = 16.88 # 14.5 is spec end-to-end
ret.tireStiffnessFactor = 0.5533
ret.mass = 3650. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate == CAR.COROLLA:
ret.wheelbase = 2.70
ret.steerRatio = 18.27
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 2860. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
stop_and_go = True
ret.wheelbase = 2.79
ret.steerRatio = 16. # 14.8 is spec end-to-end
ret.wheelSpeedFactor = 1.035
ret.tireStiffnessFactor = 0.5533
ret.mass = 4481. * CV.LB_TO_KG # mean between min and max
elif candidate in (CAR.CHR, CAR.CHR_TSS2):
stop_and_go = True
ret.wheelbase = 2.63906
ret.steerRatio = 13.6
ret.tireStiffnessFactor = 0.7933
ret.mass = 3300. * CV.LB_TO_KG
elif candidate in (CAR.CAMRY, CAR.CAMRY_TSS2):
stop_and_go = True
ret.wheelbase = 2.82448
ret.steerRatio = 13.7
ret.tireStiffnessFactor = 0.7933
ret.mass = 3400. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDER_TSS2):
# TODO: TSS-P models can do stop and go, but unclear if it requires sDSU or unplugging DSU
stop_and_go = True
ret.wheelbase = 2.8194 # average of 109.8 and 112.2 in
ret.steerRatio = 16.0
ret.tireStiffnessFactor = 0.8
ret.mass = 4516. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALON_TSS2):
# starting from 2019, all Avalon variants have stop and go
# https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf
stop_and_go = candidate != CAR.AVALON
ret.wheelbase = 2.82
ret.steerRatio = 14.8 # Found at https://pressroom.toyota.com/releases/2016+avalon+product+specs.download
ret.tireStiffnessFactor = 0.7983
ret.mass = 3505. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023):
ret.wheelbase = 2.68986
ret.steerRatio = 14.3
ret.tireStiffnessFactor = 0.7933
ret.mass = 3585. * CV.LB_TO_KG # Average between ICE and Hybrid
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kiBP = [0.0]
ret.lateralTuning.pid.kpBP = [0.0]
@ -136,75 +91,14 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.00004
break
elif candidate == CAR.COROLLA_TSS2:
ret.wheelbase = 2.67 # Average between 2.70 for sedan and 2.64 for hatchback
ret.steerRatio = 13.9
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 3060. * CV.LB_TO_KG
elif candidate in (CAR.LEXUS_ES, CAR.LEXUS_ES_TSS2):
ret.wheelbase = 2.8702
ret.steerRatio = 16.0 # not optimized
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 3677. * CV.LB_TO_KG # mean between min and max
elif candidate == CAR.SIENNA:
elif candidate in (CAR.RAV4H, CAR.CHR, CAR.CAMRY, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_NX):
# TODO: Some of these platforms are not advertised to have full range ACC, are they similar to SNG_WITHOUT_DSU cars?
stop_and_go = True
ret.wheelbase = 3.03
ret.steerRatio = 15.5
ret.tireStiffnessFactor = 0.444
ret.mass = 4590. * CV.LB_TO_KG
elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC):
ret.wheelbase = 2.79908
ret.steerRatio = 13.3
ret.tireStiffnessFactor = 0.444
ret.mass = 3736.8 * CV.LB_TO_KG
elif candidate == CAR.LEXUS_GS_F:
ret.wheelbase = 2.84988
ret.steerRatio = 13.3
ret.tireStiffnessFactor = 0.444
ret.mass = 4034. * CV.LB_TO_KG
elif candidate == CAR.LEXUS_CTH:
stop_and_go = True
ret.wheelbase = 2.60
ret.steerRatio = 18.6
ret.tireStiffnessFactor = 0.517
ret.mass = 3108 * CV.LB_TO_KG # mean between min and max
elif candidate in (CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2):
stop_and_go = True
ret.wheelbase = 2.66
ret.steerRatio = 14.7
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4070 * CV.LB_TO_KG
elif candidate == CAR.LEXUS_LC_TSS2:
ret.wheelbase = 2.87
ret.steerRatio = 13.0
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4500 * CV.LB_TO_KG
elif candidate == CAR.PRIUS_TSS2:
ret.wheelbase = 2.70002 # from toyota online sepc.
ret.steerRatio = 13.4 # True steerRatio from older prius
ret.tireStiffnessFactor = 0.6371 # hand-tune
ret.mass = 3115. * CV.LB_TO_KG
elif candidate == CAR.MIRAI:
stop_and_go = True
ret.wheelbase = 2.91
ret.steerRatio = 14.8
ret.tireStiffnessFactor = 0.8
ret.mass = 4300. * CV.LB_TO_KG
elif candidate == CAR.ALPHARD_TSS2:
ret.wheelbase = 3.00
ret.steerRatio = 14.2
ret.tireStiffnessFactor = 0.444
ret.mass = 4305. * CV.LB_TO_KG
# TODO: these models can do stop and go, but unclear if it requires sDSU or unplugging DSU.
# For now, don't list stop and go functionality in the docs
if ret.flags & ToyotaFlags.SNG_WITHOUT_DSU:
stop_and_go = stop_and_go or bool(ret.flags & ToyotaFlags.SMART_DSU.value) or (ret.enableDsu and not docs)
ret.centerToFront = ret.wheelbase * 0.44
@ -212,20 +106,10 @@ class CarInterface(CarInterfaceBase):
# Detect flipped signals and enable for C-HR and others
ret.enableBsm = 0x3F6 in fingerprint[0] and candidate in TSS2_CAR
# Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it
# 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs
if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR):
ret.flags |= ToyotaFlags.SMART_DSU.value
# No radar dbc for cars without DSU which are not TSS 2.0
# TODO: make an adas dbc file for dsu-less models
ret.radarUnavailable = DBC[candidate]['radar'] is None or candidate in (NO_DSU_CAR - TSS2_CAR)
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \
and not (ret.flags & ToyotaFlags.SMART_DSU)
# if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar.
# since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate longitudinal behind experimental toggle
use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU)
@ -292,6 +176,9 @@ class CarInterface(CarInterfaceBase):
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam)
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER):
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
# events
events = self.create_common_events(ret)

@ -1,10 +1,11 @@
import re
from collections import defaultdict
from dataclasses import dataclass, field
from enum import Enum, IntFlag, StrEnum
from enum import Enum, IntFlag
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, CarParts, CarHarness
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
@ -41,50 +42,22 @@ class CarControllerParams:
class ToyotaFlags(IntFlag):
# Detected flags
HYBRID = 1
SMART_DSU = 2
DISABLE_RADAR = 4
RADAR_CAN_FILTER = 1024
class CAR(StrEnum):
# Toyota
ALPHARD_TSS2 = "TOYOTA ALPHARD 2020"
AVALON = "TOYOTA AVALON 2016"
AVALON_2019 = "TOYOTA AVALON 2019"
AVALON_TSS2 = "TOYOTA AVALON 2022" # TSS 2.5
CAMRY = "TOYOTA CAMRY 2018"
CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5
CHR = "TOYOTA C-HR 2018"
CHR_TSS2 = "TOYOTA C-HR 2021"
COROLLA = "TOYOTA COROLLA 2017"
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019"
HIGHLANDER = "TOYOTA HIGHLANDER 2017"
HIGHLANDER_TSS2 = "TOYOTA HIGHLANDER 2020"
PRIUS = "TOYOTA PRIUS 2017"
PRIUS_V = "TOYOTA PRIUS v 2017"
PRIUS_TSS2 = "TOYOTA PRIUS TSS2 2021"
RAV4 = "TOYOTA RAV4 2017"
RAV4H = "TOYOTA RAV4 HYBRID 2017"
RAV4_TSS2 = "TOYOTA RAV4 2019"
RAV4_TSS2_2022 = "TOYOTA RAV4 2022"
RAV4_TSS2_2023 = "TOYOTA RAV4 2023"
MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5
SIENNA = "TOYOTA SIENNA 2018"
# Lexus
LEXUS_CTH = "LEXUS CT HYBRID 2018"
LEXUS_ES = "LEXUS ES 2018"
LEXUS_ES_TSS2 = "LEXUS ES 2019"
LEXUS_IS = "LEXUS IS 2018"
LEXUS_IS_TSS2 = "LEXUS IS 2023"
LEXUS_NX = "LEXUS NX 2018"
LEXUS_NX_TSS2 = "LEXUS NX 2020"
LEXUS_LC_TSS2 = "LEXUS LC 2024"
LEXUS_RC = "LEXUS RC 2020"
LEXUS_RX = "LEXUS RX 2016"
LEXUS_RX_TSS2 = "LEXUS RX 2020"
LEXUS_GS_F = "LEXUS GS F 2016"
# Static flags
TSS2 = 8
NO_DSU = 16
UNSUPPORTED_DSU = 32
RADAR_ACC = 64
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL = 128
NO_STOP_TIMER = 256
# these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU
SNG_WITHOUT_DSU = 512
class Footnote(Enum):
@ -99,127 +72,305 @@ class ToyotaCarInfo(CarInfo):
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.toyota_a]))
CAR_INFO: dict[str, ToyotaCarInfo | list[ToyotaCarInfo]] = {
@dataclass
class ToyotaTSS2PlatformConfig(PlatformConfig):
dbc_dict: dict = field(default_factory=lambda: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'))
def init(self):
self.flags |= ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU
if self.flags & ToyotaFlags.RADAR_ACC:
self.dbc_dict = dbc_dict('toyota_nodsu_pt_generated', None)
class CAR(Platforms):
# Toyota
CAR.ALPHARD_TSS2: [
ToyotaCarInfo("Toyota Alphard 2019-20"),
ToyotaCarInfo("Toyota Alphard Hybrid 2021"),
],
CAR.AVALON: [
ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota Avalon 2017-18"),
],
CAR.AVALON_2019: [
ToyotaCarInfo("Toyota Avalon 2019-21"),
ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"),
],
CAR.AVALON_TSS2: [
ToyotaCarInfo("Toyota Avalon 2022"),
ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
],
CAR.CAMRY: [
ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CAR.CAMRY_TSS2: [
ToyotaCarInfo("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2021-24"),
],
CAR.CHR: [
ToyotaCarInfo("Toyota C-HR 2017-20"),
ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"),
],
CAR.CHR_TSS2: [
ToyotaCarInfo("Toyota C-HR 2021"),
ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"),
],
CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"),
CAR.COROLLA_TSS2: [
ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
ToyotaCarInfo("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
# Hybrid platforms
ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"),
ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5),
ToyotaCarInfo("Lexus UX Hybrid 2019-23"),
],
CAR.HIGHLANDER: [
ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"),
ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"),
],
CAR.HIGHLANDER_TSS2: [
ToyotaCarInfo("Toyota Highlander 2020-23"),
ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"),
],
CAR.PRIUS: [
ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
],
CAR.PRIUS_V: ToyotaCarInfo("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED),
CAR.PRIUS_TSS2: [
ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
],
CAR.RAV4: [
ToyotaCarInfo("Toyota RAV4 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota RAV4 2017-18")
],
CAR.RAV4H: [
ToyotaCarInfo("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26")
],
CAR.RAV4_TSS2: [
ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"),
],
CAR.RAV4_TSS2_2022: [
ToyotaCarInfo("Toyota RAV4 2022"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
],
CAR.RAV4_TSS2_2023: [
ToyotaCarInfo("Toyota RAV4 2023-24"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2023-24"),
],
CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"),
CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED),
ALPHARD_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA ALPHARD 2020",
[
ToyotaCarInfo("Toyota Alphard 2019-20"),
ToyotaCarInfo("Toyota Alphard Hybrid 2021"),
],
CarSpecs(mass=4305. * CV.LB_TO_KG, wheelbase=3.0, steerRatio=14.2, tireStiffnessFactor=0.444),
)
AVALON = PlatformConfig(
"TOYOTA AVALON 2016",
[
ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota Avalon 2017-18"),
],
CarSpecs(mass=3505. * CV.LB_TO_KG, wheelbase=2.82, steerRatio=14.8, tireStiffnessFactor=0.7983),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
AVALON_2019 = PlatformConfig(
"TOYOTA AVALON 2019",
[
ToyotaCarInfo("Toyota Avalon 2019-21"),
ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"),
],
AVALON.specs,
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
)
AVALON_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA AVALON 2022", # TSS 2.5
[
ToyotaCarInfo("Toyota Avalon 2022"),
ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
],
AVALON.specs,
)
CAMRY = PlatformConfig(
"TOYOTA CAMRY 2018",
[
ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_DSU,
)
CAMRY_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA CAMRY 2021", # TSS 2.5
[
ToyotaCarInfo("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2021-24"),
],
CAMRY.specs,
)
CHR = PlatformConfig(
"TOYOTA C-HR 2018",
[
ToyotaCarInfo("Toyota C-HR 2017-20"),
ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"),
],
CarSpecs(mass=3300. * CV.LB_TO_KG, wheelbase=2.63906, steerRatio=13.6, tireStiffnessFactor=0.7933),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_DSU,
)
CHR_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA C-HR 2021",
[
ToyotaCarInfo("Toyota C-HR 2021"),
ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"),
],
CHR.specs,
flags=ToyotaFlags.RADAR_ACC,
)
COROLLA = PlatformConfig(
"TOYOTA COROLLA 2017",
ToyotaCarInfo("Toyota Corolla 2017-19"),
CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
COROLLA_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA COROLLA TSS2 2019",
[
ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
ToyotaCarInfo("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
# Hybrid platforms
ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"),
ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5),
ToyotaCarInfo("Lexus UX Hybrid 2019-23"),
],
CarSpecs(mass=3060. * CV.LB_TO_KG, wheelbase=2.67, steerRatio=13.9, tireStiffnessFactor=0.444),
)
HIGHLANDER = PlatformConfig(
"TOYOTA HIGHLANDER 2017",
[
ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"),
ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"),
],
CarSpecs(mass=4516. * CV.LB_TO_KG, wheelbase=2.8194, steerRatio=16.0, tireStiffnessFactor=0.8),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU,
)
HIGHLANDER_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA HIGHLANDER 2020",
[
ToyotaCarInfo("Toyota Highlander 2020-23"),
ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"),
],
HIGHLANDER.specs,
)
PRIUS = PlatformConfig(
"TOYOTA PRIUS 2017",
[
ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
],
CarSpecs(mass=3045. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.74, tireStiffnessFactor=0.6371),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
)
PRIUS_V = PlatformConfig(
"TOYOTA PRIUS v 2017",
ToyotaCarInfo("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED),
CarSpecs(mass=3340. * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.4, tireStiffnessFactor=0.5533),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU,
)
PRIUS_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA PRIUS TSS2 2021",
[
ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
],
CarSpecs(mass=3115. * CV.LB_TO_KG, wheelbase=2.70002, steerRatio=13.4, tireStiffnessFactor=0.6371),
)
RAV4 = PlatformConfig(
"TOYOTA RAV4 2017",
[
ToyotaCarInfo("Toyota RAV4 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota RAV4 2017-18")
],
CarSpecs(mass=3650. * CV.LB_TO_KG, wheelbase=2.65, steerRatio=16.88, tireStiffnessFactor=0.5533),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
RAV4H = PlatformConfig(
"TOYOTA RAV4 HYBRID 2017",
[
ToyotaCarInfo("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26")
],
RAV4.specs,
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER,
)
RAV4_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2019",
[
ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"),
],
CarSpecs(mass=3585. * CV.LB_TO_KG, wheelbase=2.68986, steerRatio=14.3, tireStiffnessFactor=0.7933),
)
RAV4_TSS2_2022 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2022",
[
ToyotaCarInfo("Toyota RAV4 2022"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
],
RAV4_TSS2.specs,
flags=ToyotaFlags.RADAR_ACC,
)
RAV4_TSS2_2023 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2023",
[
ToyotaCarInfo("Toyota RAV4 2023-24"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2023-24"),
],
RAV4_TSS2.specs,
flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL,
)
MIRAI = ToyotaTSS2PlatformConfig(
"TOYOTA MIRAI 2021", # TSS 2.5
ToyotaCarInfo("Toyota Mirai 2021"),
CarSpecs(mass=4300. * CV.LB_TO_KG, wheelbase=2.91, steerRatio=14.8, tireStiffnessFactor=0.8),
)
SIENNA = PlatformConfig(
"TOYOTA SIENNA 2018",
ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED),
CarSpecs(mass=4590. * CV.LB_TO_KG, wheelbase=3.03, steerRatio=15.5, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER,
)
# Lexus
CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"),
CAR.LEXUS_ES: [
ToyotaCarInfo("Lexus ES 2017-18"),
ToyotaCarInfo("Lexus ES Hybrid 2017-18"),
],
CAR.LEXUS_ES_TSS2: [
ToyotaCarInfo("Lexus ES 2019-24"),
ToyotaCarInfo("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"),
],
CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"),
CAR.LEXUS_IS_TSS2: ToyotaCarInfo("Lexus IS 2022-23"),
CAR.LEXUS_GS_F: ToyotaCarInfo("Lexus GS F 2016"),
CAR.LEXUS_NX: [
ToyotaCarInfo("Lexus NX 2018-19"),
ToyotaCarInfo("Lexus NX Hybrid 2018-19"),
],
CAR.LEXUS_NX_TSS2: [
ToyotaCarInfo("Lexus NX 2020-21"),
ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
],
CAR.LEXUS_LC_TSS2: ToyotaCarInfo("Lexus LC 2024"),
CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2018-20"),
CAR.LEXUS_RX: [
ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX 2017-19"),
# Hybrid platforms
ToyotaCarInfo("Lexus RX Hybrid 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX Hybrid 2017-19"),
],
CAR.LEXUS_RX_TSS2: [
ToyotaCarInfo("Lexus RX 2020-22"),
ToyotaCarInfo("Lexus RX Hybrid 2020-22"),
],
}
LEXUS_CTH = PlatformConfig(
"LEXUS CT HYBRID 2018",
ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"),
CarSpecs(mass=3108. * CV.LB_TO_KG, wheelbase=2.6, steerRatio=18.6, tireStiffnessFactor=0.517),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
LEXUS_ES = PlatformConfig(
"LEXUS ES 2018",
[
ToyotaCarInfo("Lexus ES 2017-18"),
ToyotaCarInfo("Lexus ES Hybrid 2017-18"),
],
CarSpecs(mass=3677. * CV.LB_TO_KG, wheelbase=2.8702, steerRatio=16.0, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
LEXUS_ES_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS ES 2019",
[
ToyotaCarInfo("Lexus ES 2019-24"),
ToyotaCarInfo("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"),
],
LEXUS_ES.specs,
)
LEXUS_IS = PlatformConfig(
"LEXUS IS 2018",
ToyotaCarInfo("Lexus IS 2017-19"),
CarSpecs(mass=3736.8 * CV.LB_TO_KG, wheelbase=2.79908, steerRatio=13.3, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
LEXUS_IS_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS IS 2023",
ToyotaCarInfo("Lexus IS 2022-23"),
LEXUS_IS.specs,
)
LEXUS_NX = PlatformConfig(
"LEXUS NX 2018",
[
ToyotaCarInfo("Lexus NX 2018-19"),
ToyotaCarInfo("Lexus NX Hybrid 2018-19"),
],
CarSpecs(mass=4070. * CV.LB_TO_KG, wheelbase=2.66, steerRatio=14.7, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
LEXUS_NX_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS NX 2020",
[
ToyotaCarInfo("Lexus NX 2020-21"),
ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
],
LEXUS_NX.specs,
)
LEXUS_LC_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS LC 2024",
ToyotaCarInfo("Lexus LC 2024"),
CarSpecs(mass=4500. * CV.LB_TO_KG, wheelbase=2.87, steerRatio=13.0, tireStiffnessFactor=0.444),
)
LEXUS_RC = PlatformConfig(
"LEXUS RC 2020",
ToyotaCarInfo("Lexus RC 2018-20"),
LEXUS_IS.specs,
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
LEXUS_RX = PlatformConfig(
"LEXUS RX 2016",
[
ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX 2017-19"),
# Hybrid platforms
ToyotaCarInfo("Lexus RX Hybrid 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX Hybrid 2017-19"),
],
CarSpecs(mass=4481. * CV.LB_TO_KG, wheelbase=2.79, steerRatio=16., tireStiffnessFactor=0.5533),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
LEXUS_RX_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS RX 2020",
[
ToyotaCarInfo("Lexus RX 2020-22"),
ToyotaCarInfo("Lexus RX Hybrid 2020-22"),
],
LEXUS_RX.specs,
)
LEXUS_GS_F = PlatformConfig(
"LEXUS GS F 2016",
ToyotaCarInfo("Lexus GS F 2016"),
CarSpecs(mass=4034. * CV.LB_TO_KG, wheelbase=2.84988, steerRatio=13.3, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
# (addr, cars, bus, 1/freq*100, vl)
STATIC_DSU_MSGS = [
@ -399,7 +550,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS, CAR.ALPHARD_TSS2],
# On some models, the engine can show on two different addresses
Ecu.engine: [CAR.HIGHLANDER, CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS,
CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
},
extra_ecus=[
# All known ECUs on a late-model Toyota vehicle not queried here:
@ -439,62 +590,23 @@ FW_QUERY_CONFIG = FwQueryConfig(
STEER_THRESHOLD = 100
DBC = {
CAR.RAV4H: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.RAV4: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.PRIUS: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.PRIUS_V: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.COROLLA: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_LC_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_RC: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.HIGHLANDER: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.HIGHLANDER_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.AVALON: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.AVALON_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.AVALON_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_ES: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.SIENNA: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_IS: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_IS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_GS_F: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
}
# These cars have non-standard EPS torque scale factors. All others are 73
EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.PRIUS_V: 100})
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.LEXUS_ES_TSS2,
CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2,
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_LC_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2,
CAR.CHR_TSS2}
TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2)
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY}
NO_DSU_CAR = CAR.with_flags(ToyotaFlags.NO_DSU)
# the DSU uses the AEB message for longitudinal on these cars
UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC, CAR.LEXUS_GS_F}
UNSUPPORTED_DSU_CAR = CAR.with_flags(ToyotaFlags.UNSUPPORTED_DSU)
# these cars have a radar which sends ACC messages instead of the camera
RADAR_ACC_CAR = {CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2}
RADAR_ACC_CAR = CAR.with_flags(ToyotaFlags.RADAR_ACC)
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL_CAR = {CAR.RAV4_TSS2_2023}
ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL)
# no resume button press required
NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDER, CAR.SIENNA}
NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER)
DBC = CAR.create_dbc_map()

@ -1,4 +1,4 @@
from typing import cast
from typing import Any, Callable, cast
from openpilot.selfdrive.car.body.values import CAR as BODY
from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER
from openpilot.selfdrive.car.ford.values import CAR as FORD
@ -6,13 +6,20 @@ from openpilot.selfdrive.car.gm.values import CAR as GM
from openpilot.selfdrive.car.honda.values import CAR as HONDA
from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI
from openpilot.selfdrive.car.mazda.values import CAR as MAZDA
from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.nissan.values import CAR as NISSAN
from openpilot.selfdrive.car.subaru.values import CAR as SUBARU
from openpilot.selfdrive.car.tesla.values import CAR as TESLA
from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA
from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN
Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN
BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN]
Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | MOCK | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN
BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, MOCK, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN]
PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in cast(list[Platform], brand)}
MapFunc = Callable[[Platform], Any]
def create_platform_map(func: MapFunc):
return {str(platform): func(platform) for platform in PLATFORMS.values() if func(platform) is not None}

@ -6,7 +6,7 @@ from openpilot.common.realtime import DT_CTRL
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.values import CANBUS, PQ_CARS, CarControllerParams, VolkswagenFlags
from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
@ -16,7 +16,7 @@ class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.CCP = CarControllerParams(CP)
self.CCS = pqcan if CP.carFingerprint in PQ_CARS else mqbcan
self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan
self.packer_pt = CANPacker(dbc_name)
self.apply_steer_last = 0

@ -3,13 +3,15 @@ from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, PQ_CARS, NetworkLocation, TransmissionType, GearShifter, \
from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \
CarControllerParams, VolkswagenFlags
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
self.frame = 0
self.eps_init_complete = False
self.CCP = CarControllerParams(CP)
self.button_states = {button.event_type: False for button in self.CCP.BUTTONS}
self.esp_hold_confirmation = False
@ -31,7 +33,7 @@ class CarState(CarStateBase):
return button_events
def update(self, pt_cp, cam_cp, ext_cp, trans_type):
if self.CP.carFingerprint in PQ_CARS:
if self.CP.flags & VolkswagenFlags.PQ:
return self.update_pq(pt_cp, cam_cp, ext_cp, trans_type)
ret = car.CarState.new_message()
@ -47,18 +49,14 @@ class CarState(CarStateBase):
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw == 0
# Update steering angle, rate, yaw rate, and driver input torque. VW send
# the sign/direction in a separate signal so they must be recombined.
# Update EPS position and state info. For signed values, VW sends the sign in a separate signal.
ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])]
ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])]
ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])]
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD
# Verify EPS readiness to accept steering commands
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"])
ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT")
ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED")
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status)
# VW Emergency Assist status tracking and mitigation
self.eps_stock_values = pt_cp.vl["LH_EPS_03"]
@ -151,6 +149,7 @@ class CarState(CarStateBase):
# Digital instrument clusters expect the ACC HUD lead car distance to be scaled differently
self.upscale_lead_car_signal = bool(pt_cp.vl["Kombi_03"]["KBI_Variante"])
self.frame += 1
return ret
def update_pq(self, pt_cp, cam_cp, ext_cp, trans_type):
@ -168,18 +167,14 @@ class CarState(CarStateBase):
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw == 0
# Update steering angle, rate, yaw rate, and driver input torque. VW send
# the sign/direction in a separate signal so they must be recombined.
# Update EPS position and state info. For signed values, VW sends the sign in a separate signal.
ret.steeringAngleDeg = pt_cp.vl["Lenkhilfe_3"]["LH3_BLW"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_BLWSign"])]
ret.steeringRateDeg = pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit_S"])]
ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])]
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
ret.yawRate = pt_cp.vl["Bremse_5"]["Giergeschwindigkeit"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["Vorzeichen_der_Giergeschwindigk"])] * CV.DEG_TO_RAD
# Verify EPS readiness to accept steering commands
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT")
ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED")
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status)
# Update gas, brakes, and gearshift.
ret.gas = pt_cp.vl["Motor_3"]["Fahrpedal_Rohsignal"] / 100.0
@ -253,11 +248,20 @@ class CarState(CarStateBase):
# Additional safety checks performed in CarInterface.
ret.espDisabled = bool(pt_cp.vl["Bremse_1"]["ESP_Passiv_getastet"])
self.frame += 1
return ret
def update_hca_state(self, hca_status):
# Treat INITIALIZING and FAULT as temporary for worst likely EPS recovery time, for cars without factory Lane Assist
# DISABLED means the EPS hasn't been configured to support Lane Assist
self.eps_init_complete = self.eps_init_complete or (hca_status in ("DISABLED", "READY", "ACTIVE") or self.frame > 600)
perm_fault = hca_status == "DISABLED" or (self.eps_init_complete and hca_status in ("INITIALIZING", "FAULT"))
temp_fault = hca_status == "REJECTED" or not self.eps_init_complete
return temp_fault, perm_fault
@staticmethod
def get_can_parser(CP):
if CP.carFingerprint in PQ_CARS:
if CP.flags & VolkswagenFlags.PQ:
return CarState.get_can_parser_pq(CP)
messages = [
@ -294,7 +298,7 @@ class CarState(CarStateBase):
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in PQ_CARS:
if CP.flags & VolkswagenFlags.PQ:
return CarState.get_cam_can_parser_pq(CP)
messages = []

@ -99,6 +99,17 @@ FW_VERSIONS = {
b'\xf1\x875Q0907572P \xf1\x890682',
],
},
CAR.CADDY_MK3: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027T \xf1\x892363',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x872K5959655E \xf1\x890018\xf1\x82\x05000P037605',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0155',
],
},
CAR.CRAFTER_MK2: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906056BP\xf1\x894729',
@ -938,6 +949,7 @@ FW_VERSIONS = {
},
CAR.SKODA_KAROQ_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8705E906013CL\xf1\x892541',
b'\xf1\x8705E906013H \xf1\x892407',
b'\xf1\x8705E906018P \xf1\x895472',
b'\xf1\x8705E906018P \xf1\x896020',
@ -957,6 +969,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x712, None): [
b'\xf1\x875Q0910143B \xf1\x892201\xf1\x82\x0563T6090500',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100500',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100600',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100700',
],
(Ecu.fwdRadar, 0x757, None): [

@ -2,7 +2,7 @@ from cereal import car
from panda import Panda
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.volkswagen.values import PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags
from openpilot.selfdrive.car.volkswagen.values import CAR, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
@ -22,11 +22,11 @@ class CarInterface(CarInterfaceBase):
self.eps_timer_soft_disable_alert = False
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs):
ret.carName = "volkswagen"
ret.radarUnavailable = True
if candidate in PQ_CARS:
if ret.flags & VolkswagenFlags.PQ:
# Set global PQ35/PQ46/NMS parameters
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagenPq)]
ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
@ -72,7 +72,7 @@ class CarInterface(CarInterfaceBase):
# Global lateral tuning defaults, can be overridden per-vehicle
ret.steerLimitTimer = 0.4
if candidate in PQ_CARS:
if ret.flags & VolkswagenFlags.PQ:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
else:
@ -100,11 +100,8 @@ class CarInterface(CarInterfaceBase):
ret.vEgoStopping = 0.5
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
# Per-chassis tuning values, override tuning defaults here if desired
ret.autoResumeSng = ret.minEnableSpeed == -1
ret.centerToFront = ret.wheelbase * 0.45
return ret
# returns a car.CarState

@ -40,7 +40,7 @@ class CarControllerParams:
def __init__(self, CP):
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
if CP.carFingerprint in PQ_CARS:
if CP.flags & VolkswagenFlags.PQ:
self.LDW_STEP = 5 # LDW_1 message frequency 20Hz
self.ACC_HUD_STEP = 4 # ACC_GRA_Anzeige frequency 25Hz
self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
@ -110,8 +110,12 @@ class CANBUS:
class VolkswagenFlags(IntFlag):
# Detected flags
STOCK_HCA_PRESENT = 1
# Static flags
PQ = 2
@dataclass
class VolkswagenMQBPlatformConfig(PlatformConfig):
@ -122,9 +126,13 @@ class VolkswagenMQBPlatformConfig(PlatformConfig):
class VolkswagenPQPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None))
def init(self):
self.flags |= VolkswagenFlags.PQ
@dataclass(frozen=True, kw_only=True)
class VolkswagenCarSpecs(CarSpecs):
centerToFrontRatio: float = 0.45
steerRatio: float = 15.6
@ -177,7 +185,7 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Arteon eHybrid 2020-23", video_link="https://youtu.be/FAomFKPFlDA"),
VWCarInfo("Volkswagen CC 2018-22", video_link="https://youtu.be/FAomFKPFlDA"),
],
specs=VolkswagenCarSpecs(mass=1733, wheelbase=2.84),
VolkswagenCarSpecs(mass=1733, wheelbase=2.84),
)
ATLAS_MK1 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN ATLAS 1ST GEN", # Chassis CA
@ -188,7 +196,15 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Teramont Cross Sport 2021-22"),
VWCarInfo("Volkswagen Teramont X 2021-22"),
],
specs=VolkswagenCarSpecs(mass=2011, wheelbase=2.98),
VolkswagenCarSpecs(mass=2011, wheelbase=2.98),
)
CADDY_MK3 = VolkswagenPQPlatformConfig(
"VOLKSWAGEN CADDY 3RD GEN", # Chassis 2K
[
VWCarInfo("Volkswagen Caddy 2019"),
VWCarInfo("Volkswagen Caddy Maxi 2019"),
],
VolkswagenCarSpecs(mass=1613, wheelbase=2.6, minSteerSpeed=21 * CV.KPH_TO_MS),
)
CRAFTER_MK2 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN CRAFTER 2ND GEN", # Chassis SY/SZ
@ -199,7 +215,7 @@ class CAR(Platforms):
VWCarInfo("MAN TGE 2017-23", video_link="https://youtu.be/4100gLeabmo"),
VWCarInfo("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"),
],
specs=VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS),
VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS),
)
GOLF_MK7 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN GOLF 7TH GEN", # Chassis 5G/AU/BA/BE
@ -213,7 +229,7 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Golf R 2015-19"),
VWCarInfo("Volkswagen Golf SportsVan 2015-20"),
],
specs=VolkswagenCarSpecs(mass=1397, wheelbase=2.62),
VolkswagenCarSpecs(mass=1397, wheelbase=2.62),
)
JETTA_MK7 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN JETTA 7TH GEN", # Chassis BU
@ -221,7 +237,7 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Jetta 2018-24"),
VWCarInfo("Volkswagen Jetta GLI 2021-24"),
],
specs=VolkswagenCarSpecs(mass=1328, wheelbase=2.71),
VolkswagenCarSpecs(mass=1328, wheelbase=2.71),
)
PASSAT_MK8 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN PASSAT 8TH GEN", # Chassis 3G
@ -230,12 +246,12 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Passat Alltrack 2015-22"),
VWCarInfo("Volkswagen Passat GTE 2015-22"),
],
specs=VolkswagenCarSpecs(mass=1551, wheelbase=2.79),
VolkswagenCarSpecs(mass=1551, wheelbase=2.79),
)
PASSAT_NMS = VolkswagenPQPlatformConfig(
"VOLKSWAGEN PASSAT NMS", # Chassis A3
VWCarInfo("Volkswagen Passat NMS 2017-22"),
specs=VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50*CV.KPH_TO_MS, minEnableSpeed=20*CV.KPH_TO_MS),
VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50*CV.KPH_TO_MS, minEnableSpeed=20*CV.KPH_TO_MS),
)
POLO_MK6 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN POLO 6TH GEN", # Chassis AW
@ -243,7 +259,7 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]),
VWCarInfo("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]),
],
specs=VolkswagenCarSpecs(mass=1230, wheelbase=2.55),
VolkswagenCarSpecs(mass=1230, wheelbase=2.55),
)
SHARAN_MK2 = VolkswagenPQPlatformConfig(
"VOLKSWAGEN SHARAN 2ND GEN", # Chassis 7N
@ -251,17 +267,17 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Sharan 2018-22"),
VWCarInfo("SEAT Alhambra 2018-20"),
],
specs=VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50*CV.KPH_TO_MS),
VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50*CV.KPH_TO_MS),
)
TAOS_MK1 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN TAOS 1ST GEN", # Chassis B2
VWCarInfo("Volkswagen Taos 2022-23"),
specs=VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
)
TCROSS_MK1 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN T-CROSS 1ST GEN", # Chassis C1
car_info=VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]),
specs=VolkswagenCarSpecs(mass=1150, wheelbase=2.60),
VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]),
VolkswagenCarSpecs(mass=1150, wheelbase=2.60),
)
TIGUAN_MK2 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN TIGUAN 2ND GEN", # Chassis AD/BW
@ -269,12 +285,12 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Tiguan 2018-24"),
VWCarInfo("Volkswagen Tiguan eHybrid 2021-23"),
],
specs=VolkswagenCarSpecs(mass=1715, wheelbase=2.74),
VolkswagenCarSpecs(mass=1715, wheelbase=2.74),
)
TOURAN_MK2 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN TOURAN 2ND GEN", # Chassis 1T
VWCarInfo("Volkswagen Touran 2016-23"),
specs=VolkswagenCarSpecs(mass=1516, wheelbase=2.79),
VolkswagenCarSpecs(mass=1516, wheelbase=2.79),
)
TRANSPORTER_T61 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN TRANSPORTER T6.1", # Chassis 7H/7L
@ -282,12 +298,12 @@ class CAR(Platforms):
VWCarInfo("Volkswagen Caravelle 2020"),
VWCarInfo("Volkswagen California 2021-23"),
],
specs=VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0),
VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0),
)
TROC_MK1 = VolkswagenMQBPlatformConfig(
"VOLKSWAGEN T-ROC 1ST GEN", # Chassis A1
VWCarInfo("Volkswagen T-Roc 2018-22", footnotes=[Footnote.VW_MQB_A0]),
specs=VolkswagenCarSpecs(mass=1413, wheelbase=2.63),
VolkswagenCarSpecs(mass=1413, wheelbase=2.63),
)
AUDI_A3_MK3 = VolkswagenMQBPlatformConfig(
"AUDI A3 3RD GEN", # Chassis 8V/FF
@ -297,47 +313,47 @@ class CAR(Platforms):
VWCarInfo("Audi RS3 2018"),
VWCarInfo("Audi S3 2015-17"),
],
specs=VolkswagenCarSpecs(mass=1335, wheelbase=2.61),
VolkswagenCarSpecs(mass=1335, wheelbase=2.61),
)
AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig(
"AUDI Q2 1ST GEN", # Chassis GA
VWCarInfo("Audi Q2 2018"),
specs=VolkswagenCarSpecs(mass=1205, wheelbase=2.61),
VolkswagenCarSpecs(mass=1205, wheelbase=2.61),
)
AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig(
"AUDI Q3 2ND GEN", # Chassis 8U/F3/FS
VWCarInfo("Audi Q3 2019-23"),
specs=VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
)
SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig(
"SEAT ATECA 1ST GEN", # Chassis 5F
VWCarInfo("SEAT Ateca 2018"),
specs=VolkswagenCarSpecs(mass=1900, wheelbase=2.64),
VolkswagenCarSpecs(mass=1900, wheelbase=2.64),
)
SEAT_LEON_MK3 = VolkswagenMQBPlatformConfig(
"SEAT LEON 3RD GEN", # Chassis 5F
VWCarInfo("SEAT Leon 2014-20"),
specs=VolkswagenCarSpecs(mass=1227, wheelbase=2.64),
VolkswagenCarSpecs(mass=1227, wheelbase=2.64),
)
SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig(
"SKODA FABIA 4TH GEN", # Chassis PJ
VWCarInfo("Škoda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0]),
specs=VolkswagenCarSpecs(mass=1266, wheelbase=2.56),
VolkswagenCarSpecs(mass=1266, wheelbase=2.56),
)
SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig(
"SKODA KAMIQ 1ST GEN", # Chassis NW
VWCarInfo("Škoda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]),
specs=VolkswagenCarSpecs(mass=1265, wheelbase=2.66),
VolkswagenCarSpecs(mass=1265, wheelbase=2.66),
)
SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig(
"SKODA KAROQ 1ST GEN", # Chassis NU
VWCarInfo("Škoda Karoq 2019-23"),
specs=VolkswagenCarSpecs(mass=1278, wheelbase=2.66),
VolkswagenCarSpecs(mass=1278, wheelbase=2.66),
)
SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig(
"SKODA KODIAQ 1ST GEN", # Chassis NS
VWCarInfo("Škoda Kodiaq 2017-23"),
specs=VolkswagenCarSpecs(mass=1569, wheelbase=2.79),
VolkswagenCarSpecs(mass=1569, wheelbase=2.79),
)
SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig(
"SKODA OCTAVIA 3RD GEN", # Chassis NE
@ -345,22 +361,20 @@ class CAR(Platforms):
VWCarInfo("Škoda Octavia 2015-19"),
VWCarInfo("Škoda Octavia RS 2016"),
],
specs=VolkswagenCarSpecs(mass=1388, wheelbase=2.68),
VolkswagenCarSpecs(mass=1388, wheelbase=2.68),
)
SKODA_SCALA_MK1 = VolkswagenMQBPlatformConfig(
"SKODA SCALA 1ST GEN", # Chassis NW
VWCarInfo("Škoda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]),
specs=VolkswagenCarSpecs(mass=1192, wheelbase=2.65),
VolkswagenCarSpecs(mass=1192, wheelbase=2.65),
)
SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig(
"SKODA SUPERB 3RD GEN", # Chassis 3V/NP
VWCarInfo("Škoda Superb 2015-22"),
specs=VolkswagenCarSpecs(mass=1505, wheelbase=2.84),
VolkswagenCarSpecs(mass=1505, wheelbase=2.84),
)
PQ_CARS = {CAR.PASSAT_NMS, CAR.SHARAN_MK2}
# All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars
# with a manual trans won't return transmission firmware, but all other cars will.
#
@ -402,5 +416,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
extra_ecus=[(Ecu.fwdCamera, 0x74f, None)],
)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map()

@ -10,7 +10,6 @@ import cereal.messaging as messaging
from cereal import car, log
from cereal.visionipc import VisionIpcClient, VisionStreamType
from panda import ALTERNATIVE_EXPERIENCE
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip
@ -18,9 +17,8 @@ from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.car_helpers import get_startup_event
from openpilot.selfdrive.car.card import CarD
from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert
from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature
from openpilot.selfdrive.controls.lib.events import Events, ET
@ -61,128 +59,18 @@ ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding)
ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES)
class CarD:
CI: CarInterfaceBase
CS: car.CarState
class Controls:
def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams'])
self.can_rcv_timeout_counter = 0 # conseuctive timeout count
self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count
self.card = CarD(CI)
self.params = Params()
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
get_one_can(self.can_sock)
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled")
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
else:
self.CI, self.CP = CI, CI.CP
# set alternative experiences from parameters
disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
self.CP.alternativeExperience = 0
if not disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
car_recognized = self.CP.carName != 'mock'
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly
if self.CP.passive:
safety_config = car.CarParams.SafetyConfig.new_message()
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config]
# Write previous route's CarParams
prev_cp = self.params.get("CarParamsPersistent")
if prev_cp is not None:
self.params.put("CarParamsPrevRoute", prev_cp)
# Write CarParams for radard
cp_bytes = self.CP.to_bytes()
self.params.put("CarParams", cp_bytes)
self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
def initialize(self):
"""Initialize CarInterface, once controls are ready"""
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
def state_update(self, CC: car.CarControl):
"""carState update loop, driven by can"""
# TODO: This should not depend on carControl
with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg:
# TODO: this shouldn't need to be a builder
self.CP = msg.as_builder()
# Update carState from CAN
can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
self.CS = self.CI.update(CC, can_strs)
self.sm.update(0)
can_rcv_valid = len(can_strs) > 0
# Check for CAN timeout
if not can_rcv_valid:
self.can_rcv_timeout_counter += 1
self.can_rcv_cum_timeout_counter += 1
else:
self.can_rcv_timeout_counter = 0
self.can_rcv_timeout = self.can_rcv_timeout_counter >= 5
if can_rcv_valid and REPLAY:
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
return self.CS
def state_publish(self, car_events):
"""carState and carParams publish loop"""
# TODO: carState should be independent of the event loop
# carState
cs_send = messaging.new_message('carState')
cs_send.valid = self.CS.canValid
cs_send.carState = self.CS
cs_send.carState.events = car_events
self.pm.send('carState', cs_send)
# carParams - logged every 50 seconds (> 1 per segment)
if (self.sm.frame % int(50. / DT_CTRL) == 0):
cp_send = messaging.new_message('carParams')
cp_send.valid = True
cp_send.carParams = self.CP
self.pm.send('carParams', cp_send)
def controls_update(self, CC: car.CarControl):
"""control update loop, driven by carControl"""
# send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
actuators_output, can_sends = self.CI.apply(CC, now_nanos)
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid))
return actuators_output
class Controls:
def __init__(self, CI=None):
self.card = CarD(CI)
self.CP = self.card.CP
self.CI = self.card.CI
config_realtime_process(4, Priority.CTRL_HIGH)
# Ensure the current branch is cached, otherwise the first iteration of controlsd lags
self.branch = get_short_branch()
@ -195,12 +83,11 @@ class Controls:
self.log_sock = messaging.sub_sock('androidLog')
self.params = Params()
ignore = self.sensor_packets + ['testJoystick']
if SIMULATION:
ignore += ['driverCameraState', 'managerState']
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'testJoystick'] + self.camera_packets + self.sensor_packets,
ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ],
@ -212,15 +99,12 @@ class Controls:
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
self.is_metric = self.params.get_bool("IsMetric")
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
# detect sound card presence and ensure successful init
sounds_available = HARDWARE.get_sound_card_online()
car_recognized = self.CP.carName != 'mock'
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
# cleanup old params
if not self.CP.experimentalLongitudinalAvailable:
self.params.remove("ExperimentalLongitudinalEnabled")
@ -267,7 +151,7 @@ class Controls:
self.can_log_mono_time = 0
self.startup_event = get_startup_event(car_recognized, controller_available, len(self.CP.carFw) > 0)
self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0)
if not sounds_available:
self.events.add(EventName.soundsUnavailable, static=True)
@ -513,7 +397,7 @@ class Controls:
def data_sample(self):
"""Receive data from sockets and update carState"""
CS = self.card.state_update(self.CC)
CS = self.card.state_update()
self.sm.update(0)
@ -771,6 +655,8 @@ class Controls:
def publish_logs(self, CS, start_time, CC, lac_log):
"""Send actuators and hud commands to the car, send controlsstate and MPC logging"""
CO = self.sm['carOutput']
# Orientation and angle rates can be useful for carcontroller
# Only calibrated (car) frame is relevant for the carcontroller
orientation_value = list(self.sm['liveLocationKalman'].calibratedOrientationNED.value)
@ -833,13 +719,12 @@ class Controls:
hudControl.visualAlert = current_alert.visual_alert
if not self.CP.passive and self.initialized:
self.last_actuators = self.card.controls_update(CC)
CC.actuatorsOutput = self.last_actuators
self.card.controls_update(CC)
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
self.steer_limited = abs(CC.actuators.steeringAngleDeg - CC.actuatorsOutput.steeringAngleDeg) > \
self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
STEER_ANGLE_SATURATION_THRESHOLD
else:
self.steer_limited = abs(CC.actuators.steer - CC.actuatorsOutput.steer) > 1e-2
self.steer_limited = abs(CC.actuators.steer - CO.actuatorsOutput.steer) > 1e-2
force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \
(self.state == State.softDisabling)
@ -896,15 +781,11 @@ class Controls:
self.pm.send('controlsState', dat)
car_events = self.events.to_msg()
self.card.state_publish(car_events)
# onroadEvents - logged every second or on change
if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events.names != self.events_prev):
ce_send = messaging.new_message('onroadEvents', len(self.events))
ce_send.valid = True
ce_send.onroadEvents = car_events
ce_send.onroadEvents = self.events.to_msg()
self.pm.send('onroadEvents', ce_send)
self.events_prev = self.events.names.copy()
@ -961,6 +842,7 @@ class Controls:
def main():
config_realtime_process(4, Priority.CTRL_HIGH)
controls = Controls()
controls.controlsd_thread()

@ -7,6 +7,7 @@ from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.controls.controlsd import Controls, SOFT_DISABLE_TIME
from openpilot.selfdrive.controls.lib.events import Events, ET, Alert, Priority, AlertSize, AlertStatus, VisualAlert, \
AudibleAlert, EVENTS
from openpilot.selfdrive.car.mock.values import CAR as MOCK
State = log.ControlsState.OpenpilotState
@ -30,8 +31,8 @@ def make_event(event_types):
class TestStateMachine(unittest.TestCase):
def setUp(self):
CarInterface, CarController, CarState = interfaces["mock"]
CP = CarInterface.get_non_essential_params("mock")
CarInterface, CarController, CarState = interfaces[MOCK.MOCK]
CP = CarInterface.get_non_essential_params(MOCK.MOCK)
CI = CarInterface(CP, CarController, CarState)
self.controlsd = Controls(CI=CI)

@ -36,6 +36,9 @@ SUPPORTED_FW_VERSIONS = {
default_config=b"\x00\x00\x00\x01\x00\x00",
tracks_enabled=b"\x00\x00\x00\x01\x00\x01"),
# 2021 SONATA HYBRID
b"DNhe SCC FHCUP 1.00 1.00 99110-L5000\x19\x04&\x13' ": ConfigValues(
default_config=b"\x00\x00\x00\x01\x00\x00",
tracks_enabled=b"\x00\x00\x00\x01\x00\x01"),
b"DNhe SCC FHCUP 1.00 1.02 99110-L5000 \x01#\x15# ": ConfigValues(
default_config=b"\x00\x00\x00\x01\x00\x00",
tracks_enabled=b"\x00\x00\x00\x01\x00\x01"),

@ -27,4 +27,5 @@ if __name__ == "__main__":
outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime)
fn = f"{args.route.replace('/', '_')}_{args.process}.bz2"
print(f"Saved log to {fn}")
save_log(fn, outputs)

@ -159,8 +159,10 @@ class TorqueEstimator(ParameterEstimator):
def handle_log(self, t, which, msg):
if which == "carControl":
self.raw_points["carControl_t"].append(t + self.lag)
self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer)
self.raw_points["active"].append(msg.latActive)
elif which == "carOutput":
self.raw_points["carOutput_t"].append(t + self.lag)
self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer)
elif which == "carState":
self.raw_points["carState_t"].append(t + self.lag)
self.raw_points["vego"].append(msg.vEgo)
@ -172,7 +174,7 @@ class TorqueEstimator(ParameterEstimator):
active = np.interp(np.arange(t - MIN_ENGAGE_BUFFER, t, DT_MDL), self.raw_points['carControl_t'], self.raw_points['active']).astype(bool)
steer_override = np.interp(np.arange(t - MIN_ENGAGE_BUFFER, t, DT_MDL), self.raw_points['carState_t'], self.raw_points['steer_override']).astype(bool)
vego = np.interp(t, self.raw_points['carState_t'], self.raw_points['vego'])
steer = np.interp(t, self.raw_points['carControl_t'], self.raw_points['steer_torque'])
steer = np.interp(t, self.raw_points['carOutput_t'], self.raw_points['steer_torque'])
lateral_acc = (vego * yaw_rate) - (np.sin(roll) * ACCELERATION_DUE_TO_GRAVITY)
if all(active) and (not any(steer_override)) and (vego > MIN_VEL) and (abs(steer) > STEER_MIN_THRESHOLD) and (abs(lateral_acc) <= LAT_ACC_THRESHOLD):
self.filtered_points.add_point(float(steer), float(lateral_acc))
@ -218,7 +220,7 @@ def main(demo=False):
config_realtime_process([0, 1, 2, 3], 5)
pm = messaging.PubMaster(['liveTorqueParameters'])
sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll='liveLocationKalman')
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveLocationKalman'], poll='liveLocationKalman')
params = Params()
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP:

@ -78,7 +78,7 @@ procs = [
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
PythonProcess("thermald", "selfdrive.thermald.thermald", always_run),
PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC),
PythonProcess("updated", "selfdrive.updated", only_offroad, enabled=not PC),
PythonProcess("updated", "selfdrive.updated.updated", only_offroad, enabled=not PC),
PythonProcess("uploader", "system.loggerd.uploader", always_run),
PythonProcess("statsd", "selfdrive.statsd", always_run),

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c11f99aab832242a604b1bb4c4cf391c9c3d5e90cbc07ab0d4c133473b56a3a4
oid sha256:cd4b0cc83d5ff275ee77ec430ea686603ad50fd6ad874f599ea6e95b123afc3e
size 48193749

@ -1,3 +1,4 @@
import contextlib
import http.server
import os
import threading
@ -43,27 +44,34 @@ def release_only(f):
f(self, *args, **kwargs)
return wrap
def with_processes(processes, init_time=0, ignore_stopped=None):
@contextlib.contextmanager
def processes_context(processes, init_time=0, ignore_stopped=None):
ignore_stopped = [] if ignore_stopped is None else ignore_stopped
# start and assert started
for n, p in enumerate(processes):
managed_processes[p].start()
if n < len(processes) - 1:
time.sleep(init_time)
assert all(managed_processes[name].proc.exitcode is None for name in processes)
try:
yield [managed_processes[name] for name in processes]
# assert processes are still started
assert all(managed_processes[name].proc.exitcode is None for name in processes if name not in ignore_stopped)
finally:
for p in processes:
managed_processes[p].stop()
def with_processes(processes, init_time=0, ignore_stopped=None):
def wrapper(func):
@wraps(func)
def wrap(*args, **kwargs):
# start and assert started
for n, p in enumerate(processes):
managed_processes[p].start()
if n < len(processes) - 1:
time.sleep(init_time)
assert all(managed_processes[name].proc.exitcode is None for name in processes)
# call the function
try:
func(*args, **kwargs)
# assert processes are still started
assert all(managed_processes[name].proc.exitcode is None for name in processes if name not in ignore_stopped)
finally:
for p in processes:
managed_processes[p].stop()
with processes_context(processes, init_time, ignore_stopped):
return func(*args, **kwargs)
return wrap
return wrapper

@ -1 +1 @@
fd6421f7551573c549480f9d29bb0dee4678344d
e8b359a82316e6dfce3b6fb0fb9684431bfa0a1b

@ -1 +1 @@
b9d29ac9402cfc04bf3e48867415efa70c144029
43efe1cf08cba8c86bc1ae8234b3d3d084a40e5d

@ -156,7 +156,8 @@ if __name__ == "__main__":
assert full_test, "Need to run full test when updating refs"
try:
ref_commit = open(REF_COMMIT_FN).read().strip()
with open(REF_COMMIT_FN) as f:
ref_commit = f.read().strip()
except FileNotFoundError:
print("Couldn't find reference commit")
sys.exit(1)

@ -29,7 +29,7 @@ from openpilot.tools.lib.logreader import LogReader
# Baseline CPU usage by process
PROCS = {
"selfdrive.controls.controlsd": 41.0,
"selfdrive.controls.controlsd": 46.0,
"./loggerd": 14.0,
"./encoderd": 17.0,
"./camerad": 14.5,

@ -17,7 +17,7 @@
void SoftwarePanel::checkForUpdates() {
std::system("pkill -SIGUSR1 -f selfdrive.updated");
std::system("pkill -SIGUSR1 -f selfdrive.updated.updated");
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
@ -36,7 +36,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
if (downloadBtn->text() == tr("CHECK")) {
checkForUpdates();
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
std::system("pkill -SIGHUP -f selfdrive.updated.updated");
}
});
addItem(downloadBtn);

@ -0,0 +1,77 @@
#!/usr/bin/env python3
import os
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
import cereal.messaging as messaging
from openpilot.system.hardware import HARDWARE
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QStackedLayout, QApplication
from openpilot.selfdrive.ui.qt.python_helpers import set_main_window
if __name__ == "__main__":
app = QApplication([])
win = QWidget()
set_main_window(win)
bg = QLabel("", alignment=Qt.AlignCenter)
alert1 = QLabel()
alert2 = QLabel()
vlayout = QVBoxLayout()
vlayout.addWidget(alert1, alignment=Qt.AlignCenter)
vlayout.addWidget(alert2, alignment=Qt.AlignCenter)
tmp = QWidget()
tmp.setLayout(vlayout)
stack = QStackedLayout(win)
stack.addWidget(tmp)
stack.addWidget(bg)
stack.setStackingMode(QStackedLayout.StackAll)
win.setObjectName("win")
win.setStyleSheet("""
#win {
background-color: black;
}
QLabel {
color: white;
font-size: 40px;
}
""")
sm = messaging.SubMaster(['deviceState', 'controlsState'])
def update():
sm.update(0)
onroad = sm.all_checks(['deviceState']) and sm['deviceState'].started
if onroad:
cs = sm['controlsState']
color = ("grey" if str(cs.status) in ("overriding", "preEnabled") else "green") if cs.enabled else "blue"
bg.setText("\U0001F44D" if cs.engageable else "\U0001F6D1")
bg.setStyleSheet(f"font-size: 100px; background-color: {color};")
bg.show()
alert1.setText(cs.alertText1)
alert2.setText(cs.alertText2)
if not sm.alive['controlsState']:
alert1.setText("waiting for controls...")
else:
bg.hide()
alert1.setText("")
alert2.setText("offroad")
HARDWARE.set_screen_brightness(100 if onroad else 40)
os.system("echo 0 > /sys/class/backlight/panel0-backlight/bl_power")
timer = QTimer()
timer.timeout.connect(update)
timer.start(50)
app.exec_()

@ -0,0 +1,172 @@
#!/usr/bin/env python3
import os
import pathlib
import shutil
import signal
import subprocess
import tempfile
import time
import unittest
from unittest import mock
import pytest
from openpilot.selfdrive.manager.process import ManagerProcess
from openpilot.selfdrive.test.helpers import processes_context
from openpilot.common.params import Params
def run(args, **kwargs):
return subprocess.run(args, **kwargs, check=True)
def update_release(directory, name, version, release_notes):
with open(directory / "RELEASES.md", "w") as f:
f.write(release_notes)
(directory / "common").mkdir(exist_ok=True)
with open(directory / "common" / "version.h", "w") as f:
f.write(f'#define COMMA_VERSION "{version}"')
run(["git", "add", "."], cwd=directory)
run(["git", "commit", "-m", f"openpilot release {version}"], cwd=directory)
@pytest.mark.slow # TODO: can we test overlayfs in GHA?
class TestUpdateD(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
run(["sudo", "mount", "-t", "tmpfs", "tmpfs", self.tmpdir]) # overlayfs doesn't work inside of docker unless this is a tmpfs
self.mock_update_path = pathlib.Path(self.tmpdir)
self.params = Params()
self.basedir = self.mock_update_path / "openpilot"
self.basedir.mkdir()
self.staging_root = self.mock_update_path / "safe_staging"
self.staging_root.mkdir()
self.remote_dir = self.mock_update_path / "remote"
self.remote_dir.mkdir()
mock.patch("openpilot.common.basedir.BASEDIR", self.basedir).start()
os.environ["UPDATER_STAGING_ROOT"] = str(self.staging_root)
os.environ["UPDATER_LOCK_FILE"] = str(self.mock_update_path / "safe_staging_overlay.lock")
self.MOCK_RELEASES = {
"release3": ("0.1.2", "0.1.2 release notes"),
"master": ("0.1.3", "0.1.3 release notes"),
}
def set_target_branch(self, branch):
self.params.put("UpdaterTargetBranch", branch)
def setup_basedir_release(self, release):
self.params = Params()
self.set_target_branch(release)
run(["git", "clone", "-b", release, self.remote_dir, self.basedir])
def update_remote_release(self, release):
update_release(self.remote_dir, release, *self.MOCK_RELEASES[release])
def setup_remote_release(self, release):
run(["git", "init"], cwd=self.remote_dir)
run(["git", "checkout", "-b", release], cwd=self.remote_dir)
self.update_remote_release(release)
def tearDown(self):
mock.patch.stopall()
run(["sudo", "umount", "-l", str(self.staging_root / "merged")])
run(["sudo", "umount", "-l", self.tmpdir])
shutil.rmtree(self.tmpdir)
def send_check_for_updates_signal(self, updated: ManagerProcess):
updated.signal(signal.SIGUSR1.value)
def send_download_signal(self, updated: ManagerProcess):
updated.signal(signal.SIGHUP.value)
def _test_params(self, branch, fetch_available, update_available):
self.assertEqual(self.params.get("UpdaterTargetBranch", encoding="utf-8"), branch)
self.assertEqual(self.params.get_bool("UpdaterFetchAvailable"), fetch_available)
self.assertEqual(self.params.get_bool("UpdateAvailable"), update_available)
def _test_update_params(self, branch, version, release_notes):
self.assertTrue(self.params.get("UpdaterNewDescription", encoding="utf-8").startswith(f"{version} / {branch}"))
self.assertEqual(self.params.get("UpdaterNewReleaseNotes", encoding="utf-8"), f"<p>{release_notes}</p>\n")
def wait_for_idle(self, timeout=5, min_wait_time=2):
start = time.monotonic()
time.sleep(min_wait_time)
while True:
waited = time.monotonic() - start
if self.params.get("UpdaterState", encoding="utf-8") == "idle":
print(f"waited {waited}s for idle")
break
if waited > timeout:
raise TimeoutError("timed out waiting for idle")
time.sleep(1)
def test_new_release(self):
# Start on release3, simulate a release3 commit, ensure we fetch that update properly
self.setup_remote_release("release3")
self.setup_basedir_release("release3")
with processes_context(["updated"]) as [updated]:
self._test_params("release3", False, False)
time.sleep(1)
self._test_params("release3", False, False)
self.MOCK_RELEASES["release3"] = ("0.1.3", "0.1.3 release notes")
self.update_remote_release("release3")
self.send_check_for_updates_signal(updated)
self.wait_for_idle()
self._test_params("release3", True, False)
self.send_download_signal(updated)
self.wait_for_idle()
self._test_params("release3", False, True)
self._test_update_params("release3", *self.MOCK_RELEASES["release3"])
def test_switch_branches(self):
# Start on release3, request to switch to master manually, ensure we switched
self.setup_remote_release("release3")
self.setup_remote_release("master")
self.setup_basedir_release("release3")
with processes_context(["updated"]) as [updated]:
self._test_params("release3", False, False)
self.wait_for_idle()
self._test_params("release3", False, False)
self.set_target_branch("master")
self.send_check_for_updates_signal(updated)
self.wait_for_idle()
self._test_params("master", True, False)
self.send_download_signal(updated)
self.wait_for_idle()
self._test_params("master", False, True)
self._test_update_params("master", *self.MOCK_RELEASES["master"])
if __name__ == "__main__":
unittest.main()

@ -79,8 +79,12 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s,
int nv12_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, rgb_height);
assert(nv12_width == VENUS_UV_STRIDE(COLOR_FMT_NV12, rgb_width));
assert(nv12_height/2 == VENUS_UV_SCANLINES(COLOR_FMT_NV12, rgb_height));
size_t nv12_size = 2346 * nv12_width; // comes from v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage
size_t nv12_uv_offset = nv12_width * nv12_height;
// the encoder HW tells us the size it wants after setting it up.
// TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings?
size_t nv12_size = (rgb_width >= 2688 ? 2900 : 2346)*nv12_width;
vipc_server->create_buffers_with_sizes(stream_type, YUV_BUFFER_COUNT, false, rgb_width, rgb_height, nv12_size, nv12_width, nv12_uv_offset);
LOGD("created %d YUV vipc buffers with size %dx%d", YUV_BUFFER_COUNT, nv12_width, nv12_height);

@ -97,7 +97,6 @@ CONFIGS = {
AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001),
AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000),
],
"mici": [],
}
class Amplifier:

@ -94,11 +94,7 @@ def get_device_type():
# lru_cache and cache can cause memory leaks when used in classes
with open("/sys/firmware/devicetree/base/model") as f:
model = f.read().strip('\x00')
model = model.split('comma ')[-1]
# TODO: remove this with AGNOS 7+
if model.startswith('Qualcomm'):
model = 'tici'
return model
return model.split('comma ')[-1]
class Tici(HardwareBase):
@cached_property
@ -116,6 +112,8 @@ class Tici(HardwareBase):
@cached_property
def amplifier(self):
if self.get_device_type() == "mici":
return None
return Amplifier()
def get_os_version(self):
@ -374,9 +372,10 @@ class Tici(HardwareBase):
def set_power_save(self, powersave_enabled):
# amplifier, 100mW at idle
self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled)
if not powersave_enabled:
self.amplifier.initialize_configuration(self.get_device_type())
if self.amplifier is not None:
self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled)
if not powersave_enabled:
self.amplifier.initialize_configuration(self.get_device_type())
# *** CPU config ***
@ -414,7 +413,8 @@ class Tici(HardwareBase):
return 0
def initialize_hardware(self):
self.amplifier.initialize_configuration(self.get_device_type())
if self.amplifier is not None:
self.amplifier.initialize_configuration(self.get_device_type())
# Allow thermald to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
@ -478,6 +478,12 @@ class Tici(HardwareBase):
'AT+QNVFW="/nv/item_files/ims/IMS_enable",00',
'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01',
]
if self.get_device_type() == "tizi":
cmds += [
# SIM hot swap
'AT+QSIMDET=1,0',
'AT+QSIMSTAT=1',
]
# clear out old blue prime initial APN
os.system('mmcli -m any --3gpp-set-initial-eps-bearer-settings="apn="')

@ -16,7 +16,12 @@
#define V4L2_QCOM_BUF_FLAG_CODECCONFIG 0x00020000
#define V4L2_QCOM_BUF_FLAG_EOS 0x02000000
// echo 0x7fffffff > /sys/kernel/debug/msm_vidc/debug_level
/*
kernel debugging:
echo 0xff > /sys/module/videobuf2_core/parameters/debug
echo 0x7fffffff > /sys/kernel/debug/msm_vidc/debug_level
echo 0xff > /sys/devices/platform/soc/aa00000.qcom,vidc/video4linux/video33/dev_debug
*/
const int env_debug_encoder = (getenv("DEBUG_ENCODER") != NULL) ? atoi(getenv("DEBUG_ENCODER")) : 0;
static void checked_ioctl(int fd, unsigned long request, void *argp) {

@ -89,15 +89,6 @@ kj::Array<capnp::word> logger_build_init_data() {
return capnp::messageToFlatArray(msg);
}
std::string logger_get_route_name() {
char route_name[64] = {'\0'};
time_t rawtime = time(NULL);
struct tm timeinfo;
localtime_r(&rawtime, &timeinfo);
strftime(route_name, sizeof(route_name), "%Y-%m-%d--%H-%M-%S", &timeinfo);
return route_name;
}
std::string logger_get_identifier(std::string key) {
// a log identifier is a 32 bit counter, plus a 10 character unique ID.
// e.g. 000001a3--c20ba54385
@ -131,7 +122,7 @@ static void log_sentinel(LoggerState *log, SentinelType type, int eixt_signal =
}
LoggerState::LoggerState(const std::string &log_root) {
route_name = logger_get_route_name();
route_name = logger_get_identifier("RouteCount");
route_path = log_root + "/" + route_name;
init_data = logger_build_init_data();
}

@ -52,5 +52,4 @@ protected:
};
kj::Array<capnp::word> logger_build_init_data();
std::string logger_get_route_name();
std::string logger_get_identifier(std::string key);

@ -72,8 +72,8 @@ class UploaderTestCase(unittest.TestCase):
uploader.force_wifi = True
uploader.allow_sleep = False
self.seg_num = random.randint(1, 300)
self.seg_format = "2019-04-18--12-52-54--{}"
self.seg_format2 = "2019-05-18--11-22-33--{}"
self.seg_format = "00000004--0ac3964c96--{}"
self.seg_format2 = "00000005--4c4e99b08b--{}"
self.seg_dir = self.seg_format.format(self.seg_num)
self.params = Params()

@ -11,8 +11,15 @@ from openpilot.system.webrtc.webrtcd import get_stream
import aiortc
from teleoprtc import WebRTCOfferBuilder
from parameterized import parameterized_class
@parameterized_class(("in_services", "out_services"), [
(["testJoystick"], ["carState"]),
([], ["carState"]),
(["testJoystick"], []),
([], []),
])
class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase):
async def assertCompletesWithTimeout(self, awaitable, timeout=1):
try:
@ -24,7 +31,7 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase):
async def test_webrtcd(self):
mock_request = MagicMock()
async def connect(offer):
body = {'sdp': offer.sdp, 'cameras': offer.video, 'bridge_services_in': [], 'bridge_services_out': ['carState']}
body = {'sdp': offer.sdp, 'cameras': offer.video, 'bridge_services_in': self.in_services, 'bridge_services_out': self.out_services}
mock_request.json.side_effect = AsyncMock(return_value=body)
response = await get_stream(mock_request)
response_json = json.loads(response.text)
@ -33,7 +40,8 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase):
builder = WebRTCOfferBuilder(connect)
builder.offer_to_receive_video_stream("road")
builder.offer_to_receive_audio_stream()
builder.add_messaging()
if len(self.in_services) > 0 or len(self.out_services) > 0:
builder.add_messaging()
stream = builder.stream()
@ -42,7 +50,7 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase):
self.assertTrue(stream.has_incoming_video_track("road"))
self.assertTrue(stream.has_incoming_audio_track())
self.assertTrue(stream.has_messaging_channel())
self.assertEqual(stream.has_messaging_channel(), len(self.in_services) > 0 or len(self.out_services) > 0)
video_track, audio_track = stream.get_incoming_video_track("road"), stream.get_incoming_audio_track()
await self.assertCompletesWithTimeout(video_track.recv())

@ -102,7 +102,21 @@ class CerealProxyRunner:
await asyncio.sleep(0.01)
class DynamicPubMaster(messaging.PubMaster):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lock = asyncio.Lock()
async def add_services_if_needed(self, services):
async with self.lock:
for service in services:
if service not in self.sock:
self.sock[service] = messaging.pub_sock(service)
class StreamSession:
shared_pub_master = DynamicPubMaster([])
def __init__(self, sdp: str, cameras: list[str], incoming_services: list[str], outgoing_services: list[str], debug_mode: bool = False):
from aiortc.mediastreams import VideoStreamTrack, AudioStreamTrack
from aiortc.contrib.media import MediaBlackhole
@ -128,9 +142,15 @@ class StreamSession:
self.stream = builder.stream()
self.identifier = str(uuid.uuid4())
self.outgoing_bridge = CerealOutgoingMessageProxy(messaging.SubMaster(outgoing_services))
self.incoming_bridge = CerealIncomingMessageProxy(messaging.PubMaster(incoming_services))
self.outgoing_bridge_runner = CerealProxyRunner(self.outgoing_bridge)
self.incoming_bridge: CerealIncomingMessageProxy | None = None
self.incoming_bridge_services = incoming_services
self.outgoing_bridge: CerealOutgoingMessageProxy | None = None
self.outgoing_bridge_runner: CerealProxyRunner | None = None
if len(incoming_services) > 0:
self.incoming_bridge = CerealIncomingMessageProxy(self.shared_pub_master)
if len(outgoing_services) > 0:
self.outgoing_bridge = CerealOutgoingMessageProxy(messaging.SubMaster(outgoing_services))
self.outgoing_bridge_runner = CerealProxyRunner(self.outgoing_bridge)
self.audio_output: AudioOutputSpeaker | MediaBlackhole | None = None
self.run_task: asyncio.Task | None = None
@ -152,6 +172,7 @@ class StreamSession:
return await self.stream.start()
async def message_handler(self, message: bytes):
assert self.incoming_bridge is not None
try:
self.incoming_bridge.send(message)
except Exception as ex:
@ -161,10 +182,13 @@ class StreamSession:
try:
await self.stream.wait_for_connection()
if self.stream.has_messaging_channel():
self.stream.set_message_handler(self.message_handler)
channel = self.stream.get_messaging_channel()
self.outgoing_bridge_runner.proxy.add_channel(channel)
self.outgoing_bridge_runner.start()
if self.incoming_bridge is not None:
await self.shared_pub_master.add_services_if_needed(self.incoming_bridge_services)
self.stream.set_message_handler(self.message_handler)
if self.outgoing_bridge_runner is not None:
channel = self.stream.get_messaging_channel()
self.outgoing_bridge_runner.proxy.add_channel(channel)
self.outgoing_bridge_runner.start()
if self.stream.has_incoming_audio_track():
track = self.stream.get_incoming_audio_track(buffered=False)
self.audio_output = self.audio_output_cls()
@ -181,7 +205,8 @@ class StreamSession:
async def post_run_cleanup(self):
await self.stream.stop()
self.outgoing_bridge_runner.stop()
if self.outgoing_bridge is not None:
self.outgoing_bridge_runner.stop()
if self.audio_output:
self.audio_output.stop()

@ -1 +1 @@
Subproject commit 3f9e8176d1be3d217528baee09fc418fa980a0c3
Subproject commit ab2f09706e8f64390e196f079ac69e67131b07f5

@ -2,13 +2,11 @@
import argparse
import json
from openpilot.selfdrive.car.car_helpers import get_interface_attr
from openpilot.selfdrive.car.values import create_platform_map
def generate_dbc_json() -> str:
all_cars_by_brand = get_interface_attr("CAR_INFO")
all_dbcs_by_brand = get_interface_attr("DBC")
dbc_map = {car: all_dbcs_by_brand[brand][car]['pt'] for brand, cars in all_cars_by_brand.items() for car in cars if car != 'mock'}
dbc_map = create_platform_map(lambda platform: platform.config.dbc_dict["pt"] if platform != "mock" else None)
return json.dumps(dict(sorted(dbc_map.items())), indent=2)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save