Merge remote-tracking branch 'upstream/master' into check-untyped-funcs

pull/29414/head
Shane Smiskol 2 years ago
commit 94ac47caa4
  1. 1
      .devcontainer/.gitignore
  2. 6
      .devcontainer/Dockerfile
  3. 23
      .devcontainer/devcontainer.json
  4. 16
      .devcontainer/setup_host.sh
  5. 3
      .dockerignore
  6. 16
      .github/workflows/selfdrive_tests.yaml
  7. 20
      .github/workflows/setup/action.yaml
  8. 41
      .github/workflows/tools_tests.yaml
  9. 1
      .gitignore
  10. 18
      .pre-commit-config.yaml
  11. 1
      Dockerfile.openpilot
  12. 2
      Dockerfile.openpilot_base
  13. 2
      Jenkinsfile
  14. 1
      RELEASES.md
  15. 3
      SConstruct
  16. 2
      cereal
  17. 3
      common/SConscript
  18. 4
      common/api/__init__.py
  19. 2
      common/basedir.py
  20. 24
      common/clock.pyx
  21. 4
      common/clutil.cc
  22. 1
      common/clutil.h
  23. 2
      common/gpio.cc
  24. 4
      common/gpio.py
  25. 2
      common/kalman/simple_kalman.py
  26. 8
      common/kalman/tests/test_simple_kalman.py
  27. 3
      common/params.py
  28. 40
      common/ratekeeper.cc
  29. 23
      common/ratekeeper.h
  30. 13
      common/realtime.py
  31. 2
      common/spinner.py
  32. 2
      common/swaglog.h
  33. 1
      common/tests/.gitignore
  34. 4
      common/tests/test_file_helpers.py
  35. 2
      common/tests/test_numpy_fast.py
  36. 2
      common/tests/test_params.py
  37. 17
      common/tests/test_ratekeeper.cc
  38. 2
      common/text_window.py
  39. 2
      common/transformations/camera.py
  40. 6
      common/transformations/coordinates.py
  41. 10
      common/transformations/model.py
  42. 2
      common/transformations/orientation.py
  43. 2
      common/transformations/tests/test_coordinates.py
  44. 2
      common/transformations/tests/test_orientation.py
  45. 30
      common/transformations/transformations.pyx
  46. 2
      common/util.h
  47. 2
      common/watchdog.cc
  48. 11
      docs/CARS.md
  49. 4
      docs/conf.py
  50. 9
      docs/docker/Dockerfile
  51. 2
      laika_repo
  52. 16
      mypy.ini
  53. 2
      panda
  54. 119
      poetry.lock
  55. 38
      pyproject.toml
  56. 6
      release/files_common
  57. 2
      scripts/count_cars.py
  58. 2
      scripts/disable-powersave.py
  59. 2
      scripts/pyqt_demo.py
  60. 10
      scripts/waste.py
  61. BIN
      selfdrive/assets/navigation/direction_turn_slight_left_inactive.png
  62. BIN
      selfdrive/assets/navigation/direction_turn_slight_right_inactive.png
  63. 38
      selfdrive/athena/athenad.py
  64. 8
      selfdrive/athena/manage_athenad.py
  65. 14
      selfdrive/athena/registration.py
  66. 2
      selfdrive/athena/tests/helpers.py
  67. 10
      selfdrive/athena/tests/test_athenad.py
  68. 10
      selfdrive/athena/tests/test_athenad_ping.py
  69. 16
      selfdrive/athena/tests/test_registration.py
  70. 27
      selfdrive/boardd/boardd.cc
  71. 3
      selfdrive/boardd/boardd.h
  72. 2
      selfdrive/boardd/boardd.py
  73. 1
      selfdrive/boardd/panda.cc
  74. 1
      selfdrive/boardd/panda.h
  75. 4
      selfdrive/boardd/panda_comms.h
  76. 10
      selfdrive/boardd/pandad.py
  77. 2
      selfdrive/boardd/set_time.py
  78. 2
      selfdrive/boardd/spi.cc
  79. 12
      selfdrive/boardd/tests/test_boardd_loopback.py
  80. 12
      selfdrive/boardd/tests/test_pandad.py
  81. 38
      selfdrive/car/__init__.py
  82. 10
      selfdrive/car/body/carcontroller.py
  83. 4
      selfdrive/car/body/carstate.py
  84. 8
      selfdrive/car/body/interface.py
  85. 2
      selfdrive/car/body/radar_interface.py
  86. 6
      selfdrive/car/body/values.py
  87. 102
      selfdrive/car/car_helpers.py
  88. 8
      selfdrive/car/chrysler/carcontroller.py
  89. 6
      selfdrive/car/chrysler/carstate.py
  90. 2
      selfdrive/car/chrysler/chryslercan.py
  91. 14
      selfdrive/car/chrysler/interface.py
  92. 6
      selfdrive/car/chrysler/radar_interface.py
  93. 6
      selfdrive/car/chrysler/values.py
  94. 4
      selfdrive/car/disable_ecu.py
  95. 8
      selfdrive/car/docs.py
  96. 2
      selfdrive/car/docs_definitions.py
  97. 8
      selfdrive/car/ecu_addrs.py
  98. 7
      selfdrive/car/fingerprints.py
  99. 8
      selfdrive/car/ford/carcontroller.py
  100. 8
      selfdrive/car/ford/carstate.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1 @@
.Xauthority

@ -0,0 +1,6 @@
FROM ghcr.io/commaai/openpilot-base:latest
# remove gitconfig if exists, since its gonna be replaced by host one
RUN rm -f /root/.gitconfig
RUN apt update && apt install -y vim net-tools usbutils htop ripgrep
RUN pip install ipython jupyter jupyterlab

@ -0,0 +1,23 @@
{
"name": "openpilot devcontainer",
"build": {
"dockerfile": "Dockerfile"
},
"postCreateCommand": "bash -c 'if [[ $DISPLAY == *xquartz* ]]; then echo \"export DISPLAY=host.docker.internal:0\" >> /root/.bashrc; fi'",
"postStartCommand": "git config --file .gitmodules --get-regexp path | awk '{ print $2 }' | xargs -I{} git config --global --add safe.directory \"$PWD/{}\"",
"initializeCommand": ".devcontainer/setup_host.sh",
"privileged": true,
"containerEnv": {
"DISPLAY": "${localEnv:DISPLAY}",
"PYTHONPATH": "${containerWorkspaceFolder}",
"force_color_prompt": "1"
},
"runArgs": [
"--volume=/tmp/.X11-unix:/tmp/.X11-unix",
"--volume=${localWorkspaceFolder}/.devcontainer/.Xauthority:/root/.Xauthority",
"--volume=${localEnv:HOME}/.comma:/root/.comma",
"--volume=/tmp/comma_download_cache:/tmp/comma_download_cache",
"--volume=/tmp/devcontainer_scons_cache:/tmp/scons_cache",
"--shm-size=1G"
]
}

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# setup links to Xauthority
XAUTHORITY_LINK=".devcontainer/.Xauthority"
rm -f $XAUTHORITY_LINK
if [[ -z $XAUTHORITY ]]; then
echo "XAUTHORITY not set. Fallback to ~/.Xauthority ..."
if ! [[ -f $HOME/.Xauthority ]]; then
echo "~/.XAuthority file does not exist. GUI tools may not work properly."
touch $XAUTHORITY_LINK # dummy file to satisfy container volume mount
else
ln -sf $HOME/.Xauthority $XAUTHORITY_LINK
fi
else
ln -sf $XAUTHORITY $XAUTHORITY_LINK
fi

@ -16,6 +16,9 @@
*.so *.so
*.a *.a
venv/
.venv/
notebooks notebooks
phone phone
massivemap massivemap

