openpilot v0.9.10 release

date: 2025-07-05T09:03:06
master commit: c807ecd7e1
Vehicle Researcher 3 days ago
parent 100f89a161
commit 09094684cd
  1. 2
      .gitignore
  2. 1
      Dockerfile.openpilot
  3. 4
      README.md
  4. 8
      RELEASES.md
  5. 5
      SConstruct
  6. 17
      cereal/log.capnp
  7. 3
      cereal/services.py
  8. 13
      codecov.yml
  9. 1
      common/params_keys.h
  10. 52
      common/spinner.py
  11. 63
      common/text_window.py
  12. 2
      common/version.h
  13. 22
      common/watchdog.py
  14. 3
      conftest.py
  15. 16
      docs/CARS.md
  16. 1
      docs/CONTRIBUTING.md
  17. 33
      docs/WORKFLOW.md
  18. 2
      git_src_commit
  19. 2
      git_src_commit_date
  20. 24
      opendbc_repo/docs/CARS.md
  21. 2
      opendbc_repo/opendbc/car/body/fingerprints.py
  22. 17
      opendbc_repo/opendbc/car/chrysler/fingerprints.py
  23. 11
      opendbc_repo/opendbc/car/debug/format_fingerprints.py
  24. 2
      opendbc_repo/opendbc/car/extra_cars.py
  25. 34
      opendbc_repo/opendbc/car/ford/carcontroller.py
  26. 12
      opendbc_repo/opendbc/car/ford/fingerprints.py
  27. 4
      opendbc_repo/opendbc/car/ford/values.py
  28. 1
      opendbc_repo/opendbc/car/gm/fingerprints.py
  29. 2
      opendbc_repo/opendbc/car/honda/carstate.py
  30. 36
      opendbc_repo/opendbc/car/honda/fingerprints.py
  31. 2
      opendbc_repo/opendbc/car/honda/interface.py
  32. 2
      opendbc_repo/opendbc/car/honda/values.py
  33. 30
      opendbc_repo/opendbc/car/hyundai/fingerprints.py
  34. 2
      opendbc_repo/opendbc/car/interfaces.py
  35. 9
      opendbc_repo/opendbc/car/mazda/fingerprints.py
  36. 1
      opendbc_repo/opendbc/car/nissan/fingerprints.py
  37. 2
      opendbc_repo/opendbc/car/rivian/fingerprints.py
  38. 5
      opendbc_repo/opendbc/car/subaru/fingerprints.py
  39. 3
      opendbc_repo/opendbc/car/tesla/fingerprints.py
  40. 2
      opendbc_repo/opendbc/car/tests/routes.py
  41. 6
      opendbc_repo/opendbc/car/tests/test_docs.py
  42. 1
      opendbc_repo/opendbc/car/torque_data/override.toml
  43. 30
      opendbc_repo/opendbc/car/toyota/fingerprints.py
  44. 32
      opendbc_repo/opendbc/car/toyota/values.py
  45. 4
      opendbc_repo/opendbc/car/volkswagen/carstate.py
  46. 35
      opendbc_repo/opendbc/car/volkswagen/fingerprints.py
  47. 6
      opendbc_repo/opendbc/car/volkswagen/values.py
  48. 2
      opendbc_repo/opendbc/dbc/generator/honda/_bosch_adas_2018.dbc
  49. 255
      opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_canfd.dbc
  50. 2
      opendbc_repo/opendbc/dbc/rivian_primary_actuator.dbc
  51. 4
      opendbc_repo/opendbc/dbc/volvo_v40_2017_pt.dbc
  52. 17
      opendbc_repo/opendbc/dbc/vw_mqb.dbc
  53. 39
      opendbc_repo/opendbc/dbc/vw_pq.dbc
  54. 14
      opendbc_repo/opendbc/safety/tests/libsafety/SConscript
  55. 4
      opendbc_repo/opendbc/safety/tests/test_ford.py
  56. 8
      panda/board/boards/board_declarations.h
  57. 35
      panda/board/boards/grey.h
  58. 166
      panda/board/boards/uno.h
  59. 45
      panda/board/jungle/scripts/panda_identification_test.py
  60. 8
      panda/board/stm32f4/board.h
  61. 1
      panda/tests/hitl/7_internal.py
  62. 14
      pyproject.toml
  63. 1
      rednose_repo/requirements.txt
  64. 9
      rednose_repo/setup.py
  65. 23
      release/README.md
  66. BIN
      selfdrive/assets/icons/microphone.png
  67. 7
      selfdrive/assets/offroad/fcc.html
  68. 9
      selfdrive/controls/lib/drive_helpers.py
  69. 29
      selfdrive/controls/lib/longitudinal_planner.py
  70. 6
      selfdrive/locationd/calibrationd.py
  71. 6
      selfdrive/locationd/helpers.py
  72. 2
      selfdrive/locationd/lagd.py
  73. 4
      selfdrive/locationd/test/test_lagd.py
  74. 25
      selfdrive/locationd/test/test_torqued.py
  75. 1
      selfdrive/locationd/torqued.py
  76. 6
      selfdrive/modeld/SConscript
  77. 7
      selfdrive/modeld/fill_model_msg.py
  78. 53
      selfdrive/modeld/modeld.py
  79. BIN
      selfdrive/modeld/models/driving_policy.onnx
  80. BIN
      selfdrive/modeld/models/driving_vision.onnx
  81. 13
      selfdrive/modeld/parse_model_outputs.py
  82. 5
      selfdrive/selfdrived/events.py
  83. 6
      selfdrive/selfdrived/selfdrived.py
  84. 20
      selfdrive/test/ci_shell.sh
  85. 35
      selfdrive/test/process_replay/model_replay.py
  86. 13
      selfdrive/test/process_replay/process_replay.py
  87. 2
      selfdrive/test/process_replay/ref_commit
  88. 27
      selfdrive/test/process_replay/regen.py
  89. 6
      selfdrive/test/process_replay/test_processes.py
  90. 4
      selfdrive/test/process_replay/test_regen.py
  91. 2
      selfdrive/ui/SConscript
  92. 221
      selfdrive/ui/layouts/home.py
  93. 79
      selfdrive/ui/layouts/main.py
  94. 17
      selfdrive/ui/layouts/network.py
  95. 19
      selfdrive/ui/layouts/settings/developer.py
  96. 141
      selfdrive/ui/layouts/settings/device.py
  97. 180
      selfdrive/ui/layouts/settings/firehose.py
  98. 73
      selfdrive/ui/layouts/settings/settings.py
  99. 35
      selfdrive/ui/layouts/settings/software.py
  100. 37
      selfdrive/ui/layouts/settings/toggles.py
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -70,8 +70,6 @@ flycheck_*
cppcheck_report.txt
comma*.sh
selfdrive/modeld/thneed/compile
selfdrive/modeld/models/*.thneed
selfdrive/modeld/models/*.pkl
*.bz2

@ -3,7 +3,6 @@ FROM ghcr.io/commaai/openpilot-base:latest
ENV PYTHONUNBUFFERED=1
ENV OPENPILOT_PATH=/home/batman/openpilot
ENV PYTHONPATH=${OPENPILOT_PATH}:${PYTHONPATH}
RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH}

@ -23,7 +23,6 @@
Quick start: `bash <(curl -fsSL openpilot.comma.ai)`
[![openpilot tests](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml/badge.svg)](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml)
[![codecov](https://codecov.io/gh/commaai/openpilot/branch/master/graph/badge.svg)](https://codecov.io/gh/commaai/openpilot)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![X Follow](https://img.shields.io/twitter/follow/comma_ai)](https://x.com/comma_ai)
[![Discord](https://img.shields.io/discord/469524606043160576)](https://discord.comma.ai)
@ -67,7 +66,6 @@ openpilot is developed by [comma](https://comma.ai/) and by users like you. We w
* Join the [community Discord](https://discord.comma.ai)
* Check out [the contributing docs](docs/CONTRIBUTING.md)
* Check out the [openpilot tools](tools/)
* Read about the [development workflow](docs/WORKFLOW.md)
* Code documentation lives at https://docs.comma.ai
* Information about running openpilot lives on the [community wiki](https://github.com/commaai/openpilot/wiki)
@ -104,7 +102,7 @@ By default, openpilot uploads the driving data to our servers. You can also acce
openpilot is open source software: the user is free to disable data collection if they wish to do so.
openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
The driver-facing camera and microphone are only logged if you explicitly opt-in in settings.
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
</details>

@ -1,10 +1,16 @@
Version 0.9.10 (2025-07-07)
========================
* New driving model
* Lead car ground-truth fixes
* Ported over VAE from the MLSIM stack
* Opt-in audio recording for dashcam video
Version 0.9.9 (2025-05-23)
========================
* New driving model
* New training architecture using parts from MLSIM
* Steering actuation delay is now learned online
* Ford Escape 2023-24 support thanks to incognitojam!
* Ford Expedition 2022-24 support thanks to alan-polk!
* Ford Kuga 2024 support thanks to incognitojam!
* Hyundai Nexo 2021 support thanks to sunnyhaibin!
* Tesla Model 3 and Y support thanks to lukasloetkolben!

@ -39,10 +39,6 @@ AddOption('--clazy',
action='store_true',
help='build with clazy')
AddOption('--compile_db',
action='store_true',
help='build clang compilation database')
AddOption('--ccflags',
action='store',
type='string',
@ -214,7 +210,6 @@ if arch == "Darwin":
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
env["LINKFLAGS"] += darwin_rpath_link_flags
if GetOption('compile_db'):
env.CompilationDatabase('compile_commands.json')
# Setup cache dir

@ -1083,7 +1083,7 @@ struct ModelDataV2 {
confidence @23: ConfidenceClass;
# Model perceived motion
temporalPose @21 :Pose;
temporalPoseDEPRECATED @21 :Pose;
# e2e lateral planner
action @26: Action;
@ -2281,6 +2281,7 @@ struct LiveTorqueParametersData {
points @10 :List(List(Float32));
version @11 :Int32;
useParams @12 :Bool;
calPerc @13 :Int8;
}
struct LiveDelayData {
@ -2291,6 +2292,7 @@ struct LiveDelayData {
lateralDelayEstimate @3 :Float32;
lateralDelayEstimateStd @5 :Float32;
points @4 :List(Float32);
calPerc @6 :Int8;
enum Status {
unestimated @0;
@ -2468,13 +2470,19 @@ struct DebugAlert {
struct UserFlag {
}
struct Microphone {
struct SoundPressure @0xdc24138990726023 {
soundPressure @0 :Float32;
# uncalibrated, A-weighted
soundPressureWeighted @3 :Float32;
soundPressureWeightedDb @1 :Float32;
filteredSoundPressureWeightedDb @2 :Float32;
filteredSoundPressureWeightedDbDEPRECATED @2 :Float32;
}
struct AudioData {
data @0 :Data;
sampleRate @1 :UInt32;
}
struct Touch {
@ -2554,7 +2562,8 @@ struct Event {
livestreamDriverEncodeIdx @119 :EncodeIndex;
# microphone data
microphone @103 :Microphone;
soundPressure @103 :SoundPressure;
rawAudioData @147 :AudioData;
# systems stuff
androidLog @20 :AndroidLogEntry;

@ -73,7 +73,8 @@ _services: dict[str, tuple] = {
"navThumbnail": (True, 0.),
"qRoadEncodeIdx": (False, 20.),
"userFlag": (True, 0., 1),
"microphone": (True, 10., 10),
"soundPressure": (True, 10., 10),
"rawAudioData": (False, 20.),
# debug
"uiDebug": (True, 0., 1),

@ -1,13 +0,0 @@
comment: false
coverage:
status:
project:
default:
informational: true
patch: off
ignore:
- "**/test_*.py"
- "selfdrive/test/**"
- "system/version.py" # codecov changes depending on if we are in a branch or not
- "tools"

@ -99,6 +99,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"PandaSignatures", CLEAR_ON_MANAGER_START},
{"PrimeType", PERSISTENT},
{"RecordAudio", PERSISTENT},
{"RecordFront", PERSISTENT},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"SecOCKey", PERSISTENT | DONT_LOG},

@ -0,0 +1,52 @@
import os
import subprocess
from openpilot.common.basedir import BASEDIR
class Spinner:
def __init__(self):
try:
self.spinner_proc = subprocess.Popen(["./spinner.py"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "system", "ui"),
close_fds=True)
except OSError:
self.spinner_proc = None
def __enter__(self):
return self
def update(self, spinner_text: str):
if self.spinner_proc is not None:
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
try:
self.spinner_proc.stdin.flush()
except BrokenPipeError:
pass
def update_progress(self, cur: float, total: float):
self.update(str(round(100 * cur / total)))
def close(self):
if self.spinner_proc is not None:
self.spinner_proc.kill()
try:
self.spinner_proc.communicate(timeout=2.)
except subprocess.TimeoutExpired:
print("WARNING: failed to kill spinner")
self.spinner_proc = None
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == "__main__":
import time
with Spinner() as s:
s.update("Spinner text")
time.sleep(5.0)
print("gone")
time.sleep(5.0)

@ -0,0 +1,63 @@
#!/usr/bin/env python3
import os
import time
import subprocess
from openpilot.common.basedir import BASEDIR
class TextWindow:
def __init__(self, text):
try:
self.text_proc = subprocess.Popen(["./text.py", text],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "system", "ui"),
close_fds=True)
except OSError:
self.text_proc = None
def get_status(self):
if self.text_proc is not None:
self.text_proc.poll()
return self.text_proc.returncode
return None
def __enter__(self):
return self
def close(self):
if self.text_proc is not None:
self.text_proc.terminate()
self.text_proc = None
def wait_for_exit(self):
if self.text_proc is not None:
while True:
if self.get_status() == 1:
return
time.sleep(0.1)
def __del__(self):
self.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == "__main__":
text = """Traceback (most recent call last):
File "./controlsd.py", line 608, in <module>
main()
File "./controlsd.py", line 604, in main
controlsd_thread(sm, pm, logcan)
File "./controlsd.py", line 455, in controlsd_thread
1/0
ZeroDivisionError: division by zero"""
print(text)
with TextWindow(text) as s:
for _ in range(100):
if s.get_status() == 1:
print("Got exit button")
break
time.sleep(0.1)
print("gone")

@ -1 +1 @@
#define COMMA_VERSION "0.9.9"
#define COMMA_VERSION "0.9.10"

@ -0,0 +1,22 @@
import os
import time
import struct
from openpilot.system.hardware.hw import Paths
WATCHDOG_FN = f"{Paths.shm_path()}/wd_"
_LAST_KICK = 0.0
def kick_watchdog():
global _LAST_KICK
current_time = time.monotonic()
if current_time - _LAST_KICK < 1.0:
return
try:
with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f:
f.write(struct.pack('<Q', int(current_time * 1e9)))
f.flush()
_LAST_KICK = current_time
except OSError:
pass

@ -2,7 +2,6 @@ import contextlib
import gc
import os
import pytest
import random
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.manager import manager
@ -48,8 +47,6 @@ def clean_env():
@pytest.fixture(scope="function", autouse=True)
def openpilot_function_fixture(request):
random.seed(0)
with clean_env():
# setup a clean environment for each test
with OpenpilotPrefix(shared_download_cache=request.node.get_closest_marker("shared_download_cache") is not None) as prefix:

@ -15,7 +15,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>|||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q2 2018">Buy Here</a></sub></details>|||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q3 2019-23">Buy Here</a></sub></details>|||
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=Q3 2019-24">Buy Here</a></sub></details>|||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=RS3 2018">Buy Here</a></sub></details>|||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=S3 2015-17">Buy Here</a></sub></details>|||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<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=Chevrolet&model=Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
@ -38,6 +38,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Hybrid 2023-24">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=uUGkH6C_EQU|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Ford&model=Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=uUGkH6C_EQU|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Expedition 2022-24">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=MewJc9LYp9M|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Ford&model=Explorer 2020-24">Buy Here</a></sub></details>|||
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Ford&model=Explorer Hybrid 2020-24">Buy Here</a></sub></details>|||
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=F-150 2021-23">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=MewJc9LYp9M|
@ -53,7 +54,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2023-24">Buy Here</a></sub></details>|||
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick Hybrid 2022">Buy Here</a></sub></details>|||
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick Hybrid 2023-24">Buy Here</a></sub></details>|||
|Ford|Mustang Mach-E 2021-23|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Mustang Mach-E 2021-23">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=uUGkH6C_EQU|
|Ford|Mustang Mach-E 2021-24|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Mustang Mach-E 2021-24">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=uUGkH6C_EQU|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Ranger 2024">Buy Here</a></sub></details>||https://www.youtube.com/watch?v=uUGkH6C_EQU|
|Genesis|G70 2018|All|Stock|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 comma 3X<br>- 1 comma power v3<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=Genesis&model=G70 2018">Buy Here</a></sub></details>|||
|Genesis|G70 2019-21|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 F connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Genesis&model=G70 2019-21">Buy Here</a></sub></details>|||
@ -77,8 +78,8 @@ A supported vehicle is one that just works when you install a comma device. All
|Honda|Civic 2022-24|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 Honda Bosch B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>|||
|Honda|Civic Hatchback 2022-24|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 Honda Bosch B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic Hatchback 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|Honda|Civic Hatchback Hybrid 2023 (Europe only)|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 Honda Bosch B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic Hatchback Hybrid 2023 (Europe only)">Buy Here</a></sub></details>|||
|Honda|Civic Hatchback Hybrid 2025|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 Honda Bosch B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic Hatchback Hybrid 2025">Buy Here</a></sub></details>|||
|Honda|Civic Hatchback Hybrid (Europe only) 2023|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 Honda Bosch B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=CR-V 2015-16">Buy Here</a></sub></details>|||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>|||
|Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Honda&model=CR-V Hybrid 2017-22">Buy Here</a></sub></details>|||
@ -115,7 +116,6 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 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 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>|||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|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 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Kona 2020">Buy Here</a></sub></details>|||
|Hyundai|Kona 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 O connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Kona 2022">Buy Here</a></sub></details>|||
|Hyundai|Kona Electric 2018-21|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 G connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>|||
|Hyundai|Kona Electric 2022-23|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 O connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Kona Electric 2022-23">Buy Here</a></sub></details>|||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Hyundai&model=Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
@ -181,12 +181,12 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|Telluride 2020-22|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 comma 3X<br>- 1 comma power v3<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=Telluride 2020-22">Buy Here</a></sub></details>|||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|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 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=CT Hybrid 2017-18">Buy Here</a></sub></details>|||
|Lexus|ES 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=ES 2017-18">Buy Here</a></sub></details>|||
|Lexus|ES 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=ES 2019-24">Buy Here</a></sub></details>|||
|Lexus|ES 2019-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=ES 2019-25">Buy Here</a></sub></details>|||
|Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=ES Hybrid 2017-18">Buy Here</a></sub></details>|||
|Lexus|ES Hybrid 2019-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=ES Hybrid 2019-25">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=GS F 2016">Buy Here</a></sub></details>|||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=IS 2017-19">Buy Here</a></sub></details>|||
|Lexus|IS 2022-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 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=IS 2022-23">Buy Here</a></sub></details>|||
|Lexus|IS 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=IS 2022-24">Buy Here</a></sub></details>|||
|Lexus|LC 2024|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=LC 2024">Buy Here</a></sub></details>|||
|Lexus|NX 2018-19|All|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 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=NX 2018-19">Buy Here</a></sub></details>|||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<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=Lexus&model=NX 2020-21">Buy Here</a></sub></details>|||
@ -313,11 +313,11 @@ A supported vehicle is one that just works when you install a comma device. All
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Cross 2021">Buy Here</a></sub></details>[<sup>17</sup>](#footnotes)|||
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=T-Roc 2018-23">Buy Here</a></sub></details>|||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Taos 2022-23">Buy Here</a></sub></details>|||
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Taos 2022-24">Buy Here</a></sub></details>|||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont 2018-22">Buy Here</a></sub></details>|||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>|||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Teramont X 2021-22">Buy Here</a></sub></details>|||
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan 2018-23">Buy Here</a></sub></details>|||
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan 2018-24">Buy Here</a></sub></details>|||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Tiguan eHybrid 2021-23">Buy Here</a></sub></details>|||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>|||

