Merge remote-tracking branch 'upstream/master' into fw_query_consistent

pull/29712/head
Shane Smiskol 2 years ago
commit af55338fec
  1. 2
      .github/workflows/docs.yaml
  2. 26
      .github/workflows/selfdrive_tests.yaml
  3. 1
      .gitignore
  4. 3
      .gitmodules
  5. 6
      .pre-commit-config.yaml
  6. 10
      Jenkinsfile
  7. 6
      RELEASES.md
  8. 2
      cereal
  9. 2
      common/basedir.py
  10. 10
      common/file_helpers.py
  11. 2
      common/kalman/simple_kalman.py
  12. 2
      common/params.cc
  13. 1
      common/params.h
  14. 1
      common/params_pyx.pyx
  15. 1
      common/prefix.py
  16. 30
      common/retry.py
  17. 0
      common/swaglog.py
  18. 2
      common/time.py
  19. 46
      common/xattr.py
  20. 19
      conftest.py
  21. 17
      docs/CARS.md
  22. 8
      docs/c_docs.rst
  23. 5
      launch_chffrplus.sh
  24. 2
      launch_env.sh
  25. 2
      opendbc
  26. 2
      panda
  27. 99
      poetry.lock
  28. 11
      pyproject.toml
  29. 1
      release/build_devel.sh
  30. 47
      release/files_common
  31. 2
      scripts/apply-pr.sh
  32. 39
      scripts/build_small.sh
  33. 27
      scripts/retry.sh
  34. BIN
      selfdrive/assets/sounds/warning_immediate.wav
  35. 7
      selfdrive/athena/athenad.py
  36. 2
      selfdrive/athena/manage_athenad.py
  37. 2
      selfdrive/athena/registration.py
  38. 2
      selfdrive/athena/tests/test_athenad.py
  39. 25
      selfdrive/athena/tests/test_athenad_ping.py
  40. 10
      selfdrive/boardd/boardd.cc
  41. 2
      selfdrive/boardd/pandad.py
  42. 2
      selfdrive/boardd/tests/test_pandad.py
  43. 2
      selfdrive/car/car_helpers.py
  44. 2
      selfdrive/car/disable_ecu.py
  45. 2
      selfdrive/car/ecu_addrs.py
  46. 6
      selfdrive/car/ford/values.py
  47. 2
      selfdrive/car/fw_versions.py
  48. 3
      selfdrive/car/hyundai/interface.py
  49. 14
      selfdrive/car/hyundai/values.py
  50. 2
      selfdrive/car/isotp_parallel_query.py
  51. 1
      selfdrive/car/mazda/values.py
  52. 1
      selfdrive/car/tests/routes.py
  53. 13
      selfdrive/car/tests/test_models.py
  54. 23
      selfdrive/car/toyota/carcontroller.py
  55. 3
      selfdrive/car/toyota/interface.py
  56. 4
      selfdrive/car/toyota/tests/test_toyota.py
  57. 14
      selfdrive/car/toyota/toyotacan.py
  58. 3
      selfdrive/car/toyota/values.py
  59. 2
      selfdrive/car/vin.py
  60. 8
      selfdrive/car/volkswagen/values.py
  61. 72
      selfdrive/controls/controlsd.py
  62. 2
      selfdrive/controls/lib/latcontrol.py
  63. 2
      selfdrive/controls/lib/latcontrol_angle.py
  64. 2
      selfdrive/controls/lib/latcontrol_pid.py
  65. 2
      selfdrive/controls/lib/latcontrol_torque.py
  66. 2
      selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
  67. 2
      selfdrive/controls/lib/longitudinal_planner.py
  68. 4
      selfdrive/controls/lib/tests/test_latcontrol.py
  69. 2
      selfdrive/controls/plannerd.py
  70. 3
      selfdrive/controls/radard.py
  71. 2
      selfdrive/debug/auto_fingerprint.py
  72. 4
      selfdrive/debug/count_events.py
  73. 6
      selfdrive/debug/run_process_on_route.py
  74. 28
      selfdrive/locationd/calibrationd.py
  75. 2
      selfdrive/locationd/helpers.py
  76. 2
      selfdrive/locationd/models/car_kf.py
  77. 2
      selfdrive/locationd/paramsd.py
  78. 1
      selfdrive/locationd/test/test_locationd.py
  79. 2
      selfdrive/locationd/torqued.py
  80. 2
      selfdrive/manager/build.py
  81. 6
      selfdrive/manager/manager.py
  82. 2
      selfdrive/manager/process.py
  83. 3
      selfdrive/manager/process_config.py
  84. 4
      selfdrive/modeld/dmonitoringmodeld.py
  85. 2
      selfdrive/modeld/modeld.py
  86. 2
      selfdrive/modeld/navmodeld.py
  87. 13
      selfdrive/monitoring/dmonitoringd.py
  88. 6
      selfdrive/navd/navd.py
  89. 2
      selfdrive/sentry.py
  90. 2
      selfdrive/statsd.py
  91. 2
      selfdrive/test/cpp_harness.py
  92. 2
      selfdrive/test/docker_build.sh
  93. 2
      selfdrive/test/helpers.py
  94. 16
      selfdrive/test/longitudinal_maneuvers/test_longitudinal.py
  95. 2
      selfdrive/test/process_replay/README.md
  96. 37
      selfdrive/test/process_replay/conftest.py
  97. 150
      selfdrive/test/process_replay/helpers.py
  98. 2
      selfdrive/test/process_replay/migration.py
  99. 20
      selfdrive/test/process_replay/process_replay.py
  100. 2
      selfdrive/test/process_replay/ref_commit
  101. Some files were not shown because too many files have changed in this diff Show More

@ -32,7 +32,7 @@ jobs:
${{ env.RUN }} "scons -j$(nproc)"
- name: Build docs
run: |
${{ env.RUN }} "apt update && apt install -y doxygen && cd docs && make html"
${{ env.RUN }} "apt update && apt install -y doxygen && cd docs && make -j$(nproc) html"
- uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'