@ -64,7 +64,6 @@ jobs:
run: | run: |
cd $GITHUB_WORKSPACE cd $GITHUB_WORKSPACE
cp .pre-commit-config.yaml $STRIPPED_DIR cp .pre-commit-config.yaml $STRIPPED_DIR
cp mypy.ini $STRIPPED_DIR
cp pyproject.toml $STRIPPED_DIR cp pyproject.toml $STRIPPED_DIR
cp poetry.lock $STRIPPED_DIR cp poetry.lock $STRIPPED_DIR
cd $STRIPPED_DIR cd $STRIPPED_DIR
@ -118,6 +117,8 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
.env
.venv
~/github_brew_cache_entries.txt ~/github_brew_cache_entries.txt
~/.pyenv ~/.pyenv
~/Library/Caches/pypoetry ~/Library/Caches/pypoetry
@ -149,18 +150,17 @@ jobs:
PYTHONWARNINGS: default PYTHONWARNINGS: default
- name: Build openpilot - name: Build openpilot
run: | run: |
source tools/openpilot_env.sh eval "$(pyenv init --path)"
poetry run scons -j$(nproc) poetry run scons -j$(nproc)
- name: Run tests - name: Run tests
run: | run: |
source tools/openpilot_env.sh eval "$(pyenv init --path)"
export PYTHONPATH=$PWD
poetry run tools/plotjuggler/test_plotjuggler.py poetry run tools/plotjuggler/test_plotjuggler.py
- name: Pre Cache - Cleanup scons cache - name: Pre Cache - Cleanup scons cache
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
run: | run: |
source tools/openpilot_env.sh
rm -rf /tmp/scons_cache/* rm -rf /tmp/scons_cache/*
eval "$(pyenv init --path)"
poetry run scons -j$(nproc) --cache-populate poetry run scons -j$(nproc) --cache-populate
- name: Save scons cache - name: Save scons cache
id: scons-save-cache id: scons-save-cache
@ -276,6 +276,7 @@ jobs:
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py && \ ./selfdrive/ui/tests/test_translations.py && \
./common/tests/test_util && \ ./common/tests/test_util && \
./common/tests/test_ratekeeper && \
./common/tests/test_swaglog && \ ./common/tests/test_swaglog && \
./selfdrive/boardd/tests/test_boardd_usbprotocol && \ ./selfdrive/boardd/tests/test_boardd_usbprotocol && \
./system/loggerd/tests/test_logger &&\ ./system/loggerd/tests/test_logger &&\
@ -306,7 +307,7 @@ jobs:
run: | run: |
${{ env.RUN }} "scons -j$(nproc)" ${{ env.RUN }} "scons -j$(nproc)"
- name: Run replay - name: Run replay
timeout-minutes: 20 timeout-minutes: 30
run: | run: |
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ ${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
coverage xml" coverage xml"
@ -382,8 +383,7 @@ jobs:
- name: Test car models - name: Test car models
timeout-minutes: 25 timeout-minutes: 25
run: | run: |
${{ env.RUN }} "coverage run -m pytest selfdrive/car/tests/test_models.py && \ ${{ env.RUN }} "pytest --cov --cov-report=xml -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \
coverage xml && \
chmod -R 777 /tmp/comma_download_cache" chmod -R 777 /tmp/comma_download_cache"
env: env:
NUM_JOBS: 5 NUM_JOBS: 5

@ -1,10 +1,21 @@
name: 'openpilot env setup' name: 'openpilot env setup'
inputs:
setup_docker_scons_cache:
description: 'Whether or not to build the scons-cache docker image'
required: false
default: 'false'
git_lfs:
description: 'Whether or not to pull the git lfs'
required: false
default: 'true'
runs: runs:
using: "composite" using: "composite"
steps: steps:
# do this after checkout to ensure our custom LFS config is used to pull from GitLab # do this after checkout to ensure our custom LFS config is used to pull from GitLab
- shell: bash - shell: bash
if: ${{ inputs.git_lfs == 'true' }}
run: git lfs pull run: git lfs pull
# build cache # build cache
@ -21,7 +32,14 @@ runs:
restore-keys: | restore-keys: |
scons-${{ env.CACHE_COMMIT_DATE }}- scons-${{ env.CACHE_COMMIT_DATE }}-
scons- scons-
- id: setup-scons-cache-docker
name: Sets up a docker image with scons cache that can by mounted as a buildkit cache mount
shell: bash
if: ${{ inputs.setup_docker_scons_cache == 'true' }}
run: |
cp selfdrive/test/Dockerfile.scons_cache ~
cd ~
DOCKER_BUILDKIT=1 docker build -t scons-cache -f Dockerfile.scons_cache .
# build our docker image # build our docker image
- shell: bash - shell: bash
run: eval ${{ env.BUILD }} run: eval ${{ env.BUILD }}

@ -57,14 +57,14 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Pull LFS - uses: ./.github/workflows/setup
run: git lfs pull with:
- name: Build base image setup_docker_scons_cache: true
run: eval "$BUILD"
- name: Build base cl image - name: Build base cl image
run: eval "$BUILD_CL" run: eval "$BUILD_CL"
- name: Build simulator image - name: Build simulator image
run: DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/sim/Dockerfile.sim . run: |
DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/sim/Dockerfile.sim .
- name: Push to container registry - name: Push to container registry
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: | run: |
@ -75,17 +75,36 @@ jobs:
name: build docs name: build docs
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 45 timeout-minutes: 45
env:
BUILD: |
docker pull $DOCKER_REGISTRY/openpilot-docs:latest
DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/openpilot-docs:latest -t $DOCKER_REGISTRY/openpilot-docs:latest -f docs/docker/Dockerfile .
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Build docker container - uses: ./.github/workflows/setup
run: | with:
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/openpilot-docs-base:latest -t $DOCKER_REGISTRY/openpilot-docs-base:latest -f docs/docker/Dockerfile --target openpilot-docs-base . setup_docker_scons_cache: true
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/openpilot-docs-base:latest --cache-from $DOCKER_REGISTRY/openpilot-docs:latest -t $DOCKER_REGISTRY/openpilot-docs:latest -f docs/docker/Dockerfile . git_lfs: false
- name: Push docker container - name: Push docker container
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: | run: |
$DOCKER_LOGIN $DOCKER_LOGIN
docker push $DOCKER_REGISTRY/openpilot-docs-base:latest docker push $DOCKER_REGISTRY/openpilot-docs:latest
docker push $DOCKER_REGISTRY/openpilot-docs:latest
devcontainer:
name: devcontainer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Setup Dev Container CLI
run: npm install -g @devcontainers/cli
- name: Build dev container image
run: devcontainer build --workspace-folder .
- name: Run dev container
run: devcontainer up --workspace-folder .
- name: Test environment
run: devcontainer exec --workspace-folder . scons --dry-run

1
.gitignore vendored

@ -1,4 +1,5 @@
venv/ venv/
.venv/
.env .env
.clang-format .clang-format
.DS_Store .DS_Store

@ -35,7 +35,7 @@ repos:
args: ['--explicit-package-bases'] args: ['--explicit-package-bases']
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.284 rev: v0.0.285
hooks: hooks:
- id: ruff - id: ruff
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
@ -53,6 +53,18 @@ repos:
- --quiet - --quiet
- --force - --force
- -j8 - -j8
- repo: https://github.com/cpplint/cpplint
rev: 1.6.1
hooks:
- id: cpplint
exclude: '^(third_party/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(generated/)'
args:
- --quiet
- --counting=total
- --linelength=240
# https://google.github.io/styleguide/cppguide.html
# relevant rules are whitelisted, see all options with: cpplint --filter=
- --filter=-build,-legal,-readability,-runtime,-whitespace,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+readability/braces
- repo: local - repo: local
hooks: hooks:
- id: test_translations - id: test_translations
@ -61,10 +73,10 @@ repos:
language: script language: script
pass_filenames: false pass_filenames: false
- repo: https://github.com/python-poetry/poetry - repo: https://github.com/python-poetry/poetry
rev: '1.5.0' rev: '1.6.0'
hooks: hooks:
- id: poetry-check - id: poetry-check
- id: poetry-lock - id: poetry-lock
name: validate poetry lock name: validate poetry lock
args: args:
- --check - --check

@ -10,6 +10,7 @@ WORKDIR ${OPENPILOT_PATH}
COPY SConstruct ${OPENPILOT_PATH} COPY SConstruct ${OPENPILOT_PATH}
COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./third_party ${OPENPILOT_PATH}/third_party COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./site_scons ${OPENPILOT_PATH}/site_scons COPY ./site_scons ${OPENPILOT_PATH}/site_scons
COPY ./laika ${OPENPILOT_PATH}/laika COPY ./laika ${OPENPILOT_PATH}/laika

@ -18,7 +18,7 @@ ENV PYENV_ROOT="/root/.pyenv"
ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH" ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
COPY pyproject.toml poetry.lock .python-version /tmp/ COPY pyproject.toml poetry.lock .python-version /tmp/
COPY tools/ubuntu_setup.sh tools/install_python_dependencies.sh /tmp/tools/ COPY tools/ubuntu_setup.sh tools/install_ubuntu_dependencies.sh tools/install_python_dependencies.sh /tmp/tools/
RUN cd /tmp && \ RUN cd /tmp && \
tools/ubuntu_setup.sh && \ tools/ubuntu_setup.sh && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \

2
Jenkinsfile vendored

@ -158,7 +158,7 @@ pipeline {
sh "git lfs pull" sh "git lfs pull"
// tests that our build system's dependencies are configured properly, needs a machine with lots of cores // tests that our build system's dependencies are configured properly, needs a machine with lots of cores
sh "scons --clean && scons --no-cache --random -j42" sh "scons --clean && scons --no-cache --random -j42"
sh "INTERNAL_SEG_CNT=400 INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt FILEREADER_CACHE=1 \ sh "INTERNAL_SEG_CNT=500 INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt FILEREADER_CACHE=1 \
pytest -n42 --dist=loadscope selfdrive/car/tests/test_models.py" pytest -n42 --dist=loadscope selfdrive/car/tests/test_models.py"
} }

@ -1,5 +1,6 @@
Version 0.9.5 (202X-XX-XX) Version 0.9.5 (202X-XX-XX)
======================== ========================
* Kia Sorento Hybrid 2023 support thanks to sunnyhaibin!
* Lexus IS 2023 support thanks to L3R5! * Lexus IS 2023 support thanks to L3R5!
Version 0.9.4 (2023-07-27) Version 0.9.4 (2023-07-27)

@ -80,7 +80,7 @@ assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
lenv = { lenv = {
"PATH": os.environ['PATH'], "PATH": os.environ['PATH'],
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath], "LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
"PYTHONPATH": Dir("#").abspath, "PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath, "ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath, "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
@ -334,7 +334,6 @@ qt_flags = [
qt_env['CXXFLAGS'] += qt_flags qt_env['CXXFLAGS'] += qt_flags
qt_env['LIBPATH'] += ['#selfdrive/ui'] qt_env['LIBPATH'] += ['#selfdrive/ui']
qt_env['LIBS'] = qt_libs qt_env['LIBS'] = qt_libs
qt_env['QT3_MOCHPREFIX'] = cache_dir + '/moc_files/moc_'
if GetOption("clazy"): if GetOption("clazy"):
checks = [ checks = [

@ -1 +1 @@
Subproject commit 2077dd1a1b0054713102aee8197434f8cfa07de7 Subproject commit 736a4cce2c51cb73465e88dae6ff4cec5b9c3b43

@ -12,6 +12,7 @@ common_libs = [
'util.cc', 'util.cc',
'i2c.cc', 'i2c.cc',
'watchdog.cc', 'watchdog.cc',
'ratekeeper.cc'
] ]
if arch != "Darwin": if arch != "Darwin":
@ -29,7 +30,7 @@ Export('_common', '_gpucommon')
if GetOption('test'): if GetOption('test'):
env.Program('tests/test_util', ['tests/test_util.cc'], LIBS=[_common]) env.Program('tests/test_util', ['tests/test_util.cc'], LIBS=[_common])
env.Program('tests/test_swaglog', ['tests/test_swaglog.cc'], LIBS=[_common, 'json11', 'zmq', 'pthread']) env.Program('tests/test_swaglog', ['tests/test_swaglog.cc'], LIBS=[_common, 'json11', 'zmq', 'pthread'])
env.Program('tests/test_ratekeeper', ['tests/test_ratekeeper.cc'], LIBS=[_common, 'json11', 'zmq', 'pthread'])
# Cython # Cython
envCython.Program('clock.so', 'clock.pyx')
envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11']) envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])

@ -2,8 +2,8 @@ import jwt
import os import os
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
from common.basedir import PERSIST from openpilot.common.basedir import PERSIST
from system.version import get_version from openpilot.system.version import get_version
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')

@ -1,7 +1,7 @@
import os import os
from pathlib import Path from pathlib import Path
from system.hardware import PC from openpilot.system.hardware import PC
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__)), "../"))

@ -1,24 +0,0 @@
# distutils: language = c++
# cython: language_level = 3
from posix.time cimport clock_gettime, timespec, CLOCK_MONOTONIC_RAW, clockid_t
IF UNAME_SYSNAME == "Darwin":
# Darwin doesn't have a CLOCK_BOOTTIME
CLOCK_BOOTTIME = CLOCK_MONOTONIC_RAW
ELSE:
from posix.time cimport CLOCK_BOOTTIME
cdef double readclock(clockid_t clock_id):
cdef timespec ts
cdef double current
clock_gettime(clock_id, &ts)
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
return current
def monotonic_time():
return readclock(CLOCK_MONOTONIC_RAW)
def sec_since_boot():
return readclock(CLOCK_BOOTTIME)

@ -75,6 +75,10 @@ cl_device_id cl_get_device_id(cl_device_type device_type) {
return nullptr; return nullptr;
} }
cl_context cl_create_context(cl_device_id device_id) {
return CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
}
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) { cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
return cl_program_from_source(ctx, device_id, util::read_file(path), args); return cl_program_from_source(ctx, device_id, util::read_file(path), args);
} }

@ -22,6 +22,7 @@
}) })
cl_device_id cl_get_device_id(cl_device_type device_type); cl_device_id cl_get_device_id(cl_device_type device_type);
cl_context cl_create_context(cl_device_id device_id);
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr); cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args = nullptr); cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args = nullptr);
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args); cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);

@ -1,5 +1,7 @@
#include "common/gpio.h" #include "common/gpio.h"
#include <string>
#ifdef __APPLE__ #ifdef __APPLE__
int gpio_init(int pin_nr, bool output) { int gpio_init(int pin_nr, bool output) {
return 0; return 0;

@ -1,3 +1,4 @@
import os
from functools import lru_cache from functools import lru_cache
from typing import Optional, List from typing import Optional, List
@ -26,6 +27,9 @@ def gpio_read(pin: int) -> Optional[bool]:
return val return val
def gpio_export(pin: int) -> None: def gpio_export(pin: int) -> None:
if os.path.isdir(f"/sys/class/gpio/gpio{pin}"):
return
try: try:
with open("/sys/class/gpio/export", 'w') as f: with open("/sys/class/gpio/export", 'w') as f:
f.write(str(pin)) f.write(str(pin))

@ -1,3 +1,3 @@
# pylint: skip-file # pylint: skip-file
from common.kalman.simple_kalman_impl import KF1D as KF1D from openpilot.common.kalman.simple_kalman_impl import KF1D as KF1D
assert KF1D assert KF1D

@ -3,8 +3,8 @@ import random
import timeit import timeit
import numpy as np import numpy as np
from common.kalman.simple_kalman import KF1D from openpilot.common.kalman.simple_kalman import KF1D
from common.kalman.simple_kalman_old import KF1D as KF1D_old from openpilot.common.kalman.simple_kalman_old import KF1D as KF1D_old
class TestSimpleKalman(unittest.TestCase): class TestSimpleKalman(unittest.TestCase):
@ -54,8 +54,8 @@ class TestSimpleKalman(unittest.TestCase):
setup = """ setup = """
import numpy as np import numpy as np
from common.kalman.simple_kalman import KF1D from openpilot.common.kalman.simple_kalman import KF1D
from common.kalman.simple_kalman_old import KF1D as KF1D_old from openpilot.common.kalman.simple_kalman_old import KF1D as KF1D_old
dt = 0.01 dt = 0.01
x0_0 = 0.0 x0_0 = 0.0

@ -1,4 +1,5 @@
from common.params_pyx import Params, ParamKeyType, UnknownKeyName, put_nonblocking, put_bool_nonblocking # pylint: disable=no-name-in-module, import-error from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName, put_nonblocking, \
put_bool_nonblocking # pylint: disable=no-name-in-module, import-error
assert Params assert Params
assert ParamKeyType assert ParamKeyType
assert UnknownKeyName assert UnknownKeyName

@ -0,0 +1,40 @@
#include "common/ratekeeper.h"
#include <algorithm>
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
RateKeeper::RateKeeper(const std::string &name, float rate, float print_delay_threshold)
: name(name),
print_delay_threshold(std::max(0.f, print_delay_threshold)) {
interval = 1 / rate;
last_monitor_time = seconds_since_boot();
next_frame_time = last_monitor_time + interval;
}
bool RateKeeper::keepTime() {
bool lagged = monitorTime();
if (remaining_ > 0) {
util::sleep_for(remaining_ * 1000);
}
return lagged;
}
bool RateKeeper::monitorTime() {
++frame_;
last_monitor_time = seconds_since_boot();
remaining_ = next_frame_time - last_monitor_time;
bool lagged = remaining_ < 0;
if (lagged) {
if (print_delay_threshold > 0 && remaining_ < -print_delay_threshold) {
LOGW("%s lagging by %.2f ms", name.c_str(), -remaining_ * 1000);
}
next_frame_time = last_monitor_time + interval;
} else {
next_frame_time += interval;
}
return lagged;
}

@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include <string>
class RateKeeper {
public:
RateKeeper(const std::string &name, float rate, float print_delay_threshold = 0);
~RateKeeper() {}
bool keepTime();
bool monitorTime();
inline double frame() const { return frame_; }
inline double remaining() const { return remaining_; }
private:
double interval;
double next_frame_time;
double last_monitor_time;
double remaining_ = 0;
float print_delay_threshold = 0;
uint64_t frame_ = 0;
std::string name;
};

@ -5,10 +5,9 @@ import time
from collections import deque from collections import deque
from typing import Optional, List, Union from typing import Optional, List, Union
from setproctitle import getproctitle # pylint: disable=no-name-in-module from setproctitle import getproctitle
from common.clock import sec_since_boot # pylint: disable=no-name-in-module, import-error from openpilot.system.hardware import PC
from system.hardware import PC
# time step for each process # time step for each process
@ -50,13 +49,13 @@ class Ratekeeper:
def __init__(self, rate: float, print_delay_threshold: Optional[float] = 0.0) -> None: def __init__(self, rate: float, print_delay_threshold: Optional[float] = 0.0) -> None:
"""Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative.""" """Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
self._interval = 1. / rate self._interval = 1. / rate
self._next_frame_time = sec_since_boot() + self._interval self._next_frame_time = time.monotonic() + self._interval
self._print_delay_threshold = print_delay_threshold self._print_delay_threshold = print_delay_threshold
self._frame = 0 self._frame = 0
self._remaining = 0.0 self._remaining = 0.0
self._process_name = getproctitle() self._process_name = getproctitle()
self._dts = deque([self._interval], maxlen=100) self._dts = deque([self._interval], maxlen=100)
self._last_monitor_time = sec_since_boot() self._last_monitor_time = time.monotonic()
@property @property
def frame(self) -> int: def frame(self) -> int:
@ -82,11 +81,11 @@ class Ratekeeper:
# this only monitor the cumulative lag, but does not enforce a rate # this only monitor the cumulative lag, but does not enforce a rate
def monitor_time(self) -> bool: def monitor_time(self) -> bool:
prev = self._last_monitor_time prev = self._last_monitor_time
self._last_monitor_time = sec_since_boot() self._last_monitor_time = time.monotonic()
self._dts.append(self._last_monitor_time - prev) self._dts.append(self._last_monitor_time - prev)
lagged = False lagged = False
remaining = self._next_frame_time - sec_since_boot() remaining = self._next_frame_time - time.monotonic()
self._next_frame_time += self._interval self._next_frame_time += self._interval
if self._print_delay_threshold is not None and remaining < -self._print_delay_threshold: if self._print_delay_threshold is not None and remaining < -self._print_delay_threshold:
print(f"{self._process_name} lagging by {-remaining * 1000:.2f} ms") print(f"{self._process_name} lagging by {-remaining * 1000:.2f} ms")

@ -1,6 +1,6 @@
import os import os
import subprocess import subprocess
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
class Spinner(): class Spinner():

@ -44,7 +44,7 @@ void cloudlog_te(int levelnum, const char* filename, int lineno, const char* fun
int __millis = (millis); \ int __millis = (millis); \
uint64_t __ts = nanos_since_boot(); \ uint64_t __ts = nanos_since_boot(); \
\ \
if (!__begin) __begin = __ts; \ if (!__begin) { __begin = __ts; } \
\ \
if (__begin + __millis*1000000ULL < __ts) { \ if (__begin + __millis*1000000ULL < __ts) { \
if (__missed) { \ if (__missed) { \

@ -1,2 +1,3 @@
test_ratekeeper
test_util test_util
test_swaglog test_swaglog

@ -2,8 +2,8 @@ import os
import unittest import unittest
from uuid import uuid4 from uuid import uuid4
from common.file_helpers import atomic_write_on_fs_tmp from openpilot.common.file_helpers import atomic_write_on_fs_tmp
from common.file_helpers import atomic_write_in_dir from openpilot.common.file_helpers import atomic_write_in_dir
class TestFileHelpers(unittest.TestCase): class TestFileHelpers(unittest.TestCase):

@ -1,7 +1,7 @@
import numpy as np import numpy as np
import unittest import unittest
from common.numpy_fast import interp from openpilot.common.numpy_fast import interp
class InterpTest(unittest.TestCase): class InterpTest(unittest.TestCase):

@ -6,7 +6,7 @@ import shutil
import uuid import uuid
import unittest import unittest
from common.params import Params, ParamKeyType, UnknownKeyName, put_nonblocking, put_bool_nonblocking from openpilot.common.params import Params, ParamKeyType, UnknownKeyName, put_nonblocking, put_bool_nonblocking
class TestParams(unittest.TestCase): class TestParams(unittest.TestCase):
def setUp(self): def setUp(self):

@ -0,0 +1,17 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "common/ratekeeper.h"
#include "common/timing.h"
#include "common/util.h"
TEST_CASE("RateKeeper") {
float freq = GENERATE(10, 50, 100);
RateKeeper rk("Test RateKeeper", freq);
for (int i = 0; i < freq; ++i) {
double begin = seconds_since_boot();
util::sleep_for(util::random_int(0, 1000.0 / freq - 1));
bool lagged = rk.keepTime();
REQUIRE(std::abs(seconds_since_boot() - begin - (1 / freq)) < 1e-3);
REQUIRE(lagged == false);
}
}

@ -2,7 +2,7 @@
import os import os
import time import time
import subprocess import subprocess
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
class TextWindow: class TextWindow:

@ -1,6 +1,6 @@
import numpy as np import numpy as np
import common.transformations.orientation as orient import openpilot.common.transformations.orientation as orient
## -- hardcoded hardware params -- ## -- hardcoded hardware params --
eon_f_focal_length = 910.0 eon_f_focal_length = 910.0

@ -1,8 +1,8 @@
# pylint: skip-file # pylint: skip-file
from common.transformations.orientation import numpy_wrap from openpilot.common.transformations.orientation import numpy_wrap
from common.transformations.transformations import (ecef2geodetic_single, from openpilot.common.transformations.transformations import (ecef2geodetic_single,
geodetic2ecef_single) geodetic2ecef_single)
from common.transformations.transformations import LocalCoord as LocalCoord_single from openpilot.common.transformations.transformations import LocalCoord as LocalCoord_single
class LocalCoord(LocalCoord_single): class LocalCoord(LocalCoord_single):

@ -1,6 +1,6 @@
import numpy as np import numpy as np
from common.transformations.camera import (FULL_FRAME_SIZE, from openpilot.common.transformations.camera import (FULL_FRAME_SIZE,
get_view_frame_from_calib_frame) get_view_frame_from_calib_frame)
# segnet # segnet
@ -61,8 +61,8 @@ medmodel_frame_from_bigmodel_frame = np.dot(medmodel_intrinsics, np.linalg.inv(b
### This function mimics the update_calibration logic in modeld.cc ### This function mimics the update_calibration logic in modeld.cc
### Manually verified to give similar results to xx.uncommon.utils.transform_img ### Manually verified to give similar results to xx.uncommon.utils.transform_img
def get_warp_matrix(rpy_calib, wide_cam=False, big_model=False, tici=True): def get_warp_matrix(rpy_calib, wide_cam=False, big_model=False, tici=True):
from common.transformations.orientation import rot_from_euler from openpilot.common.transformations.orientation import rot_from_euler
from common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics from openpilot.common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics
if tici and wide_cam: if tici and wide_cam:
intrinsics = tici_ecam_intrinsics intrinsics = tici_ecam_intrinsics
@ -85,8 +85,8 @@ def get_warp_matrix(rpy_calib, wide_cam=False, big_model=False, tici=True):
### This is old, just for debugging ### This is old, just for debugging
def get_warp_matrix_old(rpy_calib, wide_cam=False, big_model=False, tici=True): def get_warp_matrix_old(rpy_calib, wide_cam=False, big_model=False, tici=True):
from common.transformations.orientation import rot_from_euler from openpilot.common.transformations.orientation import rot_from_euler
from common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics from openpilot.common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics
def get_view_frame_from_road_frame(roll, pitch, yaw, height): def get_view_frame_from_road_frame(roll, pitch, yaw, height):

@ -2,7 +2,7 @@
import numpy as np import numpy as np
from typing import Callable from typing import Callable
from common.transformations.transformations import (ecef_euler_from_ned_single, from openpilot.common.transformations.transformations import (ecef_euler_from_ned_single,
euler2quat_single, euler2quat_single,
euler2rot_single, euler2rot_single,
ned_euler_from_ecef_single, ned_euler_from_ecef_single,

@ -3,7 +3,7 @@
import numpy as np import numpy as np
import unittest import unittest
import common.transformations.coordinates as coord import openpilot.common.transformations.coordinates as coord
geodetic_positions = np.array([[37.7610403, -122.4778699, 115], geodetic_positions = np.array([[37.7610403, -122.4778699, 115],
[27.4840915, -68.5867592, 2380], [27.4840915, -68.5867592, 2380],

@ -3,7 +3,7 @@
import numpy as np import numpy as np
import unittest import unittest
from common.transformations.orientation import euler2quat, quat2euler, euler2rot, rot2euler, \ from openpilot.common.transformations.orientation import euler2quat, quat2euler, euler2rot, rot2euler, \
rot2quat, quat2rot, \ rot2quat, quat2rot, \
ned_euler_from_ecef ned_euler_from_ecef

@ -1,20 +1,20 @@
# distutils: language = c++ # distutils: language = c++
# cython: language_level = 3 # cython: language_level = 3
from common.transformations.transformations cimport Matrix3, Vector3, Quaternion from openpilot.common.transformations.transformations cimport Matrix3, Vector3, Quaternion
from common.transformations.transformations cimport ECEF, NED, Geodetic from openpilot.common.transformations.transformations cimport ECEF, NED, Geodetic
from common.transformations.transformations cimport euler2quat as euler2quat_c from openpilot.common.transformations.transformations cimport euler2quat as euler2quat_c
from common.transformations.transformations cimport quat2euler as quat2euler_c from openpilot.common.transformations.transformations cimport quat2euler as quat2euler_c
from common.transformations.transformations cimport quat2rot as quat2rot_c from openpilot.common.transformations.transformations cimport quat2rot as quat2rot_c
from common.transformations.transformations cimport rot2quat as rot2quat_c from openpilot.common.transformations.transformations cimport rot2quat as rot2quat_c
from common.transformations.transformations cimport euler2rot as euler2rot_c from openpilot.common.transformations.transformations cimport euler2rot as euler2rot_c
from common.transformations.transformations cimport rot2euler as rot2euler_c from openpilot.common.transformations.transformations cimport rot2euler as rot2euler_c
from common.transformations.transformations cimport rot_matrix as rot_matrix_c from openpilot.common.transformations.transformations cimport rot_matrix as rot_matrix_c
from common.transformations.transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c from openpilot.common.transformations.transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c
from common.transformations.transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c from openpilot.common.transformations.transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c
from common.transformations.transformations cimport geodetic2ecef as geodetic2ecef_c from openpilot.common.transformations.transformations cimport geodetic2ecef as geodetic2ecef_c
from common.transformations.transformations cimport ecef2geodetic as ecef2geodetic_c from openpilot.common.transformations.transformations cimport ecef2geodetic as ecef2geodetic_c
from common.transformations.transformations cimport LocalCoord_c from openpilot.common.transformations.transformations cimport LocalCoord_c
import cython import cython

@ -115,7 +115,7 @@ public:
#ifndef __APPLE__ #ifndef __APPLE__
std::signal(SIGPWR, (sighandler_t)set_do_exit); std::signal(SIGPWR, (sighandler_t)set_do_exit);
#endif #endif
}; }
inline static std::atomic<bool> power_failure = false; inline static std::atomic<bool> power_failure = false;
inline static std::atomic<int> signal = 0; inline static std::atomic<int> signal = 0;
inline operator bool() { return do_exit; } inline operator bool() { return do_exit; }

@ -1,3 +1,5 @@
#include <string>
#include "common/watchdog.h" #include "common/watchdog.h"
#include "common/util.h" #include "common/util.h"

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 257 Supported Cars # 258 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| |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|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -81,7 +81,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|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 Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>|| |Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|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 Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving 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 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>|| |Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving 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 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>|| |Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>||
|Hyundai|Ioniq Electric 2020|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 H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>|| |Hyundai|Ioniq Electric 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 H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>|| |Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>||
|Hyundai|Ioniq Hybrid 2020-22|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 H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>|| |Hyundai|Ioniq Hybrid 2020-22|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 H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>|| |Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>||
@ -124,15 +124,16 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>|| |Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2021-22">Buy Here</a></sub></details>|| |Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2021-22">Buy Here</a></sub></details>||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>|| |Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[<sup>1</sup>](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>|| |Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>||
|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>|| |Kia|Niro Plug-in Hybrid 2020|All|Stock|0 mph|32 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 power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 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 power v2<br>- 1 comma three<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=Optima 2017">Buy Here</a></sub></details>|| |Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 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 power v2<br>- 1 comma three<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=Optima 2017">Buy Here</a></sub></details>||
|Kia|Optima 2019-20|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 G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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=Optima 2019-20">Buy Here</a></sub></details>|| |Kia|Optima 2019-20|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 G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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=Optima 2019-20">Buy Here</a></sub></details>||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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=Seltos 2021">Buy Here</a></sub></details>|| |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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=Seltos 2021">Buy Here</a></sub></details>||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|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 power v2<br>- 1 comma three<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 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|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 power v2<br>- 1 comma three<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 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Sorento 2019|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 E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2019|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 E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|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 power v2<br>- 1 comma three<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 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 power v2<br>- 1 comma three<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 Plug-in Hybrid 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 A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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|Sorento Hybrid 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 A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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 2023">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 power v2<br>- 1 comma three<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 power v2<br>- 1 comma three<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)|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 power v2<br>- 1 comma three<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 power v2<br>- 1 comma three<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|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 power v2<br>- 1 comma three<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 power v2<br>- 1 comma three<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 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 power v2<br>- 1 comma three<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>|

@ -17,8 +17,8 @@ import os
import sys import sys
from os.path import exists from os.path import exists
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from system.version import get_version from openpilot.system.version import get_version
sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))

@ -1,4 +1,6 @@
FROM ghcr.io/commaai/openpilot-base:latest as openpilot-docs-base FROM scons-cache as scons-cache
FROM ghcr.io/commaai/openpilot-base:latest
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
@ -11,6 +13,7 @@ WORKDIR ${OPENPILOT_PATH}
COPY SConstruct ${OPENPILOT_PATH} COPY SConstruct ${OPENPILOT_PATH}
COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./body ${OPENPILOT_PATH}/body COPY ./body ${OPENPILOT_PATH}/body
COPY ./third_party ${OPENPILOT_PATH}/third_party COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./site_scons ${OPENPILOT_PATH}/site_scons COPY ./site_scons ${OPENPILOT_PATH}/site_scons
@ -28,7 +31,7 @@ COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system COPY ./system ${OPENPILOT_PATH}/system
COPY ./*.md ${OPENPILOT_PATH}/ COPY ./*.md ${OPENPILOT_PATH}/
RUN scons -j$(nproc) RUN --mount=type=bind,from=scons-cache,source=/tmp/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc)
RUN apt update && apt install doxygen -y RUN apt update && apt install doxygen -y
COPY ./docs ${OPENPILOT_PATH}/docs COPY ./docs ${OPENPILOT_PATH}/docs
@ -37,5 +40,5 @@ WORKDIR ${OPENPILOT_PATH}/docs
RUN make html RUN make html
FROM nginx:1.21 FROM nginx:1.21
COPY --from=0 /home/batman/openpilot/build/docs/html /usr/share/nginx/html COPY --from=1 /home/batman/openpilot/build/docs/html /usr/share/nginx/html
COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf

@ -1 +1 @@
Subproject commit ef21c612f4b0d68c790b4b1a6d637cd5fce7732e Subproject commit 989b6a8505441a550c94f60b4378be296ea3fe92

@ -1,16 +0,0 @@
[mypy]
python_version = 3.11
plugins = numpy.typing.mypy_plugin
files = body, common, docs, scripts, selfdrive, site_scons, system, tools
exclude = ^(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)
; third-party packages
ignore_missing_imports = True
; helpful warnings
warn_redundant_casts = True
warn_unreachable = True
warn_unused_ignores = True
; restrict dynamic typing
warn_return_any = True

@ -1 +1 @@
Subproject commit a0344a11e742ffb3658b2d1587d58f2836d66e2f Subproject commit 57ec466a91d1e2d51b43d54417091a50f0c9dc78

119
poetry.lock generated

@ -387,9 +387,7 @@ version = "0.9.14"
description = "Python API for communicating with the CARLA server." description = "Python API for communicating with the CARLA server."
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = []
{file = "carla-0.9.14-cp311-cp311-linux_x86_64.whl", hash = "sha256:f0a8ce0c760d1fef3577e2ef90e9d468e3d85e65bd6d68b44bce51f0d5a0723a"},
]
[package.source] [package.source]
type = "url" type = "url"
@ -732,6 +730,27 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pill
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "wurlitzer"] test-no-images = ["pytest", "pytest-cov", "wurlitzer"]
[[package]]
name = "control"
version = "0.9.4"
description = "Python Control Systems Library"
optional = false
python-versions = ">=3.8"
files = [
{file = "control-0.9.4-py3-none-any.whl", hash = "sha256:ab68980abd8d35ae5015ffa090865cbbd926deea7e66d0b9a41cfd12577e63ff"},
{file = "control-0.9.4.tar.gz", hash = "sha256:0fa57d2216b7ac4e9339c09eab6827660318a641779335864feee940bd19c9ce"},
]
[package.dependencies]
matplotlib = "*"
numpy = "*"
scipy = ">=1.3"
[package.extras]
cvxopt = ["cvxopt (>=1.2.0)"]
slycot = ["slycot (>=0.4.0)"]
test = ["pytest", "pytest-timeout"]
[[package]] [[package]]
name = "coverage" name = "coverage"
version = "7.3.0" version = "7.3.0"
@ -2178,39 +2197,36 @@ setuptools = "*"
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "1.23.5" version = "1.25.2"
description = "NumPy is the fundamental package for array computing with Python." description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "numpy-1.23.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9c88793f78fca17da0145455f0d7826bcb9f37da4764af27ac945488116efe63"}, {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"},
{file = "numpy-1.23.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e9f4c4e51567b616be64e05d517c79a8a22f3606499941d97bb76f2ca59f982d"}, {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"},
{file = "numpy-1.23.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7903ba8ab592b82014713c491f6c5d3a1cde5b4a3bf116404e08f5b52f6daf43"}, {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"},
{file = "numpy-1.23.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e05b1c973a9f858c74367553e236f287e749465f773328c8ef31abe18f691e1"}, {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"},
{file = "numpy-1.23.5-cp310-cp310-win32.whl", hash = "sha256:522e26bbf6377e4d76403826ed689c295b0b238f46c28a7251ab94716da0b280"}, {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"},
{file = "numpy-1.23.5-cp310-cp310-win_amd64.whl", hash = "sha256:dbee87b469018961d1ad79b1a5d50c0ae850000b639bcb1b694e9981083243b6"}, {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"},
{file = "numpy-1.23.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ce571367b6dfe60af04e04a1834ca2dc5f46004ac1cc756fb95319f64c095a96"}, {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"},
{file = "numpy-1.23.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56e454c7833e94ec9769fa0f86e6ff8e42ee38ce0ce1fa4cbb747ea7e06d56aa"}, {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"},
{file = "numpy-1.23.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5039f55555e1eab31124a5768898c9e22c25a65c1e0037f4d7c495a45778c9f2"}, {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"},
{file = "numpy-1.23.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f545efd1108e647604a1b5aa809591ccd2540f468a880bedb97247e72db387"}, {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"},
{file = "numpy-1.23.5-cp311-cp311-win32.whl", hash = "sha256:b2a9ab7c279c91974f756c84c365a669a887efa287365a8e2c418f8b3ba73fb0"}, {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"},
{file = "numpy-1.23.5-cp311-cp311-win_amd64.whl", hash = "sha256:0cbe9848fad08baf71de1a39e12d1b6310f1d5b2d0ea4de051058e6e1076852d"}, {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"},
{file = "numpy-1.23.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f063b69b090c9d918f9df0a12116029e274daf0181df392839661c4c7ec9018a"}, {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"},
{file = "numpy-1.23.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0aaee12d8883552fadfc41e96b4c82ee7d794949e2a7c3b3a7201e968c7ecab9"}, {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"},
{file = "numpy-1.23.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92c8c1e89a1f5028a4c6d9e3ccbe311b6ba53694811269b992c0b224269e2398"}, {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"},
{file = "numpy-1.23.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d208a0f8729f3fb790ed18a003f3a57895b989b40ea4dce4717e9cf4af62c6bb"}, {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"},
{file = "numpy-1.23.5-cp38-cp38-win32.whl", hash = "sha256:06005a2ef6014e9956c09ba07654f9837d9e26696a0470e42beedadb78c11b07"}, {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"},
{file = "numpy-1.23.5-cp38-cp38-win_amd64.whl", hash = "sha256:ca51fcfcc5f9354c45f400059e88bc09215fb71a48d3768fb80e357f3b457e1e"}, {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"},
{file = "numpy-1.23.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8969bfd28e85c81f3f94eb4a66bc2cf1dbdc5c18efc320af34bffc54d6b1e38f"}, {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"},
{file = "numpy-1.23.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7ac231a08bb37f852849bbb387a20a57574a97cfc7b6cabb488a4fc8be176de"}, {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"},
{file = "numpy-1.23.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf837dc63ba5c06dc8797c398db1e223a466c7ece27a1f7b5232ba3466aafe3d"}, {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"},
{file = "numpy-1.23.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33161613d2269025873025b33e879825ec7b1d831317e68f4f2f0f84ed14c719"}, {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"},
{file = "numpy-1.23.5-cp39-cp39-win32.whl", hash = "sha256:af1da88f6bc3d2338ebbf0e22fe487821ea4d8e89053e25fa59d1d79786e7481"}, {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"},
{file = "numpy-1.23.5-cp39-cp39-win_amd64.whl", hash = "sha256:09b7847f7e83ca37c6e627682f145856de331049013853f344f37b0c9690e3df"}, {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"},
{file = "numpy-1.23.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:abdde9f795cf292fb9651ed48185503a2ff29be87770c3b8e2a14b0cd7aa16f8"}, {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"},
{file = "numpy-1.23.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9a909a8bae284d46bbfdefbdd4a262ba19d3bc9921b1e76126b1d21c3c34135"},
{file = "numpy-1.23.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:01dd17cbb340bf0fc23981e52e1d18a9d4050792e8fb8363cecbf066a84b827d"},
{file = "numpy-1.23.5.tar.gz", hash = "sha256:1b1766d6f397c18153d40015ddfc79ddb715cabadc04d2d228d4e5a8bc4ded1a"},
] ]
[[package]] [[package]]
@ -3108,6 +3124,39 @@ pluggy = ">=0.12,<2.0"
[package.extras] [package.extras]
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
version = "4.1.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
]
[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pytest-subtests"
version = "0.11.0"
description = "unittest subTest() support and subtests fixture"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-subtests-0.11.0.tar.gz", hash = "sha256:51865c88457545f51fb72011942f0a3c6901ee9e24cbfb6d1b9dc1348bafbe37"},
{file = "pytest_subtests-0.11.0-py3-none-any.whl", hash = "sha256:453389984952eec85ab0ce0c4f026337153df79587048271c7fd0f49119c07e4"},
]
[package.dependencies]
attrs = ">=19.2.0"
pytest = ">=7.0"
[[package]] [[package]]
name = "pytest-xdist" name = "pytest-xdist"
version = "3.3.1" version = "3.3.1"
@ -4164,4 +4213,4 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "~3.11" python-versions = "~3.11"
content-hash = "a8e81d5e0f650dc36a228cf4639c09700f0b52f65869eb8444f8365d507fdba0" content-hash = "320aa5cc075d746403abb872211ebdaf55e9504face8dab8c9b270c71953675d"

@ -4,6 +4,37 @@ addopts = "--ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --igno
python_files = "test_*.py" python_files = "test_*.py"
timeout = "30" # you get this long by default timeout = "30" # you get this long by default
[tool.mypy]
python_version = "3.11"
plugins = [
"numpy.typing.mypy_plugin",
]
exclude = [
"body/",
"cereal/",
"opendbc/",
"panda/",
"laika/",
"laika_repo/",
"rednose/",
"rednose_repo/",
"tinygrad/",
"tinygrad_repo/",
"third_party/",
]
# third-party packages
ignore_missing_imports=true
# helpful warnings
warn_redundant_casts=true
warn_unreachable=true
warn_unused_ignores=true
# restrict dynamic typing
warn_return_any=true
[tool.poetry] [tool.poetry]
name = "openpilot" name = "openpilot"
version = "0.1.0" version = "0.1.0"
@ -22,6 +53,7 @@ aiohttp = "*"
aiortc = "*" aiortc = "*"
casadi = "==3.6.3" casadi = "==3.6.3"
cffi = "*" cffi = "*"
control = "*"
crcmod = "*" crcmod = "*"
cryptography = "*" cryptography = "*"
Cython = "*" Cython = "*"
@ -31,7 +63,7 @@ hexdump = "*"
Jinja2 = "*" Jinja2 = "*"
json-rpc = "*" json-rpc = "*"
libusb1 = "*" libusb1 = "*"
numpy = "~1.23" # pinned for acados numpy = "*"
onnx = ">=1.14.0" onnx = ">=1.14.0"
onnxruntime-gpu = { version = ">=1.15.1", platform = "linux", markers = "platform_machine == 'x86_64'" } onnxruntime-gpu = { version = ">=1.15.1", platform = "linux", markers = "platform_machine == 'x86_64'" }
pillow = "*" pillow = "*"
@ -88,6 +120,8 @@ pycurl = "*"
pygame = "*" pygame = "*"
pyprof2calltree = "*" pyprof2calltree = "*"
pytest = "*" pytest = "*"
pytest-cov = "*"
pytest-subtests = "*"
pytest-xdist = "*" pytest-xdist = "*"
scipy = "*" scipy = "*"
sphinx = "*" sphinx = "*"
@ -101,7 +135,7 @@ types-pycurl = "*"
types-PyYAML = "*" types-PyYAML = "*"
types-requests = "*" types-requests = "*"
types-tabulate = "*" types-tabulate = "*"
pyqt5 = "*" pyqt5 = { version = "*", markers = "platform_machine == 'x86_64'" } # no aarch64 wheels for macOS/linux
[build-system] [build-system]

@ -15,12 +15,14 @@ docs/INTEGRATION.md
docs/LIMITATIONS.md docs/LIMITATIONS.md
site_scons/site_tools/cython.py site_scons/site_tools/cython.py
openpilot/__init__.py
openpilot/**
common/.gitignore common/.gitignore
common/__init__.py common/__init__.py
common/conversions.py common/conversions.py
common/gpio.py common/gpio.py
common/realtime.py common/realtime.py
common/clock.pyx
common/timeout.py common/timeout.py
common/ffi_wrapper.py common/ffi_wrapper.py
common/file_helpers.py common/file_helpers.py
@ -160,6 +162,8 @@ common/clutil.cc
common/clutil.h common/clutil.h
common/params.h common/params.h
common/params.cc common/params.cc
common/ratekeeper.cc
common/ratekeeper.h
common/watchdog.cc common/watchdog.cc
common/watchdog.h common/watchdog.h

@ -2,7 +2,7 @@
from collections import Counter from collections import Counter
from pprint import pprint from pprint import pprint
from selfdrive.car.docs import get_all_car_info from openpilot.selfdrive.car.docs import get_all_car_info
if __name__ == "__main__": if __name__ == "__main__":
cars = get_all_car_info() cars = get_all_car_info()

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
if __name__ == "__main__": if __name__ == "__main__":
HARDWARE.set_power_save(False) HARDWARE.set_power_save(False)

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from PyQt5.QtWidgets import QApplication, QLabel # pylint: disable=no-name-in-module, import-error from PyQt5.QtWidgets import QApplication, QLabel # pylint: disable=no-name-in-module, import-error
from selfdrive.ui.qt.python_helpers import set_main_window from openpilot.selfdrive.ui.qt.python_helpers import set_main_window
if __name__ == "__main__": if __name__ == "__main__":

@ -1,23 +1,23 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import time
import numpy as np import numpy as np
from common.realtime import sec_since_boot
from multiprocessing import Process from multiprocessing import Process
from setproctitle import setproctitle # pylint: disable=no-name-in-module from setproctitle import setproctitle
def waste(core): def waste(core):
os.sched_setaffinity(0, [core,]) # pylint: disable=no-member os.sched_setaffinity(0, [core,])
m1 = np.zeros((200, 200)) + 0.8 m1 = np.zeros((200, 200)) + 0.8
m2 = np.zeros((200, 200)) + 1.2 m2 = np.zeros((200, 200)) + 1.2
i = 1 i = 1
st = sec_since_boot() st = time.monotonic()
j = 0 j = 0
while 1: while 1:
if (i % 100) == 0: if (i % 100) == 0:
setproctitle("%3d: %8d" % (core, i)) setproctitle("%3d: %8d" % (core, i))
lt = sec_since_boot() lt = time.monotonic()
print("%3d: %8d %f %.2f" % (core, i, lt-st, j)) print("%3d: %8d %f %.2f" % (core, i, lt-st, j))
st = lt st = lt
i += 1 i += 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -30,17 +30,17 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
import cereal.messaging as messaging import cereal.messaging as messaging
from cereal import log from cereal import log
from cereal.services import service_list from cereal.services import service_list
from common.api import Api from openpilot.common.api import Api
from common.basedir import PERSIST from openpilot.common.basedir import PERSIST
from common.file_helpers import CallbackReader from openpilot.common.file_helpers import CallbackReader
from common.params import Params from openpilot.common.params import Params
from common.realtime import sec_since_boot, set_core_affinity from openpilot.common.realtime import set_core_affinity
from system.hardware import HARDWARE, PC, AGNOS from openpilot.system.hardware import HARDWARE, PC, AGNOS
from system.loggerd.config import ROOT from openpilot.system.loggerd.config import ROOT
from system.loggerd.xattr_cache import getxattr, setxattr from openpilot.system.loggerd.xattr_cache import getxattr, setxattr
from selfdrive.statsd import STATS_DIR from openpilot.selfdrive.statsd import STATS_DIR
from system.swaglog import SWAGLOG_DIR, cloudlog from openpilot.system.swaglog import SWAGLOG_DIR, cloudlog
from system.version import get_commit, get_origin, get_short_branch, get_version from openpilot.system.version import get_commit, get_origin, get_short_branch, get_version
# TODO: use socket constant when mypy recognizes this as a valid attribute # TODO: use socket constant when mypy recognizes this as a valid attribute
@ -552,7 +552,7 @@ def getNetworks():
@dispatcher.add_method @dispatcher.add_method
def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]: def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]:
from system.camerad.snapshot.snapshot import jpeg_write, snapshot from openpilot.system.camerad.snapshot.snapshot import jpeg_write, snapshot
ret = snapshot() ret = snapshot()
if ret is not None: if ret is not None:
def b64jpeg(x): def b64jpeg(x):
@ -593,10 +593,10 @@ def log_handler(end_event: threading.Event) -> None:
return return
log_files = [] log_files = []
last_scan = 0 last_scan = 0.
while not end_event.is_set(): while not end_event.is_set():
try: try:
curr_scan = sec_since_boot() curr_scan = time.monotonic()
if curr_scan - last_scan > 10: if curr_scan - last_scan > 10:
log_files = get_logs_to_send_sorted() log_files = get_logs_to_send_sorted()
last_scan = curr_scan last_scan = curr_scan
@ -652,8 +652,8 @@ def log_handler(end_event: threading.Event) -> None:
def stat_handler(end_event: threading.Event) -> None: def stat_handler(end_event: threading.Event) -> None:
while not end_event.is_set(): while not end_event.is_set():
last_scan = 0 last_scan = 0.
curr_scan = sec_since_boot() curr_scan = time.monotonic()
try: try:
if curr_scan - last_scan > 10: if curr_scan - last_scan > 10:
stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(STATS_DIR))) stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(STATS_DIR)))
@ -721,7 +721,7 @@ def ws_proxy_send(ws: WebSocket, local_sock: socket.socket, signal_sock: socket.
def ws_recv(ws: WebSocket, end_event: threading.Event) -> None: def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
last_ping = int(sec_since_boot() * 1e9) last_ping = int(time.monotonic() * 1e9)
while not end_event.is_set(): while not end_event.is_set():
try: try:
opcode, data = ws.recv_data(control_frame=True) opcode, data = ws.recv_data(control_frame=True)
@ -730,10 +730,10 @@ def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
data = data.decode("utf-8") data = data.decode("utf-8")
recv_queue.put_nowait(data) recv_queue.put_nowait(data)
elif opcode == ABNF.OPCODE_PING: elif opcode == ABNF.OPCODE_PING:
last_ping = int(sec_since_boot() * 1e9) last_ping = int(time.monotonic() * 1e9)
Params().put("LastAthenaPingTime", str(last_ping)) Params().put("LastAthenaPingTime", str(last_ping))
except WebSocketTimeoutException: except WebSocketTimeoutException:
ns_since_last_ping = int(sec_since_boot() * 1e9) - last_ping ns_since_last_ping = int(time.monotonic() * 1e9) - last_ping
if ns_since_last_ping > RECONNECT_TIMEOUT_S * 1e9: if ns_since_last_ping > RECONNECT_TIMEOUT_S * 1e9:
cloudlog.exception("athenad.ws_recv.timeout") cloudlog.exception("athenad.ws_recv.timeout")
end_event.set() end_event.set()

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

@ -6,13 +6,13 @@ from pathlib import Path
from typing import Optional from typing import Optional
from datetime import datetime, timedelta from datetime import datetime, timedelta
from common.api import api_get from openpilot.common.api import api_get
from common.params import Params from openpilot.common.params import Params
from common.spinner import Spinner from openpilot.common.spinner import Spinner
from common.basedir import PERSIST from openpilot.common.basedir import PERSIST
from selfdrive.controls.lib.alertmanager import set_offroad_alert from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
from system.hardware import HARDWARE, PC from openpilot.system.hardware import HARDWARE, PC
from system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
UNREGISTERED_DONGLE_ID = "UnregisteredDevice" UNREGISTERED_DONGLE_ID = "UnregisteredDevice"

@ -6,7 +6,7 @@ import time
from functools import wraps from functools import wraps
from multiprocessing import Process from multiprocessing import Process
from common.timeout import Timeout from openpilot.common.timeout import Timeout
class MockResponse: class MockResponse:

@ -18,10 +18,10 @@ from unittest import mock
from websocket import ABNF from websocket import ABNF
from websocket._exceptions import WebSocketConnectionClosedException from websocket._exceptions import WebSocketConnectionClosedException
from system import swaglog from openpilot.system import swaglog
from selfdrive.athena import athenad from openpilot.selfdrive.athena import athenad
from selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher from openpilot.selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher
from selfdrive.athena.tests.helpers import MockWebsocket, MockParams, MockApi, EchoSocket, with_http_server from openpilot.selfdrive.athena.tests.helpers import MockWebsocket, MockParams, MockApi, EchoSocket, with_http_server
from cereal import messaging from cereal import messaging
@ -351,7 +351,7 @@ class TestAthenadMethods(unittest.TestCase):
self.assertEqual(athenad.upload_queue.qsize(), 1) self.assertEqual(athenad.upload_queue.qsize(), 1)
self.assertDictEqual(asdict(athenad.upload_queue.queue[-1]), asdict(item1)) self.assertDictEqual(asdict(athenad.upload_queue.queue[-1]), asdict(item1))
@mock.patch('selfdrive.athena.athenad.create_connection') @mock.patch('openpilot.selfdrive.athena.athenad.create_connection')
def test_startLocalProxy(self, mock_create_connection): def test_startLocalProxy(self, mock_create_connection):
end_event = threading.Event() end_event = threading.Event()

@ -6,11 +6,11 @@ import unittest
from typing import Callable, cast, Optional from typing import Callable, cast, Optional
from unittest.mock import MagicMock from unittest.mock import MagicMock
from common.params import Params from openpilot.common.params import Params
from common.timeout import Timeout from openpilot.common.timeout import Timeout
from selfdrive.athena import athenad from openpilot.selfdrive.athena import athenad
from selfdrive.manager.helpers import write_onroad_params from openpilot.selfdrive.manager.helpers import write_onroad_params
from system.hardware import TICI from openpilot.system.hardware import TICI
def wifi_radio(on: bool) -> None: def wifi_radio(on: bool) -> None:

@ -7,9 +7,9 @@ from Crypto.PublicKey import RSA
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
from common.params import Params from openpilot.common.params import Params
from selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID from openpilot.selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID
from selfdrive.athena.tests.helpers import MockResponse from openpilot.selfdrive.athena.tests.helpers import MockResponse
class TestRegistration(unittest.TestCase): class TestRegistration(unittest.TestCase):
@ -23,7 +23,7 @@ class TestRegistration(unittest.TestCase):
os.mkdir(os.path.join(self.persist.name, "comma")) os.mkdir(os.path.join(self.persist.name, "comma"))
self.priv_key = Path(os.path.join(self.persist.name, "comma/id_rsa")) self.priv_key = Path(os.path.join(self.persist.name, "comma/id_rsa"))
self.pub_key = Path(os.path.join(self.persist.name, "comma/id_rsa.pub")) self.pub_key = Path(os.path.join(self.persist.name, "comma/id_rsa.pub"))
self.persist_patcher = mock.patch("selfdrive.athena.registration.PERSIST", self.persist.name) self.persist_patcher = mock.patch("openpilot.selfdrive.athena.registration.PERSIST", self.persist.name)
self.persist_patcher.start() self.persist_patcher.start()
def tearDown(self): def tearDown(self):
@ -44,7 +44,7 @@ class TestRegistration(unittest.TestCase):
self.params.put("HardwareSerial", "serial") self.params.put("HardwareSerial", "serial")
self._generate_keys() self._generate_keys()
with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m: with mock.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) as m:
dongle = "DONGLE_ID_123" dongle = "DONGLE_ID_123"
self.params.put("DongleId", dongle) self.params.put("DongleId", dongle)
self.assertEqual(register(), dongle) self.assertEqual(register(), dongle)
@ -52,7 +52,7 @@ class TestRegistration(unittest.TestCase):
def test_no_keys(self): def test_no_keys(self):
# missing pubkey # missing pubkey
with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m: with mock.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) as m:
dongle = register() dongle = register()
self.assertEqual(m.call_count, 0) self.assertEqual(m.call_count, 0)
self.assertEqual(dongle, UNREGISTERED_DONGLE_ID) self.assertEqual(dongle, UNREGISTERED_DONGLE_ID)
@ -61,7 +61,7 @@ class TestRegistration(unittest.TestCase):
def test_missing_cache(self): def test_missing_cache(self):
# keys exist but no dongle id # keys exist but no dongle id
self._generate_keys() self._generate_keys()
with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m: with mock.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) as m:
dongle = "DONGLE_ID_123" dongle = "DONGLE_ID_123"
m.return_value = MockResponse(json.dumps({'dongle_id': dongle}), 200) m.return_value = MockResponse(json.dumps({'dongle_id': dongle}), 200)
self.assertEqual(register(), dongle) self.assertEqual(register(), dongle)
@ -75,7 +75,7 @@ class TestRegistration(unittest.TestCase):
def test_unregistered(self): def test_unregistered(self):
# keys exist, but unregistered # keys exist, but unregistered
self._generate_keys() self._generate_keys()
with mock.patch("selfdrive.athena.registration.api_get", autospec=True) as m: with mock.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) as m:
m.return_value = MockResponse(None, 402) m.return_value = MockResponse(None, 402)
dongle = register() dongle = register()
self.assertEqual(m.call_count, 1) self.assertEqual(m.call_count, 1)

@ -18,11 +18,13 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <future> #include <future>
#include <memory>
#include <thread> #include <thread>
#include "cereal/gen/cpp/car.capnp.h" #include "cereal/gen/cpp/car.capnp.h"
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "common/params.h" #include "common/params.h"
#include "common/ratekeeper.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/timing.h" #include "common/timing.h"
#include "common/util.h" #include "common/util.h"
@ -248,8 +250,7 @@ void can_recv_thread(std::vector<Panda *> pandas) {
PubMaster pm({"can"}); PubMaster pm({"can"});
// run at 100Hz // run at 100Hz
const uint64_t dt = 10000000ULL; RateKeeper rk("boardd_can_recv", 100);
uint64_t next_frame_time = nanos_since_boot() + dt;
std::vector<can_frame> raw_can_data; std::vector<can_frame> raw_can_data;
while (!do_exit && check_all_connected(pandas)) { while (!do_exit && check_all_connected(pandas)) {
@ -271,18 +272,7 @@ void can_recv_thread(std::vector<Panda *> pandas) {
} }
pm.send("can", msg); pm.send("can", msg);
uint64_t cur_time = nanos_since_boot(); rk.keepTime();
int64_t remaining = next_frame_time - cur_time;
if (remaining > 0) {
std::this_thread::sleep_for(std::chrono::nanoseconds(remaining));
} else {
if (ignition) {
LOGW("missed cycles (%lu) %lld", (unsigned long)(-1*remaining/dt), (long long)remaining);
}
next_frame_time = cur_time;
}
next_frame_time += dt;
} }
} }
@ -483,14 +473,16 @@ void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) {
LOGD("start panda state thread"); LOGD("start panda state thread");
// run at 2hz // run at 2hz
while (!do_exit && check_all_connected(pandas)) { RateKeeper rk("panda_state_thread", 2);
uint64_t start_time = nanos_since_boot();
while (!do_exit && check_all_connected(pandas)) {
// send out peripheralState // send out peripheralState
send_peripheral_state(&pm, peripheral_panda); send_peripheral_state(&pm, peripheral_panda);
auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started); auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started);
if (!ignition_opt) { if (!ignition_opt) {
LOGE("Failed to get ignition_opt");
rk.keepTime();
continue; continue;
} }
@ -543,8 +535,7 @@ void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) {
panda->send_heartbeat(engaged); panda->send_heartbeat(engaged);
} }
uint64_t dt = nanos_since_boot() - start_time; rk.keepTime();
util::sleep_for(500 - dt / 1000000ULL);
} }
} }

@ -1,5 +1,8 @@
#pragma once #pragma once
#include <string>
#include <vector>
#include "selfdrive/boardd/panda.h" #include "selfdrive/boardd/panda.h"
bool safety_setter_thread(std::vector<Panda *> pandas); bool safety_setter_thread(std::vector<Panda *> pandas);

@ -1,7 +1,7 @@
# pylint: skip-file # pylint: skip-file
# Cython, now uses scons to build # Cython, now uses scons to build
from selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp from openpilot.selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp
assert can_list_to_can_capnp assert can_list_to_can_capnp
def can_capnp_to_can_list(can, src_filter=None): def can_capnp_to_can_list(can, src_filter=None):

@ -4,6 +4,7 @@
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <vector>
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "common/swaglog.h" #include "common/swaglog.h"

@ -6,6 +6,7 @@
#include <list> #include <list>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string>
#include <vector> #include <vector>
#include "cereal/gen/cpp/car.capnp.h" #include "cereal/gen/cpp/car.capnp.h"

@ -20,8 +20,8 @@
// comms base class // comms base class
class PandaCommsHandle { class PandaCommsHandle {
public: public:
PandaCommsHandle(std::string serial) {}; PandaCommsHandle(std::string serial) {}
virtual ~PandaCommsHandle() {}; virtual ~PandaCommsHandle() {}
virtual void cleanup() = 0; virtual void cleanup() = 0;
std::string hw_serial; std::string hw_serial;

@ -9,11 +9,11 @@ from typing import List, NoReturn
from functools import cmp_to_key from functools import cmp_to_key
from panda import Panda, PandaDFU, PandaProtocolMismatch, FW_PATH from panda import Panda, PandaDFU, PandaProtocolMismatch, FW_PATH
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from common.params import Params from openpilot.common.params import Params
from selfdrive.boardd.set_time import set_time from openpilot.selfdrive.boardd.set_time import set_time
from system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
def get_expected_signature(panda: Panda) -> bytes: def get_expected_signature(panda: Panda) -> bytes:

@ -3,7 +3,7 @@ import os
import datetime import datetime
from panda import Panda from panda import Panda
from common.time import MIN_DATE from openpilot.common.time import MIN_DATE
def set_time(logger): def set_time(logger):
sys_time = datetime.datetime.today() sys_time = datetime.datetime.today()

@ -43,7 +43,7 @@ public:
LockEx(int fd, std::recursive_mutex &m) : fd(fd), m(m) { LockEx(int fd, std::recursive_mutex &m) : fd(fd), m(m) {
m.lock(); m.lock();
flock(fd, LOCK_EX); flock(fd, LOCK_EX);
}; }
~LockEx() { ~LockEx() {
flock(fd, LOCK_UN); flock(fd, LOCK_UN);

@ -9,12 +9,12 @@ from pprint import pprint
import cereal.messaging as messaging import cereal.messaging as messaging
from cereal import car, log from cereal import car, log
from common.params import Params from openpilot.common.params import Params
from common.timeout import Timeout from openpilot.common.timeout import Timeout
from selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.car import make_can_msg from openpilot.selfdrive.car import make_can_msg
from system.hardware import TICI from openpilot.system.hardware import TICI
from selfdrive.test.helpers import phone_only, with_processes from openpilot.selfdrive.test.helpers import phone_only, with_processes
class TestBoardd(unittest.TestCase): class TestBoardd(unittest.TestCase):

@ -5,13 +5,13 @@ import unittest
import cereal.messaging as messaging import cereal.messaging as messaging
from cereal import log from cereal import log
from common.gpio import gpio_set, gpio_init from openpilot.common.gpio import gpio_set, gpio_init
from common.params import Params from openpilot.common.params import Params
from panda import Panda, PandaDFU, PandaProtocolMismatch from panda import Panda, PandaDFU, PandaProtocolMismatch
from selfdrive.test.helpers import phone_only from openpilot.selfdrive.test.helpers import phone_only
from selfdrive.manager.process_config import managed_processes from openpilot.selfdrive.manager.process_config import managed_processes
from system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from system.hardware.tici.pins import GPIO from openpilot.system.hardware.tici.pins import GPIO
HERE = os.path.dirname(os.path.realpath(__file__)) HERE = os.path.dirname(os.path.realpath(__file__))

@ -5,7 +5,7 @@ from typing import Dict, Optional
import capnp import capnp
from cereal import car from cereal import car
from common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
# kg of standard extra cargo to count for drive, gas, etc... # kg of standard extra cargo to count for drive, gas, etc...
@ -40,9 +40,8 @@ def gen_empty_fingerprint():
return {i: {} for i in range(0, 8)} return {i: {} for i in range(0, 8)}
# FIXME: hardcoding honda civic 2016 touring params so they can be used to # these params were derived for the Civic and used to calculate params for other cars
# scale unknown params for other cars class VehicleDynamicsParams:
class CivicParams:
MASS = 1326. + STD_CARGO_KG MASS = 1326. + STD_CARGO_KG
WHEELBASE = 2.70 WHEELBASE = 2.70
CENTER_TO_FRONT = WHEELBASE * 0.4 CENTER_TO_FRONT = WHEELBASE * 0.4
@ -55,18 +54,18 @@ class CivicParams:
# TODO: get actual value, for now starting with reasonable value for # TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase # civic and scaling by mass and wheelbase
def scale_rot_inertia(mass, wheelbase): def scale_rot_inertia(mass, wheelbase):
return CivicParams.ROTATIONAL_INERTIA * mass * wheelbase ** 2 / (CivicParams.MASS * CivicParams.WHEELBASE ** 2) return VehicleDynamicsParams.ROTATIONAL_INERTIA * mass * wheelbase ** 2 / (VehicleDynamicsParams.MASS * VehicleDynamicsParams.WHEELBASE ** 2)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor): def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor):
center_to_rear = wheelbase - center_to_front center_to_rear = wheelbase - center_to_front
tire_stiffness_front = (CivicParams.TIRE_STIFFNESS_FRONT * tire_stiffness_factor) * mass / CivicParams.MASS * \ tire_stiffness_front = (VehicleDynamicsParams.TIRE_STIFFNESS_FRONT * tire_stiffness_factor) * mass / VehicleDynamicsParams.MASS * \
(center_to_rear / wheelbase) / (CivicParams.CENTER_TO_REAR / CivicParams.WHEELBASE) (center_to_rear / wheelbase) / (VehicleDynamicsParams.CENTER_TO_REAR / VehicleDynamicsParams.WHEELBASE)
tire_stiffness_rear = (CivicParams.TIRE_STIFFNESS_REAR * tire_stiffness_factor) * mass / CivicParams.MASS * \ tire_stiffness_rear = (VehicleDynamicsParams.TIRE_STIFFNESS_REAR * tire_stiffness_factor) * mass / VehicleDynamicsParams.MASS * \
(center_to_front / wheelbase) / (CivicParams.CENTER_TO_FRONT / CivicParams.WHEELBASE) (center_to_front / wheelbase) / (VehicleDynamicsParams.CENTER_TO_FRONT / VehicleDynamicsParams.WHEELBASE)
return tire_stiffness_front, tire_stiffness_rear return tire_stiffness_front, tire_stiffness_rear
@ -213,3 +212,24 @@ class CanBusBase:
else: else:
num = len(CP.safetyConfigs) num = len(CP.safetyConfigs)
self.offset = 4 * (num - 1) self.offset = 4 * (num - 1)
class CanSignalRateCalculator:
"""
Calculates the instantaneous rate of a CAN signal by using the counter
variable and the known frequency of the CAN message that contains it.
"""
def __init__(self, frequency):
self.frequency = frequency
self.previous_counter = 0
self.previous_value = 0
self.rate = 0
def update(self, current_value, current_counter):
if current_counter != self.previous_counter:
self.rate = (current_value - self.previous_value) * self.frequency
self.previous_counter = current_counter
self.previous_value = current_value
return self.rate

@ -1,11 +1,11 @@
import numpy as np import numpy as np
from common.params import Params from openpilot.common.params import Params
from common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car.body import bodycan from openpilot.selfdrive.car.body import bodycan
from selfdrive.car.body.values import SPEED_FROM_RPM from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM
from selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.controls.lib.pid import PIDController
MAX_TORQUE = 500 MAX_TORQUE = 500

@ -1,7 +1,7 @@
from cereal import car from cereal import car
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from selfdrive.car.body.values import DBC from openpilot.selfdrive.car.body.values import DBC
STARTUP_TICKS = 100 STARTUP_TICKS = 100

@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math import math
from cereal import car from cereal import car
from common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from selfdrive.car import get_safety_config from openpilot.selfdrive.car import get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.body.values import SPEED_FROM_RPM from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
class RadarInterface(RadarInterfaceBase): class RadarInterface(RadarInterfaceBase):
pass pass

@ -1,9 +1,9 @@
from typing import Dict from typing import Dict
from cereal import car from cereal import car
from selfdrive.car import dbc_dict from openpilot.selfdrive.car import dbc_dict
from selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.docs_definitions import CarInfo
from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu

@ -1,17 +1,17 @@
import os import os
from typing import Dict, List from typing import Callable, Dict, List, Optional, Tuple
from cereal import car from cereal import car
from common.params import Params from openpilot.common.params import Params
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from system.version import is_comma_remote, is_tested_branch from openpilot.system.version import is_comma_remote, is_tested_branch
from selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
from system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
import cereal.messaging as messaging import cereal.messaging as messaging
from selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
FRAME_FINGERPRINT = 100 # 1s FRAME_FINGERPRINT = 100 # 1s
@ -44,7 +44,7 @@ def get_one_can(logcan):
def load_interfaces(brand_names): def load_interfaces(brand_names):
ret = {} ret = {}
for brand_name in brand_names: for brand_name in brand_names:
path = f'selfdrive.car.{brand_name}' path = f'openpilot.selfdrive.car.{brand_name}'
CarInterface = __import__(path + '.interface', fromlist=['CarInterface']).CarInterface CarInterface = __import__(path + '.interface', fromlist=['CarInterface']).CarInterface
if os.path.exists(BASEDIR + '/' + path.replace('.', '/') + '/carstate.py'): if os.path.exists(BASEDIR + '/' + path.replace('.', '/') + '/carstate.py'):
@ -77,6 +77,46 @@ interface_names = _get_interface_names()
interfaces = load_interfaces(interface_names) interfaces = load_interfaces(interface_names)
def can_fingerprint(next_can: Callable) -> Tuple[Optional[str], Dict[int, dict]]:
finger = gen_empty_fingerprint()
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
frame = 0
car_fingerprint = None
done = False
while not done:
a = next_can()
for can in a.can:
# The fingerprint dict is generated for all buses, this way the car interface
# can use it to detect a (valid) multipanda setup and initialize accordingly
if can.src < 128:
if can.src not in finger:
finger[can.src] = {}
finger[can.src][can.address] = len(can.dat)
for b in candidate_cars:
# Ignore extended messages and VIN query response.
if can.src == b and can.address < 0x800 and can.address not in (0x7df, 0x7e0, 0x7e8):
candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b])
# if we only have one car choice and the time since we got our first
# message has elapsed, exit
for b in candidate_cars:
if len(candidate_cars[b]) == 1 and frame > FRAME_FINGERPRINT:
# fingerprint done
car_fingerprint = candidate_cars[b][0]
# bail if no cars left or we've been waiting for more than 2s
failed = (all(len(cc) == 0 for cc in candidate_cars.values()) and frame > FRAME_FINGERPRINT) or frame > 200
succeeded = car_fingerprint is not None
done = failed or succeeded
frame += 1
return car_fingerprint, finger
# **** for use live only **** # **** for use live only ****
def fingerprint(logcan, sendcan, num_pandas): def fingerprint(logcan, sendcan, num_pandas):
fixed_fingerprint = os.environ.get('FINGERPRINT', "") fixed_fingerprint = os.environ.get('FINGERPRINT', "")
@ -125,44 +165,10 @@ def fingerprint(logcan, sendcan, num_pandas):
set_obd_multiplexing(params, False) set_obd_multiplexing(params, False)
params.put_bool("FirmwareQueryDone", True) params.put_bool("FirmwareQueryDone", True)
finger = gen_empty_fingerprint() # CAN fingerprint
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 # drain CAN socket so we get the latest messages
frame = 0
car_fingerprint = None
done = False
# drain CAN socket so we always get the latest messages
messaging.drain_sock_raw(logcan) messaging.drain_sock_raw(logcan)
car_fingerprint, finger = can_fingerprint(lambda: get_one_can(logcan))
while not done:
a = get_one_can(logcan)
for can in a.can:
# The fingerprint dict is generated for all buses, this way the car interface
# can use it to detect a (valid) multipanda setup and initialize accordingly
if can.src < 128:
if can.src not in finger:
finger[can.src] = {}
finger[can.src][can.address] = len(can.dat)
for b in candidate_cars:
# Ignore extended messages and VIN query response.
if can.src == b and can.address < 0x800 and can.address not in (0x7df, 0x7e0, 0x7e8):
candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b])
# if we only have one car choice and the time since we got our first
# message has elapsed, exit
for b in candidate_cars:
if len(candidate_cars[b]) == 1 and frame > FRAME_FINGERPRINT:
# fingerprint done
car_fingerprint = candidate_cars[b][0]
# bail if no cars left or we've been waiting for more than 2s
failed = (all(len(cc) == 0 for cc in candidate_cars.values()) and frame > FRAME_FINGERPRINT) or frame > 200
succeeded = car_fingerprint is not None
done = failed or succeeded
frame += 1
exact_match = True exact_match = True
source = car.CarParams.FingerprintSource.can source = car.CarParams.FingerprintSource.can

@ -1,8 +1,8 @@
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from selfdrive.car import apply_meas_steer_torque_limits from openpilot.selfdrive.car import apply_meas_steer_torque_limits
from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons from openpilot.selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons
from selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags from openpilot.selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
class CarController: class CarController:

@ -1,9 +1,9 @@
from cereal import car from cereal import car
from common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS from openpilot.selfdrive.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS
class CarState(CarStateBase): class CarState(CarStateBase):

@ -1,5 +1,5 @@
from cereal import car from cereal import car
from selfdrive.car.chrysler.values import RAM_CARS from openpilot.selfdrive.car.chrysler.values import RAM_CARS
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert

@ -1,9 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from selfdrive.car import STD_CARGO_KG, get_safety_config from openpilot.selfdrive.car import get_safety_config
from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags
from selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -35,7 +35,7 @@ class CarInterface(CarInterfaceBase):
# Chrysler # Chrysler
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020): if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020):
ret.mass = 2242. + STD_CARGO_KG ret.mass = 2242.
ret.wheelbase = 3.089 ret.wheelbase = 3.089
ret.steerRatio = 16.2 # Pacifica Hybrid 2017 ret.steerRatio = 16.2 # Pacifica Hybrid 2017
@ -46,7 +46,7 @@ class CarInterface(CarInterfaceBase):
# Jeep # Jeep
elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019): elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019):
ret.mass = 1778 + STD_CARGO_KG ret.mass = 1778
ret.wheelbase = 2.71 ret.wheelbase = 2.71
ret.steerRatio = 16.7 ret.steerRatio = 16.7
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
@ -61,7 +61,7 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.wheelbase = 3.88 ret.wheelbase = 3.88
ret.steerRatio = 16.3 ret.steerRatio = 16.3
ret.mass = 2493. + STD_CARGO_KG ret.mass = 2493.
ret.minSteerSpeed = 14.5 ret.minSteerSpeed = 14.5
# Older EPS FW allow steer to zero # Older EPS FW allow steer to zero
if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw): if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw):
@ -71,7 +71,7 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
ret.wheelbase = 3.785 ret.wheelbase = 3.785
ret.steerRatio = 15.61 ret.steerRatio = 15.61
ret.mass = 3405. + STD_CARGO_KG ret.mass = 3405.
ret.minSteerSpeed = 16 ret.minSteerSpeed = 16
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False)

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from cereal import car from cereal import car
from selfdrive.car.interfaces import RadarInterfaceBase from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
from selfdrive.car.chrysler.values import DBC from openpilot.selfdrive.car.chrysler.values import DBC
RADAR_MSGS_C = list(range(0x2c2, 0x2d4+2, 2)) # c_ messages 706,...,724 RADAR_MSGS_C = list(range(0x2c2, 0x2d4+2, 2)) # c_ messages 706,...,724
RADAR_MSGS_D = list(range(0x2a2, 0x2b4+2, 2)) # d_ messages RADAR_MSGS_D = list(range(0x2a2, 0x2b4+2, 2)) # d_ messages
@ -46,7 +46,7 @@ class RadarInterface(RadarInterfaceBase):
def update(self, can_strings): def update(self, can_strings):
if self.rcp is None or self.CP.radarUnavailable: if self.rcp is None or self.CP.radarUnavailable:
return None return super().update(None)
vls = self.rcp.update_strings(can_strings) vls = self.rcp.update_strings(can_strings)
self.updated_messages.update(vls) self.updated_messages.update(vls)

@ -5,9 +5,9 @@ from typing import Dict, List, Optional, Union
from cereal import car from cereal import car
from panda.python import uds from panda.python import uds
from selfdrive.car import dbc_dict from openpilot.selfdrive.car import dbc_dict
from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts
from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu

@ -1,5 +1,5 @@
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
EXT_DIAG_REQUEST = b'\x10\x03' EXT_DIAG_REQUEST = b'\x10\x03'
EXT_DIAG_RESPONSE = b'\x50\x03' EXT_DIAG_RESPONSE = b'\x50\x03'

@ -8,10 +8,10 @@ from natsort import natsorted
from typing import Dict, List from typing import Dict, List
from cereal import car from cereal import car
from common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType
from selfdrive.car.car_helpers import interfaces, get_interface_attr from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
def get_all_footnotes() -> Dict[Enum, int]: def get_all_footnotes() -> Dict[Enum, int]:

@ -6,7 +6,7 @@ from enum import Enum
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from cereal import car from cereal import car
from common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2 GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2
MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)" MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)"

@ -5,10 +5,10 @@ from typing import Optional, Set
import cereal.messaging as messaging import cereal.messaging as messaging
from panda.python.uds import SERVICE_TYPE from panda.python.uds import SERVICE_TYPE
from selfdrive.car import make_can_msg from openpilot.selfdrive.car import make_can_msg
from selfdrive.car.fw_query_definitions import EcuAddrBusType from openpilot.selfdrive.car.fw_query_definitions import EcuAddrBusType
from selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from system.swaglog import cloudlog from openpilot.system.swaglog import cloudlog
def make_tester_present_msg(addr, bus, subaddr=None): def make_tester_present_msg(addr, bus, subaddr=None):

@ -1,4 +1,4 @@
from selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.interfaces import get_interface_attr
FW_VERSIONS = get_interface_attr('FW_VERSIONS', combine_brands=True, ignore_none=True) FW_VERSIONS = get_interface_attr('FW_VERSIONS', combine_brands=True, ignore_none=True)
@ -29,9 +29,8 @@ def eliminate_incompatible_cars(msg, candidate_cars):
car_fingerprints = _FINGERPRINTS[car_name] car_fingerprints = _FINGERPRINTS[car_name]
for fingerprint in car_fingerprints: for fingerprint in car_fingerprints:
fingerprint.update(_DEBUG_ADDRESS) # add alien debug address # add alien debug address
if is_valid_for_fingerprint(msg, fingerprint | _DEBUG_ADDRESS):
if is_valid_for_fingerprint(msg, fingerprint):
compatible_cars.append(car_name) compatible_cars.append(car_name)
break break

@ -1,10 +1,10 @@
from cereal import car from cereal import car
from common.numpy_fast import clip from openpilot.common.numpy_fast import clip
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_angle_limits from openpilot.selfdrive.car import apply_std_steer_angle_limits
from selfdrive.car.ford.fordcan import CanBus, create_acc_msg, create_acc_ui_msg, create_button_msg, \ from openpilot.selfdrive.car.ford.fordcan import CanBus, create_acc_msg, create_acc_ui_msg, create_button_msg, \
create_lat_ctl_msg, create_lat_ctl2_msg, create_lka_msg, create_lkas_ui_msg create_lat_ctl_msg, create_lat_ctl2_msg, create_lka_msg, create_lkas_ui_msg
from selfdrive.car.ford.values import CANFD_CAR, CarControllerParams from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert

@ -1,10 +1,10 @@
from cereal import car from cereal import car
from common.conversions import Conversions as CV from openpilot.common.conversions import Conversions as CV
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from selfdrive.car.ford.fordcan import CanBus from openpilot.selfdrive.car.ford.fordcan import CanBus
from selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType

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

Loading…
Cancel
Save