@ -7,7 +7,6 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu
### Getting Started
* Setup your [development environment](../tools/)
* Read about the [development workflow](WORKFLOW.md)
* Join our [Discord](https://discord.comma.ai)
* Docs are at https://docs.comma.ai and https://blog.comma.ai

@ -1,33 +0,0 @@
# openpilot development workflow
Aside from the ML models, most tools used for openpilot development are in this repo.
Most development happens on normal Ubuntu workstations, and not in cars or directly on comma devices. See the [setup guide](../tools) for getting your PC setup for openpilot development.
## Quick start
```bash
# get the latest stuff
git pull
git lfs pull
git submodule update --init --recursive
# update dependencies
tools/ubuntu_setup.sh
# build everything
scons -j$(nproc)
# build just the ui with either of these
scons -j8 selfdrive/ui/
cd selfdrive/ui/ && scons -u -j8
# test everything
pytest
# test just logging services
cd system/loggerd && pytest .
# run the linter
op lint
```

@ -1 +1 @@
8aadf02b2fd91f4e1285e18c2c7feb32d93b66f5
c807ecd7e173908ef0ae6ce0e72102a629369957

@ -1 +1 @@
1749153081 2025-06-05 12:51:21 -0700
1751594279 2025-07-03 18:57:59 -0700

@ -1,6 +1,6 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 361 Known Cars
# Support Information for 363 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
@ -14,7 +14,7 @@
|Audi|A4 2016-24|All|[Not compatible](#flexray)|
|Audi|A5 2016-24|All|[Not compatible](#flexray)|
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q5 2017-24|All|[Not compatible](#flexray)|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
@ -38,6 +38,7 @@
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
@ -53,7 +54,7 @@
|Ford|Maverick 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-23|All|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-24|All|[Upstream](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Genesis|G70 2018|All|[Upstream](#upstream)|
|Genesis|G70 2019-21|All|[Upstream](#upstream)|
@ -79,8 +80,8 @@
|Honda|Civic 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback 2017-21|Honda Sensing|[Upstream](#upstream)|
|Honda|Civic Hatchback 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid 2023 (Europe only)|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid 2025|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|[Upstream](#upstream)|
|Honda|Clarity 2018-21|All|[Community](#community)|
|Honda|CR-V 2015-16|Touring Trim|[Upstream](#upstream)|
|Honda|CR-V 2017-22|Honda Sensing|[Upstream](#upstream)|
@ -192,13 +193,13 @@
|Kia|Telluride 2023-24|HDA2|[Community](#community)|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|ES 2017-18|All|[Upstream](#upstream)|
|Lexus|ES 2019-24|All|[Upstream](#upstream)|
|Lexus|ES 2019-25|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2017-18|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2019-25|All|[Upstream](#upstream)|
|Lexus|GS F 2016|All|[Upstream](#upstream)|
|Lexus|IS 2017-19|All|[Upstream](#upstream)|
|Lexus|IS 2022-23|All|[Upstream](#upstream)|
|Lexus|LC 2024|All|[Upstream](#upstream)|
|Lexus|IS 2022-24|All|[Upstream](#upstream)|
|Lexus|LC 2024-25|All|[Upstream](#upstream)|
|Lexus|NS 2022-25|Any|[Not compatible](#can-bus-security)|
|Lexus|NX 2018-19|All|[Upstream](#upstream)|
|Lexus|NX 2020-21|All|[Upstream](#upstream)|
@ -316,14 +317,15 @@
|Toyota|RAV4 Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Prime 2021-23|Any|[Community](#community)|
|Toyota|RAV4 Prime 2021-23|All|[Community](#community)|
|Toyota|RAV4 Prime 2024-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Sienna 2018-20|All|[Upstream](#upstream)|
|Toyota|Sienna 2021-23|Any|[Community](#community)|
|Toyota|Sienna 2021-23|All|[Community](#community)|
|Toyota|Sienna 2024-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Tundra 2022-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Venza 2021-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Yaris (Non-US only) 2023|All|[Community](#community)|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
@ -358,11 +360,11 @@
|Volkswagen|Sharan 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|

@ -1,4 +1,4 @@
# ruff: noqa: E501
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.body.values import CAR

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.chrysler.values import CAR
@ -130,6 +131,7 @@ FW_VERSIONS = {
b'68496647AJ ',
b'68496650AH ',
b'68496650AI ',
b'68496650AL ',
b'68496652AH ',
b'68526752AD ',
b'68526752AE ',
@ -145,6 +147,7 @@ FW_VERSIONS = {
b'68414275AC',
b'68414275AD',
b'68443154AB',
b'68443154AC',
b'68443155AC',
b'68443158AB',
b'68501050AD',
@ -206,6 +209,7 @@ FW_VERSIONS = {
b'68529064AB',
b'68594990AB',
b'68594990AD',
b'68594991AB',
],
(Ecu.srs, 0x744, None): [
b'68405567AB',
@ -233,6 +237,7 @@ FW_VERSIONS = {
b'68594341AB',
],
(Ecu.engine, 0x7e0, None): [
b'68416680AD ',
b'68416680AE ',
b'68416680AF ',
b'68416680AG ',
@ -262,6 +267,7 @@ FW_VERSIONS = {
b'05190289AE',
b'68540977AH',
b'68540977AK',
b'68540977AL',
b'68597647AE',
b'68597647AF',
b'68632416AB',
@ -382,6 +388,7 @@ FW_VERSIONS = {
b'68412635AE ',
b'68412635AG ',
b'68412660AD ',
b'68412660AF ',
b'68422860AB',
b'68449435AE ',
b'68496223AA ',
@ -417,6 +424,7 @@ FW_VERSIONS = {
b'68434847AC',
b'68434849AC',
b'68434850AC',
b'68434855AC',
b'68434856AC',
b'68434858AC',
b'68434859AC',
@ -469,6 +477,7 @@ FW_VERSIONS = {
],
(Ecu.srs, 0x744, None): [
b'68428609AB',
b'68441329AA',
b'68441329AB',
b'68473844AB',
b'68490898AA',
@ -494,6 +503,8 @@ FW_VERSIONS = {
b'68548900AC',
b'68586307AB',
b'68586307AC',
b'68728724AA',
b'68728727AA',
],
(Ecu.fwdRadar, 0x753, None): [
b'04672892AB',
@ -525,6 +536,7 @@ FW_VERSIONS = {
b'68466110AA',
b'68466110AB',
b'68466113AA',
b'68466116AA',
b'68469901AA',
b'68469904AA',
b'68469907AA',
@ -570,6 +582,7 @@ FW_VERSIONS = {
b'05190346AD',
b'68378695AI ',
b'68378695AJ ',
b'68378695AK ',
b'68378696AJ ',
b'68378696AK ',
b'68378701AI ',
@ -591,6 +604,7 @@ FW_VERSIONS = {
b'68455119AC ',
b'68455137AC ',
b'68455142AC ',
b'68455142AE ',
b'68455145AC ',
b'68455145AE ',
b'68455146AC ',
@ -602,11 +616,13 @@ FW_VERSIONS = {
b'68467936AC ',
b'68500630AD',
b'68500630AE',
b'68500630AF',
b'68500631AE',
b'68502719AC ',
b'68502722AC ',
b'68502733AC ',
b'68502734AF ',
b'68502737AF ',
b'68502740AF ',
b'68502741AF ',
b'68502742AC ',
@ -639,6 +655,7 @@ FW_VERSIONS = {
b'68360081AN',
b'68360085AH',
b'68360085AJ',
b'68360085AK',
b'68360085AL',
b'68360085AO',
b'68360086AH',

@ -2,11 +2,11 @@
import jinja2
import os
from cereal import car
from openpilot.common.basedir import BASEDIR
from opendbc.car.common.basedir import BASEDIR
from opendbc.car.interfaces import get_interface_attr
from opendbc.car.structs import CarParams
Ecu = car.CarParams.Ecu
Ecu = CarParams.Ecu
CARS = get_interface_attr('CAR')
FW_VERSIONS = get_interface_attr('FW_VERSIONS')
@ -14,9 +14,10 @@ FINGERPRINTS = get_interface_attr('FINGERPRINTS')
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
FINGERPRINTS_PY_TEMPLATE = jinja2.Template("""
{%- if FINGERPRINTS[brand] %}
{%- if FINGERPRINTS[brand] and brand != 'body' %}
# ruff: noqa: E501
{% endif %}
\"\"\" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE.\"\"\"
{% if FW_VERSIONS[brand] %}
from opendbc.car.structs import CarParams
{% endif %}
@ -66,7 +67,7 @@ FW_VERSIONS{% if not FW_VERSIONS[brand] %}: dict[str, dict[tuple, list[bytes]]]{
def format_brand_fw_versions(brand, extra_fw_versions: None | dict[str, dict[tuple, list[bytes]]] = None):
extra_fw_versions = extra_fw_versions or {}
fingerprints_file = os.path.join(BASEDIR, f"opendbc/car/{brand}/fingerprints.py")
fingerprints_file = os.path.join(BASEDIR, f"{brand}/fingerprints.py")
with open(fingerprints_file) as f:
comments = [line for line in f.readlines() if line.startswith("#") and "noqa" not in line]

@ -55,10 +55,8 @@ class CAR(Platforms):
ToyotaSecurityCarDocs("Toyota Camry 2025"),
ToyotaSecurityCarDocs("Toyota Corolla Cross 2022-25"),
ToyotaSecurityCarDocs("Toyota Highlander 2025"),
CommunityCarDocs("Toyota RAV4 Prime 2021-23"),
ToyotaSecurityCarDocs("Toyota RAV4 Prime 2024-25"),
ToyotaSecurityCarDocs("Toyota Sequoia 2023-25"),
CommunityCarDocs("Toyota Sienna 2021-23"),
ToyotaSecurityCarDocs("Toyota Sienna 2024-25"),
ToyotaSecurityCarDocs("Toyota Tundra 2022-25"),
ToyotaSecurityCarDocs("Toyota Venza 2021-25"),

@ -3,7 +3,7 @@ import numpy as np
from opendbc.can.packer import CANPacker
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, apply_std_steer_angle_limits, structs
from opendbc.car.ford import fordcan
from opendbc.car.ford.values import CarControllerParams, FordFlags
from opendbc.car.ford.values import CarControllerParams, FordFlags, CAR
from opendbc.car.interfaces import CarControllerBase, ISO_LATERAL_ACCEL, V_CRUISE_MAX
LongCtrlState = structs.CarControl.Actuators.LongControlState
@ -15,6 +15,26 @@ AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
def anti_overshoot(new_curvature, last_curvature, v_ego):
diff = 0.05
tau = 5 # 5s smooths over the overshoot
dt = DT_CTRL * CarControllerParams.STEER_STEP
alpha = 1 - np.exp(-dt/tau)
lataccel = new_curvature * (v_ego ** 2)
last_lataccel = last_curvature * (v_ego ** 2)
if lataccel > last_lataccel + diff:
last_lataccel = lataccel - diff
elif lataccel < last_lataccel - diff:
last_lataccel = lataccel + diff
last_lataccel = alpha * lataccel + (1 - alpha) * last_lataccel
output_curvature = last_lataccel / (v_ego ** 2 + 1e-6)
return np.interp(v_ego, [5, 10], [new_curvature, output_curvature])
def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw, steering_angle, lat_active, CP):
# No blending at low speed due to lack of torque wind-up and inaccurate current curvature
if v_ego_raw > 9:
@ -48,6 +68,7 @@ class CarController(CarControllerBase):
self.CAN = fordcan.CanBus(CP)
self.apply_curvature_last = 0
self.anti_overshoot_curvature_last = 0
self.accel = 0.0
self.gas = 0.0
self.brake_request = False
@ -82,9 +103,18 @@ class CarController(CarControllerBase):
### lateral control ###
# send steer msg at 20Hz
if (self.frame % CarControllerParams.STEER_STEP) == 0:
# Bronco and some other cars consistently overshoot curv requests
# Apply some deadzone + smoothing convergence to avoid oscillations
if self.CP.carFingerprint in [CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14]:
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
request_curvature = self.anti_overshoot_curvature_last
else:
request_curvature = actuators.curvature
# apply rate limits, curvature error limit, and clip to signal range
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
self.apply_curvature_last = apply_ford_curvature_limits(request_curvature, self.apply_curvature_last, current_curvature,
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
if self.CP.flags & FordFlags.CANFD:

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.ford.values import CAR
@ -106,21 +107,27 @@ FW_VERSIONS = {
CAR.FORD_F_150_MK14: {
(Ecu.eps, 0x730, None): [
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3V-14D003-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'NL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL34-2D053-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL3V-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL3V-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'ML3T-14D049-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x706, None): [
b'ML3T-14H102-ABR\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PJ6T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'RJ6T-14H102-BBC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.FORD_F_150_LIGHTNING_MK1: {
@ -144,10 +151,13 @@ FW_VERSIONS = {
(Ecu.eps, 0x730, None): [
b'LJ9C-14D003-AM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LJ9C-14D003-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LJ9C-14D003-FA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LJ9C-14D003-GA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LJ9C-14D003-HA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'LK9C-2D053-CK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LK9C-2D053-CN\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -193,11 +203,13 @@ FW_VERSIONS = {
},
CAR.FORD_RANGER_MK2: {
(Ecu.eps, 0x730, None): [
b'NB3C-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NL14-14D003-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'RB3C-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'PB3C-2D053-ZD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PB3C-2D053-ZG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PB3C-2D053-ZJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [

@ -135,7 +135,7 @@ class CAR(Platforms):
)
FORD_ESCAPE_MK4_5 = FordCANFDPlatformConfig(
[
FordCarDocs("Ford Escape 2023-24", hybrid=True, plug_in_hybrid=True),
FordCarDocs("Ford Escape 2023-24", hybrid=True, plug_in_hybrid=True, setup_video="https://www.youtube.com/watch?v=M6uXf4b2SHM"),
FordCarDocs("Ford Kuga Hybrid 2024", "All"),
FordCarDocs("Ford Kuga Plug-in Hybrid 2024", "All"),
],
@ -172,7 +172,7 @@ class CAR(Platforms):
CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0),
)
FORD_MUSTANG_MACH_E_MK1 = FordCANFDPlatformConfig(
[FordCarDocs("Ford Mustang Mach-E 2021-23", "All", setup_video="https://www.youtube.com/watch?v=AR4_eTF3b_A")],
[FordCarDocs("Ford Mustang Mach-E 2021-24", "All", setup_video="https://www.youtube.com/watch?v=AR4_eTF3b_A")],
CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio
)
FORD_RANGER_MK2 = FordCANFDPlatformConfig(

@ -1,4 +1,5 @@
# ruff: noqa: E501
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.gm.values import CAR
# Trailblazer also matches as a SILVERADO, TODO: split with fw versions

@ -83,7 +83,7 @@ class CarState(CarStateBase):
self.gearbox_msg = "GEARBOX"
if CP.carFingerprint == CAR.HONDA_ACCORD and CP.transmissionType == TransmissionType.cvt:
self.gearbox_msg = "GEARBOX_15T"
elif CP.carFingerprint == CAR.HONDA_CIVIC_2022 and CP.transmissionType == TransmissionType.cvt:
elif CP.carFingerprint in (CAR.HONDA_CIVIC_2022, CAR.HONDA_HRV_3G) and CP.transmissionType == TransmissionType.cvt:
self.gearbox_msg = "GEARBOX_ALT"
elif CP.transmissionType == TransmissionType.manual:
self.gearbox_msg = "GEARBOX_ALT_2"

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.honda.values import CAR
@ -54,6 +55,7 @@ FW_VERSIONS = {
b'57114-TVA-C050\x00\x00',
b'57114-TVA-C060\x00\x00',
b'57114-TVA-C530\x00\x00',
b'57114-TVA-D520\x00\x00',
b'57114-TVA-E520\x00\x00',
b'57114-TVE-H250\x00\x00',
b'57114-TWA-A040\x00\x00',
@ -190,6 +192,7 @@ FW_VERSIONS = {
b'28101-5CK-C150\x00\x00',
b'28101-5CK-G210\x00\x00',
b'28101-5CK-J710\x00\x00',
b'28101-5CK-Q110\x00\x00',
b'28101-5CK-Q610\x00\x00',
b'28101-5DJ-A610\x00\x00',
b'28101-5DJ-A710\x00\x00',
@ -207,12 +210,14 @@ FW_VERSIONS = {
b'57114-TGG-L320\x00\x00',
b'57114-TGG-L330\x00\x00',
b'57114-TGH-L130\x00\x00',
b'57114-TGJ-Q330\x00\x00',
b'57114-TGK-T320\x00\x00',
b'57114-TGL-G330\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TBA-C020\x00\x00',
b'39990-TBA-C120\x00\x00',
b'39990-TEA-T330\x00\x00',
b'39990-TEA-T820\x00\x00',
b'39990-TEZ-T020\x00\x00',
b'39990-TGG,A020\x00\x00',
@ -234,6 +239,7 @@ FW_VERSIONS = {
b'77959-TGG-G010\x00\x00',
b'77959-TGG-G110\x00\x00',
b'77959-TGG-J320\x00\x00',
b'77959-TGG-Q810\x00\x00',
b'77959-TGG-Z820\x00\x00',
b'77959-TGH-J110\x00\x00',
],
@ -248,6 +254,7 @@ FW_VERSIONS = {
b'36802-TGG-G040\x00\x00',
b'36802-TGG-G130\x00\x00',
b'36802-TGH-A140\x00\x00',
b'36802-TGK-Q030\x00\x00',
b'36802-TGK-Q120\x00\x00',
b'36802-TGL-G040\x00\x00',
],
@ -263,6 +270,7 @@ FW_VERSIONS = {
b'36161-TGG-G130\x00\x00',
b'36161-TGG-G140\x00\x00',
b'36161-TGH-A140\x00\x00',
b'36161-TGK-Q040\x00\x00',
b'36161-TGK-Q120\x00\x00',
b'36161-TGL-G050\x00\x00',
b'36161-TGL-G070\x00\x00',
@ -793,18 +801,22 @@ FW_VERSIONS = {
CAR.HONDA_HRV_3G: {
(Ecu.eps, 0x18da30f1, None): [
b'39990-3M0-G110\x00\x00',
b'39990-3M0-J030\x00\x00',
b'39990-3W0-A030\x00\x00',
],
(Ecu.gateway, 0x18daeff1, None): [
b'38897-3M0-M110\x00\x00',
b'38897-3M0-Z010\x00\x00',
b'38897-3W1-A010\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-3M0-K840\x00\x00',
b'77959-3N1-Z840\x00\x00',
b'77959-3V0-A820\x00\x00',
b'77959-3V0-A910\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'8S102-3M3-T050\x00\x00',
b'8S102-3M6-P030\x00\x00',
b'8S102-3W0-A060\x00\x00',
b'8S102-3W0-AB10\x00\x00',
@ -812,6 +824,7 @@ FW_VERSIONS = {
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-3M6-M010\x00\x00',
b'57114-3N1-T020\x00\x00',
b'57114-3W0-A040\x00\x00',
],
(Ecu.transmission, 0x18da1ef1, None): [
@ -867,10 +880,10 @@ FW_VERSIONS = {
b'39990-T39-A130\x00\x00',
b'39990-T43-J020\x00\x00',
b'39990-T43-J030\x00\x00',
b'39990-T60-J030\x00\x00',
b'39990-T56-A040\x00\x00',
b'39990-T50-J030\x00\x00',
b'39990-T50-J110\x00\x00',
b'39990-T56-A040\x00\x00',
b'39990-T60-J030\x00\x00',
],
(Ecu.gateway, 0x18daeff1, None): [
b'38897-T20-A020\x00\x00',
@ -881,10 +894,10 @@ FW_VERSIONS = {
b'38897-T22-A110\x00\x00',
b'38897-T24-Z120\x00\x00',
b'38897-T47-AA20\x00\x00',
b'38897-T60-A110\x00\x00',
b'38897-T61-A320\x00\x00',
b'38897-T50-E310\x00\x00',
b'38897-T50-EA10\x00\x00',
b'38897-T60-A110\x00\x00',
b'38897-T61-A320\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-T20-A970\x00\x00',
@ -893,11 +906,11 @@ FW_VERSIONS = {
b'77959-T39-A910\x00\x00',
b'77959-T47-A940\x00\x00',
b'77959-T47-A950\x00\x00',
b'77959-T50-G010\x00\x00',
b'77959-T50-G930\x00\x00',
b'77959-T60-A920\x00\x00',
b'77959-T61-A920\x00\x00',
b'77959-T50-G930\x00\x00',
b'77959-T65-A920\x00\x00',
b'77959-T50-G010\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36161-T20-A060\x00\x00',
@ -906,15 +919,16 @@ FW_VERSIONS = {
b'36161-T24-T070\x00\x00',
b'36161-T38-A060\x00\x00',
b'36161-T47-A050\x00\x00',
b'36161-T47-A060\x00\x00',
b'36161-T47-A070\x00\x00',
b'8S102-T47-AA20\x00\x00',
b'8S102-T20-AA10\x00\x00',
b'8S102-T43-J540\x00\x00',
b'8S102-T47-AA10\x00\x00',
b'8S102-T60-AA10\x00\x00',
b'8S102-T56-A060\x00\x00',
b'8S102-T47-AA20\x00\x00',
b'8S102-T50-EA10\x00\x00',
b'8S102-T56-A060\x00\x00',
b'8S102-T60-AA10\x00\x00',
b'8S102-T64-A040\x00\x00',
b'8S102-T43-J540\x00\x00',
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-T20-AB40\x00\x00',
@ -922,9 +936,9 @@ FW_VERSIONS = {
b'57114-T38-AA20\x00\x00',
b'57114-T43-JA30\x00\x00',
b'57114-T43-JB30\x00\x00',
b'57114-T50-JC20\x00\x00',
b'57114-T60-AA20\x00\x00',
b'57114-T61-AJ30\x00\x00',
b'57114-T50-JC20\x00\x00',
],
(Ecu.transmission, 0x18da1ef1, None): [
b'28101-65D-A020\x00\x00',

@ -73,7 +73,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.HONDA_CIVIC_2022 and all(msg not in fingerprint[CAN.pt] for msg in (0x191, 0x1A3)):
ret.transmissionType = TransmissionType.manual
# New Civics don't have 0x191, but do have 0x1A3
elif candidate == CAR.HONDA_CIVIC_2022 and 0x1A3 in fingerprint[CAN.pt]:
elif candidate in (CAR.HONDA_CIVIC_2022, CAR.HONDA_HRV_3G) and 0x1A3 in fingerprint[CAN.pt]:
ret.transmissionType = TransmissionType.cvt
# Certain Hondas have an extra steering sensor at the bottom of the steering rack,

@ -168,7 +168,7 @@ class CAR(Platforms):
[
HondaCarDocs("Honda Civic 2022-24", "All", video="https://youtu.be/ytiOT5lcp6Q"),
HondaCarDocs("Honda Civic Hatchback 2022-24", "All", video="https://youtu.be/ytiOT5lcp6Q"),
HondaCarDocs("Honda Civic Hatchback Hybrid 2023 (Europe only)", "All"),
HondaCarDocs("Honda Civic Hatchback Hybrid (Europe only) 2023", "All"),
# TODO: Confirm 2024
HondaCarDocs("Honda Civic Hatchback Hybrid 2025", "All"),
],

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.hyundai.values import CAR
@ -50,6 +51,8 @@ FW_VERSIONS = {
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00AE MDPS C 1.00 1.03 56310/G2300 4AEHC103',
b'\xf1\x00AE MDPS C 1.00 1.03 56310G2300\x00 4AEHC103',
b'\xf1\x00AE MDPS C 1.00 1.05 56310/G2500 4AEHC105',
b'\xf1\x00AE MDPS C 1.00 1.05 56310/G2501 4AEHC105',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2301 4AEHC107',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2501 4AEHC107',
@ -58,8 +61,9 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G7200 160418',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEH MFC AT EUR RHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2300 170703',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2400 180222',
],
},
CAR.HYUNDAI_IONIQ_PHEV_2019: {
@ -141,8 +145,8 @@ FW_VERSIONS = {
},
CAR.HYUNDAI_IONIQ_HEV_2022: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00AEhe SCC F-CUP 1.00 1.02 99110-G2100 ',
b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2600 ',
b'\xf1\x00AEhe SCC F-CUP 1.00 1.02 99110-G2100 ',
b'\xf1\x00AEhe SCC FHCUP 1.00 1.00 99110-G2600 ',
b'\xf1\x00AEhe SCC FHCUP 1.00 1.02 99110-G2100 ',
],
@ -151,8 +155,8 @@ FW_VERSIONS = {
b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.01 95740-G2600 190819',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2700 201027',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.01 95740-G2600 190819',
],
},
CAR.HYUNDAI_SONATA: {
@ -160,6 +164,7 @@ FW_VERSIONS = {
b'\xf1\x00DN8_ SCC F-CU- 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC F-CUP 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC F-CUP 1.00 1.02 99110-L1000 ',
b'\xf1\x00DN8_ SCC FHCU- 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.02 99110-L1000 ',
@ -279,8 +284,8 @@ FW_VERSIONS = {
b'\xf1\x00TM ESC \x04 101 \x08\x04 58910-S2GA0',
b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
b'\xf1\x00TM ESC \x04 103"\x07\x08 58910-S2GA0',
b'\xf1\x00TM ESC \x1e 102 \x08\x08 58910-S1DA0',
b'\xf1\x00TM ESC \x1b 102 \x08\x08 58910-S1DA0',
b'\xf1\x00TM ESC \x1e 102 \x08\x08 58910-S1DA0',
b'\xf1\x00TM ESC 103!\x030 58910-S1MA0',
],
(Ecu.eps, 0x7d4, None): [
@ -353,6 +358,7 @@ FW_VERSIONS = {
},
CAR.KIA_STINGER: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC FHCUP 1.00 1.02 96400-J5000 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.02 96400-J5100 ',
@ -368,6 +374,7 @@ FW_VERSIONS = {
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT KOR LHD 1.00 1.04 95740-J5000 180504',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504',
],
@ -384,6 +391,7 @@ FW_VERSIONS = {
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5320 4C2VL503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5380 4C2VR503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5520 4C4VL503',
b'\xf1\x00CK MDPS R 1.00 5.04 57700-J5320 4C2VL504',
b'\xf1\x00CK MDPS R 1.00 5.04 57700-J5520 4C4VL504',
],
(Ecu.fwdCamera, 0x7c4, None): [
@ -425,6 +433,7 @@ FW_VERSIONS = {
b'\xf1\x00LX ESC \x0b 104 \x10\x13 58910-S8330',
b'\xf1\x00LX ESC \x0b 104 \x10\x16 58910-S8360',
b'\xf1\x00ON ESC \x01 101\x19\t\x08 58910-S9360',
b'\xf1\x00ON ESC \x01 103$\x04\x08 58910-S9360',
b'\xf1\x00ON ESC \x0b 100\x18\x12\x18 58910-S9360',
b'\xf1\x00ON ESC \x0b 101\x19\t\x05 58910-S9320',
b'\xf1\x00ON ESC \x0b 101\x19\t\x08 58910-S9360',
@ -733,14 +742,14 @@ FW_VERSIONS = {
},
CAR.KIA_NIRO_EV_2ND_GEN: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SG__ RDR ----- 1.00 1.00 99110-AT200 ',
b'\xf1\x00SG2_ RDR ----- 1.00 1.01 99110-AT000 ',
b'\xf1\x00SG__ RDR ----- 1.00 1.00 99110-AT200 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SG2EMFC AT EUR LHD 1.00 1.00 99211-AT200 240315',
b'\xf1\x00SG2EMFC AT EUR LHD 1.01 1.09 99211-AT000 220801',
b'\xf1\x00SG2EMFC AT USA LHD 1.01 1.09 99211-AT000 220801',
b'\xf1\x00SG2EMFC AT USA LHD 1.00 1.00 99211-AT200 240401',
b'\xf1\x00SG2EMFC AT USA LHD 1.01 1.09 99211-AT000 220801',
],
},
CAR.KIA_NIRO_PHEV: {
@ -1015,6 +1024,7 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.00 99211-GI100 230915',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI100 240110',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.03 99211-GI010 220401',
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',
@ -1028,6 +1038,7 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI020 230719',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI100 230915',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI100 240110',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614',
@ -1049,9 +1060,9 @@ FW_VERSIONS = {
},
CAR.HYUNDAI_TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N9220 14K',
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N9260 14Y',
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.01 99211-N9100 14A',
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N9220 14K',
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',
@ -1153,6 +1164,7 @@ FW_VERSIONS = {
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.10 99210-P2000 210406',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.12 99210-P2000 230331',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.04 99110-P4000 ',
@ -1232,16 +1244,16 @@ FW_VERSIONS = {
b'\xf1\x00OSP LKA AT USA LHD 1.00 1.04 99211-J9200 904',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00OSP MDPS C 1.00 1.04 56310/J9291 4OPCC104',
b'\xf1\x00OSP MDPS C 1.00 1.04 56310/J9290 4OPCC104',
b'\xf1\x00OSP MDPS C 1.00 1.04 56310/J9291 4OPCC104',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00YB__ FCA ----- 1.00 1.01 99110-J9000 \x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00T01960BL T01E60A1 DOS2T16X4XE60NS4N\x90\xe6\xcb',
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X2XI00NS0\x8c`\xff\xe7',
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X4XI00NS0\x99L\xeeq',
b'\xf1\x00T01960BL T01E60A1 DOS2T16X4XE60NS4N\x90\xe6\xcb',
],
},
}

@ -233,7 +233,7 @@ class CarInterfaceBase(ABC):
tune.torque.useSteeringAngle = use_steering_angle
tune.torque.kp = 1.0
tune.torque.kf = 1.0
tune.torque.ki = 0.1
tune.torque.ki = 0.3
tune.torque.friction = params['FRICTION']
tune.torque.latAccelFactor = params['LAT_ACCEL_FACTOR']
tune.torque.latAccelOffset = 0.0

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.mazda.values import CAR
@ -13,8 +14,11 @@ FW_VERSIONS = {
b'PEW5-188K2-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PW67-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PW67-188K2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PW8F-188K2-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PW8G-188K2-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2C-188K2-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2D-188K2-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2D-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -23,7 +27,6 @@ FW_VERSIONS = {
b'PXFG-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PXGC-188K2-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH54-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PW8F-188K2-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -49,8 +52,8 @@ FW_VERSIONS = {
b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB2-21PS1-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYJ3-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYJ3-21PS1-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.MAZDA_CX5: {
@ -270,7 +273,7 @@ FW_VERSIONS = {
b'PXM4-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PXM6-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PXM7-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PXM7-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'PXM7-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
}

@ -1,4 +1,5 @@
# ruff: noqa: E501
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.nissan.values import CAR

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.rivian.values import CAR
@ -7,6 +8,7 @@ FW_VERSIONS = {
CAR.RIVIAN_R1_GEN1: {
(Ecu.eps, 0x733, None): [
b'R1TS_v3.4.1(51),3.4.1\x00',
b'R1TS_v4.4.1(63),4.4.1\x00',
],
},
}

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.subaru.values import CAR
@ -68,8 +69,8 @@ FW_VERSIONS = {
],
(Ecu.engine, 0x7e0, None): [
b'\xde"a0\x07',
b'\xe2"a0\x07',
b'\xde,\xa0@\x07',
b'\xe2"a0\x07',
b'\xe2"aq\x07',
b'\xe2,\xa0@\x07',
],
@ -498,8 +499,8 @@ FW_VERSIONS = {
b'\xe2"`0\x07',
b'\xe2"`p\x07',
b'\xe2"`q\x07',
b'\xe3,\xa0@\x07',
b'\xe2,\xa0p\x07',
b'\xe3,\xa0@\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xa5\xf6D@\x00',

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.tesla.values import CAR
@ -29,8 +30,8 @@ FW_VERSIONS = {
b'TeMYG4_DCS_Update_0.0.0 (13),Y4P002.27.1',
b'TeMYG4_DCS_Update_0.0.0 (9),Y4P002.25.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4003.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4P003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4003.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4P003.03.2',
b'TeMYG4_SingleECU_0.0.0 (28),Y4S002.23.0',
b'TeMYG4_SingleECU_0.0.0 (33),Y4S002.26',

@ -59,6 +59,7 @@ routes = [
CarTestRoute("e36b272d5679115f/00000369--a3e8499a85", FORD.FORD_F_150_MK14),
CarTestRoute("83a4e056c7072678|2023-11-13--16-51-33", FORD.FORD_MUSTANG_MACH_E_MK1),
CarTestRoute("37998aa0fade36ab/00000000--48f927c4f5", FORD.FORD_RANGER_MK2),
CarTestRoute("61a1b9e7a4eae0f6/00000000--79d85d1315", FORD.FORD_EXPEDITION_MK4),
#TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION),
CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.GMC_ACADIA),
@ -88,6 +89,7 @@ routes = [
CarTestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HONDA_HRV),
CarTestRoute("320098ff6c5e4730|2023-04-13--17-47-46", HONDA.HONDA_HRV_3G),
CarTestRoute("147613502316e718/00000001--dd141a3140", HONDA.HONDA_HRV_3G), # Brazilian model
CarTestRoute("1e4baee1aa2687a0/00000001--74c4cc0b23", HONDA.HONDA_HRV_3G), # Thailand model use ALT_GEAR
CarTestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX),
CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.HONDA_ACCORD), # 1.5T
CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.HONDA_ACCORD), # 2.0T

@ -1,6 +1,5 @@
from collections import defaultdict
import pytest
import re
from opendbc.car.car_helpers import interfaces
from opendbc.car.docs import get_all_car_docs
@ -61,7 +60,10 @@ class TestCarDocs:
def test_year_format(self, subtests):
for car in self.all_cars:
with subtests.test(car=car.name):
assert re.search(r"\d{4}-\d{4}", car.name) is None, f"Format years correctly: {car.name}"
if car.name == "comma body":
pytest.skip()
assert car.years and car.year_list, f"Format years correctly: {car.name}"
def test_harnesses(self, subtests):
for car in self.all_cars:

@ -24,6 +24,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"FORD_ESCAPE_MK4" = [nan, 1.5, nan]
"FORD_ESCAPE_MK4_5" = [nan, 1.5, nan]
"FORD_EXPLORER_MK6" = [nan, 1.5, nan]
"FORD_EXPEDITION_MK4" = [nan, 1.5, nan]
"FORD_F_150_MK14" = [nan, 1.5, nan]
"FORD_FOCUS_MK4" = [nan, 1.5, nan]
"FORD_MAVERICK_MK1" = [nan, 1.5, nan]

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.toyota.values import CAR
@ -152,6 +153,7 @@ FW_VERSIONS = {
b'8821F0601300 ',
b'8821F0601400 ',
b'8821F0601500 ',
b'8821F0601600 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0603400 ',
@ -201,6 +203,7 @@ FW_VERSIONS = {
b'8821F0601300 ',
b'8821F0601400 ',
b'8821F0601500 ',
b'8821F0601600 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0603400 ',
@ -478,6 +481,7 @@ FW_VERSIONS = {
b'\x01896637621000\x00\x00\x00\x00',
b'\x01896637623000\x00\x00\x00\x00',
b'\x01896637624000\x00\x00\x00\x00',
b'\x01896637625000\x00\x00\x00\x00',
b'\x01896637626000\x00\x00\x00\x00',
b'\x01896637639000\x00\x00\x00\x00',
b'\x01896637643000\x00\x00\x00\x00',
@ -542,6 +546,7 @@ FW_VERSIONS = {
b'8965B76012\x00\x00\x00\x00\x00\x00',
b'8965B76050\x00\x00\x00\x00\x00\x00',
b'8965B76091\x00\x00\x00\x00\x00\x00',
b'8965B76101\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F152602280\x00\x00\x00\x00\x00\x00',
@ -591,6 +596,7 @@ FW_VERSIONS = {
b'F152676293\x00\x00\x00\x00\x00\x00',
b'F152676303\x00\x00\x00\x00\x00\x00',
b'F152676304\x00\x00\x00\x00\x00\x00',
b'F152676311\x00\x00\x00\x00\x00\x00',
b'F152676371\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@ -629,6 +635,7 @@ FW_VERSIONS = {
b'\x01896630E43000\x00\x00\x00\x00',
b'\x01896630E43100\x00\x00\x00\x00',
b'\x01896630E43200\x00\x00\x00\x00',
b'\x01896630E43500\x00\x00\x00\x00',
b'\x01896630E44200\x00\x00\x00\x00',
b'\x01896630E44400\x00\x00\x00\x00',
b'\x01896630E45000\x00\x00\x00\x00',
@ -636,6 +643,7 @@ FW_VERSIONS = {
b'\x01896630E45200\x00\x00\x00\x00',
b'\x01896630E46000\x00\x00\x00\x00',
b'\x01896630E46200\x00\x00\x00\x00',
b'\x01896630E48200\x00\x00\x00\x00',
b'\x01896630E74000\x00\x00\x00\x00',
b'\x01896630E75000\x00\x00\x00\x00',
b'\x01896630E76000\x00\x00\x00\x00',
@ -764,6 +772,7 @@ FW_VERSIONS = {
b'\x018966353Q2300\x00\x00\x00\x00',
b'\x018966353Q4000\x00\x00\x00\x00',
b'\x018966353R1100\x00\x00\x00\x00',
b'\x018966353R5000\x00\x00\x00\x00',
b'\x018966353R7000\x00\x00\x00\x00',
b'\x018966353R7100\x00\x00\x00\x00',
b'\x018966353R8000\x00\x00\x00\x00',
@ -822,6 +831,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x7a1, None): [
b'8965B53450\x00\x00\x00\x00\x00\x00',
b'8965B53800\x00\x00\x00\x00\x00\x00',
b'8965B53801\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
@ -856,6 +866,7 @@ FW_VERSIONS = {
b'\x02896634782000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896634784000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347A0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347A1000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347A5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347A8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347B0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
@ -959,6 +970,7 @@ FW_VERSIONS = {
b'\x02342Q2000\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342Q2100\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342Q2200\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342Q3000\x00\x00\x00\x00\x00\x00\x00\x0054214000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342Q4000\x00\x00\x00\x00\x00\x00\x00\x0054215000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
@ -1076,6 +1088,7 @@ FW_VERSIONS = {
b'\x018966342W5000\x00\x00\x00\x00',
b'\x018966342W7000\x00\x00\x00\x00',
b'\x018966342W8000\x00\x00\x00\x00',
b'\x018966342W9000\x00\x00\x00\x00',
b'\x018966342X5000\x00\x00\x00\x00',
b'\x018966342X6000\x00\x00\x00\x00',
b'\x01896634A05000\x00\x00\x00\x00',
@ -1225,6 +1238,7 @@ FW_VERSIONS = {
b'\x01896634A61000\x00\x00\x00\x00',
b'\x01896634A88100\x00\x00\x00\x00',
b'\x01896634A89100\x00\x00\x00\x00',
b'\x01896634AD7000\x00\x00\x00\x00',
b'\x01896634AE1001\x00\x00\x00\x00',
b'\x01896634AF0000\x00\x00\x00\x00',
b'\x01896634AJ2000\x00\x00\x00\x00',
@ -1232,7 +1246,9 @@ FW_VERSIONS = {
b'\x01896634AL5000\x00\x00\x00\x00',
b'\x01896634AL6000\x00\x00\x00\x00',
b'\x01896634AL8000\x00\x00\x00\x00',
b'\x01896634AS8001\x00\x00\x00\x00',
b'\x01896634AS9000\x00\x00\x00\x00',
b'\x01896634AT7000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R03100\x00\x00\x00\x00',
@ -1326,6 +1342,8 @@ FW_VERSIONS = {
CAR.LEXUS_ES_TSS2: {
(Ecu.engine, 0x700, None): [
b'\x018966306U6000\x00\x00\x00\x00',
b'\x018966306V5000\x00\x00\x00\x00',
b'\x018966306W6000\x00\x00\x00\x00',
b'\x018966333T5000\x00\x00\x00\x00',
b'\x018966333T5100\x00\x00\x00\x00',
b'\x018966333X6000\x00\x00\x00\x00',
@ -1346,6 +1364,7 @@ FW_VERSIONS = {
b'\x01F152606340\x00\x00\x00\x00\x00\x00',
b'\x01F152606461\x00\x00\x00\x00\x00\x00',
b'\x01F15260646200\x00\x00\x00\x00',
b'\x01F152633A71\x00\x00\x00\x00\x00\x00',
b'F152633423\x00\x00\x00\x00\x00\x00',
b'F152633680\x00\x00\x00\x00\x00\x00',
b'F152633681\x00\x00\x00\x00\x00\x00',
@ -1487,6 +1506,7 @@ FW_VERSIONS = {
b'\x018966378B4100\x00\x00\x00\x00',
b'\x018966378G2000\x00\x00\x00\x00',
b'\x018966378G3000\x00\x00\x00\x00',
b'\x018966378G4000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x0237881000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1516,13 +1536,16 @@ FW_VERSIONS = {
},
CAR.LEXUS_LC_TSS2: {
(Ecu.engine, 0x7e0, None): [
b'\x0131126000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0131130000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152611390\x00\x00\x00\x00\x00\x00',
b'F152611430\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B11091\x00\x00\x00\x00\x00\x00',
b'8965B11120\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201400\x00\x00\x00\x00',
@ -1539,20 +1562,24 @@ FW_VERSIONS = {
b'\x01896632478200\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x0232480000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0232484000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152624150\x00\x00\x00\x00\x00\x00',
b'F152624171\x00\x00\x00\x00\x00\x00',
b'F152624221\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'881512404100\x00\x00\x00\x00',
b'881512405100\x00\x00\x00\x00',
b'881512407000\x00\x00\x00\x00',
b'881512409100\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B24081\x00\x00\x00\x00\x00\x00',
b'8965B24240\x00\x00\x00\x00\x00\x00',
b'8965B24260\x00\x00\x00\x00\x00\x00',
b'8965B24320\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@ -1734,6 +1761,7 @@ FW_VERSIONS = {
b'\x028966347C7000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347C8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x038966347C0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4710101\x00\x00\x00\x00',
b'\x038966347C0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4710102\x00\x00\x00\x00',
b'\x038966347C1000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4710101\x00\x00\x00\x00',
b'\x038966347C5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4707101\x00\x00\x00\x00',
b'\x038966347C5100\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4707101\x00\x00\x00\x00',
@ -1771,8 +1799,10 @@ FW_VERSIONS = {
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F6201400\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F6201400\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
],
},

@ -6,7 +6,7 @@ from enum import Enum, IntFlag
from opendbc.car import Bus, CarSpecs, PlatformConfig, Platforms, AngleSteeringLimits
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness
from opendbc.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness, SupportType
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
@ -76,6 +76,11 @@ class ToyotaFlags(IntFlag):
RAISED_ACCEL_LIMIT = 1024
SECOC = 2048
def dbc_dict(pt, radar):
return {Bus.pt: pt, Bus.radar: radar}
class Footnote(Enum):
CAMRY = CarFootnote(
"openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.",
@ -87,8 +92,12 @@ class ToyotaCarDocs(CarDocs):
package: str = "All"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.toyota_a]))
def dbc_dict(pt, radar):
return {Bus.pt: pt, Bus.radar: radar}
@dataclass
class ToyotaCommunityCarDocs(ToyotaCarDocs):
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "#community"
@dataclass
class ToyotaTSS2PlatformConfig(PlatformConfig):
@ -100,14 +109,12 @@ class ToyotaTSS2PlatformConfig(PlatformConfig):
if self.flags & ToyotaFlags.RADAR_ACC:
self.dbc_dict = {Bus.pt: 'toyota_nodsu_pt_generated'}
@dataclass
class ToyotaSecOCPlatformConfig(PlatformConfig):
dbc_dict: dict = field(default_factory=lambda: dbc_dict('toyota_secoc_pt_generated', 'toyota_tss2_adas'))
def init(self):
# don't expose car docs until SecOC cars can be suppressed from the comma website
self.car_docs = []
self.flags |= ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC
if self.flags & ToyotaFlags.RADAR_ACC:
@ -278,11 +285,11 @@ class CAR(Platforms):
flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL,
)
TOYOTA_RAV4_PRIME = ToyotaSecOCPlatformConfig(
[ToyotaCarDocs("Toyota RAV4 Prime 2021-23", min_enable_speed=MIN_ACC_SPEED)],
[ToyotaCommunityCarDocs("Toyota RAV4 Prime 2021-23", min_enable_speed=MIN_ACC_SPEED)],
CarSpecs(mass=4372. * CV.LB_TO_KG, wheelbase=2.68, steerRatio=16.88, tireStiffnessFactor=0.5533),
)
TOYOTA_YARIS = ToyotaSecOCPlatformConfig(
[ToyotaCarDocs("Toyota Yaris 2023 (Non-US only)", min_enable_speed=MIN_ACC_SPEED)],
[ToyotaCommunityCarDocs("Toyota Yaris (Non-US only) 2023", min_enable_speed=MIN_ACC_SPEED)],
CarSpecs(mass=1170, wheelbase=2.55, steerRatio=14.80, tireStiffnessFactor=0.5533),
flags=ToyotaFlags.RADAR_ACC,
)
@ -297,7 +304,7 @@ class CAR(Platforms):
flags=ToyotaFlags.NO_STOP_TIMER,
)
TOYOTA_SIENNA_4TH_GEN = ToyotaSecOCPlatformConfig(
[ToyotaCarDocs("Toyota Sienna 2021-23", min_enable_speed=MIN_ACC_SPEED)],
[ToyotaCommunityCarDocs("Toyota Sienna 2021-23", min_enable_speed=MIN_ACC_SPEED)],
CarSpecs(mass=4625. * CV.LB_TO_KG, wheelbase=3.06, steerRatio=17.8, tireStiffnessFactor=0.444),
)
@ -317,7 +324,7 @@ class CAR(Platforms):
)
LEXUS_ES_TSS2 = ToyotaTSS2PlatformConfig(
[
ToyotaCarDocs("Lexus ES 2019-24"),
ToyotaCarDocs("Lexus ES 2019-25"),
ToyotaCarDocs("Lexus ES Hybrid 2019-25", video="https://youtu.be/BZ29osRVJeg?t=12"),
],
LEXUS_ES.specs,
@ -329,7 +336,7 @@ class CAR(Platforms):
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
LEXUS_IS_TSS2 = ToyotaTSS2PlatformConfig(
[ToyotaCarDocs("Lexus IS 2022-23")],
[ToyotaCarDocs("Lexus IS 2022-24")],
LEXUS_IS.specs,
)
LEXUS_NX = PlatformConfig(
@ -348,7 +355,7 @@ class CAR(Platforms):
LEXUS_NX.specs,
)
LEXUS_LC_TSS2 = ToyotaTSS2PlatformConfig(
[ToyotaCarDocs("Lexus LC 2024")],
[ToyotaCarDocs("Lexus LC 2024-25")],
CarSpecs(mass=4500. * CV.LB_TO_KG, wheelbase=2.87, steerRatio=13.0, tireStiffnessFactor=0.444),
)
LEXUS_RC = PlatformConfig(
@ -600,7 +607,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
)
STEER_THRESHOLD = 100
# These cars have non-standard EPS torque scale factors. All others are 73

@ -167,14 +167,14 @@ class CarState(CarStateBase):
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
ret.yawRate = pt_cp.vl["Bremse_5"]["BR5_Giergeschw"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["BR5_Vorzeichen"])] * CV.DEG_TO_RAD
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
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
ret.gasPressed = ret.gas > 0
ret.brake = pt_cp.vl["Bremse_5"]["Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
ret.brake = pt_cp.vl["Bremse_5"]["BR5_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
ret.brakePressed = bool(pt_cp.vl["Motor_2"]["Bremslichtschalter"])
ret.parkingBrake = bool(pt_cp.vl["Kombi_1"]["Bremsinfo"])

@ -1,3 +1,4 @@
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from opendbc.car.structs import CarParams
from opendbc.car.volkswagen.values import CAR
@ -68,9 +69,11 @@ FW_VERSIONS = {
b'\xf1\x8703H906026BC\xf1\x892664',
b'\xf1\x8703H906026F \xf1\x896696',
b'\xf1\x8703H906026F \xf1\x899970',
b'\xf1\x8703H906026F \xf1\x899973',
b'\xf1\x8703H906026J \xf1\x896026',
b'\xf1\x8703H906026J \xf1\x899970',
b'\xf1\x8703H906026J \xf1\x899971',
b'\xf1\x8703H906026J \xf1\x899972',
b'\xf1\x8703H906026S \xf1\x896693',
b'\xf1\x8703H906026S \xf1\x899970',
b'\xf1\x8703H906026S \xf1\x899972',
@ -131,12 +134,14 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906056BP\xf1\x894729',
b'\xf1\x8704L906056EK\xf1\x896391',
b'\xf1\x8704L906056SH\xf1\x899973',
b'\xf1\x8705L906023BC\xf1\x892688',
b'\xf1\x8705L906023MH\xf1\x892588',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x873Q0959655AL\xf1\x890505\xf1\x82\x0e1411001413001203151311031100',
b'\xf1\x873Q0959655BG\xf1\x890703\xf1\x82\x0e16120016130012051G1313052900',
b'\xf1\x875QF959655AG\xf1\x890753\xf1\x82\x13151300151500111111000502----1111120749',
b'\xf1\x875QF959655AS\xf1\x890755\xf1\x82\x1315140015150011111100050200--1311120749',
],
(Ecu.eps, 0x712, None): [
@ -149,6 +154,7 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572J \xf1\x890156',
b'\xf1\x872Q0907572M \xf1\x890233',
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
CAR.VOLKSWAGEN_GOLF_MK7: {
@ -157,6 +163,7 @@ FW_VERSIONS = {
b'\xf1\x8704E906016AD\xf1\x895758',
b'\xf1\x8704E906016CE\xf1\x899096',
b'\xf1\x8704E906016CH\xf1\x899226',
b'\xf1\x8704E906016DF\xf1\x896188',
b'\xf1\x8704E906016N \xf1\x899105',
b'\xf1\x8704E906023AG\xf1\x891726',
b'\xf1\x8704E906023BN\xf1\x894518',
@ -173,6 +180,7 @@ FW_VERSIONS = {
b'\xf1\x8704L906026BN\xf1\x891197',
b'\xf1\x8704L906026BP\xf1\x897608',
b'\xf1\x8704L906026NF\xf1\x899528',
b'\xf1\x8704L906027AA\xf1\x899525',
b'\xf1\x8704L906056CL\xf1\x893823',
b'\xf1\x8704L906056CR\xf1\x895813',
b'\xf1\x8704L906056HE\xf1\x893758',
@ -186,6 +194,7 @@ FW_VERSIONS = {
b'\xf1\x870EA906016Q \xf1\x895993',
b'\xf1\x870EA906016S \xf1\x897207',
b'\xf1\x875G0906259 \xf1\x890007',
b'\xf1\x875G0906259C \xf1\x890002',
b'\xf1\x875G0906259D \xf1\x890002',
b'\xf1\x875G0906259J \xf1\x890002',
b'\xf1\x875G0906259L \xf1\x890002',
@ -218,6 +227,7 @@ FW_VERSIONS = {
b'\xf1\x870CW300044S \xf1\x894530',
b'\xf1\x870CW300044T \xf1\x895245',
b'\xf1\x870CW300045 \xf1\x894531',
b'\xf1\x870CW300046A \xf1\x895113',
b'\xf1\x870CW300047D \xf1\x895261',
b'\xf1\x870CW300047E \xf1\x895261',
b'\xf1\x870CW300048J \xf1\x890611',
@ -240,6 +250,7 @@ FW_VERSIONS = {
b'\xf1\x870D9300041H \xf1\x895220',
b'\xf1\x870D9300041N \xf1\x894512',
b'\xf1\x870D9300041P \xf1\x894507',
b'\xf1\x870D9300043F \xf1\x895204',
b'\xf1\x870DD300045K \xf1\x891120',
b'\xf1\x870DD300046F \xf1\x891601',
b'\xf1\x870GC300012A \xf1\x891401',
@ -249,6 +260,7 @@ FW_VERSIONS = {
b'\xf1\x870GC300014B \xf1\x892401',
b'\xf1\x870GC300014B \xf1\x892403',
b'\xf1\x870GC300014B \xf1\x892405',
b'\xf1\x870GC300014E \xf1\x892407',
b'\xf1\x870GC300020G \xf1\x892401',
b'\xf1\x870GC300020G \xf1\x892403',
b'\xf1\x870GC300020G \xf1\x892404',
@ -264,6 +276,8 @@ FW_VERSIONS = {
b'\xf1\x875Q0959655AR\xf1\x890317\xf1\x82\x13141500111233003142114A2131219333313100',
b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\x1314160011123300314211012230229333423100',
b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\x1314160011123300314211012230229333463100',
b'\xf1\x875Q0959655BJ\xf1\x890336\xf1\x82\x13141300111233003142115A1932199333463100',
b'\xf1\x875Q0959655BJ\xf1\x890336\xf1\x82\x13141600111233003142115A2231229333423100',
b'\xf1\x875Q0959655BJ\xf1\x890339\xf1\x82\x13141600111233003142115A2232229333463100',
b'\xf1\x875Q0959655BS\xf1\x890403\xf1\x82\x1314160011123300314240012250229333463100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x13141600111233003142404A2251229333463100',
@ -272,6 +286,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x13141600111233003142405A2252229333463100',
b'\xf1\x875Q0959655C \xf1\x890361\xf1\x82\x111413001112120004110415121610169112',
b'\xf1\x875Q0959655CA\xf1\x890403\xf1\x82\x1314160011123300314240012250229333463100',
b'\xf1\x875Q0959655CA\xf1\x890403\xf1\x82\x13141600111233003142405A2251229333463100',
b'\xf1\x875Q0959655D \xf1\x890388\xf1\x82\x111413001113120006110417121A101A9113',
b'\xf1\x875Q0959655J \xf1\x890825\xf1\x82\x13271112111312--071104171825102591131211',
b'\xf1\x875Q0959655J \xf1\x890830\xf1\x82\x13271112111312--071104171825102591131211',
@ -295,6 +310,7 @@ FW_VERSIONS = {
b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A01613A1',
b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A0J712A1',
b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571A0J714A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A01A16A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A0JA15A1',
b'\xf1\x873Q0909144M \xf1\x895082\xf1\x82\x0571A01A18A1',
b'\xf1\x873Q0909144M \xf1\x895082\xf1\x82\x0571A02A16A1',
@ -321,6 +337,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\x0521A07B04A1',
b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\x0521A20B03A1',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A2000400',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A2000600',
b'\xf1\x875QD909144B \xf1\x891072\xf1\x82\x0521A00507A1',
b'\xf1\x875QM909144A \xf1\x891072\xf1\x82\x0521A20B03A1',
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A00442A1',
@ -364,6 +381,7 @@ FW_VERSIONS = {
b'\xf1\x8704E906024AF\xf1\x899970',
b'\xf1\x8704E906024AK\xf1\x899937',
b'\xf1\x8704E906024AS\xf1\x899912',
b'\xf1\x8704E906024AS\xf1\x899970',
b'\xf1\x8704E906024B \xf1\x895594',
b'\xf1\x8704E906024BC\xf1\x899971',
b'\xf1\x8704E906024BG\xf1\x891057',
@ -375,6 +393,7 @@ FW_VERSIONS = {
b'\xf1\x8705E906013BN\xf1\x893711',
b'\xf1\x8705E906013DB\xf1\x893361',
b'\xf1\x875G0906259T \xf1\x890003',
b'\xf1\x875G09C0BB02 \xf1\x890003',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158BQ\xf1\x893545',
@ -386,6 +405,7 @@ FW_VERSIONS = {
b'\xf1\x8709S927158R \xf1\x893552',
b'\xf1\x8709S927158R \xf1\x893587',
b'\xf1\x870GC300020N \xf1\x892803',
b'\xf1\x870GC300020N \xf1\x892804',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655AG\xf1\x890336\xf1\x82\x1313171231313500314611011530159333463100',
@ -609,6 +629,7 @@ FW_VERSIONS = {
b'\xf1\x8704E906027NB\xf1\x899504',
b'\xf1\x8704L906026EJ\xf1\x893661',
b'\xf1\x8704L906026EJ\xf1\x893916',
b'\xf1\x8704L906026KR\xf1\x893919',
b'\xf1\x8704L906027G \xf1\x899893',
b'\xf1\x8705E906018BS\xf1\x890914',
b'\xf1\x875N0906259 \xf1\x890002',
@ -624,6 +645,7 @@ FW_VERSIONS = {
b'\xf1\x8783A907115K \xf1\x890001',
b'\xf1\x8783A907115K \xf1\x890002',
b'\xf1\x8783A907115Q \xf1\x890001',
b'\xf1\x8783A907115Q \xf1\x890002',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158DS\xf1\x893699',
@ -641,6 +663,7 @@ FW_VERSIONS = {
b'\xf1\x870DL300011N \xf1\x892012',
b'\xf1\x870DL300011N \xf1\x892014',
b'\xf1\x870DL300012M \xf1\x892107',
b'\xf1\x870DL300012N \xf1\x892110',
b'\xf1\x870DL300012P \xf1\x892103',
b'\xf1\x870DL300013A \xf1\x893005',
b'\xf1\x870DL300013G \xf1\x892119',
@ -656,12 +679,14 @@ FW_VERSIONS = {
b'\xf1\x875Q0959655BJ\xf1\x890336\xf1\x82\x1311140031333300314232583632369333423100',
b'\xf1\x875Q0959655BJ\xf1\x890336\xf1\x82\x1312110031333300314232583732379333423100',
b'\xf1\x875Q0959655BJ\xf1\x890339\xf1\x82\x1331310031333334313132013730379333423100',
b'\xf1\x875Q0959655BK\xf1\x890339\xf1\x82\x1331310031333334313132573732379333423100',
b'\xf1\x875Q0959655BM\xf1\x890403\xf1\x82\x1316143231313500314641011750179333423100',
b'\xf1\x875Q0959655BS\xf1\x890403\xf1\x82\x1312110031333300314240013750379333423100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1312110031333300314240583752379333423100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140013750379333423100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140573752379333423100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333336313140013950399333423100',
b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333336313140573952399333423100',
b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1316143231313500314647021750179333613100',
b'\xf1\x875Q0959655CD\xf1\x890421\xf1\x82\x13123112313333003145406F6154619333613100',
b'\xf1\x875Q0959655CG\xf1\x890421\xf1\x82\x1331310031333300314240024050409333613100',
@ -670,6 +695,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820529A6060603',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527A6050705',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527A6070705',
b'\xf1\x875Q0909144AA\xf1\x891081\xf1\x82\x0521A60803A1',
b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\x0521A60604A1',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6000600',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6017A00',
@ -683,8 +709,10 @@ FW_VERSIONS = {
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60804A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60804A1',
b'\xf1\x875QV907144F \xf1\x891122\xf1\x82\x0001A6CA01]V',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907567B \xf1\x890534',
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572AB\xf1\x890397',
b'\xf1\x872Q0907572J \xf1\x890156',
@ -807,6 +835,7 @@ FW_VERSIONS = {
b'\xf1\x878V0906264B \xf1\x890003',
b'\xf1\x878V0907115B \xf1\x890007',
b'\xf1\x878V0907404A \xf1\x890005',
b'\xf1\x878V0907404G \xf1\x890004',
b'\xf1\x878V0907404G \xf1\x890005',
],
(Ecu.transmission, 0x7e1, None): [
@ -896,12 +925,16 @@ FW_VERSIONS = {
b'\xf1\x8783A906259C \xf1\x890002',
b'\xf1\x8783A906259D \xf1\x890001',
b'\xf1\x8783A906259F \xf1\x890001',
b'\xf1\x8783A906259H \xf1\x890002',
b'\xf1\x8783A907115P \xf1\x890002',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158CN\xf1\x893608',
b'\xf1\x8709G927158FL\xf1\x893758',
b'\xf1\x8709G927158GG\xf1\x893825',
b'\xf1\x8709G927158GP\xf1\x893937',
b'\xf1\x8709G927158HC\xf1\x894070',
b'\xf1\x8709G927158HG\xf1\x894168',
b'\xf1\x870GC300045D \xf1\x892802',
b'\xf1\x870GC300046F \xf1\x892701',
],
@ -917,10 +950,12 @@ FW_VERSIONS = {
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567G6000800',
b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\x0571G60533A1',
b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\x0571G60733A1',
b'\xf1\x875QS907145E \xf1\x891122\xf1\x82\x0001G67801]V',
b'\xf1\x875TA907145D \xf1\x891051\xf1\x82\x001PG60A1P7N',
b'\xf1\x875TA907145F \xf1\x891063\xf1\x82\x002VG60A2VOM',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907567C \xf1\x890541',
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572R \xf1\x890372',
b'\xf1\x872Q0907572T \xf1\x890383',

@ -324,7 +324,7 @@ class CAR(Platforms):
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
)
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig(
[VWCarDocs("Volkswagen Taos 2022-23")],
[VWCarDocs("Volkswagen Taos 2022-24")],
VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
chassis_codes={"B2"},
wmis={WMI.VOLKSWAGEN_MEXICO_SUV, WMI.VOLKSWAGEN_ARGENTINA},
@ -337,7 +337,7 @@ class CAR(Platforms):
)
VOLKSWAGEN_TIGUAN_MK2 = VolkswagenMQBPlatformConfig(
[
VWCarDocs("Volkswagen Tiguan 2018-23"),
VWCarDocs("Volkswagen Tiguan 2018-24"),
VWCarDocs("Volkswagen Tiguan eHybrid 2021-23"),
],
VolkswagenCarSpecs(mass=1715, wheelbase=2.74),
@ -383,7 +383,7 @@ class CAR(Platforms):
wmis={WMI.AUDI_GERMANY_CAR},
)
AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig(
[VWCarDocs("Audi Q3 2019-23")],
[VWCarDocs("Audi Q3 2019-24")],
VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
chassis_codes={"8U", "F3", "FS"},
wmis={WMI.AUDI_EUROPE_MPV, WMI.AUDI_GERMANY_CAR},

@ -50,7 +50,7 @@ CM_ SG_ 479 CONTROL_ON "Set to 5 when car is being controlled";
CM_ SG_ 479 AEB_STATUS "set for the duration of AEB event";
CM_ SG_ 479 AEB_BRAKING "set when braking is commanded during AEB event";
CM_ SG_ 479 AEB_PREPARE "set 1s before AEB";
CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnngs etc...";
CM_ SG_ 829 BEEP "beeps are pleasant, chimes are for warnings etc...";
CM_ SG_ 829 CAM_TEMP_HIGH "Some Driver Assist Systems Cannot Operate: Camera Temperature Too High";
CM_ SG_ 829 CAMERA_OVERHEAT "Lane Keeping Assist Cannot Operate: Camera Too Hot";

@ -46,12 +46,15 @@ BO_ 160 WHEEL_SPEEDS: 24 XXX
SG_ WHL_SpdRLVal : 96|14@1+ (0.03125,0) [0|0] "km^h" XXX
SG_ WHL_SpdRRVal : 112|14@1+ (0.03125,0) [0|0] "km^h" XXX
BO_ 203 LFA_ALT: 24 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ LKAS_ANGLE_ACTIVE : 29|2@0+ (1,0) [0|1] "" XXX
SG_ LKAS_ANGLE_CMD : 32|14@1- (0.1,0) [0|511] "" XXX
SG_ LKAS_ANGLE_MAX_TORQUE : 55|8@0+ (1,0) [0|255] "" XXX
BO_ 203 ADAS_CMD_35_10ms: 24 CGW_CCU
SG_ CHECKSUM : 0|16@1+ (1,0) [0|0] "0" FCU_C,GW_RGW,MDPS,SFA
SG_ COUNTER : 16|8@1+ (1,0) [0|0] "0" FCU_C,GW_RGW,MDPS,SFA
SG_ ADAS_ActvACISta : 24|4@1+ (1,0) [0|0] "" GW_RGW,MDPS,SFA
SG_ ADAS_ActvACILvl2Sta : 28|4@1+ (1,0) [0|0] "" ESC,FCU_C,GW_RGW,MDPS,SFA,VPC_C
SG_ ADAS_StrAnglReqVal : 32|14@1- (0.1,0) [0|119.9] "Deg" GW_RGW,MDPS,SFA
SG_ ADAS_ACIAnglTqRedcGainVal : 48|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
SG_ FCA_ESA_ActvSta : 56|2@1+ (1,0) [0|0] "" GW_RGW,MDPS,SFA
SG_ FCA_ESA_TqBstGainVal : 64|8@1+ (0.004,0) [0|0] "" GW_RGW,MDPS,SFA
BO_ 234 MDPS: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
@ -92,10 +95,35 @@ BO_ 272 LKAS_ALT: 32 XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ LKAS_ANGLE_ACTIVE : 77|2@0+ (1,0) [0|3] "" XXX
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ LKAS_ANGLE_CMD : 82|14@1- (0.1,0) [0|511] "" XXX
SG_ LKAS_ANGLE_MAX_TORQUE : 96|8@1+ (1,0) [0|255] "" XXX
SG_ ADAS_StrAnglReqVal : 82|14@1- (0.1,0) [0|176.7] "Deg" GW_RGW,MDPS,SFA
SG_ ADAS_ACIAnglTqRedcGainVal : 96|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
BO_ 282 FR_CMR_01_10ms: 16 FR_CMR
SG_ FR_CMR_Crc1Val : 0|16@1+ (1,0) [0|0] "" Dummy,IBU_HS,vBDM
SG_ FR_CMR_AlvCnt1Val : 16|8@1+ (1,0) [0|0] "" CLU,IBU_HS,vBDM
SG_ HBA_SysOptSta : 24|2@1+ (1,0) [0|3] "" CLU,IBU_HS,vBDM
SG_ HBA_SysSta : 26|3@1+ (1,0) [0|7] "" CLU,IBU_HS,vBDM
SG_ HBA_IndLmpReq : 29|2@1+ (1,0) [0|3] "" CLU,vBDM
SG_ iHBAref_VehLftSta : 31|2@1+ (1,0) [0|3] "" IBU_HS,ICU,vBDM
SG_ iHBAref_VehCtrSta : 33|2@1+ (1,0) [0|3] "" IBU_HS,ICU,vBDM
SG_ iHBAref_VehRtSta : 35|2@1+ (1,0) [0|3] "" IBU_HS,ICU,vBDM
SG_ iHBAref_ILLAmbtSta : 37|2@1+ (1,0) [0|3] "" IBU_HS,ICU,vBDM
SG_ FCA_Equip_MFC : 39|3@1+ (1,0) [0|0] "" ADAS_DRV,RR_C_RDR,vBDM
SG_ HBA_OptUsmSta : 42|2@1+ (1,0) [0|3] "" CLU,H_U_MM
SG_ FCAref_FusSta : 45|3@1+ (1,0) [0|0] "" vBDM
SG_ DAW_LVDA_PUDis : 48|2@1+ (1,0) [0|0] "" CLU,vBDM
SG_ DAW_LVDA_OptUsmSta : 50|2@1+ (1,0) [0|3] "" CLU,H_U_MM,vBDM
SG_ DAW_OptUsmSta : 52|3@1+ (1,0) [0|0] "" CLU,H_U_MM
SG_ DAW_SysSta : 55|4@1+ (1,0) [0|0] "" CLU
SG_ DAW_WrnMsgSta : 59|3@1+ (1,0) [0|0] "" CLU
SG_ DAW_TimeRstReq : 62|2@1+ (1,0) [0|0] "" CLU
SG_ DAW_SnstvtyModRetVal : 64|3@1+ (1,0) [0|0] "" CLU,H_U_MMz
SG_ FR_CMR_SCCEquipSta : 85|2@1+ (1,0) [0|3] "" CGW
SG_ FR_CMR_ReqADASMapMsgVal : 96|16@1+ (1,0) [0|65535] "" CGW
SG_ FR_CMR_SwVer1Val : 112|4@1+ (1,0) [0|15] "" CGW
SG_ FR_CMR_SwVer2Val : 120|8@1+ (1,0) [0|255] "" CGW
BO_ 293 STEERING_SENSORS: 16 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
@ -343,28 +371,23 @@ BO_ 426 CRUISE_BUTTONS_ALT: 16 XXX
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
BO_ 437 CCNC_0x1B5: 32 CCNC
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ LEFT : 24|2@1+ (1,0) [0|3] "" XXX
SG_ LEFT_LDW : 27|1@0+ (1,0) [0|1] "" XXX
SG_ LEFT_1 : 29|9@1+ (1,0) [0|511] "" XXX
SG_ LEFT_2 : 38|5@1+ (1,0) [0|31] "" XXX
SG_ LEFT_3 : 43|10@1- (1,0) [0|1023] "" XXX
SG_ LEFT_4 : 64|16@1- (1,0) [0|65535] "" XXX
SG_ LEFT_5 : 80|16@1- (1,0) [0|65535] "" XXX
SG_ RIGHT : 96|2@1+ (1,0) [0|3] "" XXX
SG_ RIGHT_LDW : 99|1@0+ (1,0) [0|1] "" XXX
SG_ RIGHT_1 : 101|9@1+ (1,0) [0|511] "" XXX
SG_ RIGHT_2 : 110|5@1+ (1,0) [0|31] "" XXX
SG_ RIGHT_3 : 115|10@1- (1,0) [0|1023] "" XXX
SG_ RIGHT_4 : 128|16@1- (1,0) [0|65535] "" XXX
SG_ RIGHT_5 : 144|16@1- (1,0) [0|65535] "" XXX
SG_ LEAD : 192|2@1+ (1,0) [0|3] "" XXX
SG_ LEAD_1 : 194|6@1+ (1,0) [0|63] "" XXX
SG_ LEAD_2 : 200|11@1- (1,0) [0|4095] "" XXX
SG_ LEAD_3 : 211|1@0+ (1,0) [0|1] "" XXX
SG_ LEAD_DISTANCE : 213|11@1+ (0.1,0) [0|204.7] "m" XXX
BO_ 437 FR_CMR_03_50ms: 32 FR_CMR
SG_ FR_CMR_Crc3Val : 0|16@1+ (1,0) [0|65535] "" RR_C_RDR,CGW
SG_ FR_CMR_AlvCnt3Val : 16|8@1+ (1,0) [0|255] "" RR_C_RDR,CGW
SG_ Info_LftLnQualSta : 24|3@1+ (1,0) [0|7] "" RR_C_RDR,CGW
SG_ Info_LftLnDptSta : 27|2@1+ (1,0) [0|3] "" RR_C_RDR,CGW
SG_ Info_LftLnPosVal : 29|14@1- (0.0039625,0) [-32.4608|32.4568375] "m" RR_C_RDR,CGW
SG_ Info_LftLnHdingAnglVal : 43|10@1- (0.000976563,0) [-0.500000256|0.499023693] "rad" RR_C_RDR,CGW
SG_ Info_LftLnCvtrVal : 64|16@1- (1e-06,0) [-0.032768|0.032767] "1/m" CGW
SG_ Info_LftLnCrvtrDrvtvVal : 80|16@1- (4e-09,0) [-0.000131072|0.000131068] "1/m2" CGW
SG_ Info_RtLnQualSta : 96|3@1+ (1,0) [0|7] "" RR_C_RDR,CGW
SG_ Info_RtLnDptSta : 99|2@1+ (1,0) [0|3] "" RR_C_RDR,CGW
SG_ Info_RtLnPosVal : 101|14@1- (0.0039625,0) [-32.4608|32.4568375] "m" RR_C_RDR,CGW
SG_ Info_RtLnHdingAnglVal : 115|10@1- (0.000976563,0) [-0.500000256|0.499023693] "rad" RR_C_RDR,CGW
SG_ Info_RtLnCvtrVal : 128|16@1- (1,0) [0|65535] "" CGW
SG_ Info_RtLnCrvtrDrvtvVal : 144|16@1- (1,0) [0|65535] "" CGW
SG_ ID_CIPV : 192|7@1+ (1,0) [0|127] "" Dummy
SG_ Longitudinal_Distance : 212|12@1+ (0.05,0) [0|204.75] "m" Dummy
BO_ 442 BLINDSPOTS_REAR_CORNERS: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
@ -449,18 +472,36 @@ BO_ 490 ADRV_0x1ea: 32 ADRV
SG_ SET_ME_TMP_F : 232|5@1+ (1,0) [0|31] "" XXX
SG_ SET_ME_TMP_F_2 : 240|5@1+ (1,0) [0|31] "" XXX
BO_ 506 CLUSTER_SPEED_LIMIT: 32 XXX
SG_ SPEED_LIMIT_1 : 39|7@0+ (1,0) [0|255] "" XXX
SG_ SPEED_LIMIT_2 : 47|7@0+ (1,0) [0|255] "" XXX
SG_ SECONDARY_LIMIT_1 : 79|8@0+ (1,0) [0|127] "" XXX
SG_ SECONDARY_LIMIT_2 : 103|8@0+ (1,0) [0|127] "" XXX
SG_ SPEED_LIMIT_3 : 119|8@0+ (1,0) [0|255] "" XXX
SG_ ARROW_DOWN : 120|1@0+ (1,0) [0|1] "" XXX
SG_ ARROW_UP : 121|1@0+ (1,0) [0|1] "" XXX
SG_ CHIME_2 : 122|2@1+ (1,0) [0|7] "" XXX
SG_ SPEED_CHANGE_BLINKING : 129|1@1+ (1,0) [0|3] "" XXX
SG_ CHIME_1 : 133|1@0+ (1,0) [0|1] "" XXX
SG_ SCHOOL_ZONE : 155|1@0+ (1,0) [0|1] "" XXX
BO_ 506 FR_CMR_02_100ms: 32 FR_CMR
SG_ FR_CMR_Crc2Val : 0|16@1+ (1,0) [0|65535] "" CGW
SG_ FR_CMR_AlvCnt2Val : 16|8@1+ (1,0) [0|255] "" CGW
SG_ ISLW_OptUsmSta : 24|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLW_SysSta : 26|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLW_NoPassingInfoDis : 28|3@1+ (1,0) [0|7] "" CLU,CGW
SG_ ISLW_OvrlpSignDis : 31|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLW_SpdCluMainDis : 33|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLW_SpdNaviMainDis : 41|8@1+ (1,0) [0|255] "" CGW
SG_ ISLW_SubCondinfoSta1 : 49|4@1+ (1,0) [0|15] "" CLU,CGW
SG_ ISLW_SubCondinfoSta2 : 53|4@1+ (1,0) [0|15] "" CLU,CGW
SG_ ISLW_SpdCluSubMainDis : 64|8@1+ (1,0) [0|255] "" CLU
SG_ ISLW_SpdCluDisSubCond1 : 72|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLW_SpdCluDisSubCond2 : 80|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLW_SpdNaviSubMainDis : 88|8@1+ (1,0) [0|255] "" CLU
SG_ ISLW_SpdNaviDisSubCond1 : 96|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLW_SpdNaviDisSubCond2 : 104|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLA_SpdwOffst : 112|8@1+ (1,0) [0|255] "" CLU,CGW
SG_ ISLA_SwIgnoreReq : 120|2@1+ (1,0) [0|3] "" CGW
SG_ ISLA_SpdChgReq : 122|2@1+ (1,0) [0|3] "" CGW
SG_ ISLA_SpdWrn : 124|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLA_IcyWrn : 126|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLA_SymFlashMod : 128|3@1+ (1,0) [0|7] "" CLU,CGW
SG_ ISLA_Popup : 131|3@1+ (1,0) [0|7] "" CLU
SG_ ISLA_OptUsmSta : 136|3@1+ (1,0) [0|7] "" CLU,CGW
SG_ ISLA_OffstUsmSta : 139|3@1+ (1,0) [0|7] "" CLU,CGW
SG_ ISLA_AutoUsmSta : 142|2@1+ (1,0) [0|3] "" CLU,CGW
SG_ ISLA_Cntry : 144|4@1+ (1,0) [0|15] "" CLU,CGW
SG_ ISLA_AddtnlSign : 149|5@1+ (1,0) [0|31] "" CLU,CGW
SG_ ISLA_SchoolZone : 154|2@1+ (1,0) [0|3] "" CLU,CGW
BO_ 507 CAM_0x1fb: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
@ -476,6 +517,43 @@ BO_ 593 RADAR_0x251: 16 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 698 FR_CMR_04_40ms: 32 FR_CMR
SG_ FR_CMR_Crc4Val : 0|16@1+ (1,0) [0|65535] "" Dummy
SG_ FR_CMR_AlvCnt4Val : 16|8@1+ (1,0) [0|255] "" Dummy
SG_ IFSref_FR_CMR_Sta : 24|2@1+ (1,0) [0|3] "" CGW
SG_ IFSref_VehNumVal : 26|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_ILLAmbtSta : 30|2@1+ (0.1,0) [0|0.3] "" CGW
SG_ IFSref_VehLftAngl1Val : 32|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl1Val : 41|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl2Val : 50|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehDst1Val : 59|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehRtAngl2Val : 64|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl3Val : 73|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl3Val : 82|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl4Val : 91|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl4Val : 100|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl5Val : 109|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl5Val : 118|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl6Val : 128|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl6Val : 137|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl7Val : 146|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl7Val : 155|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl8Val : 164|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl8Val : 173|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl9Val : 182|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl9Val : 192|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehLftAngl10Val : 201|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehRtAngl10Val : 210|9@1+ (0.1,-25) [-25|26.1] "Deg" CGW
SG_ IFSref_VehDst2Val : 219|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst3Val : 223|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst4Val : 227|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst5Val : 231|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst6Val : 235|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst7Val : 239|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst8Val : 243|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst9Val : 247|4@1+ (1,0) [0|15] "" CGW
SG_ IFSref_VehDst10Val : 251|4@1+ (1,0) [0|15] "" CGW
BO_ 736 MANUAL_SPEED_LIMIT_ASSIST: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
@ -591,9 +669,16 @@ BO_ 1264 LOCAL_TIME: 8 XXX
CM_ SG_ 96 BRAKE_PRESSURE "User applied brake pedal pressure. Ramps from computer applied pressure on falling edge of cruise. Cruise cancels if !=0";
CM_ SG_ 101 BRAKE_POSITION "User applied brake pedal position, max is ~700. Signed on some vehicles";
CM_ SG_ 203 ADAS_ActvACISta "ADAS Active AngleControlInterface State";
CM_ SG_ 203 ADAS_ActvACILvl2Sta "ADAS Active AngleControlInterface Level 2 State";
CM_ SG_ 203 ADAS_StrAnglReqVal "(LFA) ADAS Steering Angle Request Value; Capped at 119.9;";
CM_ SG_ 203 ADAS_ACIAnglTqRedcGainVal "(LFA) ADAS AngleControlInterface Angle Torque Reduction Gain Value";
CM_ SG_ 203 FCA_ESA_ActvSta "FCA_ESA Active State";
CM_ SG_ 203 FCA_ESA_TqBstGainVal "FCA_ESA Torque Boost Gain Value";
CM_ BO_ 272 "Alternative LKAS message, used on cars such as 2023 Ioniq 6, 2nd gen Kona. Matches LKAS except size is 32 bytes";
CM_ SG_ 272 LKA_AVAILABLE "Angle control cars: 3 when LKA is generally available, goes to 0 during changes with LFA";
CM_ SG_ 272 LKAS_ANGLE_CMD "tracks MDPS->STEERING_ANGLE when not engaged, not STEERING_SENSORS->STEERING_ANGLE";
CM_ SG_ 272 ADAS_StrAnglReqVal "(LKAS) ADAS Steering Angle Request Value; Capped at 176.7; tracks MDPS->STEERING_ANGLE when not engaged, not STEERING_SENSORS->STEERING_ANGLE";
CM_ SG_ 272 ADAS_ACIAnglTqRedcGainVal "(LKAS) ADAS AngleControlInterface Angle Torque Reduction Gain Value";
CM_ SG_ 298 NEW_SIGNAL_4 "todo: figure out why always set to 9";
CM_ SG_ 352 SET_ME_9 "has something to do with AEB settings";
CM_ SG_ 373 ACCEnable "Likely a copy of CAN's TCS13->ACCEnable";
@ -616,10 +701,29 @@ VAL_ 64 GEAR 0 "P" 5 "D" 6 "N" 7 "R";
VAL_ 69 GEAR 0 "P" 5 "D" 6 "N" 7 "R";
VAL_ 96 TRACTION_AND_STABILITY_CONTROL 0 "On" 5 "Limited" 1 "Off";
VAL_ 112 GEAR 0 "P" 5 "D" 6 "N" 7 "R";
VAL_ 203 ADAS_ActvACISta 0 "INIT" 1 "INACTIVE" 2 "ACTIVE35(ACTIVE)" 3 "ACTIVE33(Redundancy)" 4 "RESERVED" 5 "RESERVED" 6 "RESERVED" 7 "RESERVED" 8 "RESERVED" 9 "RESERVED" 10 "RESERVED" 11 "RESERVED" 12 "RESERVED" 13 "RESERVED" 14 "RESERVED" 15 "RESERVED";
VAL_ 203 ADAS_ActvACILvl2Sta 0 "INIT" 1 "INACTIVE" 2 "ACTIVE35(ACTIVE)" 3 "RESERVED" 4 "RESERVED" 5 "RESERVED" 6 "RESERVED" 7 "RESERVED" 8 "RESERVED" 9 "RESERVED" 10 "RESERVED" 11 "RESERVED" 12 "RESERVED" 13 "RESERVED" 14 "RESERVED" 15 "RESERVED";
VAL_ 203 ADAS_StrAnglReqVal 0 "0x0x000~0x3FFF:Real Values" 16383 "0x0x000~0x3FFF:Real Values";
VAL_ 203 ADAS_ACIAnglTqRedcGainVal 0 "0x0x00~0xFA:Real Values" 250 "0x0x00~0xFA:Real Values" 251 "RESERVED" 252 "RESERVED" 253 "RESERVED" 254 "RESERVED" 255 "RESERVED";
VAL_ 203 FCA_ESA_ActvSta 0 "Inactive" 1 "Active" 2 "RESERVED" 3 "RESERVED";
VAL_ 203 FCA_ESA_TqBstGainVal 0 "0x0x00~0xFA:Real Values" 250 "0x0x00~0xFA:Real Values" 251 "RESERVED" 252 "RESERVED" 253 "RESERVED" 254 "RESERVED" 255 "RESERVED";
VAL_ 234 LKA_FAULT 0 "ok" 1 "lka fault";
VAL_ 272 LKA_MODE 1 "warning only" 2 "assist" 6 "off";
VAL_ 272 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green";
VAL_ 272 LKAS_ANGLE_ACTIVE 0 "off" 1 "not active" 2 "active";
VAL_ 282 HBA_SysOptSta 0 "None HBA Option (Default)" 1 "HBA Option" 2 "Reserved" 3 "Error indicator";
VAL_ 282 HBA_SysSta 0 "HBA Disable" 1 "HBA Enable & High Beam Off" 2 "HBA Enable & High Beam On" 3 "Reserved" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "System Fail";
VAL_ 282 HBA_IndLmpReq 0 "HBA Indicator Lamp Off" 1 "HBA Indicator Lamp On" 2 "Reserved" 3 "Error indicator";
VAL_ 282 FCA_Equip_MFC 0 "No Coding" 1 "Sensor Fusion FCA" 2 "Camera only FCA" 3 "No FCA Option" 4 "ADAS_DRV Option" 5 "Reserved" 6 "Not used" 7 "Error indicator";
VAL_ 282 HBA_OptUsmSta 0 "None HBA Option (Default)" 1 "HBA Function Off" 2 "HBA Function On" 3 "Invalid (Fail)";
VAL_ 282 DAW_LVDA_PUDis 0 "Default" 1 "Display “Leading vehicle departure alert”" 2 "Reserved" 3 "Error indicator";
VAL_ 282 DAW_LVDA_OptUsmSta 0 "No Option (default)" 1 "Off" 2 "On" 3 "Error Indicator";
VAL_ 282 DAW_OptUsmSta 0 "None DAW Option (Default)" 1 "System Off" 2 "System On" 3 "Reserved" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Invalid (Gray)";
VAL_ 282 DAW_SysSta 0 "System Off" 1 "Attention Level 1" 2 "Attention Level 2" 3 "Attention Level 3" 4 "Attention Level 4" 5 "Attention Level 5" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "System Standby" 15 "System Fail";
VAL_ 282 DAW_WrnMsgSta 0 "No Warning" 1 "Rest Recommend Warning" 2 "Hands-Off TMS call request" 3 "Reserved" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Error indicator";
VAL_ 282 DAW_TimeRstReq 0 "No signal" 1 "Time reset" 2 "Reserved" 3 "Error indicator";
VAL_ 282 DAW_SnstvtyModRetVal 0 "Default" 1 "Late" 2 "Normal" 3 "Reserved" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Invalid";
VAL_ 282 FR_CMR_SCCEquipSta 0 "Not Applied" 1 "Applied" 2 "Not used" 3 "Error Indicator";
VAL_ 298 LKA_MODE 1 "warning only" 2 "assist" 6 "off";
VAL_ 298 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green";
VAL_ 304 PARK_BUTTON 1 "Pressed" 2 "Not Pressed";
@ -693,9 +797,74 @@ VAL_ 362 BLINKER_CONTROL 1 "hazards" 2 "hazards button backlight" 3 "left blinke
VAL_ 373 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
VAL_ 416 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled";
VAL_ 426 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume";
VAL_ 437 Info_LftLnQualSta 0 "Very Low Quality" 1 "Low Quality" 2 "High Quality" 3 "Very High Quality" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Error indicator";
VAL_ 437 Info_LftLnDptSta 0 "No Left Line Departure" 1 "Left Line Departure" 2 "Reserved" 3 "Error Indicator";
VAL_ 437 Info_RtLnQualSta 0 "Very Low Quality" 1 "Low Quality" 2 "High Quality" 3 "Very High Quality" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Error indicator";
VAL_ 437 Info_RtLnDptSta 0 "No Right Line Departure" 1 "Right Line Departure" 2 "Reserved" 3 "Error Indicator";
VAL_ 437 Info_RtLnCvtrVal 65534 "Reserved" 65535 "Error indicator";
VAL_ 463 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume";
VAL_ 463 RIGHT_PADDLE 0 "Not Pulled" 1 "Pulled";
VAL_ 463 LEFT_PADDLE 0 "Not Pulled" 1 "Pulled";
VAL_ 506 ISLW_OptUsmSta 0 "None ISLW Option (Default)" 1 "System Disabled by USM" 2 "System Enable by USM" 3 "Invalid";
VAL_ 506 ISLW_SysSta 0 "Normal (Default)" 1 "System Fail" 2 "ISLW Temporary Unavailable" 3 "Reserved";
VAL_ 506 ISLW_NoPassingInfoDis 0 "None Display (Default)" 1 "LHD No Passing Zone Display" 2 "RHD No Passing Zone Display" 3 "Reserved" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Invalid";
VAL_ 506 ISLW_OvrlpSignDis 0 "None (Default)" 1 "Overlap Sign" 2 "Reserved" 3 "Error indicator";
VAL_ 506 ISLW_SpdCluMainDis 0 "No Recognition (Default)" 253 "Unlimited Speed" 254 "Reserved" 255 "Invalid";
VAL_ 506 ISLW_SpdNaviMainDis 0 "No Recognition (Default)" 253 "Unlimited Speed" 254 "Reserved" 255 "Invalid";
VAL_ 506 ISLW_SubCondinfoSta1 0 "None (Default)" 1 "Rain" 2 "Snow" 3 "Snow&Rain" 4 "Trailer" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Generic" 15 "Invalid";
VAL_ 506 ISLW_SubCondinfoSta2 0 "None (Default)" 1 "Rain" 2 "Snow" 3 "Snow&Rain" 4 "Trailer" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Generic" 15 "Invalid";
VAL_ 506 ISLW_SpdCluSubMainDis 0 "No Recognition (Default)" 253 "Unlimited Speed" 254 "Reserved" 255 "Invalid";
VAL_ 506 ISLW_SpdCluDisSubCond1 0 "No Recognition (Default)" 253 "LHD Conditional No Passing ZONE" 254 "RHD Conditional No Passing ZONE" 255 "Invalid";
VAL_ 506 ISLW_SpdCluDisSubCond2 0 "No Recognition (Default)" 253 "LHD Conditional No Passing ZONE" 254 "RHD Conditional No Passing ZONE" 255 "Invalid";
VAL_ 506 ISLW_SpdNaviSubMainDis 0 "No Recognition (Default)" 253 "Unlimited Speed" 254 "Reserved" 255 "Invalid";
VAL_ 506 ISLW_SpdNaviDisSubCond1 0 "No Recognition (Default)" 253 "LHD Conditional No Passing ZONE" 254 "RHD Conditional No Passing ZONE" 255 "Invalid";
VAL_ 506 ISLW_SpdNaviDisSubCond2 0 "No Recognition (Default)" 253 "LHD Conditional No Passing ZONE" 254 "RHD Conditional No Passing ZONE" 255 "Invalid";
VAL_ 506 ISLA_SpdwOffst 0 "No Recognition" 253 "Unlimited Speed" 254 "Reserved" 255 "Invalid";
VAL_ 506 ISLA_SwIgnoreReq 0 "Allow All Switch Inputs (default)" 1 "-(SET) Switch Input Ignore" 2 "+(SET) Switch Input Ignore" 3 "-(SET) & +(SET) Switch Inputs Ignore";
VAL_ 506 ISLA_SpdChgReq 0 "Default" 1 "Speed Change Request" 2 "Reserved" 3 "Reserved";
VAL_ 506 ISLA_SpdWrn 0 "No Warning" 1 "Warning" 2 "Reserved" 3 "Reserved";
VAL_ 506 ISLA_IcyWrn 0 "No Warning" 1 "Warning" 2 "Reserved" 3 "Reserved";
VAL_ 506 ISLA_SymFlashMod 0 "No Flasing" 1 "Flashing Sign" 2 "Flashing - Arrow Symbol" 3 "Flashing + Arrow Symbol" 4 "Flashing Auto Symbol" 5 "Reserved" 6 "Reserved" 7 "Reserved";
VAL_ 506 ISLA_Popup 0 "No Popup" 1 "MSLA Speed will Change" 2 "MSLA Speed has Changed" 3 "CC_SCC Speed will Change" 4 "CC_SCC Speed has Changed" 5 "Reserved" 6 "Reserved" 7 "Reserved";
VAL_ 506 ISLA_OptUsmSta 0 "None ISLA Option (속도 제한 메뉴 삭제)" 1 "Off" 2 "Warning" 3 "Assist" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Invalid (GRAY)";
VAL_ 506 ISLA_OffstUsmSta 0 "None Offset Function" 1 "-10kph or -5mph" 2 "-5kph or -3mph" 3 "0kph or 0mph" 4 "+5kph or +3mph" 5 "+10kph or +5mph" 6 "Reserved" 7 "Invalid (GRAY)";
VAL_ 506 ISLA_AutoUsmSta 0 "None Auto Function (Delete Menu)" 1 "Auto Off" 2 "Auto On" 3 "Invalid (GRAY)";
VAL_ 506 ISLA_Cntry 0 "Europe/Russia/Australia" 1 "Domestic" 2 "China" 3 "USA" 4 "Canada" 5 "Australia" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Initial Value (default)";
VAL_ 506 ISLA_AddtnlSign 0 "No Recognition (default)" 1 "School Crossing" 16 "Do Not Pass" 17 "Reserved" 18 "Reserved" 19 "Reserved" 20 "Reserved" 21 "Reserved" 22 "Reserved" 23 "Reserved" 24 "Exit" 25 "Roundabout" 26 "Right Curve" 27 "Left Curve" 28 "Winding Road" 29 "Reserved" 30 "Reserved" 31 "Reserved" 2 "Pedestrian Crossing" 3 "Bicycle Crossing" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Stop" 9 "Yield" 10 "Stop Ahead" 11 "Yield Ahead" 12 "Road Construction Ahead" 13 "Lane Reduction" 14 "Reserved" 15 "Reserved";
VAL_ 506 ISLA_SchoolZone 0 "No School Zone" 1 "School Zone" 2 "Reserved" 3 "Reserved";
VAL_ 698 IFSref_FR_CMR_Sta 0 "None Option (Default)" 1 "Normal" 2 "Blockage Status" 3 "Error Indicator";
VAL_ 698 IFSref_VehNumVal 0 "No vehicle" 1 "Number of vehicles" 2 "Number of vehicles" 3 "Number of vehicles" 4 "Number of vehicles" 5 "Number of vehicles" 6 "Number of vehicles" 7 "Number of vehicles" 8 "Number of vehicles" 9 "Number of vehicles" 10 "Number of vehicles" 11 "Over than 10 vehicles" 12 "Reserved" 13 "Reserved" 14 "Default" 15 "Error indicator";
VAL_ 698 IFSref_ILLAmbtSta 0 "Bright" 1 "Dark" 2 "Not used" 3 "Error indicator";
VAL_ 698 IFSref_VehLftAngl1Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl1Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl2Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehDst1Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehRtAngl2Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl3Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl3Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl4Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl4Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl5Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl5Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl6Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl6Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl7Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl7Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl8Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl8Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl9Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl9Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehLftAngl10Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehRtAngl10Val 501 "Not used" 502 "Not used" 503 "Not used" 504 "Not used" 505 "Not used" 506 "Not used" 507 "Not used" 508 "Not used" 509 "Not used" 510 "Default" 511 "Error indicator";
VAL_ 698 IFSref_VehDst2Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst3Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst4Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst5Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst6Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst7Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst8Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst9Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 698 IFSref_VehDst10Val 0 "No Value" 1 "1~10m" 2 "11~20m" 3 "21~30m" 4 "31~40m" 5 "41~50m" 6 "51~60m" 7 "61~70m" 8 "71~80m" 9 "81~90m" 10 "91~100m" 11 "101~200m" 12 "201~300m" 13 "301~400m" 14 "401m~" 15 "Error indicator";
VAL_ 736 MSLA_STATUS 0 "disabled" 1 "active" 2 "paused";
VAL_ 866 LEFT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
VAL_ 866 RIGHT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";

@ -679,7 +679,7 @@ CM_ SG_ 524 ESP_BrakePressure_LF "Brake Pressure Left Front";
CM_ SG_ 524 ESP_BrakePressure_LR "Brake Pressure Left Rear";
CM_ SG_ 524 ESP_BrakePressure_RF "Brake Pressure Right Front";
CM_ SG_ 524 ESP_BrakePressure_RR "Brake Pressure Right Rear";
CM_ SG_ 524 ESP_MasterCyl_Pressure "Master Cyclinder Brake Pressure";
CM_ SG_ 524 ESP_MasterCyl_Pressure "Master Cylinder Brake Pressure";
CM_ SG_ 524 ESP_MasterCyl_Pressure_Q "Indication of whether signal with corresponding name is valid";
CM_ SG_ 524 ESP_BrakePressure_RR_Q "Indication of whether signal with corresponding name is valid";
CM_ SG_ 524 ESP_BrakePressure_LR_Q "Indication of whether signal with corresponding name is valid";

@ -315,7 +315,7 @@ CM_ SG_ 192 TransmissionGear "0 = 1st gear, 1= 2nd gear...";
CM_ SG_ 681 TurnSignal "0 = Nothing, 1= Left, 3=Right";
CM_ SG_ 681 HighBeamOn "1=HighBeam On, 0=HighBeam Off";
CM_ SG_ 48 ACCStatusTracking "ACC Tracking vehicle, distance control.";
CM_ SG_ 48 ACCStatusOnOff "Turns one after pressing on/off button on steering wheeel";
CM_ SG_ 48 ACCStatusOnOff "Turns one after pressing on/off button on steering wheel";
CM_ SG_ 48 ACCStatusActive "ACC Active";
CM_ SG_ 48 FCWSomething "All bit set during fcw";
CM_ SG_ 48 StatusSomething "Some status changes when zeroing DTCs";
@ -358,6 +358,6 @@ CM_ SG_ 114 NEW_SIGNAL_1 "Jumped from 0 -> 120 during start. Makes triangle from
CM_ SG_ 608 NEW_SIGNAL_1 "Status?";
CM_ SG_ 608 NEW_SIGNAL_2 "Classification of object?";
CM_ SG_ 608 Distance "Distance to object in front.";
CM_ SG_ 968 PassengerSeatBelt "1 = Seatbalt latched";
CM_ SG_ 968 PassengerSeatBelt "1 = Seatbelt latched";
CM_ SG_ 968 DriverSeatBelt "1=Seatbelt latched";
VAL_ 64 GearShifter 0 "P" 1 "R" 2 "N" 3 "D" ;

@ -1304,6 +1304,23 @@ BO_ 294 HCA_01: 8 Frontsensorik
SG_ EA_Ruckfreigabe : 40|1@1+ (1,0) [0|1] "" Vector__XXX
SG_ EA_ACC_Wunschgeschwindigkeit : 41|10@1+ (0.32,0) [0|327.04] "Unit_KiloMeterPerHour" Frontradar
BO_ 810 LH_EPS_01: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
SG_ EPS_SpannungsAnf : 12|2@1+ (1.0,0.0) [0.0|3] "" XXX
SG_ EPS_Endanschlag : 14|2@1+ (1.0,0.0) [0.0|3] "" XXX
SG_ EPS_Akustiksignal : 16|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ EPS_Fehlerlampe : 17|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ EPS_Warnungen : 19|3@1+ (1.0,0.0) [0.0|7] "" XXX
SG_ EPS_PLA_Abbruch : 22|4@1+ (1,0) [0|15] "" XXX
SG_ EPS_PLA_Fehler : 26|4@1+ (1,0) [0|15] "" XXX
SG_ EPS_PLA_Status : 30|4@1+ (1.0,0.0) [0.0|15] "" XXX
SG_ EPS_Charisma_FahrPr : 34|4@1+ (1.0,0.0) [0.0|15] "" XXX
SG_ EPS_Charisma_Status : 38|2@1+ (1.0,0.0) [0.0|3] "" XXX
SG_ EPS_Lenkerposition : 41|2@1+ (1.0,0.0) [0.0|3] "" XXX
SG_ EPS_Anf_KL : 43|1@1+ (1.0,0.0) [0.0|1] "" XXX
SG_ EPS_ARA_Status : 44|4@1+ (1.0,0.0) [0.0|15] "" XXX
BO_ 159 LH_EPS_03: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX

@ -880,18 +880,31 @@ BO_ 424 Bremse_6: 3 XXX
BO_ 1192 Bremse_5: 8 XXX
SG_ CHECKSUM : 56|8@1+ (1,0) [0|0] "" XXX
SG_ COUNTER : 52|4@1+ (1,0) [0|15] "" XXX
SG_ Bremslicht_ECD : 51|1@1+ (1,0) [0|0] "" XXX
SG_ Bremsentemperatur_vorn : 48|3@1+ (125,125) [125|1000] "C" XXX
SG_ Frei_Bremse_5_5 : 40|8@1+ (1,0) [0|0] "" XXX
SG_ Offset_Gierrate : 32|8@1+ (0.05,-6.375) [-6.375|6.375] "deg/s" XXX
SG_ Vorzeichen_Bremsdruck : 31|1@1+ (1,0) [0|0] "" XXX
SG_ Status_Bremsdruck_durch_ESP_Sys : 30|1@1+ (1,0) [0|0] "" XXX
SG_ Bremsdruck_ungueltig : 29|1@1+ (1,0) [0|0] "" XXX
SG_ Frei_Bremse_5_3 : 28|1@1+ (1,0) [0|0] "" XXX
SG_ Bremsdruck : 16|12@1+ (0.1,0) [0|250] "bar" XXX
SG_ Vorzeichen_der_Giergeschwindigk : 15|1@1+ (1,0) [0|0] "" XXX
SG_ Gierrate_ungueltig : 14|1@1+ (1,0) [0|0] "" XXX
SG_ Giergeschwindigkeit : 0|14@1+ (0.01,0) [0|100] "Grad/sec" XXX
SG_ BR5_ECD_Lampe : 51|1@1+ (1,0) [0|0] "" XXX
SG_ BR5_ZT_Rueckk_Umsetz : 48|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Anhi_Sta : 40|1@1+ (1,0) [0|1] "" XXX
SG_ ESP_Rollenmodus_Deactiveieren : 34|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Sign_Druck : 31|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Sta_Druck : 30|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Druckvalid : 29|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Stillstand : 28|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Bremsdruck : 16|12@1+ (0.1,0) [0|250] "bar" XXX
SG_ BR5_Vorzeichen : 15|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Sta_Gierrate : 14|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Giergeschw : 0|14@1+ (0.01,0) [0|100] "Grad/sec" XXX
SG_ BR5_ANB_CM_Rueckk_Umsetz : 49|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_HDC_bereit : 50|1@1+ (1,0) [0|1] "" XXX
SG_ ESP_Stat_FallBack_eBKV : 35|1@1+ (1,0) [0|1] "" XXX
SG_ ESP_Anforderung_EPB : 36|2@1+ (1,0) [0|3] "" XXX
SG_ ESP_Autohold_active : 38|1@1+ (1,0) [0|1] "" XXX
SG_ ESP_Autohold_Standby : 39|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Anhi_akt : 41|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_v_Ueberw : 42|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Bremslicht : 43|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Notbremsung : 44|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_Fahrer_tritt_ZBR_Schw : 45|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_AWV2_Bremsruck : 46|1@1+ (1,0) [0|1] "" XXX
SG_ BR5_AWV2_Fehler : 47|1@1+ (1,0) [0|1] "" XXX
BO_ 672 Bremse_4: 3 XXX
SG_ Frei_Bremse_4_1 : 17|7@1+ (1,0) [0|0] "" XXX
@ -1637,7 +1650,7 @@ VAL_ 872 ACS_Fehler 1 "Fehlerspeichereintrag" 0 "kein_Fehlerspeichereintrag" ;
VAL_ 872 ACS_zul_Regelabw 254 "ADR_nicht_aktiv" 255 "Fehler" ;
VAL_ 872 ACS_max_AendGrad 254 "Neutralwert" 0 "Neutralwert" 255 "Fehler" ;
VAL_ 978 LH2_Sta_HCA 0 "disabled" 1 "initializing" 2 "fault" 3 "ready" 4 "rejected" 5 "active";
VAL_ 978 LH2_Sta_HCA 0 "disabled" 1 "initializing" 2 "fault" 3 "ready" 4 "rejected" 5 "active" 7 "active";
VAL_ 1088 Waehlhebelposition__Getriebe_1_ 8 "P" 7 "R" 6 "N" 5 "D" 9 "U" 12 "S" 14 "T" 10 "T" 11 "T";
VAL_ 1386 ACA_StaACC 6 "ACC_rev_aus" 0 "Hauptschalter_aus" 4 "ACC_im_Hintergrund" 3 "ACC_aktiv" 1 "Reserve" 2 "ACC_passiv" 7 "ACC_irrev_aus" 5 "frei" ;

@ -23,12 +23,8 @@ env = Environment(
'-Wfatal-errors',
'-Wno-pointer-to-int-cast',
'-DCANFD',
# GCC coverage flags
'-fprofile-arcs',
'-ftest-coverage',
],
CPPPATH=["#", "../../board/"],
LIBS=["gcov"],
)
if system == "Darwin":
env.PrependENVPath('PATH', '/opt/homebrew/bin')
@ -56,5 +52,15 @@ if GetOption('ubsan'):
safety = env.SharedObject("safety.os", "safety.c")
libsafety = env.SharedLibrary("libsafety.so", [safety])
coverage_flags = [
# GCC coverage flags
'-fprofile-arcs',
'-ftest-coverage',
]
env.Append(
CFLAGS=coverage_flags,
LINKFLAGS=coverage_flags,
)
# GCC note file is generated by compiler, allow scons to clean it up
env.SideEffect("safety.gcno", safety)

@ -287,8 +287,8 @@ class TestFordSafetyBase(common.PandaCarSafetyTest):
should_tx = should_tx and abs(curvature) <= curvature_accel_limit_upper
with self.subTest(controls_allowed=controls_allowed, steer_control_enabled=steer_control_enabled,
path_offset=path_offset, path_angle=path_angle, curvature_rate=curvature_rate,
curvature=curvature):
path_offset=float(path_offset), path_angle=float(path_angle), curvature_rate=float(curvature_rate),
curvature=float(curvature)):
self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(steer_control_enabled, path_offset, path_angle, curvature, curvature_rate)))
def test_curvature_rate_limits(self):

@ -55,10 +55,10 @@ struct board {
// These should match the enums in cereal/log.capnp and __init__.py
#define HW_TYPE_UNKNOWN 0U
#define HW_TYPE_WHITE_PANDA 1U
#define HW_TYPE_GREY_PANDA 2U
//#define HW_TYPE_GREY_PANDA 2U
#define HW_TYPE_BLACK_PANDA 3U
#define HW_TYPE_PEDAL 4U
#define HW_TYPE_UNO 5U
//#define HW_TYPE_PEDAL 4U
//#define HW_TYPE_UNO 5U
#define HW_TYPE_DOS 6U
#define HW_TYPE_RED_PANDA 7U
#define HW_TYPE_RED_PANDA_V2 8U
@ -77,9 +77,7 @@ struct board {
extern struct board board_black;
extern struct board board_dos;
extern struct board board_uno;
extern struct board board_tres;
extern struct board board_grey;
extern struct board board_white;
extern struct board board_cuatro;
extern struct board board_red;

@ -1,35 +0,0 @@
#pragma once
#include "board_declarations.h"
// //////////////////// //
// Grey Panda (STM32F4) //
// //////////////////// //
// Most hardware functionality is similar to white panda
board board_grey = {
.set_bootkick = unused_set_bootkick,
.harness_config = &white_harness_config,
.has_spi = false,
.has_canfd = false,
.fan_max_rpm = 0U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
.fan_stall_recovery = false,
.fan_enable_cooldown_time = 0U,
.init = white_grey_init,
.init_bootloader = white_grey_init_bootloader,
.enable_can_transceiver = white_enable_can_transceiver,
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.set_can_mode = white_set_can_mode,
.check_ignition = white_check_ignition,
.read_voltage_mV = white_read_voltage_mV,
.read_current_mA = white_read_current_mA,
.set_fan_enabled = unused_set_fan_enabled,
.set_ir_power = unused_set_ir_power,
.set_siren = unused_set_siren,
.read_som_gpio = unused_read_som_gpio,
.set_amp_enabled = unused_set_amp_enabled
};

@ -1,166 +0,0 @@
#pragma once
#include "board_declarations.h"
// /////////////////////// //
// Uno (STM32F4) + Harness //
// /////////////////////// //
static void uno_enable_can_transceiver(uint8_t transceiver, bool enabled) {
switch (transceiver){
case 1U:
set_gpio_output(GPIOC, 1, !enabled);
break;
case 2U:
set_gpio_output(GPIOC, 13, !enabled);
break;
case 3U:
set_gpio_output(GPIOA, 0, !enabled);
break;
case 4U:
set_gpio_output(GPIOB, 10, !enabled);
break;
default:
print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n");
break;
}
}
static void uno_set_bootkick(BootState state) {
if (state == BOOT_BOOTKICK) {
set_gpio_output(GPIOB, 14, false);
} else {
// We want the pin to be floating, not forced high!
set_gpio_mode(GPIOB, 14, MODE_INPUT);
}
}
static void uno_set_can_mode(uint8_t mode) {
uno_enable_can_transceiver(2U, false);
uno_enable_can_transceiver(4U, false);
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
// B5,B6: normal CAN2 mode
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2);
uno_enable_can_transceiver(2U, true);
} else {
// B5,B6: disable normal CAN2 mode
set_gpio_mode(GPIOB, 5, MODE_INPUT);
set_gpio_mode(GPIOB, 6, MODE_INPUT);
// B12,B13: OBD mode
set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2);
set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2);
uno_enable_can_transceiver(4U, true);
}
break;
default:
print("Tried to set unsupported CAN mode: "); puth(mode); print("\n");
break;
}
}
static bool uno_check_ignition(void){
// ignition is checked through harness
return harness_check_ignition();
}
static void uno_set_usb_switch(bool phone){
set_gpio_output(GPIOB, 3, phone);
}
static void uno_set_ir_power(uint8_t percentage){
pwm_set(TIM4, 2, percentage);
}
static void uno_set_fan_enabled(bool enabled){
set_gpio_output(GPIOA, 1, enabled);
}
static void uno_init(void) {
common_init_gpio();
// A8,A15: normal CAN3 mode
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3);
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);
// GPS off
set_gpio_output(GPIOB, 1, 0);
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 12, 0);
// C8: FAN PWM aka TIM3_CH3
set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3);
// Turn on phone regulator
set_gpio_output(GPIOB, 4, true);
// Initialize IR PWM and set to 0%
set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4);
pwm_init(TIM4, 2);
uno_set_ir_power(0U);
// Switch to phone usb mode if harness connection is powered by less than 7V
if(white_read_voltage_mV() < 7000U){
uno_set_usb_switch(true);
} else {
uno_set_usb_switch(false);
}
// Bootkick phone
uno_set_bootkick(BOOT_BOOTKICK);
}
static void uno_init_bootloader(void) {
// GPS off
set_gpio_output(GPIOB, 1, 0);
set_gpio_output(GPIOC, 5, 0);
set_gpio_output(GPIOC, 12, 0);
}
static harness_configuration uno_harness_config = {
.has_harness = true,
.GPIO_SBU1 = GPIOC,
.GPIO_SBU2 = GPIOC,
.GPIO_relay_SBU1 = GPIOC,
.GPIO_relay_SBU2 = GPIOC,
.pin_SBU1 = 0,
.pin_SBU2 = 3,
.pin_relay_SBU1 = 10,
.pin_relay_SBU2 = 11,
.adc_channel_SBU1 = 10,
.adc_channel_SBU2 = 13
};
board board_uno = {
.harness_config = &uno_harness_config,
.has_spi = false,
.has_canfd = false,
.fan_max_rpm = 5100U,
.fan_max_pwm = 100U,
.avdd_mV = 3300U,
.fan_stall_recovery = false,
.fan_enable_cooldown_time = 0U,
.init = uno_init,
.init_bootloader = uno_init_bootloader,
.enable_can_transceiver = uno_enable_can_transceiver,
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {9, 7, 6},
.set_can_mode = uno_set_can_mode,
.check_ignition = uno_check_ignition,
.read_voltage_mV = white_read_voltage_mV,
.read_current_mA = unused_read_current,
.set_fan_enabled = uno_set_fan_enabled,
.set_ir_power = uno_set_ir_power,
.set_siren = unused_set_siren,
.set_bootkick = uno_set_bootkick,
.read_som_gpio = unused_read_som_gpio,
.set_amp_enabled = unused_set_amp_enabled
};

@ -1,45 +0,0 @@
#!/usr/bin/env python3
import os
import time
import random
import contextlib
from panda import PandaJungle
from panda import Panda
PANDA_UNDER_TEST = Panda.HW_TYPE_UNO
panda_jungle = PandaJungle()
def silent_panda_connect():
with open(os.devnull, "w") as devnull:
with contextlib.redirect_stdout(devnull):
panda = Panda()
return panda
def reboot_panda(harness_orientation=PandaJungle.HARNESS_ORIENTATION_NONE, ignition=False):
print(f"Restarting panda with harness orientation: {harness_orientation} and ignition: {ignition}")
panda_jungle.set_panda_power(False)
panda_jungle.set_harness_orientation(harness_orientation)
panda_jungle.set_ignition(ignition)
time.sleep(2)
panda_jungle.set_panda_power(True)
time.sleep(2)
count = 0
if __name__ == "__main__":
while True:
ignition = random.randint(0, 1)
harness_orientation = random.randint(0, 2)
reboot_panda(harness_orientation, ignition)
p = silent_panda_connect()
assert p.get_type() == PANDA_UNDER_TEST
assert p.health()['car_harness_status'] == harness_orientation
if harness_orientation != PandaJungle.HARNESS_ORIENTATION_NONE:
assert p.health()['ignition_line'] == ignition
count += 1
print(f"Passed {count} loops")

@ -11,9 +11,7 @@
#include "stm32f4/llfan.h"
#include "drivers/clock_source.h"
#include "boards/white.h"
#include "boards/grey.h"
#include "boards/black.h"
#include "boards/uno.h"
#include "boards/dos.h"
// Unused functions on F4
@ -30,11 +28,9 @@ void detect_board_type(void) {
hw_type = HW_TYPE_WHITE_PANDA;
current_board = &board_white;
} else if(detect_with_pull(GPIOA, 13, PULL_DOWN)) { // Rev AB deprecated, so no pullup means black. In REV C, A13 is pulled up to 5V with a 10K
hw_type = HW_TYPE_GREY_PANDA;
current_board = &board_grey;
// grey is deprecated
} else if(!detect_with_pull(GPIOB, 15, PULL_UP)) {
hw_type = HW_TYPE_UNO;
current_board = &board_uno;
// uno is deprecated
} else {
hw_type = HW_TYPE_BLACK_PANDA;
current_board = &board_black;

@ -4,7 +4,6 @@ import pytest
from panda import Panda
pytestmark = [
pytest.mark.skip_panda_types(Panda.HW_TYPE_UNO),
pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES)
]

@ -68,6 +68,9 @@ dependencies = [
# logreader
"zstandard",
# ui
"qrcode",
]
[project.optional-dependencies]
@ -78,14 +81,13 @@ docs = [
]
testing = [
"coverage",
"hypothesis ==6.47.*",
"mypy",
"pytest",
"pytest-cov",
"pytest-cpp",
"pytest-subtests",
"pytest-xdist",
# https://github.com/pytest-dev/pytest-xdist/issues/1215
"pytest-xdist @ git+https://github.com/sshane/pytest-xdist@909e97b49d12401c10608f9d777bfc9dab8a4413",
"pytest-timeout",
"pytest-randomly",
"pytest-asyncio",
@ -102,8 +104,8 @@ dev = [
"azure-storage-blob",
"dbus-next",
"dictdiffer",
"lru-dict",
"matplotlib",
"opencv-python-headless",
"parameterized >=0.8, <0.9",
"pyautogui",
"pygame",
@ -259,9 +261,7 @@ lint.flake8-implicit-str-concat.allow-multiline = false
"tools".msg = "Use openpilot.tools"
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
"unittest".msg = "Use pytest"
[tool.coverage.run]
concurrency = ["multiprocessing", "thread"]
"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure"
[tool.ruff.format]
quote-style = "preserve"

@ -2,7 +2,6 @@ ruff
sympy
numpy
scipy
tqdm
cffi
scons
pre-commit

@ -14,12 +14,15 @@ setup(
license='MIT',
package_data={'': ['helpers/chi2_lookup_table.npy', 'templates/*']},
install_requires=[
'sympy',
'numpy',
'scipy',
'tqdm',
'cffi',
'sympy',
],
extras_require={
'dev': [
'scipy',
],
},
ext_modules=[],
description="Kalman filter library",
long_description='See https://github.com/commaai/rednose',

@ -1,36 +1,31 @@
# openpilot releases
```
## release checklist
**Go to `devel-staging`**
- [ ] update RELEASES.md
- [ ] update `devel-staging`: `git reset --hard origin/master-ci`
- [ ] open a pull request from `devel-staging` to `devel`
- [ ] post on Discord
**Go to `devel`**
- [ ] update RELEASES.md
- [ ] close out milestone
- [ ] post on Discord dev channel
- [ ] bump version on master: `common/version.h` and `RELEASES.md`
- [ ] merge the pull request
tests:
- [ ] before merging the pull request
- [ ] update from previous release -> new release
- [ ] update from new release -> previous release
- [ ] fresh install with `openpilot-test.comma.ai`
- [ ] drive on fresh install
- [ ] comma body test
- [ ] no submodules or LFS
- [ ] check sentry, MTBF, etc.
**Go to `release3`**
- [ ] publish the blog post
- [ ] `git reset --hard origin/release3-staging`
- [ ] tag the release
```
git tag v0.X.X <commit-hash>
git push origin v0.X.X
```
- [ ] tag the release: `git tag v0.X.X <commit-hash> && git push origin v0.X.X`
- [ ] create GitHub release
- [ ] final test install on `openpilot.comma.ai`
- [ ] update production
- [ ] Post on Discord, X, etc.
- [ ] update factory provisioning
- [ ] close out milestone
- [ ] post on Discord, X, etc.
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -12,11 +12,10 @@
<h5>Quectel/EG25-G</h5>
<p>FCC ID: XMR201903EG25G</p>
<p>
This device complies with Part 15 of the FCC Rules.
Operation is subject to the following two conditions:
<p>This device complies with Part 15 of the FCC Rules.</p>
<p>Operation is subject to the following two conditions:</p>
<p>(1) this device may not cause harmful interference, and
<p>(1) this device may not cause harmful interference, and</p>
<p>(2) this device must accept any interference received, including interference that may cause undesired operation.</p>
The following test reports are subject to this declaration:

@ -1,5 +1,4 @@
import numpy as np
from cereal import log
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.common.realtime import DT_CTRL, DT_MDL
@ -40,14 +39,6 @@ def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
return float(new_curvature), limited_accel or limited_max_curv
def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float:
# ToDo: Try relative error, and absolute speed
if len(modelV2.temporalPose.trans):
vel_err = np.clip(modelV2.temporalPose.trans[0] - v_ego, -MAX_VEL_ERR, MAX_VEL_ERR)
return float(vel_err)
return 0.0
def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
if len(speeds) == len(t_idxs):
v_now = speeds[0]

@ -11,7 +11,7 @@ from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_speed_error, get_accel_from_plan
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_accel_from_plan
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET
from openpilot.common.swaglog import cloudlog
@ -52,6 +52,7 @@ class LongitudinalPlanner:
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
self.CP = CP
self.mpc = LongitudinalMpc(dt=dt)
# TODO remove mpc modes when TR released
self.mpc.mode = 'acc'
self.fcw = False
self.dt = dt
@ -60,7 +61,6 @@ class LongitudinalPlanner:
self.a_desired = init_a
self.v_desired_filter = FirstOrderFilter(init_v, 2.0, self.dt)
self.prev_accel_clip = [ACCEL_MIN, ACCEL_MAX]
self.v_model_error = 0.0
self.output_a_target = 0.0
self.output_should_stop = False
@ -70,12 +70,12 @@ class LongitudinalPlanner:
self.solverExecutionTime = 0.0
@staticmethod
def parse_model(model_msg, model_error):
def parse_model(model_msg):
if (len(model_msg.position.x) == ModelConstants.IDX_N and
len(model_msg.velocity.x) == ModelConstants.IDX_N and
len(model_msg.acceleration.x) == ModelConstants.IDX_N):
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x)
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x)
a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x)
j = np.zeros(len(T_IDXS_MPC))
else:
@ -90,7 +90,7 @@ class LongitudinalPlanner:
return x, v, a, j, throttle_prob
def update(self, sm):
self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
if len(sm['carControl'].orientationNED) == 3:
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
@ -113,7 +113,7 @@ class LongitudinalPlanner:
# No change cost when user is controlling the speed, or when standstill
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
if self.mpc.mode == 'acc':
if self.mode == 'acc':
accel_clip = [ACCEL_MIN, get_max_accel(v_ego)]
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP)
@ -127,9 +127,7 @@ class LongitudinalPlanner:
# Prevent divergence, smooth in current v_ego
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
# Compute model v_ego error
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'])
# Don't clip at low speeds since throttle_prob doesn't account for creep
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
@ -160,8 +158,17 @@ class LongitudinalPlanner:
self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
action_t=action_t, vEgoStopping=self.CP.vEgoStopping)
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
output_should_stop_e2e = sm['modelV2'].action.shouldStop
if self.mode == 'acc':
output_a_target = output_a_target_mpc
self.output_should_stop = output_should_stop_mpc
else:
output_a_target = min(output_a_target_mpc, output_a_target_e2e)
self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc
for idx in range(2):
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)

@ -13,6 +13,7 @@ from typing import NoReturn
from cereal import log, car
import cereal.messaging as messaging
from openpilot.system.hardware import HARDWARE
from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process
@ -36,7 +37,10 @@ RPY_INIT = np.array([0.0,0.0,0.0])
WIDE_FROM_DEVICE_EULER_INIT = np.array([0.0, 0.0, 0.0])
HEIGHT_INIT = np.array([1.22])
# These values are needed to accommodate the model frame in the narrow cam of the C3
# These values are needed to accommodate the model frame in the narrow cam
if HARDWARE.get_device_type() == 'mici':
PITCH_LIMITS = np.array([-0.143101, 0.22235988])
else:
PITCH_LIMITS = np.array([-0.09074112085129739, 0.17])
YAW_LIMITS = np.array([-0.06912048084718224, 0.06912048084718235])
DEBUG = os.getenv("DEBUG") is not None

@ -82,6 +82,12 @@ class PointBuckets:
total_points_valid = self.__len__() >= self.min_points_total
return individual_buckets_valid and total_points_valid
def get_valid_percent(self) -> int:
total_points_perc = min(self.__len__() / self.min_points_total * 100, 100)
individual_buckets_perc = min(min(len(v) / min_pts * 100 for v, min_pts in
zip(self.buckets.values(), self.buckets_min_points.values(), strict=True)), 100)
return int((total_points_perc + individual_buckets_perc) / 2)
def is_calculable(self) -> bool:
return all(len(v) > 0 for v in self.buckets.values())

@ -229,6 +229,8 @@ class LateralLagEstimator:
liveDelay.lateralDelayEstimateStd = 0.0
liveDelay.validBlocks = self.block_avg.valid_blocks
liveDelay.calPerc = min(100 * (self.block_avg.valid_blocks * self.block_size + self.block_avg.idx) //
(self.min_valid_block_count * self.block_size), 100)
if debug:
liveDelay.points = self.block_avg.values.flatten().tolist()

@ -94,6 +94,7 @@ class TestLagd:
assert np.allclose(msg.liveDelay.lateralDelay, estimator.initial_lag)
assert np.allclose(msg.liveDelay.lateralDelayEstimate, estimator.initial_lag)
assert msg.liveDelay.validBlocks == 0
assert msg.liveDelay.calPerc == 0
def test_estimator_basics(self, subtests):
for lag_frames in range(5):
@ -107,6 +108,7 @@ class TestLagd:
assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01)
assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01)
assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED
assert msg.liveDelay.calPerc == 100
def test_disabled_estimator(self):
mocked_CP = car.CarParams(steerActuatorDelay=0.8)
@ -119,6 +121,7 @@ class TestLagd:
assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01)
assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01)
assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED
assert msg.liveDelay.calPerc == 100
def test_estimator_masking(self):
mocked_CP, lag_frames = car.CarParams(steerActuatorDelay=0.8), random.randint(1, 19)
@ -127,6 +130,7 @@ class TestLagd:
msg = estimator.get_msg(True)
assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01)
assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01)
assert msg.liveDelay.calPerc == 100
@pytest.mark.skipif(PC, reason="only on device")
@pytest.mark.timeout(60)

@ -0,0 +1,25 @@
from cereal import car
from openpilot.selfdrive.locationd.torqued import TorqueEstimator
def test_cal_percent():
est = TorqueEstimator(car.CarParams())
msg = est.get_msg()
assert msg.liveTorqueParameters.calPerc == 0
for (low, high), min_pts in zip(est.filtered_points.buckets.keys(),
est.filtered_points.buckets_min_points.values(), strict=True):
for _ in range(int(min_pts)):
est.filtered_points.add_point((low + high) / 2.0, 0.0)
# enough bucket points, but not enough total points
msg = est.get_msg()
assert msg.liveTorqueParameters.calPerc == (len(est.filtered_points) / est.min_points_total * 100 + 100) / 2
# add enough points to bucket with most capacity
key = list(est.filtered_points.buckets)[0]
for _ in range(est.min_points_total - len(est.filtered_points)):
est.filtered_points.add_point((key[0] + key[1]) / 2.0, 0.0)
msg = est.get_msg()
assert msg.liveTorqueParameters.calPerc == 100

@ -233,6 +233,7 @@ class TorqueEstimator(ParameterEstimator):
liveTorqueParameters.latAccelOffsetFiltered = float(self.filtered_params['latAccelOffset'].x)
liveTorqueParameters.frictionCoefficientFiltered = float(self.filtered_params['frictionCoefficient'].x)
liveTorqueParameters.totalBucketPoints = len(self.filtered_points)
liveTorqueParameters.calPerc = self.filtered_points.get_valid_percent()
liveTorqueParameters.decay = self.decay
liveTorqueParameters.maxResets = self.resets
return msg

@ -56,13 +56,11 @@ for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
tg_compile(flags, model_name)
# Compile BIG model if USB GPU is available
if "USBGPU" in os.environ:
import subprocess
from tinygrad import Device
# because tg doesn't support multi-process
devs = subprocess.check_output('python3 -c "from tinygrad import Device; print(list(Device.get_available_devices()))"', shell=True)
devs = subprocess.check_output('python3 -c "from tinygrad import Device; print(list(Device.get_available_devices()))"', shell=True, cwd=env.Dir('#').abspath)
if b"AMD" in devs:
del Device
print("USB GPU detected... building")
flags = "AMD=1 AMD_IFACE=USB AMD_LLVM=1 NOLOCALS=0 IMAGE=0"
bp = tg_compile(flags, "big_driving_policy")

@ -89,13 +89,6 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T)
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# temporal pose
temporal_pose = modelV2.temporalPose
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)

@ -31,7 +31,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan
from openpilot.selfdrive.modeld.parse_model_outputs import Parser
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
@ -46,8 +46,8 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
LAT_SMOOTH_SECONDS = 0.0
LONG_SMOOTH_SECONDS = 0.0
LAT_SMOOTH_SECONDS = 0.1
LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3
@ -60,7 +60,11 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
desired_curvature = model_output['desired_curvature'][0, 0]
desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2],
plan[:,Plan.ORIENTATION_RATE][:,2],
ModelConstants.T_IDXS,
v_ego,
lat_action_t)
if v_ego > MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else:
@ -86,10 +90,20 @@ class ModelState:
prev_desire: np.ndarray # for tracking the rising edge of the pulse
def __init__(self, context: CLContext):
self.frames = {
'input_imgs': DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP),
'big_input_imgs': DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP)
}
with open(VISION_METADATA_PATH, 'rb') as f:
vision_metadata = pickle.load(f)
self.vision_input_shapes = vision_metadata['input_shapes']
self.vision_input_names = list(self.vision_input_shapes.keys())
self.vision_output_slices = vision_metadata['output_slices']
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
with open(POLICY_METADATA_PATH, 'rb') as f:
policy_metadata = pickle.load(f)
self.policy_input_shapes = policy_metadata['input_shapes']
self.policy_output_slices = policy_metadata['output_slices']
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
self.frames = {name: DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP) for name in self.vision_input_names}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
@ -106,18 +120,6 @@ class ModelState:
'features_buffer': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
}
with open(VISION_METADATA_PATH, 'rb') as f:
vision_metadata = pickle.load(f)
self.vision_input_shapes = vision_metadata['input_shapes']
self.vision_output_slices = vision_metadata['output_slices']
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
with open(POLICY_METADATA_PATH, 'rb') as f:
policy_metadata = pickle.load(f)
self.policy_input_shapes = policy_metadata['input_shapes']
self.policy_output_slices = policy_metadata['output_slices']
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
# img buffers are managed in openCL transform code
self.vision_inputs: dict[str, Tensor] = {}
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
@ -135,7 +137,7 @@ class ModelState:
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
return parsed_model_outputs
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray],
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge
inputs['desire'][0] = 0
@ -148,8 +150,7 @@ class ModelState:
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()),
'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())}
imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names}
if TICI and not USBGPU:
# The imgs tensors are backed by opencl memory, only need init once
@ -177,7 +178,7 @@ class ModelState:
# TODO model only uses last value now
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs]
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
if SEND_RAW_PRED:
@ -328,6 +329,8 @@ def main(demo=False):
if prepare_only:
cloudlog.error(f"skipping model eval. Dropped {vipc_dropped_frames} frames")
bufs = {name: buf_extra if 'big' in name else buf_main for name in model.vision_input_names}
transforms = {name: model_transform_extra if 'big' in name else model_transform_main for name in model.vision_input_names}
inputs:dict[str, np.ndarray] = {
'desire': vec_desire,
'traffic_convention': traffic_convention,
@ -335,7 +338,7 @@ def main(demo=False):
}
mt1 = time.perf_counter()
model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
model_output = model.run(bufs, transforms, inputs, prepare_only)
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1

@ -88,6 +88,12 @@ class Parser:
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
self.parse_binary_crossentropy('meta', outs)
return outs
@ -95,17 +101,10 @@ class Parser:
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
return outs

@ -752,11 +752,6 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
},
EventName.noGps: {
ET.PERMANENT: Alert(
"Poor GPS reception",
"Ensure device has a clear view of the sky",
AlertStatus.normal, AlertSize.mid,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=600.)
},
EventName.tooDistracted: {

@ -356,9 +356,7 @@ class SelfdriveD:
if (planner_fcw or model_fcw) and not self.CP.notCar:
self.events.add(EventName.fcw)
# TODO: fix simulator
if not SIMULATION or REPLAY:
# Not show in first 1.5 km to allow for driving out of garage. This event shows after 5 minutes
# GPS checks
gps_ok = self.sm.recv_frame[self.gps_location_service] > 0 and (self.sm.frame - self.sm.recv_frame[self.gps_location_service]) * DT_CTRL < 2.0
if not gps_ok and self.sm['livePose'].inputsOK and (self.distance_traveled > 1500):
self.events.add(EventName.noGps)
@ -366,6 +364,8 @@ class SelfdriveD:
self.distance_traveled = 0
self.distance_traveled += abs(CS.vEgo) * DT_CTRL
# TODO: fix simulator
if not SIMULATION or REPLAY:
if self.sm['modelV2'].frameDropPerc > 20:
self.events.add(EventName.modeldLagging)

@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
OP_ROOT="$DIR/../../"
if [ -z "$BUILD" ]; then
docker pull ghcr.io/commaai/openpilot-base:latest
else
docker build --cache-from ghcr.io/commaai/openpilot-base:latest -t ghcr.io/commaai/openpilot-base:latest -f $OP_ROOT/Dockerfile.openpilot_base .
fi
docker run \
-it \
--rm \
--volume $OP_ROOT:$OP_ROOT \
--workdir $PWD \
--env PYTHONPATH=$OP_ROOT \
ghcr.io/commaai/openpilot-base:latest \
/bin/bash

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import os
import pickle
import sys
from collections import defaultdict
from typing import Any
@ -189,22 +190,44 @@ def model_replay(lr, frs):
print("----------------- Model Timing -----------------")
print("------------------------------------------------")
print(tabulate(rows, header, tablefmt="simple_grid", stralign="center", numalign="center", floatfmt=".4f"))
assert timings_ok
assert timings_ok or PC
return msgs
def get_frames():
regen_cache = "--regen-cache" in sys.argv
frames_cache = '/tmp/model_replay_cache' if PC else '/data/model_replay_cache'
os.makedirs(frames_cache, exist_ok=True)
cache_name = f'{frames_cache}/{TEST_ROUTE}_{SEGMENT}_{START_FRAME}_{END_FRAME}.pkl'
if os.path.isfile(cache_name) and not regen_cache:
try:
print(f"Loading frames from cache {cache_name}")
return pickle.load(open(cache_name, "rb"))
except Exception as e:
print(f"Failed to load frames from cache {cache_name}: {e}")
frs = {
'roadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "fcamera.hevc"), pix_fmt='nv12', cache_size=END_FRAME - START_FRAME),
'driverCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "dcamera.hevc"), pix_fmt='nv12', cache_size=END_FRAME - START_FRAME),
'wideRoadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "ecamera.hevc"), pix_fmt='nv12', cache_size=END_FRAME - START_FRAME),
}
for fr in frs.values():
for fidx in range(START_FRAME, END_FRAME):
fr.get(fidx)
fr.it = None
print(f"Dumping frame cache {cache_name}")
pickle.dump(frs, open(cache_name, "wb"))
return frs
if __name__ == "__main__":
update = "--update" in sys.argv or (os.getenv("GIT_BRANCH", "") == 'master')
replay_dir = os.path.dirname(os.path.abspath(__file__))
# load logs
lr = list(LogReader(get_url(TEST_ROUTE, SEGMENT, "rlog.zst")))
frs = {
'roadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "fcamera.hevc"), readahead=True),
'driverCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "dcamera.hevc"), readahead=True),
'wideRoadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "ecamera.hevc"), readahead=True)
}
frs = get_frames()
log_msgs = []
# run replays

@ -27,7 +27,7 @@ from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera
from openpilot.selfdrive.test.process_replay.migration import migrate_all
from openpilot.selfdrive.test.process_replay.capture import ProcessOutputCapture
from openpilot.tools.lib.logreader import LogIterable
from openpilot.tools.lib.framereader import BaseFrameReader
from openpilot.tools.lib.framereader import FrameReader
# Numpy gives different results based on CPU features after version 19
NUMPY_TOLERANCE = 1e-7
@ -209,6 +209,7 @@ class ProcessContainer:
streams_metas = available_streams(all_msgs)
for meta in streams_metas:
if meta.camera_state in self.cfg.vision_pubs:
assert frs[meta.camera_state].pix_fmt == 'nv12'
frame_size = (frs[meta.camera_state].w, frs[meta.camera_state].h)
vipc_server.create_buffers(meta.stream, 2, *frame_size)
vipc_server.start_listener()
@ -224,7 +225,7 @@ class ProcessContainer:
def start(
self, params_config: dict[str, Any], environ_config: dict[str, Any],
all_msgs: LogIterable, frs: dict[str, BaseFrameReader] | None,
all_msgs: LogIterable, frs: dict[str, FrameReader] | None,
fingerprint: str | None, capture_output: bool
):
with self.prefix as p:
@ -266,7 +267,7 @@ class ProcessContainer:
self.prefix.clean_dirs()
self._clean_env()
def run_step(self, msg: capnp._DynamicStructReader, frs: dict[str, BaseFrameReader] | None) -> list[capnp._DynamicStructReader]:
def run_step(self, msg: capnp._DynamicStructReader, frs: dict[str, FrameReader] | None) -> list[capnp._DynamicStructReader]:
assert self.rc and self.pm and self.sockets and self.process.proc
output_msgs = []
@ -296,7 +297,7 @@ class ProcessContainer:
camera_state = getattr(m, m.which())
camera_meta = meta_from_camera_state(m.which())
assert frs is not None
img = frs[m.which()].get(camera_state.frameId, pix_fmt="nv12")[0]
img = frs[m.which()].get(camera_state.frameId)
self.vipc_server.send(camera_meta.stream, img.flatten().tobytes(),
camera_state.frameId, camera_state.timestampSof, camera_state.timestampEof)
self.msg_queue = []
@ -652,7 +653,7 @@ def replay_process_with_name(name: str | Iterable[str], lr: LogIterable, *args,
def replay_process(
cfg: ProcessConfig | Iterable[ProcessConfig], lr: LogIterable, frs: dict[str, BaseFrameReader] = None,
cfg: ProcessConfig | Iterable[ProcessConfig], lr: LogIterable, frs: dict[str, FrameReader] = None,
fingerprint: str = None, return_all_logs: bool = False, custom_params: dict[str, Any] = None,
captured_output_store: dict[str, dict[str, str]] = None, disable_progress: bool = False
) -> list[capnp._DynamicStructReader]:
@ -680,7 +681,7 @@ def replay_process(
def _replay_multi_process(
cfgs: list[ProcessConfig], lr: LogIterable, frs: dict[str, BaseFrameReader] | None, fingerprint: str | None,
cfgs: list[ProcessConfig], lr: LogIterable, frs: dict[str, FrameReader] | None, fingerprint: str | None,
custom_params: dict[str, Any] | None, captured_output_store: dict[str, dict[str, str]] | None, disable_progress: bool
) -> list[capnp._DynamicStructReader]:
if fingerprint is not None:

@ -1 +1 @@
9e2fe2942fbf77f24bccdbef15893831f9c0b390
502c90d4859d2529ae2adf7097d575852673be74

@ -3,40 +3,17 @@ import os
import argparse
import time
import capnp
import numpy as np
from typing import Any
from collections.abc import Iterable
from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, ProcessConfig, replay_process, get_process_config, \
check_openpilot_enabled, check_most_messages_valid, get_custom_params_from_lr
from openpilot.selfdrive.test.process_replay.vision_meta import DRIVER_CAMERA_FRAME_SIZES
from openpilot.selfdrive.test.update_ci_routes import upload_route
from openpilot.tools.lib.framereader import FrameReader, BaseFrameReader, FrameType
from openpilot.tools.lib.framereader import FrameReader
from openpilot.tools.lib.logreader import LogReader, LogIterable, save_log
from openpilot.tools.lib.openpilotci import get_url
class DummyFrameReader(BaseFrameReader):
def __init__(self, w: int, h: int, frame_count: int, pix_val: int):
self.pix_val = pix_val
self.w, self.h = w, h
self.frame_count = frame_count
self.frame_type = FrameType.raw
def get(self, idx, count=1, pix_fmt="rgb24"):
if pix_fmt == "rgb24":
shape = (self.h, self.w, 3)
elif pix_fmt == "nv12" or pix_fmt == "yuv420p":
shape = (int((self.h * self.w) * 3 / 2),)
else:
raise NotImplementedError
return [np.full(shape, self.pix_val, dtype=np.uint8) for _ in range(count)]
@staticmethod
def zero_dcamera():
return DummyFrameReader(*DRIVER_CAMERA_FRAME_SIZES[("tici", "ar0231")], 1200, 0)
def regen_segment(
lr: LogIterable, frs: dict[str, Any] = None,
@ -64,7 +41,7 @@ def setup_data_readers(
frs['wideRoadCameraState'] = FrameReader(get_url(route, str(sidx), "ecamera.hevc"))
if needs_driver_cam:
if dummy_driver_cam:
frs['driverCameraState'] = DummyFrameReader.zero_dcamera()
frs['driverCameraState'] = FrameReader(get_url(route, str(sidx), "fcamera.hevc")) # Use fcam as dummy
else:
device_type = next(str(msg.initData.deviceType) for msg in lr if msg.which() == "initData")
assert device_type != "neo", "Driver camera not supported on neo segments. Use dummy dcamera."

@ -17,7 +17,6 @@ from openpilot.tools.lib.filereader import FileReader
from openpilot.tools.lib.logreader import LogReader, save_log
source_segments = [
("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.COMMA_BODY
("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.HYUNDAI_SONATA
("HYUNDAI2", "d545129f3ca90f28|2022-11-07--20-43-08--3"), # HYUNDAI.HYUNDAI_KIA_EV6 (+ QCOM GPS)
("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.TOYOTA_PRIUS
@ -42,7 +41,6 @@ source_segments = [
]
segments = [
("BODY", "regen2F3C7259F1B|2025-04-08--23-00-23--0"),
("HYUNDAI", "regenAA0FC4ED71E|2025-04-08--22-57-50--0"),
("HYUNDAI2", "regenAFB9780D823|2025-04-08--23-00-34--0"),
("TOYOTA", "regen218A4DCFAA1|2025-04-08--22-57-51--0"),
@ -63,7 +61,7 @@ segments = [
]
# dashcamOnly makes don't need to be tested until a full port is done
excluded_interfaces = ["mock", "tesla"]
excluded_interfaces = ["mock", "body"]
BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/"
REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit")
@ -197,7 +195,7 @@ if __name__ == "__main__":
continue
# to speed things up, we only test all segments on card
if cfg.proc_name != 'card' and car_brand not in ('HYUNDAI', 'TOYOTA', 'HONDA', 'SUBARU', 'FORD', 'RIVIAN', 'TESLA'):
if cfg.proc_name not in ('card', 'controlsd', 'lagd') and car_brand not in ('HYUNDAI', 'TOYOTA'):
continue
cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.zst")

@ -1,6 +1,6 @@
from parameterized import parameterized
from openpilot.selfdrive.test.process_replay.regen import regen_segment, DummyFrameReader
from openpilot.selfdrive.test.process_replay.regen import regen_segment
from openpilot.selfdrive.test.process_replay.process_replay import check_openpilot_enabled
from openpilot.tools.lib.openpilotci import get_url
from openpilot.tools.lib.logreader import LogReader
@ -18,7 +18,7 @@ def ci_setup_data_readers(route, sidx):
lr = LogReader(get_url(route, sidx, "rlog.bz2"))
frs = {
'roadCameraState': FrameReader(get_url(route, sidx, "fcamera.hevc")),
'driverCameraState': DummyFrameReader.zero_dcamera()
'driverCameraState': FrameReader(get_url(route, sidx, "fcamera.hevc")),
}
if next((True for m in lr if m.which() == "wideRoadCameraState"), False):
frs["wideRoadCameraState"] = FrameReader(get_url(route, sidx, "ecamera.hevc"))

@ -105,7 +105,7 @@ if GetOption('extras'):
obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs)
# keep installers small
assert f[0].get_size() < 1300*1e3, f[0].get_size()
assert f[0].get_size() < 1900*1e3, f[0].get_size()
# build watch3
if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'):

@ -1,17 +1,214 @@
import time
import pyray as rl
from openpilot.system.ui.lib.label import gui_text_box
from collections.abc import Callable
from enum import IntEnum
from openpilot.common.params import Params
from openpilot.selfdrive.ui.widgets.offroad_alerts import UpdateAlert, OffroadAlert
from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButton
from openpilot.selfdrive.ui.widgets.prime import PrimeWidget
from openpilot.selfdrive.ui.widgets.setup import SetupWidget
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR
from openpilot.system.ui.lib.widget import Widget
HEADER_HEIGHT = 80
HEAD_BUTTON_FONT_SIZE = 40
CONTENT_MARGIN = 40
SPACING = 25
RIGHT_COLUMN_WIDTH = 750
REFRESH_INTERVAL = 10.0
class HomeLayout:
PRIME_BG_COLOR = rl.Color(51, 51, 51, 255)
class HomeLayoutState(IntEnum):
HOME = 0
UPDATE = 1
ALERTS = 2
class HomeLayout(Widget):
def __init__(self):
pass
def render(self, rect: rl.Rectangle):
gui_text_box(
rect,
"Demo Home Layout",
font_size=170,
color=rl.WHITE,
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE,
super().__init__()
self.params = Params()
self.update_alert = UpdateAlert()
self.offroad_alert = OffroadAlert()
self.current_state = HomeLayoutState.HOME
self.last_refresh = 0
self.settings_callback: callable | None = None
self.update_available = False
self.alert_count = 0
self.header_rect = rl.Rectangle(0, 0, 0, 0)
self.content_rect = rl.Rectangle(0, 0, 0, 0)
self.left_column_rect = rl.Rectangle(0, 0, 0, 0)
self.right_column_rect = rl.Rectangle(0, 0, 0, 0)
self.update_notif_rect = rl.Rectangle(0, 0, 200, HEADER_HEIGHT - 10)
self.alert_notif_rect = rl.Rectangle(0, 0, 220, HEADER_HEIGHT - 10)
self._prime_widget = PrimeWidget()
self._setup_widget = SetupWidget()
self._exp_mode_button = ExperimentalModeButton()
self._setup_callbacks()
def _setup_callbacks(self):
self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
def set_settings_callback(self, callback: Callable):
self.settings_callback = callback
def _set_state(self, state: HomeLayoutState):
self.current_state = state
def _render(self, rect: rl.Rectangle):
current_time = time.time()
if current_time - self.last_refresh >= REFRESH_INTERVAL:
self._refresh()
self.last_refresh = current_time
self._handle_input()
self._render_header()
# Render content based on current state
if self.current_state == HomeLayoutState.HOME:
self._render_home_content()
elif self.current_state == HomeLayoutState.UPDATE:
self._render_update_view()
elif self.current_state == HomeLayoutState.ALERTS:
self._render_alerts_view()
def _update_layout_rects(self):
self.header_rect = rl.Rectangle(
self._rect.x + CONTENT_MARGIN, self._rect.y + CONTENT_MARGIN, self._rect.width - 2 * CONTENT_MARGIN, HEADER_HEIGHT
)
content_y = self._rect.y + CONTENT_MARGIN + HEADER_HEIGHT + SPACING
content_height = self._rect.height - CONTENT_MARGIN - HEADER_HEIGHT - SPACING - CONTENT_MARGIN
self.content_rect = rl.Rectangle(
self._rect.x + CONTENT_MARGIN, content_y, self._rect.width - 2 * CONTENT_MARGIN, content_height
)
left_width = self.content_rect.width - RIGHT_COLUMN_WIDTH - SPACING
self.left_column_rect = rl.Rectangle(self.content_rect.x, self.content_rect.y, left_width, self.content_rect.height)
self.right_column_rect = rl.Rectangle(
self.content_rect.x + left_width + SPACING, self.content_rect.y, RIGHT_COLUMN_WIDTH, self.content_rect.height
)
self.update_notif_rect.x = self.header_rect.x
self.update_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2
notif_x = self.header_rect.x + (220 if self.update_available else 0)
self.alert_notif_rect.x = notif_x
self.alert_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2
def _handle_input(self):
if not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT):
return
mouse_pos = rl.get_mouse_position()
if self.update_available and rl.check_collision_point_rec(mouse_pos, self.update_notif_rect):
self._set_state(HomeLayoutState.UPDATE)
return
if self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect):
self._set_state(HomeLayoutState.ALERTS)
return
# Content area input handling
if self.current_state == HomeLayoutState.UPDATE:
self.update_alert.handle_input(mouse_pos, True)
elif self.current_state == HomeLayoutState.ALERTS:
self.offroad_alert.handle_input(mouse_pos, True)
def _render_header(self):
font = gui_app.font(FontWeight.MEDIUM)
# Update notification button
if self.update_available:
# Highlight if currently viewing updates
highlight_color = rl.Color(255, 140, 40, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(255, 102, 0, 255)
rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color)
text = "UPDATE"
text_width = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE).x
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_width) // 2
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2
rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE)
# Alert notification button
if self.alert_count > 0:
# Highlight if currently viewing alerts
highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255)
rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color)
alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}"
text_width = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE).x
text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_width) // 2
text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2
rl.draw_text_ex(font, alert_text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE)
# Version text (right aligned)
version_text = self._get_version_text()
text_width = measure_text_cached(gui_app.font(FontWeight.NORMAL), version_text, 48).x
version_x = self.header_rect.x + self.header_rect.width - text_width
version_y = self.header_rect.y + (self.header_rect.height - 48) // 2
rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), version_text, rl.Vector2(int(version_x), int(version_y)), 48, 0, DEFAULT_TEXT_COLOR)
def _render_home_content(self):
self._render_left_column()
self._render_right_column()
def _render_update_view(self):
self.update_alert.render(self.content_rect)
def _render_alerts_view(self):
self.offroad_alert.render(self.content_rect)
def _render_left_column(self):
self._prime_widget.render(self.left_column_rect)
def _render_right_column(self):
exp_height = 125
exp_rect = rl.Rectangle(
self.right_column_rect.x, self.right_column_rect.y, self.right_column_rect.width, exp_height
)
self._exp_mode_button.render(exp_rect)
setup_rect = rl.Rectangle(
self.right_column_rect.x,
self.right_column_rect.y + exp_height + SPACING,
self.right_column_rect.width,
self.right_column_rect.height - exp_height - SPACING,
)
self._setup_widget.render(setup_rect)
def _refresh(self):
# TODO: implement _update_state with a timer
self.update_available = self.update_alert.refresh()
self.alert_count = self.offroad_alert.refresh()
self._update_state_priority(self.update_available, self.alert_count > 0)
def _update_state_priority(self, update_available: bool, alerts_present: bool):
current_state = self.current_state
if not update_available and not alerts_present:
self.current_state = HomeLayoutState.HOME
elif update_available and (current_state == HomeLayoutState.HOME or (not alerts_present and current_state == HomeLayoutState.ALERTS)):
self.current_state = HomeLayoutState.UPDATE
elif alerts_present and (current_state == HomeLayoutState.HOME or (not update_available and current_state == HomeLayoutState.UPDATE)):
self.current_state = HomeLayoutState.ALERTS
def _get_version_text(self) -> str:
brand = "openpilot"
description = self.params.get("UpdaterCurrentDescription", encoding='utf-8')
return f"{brand} {description}" if description else brand

@ -1,10 +1,12 @@
import pyray as rl
from enum import IntEnum
import cereal.messaging as messaging
from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH
from openpilot.selfdrive.ui.layouts.home import HomeLayout
from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView
from openpilot.system.ui.lib.widget import Widget
class MainState(IntEnum):
@ -13,14 +15,15 @@ class MainState(IntEnum):
ONROAD = 2
class MainLayout:
class MainLayout(Widget):
def __init__(self):
super().__init__()
self._pm = messaging.PubMaster(['userFlag'])
self._sidebar = Sidebar()
self._sidebar_visible = True
self._current_mode = MainState.HOME
self._prev_onroad = False
self._window_rect = None
self._current_callback: callable | None = None
# Initialize layouts
self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()}
@ -31,32 +34,23 @@ class MainLayout:
# Set callbacks
self._setup_callbacks()
def render(self, rect):
self._current_callback = None
self._update_layout_rects(rect)
def _render(self, _):
self._handle_onroad_transition()
self._render_main_content()
self._handle_input()
if self._current_callback:
self._current_callback()
def _setup_callbacks(self):
self._sidebar.set_callbacks(
on_settings=lambda: setattr(self, '_current_callback', self._on_settings_clicked),
on_flag=lambda: setattr(self, '_current_callback', self._on_flag_clicked),
)
self._layouts[MainState.SETTINGS].set_callbacks(
on_close=lambda: setattr(self, '_current_callback', self._set_mode_for_state)
)
self._sidebar.set_callbacks(on_settings=self._on_settings_clicked,
on_flag=self._on_flag_clicked)
self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE))
self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state)
self._layouts[MainState.ONROAD].set_callbacks(on_click=self._on_onroad_clicked)
device.add_interactive_timeout_callback(self._set_mode_for_state)
def _update_layout_rects(self, rect):
self._window_rect = rect
self._sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
def _update_layout_rects(self):
self._sidebar_rect = rl.Rectangle(self._rect.x, self._rect.y, SIDEBAR_WIDTH, self._rect.height)
x_offset = SIDEBAR_WIDTH if self._sidebar_visible else 0
self._content_rect = rl.Rectangle(rect.y + x_offset, rect.y, rect.width - x_offset, rect.height)
x_offset = SIDEBAR_WIDTH if self._sidebar.is_visible else 0
self._content_rect = rl.Rectangle(self._rect.y + x_offset, self._rect.y, self._rect.width - x_offset, self._rect.height)
def _handle_onroad_transition(self):
if ui_state.started != self._prev_onroad:
@ -66,31 +60,34 @@ class MainLayout:
def _set_mode_for_state(self):
if ui_state.started:
# Don't hide sidebar from interactive timeout
if self._current_mode != MainState.ONROAD:
self._sidebar.set_visible(False)
self._current_mode = MainState.ONROAD
self._sidebar_visible = False
else:
self._current_mode = MainState.HOME
self._sidebar_visible = True
self._sidebar.set_visible(True)
def _on_settings_clicked(self):
def open_settings(self, panel_type: PanelType):
self._layouts[MainState.SETTINGS].set_current_panel(panel_type)
self._current_mode = MainState.SETTINGS
self._sidebar_visible = False
self._sidebar.set_visible(False)
def _on_settings_clicked(self):
self.open_settings(PanelType.DEVICE)
def _on_flag_clicked(self):
pass
user_flag = messaging.new_message('userFlag')
user_flag.valid = True
self._pm.send('userFlag', user_flag)
def _on_onroad_clicked(self):
self._sidebar.set_visible(not self._sidebar.is_visible)
def _render_main_content(self):
# Render sidebar
if self._sidebar_visible:
if self._sidebar.is_visible:
self._sidebar.render(self._sidebar_rect)
content_rect = self._content_rect if self._sidebar_visible else self._window_rect
content_rect = self._content_rect if self._sidebar.is_visible else self._rect
self._layouts[self._current_mode].render(content_rect)
def _handle_input(self):
if self._current_mode != MainState.ONROAD or not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT):
return
mouse_pos = rl.get_mouse_position()
if rl.check_collision_point_rec(mouse_pos, self._content_rect):
self._sidebar_visible = not self._sidebar_visible

@ -0,0 +1,17 @@
import pyray as rl
from openpilot.system.ui.lib.widget import Widget
from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper
from openpilot.system.ui.widgets.network import WifiManagerUI
class NetworkLayout(Widget):
def __init__(self):
super().__init__()
self.wifi_manager = WifiManagerWrapper()
self.wifi_ui = WifiManagerUI(self.wifi_manager)
def _render(self, rect: rl.Rectangle):
self.wifi_ui.render(rect)
def shutdown(self):
self.wifi_manager.shutdown()

@ -1,5 +1,8 @@
from openpilot.system.ui.lib.list_view import ListView, toggle_item
from openpilot.system.ui.lib.list_view import toggle_item
from openpilot.system.ui.lib.scroller import Scroller
from openpilot.system.ui.lib.widget import Widget
from openpilot.common.params import Params
from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item
# Description constants
DESCRIPTIONS = {
@ -8,11 +11,16 @@ DESCRIPTIONS = {
"See https://docs.comma.ai/how-to/connect-to-comma for more info."
),
'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
'ssh_key': (
"Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " +
"other than your own. A comma employee will NEVER ask you to add their GitHub username."
),
}
class DeveloperLayout:
class DeveloperLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = [
toggle_item(
@ -21,6 +29,7 @@ class DeveloperLayout:
initial_state=self._params.get_bool("AdbEnabled"),
callback=self._on_enable_adb,
),
ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]),
toggle_item(
"Joystick Debug Mode",
description=DESCRIPTIONS["joystick_debug_mode"],
@ -41,10 +50,10 @@ class DeveloperLayout:
),
]
self._list_widget = ListView(items)
self._scroller = Scroller(items, line_separator=True, spacing=0)
def render(self, rect):
self._list_widget.render(rect)
def _render(self, rect):
self._scroller.render(rect)
def _on_enable_adb(self): pass
def _on_joystick_debug_mode(self): pass

@ -1,6 +1,19 @@
from openpilot.system.ui.lib.list_view import ListView, text_item, button_item
import os
import json
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.list_view import text_item, button_item, dual_button_item
from openpilot.system.ui.lib.scroller import Scroller
from openpilot.system.ui.lib.widget import Widget, DialogResult
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog, alert_dialog
from openpilot.system.ui.widgets.html_render import HtmlRenderer
# Description constants
DESCRIPTIONS = {
@ -14,34 +27,124 @@ DESCRIPTIONS = {
}
class DeviceLayout:
class DeviceLayout(Widget):
def __init__(self):
params = Params()
dongle_id = params.get("DongleId", encoding="utf-8") or "N/A"
serial = params.get("HardwareSerial") or "N/A"
super().__init__()
self._params = Params()
self._select_language_dialog: MultiOptionDialog | None = None
self._driver_camera: DriverCameraDialog | None = None
self._pair_device_dialog: PairingDialog | None = None
self._fcc_dialog: HtmlRenderer | None = None
items = self._initialize_items()
self._scroller = Scroller(items, line_separator=True, spacing=0)
def _initialize_items(self):
dongle_id = self._params.get("DongleId", encoding="utf-8") or "N/A"
serial = self._params.get("HardwareSerial") or "N/A"
items = [
text_item("Dongle ID", dongle_id),
text_item("Serial", serial),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], self._on_pair_device),
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], self._on_driver_camera),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], self._on_reset_calibration),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device),
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt),
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory),
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt),
]
regulatory_btn.set_visible(TICI)
return items
def _render(self, rect):
self._scroller.render(rect)
def _show_language_selection(self):
try:
languages_file = os.path.join(BASEDIR, "selfdrive/ui/translations/languages.json")
with open(languages_file, encoding='utf-8') as f:
languages = json.load(f)
self._select_language_dialog = MultiOptionDialog("Select a language", languages)
gui_app.set_modal_overlay(self._select_language_dialog, callback=self._handle_language_selection)
except FileNotFoundError:
pass
def _handle_language_selection(self, result: int):
if result == 1 and self._select_language_dialog:
selected_language = self._select_language_dialog.selection
self._params.put("LanguageSetting", selected_language)
self._select_language_dialog = None
def _show_driver_camera(self):
if not self._driver_camera:
self._driver_camera = DriverCameraDialog()
gui_app.set_modal_overlay(self._driver_camera, callback=lambda result: setattr(self, '_driver_camera', None))
def _reset_calibration_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reset Calibration"))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to reset calibration?", "Reset"),
callback=self._reset_calibration,
)
def _reset_calibration(self, result: int):
if ui_state.engaged or result != DialogResult.CONFIRM:
return
self._params.remove("CalibrationParams")
self._params.remove("LiveTorqueParameters")
self._params.remove("LiveParameters")
self._params.remove("LiveParametersV2")
self._params.remove("LiveDelay")
self._params.put_bool("OnroadCycleRequested", True)
def _reboot_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reboot"))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to reboot?", "Reboot"),
callback=self._perform_reboot,
)
def _perform_reboot(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoReboot", True)
def _power_off_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Power Off"))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to power off?", "Power Off"),
callback=self._perform_power_off,
)
if TICI:
items.append(button_item("Regulatory", "VIEW", callback=self._on_regulatory))
def _perform_power_off(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
self._params.put_bool_nonblocking("DoShutdown", True)
items.append(button_item("Change Language", "CHANGE", callback=self._on_change_language))
def _pair_device(self):
if not self._pair_device_dialog:
self._pair_device_dialog = PairingDialog()
gui_app.set_modal_overlay(self._pair_device_dialog, callback=lambda result: setattr(self, '_pair_device_dialog', None))
self._list_widget = ListView(items)
def _on_regulatory(self):
if not self._fcc_dialog:
self._fcc_dialog = HtmlRenderer(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html"))
def render(self, rect):
self._list_widget.render(rect)
gui_app.set_modal_overlay(self._fcc_dialog,
callback=lambda result: setattr(self, '_fcc_dialog', None),
)
def _on_pair_device(self): pass
def _on_driver_camera(self): pass
def _on_reset_calibration(self): pass
def _on_review_training_guide(self): pass
def _on_regulatory(self): pass
def _on_change_language(self): pass

@ -0,0 +1,180 @@
import pyray as rl
import json
import time
import threading
from openpilot.common.api import Api, api_get
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.wrap_text import wrap_text
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
from openpilot.system.ui.lib.widget import Widget
from openpilot.selfdrive.ui.ui_state import ui_state
TITLE = "Firehose Mode"
DESCRIPTION = (
"openpilot learns to drive by watching humans, like you, drive.\n\n"
+ "Firehose Mode allows you to maximize your training data uploads to improve "
+ "openpilot's driving models. More data means bigger models, which means better Experimental Mode."
)
INSTRUCTIONS = (
"For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n"
+ "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n"
+ "Frequently Asked Questions\n\n"
+ "Does it matter how or where I drive? Nope, just drive as you normally would.\n\n"
+ "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.\n\n"
+ "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.\n\n"
+ "Does it matter which software I run? Yes, only upstream openpilot (and particular forks) are able to be used for training."
)
class FirehoseLayout(Widget):
PARAM_KEY = "ApiCache_FirehoseStats"
GREEN = rl.Color(46, 204, 113, 255)
RED = rl.Color(231, 76, 60, 255)
GRAY = rl.Color(68, 68, 68, 255)
LIGHT_GRAY = rl.Color(228, 228, 228, 255)
UPDATE_INTERVAL = 30 # seconds
def __init__(self):
super().__init__()
self.params = Params()
self.segment_count = self._get_segment_count()
self.scroll_panel = GuiScrollPanel()
self.running = True
self.update_thread = threading.Thread(target=self._update_loop, daemon=True)
self.update_thread.start()
self.last_update_time = 0
def _get_segment_count(self) -> int:
stats = self.params.get(self.PARAM_KEY, encoding='utf8')
if not stats:
return 0
try:
return int(json.loads(stats).get("firehose", 0))
except Exception:
cloudlog.exception(f"Failed to decode firehose stats: {stats}")
return 0
def __del__(self):
self.running = False
if self.update_thread and self.update_thread.is_alive():
self.update_thread.join(timeout=1.0)
def _render(self, rect: rl.Rectangle):
# Calculate content dimensions
content_width = rect.width - 80
content_height = self._calculate_content_height(int(content_width))
content_rect = rl.Rectangle(rect.x, rect.y, rect.width, content_height)
# Handle scrolling and render with clipping
scroll_offset = self.scroll_panel.handle_scroll(rect, content_rect)
rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height))
self._render_content(rect, scroll_offset)
rl.end_scissor_mode()
def _calculate_content_height(self, content_width: int) -> int:
height = 80 # Top margin
# Title
height += 100 + 40
# Description
desc_font = gui_app.font(FontWeight.NORMAL)
desc_lines = wrap_text(desc_font, DESCRIPTION, 45, content_width)
height += len(desc_lines) * 45 + 40
# Status section
height += 32 # Separator
status_text, _ = self._get_status()
status_lines = wrap_text(gui_app.font(FontWeight.BOLD), status_text, 60, content_width)
height += len(status_lines) * 60 + 20
# Contribution count (if available)
if self.segment_count > 0:
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
contrib_lines = wrap_text(gui_app.font(FontWeight.BOLD), contrib_text, 52, content_width)
height += len(contrib_lines) * 52 + 20
# Instructions section
height += 32 # Separator
inst_lines = wrap_text(gui_app.font(FontWeight.NORMAL), INSTRUCTIONS, 40, content_width)
height += len(inst_lines) * 40 + 40 # Bottom margin
return height
def _render_content(self, rect: rl.Rectangle, scroll_offset: rl.Vector2):
x = int(rect.x + 40)
y = int(rect.y + 40 + scroll_offset.y)
w = int(rect.width - 80)
# Title
title_font = gui_app.font(FontWeight.MEDIUM)
rl.draw_text_ex(title_font, TITLE, rl.Vector2(x, y), 100, 0, rl.WHITE)
y += 140
# Description
y = self._draw_wrapped_text(x, y, w, DESCRIPTION, gui_app.font(FontWeight.NORMAL), 45, rl.WHITE)
y += 40
# Separator
rl.draw_rectangle(x, y, w, 2, self.GRAY)
y += 30
# Status
status_text, status_color = self._get_status()
y = self._draw_wrapped_text(x, y, w, status_text, gui_app.font(FontWeight.BOLD), 60, status_color)
y += 20
# Contribution count (if available)
if self.segment_count > 0:
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE)
y += 20
# Separator
rl.draw_rectangle(x, y, w, 2, self.GRAY)
y += 30
# Instructions
self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY)
def _draw_wrapped_text(self, x, y, width, text, font, size, color):
wrapped = wrap_text(font, text, size, width)
for line in wrapped:
rl.draw_text_ex(font, line, rl.Vector2(x, y), size, 0, color)
y += size
return y
def _get_status(self) -> tuple[str, rl.Color]:
network_type = ui_state.sm["deviceState"].networkType
network_metered = ui_state.sm["deviceState"].networkMetered
if not network_metered and network_type != 0: # Not metered and connected
return "ACTIVE", self.GREEN
else:
return "INACTIVE: connect to an unmetered network", self.RED
def _fetch_firehose_stats(self):
try:
dongle_id = self.params.get("DongleId", encoding='utf8')
if not dongle_id or dongle_id == UNREGISTERED_DONGLE_ID:
return
identity_token = Api(dongle_id).get_token()
response = api_get(f"v1/devices/{dongle_id}/firehose_stats", access_token=identity_token)
if response.status_code == 200:
data = response.json()
self.segment_count = data.get("firehose", 0)
self.params.put(self.PARAM_KEY, json.dumps(data))
except Exception as e:
cloudlog.error(f"Failed to fetch firehose stats: {e}")
def _update_loop(self):
while self.running:
if not ui_state.started:
self._fetch_firehose_stats()
time.sleep(self.UPDATE_INTERVAL)

@ -2,13 +2,15 @@ import pyray as rl
from dataclasses import dataclass
from enum import IntEnum
from collections.abc import Callable
from openpilot.common.params import Params
from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.label import gui_text_box
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.selfdrive.ui.layouts.network import NetworkLayout
from openpilot.system.ui.lib.widget import Widget
# Import individual panels
@ -16,9 +18,8 @@ SETTINGS_CLOSE_TEXT = "X"
# Constants
SIDEBAR_WIDTH = 500
CLOSE_BTN_SIZE = 200
NAV_BTN_HEIGHT = 80
NAV_BTN_HEIGHT = 110
PANEL_MARGIN = 50
SCROLL_SPEED = 30
# Colors
SIDEBAR_COLOR = rl.BLACK
@ -27,7 +28,6 @@ CLOSE_BTN_COLOR = rl.Color(41, 41, 41, 255)
CLOSE_BTN_PRESSED = rl.Color(59, 59, 59, 255)
TEXT_NORMAL = rl.Color(128, 128, 128, 255)
TEXT_SELECTED = rl.Color(255, 255, 255, 255)
TEXT_PRESSED = rl.Color(173, 173, 173, 255)
class PanelType(IntEnum):
@ -43,23 +43,22 @@ class PanelType(IntEnum):
class PanelInfo:
name: str
instance: object
button_rect: rl.Rectangle
button_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0)
class SettingsLayout:
class SettingsLayout(Widget):
def __init__(self):
self._params = Params()
super().__init__()
self._current_panel = PanelType.DEVICE
self._max_scroll = 0.0
# Panel configuration
self._panels = {
PanelType.DEVICE: PanelInfo("Device", DeviceLayout(), rl.Rectangle(0, 0, 0, 0)),
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout(), rl.Rectangle(0, 0, 0, 0)),
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout(), rl.Rectangle(0, 0, 0, 0)),
PanelType.FIREHOSE: PanelInfo("Firehose", None, rl.Rectangle(0, 0, 0, 0)),
PanelType.NETWORK: PanelInfo("Network", None, rl.Rectangle(0, 0, 0, 0)),
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout(), rl.Rectangle(0, 0, 0, 0)),
PanelType.DEVICE: PanelInfo("Device", DeviceLayout()),
PanelType.NETWORK: PanelInfo("Network", NetworkLayout()),
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()),
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()),
PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()),
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout()),
}
self._font_medium = gui_app.font(FontWeight.MEDIUM)
@ -71,7 +70,7 @@ class SettingsLayout:
def set_callbacks(self, on_close: Callable):
self._close_callback = on_close
def render(self, rect: rl.Rectangle):
def _render(self, rect: rl.Rectangle):
# Calculate layout
sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
panel_rect = rl.Rectangle(rect.x + SIDEBAR_WIDTH, rect.y, rect.width - SIDEBAR_WIDTH, rect.height)
@ -80,9 +79,6 @@ class SettingsLayout:
self._draw_sidebar(sidebar_rect)
self._draw_current_panel(panel_rect)
if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
self.handle_mouse_release(rl.get_mouse_position())
def _draw_sidebar(self, rect: rl.Rectangle):
rl.draw_rectangle_rec(rect, SIDEBAR_COLOR)
@ -96,7 +92,7 @@ class SettingsLayout:
close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR
rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color)
close_text_size = rl.measure_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, 140, 0)
close_text_size = measure_text_cached(self._font_bold, SETTINGS_CLOSE_TEXT, 140)
close_text_pos = rl.Vector2(
close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2,
close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2,
@ -107,23 +103,15 @@ class SettingsLayout:
self._close_btn_rect = close_btn_rect
# Navigation buttons
nav_start_y = rect.y + 300
button_spacing = 20
i = 0
y = rect.y + 300
for panel_type, panel_info in self._panels.items():
button_rect = rl.Rectangle(
rect.x + 50,
nav_start_y + i * (NAV_BTN_HEIGHT + button_spacing),
rect.width - 150, # Right-aligned with margin
NAV_BTN_HEIGHT,
)
button_rect = rl.Rectangle(rect.x + 50, y, rect.width - 150, NAV_BTN_HEIGHT)
# Button styling
is_selected = panel_type == self._current_panel
text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL
# Draw button text (right-aligned)
text_size = rl.measure_text_ex(self._font_medium, panel_info.name, 65, 0)
text_size = measure_text_cached(self._font_medium, panel_info.name, 65)
text_pos = rl.Vector2(
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
)
@ -131,7 +119,8 @@ class SettingsLayout:
# Store button rect for click detection
panel_info.button_rect = button_rect
i += 1
y += NAV_BTN_HEIGHT
def _draw_current_panel(self, rect: rl.Rectangle):
rl.draw_rectangle_rounded(
@ -142,17 +131,8 @@ class SettingsLayout:
panel = self._panels[self._current_panel]
if panel.instance:
panel.instance.render(content_rect)
else:
gui_text_box(
content_rect,
f"Demo {self._panels[self._current_panel].name} Panel",
font_size=170,
color=rl.WHITE,
alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE,
)
def handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool:
def _handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool:
# Check close button
if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect):
if self._close_callback:
@ -162,20 +142,15 @@ class SettingsLayout:
# Check navigation buttons
for panel_type, panel_info in self._panels.items():
if rl.check_collision_point_rec(mouse_pos, panel_info.button_rect):
self._switch_to_panel(panel_type)
self.set_current_panel(panel_type)
return True
return False
def _switch_to_panel(self, panel_type: PanelType):
def set_current_panel(self, panel_type: PanelType):
if panel_type != self._current_panel:
self._current_panel = panel_type
def set_current_panel(self, index: int, param: str = ""):
panel_types = list(self._panels.keys())
if 0 <= index < len(panel_types):
self._switch_to_panel(panel_types[index])
def close_settings(self):
if self._close_callback:
self._close_callback()

@ -1,7 +1,20 @@
from openpilot.system.ui.lib.list_view import ListView, button_item, text_item
from openpilot.common.params import Params
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.list_view import button_item, text_item
from openpilot.system.ui.lib.scroller import Scroller
from openpilot.system.ui.lib.widget import Widget, DialogResult
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog
class SoftwareLayout:
class SoftwareLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = self._init_items()
self._scroller = Scroller(items, line_separator=True, spacing=0)
def _init_items(self):
items = [
text_item("Current Version", ""),
button_item("Download", "CHECK", callback=self._on_download_update),
@ -9,13 +22,21 @@ class SoftwareLayout:
button_item("Target Branch", "SELECT", callback=self._on_select_branch),
button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall),
]
return items
self._list_widget = ListView(items)
def render(self, rect):
self._list_widget.render(rect)
def _render(self, rect):
self._scroller.render(rect)
def _on_download_update(self): pass
def _on_install_update(self): pass
def _on_select_branch(self): pass
def _on_uninstall(self): pass
def _on_uninstall(self):
def handle_uninstall_confirmation(result):
if result == DialogResult.CONFIRM:
self._params.put_bool("DoUninstall", True)
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to uninstall?", "Uninstall"),
callback=handle_uninstall_confirmation,
)

@ -1,4 +1,6 @@
from openpilot.system.ui.lib.list_view import ListView, toggle_item
from openpilot.system.ui.lib.list_view import multiple_button_item, toggle_item
from openpilot.system.ui.lib.scroller import Scroller
from openpilot.system.ui.lib.widget import Widget
from openpilot.common.params import Params
# Description constants
@ -8,6 +10,11 @@ DESCRIPTIONS = {
"Your attention is required at all times to use this feature."
),
"DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.",
"LongitudinalPersonality": (
"Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " +
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " +
"your steering wheel distance button."
),
"IsLdwEnabled": (
"Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " +
"without a turn signal activated while driving over 31 mph (50 km/h)."
@ -15,11 +22,13 @@ DESCRIPTIONS = {
"AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.",
'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.",
"IsMetric": "Display speed in km/h instead of mph.",
"RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.",
}
class TogglesLayout:
class TogglesLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = [
toggle_item(
@ -39,6 +48,15 @@ class TogglesLayout:
self._params.get_bool("DisengageOnAccelerator"),
icon="disengage_on_accelerator.png",
),
multiple_button_item(
"Driving Personality",
DESCRIPTIONS["LongitudinalPersonality"],
buttons=["Aggressive", "Standard", "Relaxed"],
button_width=255,
callback=self._set_longitudinal_personality,
selected_index=int(self._params.get("LongitudinalPersonality") or 0),
icon="speed_limit.png"
),
toggle_item(
"Enable Lane Departure Warnings",
DESCRIPTIONS["IsLdwEnabled"],
@ -60,9 +78,18 @@ class TogglesLayout:
toggle_item(
"Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="monitoring.png"
),
toggle_item(
"Record Microphone Audio",
DESCRIPTIONS["RecordAudio"],
self._params.get_bool("RecordAudio"),
icon="microphone.png",
),
]
self._list_widget = ListView(items)
self._scroller = Scroller(items, line_separator=True, spacing=0)
def _render(self, rect):
self._scroller.render(rect)
def render(self, rect):
self._list_widget.render(rect)
def _set_longitudinal_personality(self, button_index: int):
self._params.put("LongitudinalPersonality", str(button_index))

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

Loading…
Cancel
Save