@ -208,20 +208,30 @@ jobs:
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run replay
id: run-replay
timeout-minutes: 30
run: |
${{ env.RUN }} "CI=1 $PYTEST -n auto --dist=loadscope selfdrive/test/process_replay/test_processes.py --long-diff && \
chmod -R 777 /tmp/comma_download_cache"
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
chmod -R 777 /tmp/comma_download_cache && \
coverage combine && \
coverage xml"
- name: Print diff
id: print-diff
if: always()
run: cat selfdrive/test/process_replay/diff.txt
- uses: actions/upload-artifact@v3
if: always()
continue-on-error: true
with:
name: process_replay_diff.txt
path: selfdrive/test/process_replay/diff.txt
- name: Upload reference logs
if: ${{ failure() && steps.print-diff.outcome == 'success' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
run: |
${{ env.RUN }} "unset PYTHONWARNINGS && CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
with:
name: ${{ github.job }}
- name: Upload reference logs
if: ${{ failure() && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
run: |
${{ env.RUN }} "unset PYTHONWARNINGS && CI=1 AZURE_TOKEN='$AZURE_TOKEN' \
pytest -n auto --dist=loadscope selfdrive/test/process_replay/test_processes.py --upload-only"
regen:
name: regen

1
.gitignore vendored

@ -46,7 +46,6 @@ selfdrive/boardd/boardd
selfdrive/logcatd/logcatd
selfdrive/mapd/default_speeds_by_region.json
system/proclogd/proclogd
selfdrive/ui/_ui
selfdrive/ui/translations/alerts_generated.h
selfdrive/ui/translations/tmp
selfdrive/test/longitudinal_maneuvers/out

3
.gitmodules vendored

@ -13,6 +13,9 @@
[submodule "body"]
path = body
url = ../../commaai/body.git
[submodule "teleoprtc_repo"]
path = teleoprtc_repo
url = ../../commaai/teleoprtc
[submodule "tinygrad"]
path = tinygrad_repo
url = https://github.com/geohot/tinygrad.git

@ -26,7 +26,7 @@ repos:
rev: v2.2.6
hooks:
- id: codespell
exclude: '^(third_party/)|(body/)|(cereal/)|(panda/)|(opendbc/)|(rednose/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)|(poetry.lock)'
exclude: '^(third_party/)|(body/)|(cereal/)|(panda/)|(opendbc/)|(rednose/)|(rednose_repo/)|(teleoprtc/)|(teleoprtc_repo/)|(selfdrive/ui/translations/.*.ts)|(poetry.lock)'
args:
# if you've got a short variable name that's getting flagged, add it here
- -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints
@ -39,12 +39,12 @@ repos:
language: system
types: [python]
args: ['--explicit-package-bases', '--local-partial-types']
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
exclude: '^(third_party/)|(cereal/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)'
- repo: local
hooks:
- id: cppcheck

10
Jenkinsfile vendored

@ -14,7 +14,8 @@ export GIT_BRANCH=${env.GIT_BRANCH}
export GIT_COMMIT=${env.GIT_COMMIT}
export AZURE_TOKEN='${env.AZURE_TOKEN}'
export MAPBOX_TOKEN='${env.MAPBOX_TOKEN}'
export PYTEST_ADDOPTS="-c selfdrive/test/pytest-tici.ini --rootdir ."
# only use 1 thread for tici tests since most require HIL
export PYTEST_ADDOPTS="-n 0"
export GIT_SSH_COMMAND="ssh -i /data/gitkey"
@ -157,7 +158,7 @@ node {
// tici tests
'onroad tests': {
deviceStage("onroad", "tici-needs-can", ["SKIP_COPY=1"], [
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"],
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR $SOURCE_DIR/scripts/retry.sh ./build_devel.sh"],
["build openpilot", "cd selfdrive/manager && ./build.py"],
["check dirty", "release/check-dirty.sh"],
["onroad tests", "pytest selfdrive/test/test_onroad.py -s"],
@ -233,9 +234,8 @@ node {
'car tests': {
pcStage("car tests") {
sh label: "build", script: "selfdrive/manager/build.py"
sh label: "test_models.py", script: "INTERNAL_SEG_CNT=250 INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt FILEREADER_CACHE=1 \
pytest -n auto --dist=loadscope selfdrive/car/tests/test_models.py"
sh label: "test_car_interfaces.py", script: "MAX_EXAMPLES=100 pytest -n auto --dist=load selfdrive/car/tests/test_car_interfaces.py"
sh label: "run car tests", script: "cd selfdrive/car/tests && MAX_EXAMPLES=100 INTERNAL_SEG_CNT=250 FILEREADER_CACHE=1 \
INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt pytest test_models.py test_car_interfaces.py"
}
},

@ -1,5 +1,9 @@
Version 0.9.6 (2023-XX-XX)
Version 0.9.6 (2023-12-14)
========================
* AGNOS 9
* comma body streaming and controls over WebRTC
* Toyota RAV4 2023 support
* Toyota RAV4 Hybrid 2023 support
Version 0.9.5 (2023-11-17)
========================

@ -1 +1 @@
Subproject commit 2cb2bfb0154874775e56715ecf81f0e6b00538e9
Subproject commit d11688a90a1cebacb3fe00dcfbba5f27551b2d89

@ -1,4 +1,4 @@
import os
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))

@ -4,16 +4,6 @@ import tempfile
from atomicwrites import AtomicWriter
def mkdirs_exists_ok(path):
if path.startswith(('http://', 'https://')):
raise ValueError('URL path')
try:
os.makedirs(path)
except OSError:
if not os.path.isdir(path):
raise
def rm_not_exists_ok(path):
try:
os.remove(path)

@ -9,4 +9,4 @@ def get_kalman_gain(dt, A, C, Q, R, iterations=100):
S = C.dot(P).dot(C.T) + R
K = P.dot(C.T).dot(np.linalg.inv(S))
P = (np.eye(len(P)) - K.dot(C)).dot(P)
return K
return K

@ -114,7 +114,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DoReboot", CLEAR_ON_MANAGER_START},
{"DoShutdown", CLEAR_ON_MANAGER_START},
{"DoUninstall", CLEAR_ON_MANAGER_START},
{"ExperimentalLongitudinalEnabled", PERSISTENT},
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY},
{"ExperimentalMode", PERSISTENT},
{"ExperimentalModeConfirmed", PERSISTENT},
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},

@ -10,6 +10,7 @@ enum ParamKeyType {
CLEAR_ON_ONROAD_TRANSITION = 0x08,
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
DONT_LOG = 0x20,
DEVELOPMENT_ONLY = 0x40,
ALL = 0xFFFFFFFF
};

@ -11,6 +11,7 @@ cdef extern from "common/params.h":
CLEAR_ON_MANAGER_START
CLEAR_ON_ONROAD_TRANSITION
CLEAR_ON_OFFROAD_TRANSITION
DEVELOPMENT_ONLY
ALL
cdef cppclass c_Params "Params":

@ -21,7 +21,6 @@ class OpenpilotPrefix:
except FileExistsError:
pass
os.makedirs(Paths.log_root(), exist_ok=True)
os.makedirs(Paths.download_cache_root(), exist_ok=True)
return self

@ -0,0 +1,30 @@
import time
import functools
from openpilot.common.swaglog import cloudlog
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception:
cloudlog.exception(f"{func.__name__} failed, trying again")
time.sleep(delay)
if ignore_failure:
cloudlog.error(f"{func.__name__} failed after retry")
else:
raise Exception(f"{func.__name__} failed after retry")
return wrapper
return decorator
if __name__ == "__main__":
@retry(attempts=10)
def abc():
raise ValueError("abc failed :(")
abc()

@ -3,4 +3,4 @@ import datetime
MIN_DATE = datetime.datetime(year=2023, month=6, day=1)
def system_time_valid():
return datetime.datetime.now() > MIN_DATE
return datetime.datetime.now() > MIN_DATE

@ -1,46 +0,0 @@
import os
from cffi import FFI
from typing import Any, List
# Workaround for the EON/termux build of Python having os.*xattr removed.
ffi = FFI()
ffi.cdef("""
int setxattr(const char *path, const char *name, const void *value, size_t size, int flags);
ssize_t getxattr(const char *path, const char *name, void *value, size_t size);
ssize_t listxattr(const char *path, char *list, size_t size);
int removexattr(const char *path, const char *name);
""")
libc = ffi.dlopen(None)
def setxattr(path, name, value, flags=0) -> None:
path = path.encode()
name = name.encode()
if libc.setxattr(path, name, value, len(value), flags) == -1:
raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: setxattr({path}, {name}, {value}, {flags})")
def getxattr(path, name, size=128):
path = path.encode()
name = name.encode()
value = ffi.new(f"char[{size}]")
l = libc.getxattr(path, name, value, size)
if l == -1:
# errno 61 means attribute hasn't been set
if ffi.errno == 61:
return None
raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: getxattr({path}, {name}, {size})")
return ffi.buffer(value)[:l]
def listxattr(path, size=128) -> List[Any]:
path = path.encode()
attrs = ffi.new(f"char[{size}]")
l = libc.listxattr(path, attrs, size)
if l == -1:
raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: listxattr({path}, {size})")
# attrs is b'\0' delimited values (so chop off trailing empty item)
return [a.decode() for a in ffi.buffer(attrs)[:l].split(b"\0")[0:-1]]
def removexattr(path, name) -> None:
path = path.encode()
name = name.encode()
if libc.removexattr(path, name) == -1:
raise OSError(ffi.errno, f"{os.strerror(ffi.errno)}: removexattr({path}, {name})")

@ -6,6 +6,12 @@ from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.hardware import TICI
def pytest_sessionstart(session):
# TODO: fix tests and enable test order randomization
if session.config.pluginmanager.hasplugin('randomly'):
session.config.option.randomly_reorganize = False
@pytest.fixture(scope="function", autouse=True)
def openpilot_function_fixture():
starting_env = dict(os.environ)
@ -45,13 +51,14 @@ def pytest_collection_modifyitems(config, items):
item.add_marker(skipper)
if "xdist_group_class_property" in item.keywords:
class_property = item.get_closest_marker('xdist_group_class_property').args[0]
item.add_marker(pytest.mark.xdist_group(getattr(item.cls, class_property)))
class_property_name = item.get_closest_marker('xdist_group_class_property').args[0]
class_property_value = getattr(item.cls, class_property_name)
item.add_marker(pytest.mark.xdist_group(class_property_value))
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
config_line = (
"xdist_group_class_property: group tests by a property of the class that contains them"
)
config.addinivalue_line("markers", config_line)
config_line = (
"xdist_group_class_property: group tests by a property of the class that contains them"
)
config.addinivalue_line("markers", config_line)

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 268 Supported Cars
# 271 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -34,7 +34,7 @@ A supported vehicle is one that just works when you install a comma device. All
|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None||
|Ford|Bronco Sport 2021-22|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v2<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=Bronco Sport 2021-22">Buy Here</a></sub></details>||
|Ford|Escape 2020-22|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|Ford|Explorer 2020-22|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-22">Buy Here</a></sub></details>||
|Ford|Explorer 2020-23|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-23">Buy Here</a></sub></details>||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|Ford|Kuga 2020-22|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|Ford|Maverick 2022|LARIAT Luxury|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 Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v2<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 2022">Buy Here</a></sub></details>||
@ -73,6 +73,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera 2022">Buy Here</a></sub></details>||
|Hyundai|Azera Hybrid 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2019">Buy Here</a></sub></details>||
|Hyundai|Azera Hybrid 2020|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 K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2020">Buy Here</a></sub></details>||
|Hyundai|Custin 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 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Custin 2023">Buy Here</a></sub></details>||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra 2017-18">Buy Here</a></sub></details>||
@ -98,7 +99,7 @@ A supported vehicle is one that just works when you install a comma device. All
|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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=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>|
|Hyundai|Kona Hybrid 2020|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 I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Palisade 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 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Santa Cruz 2022-23[<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 N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|Hyundai|Santa Fe 2019-20|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 D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>|<a href="https://youtu.be/bjDR0YjM__s" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Santa Fe 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 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Santa Fe Hybrid 2022-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 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>||
@ -107,8 +108,8 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Sonata 2020-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 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Sonata Hybrid 2020-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 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Sonata Hybrid 2020-23">Buy Here</a></sub></details>||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2021">Buy Here</a></sub></details>||
|Hyundai|Tucson 2022[<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 N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|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 N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|Hyundai|Tucson Diesel 2019|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 L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson Diesel 2019">Buy Here</a></sub></details>||
|Hyundai|Tucson Hybrid 2022-24[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Tucson Hybrid 2022-24">Buy Here</a></sub></details>||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Veloster 2019-20">Buy Here</a></sub></details>||
@ -143,7 +144,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Kia|Sorento 2021-23[<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 K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|Kia|Sorento Hybrid 2021-23[<sup>6</sup>](#footnotes)|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 A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|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 A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|Kia|Sportage 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 N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|Kia|Stinger 2018-20|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 C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Stinger 2022-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 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2022-23">Buy Here</a></sub></details>||
@ -236,10 +237,12 @@ A supported vehicle is one that just works when you install a comma device. All
|Toyota|RAV4 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 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2017-18">Buy Here</a></sub></details>||
|Toyota|RAV4 2019-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 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2022">Buy Here</a></sub></details>||
|Toyota|RAV4 2023|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 2023">Buy Here</a></sub></details>||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|RAV4 Hybrid 2019-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 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2019-21">Buy Here</a></sub></details>||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|RAV4 Hybrid 2023|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=RAV4 Hybrid 2023">Buy Here</a></sub></details>||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Toyota&model=Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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=Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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=Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@ -273,7 +276,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable<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>||

@ -29,8 +29,6 @@ camerad
^^^^^^^
.. autodoxygenindex::
:project: system_camerad_cameras
.. autodoxygenindex::
:project: system_camerad_imgproc
locationd
^^^^^^^^^
@ -43,12 +41,6 @@ ui
.. autodoxygenindex::
:project: selfdrive_ui
soundd
""""""
.. autodoxygenindex::
:project: selfdrive_ui_soundd
replay
""""""
.. autodoxygenindex::

@ -9,6 +9,11 @@ source "$BASEDIR/launch_env.sh"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
function agnos_init {
# wait longer for weston to come up
if [ -f "$BASEDIR/prebuilt" ]; then
sleep 5
fi
# TODO: move this to agnos
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta

@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="8.2"
export AGNOS_VERSION="9.1"
fi
if [ -z "$PASSIVE" ]; then

@ -1 +1 @@
Subproject commit 2b96bcc45669cdd14f9c652b07ef32d6403630f6
Subproject commit 93b983d49a2d6d5c67e15ce7650f55e4f121485d

@ -1 +1 @@
Subproject commit b50455cc76a0f66aa6faf953dd3ea023783a4b3f
Subproject commit a88fe8c883758951ba2854eb2f6726d5365df099

99
poetry.lock generated

@ -112,66 +112,39 @@ ifaddr = ">=0.2.0"
[[package]]
name = "aiortc"
version = "1.5.0"
version = "1.6.0"
description = "An implementation of WebRTC and ORTC"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "aiortc-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1d3f2d6cc22fae5ea57b0371895b7830e878b9e3705fd3742b3453cdfa0fd51"},
{file = "aiortc-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2eaf758b5e0bb16f22a9aeb8ab88eb947345f47e2e46cfca18b2815d44726c4e"},
{file = "aiortc-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee76f6b30d7f39442ba7ac25d58114f077ead1460c5632d0c9e18179d01ad419"},
{file = "aiortc-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a766052d93474e9bf4186465298b7c8fb9af062ef7f83ba33f191baa79dac1e"},
{file = "aiortc-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fec292636978ed50728f1ce9b7a9f0d7d2e38bd0b920bb53e091e5728b79e231"},
{file = "aiortc-1.5.0-cp310-cp310-win32.whl", hash = "sha256:27e879b73377d4b94bd86e4c3e8cd8913905fdca1de90a9a4efb0d9d3779dbf4"},
{file = "aiortc-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a720d0dd53553f6dfc28a53bee2ffce4f13283b4cbbc7db548000054cc63a4f9"},
{file = "aiortc-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5e8cbfce84badd9a8355819343570bbec1e4eef725996cad6aebe4cc3d03ae8"},
{file = "aiortc-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7931512dbb2ff91fb78f5512ad9ca96546452d7bb627f61bd7393bf59ee48ad3"},
{file = "aiortc-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6abeb857a98014fc97265891ebf4fd989987d2ee091e0844e3c8fc543b6e2f0"},
{file = "aiortc-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dead42dc3a31570fb6f5b94f9be9c78e28b1dc045f71489858116840f299862e"},
{file = "aiortc-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a1a8081ba6d7cabc5896d10462cb50f6db7a8ccf34e6aa3e6c4a0d2d5bc5db5"},
{file = "aiortc-1.5.0-cp311-cp311-win32.whl", hash = "sha256:cbd5d35bd34b22b8f711c708d266889c973c0dcb38da14a2a9f757266987a181"},
{file = "aiortc-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:6749145e3d527ac98c80837d72fd832b0c403eded3546aeb7cec6f25592b4d5e"},
{file = "aiortc-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:50e8e8903cf55f6f2cda9b61c115fca8e444d48f299cdd071980a3b5cec594fa"},
{file = "aiortc-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15218a1b81f4fa1521f3b839eefdce638b34c46306e8eaf069cee7283fe8c838"},
{file = "aiortc-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bca7c7bbd3619296b5737a810dd0e2fc7f6264e767fca10e65a709a443bf39"},
{file = "aiortc-1.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f1d88ae0f8b3047a279e4da06f09a35777cfbe0a9177ca8b053865a98a67912"},
{file = "aiortc-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:f86b68b182537022d4ada49a7723c7a56f39372d6fbc31a29f57315d335cdc29"},
{file = "aiortc-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e4bbc2f2b97651f7aa6f5e82c69a22590901962454fc02617c4a559a1b51c21a"},
{file = "aiortc-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7243bce7c3b95e47e56ddf961fbf6015702ddbbf3579b0bbf18c6173b6a6357a"},
{file = "aiortc-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:883db8926deaf01fdcd32fbd74fcf055db63e968324ceff41d5a46ec86dff90c"},
{file = "aiortc-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd663f67344e6fe240c6372f620988db5285c9b1b8336306e9fec76ffb4e5493"},
{file = "aiortc-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe2766f5a7a8e10b445cbf83a510b791a88180c7b1f9adef3f730840fa208afc"},
{file = "aiortc-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba212562025843e8d9faf66e6156b682148f8f9995a19e5c66e8ea802f3fa121"},
{file = "aiortc-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b7432c9c78e68811ee060ade8b0f867ac42a21677e4d1a9136bb88cd93ab8299"},
{file = "aiortc-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ac6e285d4035298f3025b5767dc8f8b0a5a81b2b8744aaa19c75a8fe76f3ad8"},
{file = "aiortc-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aa3c9306d892635dd9c38cc83c6ba67fb608c7da289f422d40f9542e104b7a0f"},
{file = "aiortc-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:55dab49a38a212556adadb85ea06f6041d2a9e537e01092f9160b21b186b5039"},
{file = "aiortc-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db93641b6f31315b8fd4c81e14881aef28fbb0700f220926f82909baedfa9888"},
{file = "aiortc-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f63fd1168df72498afe0ee06555cc86b8496115ef128519a01d1ea8e404784b8"},
{file = "aiortc-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e436f49617887f2009c6ada872c2da201e3c8010b387e7c1057eab229ae438c3"},
{file = "aiortc-1.5.0-cp39-cp39-win32.whl", hash = "sha256:6f23495d4e11610117d1bad8686d42d529168d463687a1a1e0bec795d1ec33ce"},
{file = "aiortc-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:76206601082e39fdb56d86221729f04f8bd79d65f9fd6b82121947eabf7efd6d"},
{file = "aiortc-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3b2a3b4c120a73242ea0b843ecc3efeaea32861682c771e67f7f08f9d18fddc"},
{file = "aiortc-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d3f6511f2442f49dfaf4e69865b47e0d6d95440fee2f66e6a03a8b4fa1b28e3"},
{file = "aiortc-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ae221c734864c8749c27cc8add22d296ef3e06ae5f6982dbcbe2d0976b10e1"},
{file = "aiortc-1.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7732c825ee96e9bc7fb779a4008be768e7663f7f9bf0ab3cccdd412dd7f1c820"},
{file = "aiortc-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:56ffdd67161488c6d934b090a8c2d277bba8806906a3a18493f46b42976569c1"},
{file = "aiortc-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f73ba04ca3f331b0ddea0b4ff78424ba30bfd7a49d0b8bd926c75a66ad60f447"},
{file = "aiortc-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69eedeec467bd7bcac7ace6ad398133e27f18eeae195a3ad0ffda74255a8b812"},
{file = "aiortc-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e095e5fe22f5a2efd4e0657abec1fea7aca864cb32ae3f0816fbcd340a4f2b7"},
{file = "aiortc-1.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffd6899a5d3db4356d2c17521021032468931ae168545b1ff4815764a5e2873"},
{file = "aiortc-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:af3eed686d621af93befd7e68bd73d6d8a8aa3e721e8fa3ce7e21b3225e37c38"},
{file = "aiortc-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:15e222a308dcfc44351bd9acff21723c8065cdcd75d6649d53b2986ada64b6be"},
{file = "aiortc-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9ecd61c42e6a78c089805a47542a68eeeec6ba98bf7a2e30cafa3d3f4e94a7f"},
{file = "aiortc-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d839437c6000d77511ff1889133150f23fbc8a7365971260c45ce06ff007b0f"},
{file = "aiortc-1.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:025847ad6b8c5686f2895394e1de92c043e20e7d90c266de201eef1b1108c8df"},
{file = "aiortc-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:85583166ab9c9052d2539bee3ba05f27af7f7b93b15c2259c2fc1bd3de5b31d5"},
{file = "aiortc-1.5.0.tar.gz", hash = "sha256:82b4131d84f862e24e1c3550b73f78412cc9554140a2575577eb3f04675bbad2"},
{file = "aiortc-1.6.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:4675e04d8441797fef6c8a669b3a67d750670d1b897f08886072a084d743e07d"},
{file = "aiortc-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:836c0686fca67f142c52e5af8043206c2bb702ad0ddcdc94ef19caf1c22f8d54"},
{file = "aiortc-1.6.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33f846abd753935881158751994a51f14e345762130688b19c26cab42c01266f"},
{file = "aiortc-1.6.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4bd45397945a5323bd077d43c702a3a991d75023f23649c1d18df5d80c221d"},
{file = "aiortc-1.6.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f93561b515ff478b068bb9b047d8b1e896c747dcf3ee465463047c51a7bea24d"},
{file = "aiortc-1.6.0-cp38-abi3-win32.whl", hash = "sha256:325f847397af2892aa051dc2880a75e9bd79f535cc05ec8f4538b5ed098b3c5d"},
{file = "aiortc-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:98b118d53ae874126b2e9ec6bb1397ea169b85550c4bd5453e279507ff7f0cf9"},
{file = "aiortc-1.6.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6677018833a5d648754c99c70e6c6f6d4f3942682cda07ed5afa73422f8a009a"},
{file = "aiortc-1.6.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:090936c719225e50cd4d66f476e6c17293a8062cf7687a1baa5080f3c90ec8b1"},
{file = "aiortc-1.6.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e60d5bc269d4d12f1f5f47e2c17aa3799f3b5c8b73fd6d8d246ddc11bb29776"},
{file = "aiortc-1.6.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:607b9496b4a3c8cd9d32afb6d5bce07f9170831ec44a20ab8af54d53879aafc8"},
{file = "aiortc-1.6.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0a41f4c0b31e45548e7c7397ef1aecc4be49ab68afd8fa134c07581fe0b3a9c2"},
{file = "aiortc-1.6.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:64a9939016edbc8f300de6189983c983753827813ac9acd9b5be8ce61cc32684"},
{file = "aiortc-1.6.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:813e7985665c94a0e3387b66e39dba6c751e5e588aedbca06d7e52068c6e37fb"},
{file = "aiortc-1.6.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:672d0b4ad35c4d8f014f44a142aa55529ec82cfe2809226e1275e35a71fd4422"},
{file = "aiortc-1.6.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1526a1904174bb11958b8f7e93f01f37f80df2190e5089f0501984bdef79595e"},
{file = "aiortc-1.6.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cecfa5f462e73218cadc9acd8013cb3a0d9007a4515bceba6e7755d77bb80061"},
{file = "aiortc-1.6.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:febca6773de18a6bb9e5569ae87c8be55ed184695f1f9fc99aa4744a7b0375f8"},
{file = "aiortc-1.6.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5ba865be9708713397ec584ed1baeb2f15d2fa9c32594ce19a41ffa6e2517cb"},
{file = "aiortc-1.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9759a3c00e46ba1c3499dbf5a8513ae37ba65b940a56b0e7fa5070478e9379f"},
{file = "aiortc-1.6.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cff7868663d9d1c74e237b86e45126022466240439a5f63c3440e3acdf0305b"},
{file = "aiortc-1.6.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9f20479d0dd06116ac81d332850fab874d83b561a73fd7252d218f55c6bd5b79"},
{file = "aiortc-1.6.0.tar.gz", hash = "sha256:08cffbcde3401b33731b1d4169b9ff860b0aaaca200b62e10ce5978238671ad7"},
]
[package.dependencies]
aioice = ">=0.9.0,<1.0.0"
av = ">=9.0.0,<11.0.0"
av = ">=9.0.0,<12.0.0"
cffi = ">=1.0.0"
cryptography = ">=2.2"
google-crc32c = ">=1.1"
@ -180,7 +153,7 @@ pylibsrtp = ">=0.5.6"
pyopenssl = ">=23.1.0"
[package.extras]
dev = ["aiohttp (>=3.7.0)", "coverage (>=5.0)", "numpy (>=1.19.0)"]
dev = ["aiohttp (>=3.7.0)", "coverage[toml] (>=7.2.2)", "numpy (>=1.19.0)"]
[[package]]
name = "aiosignal"
@ -3336,8 +3309,6 @@ files = [
{file = "pygame-2.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e24d05184e4195fe5ebcdce8b18ecb086f00182b9ae460a86682d312ce8d31f"},
{file = "pygame-2.5.2-cp311-cp311-win32.whl", hash = "sha256:f02c1c7505af18d426d355ac9872bd5c916b27f7b0fe224749930662bea47a50"},
{file = "pygame-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:6d58c8cf937815d3b7cdc0fa9590c5129cb2c9658b72d00e8a4568dea2ff1d42"},
{file = "pygame-2.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1a2a43802bb5e89ce2b3b775744e78db4f9a201bf8d059b946c61722840ceea8"},
{file = "pygame-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1c289f2613c44fe70a1e40769de4a49c5ab5a29b9376f1692bb1a15c9c1c9bfa"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:074aa6c6e110c925f7f27f00c7733c6303407edc61d738882985091d1eb2ef17"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe0228501ec616779a0b9c4299e837877783e18df294dd690b9ab0eed3d8aaab"},
{file = "pygame-2.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31648d38ecdc2335ffc0e38fb18a84b3339730521505dac68514f83a1092e3f4"},
@ -3746,18 +3717,18 @@ colorama = "*"
pytest = ">=7.0"
[[package]]
name = "pytest-random-order"
version = "1.1.0"
description = "Randomise the order in which pytest tests are run with some control over the randomness"
name = "pytest-randomly"
version = "3.15.0"
description = "Pytest plugin to randomly order tests and control random.seed."
optional = false
python-versions = ">=3.5.0"
python-versions = ">=3.8"
files = [
{file = "pytest-random-order-1.1.0.tar.gz", hash = "sha256:dbe6debb9353a7af984cc9eddbeb3577dd4dbbcc1529a79e3d21f68ed9b45605"},
{file = "pytest_random_order-1.1.0-py3-none-any.whl", hash = "sha256:6cb1e59ab0f798bb0c3488c11ae0c70d7d3340306a466d28b28ccd8ef8c20b7e"},
{file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"},
{file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"},
]
[package.dependencies]
pytest = ">=3.0.0"
pytest = "*"
[[package]]
name = "pytest-subtests"
@ -5038,4 +5009,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "~3.11"
content-hash = "f0abc0f53443c3b98eb06fc4dd0ff736e39197bc68c79db9aeffb4736657b676"
content-hash = "7f7244a89280a41937066ebf80aa64fcb375852484c9d034ea7f2de3eae4d90a"

@ -24,6 +24,7 @@ testpaths = [
"system/proclogd",
"system/tests",
"system/ubloxd",
"system/webrtc",
"tools/lib/tests",
"tools/replay",
"tools/cabana"
@ -43,6 +44,8 @@ exclude = [
"rednose_repo/",
"tinygrad/",
"tinygrad_repo/",
"teleoprtc/",
"teleoprtc_repo/",
"third_party/",
]
@ -148,7 +151,7 @@ pytest-subtests = "*"
pytest-xdist = "*"
pytest-timeout = "*"
pytest-timeouts = "*"
pytest-random-order = "*"
pytest-randomly = "*"
ruff = "*"
scipy = "*"
sphinx = "*"
@ -178,7 +181,7 @@ build-backend = "poetry.core.masonry.api"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff]
select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"]
ignore = ["W292", "E741", "E402", "C408", "ISC003", "B027", "B024"]
ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"]
line-length = 160
target-version="py311"
exclude = [
@ -186,6 +189,8 @@ exclude = [
"opendbc",
"rednose_repo",
"tinygrad_repo",
"teleoprtc",
"teleoprtc_repo",
"third_party",
]
flake8-implicit-str-concat.allow-multiline=false
@ -197,4 +202,4 @@ flake8-implicit-str-concat.allow-multiline=false
"tools".msg = "Use openpilot.tools"
[tool.coverage.run]
concurrency = ["multiprocessing", "thread"]
concurrency = ["multiprocessing", "thread"]

@ -22,6 +22,7 @@ pre-commit uninstall || true
echo "[-] bringing master-ci and devel in sync T=$SECONDS"
cd $TARGET_DIR
git fetch --depth 1 origin master-ci
git fetch --depth 1 origin devel

@ -21,24 +21,8 @@ openpilot/**
common/.gitignore
common/__init__.py
common/conversions.py
common/gpio.py
common/realtime.py
common/timeout.py
common/ffi_wrapper.py
common/file_helpers.py
common/logging_extra.py
common/numpy_fast.py
common/params.py
common/params_pyx.pyx
common/profiler.py
common/basedir.py
common/dict_helpers.py
common/filter_simple.py
common/stat_live.py
common/spinner.py
common/text_window.py
common/time.py
common/*.py
common/*.pyx
common/kalman/.gitignore
common/kalman/*
@ -76,7 +60,6 @@ selfdrive/statsd.py
system/logmessaged.py
system/micd.py
system/swaglog.py
system/version.py
selfdrive/athena/__init__.py
@ -281,6 +264,12 @@ system/sensord/sensors/*.cc
system/sensord/sensors/*.h
system/sensord/pigeond.py
system/webrtc/__init__.py
system/webrtc/webrtcd.py
system/webrtc/schema.py
system/webrtc/device/audio.py
system/webrtc/device/video.py
selfdrive/thermald/thermald.py
selfdrive/thermald/power_monitoring.py
selfdrive/thermald/fan_controller.py
@ -291,19 +280,14 @@ selfdrive/test/helpers.py
selfdrive/test/setup_device_ci.sh
selfdrive/test/test_onroad.py
selfdrive/test/test_time_to_onroad.py
selfdrive/test/pytest-tici.ini
selfdrive/ui/.gitignore
selfdrive/ui/SConscript
selfdrive/ui/*.cc
selfdrive/ui/*.h
selfdrive/ui/ui
selfdrive/ui/text
selfdrive/ui/spinner
selfdrive/ui/soundd/*.cc
selfdrive/ui/soundd/*.h
selfdrive/ui/soundd/soundd
selfdrive/ui/soundd/.gitignore
selfdrive/ui/soundd.py
selfdrive/ui/translations/*.ts
selfdrive/ui/translations/languages.json
selfdrive/ui/update_translations.py
@ -333,12 +317,11 @@ system/camerad/main.cc
system/camerad/snapshot/*
system/camerad/cameras/camera_common.h
system/camerad/cameras/camera_common.cc
system/camerad/cameras/sensor2_i2c.h
system/camerad/imgproc/conv.cl
system/camerad/imgproc/pool.cl
system/camerad/imgproc/utils.cc
system/camerad/imgproc/utils.h
system/camerad/sensors/ar0231.cc
system/camerad/sensors/ar0231_registers.h
system/camerad/sensors/ox03c10.cc
system/camerad/sensors/ox03c10_registers.h
system/camerad/sensors/sensor.h
selfdrive/manager/__init__.py
selfdrive/manager/build.py
@ -444,6 +427,8 @@ third_party/qt5/larch64/bin/**
scripts/update_now.sh
scripts/stop_updater.sh
teleoprtc/**
rednose_repo/site_scons/site_tools/rednose_filter.py
rednose/.gitignore
rednose/**

@ -8,4 +8,4 @@ fi
BASE="https://github.com/commaai/openpilot/pull/"
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
curl -L $BASE/$PR_NUM.patch | git apply
curl -L $BASE/$PR_NUM.patch | git apply -3

@ -0,0 +1,39 @@
#!/bin/bash
set -ex
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd $DIR
# git clone --mirror
SRC=/tmp/openpilot.git/
OUT=/tmp/smallpilot/
echo "starting size $(du -hs .git/)"
rm -rf $OUT
cd $SRC
git remote update
# copy contents
#rsync -a --exclude='.git/' $DIR $OUT
cp -r $SRC $OUT
cd $OUT
# remove all tags
git tag -l | xargs git tag -d
# remove non-master branches
BRANCHES="release2 dashcam dashcam3 release3 devel master-ci nightly"
for branch in $BRANCHES; do
git branch -D $branch
git branch -D ${branch}-staging || true
done
#git gc
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
echo "new one is $(du -hs .)"

@ -0,0 +1,27 @@
#!/bin/bash
function fail {
echo $1 >&2
exit 1
}
function retry {
local n=1
local max=3 # 3 retries before failure
local delay=5 # delay between retries, 5 seconds
while true; do
echo "Running command '$@' with retry, attempt $n/$max"
"$@" && break || {
if [[ $n -lt $max ]]; then
((n++))
sleep $delay;
else
fail "The command has failed after $n attempts."
fi
}
done
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
retry "$@"
fi

@ -36,14 +36,11 @@ from openpilot.common.params import Params
from openpilot.common.realtime import set_core_affinity
from openpilot.system.hardware import HARDWARE, PC, AGNOS
from openpilot.system.loggerd.xattr_cache import getxattr, setxattr
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_commit, get_origin, get_short_branch, get_version
from openpilot.system.hardware.hw import Paths
# TODO: use socket constant when mypy recognizes this as a valid attribute
TCP_USER_TIMEOUT = 18
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
LOCAL_PORT_WHITELIST = {8022}
@ -760,7 +757,7 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
if onroad != onroad_prev:
onroad_prev = onroad
sock.setsockopt(socket.IPPROTO_TCP, TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)

@ -5,7 +5,7 @@ from multiprocessing import Process
from openpilot.common.params import Params
from openpilot.selfdrive.manager.process import launcher
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_version, is_dirty
ATHENA_MGR_PID_PARAM = "AthenadPid"

@ -12,7 +12,7 @@ from openpilot.common.spinner import Spinner
from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.hardware.hw import Paths
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
UNREGISTERED_DONGLE_ID = "UnregisteredDevice"

@ -46,8 +46,6 @@ class TestAthenadMethods(unittest.TestCase):
else:
os.unlink(p)
dispatcher["listUploadQueue"]() # ensure queue is empty at start
# *** test helpers ***
@staticmethod

@ -3,8 +3,8 @@ import subprocess
import threading
import time
import unittest
from typing import Callable, cast, Optional
from unittest.mock import MagicMock
from typing import cast, Optional
from unittest import mock
from openpilot.common.params import Params
from openpilot.common.timeout import Timeout
@ -27,8 +27,6 @@ class TestAthenadPing(unittest.TestCase):
athenad: threading.Thread
exit_event: threading.Event
_create_connection: Callable
def _get_ping_time(self) -> Optional[str]:
return cast(Optional[str], self.params.get("LastAthenaPingTime", encoding="utf-8"))
@ -38,15 +36,9 @@ class TestAthenadPing(unittest.TestCase):
def _received_ping(self) -> bool:
return self._get_ping_time() is not None
@classmethod
def setUpClass(cls) -> None:
cls._create_connection = athenad.create_connection
athenad.create_connection = MagicMock(wraps=cls._create_connection)
@classmethod
def tearDownClass(cls) -> None:
wifi_radio(True)
athenad.create_connection = cls._create_connection
def setUp(self) -> None:
self.params = Params()
@ -58,19 +50,18 @@ class TestAthenadPing(unittest.TestCase):
self.exit_event = threading.Event()
self.athenad = threading.Thread(target=athenad.main, args=(self.exit_event,))
athenad.create_connection.reset_mock()
def tearDown(self) -> None:
if self.athenad.is_alive():
self.exit_event.set()
self.athenad.join()
def assertTimeout(self, reconnect_time: float) -> None:
@mock.patch('openpilot.selfdrive.athena.athenad.create_connection', autospec=True)
def assertTimeout(self, reconnect_time: float, mock_create_connection: mock.MagicMock) -> None:
self.athenad.start()
time.sleep(1)
athenad.create_connection.assert_called_once()
athenad.create_connection.reset_mock()
mock_create_connection.assert_called_once()
mock_create_connection.reset_mock()
# check normal behaviour
with self.subTest("Wi-Fi: receives ping"), Timeout(70, "no ping received"):
@ -78,7 +69,7 @@ class TestAthenadPing(unittest.TestCase):
time.sleep(0.1)
print("ping received")
athenad.create_connection.assert_not_called()
mock_create_connection.assert_not_called()
# websocket should attempt reconnect after short time
with self.subTest("LTE: attempt reconnect"):
@ -86,7 +77,7 @@ class TestAthenadPing(unittest.TestCase):
print("waiting for reconnect attempt")
start_time = time.monotonic()
with Timeout(reconnect_time, "no reconnect attempt"):
while not athenad.create_connection.called:
while not mock_create_connection.called:
time.sleep(0.1)
print(f"reconnect attempt after {time.monotonic() - start_time:.2f}s")

@ -1,11 +1,5 @@
#include "selfdrive/boardd/boardd.h"
#include <sched.h>
#include <sys/cdefs.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <array>
#include <atomic>
@ -13,10 +7,6 @@
#include <cassert>
#include <cerrno>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <future>
#include <memory>
#include <thread>

@ -12,7 +12,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.boardd.set_time import set_time
from openpilot.system.hardware import HARDWARE
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
def get_expected_signature(panda: Panda) -> bytes:

@ -116,4 +116,4 @@ class TestPandad(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
unittest.main()

@ -10,7 +10,7 @@ from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
import cereal.messaging as messaging
from openpilot.selfdrive.car import gen_empty_fingerprint

@ -1,6 +1,6 @@
#!/usr/bin/env python3
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
EXT_DIAG_REQUEST = b'\x10\x03'
EXT_DIAG_RESPONSE = b'\x50\x03'

@ -8,7 +8,7 @@ from panda.python.uds import SERVICE_TYPE
from openpilot.selfdrive.car import make_can_msg
from openpilot.selfdrive.car.fw_query_definitions import EcuAddrBusType
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
def make_tester_present_msg(addr, bus, subaddr=None):

@ -88,7 +88,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
FordCarInfo("Ford Kuga 2020-22", "Adaptive Cruise Control with Lane Centering"),
],
CAR.EXPLORER_MK6: [
FordCarInfo("Ford Explorer 2020-22"),
FordCarInfo("Ford Explorer 2020-23"),
FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"),
],
CAR.F_150_MK14: FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"),
@ -185,6 +185,7 @@ FW_VERSIONS = {
b'L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'M1MC-14D003-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'P1MC-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'L1MC-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -192,6 +193,7 @@ FW_VERSIONS = {
b'L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-KB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -212,6 +214,8 @@ FW_VERSIONS = {
b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NB5A-14C204-AZD\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PB5A-14C204-DA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LB5A-14C204-ATS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.F_150_MK14: {

@ -12,7 +12,7 @@ from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusTyp
from openpilot.selfdrive.car.interfaces import get_interface_attr
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
Ecu = car.CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]

@ -260,8 +260,7 @@ class CarInterface(CarInterfaceBase):
if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = (candidate in (HYBRID_CAR | EV_CAR) and candidate not in
(CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR))
ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
else:
ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0]

@ -159,7 +159,10 @@ class HyundaiCarInfo(CarInfo):
CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.AZERA_HEV_6TH_GEN: HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.AZERA_HEV_6TH_GEN: [
HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CAR.ELANTRA: [
# TODO: 2017-18 could be Hyundai G
HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
@ -556,18 +559,23 @@ FW_VERSIONS = {
CAR.AZERA_HEV_6TH_GEN: {
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.02 99211-G8100 191029',
b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.00 99211-G8000 180903',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00IG MDPS C 1.00 1.00 56310M9600\x00 4IHSC100',
b'\xf1\x00IG MDPS C 1.00 1.01 56310M9350\x00 4IH8C101',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00IGhe SCC FHCUP 1.00 1.00 99110-M9100 ',
b'\xf1\x00IGhe SCC FHCUP 1.00 1.01 99110-M9000 ',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T7N0_C2\x00\x006T7VA051\x00\x00TIGSH24KA1\xc7\x85\xe2`',
b'\xf1\x006T7N0_C2\x00\x006T7Q2051\x00\x00TIG2H24KA2\x12@\x11\xb7',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H590051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H570051\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.HYUNDAI_GENESIS: {
@ -1132,6 +1140,7 @@ FW_VERSIONS = {
b'\xf1\x00CK__ SCC F-CUP 1.00 1.00 99110-J5500 ',
b'\xf1\x00CK__ SCC FHCUP 1.00 1.00 99110-J5500 ',
b'\xf1\x00CK__ SCC FHCUP 1.00 1.00 99110-J5600 ',
b'\xf1\x00CK__ SCC FHCUP 1.00 1.01 99110-J5100 ',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x81640R0051\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1141,17 +1150,20 @@ FW_VERSIONS = {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5380 4C2VR503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5300 4C2CL503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5320 4C2VL503',
b'\xf1\x00CK MDPS R 1.00 5.04 57700-J5520 4C4VL504',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT AUS RHD 1.00 1.00 99211-J5500 210622',
b'\xf1\x00CK MFC AT KOR LHD 1.00 1.00 99211-J5500 210622',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.00 99211-J5500 210622',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 99211-J5000 201209',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x87VCNLF11383972DK1vffV\x99\x99\x89\x98\x86eUU\x88wg\x89vfff\x97fff\x99\x87o\xff"\xc1\xf1\x81E30\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E30\x00\x00\x00\x00\x00\x00\x00SCK0T33GH0\xbe`\xfb\xc6',
b'\xf1\x00bcsh8p54 E31\x00\x00\x00\x00\x00\x00\x00SCK0T33NH07\xdf\xf0\xc1',
b'\xf1\x00bcsh8p54 E31\x00\x00\x00\x00\x00\x00\x00SCK0T25KH2B\xfbI\xe2',
b'\xf1\x00bcsh8p54 E31\x00\x00\x00\x00\x00\x00\x00TCK0T33NH0%g~\xd3',
],
},
CAR.PALISADE: {

@ -3,7 +3,7 @@ from collections import defaultdict
from functools import partial
import cereal.messaging as messaging
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr

@ -132,6 +132,7 @@ FW_VERSIONS = {
b'PYFC-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYFD-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYNF-188K2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2E-188K2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2F-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2G-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',

@ -157,6 +157,7 @@ routes = [
CarTestRoute("db04d2c63990e3ba|2023-02-08--16-52-39", HYUNDAI.KIA_NIRO_HEV_2ND_GEN),
CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
CarTestRoute("192283cdbb7a58c2|2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN),
CarTestRoute("09559f1fcaed4704|2023-11-16--02-24-57", HYUNDAI.KIA_SPORTAGE_5TH_GEN), # openpilot longitudinal
CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA),
CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.ELANTRA_GT_I30),
CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021),

@ -3,6 +3,7 @@ import capnp
import os
import importlib
import pytest
import random
import unittest
from collections import defaultdict, Counter
from typing import List, Optional, Tuple
@ -49,12 +50,10 @@ def get_test_cases() -> List[Tuple[str, Optional[CarTestRoute]]]:
with open(os.path.join(BASEDIR, INTERNAL_SEG_LIST), "r") as f:
seg_list = f.read().splitlines()
cnt = INTERNAL_SEG_CNT or len(seg_list)
seg_list_iter = iter(seg_list[:cnt])
for platform in seg_list_iter:
platform = platform[2:] # get rid of comment
segment_name = SegmentName(next(seg_list_iter))
seg_list_grouped = [(platform[2:], segment) for platform, segment in zip(seg_list[::2], seg_list[1::2], strict=True)]
seg_list_grouped = random.sample(seg_list_grouped, INTERNAL_SEG_CNT or len(seg_list_grouped))
for platform, segment in seg_list_grouped:
segment_name = SegmentName(segment)
test_cases.append((platform, CarTestRoute(segment_name.route_name.canonical_name, platform,
segment=segment_name.segment_num)))
return test_cases
@ -382,7 +381,7 @@ class TestCarModelBase(unittest.TestCase):
@parameterized_class(('car_model', 'test_route'), get_test_cases())
@pytest.mark.xdist_group_class_property('car_model')
@pytest.mark.xdist_group_class_property('test_route')
class TestCarModel(TestCarModelBase):
pass

@ -21,8 +21,8 @@ MAX_USER_TORQUE = 500
# LTA limits
# EPS ignores commands above this angle and causes PCS to fault
MAX_STEER_ANGLE = 94.9461 # deg
MAX_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes
MAX_LTA_ANGLE = 94.9461 # deg
MAX_LTA_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes
class CarController:
@ -71,25 +71,32 @@ class CarController:
apply_angle = actuators.steeringAngleDeg + CS.out.steeringAngleOffsetDeg
# Angular rate limit based on speed
apply_angle = apply_std_steer_angle_limits(apply_angle, self.last_angle, CS.out.vEgo, self.params)
apply_angle = apply_std_steer_angle_limits(apply_angle, self.last_angle, CS.out.vEgoRaw, self.params)
if not lat_active:
apply_angle = CS.out.steeringAngleDeg + CS.out.steeringAngleOffsetDeg
self.last_angle = clip(apply_angle, -MAX_STEER_ANGLE, MAX_STEER_ANGLE)
self.last_angle = clip(apply_angle, -MAX_LTA_ANGLE, MAX_LTA_ANGLE)
self.last_steer = apply_steer
# toyota can trace shows this message at 42Hz, with counter adding alternatively 1 and 2;
# toyota can trace shows STEERING_LKA at 42Hz, with counter adding alternatively 1 and 2;
# sending it at 100Hz seem to allow a higher rate limit, as the rate limit seems imposed
# on consecutive messages
can_sends.append(toyotacan.create_steer_command(self.packer, apply_steer, apply_steer_req))
# STEERING_LTA does not seem to allow more rate by sending faster, and may wind up easier
if self.frame % 2 == 0 and self.CP.carFingerprint in TSS2_CAR:
lta_active = lat_active and self.CP.steerControlType == SteerControlType.angle
# cut steering torque with TORQUE_WIND_DOWN when either EPS torque or driver torque is above
# the threshold, to limit max lateral acceleration and for driver torque blending respectively.
full_torque_condition = (abs(CS.out.steeringTorqueEps) < self.params.STEER_MAX and
abs(CS.out.steeringTorque) < MAX_DRIVER_TORQUE_ALLOWANCE)
setme_x64 = 100 if lta_active and full_torque_condition else 0
can_sends.append(toyotacan.create_lta_steer_command(self.packer, self.last_angle, lta_active, self.frame // 2, setme_x64))
abs(CS.out.steeringTorque) < MAX_LTA_DRIVER_TORQUE_ALLOWANCE)
# TORQUE_WIND_DOWN at 0 ramps down torque at roughly the max down rate of 1500 units/sec
torque_wind_down = 100 if lta_active and full_torque_condition else 0
can_sends.append(toyotacan.create_lta_steer_command(self.packer, self.CP.steerControlType, self.last_angle,
lta_active, self.frame // 2, torque_wind_down))
# *** gas and brake ***
if self.CP.enableGasInterceptor and CC.longActive:

@ -28,12 +28,11 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_ALT_BRAKE
if candidate in ANGLE_CONTROL_CAR:
ret.dashcamOnly = True
ret.steerControlType = SteerControlType.angle
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_LTA
# LTA control can be more delayed and winds up more often
ret.steerActuatorDelay = 0.25
ret.steerActuatorDelay = 0.18
ret.steerLimitTimer = 0.8
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)

@ -17,6 +17,10 @@ class TestToyotaInterfaces(unittest.TestCase):
self.assertTrue(len(ANGLE_CONTROL_CAR - TSS2_CAR) == 0)
self.assertTrue(len(RADAR_ACC_CAR - TSS2_CAR) == 0)
def test_lta_platforms(self):
# At this time, only RAV4 2023 is expected to use LTA/angle control
self.assertEqual(ANGLE_CONTROL_CAR, {CAR.RAV4_TSS2_2023})
def test_tss2_dbc(self):
# We make some assumptions about TSS2 platforms,
# like looking up certain signals only in this DBC

@ -1,3 +1,8 @@
from cereal import car
SteerControlType = car.CarParams.SteerControlType
def create_steer_command(packer, steer, steer_req):
"""Creates a CAN message for the Toyota Steer Command."""
@ -9,15 +14,16 @@ def create_steer_command(packer, steer, steer_req):
return packer.make_can_msg("STEERING_LKA", 0, values)
def create_lta_steer_command(packer, steer_angle, steer_req, frame, setme_x64):
def create_lta_steer_command(packer, steer_control_type, steer_angle, steer_req, frame, torque_wind_down):
"""Creates a CAN message for the Toyota LTA Steer Command."""
values = {
"COUNTER": frame + 128,
"SETME_X1": 1,
"SETME_X3": 3,
"SETME_X1": 1, # suspected LTA feature availability
# 1 for TSS 2.5 cars, 3 for TSS 2.0. Send based on whether we're using LTA for lateral control
"SETME_X3": 1 if steer_control_type == SteerControlType.angle else 3,
"PERCENTAGE": 100,
"SETME_X64": setme_x64,
"TORQUE_WIND_DOWN": torque_wind_down,
"ANGLE": 0,
"STEER_ANGLE_CMD": steer_angle,
"STEER_REQUEST": steer_req,

@ -27,7 +27,8 @@ class CarControllerParams:
# Assuming a steering ratio of 13.7:
# Limit to ~2.0 m/s^3 up (7.5 deg/s), ~3.5 m/s^3 down (13 deg/s) at 75 mph
# Worst case, the low speed limits will allow ~4.0 m/s^3 up (15 deg/s) and ~4.9 m/s^3 down (18 deg/s) at 75 mph,
# however the EPS has its own internal limits at all speeds which are less than that
# however the EPS has its own internal limits at all speeds which are less than that:
# Observed internal torque rate limit on TSS 2.5 Camry and RAV4 is ~1500 units/sec up and down when using LTA
ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 25], angle_v=[0.3, 0.15])
ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 25], angle_v=[0.36, 0.26])

@ -5,7 +5,7 @@ import cereal.messaging as messaging
from panda.python.uds import get_rx_addr_for_tx_addr, FUNCTIONAL_ADDRS
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from openpilot.selfdrive.car.fw_query_definitions import StdQueries
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
VIN_UNKNOWN = "0" * 17
VIN_RE = "[A-HJ-NPR-Z0-9]{17}"

@ -239,7 +239,7 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022-23"),
CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]),
CAR.TIGUAN_MK2: [
VWCarInfo("Volkswagen Tiguan 2018-23"),
VWCarInfo("Volkswagen Tiguan 2018-24"),
VWCarInfo("Volkswagen Tiguan eHybrid 2021-23"),
],
CAR.TOURAN_MK2: VWCarInfo("Volkswagen Touran 2016-23"),
@ -843,6 +843,7 @@ FW_VERSIONS = {
b'\xf1\x8783A907115K \xf1\x890002',
b'\xf1\x8704E906024AP\xf1\x891461',
b'\xf1\x8783A907115 \xf1\x890007',
b'\xf1\x8783A907115Q \xf1\x890001',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158DT\xf1\x893698',
@ -851,6 +852,7 @@ FW_VERSIONS = {
b'\xf1\x8709G927158GD\xf1\x893820',
b'\xf1\x8709G927158GM\xf1\x893936',
b'\xf1\x8709G927158GN\xf1\x893938',
b'\xf1\x8709G927158HB\xf1\x894069',
b'\xf1\x870D9300043 \xf1\x895202',
b'\xf1\x870DL300011N \xf1\x892001',
b'\xf1\x870DL300011N \xf1\x892012',
@ -1265,6 +1267,7 @@ FW_VERSIONS = {
b'\xf1\x875NA907115E \xf1\x890003',
b'\xf1\x875NA907115E \xf1\x890005',
b'\xf1\x875NA906259E \xf1\x890003',
b'\xf1\x8704E906027LD\xf1\x893433',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x870D9300043 \xf1\x895202',
@ -1275,6 +1278,7 @@ FW_VERSIONS = {
b'\xf1\x870GC300014N \xf1\x892801',
b'\xf1\x870GC300019H \xf1\x892806',
b'\xf1\x870GC300046Q \xf1\x892802',
b'\xf1\x870D9300014S \xf1\x895201',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x873Q0959655AP\xf1\x890306\xf1\x82\r11110011110011421111314211',
@ -1284,6 +1288,7 @@ FW_VERSIONS = {
b'\xf1\x873Q0959655CQ\xf1\x890720\xf1\x82\x0e1213111211001205212112052111',
b'\xf1\x873Q0959655DJ\xf1\x890731\xf1\x82\x0e1513001511001205232113052J00',
b'\xf1\x875QF959655AT\xf1\x890755\xf1\x82\x1311110011110011111100010200--1121240749',
b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\x0e1213001211001205212111052100',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6050405',
@ -1299,6 +1304,7 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572T \xf1\x890383',
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572AB\xf1\x890397',
b'\xf1\x872Q0907572M \xf1\x890233',
],
},
CAR.SKODA_OCTAVIA_MK3: {

@ -13,8 +13,8 @@ import cereal.messaging as messaging
from cereal.visionipc import VisionIpcClient, VisionStreamType
from openpilot.common.conversions import Conversions as CV
from panda import ALTERNATIVE_EXPERIENCE
from openpilot.system.swaglog import cloudlog
from openpilot.system.version import is_release_branch, get_short_branch
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_short_branch
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can
from openpilot.selfdrive.controls.lib.lateral_planner import CAMERA_OFFSET
@ -64,15 +64,13 @@ class Controls:
# Setup sockets
self.pm = messaging.PubMaster(['sendcan', 'controlsState', 'carState',
'carControl', 'carEvents', 'carParams'])
'carControl', 'onroadEvents', 'carParams'])
self.sensor_packets = ["accelerometer", "gyroscope"]
self.camera_packets = ["roadCameraState", "driverCameraState", "wideRoadCameraState"]
can_timeout = None if os.environ.get('NO_CAN_TIMEOUT', False) else 20
self.can_sock = messaging.sub_sock('can', timeout=can_timeout)
self.log_sock = messaging.sub_sock('androidLog')
self.can_sock = messaging.sub_sock('can', timeout=20)
self.params = Params()
ignore = self.sensor_packets + ['testJoystick']
@ -82,7 +80,7 @@ class Controls:
'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'testJoystick'] + self.camera_packets + self.sensor_packets,
ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick'])
ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick'], ignore_valid=['testJoystick', ])
if CI is None:
# wait for one pandaState and one CAN packet
@ -90,12 +88,13 @@ class Controls:
get_one_can(self.can_sock)
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") and not is_release_branch()
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled")
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
else:
self.CI, self.CP = CI, CI.CP
self.joystick_mode = self.params.get_bool("JoystickDebugMode") or self.CP.notCar
self.joystick_enabled = self.params.get_bool("JoystickDebugMode")
self.joystick_mode = self.joystick_enabled or self.CP.notCar
# set alternative experiences from parameters
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
@ -115,8 +114,8 @@ class Controls:
car_recognized = self.CP.carName != 'mock'
controller_available = self.CI.CC is not None and not passive and not self.CP.dashcamOnly
self.read_only = not car_recognized or not controller_available or self.CP.dashcamOnly
if self.read_only:
self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly
if self.CP.passive:
safety_config = car.CarParams.SafetyConfig.new_message()
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config]
@ -133,7 +132,7 @@ class Controls:
put_nonblocking("CarParamsPersistent", cp_bytes)
# cleanup old params
if not self.CP.experimentalLongitudinalAvailable or is_release_branch():
if not self.CP.experimentalLongitudinalAvailable:
self.params.remove("ExperimentalLongitudinalEnabled")
if not self.CP.openpilotLongitudinalControl:
self.params.remove("ExperimentalMode")
@ -179,8 +178,6 @@ class Controls:
self.v_cruise_helper = VCruiseHelper(self.CP)
self.recalibrating_seen = False
# TODO: no longer necessary, aside from process replay
self.sm['liveParameters'].valid = True
self.can_log_mono_time = 0
self.startup_event = get_startup_event(car_recognized, controller_available, len(self.CP.carFw) > 0)
@ -193,7 +190,7 @@ class Controls:
set_offroad_alert("Offroad_CarUnrecognized", True)
else:
set_offroad_alert("Offroad_NoFirmware", True)
elif self.read_only:
elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True)
elif self.joystick_mode:
self.events.add(EventName.joystickDebug, static=True)
@ -214,7 +211,7 @@ class Controls:
self.state = State.enabled
def update_events(self, CS):
"""Compute carEvents from carState"""
"""Compute onroadEvents from carState"""
self.events.clear()
@ -229,7 +226,7 @@ class Controls:
return
# no more events while in dashcam mode
if self.read_only:
if self.CP.passive:
return
# Block resume if cruise never previously enabled
@ -374,16 +371,17 @@ class Controls:
else:
self.logged_comm_issue = None
if not self.sm['lateralPlan'].mpcSolutionValid:
self.events.add(EventName.plannerError)
if not self.sm['liveLocationKalman'].posenetOK:
self.events.add(EventName.posenetInvalid)
if not self.sm['liveLocationKalman'].deviceStable:
self.events.add(EventName.deviceFalling)
if not self.sm['liveLocationKalman'].inputsOK:
self.events.add(EventName.locationdTemporaryError)
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY):
self.events.add(EventName.paramsdTemporaryError)
if not (self.CP.notCar and self.joystick_enabled):
if not self.sm['lateralPlan'].mpcSolutionValid:
self.events.add(EventName.plannerError)
if not self.sm['liveLocationKalman'].posenetOK:
self.events.add(EventName.posenetInvalid)
if not self.sm['liveLocationKalman'].deviceStable:
self.events.add(EventName.deviceFalling)
if not self.sm['liveLocationKalman'].inputsOK:
self.events.add(EventName.locationdTemporaryError)
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY):
self.events.add(EventName.paramsdTemporaryError)
# conservative HW alert. if the data or frequency are off, locationd will throw an error
if any((self.sm.frame - self.sm.rcv_frame[s])*DT_CTRL > 10. for s in self.sensor_packets):
@ -444,7 +442,7 @@ class Controls:
if VisionStreamType.VISION_STREAM_WIDE_ROAD not in available_streams:
self.sm.ignore_alive.append('wideRoadCameraState')
if not self.read_only:
if not self.CP.passive:
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
self.initialized = True
@ -618,7 +616,7 @@ class Controls:
lat_plan.curvatures,
lat_plan.curvatureRates)
actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
self.last_actuators, self.steer_limited, self.desired_curvature,
self.steer_limited, self.desired_curvature,
self.desired_curvature_rate, self.sm['liveLocationKalman'])
actuators.curvature = self.desired_curvature
else:
@ -637,7 +635,7 @@ class Controls:
if CC.latActive:
steer = clip(joystick_axes[1], -1, 1)
# max angle is 45 for angle-based cars, max curvature is 0.02
actuators.steer, actuators.steeringAngleDeg, actuators.curvature = steer, steer * 45., steer * -0.02
actuators.steer, actuators.steeringAngleDeg, actuators.curvature = steer, steer * 90., steer * -0.02
lac_log.active = self.active
lac_log.steeringAngleDeg = CS.steeringAngleDeg
@ -749,7 +747,7 @@ class Controls:
if current_alert:
hudControl.visualAlert = current_alert.visual_alert
if not self.read_only and self.initialized:
if not self.CP.passive and self.initialized:
# send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators, can_sends = self.CI.apply(CC, now_nanos)
@ -825,16 +823,18 @@ class Controls:
cs_send.carState.events = car_events
self.pm.send('carState', cs_send)
# carEvents - logged every second or on change
# onroadEvents - logged every second or on change
if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events.names != self.events_prev):
ce_send = messaging.new_message('carEvents', len(self.events))
ce_send.carEvents = car_events
self.pm.send('carEvents', ce_send)
ce_send = messaging.new_message('onroadEvents', len(self.events))
ce_send.valid = True
ce_send.onroadEvents = car_events
self.pm.send('onroadEvents', ce_send)
self.events_prev = self.events.names.copy()
# carParams - logged every 50 seconds (> 1 per segment)
if (self.sm.frame % int(50. / DT_CTRL) == 0):
cp_send = messaging.new_message('carParams')
cp_send.valid = True
cp_send.carParams = self.CP
self.pm.send('carParams', cp_send)
@ -862,7 +862,7 @@ class Controls:
self.update_events(CS)
cloudlog.timestamp("Events updated")
if not self.read_only and self.initialized:
if not self.CP.passive and self.initialized:
# Update control state
self.state_transition(CS)
self.prof.checkpoint("State transition")

@ -17,7 +17,7 @@ class LatControl(ABC):
self.steer_max = 1.0
@abstractmethod
def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk):
def update(self, active, CS, VM, params, steer_limited, desired_curvature, desired_curvature_rate, llk):
pass
def reset(self):

@ -11,7 +11,7 @@ class LatControlAngle(LatControl):
super().__init__(CP, CI)
self.sat_check_min_speed = 5.
def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk):
def update(self, active, CS, VM, params, steer_limited, desired_curvature, desired_curvature_rate, llk):
angle_log = log.ControlsState.LateralAngleState.new_message()
if not active:

@ -17,7 +17,7 @@ class LatControlPID(LatControl):
super().reset()
self.pid.reset()
def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk):
def update(self, active, CS, VM, params, steer_limited, desired_curvature, desired_curvature_rate, llk):
pid_log = log.ControlsState.LateralPIDState.new_message()
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
pid_log.steeringRateDeg = float(CS.steeringRateDeg)

@ -36,7 +36,7 @@ class LatControlTorque(LatControl):
self.torque_params.latAccelOffset = latAccelOffset
self.torque_params.friction = friction
def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk):
def update(self, active, CS, VM, params, steer_limited, desired_curvature, desired_curvature_rate, llk):
pid_log = log.ControlsState.LateralTorqueState.new_message()
if not active:

@ -4,7 +4,7 @@ import time
import numpy as np
from cereal import log
from openpilot.common.numpy_fast import clip
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
# WARNING: imports outside of constants will not trigger a rebuild
from openpilot.selfdrive.modeld.constants import index_function
from openpilot.selfdrive.car.interfaces import ACCEL_MIN

@ -15,7 +15,7 @@ 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 V_CRUISE_MAX, CONTROL_N, get_speed_error
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MIN = -1.2

@ -30,13 +30,11 @@ class TestLatControl(unittest.TestCase):
CS.vEgo = 30
CS.steeringPressed = False
last_actuators = car.CarControl.Actuators.new_message()
params = log.LiveParametersData.new_message()
llk = gen_llk()
for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, last_actuators, False, 1, 0, llk)
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, 0, llk)
self.assertTrue(lac_log.saturated)

@ -4,7 +4,7 @@ import numpy as np
from cereal import car
from openpilot.common.params import Params
from openpilot.common.realtime import Priority, config_realtime_process
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.lib.lateral_planner import LateralPlanner

@ -9,7 +9,7 @@ from cereal import messaging, log, car
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.common.realtime import Ratekeeper, Priority, config_realtime_process
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.common.kalman.simple_kalman import KF1D
@ -270,6 +270,7 @@ class RadarD:
# publish tracks for UI debugging (keep last)
tracks_msg = messaging.new_message('liveTracks', len(self.tracks))
tracks_msg.valid = self.radar_state_valid
for index, tid in enumerate(sorted(self.tracks.keys())):
tracks_msg.liveTracks[index] = {
"trackId": tid,

@ -102,4 +102,4 @@ if __name__ == "__main__":
if not new_fw_versions:
print("No new fw versions found...")
add_fw_versions(brand, platform, new_fw_versions)
add_fw_versions(brand, platform, new_fw_versions)

@ -32,8 +32,8 @@ if __name__ == "__main__":
end_time = max(end_time, msg.logMonoTime)
start_time = min(start_time, msg.logMonoTime)
if msg.which() == 'carEvents':
for e in msg.carEvents:
if msg.which() == 'onroadEvents':
for e in msg.onroadEvents:
cnt_events[e.name] += 1
elif msg.which() == 'controlsState':
if len(alerts) == 0 or alerts[-1][1] != msg.controlsState.alertType:

@ -10,6 +10,8 @@ from openpilot.tools.lib.helpers import save_log
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run process on route and create new logs",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--qlog", help="Use qlog instead of log", action="store_true")
parser.add_argument("--fingerprint", help="The fingerprint to use")
parser.add_argument("route", help="The route name to use")
parser.add_argument("process", help="The process to run")
args = parser.parse_args()
@ -17,10 +19,10 @@ if __name__ == "__main__":
cfg = [c for c in CONFIGS if c.proc_name == args.process][0]
route = Route(args.route)
lr = MultiLogIterator(route.log_paths())
lr = MultiLogIterator(route.qlog_paths() if args.qlog else route.log_paths())
inputs = list(lr)
outputs = replay_process(cfg, inputs)
outputs = replay_process(cfg, inputs, fingerprint=args.fingerprint)
# Remove message generated by the process under test and merge in the new messages
produces = {o.which() for o in outputs}

@ -18,7 +18,7 @@ from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params, put_nonblocking
from openpilot.common.realtime import set_realtime_priority
from openpilot.common.transformations.orientation import rot_from_euler, euler_from_rot
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
MIN_SPEED_FILTER = 15 * CV.MPH_TO_MS
MAX_VEL_ANGLE_STD = np.radians(0.25)
@ -164,7 +164,7 @@ class Calibrator:
write_this_cycle = (self.idx == 0) and (self.block_idx % (INPUTS_WANTED//5) == 5)
if self.param_put and write_this_cycle:
put_nonblocking("CalibrationParams", self.get_msg().to_bytes())
put_nonblocking("CalibrationParams", self.get_msg(True).to_bytes())
def handle_v_ego(self, v_ego: float) -> None:
self.v_ego = v_ego
@ -227,12 +227,13 @@ class Calibrator:
return new_rpy
def get_msg(self) -> capnp.lib.capnp._DynamicStructBuilder:
def get_msg(self, valid: bool) -> capnp.lib.capnp._DynamicStructBuilder:
smooth_rpy = self.get_smooth_rpy()
msg = messaging.new_message('liveCalibration')
liveCalibration = msg.liveCalibration
msg.valid = valid
liveCalibration = msg.liveCalibration
liveCalibration.validBlocks = self.valid_blocks
liveCalibration.calStatus = self.cal_status
liveCalibration.calPerc = min(100 * (self.valid_blocks * BLOCK_SIZE + self.idx) // (INPUTS_NEEDED * BLOCK_SIZE), 100)
@ -250,19 +251,16 @@ class Calibrator:
return msg
def send_data(self, pm: messaging.PubMaster) -> None:
pm.send('liveCalibration', self.get_msg())
def send_data(self, pm: messaging.PubMaster, valid: bool) -> None:
pm.send('liveCalibration', self.get_msg(valid))
def calibrationd_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[messaging.PubMaster] = None) -> NoReturn:
def main() -> NoReturn:
gc.disable()
set_realtime_priority(1)
if sm is None:
sm = messaging.SubMaster(['cameraOdometry', 'carState', 'carParams'], poll=['cameraOdometry'])
if pm is None:
pm = messaging.PubMaster(['liveCalibration'])
pm = messaging.PubMaster(['liveCalibration'])
sm = messaging.SubMaster(['cameraOdometry', 'carState', 'carParams'], poll=['cameraOdometry'])
calibrator = Calibrator(param_put=True)
@ -286,11 +284,7 @@ def calibrationd_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[m
# 4Hz driven by cameraOdometry
if sm.frame % 5 == 0:
calibrator.send_data(pm)
def main(sm: Optional[messaging.SubMaster] = None, pm: Optional[messaging.PubMaster] = None) -> NoReturn:
calibrationd_thread(sm, pm)
calibrator.send_data(pm, sm.all_checks())
if __name__ == "__main__":

@ -5,7 +5,7 @@ from typing import List, Optional, Tuple, Any
from cereal import log
from openpilot.common.params import Params
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
class NPQueue:

@ -7,7 +7,7 @@ import numpy as np
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.locationd.models.constants import ObservationKind
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from rednose.helpers.kalmanfilter import KalmanFilter

@ -12,7 +12,7 @@ from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.numpy_fast import clip
from openpilot.selfdrive.locationd.models.car_kf import CarKalman, ObservationKind, States
from openpilot.selfdrive.locationd.models.constants import GENERATED_DIR
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
MAX_ANGLE_OFFSET_DELTA = 20 * DT_MDL # Max 20 deg/s

@ -60,6 +60,7 @@ class TestLocationdProc(unittest.TestCase):
msg.cameraOdometry.trans = [0.0, 0.0, 0.0]
msg.cameraOdometry.transStd = [0.0, 0.0, 0.0]
msg.logMonoTime = t
msg.valid = True
return msg
def test_params_gps(self):

@ -10,7 +10,7 @@ from cereal import car, log
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.locationd.helpers import PointBuckets, ParameterEstimator, cache_points_onexit

@ -9,7 +9,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.spinner import Spinner
from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import AGNOS
from openpilot.system.swaglog import cloudlog, add_file_handler
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import is_dirty
MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9

@ -19,7 +19,7 @@ from openpilot.selfdrive.manager.helpers import unblock_stdout, write_onroad_par
from openpilot.selfdrive.manager.process import ensure_running
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.system.swaglog import cloudlog, add_file_handler
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \
get_normalized_origin, terms_version, training_version, \
is_tested_branch, is_release_branch
@ -37,6 +37,8 @@ def manager_init() -> None:
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
if is_release_branch():
params.clear_all(ParamKeyType.DEVELOPMENT_ONLY)
default_params: List[Tuple[str, Union[str, bytes]]] = [
("CompletedTrainingVersion", "0"),
@ -169,7 +171,7 @@ def manager_thread() -> None:
cloudlog.debug(running)
# send managerState
msg = messaging.new_message('managerState')
msg = messaging.new_message('managerState', valid=True)
msg.managerState.processes = [p.get_process_state_msg() for p in managed_processes.values()]
pm.send('managerState', msg)

@ -15,7 +15,7 @@ import cereal.messaging as messaging
import openpilot.selfdrive.sentry as sentry
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
WATCHDOG_FN = "/dev/shm/wd_"
ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None

@ -60,7 +60,7 @@ procs = [
PythonProcess("navmodeld", "selfdrive.modeld.navmodeld", only_onroad),
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)),
NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"], only_onroad),
PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad),
NativeProcess("locationd", "selfdrive/locationd", ["./locationd"], only_onroad),
NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], always_run, enabled=False),
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
@ -84,6 +84,7 @@ procs = [
# debug procs
NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar),
PythonProcess("webrtcd", "system.webrtc.webrtcd", notcar),
PythonProcess("webjoystick", "tools.bodyteleop.web", notcar),
]

@ -11,7 +11,7 @@ from typing import Tuple, Dict
from cereal import messaging
from cereal.messaging import PubMaster, SubMaster
from cereal.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params
from openpilot.common.realtime import set_realtime_priority
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime
@ -101,7 +101,7 @@ def fill_driver_state(msg, ds_result: DriverStateResult):
def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts: int, execution_time: float, dsp_execution_time: float):
model_result = ctypes.cast(model_output.ctypes.data, ctypes.POINTER(DMonitoringModelResult)).contents
msg = messaging.new_message('driverStateV2')
msg = messaging.new_message('driverStateV2', valid=True)
ds = msg.driverStateV2
ds.frameId = frame_id
ds.modelExecutionTime = execution_time

@ -9,7 +9,7 @@ from typing import Dict, Optional
from setproctitle import setproctitle
from cereal.messaging import PubMaster, SubMaster
from cereal.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL
from openpilot.common.numpy_fast import interp

@ -10,7 +10,7 @@ from typing import Tuple, Dict
from cereal import messaging
from cereal.messaging import PubMaster, SubMaster
from cereal.visionipc import VisionIpcClient, VisionStreamType
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params
from openpilot.common.realtime import set_realtime_priority
from openpilot.selfdrive.modeld.constants import ModelConstants

@ -3,7 +3,6 @@ import gc
import cereal.messaging as messaging
from cereal import car
from cereal import log
from openpilot.common.params import Params, put_bool_nonblocking
from openpilot.common.realtime import set_realtime_priority
from openpilot.selfdrive.controls.lib.events import Events
@ -19,18 +18,12 @@ def dmonitoringd_thread():
driver_status = DriverStatus(rhd_saved=Params().get_bool("IsRhdDetected"))
sm['liveCalibration'].calStatus = log.LiveCalibrationData.Status.invalid
sm['liveCalibration'].rpyCalib = [0, 0, 0]
sm['carState'].buttonEvents = []
sm['carState'].standstill = True
v_cruise_last = 0
driver_engaged = False
# 10Hz <- dmonitoringmodeld
while True:
sm.update()
if not sm.updated['driverStateV2']:
continue
@ -48,7 +41,9 @@ def dmonitoringd_thread():
# Get data from dmonitoringmodeld
events = Events()
driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled)
if sm.all_checks():
driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled)
# Block engaging after max number of distrations
if driver_status.terminal_alert_cnt >= driver_status.settings._MAX_TERMINAL_ALERTS or \
@ -59,7 +54,7 @@ def dmonitoringd_thread():
driver_status.update_events(events, driver_engaged, sm['controlsState'].enabled, sm['carState'].standstill)
# build driverMonitoringState packet
dat = messaging.new_message('driverMonitoringState')
dat = messaging.new_message('driverMonitoringState', valid=sm.all_checks())
dat.driverMonitoringState = {
"events": events.to_msg(),
"faceDetected": driver_status.face_detected,

@ -15,7 +15,7 @@ from openpilot.selfdrive.navd.helpers import (Coordinate, coordinate_from_param,
distance_along_geometry, maxspeed_to_ms,
minimum_distance,
parse_banner_instructions)
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
REROUTE_DISTANCE = 25
MANEUVER_TRANSITION_THRESHOLD = 10
@ -196,7 +196,7 @@ class RouteEngine:
self.send_route()
def send_instruction(self):
msg = messaging.new_message('navInstruction')
msg = messaging.new_message('navInstruction', valid=True)
if self.step_idx is None:
msg.valid = False
@ -302,7 +302,7 @@ class RouteEngine:
for path in self.route_geometry:
coords += [c.as_dict() for c in path]
msg = messaging.new_message('navRoute')
msg = messaging.new_message('navRoute', valid=True)
msg.navRoute.coordinates = coords
self.pm.send('navRoute', msg)

@ -6,7 +6,7 @@ from sentry_sdk.integrations.threading import ThreadingIntegration
from openpilot.common.params import Params
from openpilot.selfdrive.athena.registration import is_registered_device
from openpilot.system.hardware import HARDWARE, PC
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_branch, get_commit, get_origin, get_version, \
is_comma_remote, is_dirty, is_tested_branch

@ -10,7 +10,7 @@ from typing import NoReturn, Union, List, Dict
from openpilot.common.params import Params
from cereal.messaging import SubMaster
from openpilot.system.hardware.hw import Paths
from openpilot.system.swaglog import cloudlog
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import HARDWARE
from openpilot.common.file_helpers import atomic_write_in_dir
from openpilot.system.version import get_normalized_origin, get_short_branch, get_short_version, is_dirty

@ -8,4 +8,4 @@ from openpilot.common.prefix import OpenpilotPrefix
with OpenpilotPrefix():
ret = subprocess.call(sys.argv[1:])
exit(ret)
exit(ret)

@ -17,7 +17,7 @@ fi
source $SCRIPT_DIR/docker_common.sh $1 "$TAG_SUFFIX"
DOCKER_BUILDKIT=1 docker buildx build --platform $PLATFORM --load --cache-to type=inline --cache-from type=registry,ref=$REMOTE_TAG -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR
DOCKER_BUILDKIT=1 docker buildx build --pull --platform $PLATFORM --load --cache-to type=inline --cache-from type=registry,ref=$REMOTE_TAG -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR
if [ -n "$PUSH_IMAGE" ]; then
docker push $REMOTE_TAG

@ -71,4 +71,4 @@ def with_processes(processes, init_time=0, ignore_stopped=None):
def noop(*args, **kwargs):
pass
pass

@ -1,10 +1,8 @@
#!/usr/bin/env python3
import itertools
import os
from parameterized import parameterized_class
import unittest
from parameterized import parameterized_class
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import STOP_DISTANCE
from openpilot.selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver
@ -150,18 +148,6 @@ class LongitudinalControl(unittest.TestCase):
e2e: bool
force_decel: bool
@classmethod
def setUpClass(cls):
os.environ['SIMULATION'] = "1"
os.environ['SKIP_FW_QUERY'] = "1"
os.environ['NO_CAN_TIMEOUT'] = "1"
def setUp(self):
params = Params()
params.clear_all()
params.put_bool("Passive", bool(os.getenv("PASSIVE")))
params.put_bool("OpenpilotEnabledToggle", True)
def test_maneuver(self):
for maneuver in create_maneuvers({"e2e": self.e2e, "force_decel": self.force_decel}):
with self.subTest(title=maneuver.title, e2e=maneuver.e2e, force_decel=maneuver.force_decel):

@ -31,7 +31,7 @@ optional arguments:
--blacklist-procs BLACKLIST_PROCS Blacklist given processes from the test (e.g. controlsd)
--blacklist-cars BLACKLIST_CARS Blacklist given cars from the test (e.g. HONDA)
--ignore-fields IGNORE_FIELDS Extra fields or msgs to ignore (e.g. carState.events)
--ignore-msgs IGNORE_MSGS Msgs to ignore (e.g. carEvents)
--ignore-msgs IGNORE_MSGS Msgs to ignore (e.g. onroadEvents)
--update-refs Updates reference logs using current commit
--upload-only Skips testing processes and uploads logs from previous test run
```

@ -1,37 +0,0 @@
import pytest
from openpilot.selfdrive.test.process_replay.helpers import ALL_PROCS
from openpilot.selfdrive.test.process_replay.test_processes import ALL_CARS
def pytest_addoption(parser: pytest.Parser):
parser.addoption("--whitelist-procs", type=str, nargs="*", default=ALL_PROCS,
help="Whitelist given processes from the test (e.g. controlsd)")
parser.addoption("--whitelist-cars", type=str, nargs="*", default=ALL_CARS,
help="Whitelist given cars from the test (e.g. HONDA)")
parser.addoption("--blacklist-procs", type=str, nargs="*", default=[],
help="Blacklist given processes from the test (e.g. controlsd)")
parser.addoption("--blacklist-cars", type=str, nargs="*", default=[],
help="Blacklist given cars from the test (e.g. HONDA)")
parser.addoption("--ignore-fields", type=str, nargs="*", default=[],
help="Extra fields or msgs to ignore (e.g. carState.events)")
parser.addoption("--ignore-msgs", type=str, nargs="*", default=[],
help="Msgs to ignore (e.g. carEvents)")
parser.addoption("--update-refs", action="store_true",
help="Updates reference logs using current commit")
parser.addoption("--upload-only", action="store_true",
help="Skips testing processes and uploads logs from previous test run")
parser.addoption("--long-diff", action="store_true",
help="Outputs diff in long format")
@pytest.fixture(scope="class", autouse=True)
def process_replay_test_arguments(request):
if hasattr(request.cls, "segment"): # check if a subclass of TestProcessReplayBase
request.cls.tested_procs = list(set(request.config.getoption("--whitelist-procs")) - set(request.config.getoption("--blacklist-procs")))
request.cls.tested_cars = list({c.upper() for c in set(request.config.getoption("--whitelist-cars")) - set(request.config.getoption("--blacklist-cars"))})
request.cls.ignore_fields = request.config.getoption("--ignore-fields")
request.cls.ignore_msgs = request.config.getoption("--ignore-msgs")
request.cls.upload_only = request.config.getoption("--upload-only")
request.cls.update_refs = request.config.getoption("--update-refs")
request.cls.long_diff = request.config.getoption("--long-diff")

@ -1,150 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import unittest
from parameterized import parameterized
from typing import Optional, Union, List
from openpilot.selfdrive.test.openpilotci import get_url, upload_file
from openpilot.selfdrive.test.process_replay.compare_logs import compare_logs, format_process_diff
from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, PROC_REPLAY_DIR, FAKEDATA, replay_process
from openpilot.system.version import get_commit
from openpilot.tools.lib.filereader import FileReader
from openpilot.tools.lib.helpers import save_log
from openpilot.tools.lib.logreader import LogReader, LogIterable
BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/"
REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit")
EXCLUDED_PROCS = {"modeld", "dmonitoringmodeld"}
def get_log_data(segment):
r, n = segment.rsplit("--", 1)
with FileReader(get_url(r, n)) as f:
return f.read()
ALL_PROCS = sorted({cfg.proc_name for cfg in CONFIGS if cfg.proc_name not in EXCLUDED_PROCS})
PROC_TO_CFG = {cfg.proc_name: cfg for cfg in CONFIGS}
cpu_count = os.cpu_count() or 1
class TestProcessReplayBase(unittest.TestCase):
"""
Base class that replays all processes within test_proceses from a segment,
and puts the log messages in self.log_msgs for analysis by other tests.
"""
segment: Optional[Union[str, LogIterable]] = None
tested_procs: List[str] = ALL_PROCS
@classmethod
def setUpClass(cls, create_logs=True):
if "Base" in cls.__name__:
raise unittest.SkipTest("skipping base class")
if isinstance(cls.segment, str):
cls.log_reader = LogReader.from_bytes(get_log_data(cls.segment))
else:
cls.log_reader = cls.segment
if create_logs:
cls._create_log_msgs()
@classmethod
def _run_replay(cls, cfg):
try:
return replay_process(cfg, cls.log_reader, disable_progress=True)
except Exception as e:
raise Exception(f"failed on segment: {cls.segment} \n{e}") from e
@classmethod
def _create_log_msgs(cls):
cls.log_msgs = {}
cls.proc_cfg = {}
for proc in cls.tested_procs:
cfg = PROC_TO_CFG[proc]
log_msgs = cls._run_replay(cfg)
cls.log_msgs[proc] = log_msgs
cls.proc_cfg[proc] = cfg
class TestProcessReplayDiffBase(TestProcessReplayBase):
"""
Base class for checking for diff between process outputs.
"""
update_refs = False
upload_only = False
long_diff = False
ignore_msgs: List[str] = []
ignore_fields: List[str] = []
def setUp(self):
super().setUp()
if self.upload_only:
raise unittest.SkipTest("skipping test, uploading only")
@classmethod
def setUpClass(cls):
super().setUpClass(not cls.upload_only)
if cls.long_diff:
cls.maxDiff = None
os.makedirs(os.path.dirname(FAKEDATA), exist_ok=True)
cls.cur_commit = get_commit()
cls.assertNotEqual(cls.cur_commit, None, "Couldn't get current commit")
cls.upload = cls.update_refs or cls.upload_only
try:
with open(REF_COMMIT_FN) as f:
cls.ref_commit = f.read().strip()
except FileNotFoundError:
print("Couldn't find reference commit")
sys.exit(1)
cls._create_ref_log_msgs()
@classmethod
def _create_ref_log_msgs(cls):
cls.ref_log_msgs = {}
for proc in cls.tested_procs:
cur_log_fn = os.path.join(FAKEDATA, f"{cls.segment}_{proc}_{cls.cur_commit}.bz2")
if cls.update_refs: # reference logs will not exist if routes were just regenerated
ref_log_path = get_url(*cls.segment.rsplit("--", 1))
else:
ref_log_fn = os.path.join(FAKEDATA, f"{cls.segment}_{proc}_{cls.ref_commit}.bz2")
ref_log_path = ref_log_fn if os.path.exists(ref_log_fn) else BASE_URL + os.path.basename(ref_log_fn)
if not cls.upload_only:
save_log(cur_log_fn, cls.log_msgs[proc])
cls.ref_log_msgs[proc] = list(LogReader(ref_log_path))
if cls.upload:
assert os.path.exists(cur_log_fn), f"Cannot find log to upload: {cur_log_fn}"
upload_file(cur_log_fn, os.path.basename(cur_log_fn))
os.remove(cur_log_fn)
@parameterized.expand(ALL_PROCS)
def test_process_diff(self, proc):
if proc not in self.tested_procs:
raise unittest.SkipTest(f"{proc} was not requested to be tested")
cfg = self.proc_cfg[proc]
log_msgs = self.log_msgs[proc]
ref_log_msgs = self.ref_log_msgs[proc]
diff = compare_logs(ref_log_msgs, log_msgs, self.ignore_fields + cfg.ignore, self.ignore_msgs)
diff_short, diff_long = format_process_diff(diff)
self.assertEqual(len(diff), 0, "\n" + diff_long if self.long_diff else diff_short)

@ -85,6 +85,7 @@ def migrate_peripheralState(lr):
continue
new_msg = messaging.new_message("peripheralState")
new_msg.valid = msg.valid
new_msg.logMonoTime = msg.logMonoTime
all_msg.append(new_msg.as_reader())
@ -149,6 +150,7 @@ def migrate_carParams(lr, old_logtime=False):
for msg in lr:
if msg.which() == 'carParams':
CP = messaging.new_message('carParams')
CP.valid = True
CP.carParams = msg.carParams.as_builder()
for car_fw in CP.carParams.carFw:
car_fw.brand = CP.carParams.carName

@ -442,8 +442,8 @@ def controlsd_config_callback(params, cfg, lr):
controlsState = msg.controlsState
if initialized:
break
elif msg.which() == "carEvents":
initialized = car.CarEvent.EventName.controlsInitializing not in [e.name for e in msg.carEvents]
elif msg.which() == "onroadEvents":
initialized = car.CarEvent.EventName.controlsInitializing not in [e.name for e in msg.onroadEvents]
assert controlsState is not None and initialized, "controlsState never initialized"
params.put("ReplayControlsState", controlsState.as_builder().to_bytes())
@ -465,8 +465,8 @@ CONFIGS = [
"modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState",
"testJoystick", "liveTorqueParameters", "accelerometer", "gyroscope"
],
subs=["controlsState", "carState", "carControl", "sendcan", "carEvents", "carParams"],
ignore=["logMonoTime", "valid", "controlsState.startMonoTime", "controlsState.cumLagMs"],
subs=["controlsState", "carState", "carControl", "sendcan", "onroadEvents", "carParams"],
ignore=["logMonoTime", "controlsState.startMonoTime", "controlsState.cumLagMs"],
config_callback=controlsd_config_callback,
init_callback=controlsd_fingerprint_callback,
should_recv_callback=controlsd_rcv_callback,
@ -478,7 +478,7 @@ CONFIGS = [
proc_name="radard",
pubs=["can", "carState", "modelV2"],
subs=["radarState", "liveTracks"],
ignore=["logMonoTime", "valid", "radarState.cumLagMs"],
ignore=["logMonoTime", "radarState.cumLagMs"],
init_callback=get_car_params_callback,
should_recv_callback=MessageBasedRcvCallback("can"),
main_pub="can",
@ -487,7 +487,7 @@ CONFIGS = [
proc_name="plannerd",
pubs=["modelV2", "carControl", "carState", "controlsState", "radarState"],
subs=["lateralPlan", "longitudinalPlan", "uiPlan"],
ignore=["logMonoTime", "valid", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime", "lateralPlan.solverExecutionTime"],
ignore=["logMonoTime", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime", "lateralPlan.solverExecutionTime"],
init_callback=get_car_params_callback,
should_recv_callback=FrequencyBasedRcvCallback("modelV2"),
tolerance=NUMPY_TOLERANCE,
@ -496,14 +496,14 @@ CONFIGS = [
proc_name="calibrationd",
pubs=["carState", "cameraOdometry", "carParams"],
subs=["liveCalibration"],
ignore=["logMonoTime", "valid"],
ignore=["logMonoTime"],
should_recv_callback=calibration_rcv_callback,
),
ProcessConfig(
proc_name="dmonitoringd",
pubs=["driverStateV2", "liveCalibration", "carState", "modelV2", "controlsState"],
subs=["driverMonitoringState"],
ignore=["logMonoTime", "valid"],
ignore=["logMonoTime"],
should_recv_callback=FrequencyBasedRcvCallback("driverStateV2"),
tolerance=NUMPY_TOLERANCE,
),
@ -514,7 +514,7 @@ CONFIGS = [
"liveCalibration", "carState", "carParams", "gpsLocation"
],
subs=["liveLocationKalman"],
ignore=["logMonoTime", "valid"],
ignore=["logMonoTime"],
config_callback=locationd_config_pubsub_callback,
tolerance=NUMPY_TOLERANCE,
),
@ -522,7 +522,7 @@ CONFIGS = [
proc_name="paramsd",
pubs=["liveLocationKalman", "carState"],
subs=["liveParameters"],
ignore=["logMonoTime", "valid"],
ignore=["logMonoTime"],
init_callback=get_car_params_callback,
should_recv_callback=FrequencyBasedRcvCallback("liveLocationKalman"),
tolerance=NUMPY_TOLERANCE,

@ -1 +1 @@
dbea36698ba48429b201b138846165eb4c329b92
921222d49db204071f0a7006fc895690e1045b5d

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

Loading…
Cancel
Save