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

pull/30109/head
Shane Smiskol 2 years ago
commit 7972bdb859
  1. 3
      .devcontainer/.gitignore
  2. 11
      .devcontainer/Dockerfile
  3. 14
      .devcontainer/container_post_create.sh
  4. 7
      .devcontainer/container_post_start.sh
  5. 34
      .devcontainer/devcontainer.json
  6. 27
      .devcontainer/host_setup.sh
  7. 61
      .github/labeler.yaml
  8. 4
      .github/workflows/badges.yaml
  9. 27
      .github/workflows/compile-openpilot/action.yaml
  10. 18
      .github/workflows/labeler.yaml
  11. 18
      .github/workflows/prebuilt.yaml
  12. 16
      .github/workflows/repo-maintenance.yaml
  13. 163
      .github/workflows/selfdrive_tests.yaml
  14. 54
      .github/workflows/setup-with-retry/action.yaml
  15. 63
      .github/workflows/setup/action.yaml
  16. 69
      .github/workflows/tools_tests.yaml
  17. 3
      .gitignore
  18. 25
      .pre-commit-config.yaml
  19. 1
      Dockerfile.openpilot
  20. 22
      Dockerfile.openpilot_base
  21. 361
      Jenkinsfile
  22. 18
      README.md
  23. 9
      RELEASES.md
  24. 88
      SConstruct
  25. 2
      body
  26. 2
      cereal
  27. 34
      common/SConscript
  28. 4
      common/api/__init__.py
  29. 2
      common/basedir.py
  30. 24
      common/clock.pyx
  31. 4
      common/clutil.cc
  32. 1
      common/clutil.h
  33. 8
      common/gpio.cc
  34. 4
      common/gpio.h
  35. 19
      common/i2c.cc
  36. 2
      common/i2c.h
  37. 4
      common/kalman/SConscript
  38. 13
      common/kalman/simple_kalman.py
  39. 8
      common/kalman/tests/test_simple_kalman.py
  40. 2
      common/logging_extra.py
  41. 4
      common/mat.h
  42. 3
      common/params.cc
  43. 6
      common/params.h
  44. 3
      common/params.py
  45. 2
      common/params_pyx.pyx
  46. 32
      common/prefix.py
  47. 40
      common/ratekeeper.cc
  48. 23
      common/ratekeeper.h
  49. 17
      common/realtime.py
  50. 2
      common/spinner.py
  51. 12
      common/swaglog.cc
  52. 2
      common/swaglog.h
  53. 3
      common/tests/.gitignore
  54. 4
      common/tests/test_file_helpers.py
  55. 2
      common/tests/test_numpy_fast.py
  56. 19
      common/tests/test_params.py
  57. 23
      common/tests/test_ratekeeper.cc
  58. 2
      common/tests/test_runner.cc
  59. 9
      common/tests/test_swaglog.cc
  60. 8
      common/tests/test_util.cc
  61. 2
      common/text_window.py
  62. 5
      common/transformations/SConscript
  63. 2
      common/transformations/camera.py
  64. 6
      common/transformations/coordinates.cc
  65. 2
      common/transformations/coordinates.hpp
  66. 7
      common/transformations/coordinates.py
  67. 71
      common/transformations/model.py
  68. 4
      common/transformations/orientation.cc
  69. 2
      common/transformations/orientation.hpp
  70. 3
      common/transformations/orientation.py
  71. 2
      common/transformations/tests/test_coordinates.py
  72. 2
      common/transformations/tests/test_orientation.py
  73. 30
      common/transformations/transformations.pyx
  74. 9
      common/util.cc
  75. 23
      common/util.h
  76. 2
      common/watchdog.cc
  77. 4
      common/window.py
  78. 10
      conftest.py
  79. 31
      docs/CARS.md
  80. 5
      docs/conf.py
  81. 11
      docs/docker/Dockerfile
  82. 2
      laika_repo
  83. 16
      mypy.ini
  84. 2
      opendbc
  85. 2
      panda
  86. 2186
      poetry.lock
  87. 79
      pyproject.toml
  88. 2
      rednose_repo
  89. 39
      release/files_common
  90. 2
      release/files_pc
  91. 2
      scripts/count_cars.py
  92. 2
      scripts/disable-powersave.py
  93. 6
      scripts/dump_pll.c
  94. 4
      scripts/pyqt_demo.py
  95. 10
      scripts/waste.py
  96. 1
      selfdrive/assets/.gitignore
  97. 2
      selfdrive/assets/assets.qrc
  98. 0
      selfdrive/assets/navigation/direction_notification_right.png
  99. 0
      selfdrive/assets/navigation/direction_notification_sharp_right.png
  100. 65
      selfdrive/athena/athenad.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,3 @@
.Xauthority
.env
.host/

@ -0,0 +1,11 @@
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 tmux
RUN pip install ipython jupyter jupyterlab
RUN cd $HOME && \
curl -O https://raw.githubusercontent.com/commaai/agnos-builder/master/userspace/home/.tmux.conf && \
curl -O https://github.com/commaai/agnos-builder/blob/master/userspace/home/.vimrc

@ -0,0 +1,14 @@
#!/usr/bin/env bash
source .devcontainer/.host/.env
# override display flag for mac
if [[ $HOST_OS == darwin ]]; then
echo "Setting up DISPLAY override for macOS..."
cat <<EOF >> /root/.bashrc
if [ -n "\$DISPLAY" ]; then
DISPLAY_NUM=\$(echo "\$DISPLAY" | awk -F: '{print \$NF}')
export DISPLAY=host.docker.internal:\$DISPLAY_NUM
fi
EOF
fi

@ -0,0 +1,7 @@
#!/usr/bin/env bash
# setup safe directories for submodules
SUBMODULE_DIRS=$(git config --file .gitmodules --get-regexp path | awk '{ print $2 }')
for DIR in $SUBMODULE_DIRS; do
git config --global --add safe.directory "$PWD/$DIR"
done

@ -0,0 +1,34 @@
{
"name": "openpilot devcontainer",
"build": {
"dockerfile": "Dockerfile"
},
"postCreateCommand": ".devcontainer/container_post_create.sh",
"postStartCommand": ".devcontainer/container_post_start.sh",
"initializeCommand": ".devcontainer/host_setup.sh",
"privileged": true,
"containerEnv": {
"DISPLAY": "${localEnv:DISPLAY}",
"PYTHONPATH": "${containerWorkspaceFolder}",
"force_color_prompt": "1"
},
"runArgs": [
"--volume=/tmp/.X11-unix:/tmp/.X11-unix",
"--volume=${localWorkspaceFolder}/.devcontainer/.host/.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"
],
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-vscode.cpptools",
"ms-toolsai.jupyter",
"guyskk.language-cython",
"lharri73.dbc"
]
}
}
}

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# pull base image
docker pull ghcr.io/commaai/openpilot-base:latest
# setup .host dir
mkdir -p .devcontainer/.host
# setup links to Xauthority
XAUTHORITY_LINK=".devcontainer/.host/.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
# setup host env file
HOST_INFO_FILE=".devcontainer/.host/.env"
SYSTEM=$(uname -s | tr '[:upper:]' '[:lower:]')
echo "HOST_OS=\"$SYSTEM\"" > $HOST_INFO_FILE

@ -0,0 +1,61 @@
CI / testing:
- all:
- changed-files: ['.github/**']
- all:
- changed-files: ['**/test_*']
car:
- all:
- changed-files: ['selfdrive/car/**']
body:
- all:
- changed-files: ['selfdrive/car/body/*']
chrysler:
- all:
- changed-files: ['selfdrive/car/chrysler/*']
ford:
- all:
- changed-files: ['selfdrive/car/ford/*']
gm:
- all:
- changed-files: ['selfdrive/car/gm/*']
honda:
- all:
- changed-files: ['selfdrive/car/honda/*']
hyundai:
- all:
- changed-files: ['selfdrive/car/hyundai/*']
mazda:
- all:
- changed-files: ['selfdrive/car/mazda/*']
nissan:
- all:
- changed-files: ['selfdrive/car/nissan/*']
subaru:
- all:
- changed-files: ['selfdrive/car/subaru/*']
tesla:
- all:
- changed-files: ['selfdrive/car/tesla/*']
toyota:
- all:
- changed-files: ['selfdrive/car/toyota/*']
volkswagen:
- all:
- changed-files: ['selfdrive/car/volkswagen/*']
simulation:
- all:
- changed-files: ['tools/sim/**']
ui:
- all:
- changed-files: ['selfdrive/ui/**']
tools:
- all:
- changed-files: ['tools/**']
multilanguage:
- all:
- changed-files: ['selfdrive/ui/translations/**']

@ -7,7 +7,7 @@ on:
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v ~/scons_cache:/tmp/scons_cache -v ~/comma_download_cache:/tmp/comma_download_cache -v ~/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/sh -c
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/sh -c
jobs:
badges:
@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Push badges
run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/ui/translations/create_badges.py"

@ -0,0 +1,27 @@
name: 'compile openpilot'
inputs:
cache_key_prefix:
description: 'Prefix for caching key'
required: false
default: 'scons'
runs:
using: "composite"
steps:
- shell: bash
name: Build openpilot with all flags
run: |
${{ env.RUN }} "scons -j$(nproc)"
${{ env.RUN }} "release/check-dirty.sh"
- shell: bash
name: Cleanup scons cache and rebuild
run: |
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --cache-populate"
- name: Save scons cache
uses: actions/cache/save@v3
if: github.ref == 'refs/heads/master'
with:
path: .ci_cache/scons_cache
key: ${{ inputs.cache_key_prefix }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}

@ -0,0 +1,18 @@
name: "Pull Request Labeler"
on:
pull_request_target:
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: false
- uses: actions/labeler@v5.0.0-alpha.1
with:
dot: true
configuration-path: .github/labeler.yaml

@ -5,12 +5,8 @@ on:
workflow_dispatch:
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: |
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
BUILD: selfdrive/test/docker_build.sh prebuilt
jobs:
build_prebuilt:
@ -18,7 +14,7 @@ jobs:
runs-on: ubuntu-20.04
if: github.repository == 'commaai/openpilot'
env:
IMAGE_NAME: openpilot-prebuilt
PUSH_IMAGE: true
steps:
- name: Wait for green check mark
if: ${{ github.event_name != 'workflow_dispatch' }}
@ -31,13 +27,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build Docker image
run: |
eval "$BUILD"
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 Dockerfile.openpilot .
- name: Push to container registry
- name: Build and Push docker image
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
docker tag $DOCKER_REGISTRY/$IMAGE_NAME:latest $DOCKER_REGISTRY/$IMAGE_NAME:$GITHUB_SHA
docker push $DOCKER_REGISTRY/$IMAGE_NAME:$GITHUB_SHA
eval "$BUILD"

@ -1,4 +1,4 @@
name: repo
name: repo maintenance
on:
schedule:
@ -6,13 +6,17 @@ on:
workflow_dispatch:
jobs:
pre-commit-autoupdate:
name: pre-commit autoupdate
updates:
name: updates
runs-on: ubuntu-20.04
container:
image: ghcr.io/commaai/openpilot-base:latest
steps:
- uses: actions/checkout@v3
- name: poetry lock
run: |
pip install poetry
poetry lock
- name: pre-commit autoupdate
run: |
git config --global --add safe.directory '*'
@ -21,8 +25,8 @@ jobs:
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5
with:
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
commit-message: Update pre-commit hook versions
title: 'pre-commit: autoupdate hooks'
branch: pre-commit-updates
commit-message: Update Python packages and pre-commit hooks
title: 'Update Python packages and pre-commit hooks'
branch: auto-package-updates
base: master
delete-branch: true

@ -12,23 +12,21 @@ concurrency:
env:
PYTHONWARNINGS: error
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
DOCKER_REGISTRY: ghcr.io/commaai
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: |
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
BUILD: selfdrive/test/docker_build.sh base
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v ~/scons_cache:/tmp/scons_cache -v ~/comma_download_cache:/tmp/comma_download_cache -v ~/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: selfdrive/test/docker_build.sh cl
BUILD_CL: |
DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v ~/scons_cache:/tmp/scons_cache -v ~/comma_download_cache:/tmp/comma_download_cache -v ~/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
UNIT_TEST: coverage run --append -m unittest discover
PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5
jobs:
build_release:
@ -43,7 +41,7 @@ jobs:
- name: Build devel
timeout-minutes: 1
run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Check submodules
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
timeout-minutes: 1
@ -64,34 +62,28 @@ jobs:
run: |
cd $GITHUB_WORKSPACE
cp .pre-commit-config.yaml $STRIPPED_DIR
cp mypy.ini $STRIPPED_DIR
cp pyproject.toml $STRIPPED_DIR
cp poetry.lock $STRIPPED_DIR
cd $STRIPPED_DIR
${{ env.RUN }} "unset PYTHONWARNINGS && pre-commit run --all"
build_all:
name: build all
runs-on: ubuntu-20.04
build:
strategy:
matrix:
arch: ${{ fromJson( (github.repository == 'commaai/openpilot') && '["x86_64", "aarch64"]' || '["x86_64"]' ) }}
runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }}
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- name: Build openpilot with all flags
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 12 || 30) }} # allow more time when we missed the scons cache
run: ${{ env.RUN }} "scons -j$(nproc) --extras && release/check-dirty.sh"
- name: Cleanup scons cache and rebuild
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 2 || 30) }} # allow more time when we missed the scons cache
run: |
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --cache-populate"
- name: Save scons cache
uses: actions/cache/save@v3
if: github.ref == 'refs/heads/master'
- uses: ./.github/workflows/setup-with-retry
with:
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
cache_key_prefix: scons_${{ matrix.arch }}
- uses: ./.github/workflows/compile-openpilot
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 15 || 30) }} # allow more time when we missed the scons cache
with:
path: ~/scons_cache
key: scons-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
cache_key_prefix: scons_${{ matrix.arch }}
build_mac:
name: build macos
@ -118,6 +110,8 @@ jobs:
uses: actions/cache@v3
with:
path: |
.env
.venv
~/github_brew_cache_entries.txt
~/.pyenv
~/Library/Caches/pypoetry
@ -143,24 +137,23 @@ jobs:
fi
- name: Install dependencies
if: steps.dependency-cache.outputs.cache-hit != 'true'
run: ./tools/mac_setup.sh
run: SKIP_PROMPT=1 ./tools/mac_setup.sh
env:
# package install has DeprecationWarnings
PYTHONWARNINGS: default
- name: Build openpilot
run: |
source tools/openpilot_env.sh
eval "$(pyenv init --path)"
poetry run scons -j$(nproc)
- name: Run tests
run: |
source tools/openpilot_env.sh
export PYTHONPATH=$PWD
eval "$(pyenv init --path)"
poetry run tools/plotjuggler/test_plotjuggler.py
- name: Pre Cache - Cleanup scons cache
if: github.ref == 'refs/heads/master'
run: |
source tools/openpilot_env.sh
rm -rf /tmp/scons_cache/*
eval "$(pyenv init --path)"
poetry run scons -j$(nproc) --cache-populate
- name: Save scons cache
id: scons-save-cache
@ -186,28 +179,46 @@ jobs:
docker_push:
name: docker push
runs-on: ubuntu-20.04
strategy:
matrix:
arch: ${{ fromJson( (github.repository == 'commaai/openpilot') && '["x86_64", "aarch64"]' || '["x86_64"]' ) }}
runs-on: ${{ (matrix.arch == 'aarch64') && 'buildjet-2vcpu-ubuntu-2204-arm' || 'ubuntu-20.04' }}
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build Docker image
run: eval "$BUILD"
- name: Push to container registry
- name: Setup to push to repo
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
echo "TARGET_ARCHITECTURE=${{ matrix.arch }}" >> "$GITHUB_ENV"
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
docker tag $DOCKER_REGISTRY/$BASE_IMAGE:latest $DOCKER_REGISTRY/$BASE_IMAGE:$GITHUB_SHA
docker push $DOCKER_REGISTRY/$BASE_IMAGE:$GITHUB_SHA
- name: Build CL Docker image
run: eval "$BUILD_CL"
- name: Push to container registry
- uses: ./.github/workflows/setup-with-retry
with:
git-lfs: false
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
- name: Build and push CL Docker image
if: matrix.arch == 'x86_64'
run: |
unset TARGET_ARCHITECTURE
eval "$BUILD_CL"
docker_push_multiarch:
name: docker push multiarch tag
runs-on: ubuntu-20.04
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
needs: [docker_push]
steps:
- uses: actions/checkout@v3
with:
submodules: false
- name: Setup docker
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest
docker tag $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest $DOCKER_REGISTRY/$CL_BASE_IMAGE:$GITHUB_SHA
docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:$GITHUB_SHA
- name: Merge x64 and arm64 tags
run: |
export PUSH_IMAGE=true
selfdrive/test/docker_tag_multiarch.sh base x86_64 aarch64
static_analysis:
name: static analysis
@ -229,7 +240,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Run valgrind
@ -247,68 +258,47 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Run unit tests
timeout-minutes: 40
timeout-minutes: 15
run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
$UNIT_TEST common && \
$UNIT_TEST opendbc/can && \
$UNIT_TEST selfdrive/boardd && \
$UNIT_TEST selfdrive/controls && \
$UNIT_TEST selfdrive/monitoring && \
$UNIT_TEST system/loggerd && \
$UNIT_TEST selfdrive/car && \
$UNIT_TEST selfdrive/locationd && \
$UNIT_TEST selfdrive/test/longitudinal_maneuvers && \
$UNIT_TEST system/tests && \
$UNIT_TEST system/ubloxd && \
selfdrive/locationd/test/_test_locationd_lib.py && \
./system/ubloxd/tests/test_glonass_runner && \
$UNIT_TEST selfdrive/athena && \
$UNIT_TEST selfdrive/thermald && \
$UNIT_TEST system/hardware/tici && \
$UNIT_TEST tools/lib/tests && \
$PYTEST -n auto --dist=loadscope --timeout 30 -o cpp_files=test_* && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py && \
./common/tests/test_util && \
./common/tests/test_swaglog && \
./selfdrive/boardd/tests/test_boardd_usbprotocol && \
./system/loggerd/tests/test_logger &&\
./system/proclogd/tests/test_proclog && \
./tools/replay/tests/test_replay && \
./tools/cabana/tests/test_cabana && \
./system/camerad/test/ae_gray_test && \
./selfdrive/test/process_replay/test_fuzzy.py && \
coverage xml"
./selfdrive/test/process_replay/test_fuzzy.py"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
process_replay:
name: process replay
runs-on: ubuntu-20.04
runs-on: ${{ ((github.event.pull_request.head.repo.full_name == 'commaai/openpilot') || (github.repository == 'commaai/openpilot')) && 'buildjet-8vcpu-ubuntu-2004' || 'ubuntu-20.04' }}
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
with:
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
- name: Cache test routes
id: dependency-cache
uses: actions/cache@v3
with:
path: ~/comma_download_cache
path: .ci_cache/comma_download_cache
key: proc-replay-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/ref_commit') }}
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run replay
timeout-minutes: 20
timeout-minutes: 30
run: |
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
chmod -R 777 /tmp/comma_download_cache && \
coverage xml"
- name: Print diff
id: print-diff
@ -323,7 +313,7 @@ jobs:
- name: Upload reference logs
if: ${{ failure() && steps.print-diff.outcome == 'success' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
run: |
${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
${{ env.RUN }} "unset PYTHONWARNINGS && CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
@ -334,7 +324,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Build base Docker image
run: eval "$BUILD"
- name: Build Docker image
@ -345,7 +335,7 @@ jobs:
${{ env.RUN }} "scons -j$(nproc)"
# PYTHONWARNINGS triggers a SyntaxError in onnxruntime
- name: Run model replay with ONNX
timeout-minutes: 2
timeout-minutes: 3
run: |
${{ env.RUN_CL }} "unset PYTHONWARNINGS && \
ONNXCPU=1 CI=1 NO_NAV=1 coverage run selfdrive/test/process_replay/model_replay.py && \
@ -370,20 +360,19 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Cache test routes
id: dependency-cache
uses: actions/cache@v3
with:
path: ~/comma_download_cache
path: .ci_cache/comma_download_cache
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }}
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test car models
timeout-minutes: 25
run: |
${{ env.RUN }} "coverage run -m pytest selfdrive/car/tests/test_models.py && \
coverage xml && \
${{ env.RUN }} "$PYTEST -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
env:
NUM_JOBS: 5
@ -400,7 +389,7 @@ jobs:
with:
submodules: true
ref: ${{ github.event.pull_request.base.ref }}
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Get base car info
run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info"
@ -408,9 +397,11 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
path: current
- name: Save car docs diff
id: save_diff
run: |
cd current
${{ env.RUN }} "scons -j$(nproc)"
output=$(${{ env.RUN }} "python selfdrive/debug/print_docs_diff.py --path /tmp/openpilot_cache/base_car_info")
output="${output//$'\n'/'%0A'}"

@ -0,0 +1,54 @@
name: 'openpilot env setup, with retry on failure'
inputs:
docker_hub_pat:
description: 'Auth token for Docker Hub, required for BuildJet jobs'
required: false
default: ''
git_lfs:
description: 'Whether or not to pull the git lfs'
required: false
default: 'true'
cache_key_prefix:
description: 'Prefix for caching key'
required: false
default: 'scons_x86_64'
sleep_time:
description: 'Time to sleep between retries'
required: false
default: 30
runs:
using: "composite"
steps:
- id: setup1
uses: ./.github/workflows/setup
continue-on-error: true
with:
docker_hub_pat: ${{ inputs.docker_hub_pat }}
git_lfs: ${{ inputs.git_lfs }}
cache_key_prefix: ${{ inputs.cache_key_prefix }}
is_retried: true
- if: steps.setup1.outcome == 'failure'
shell: bash
run: sleep ${{ inputs.sleep_time }}
- id: setup2
if: steps.setup1.outcome == 'failure'
uses: ./.github/workflows/setup
continue-on-error: true
with:
docker_hub_pat: ${{ inputs.docker_hub_pat }}
git_lfs: ${{ inputs.git_lfs }}
cache_key_prefix: ${{ inputs.cache_key_prefix }}
is_retried: true
- if: steps.setup2.outcome == 'failure'
shell: bash
run: sleep ${{ inputs.sleep_time }}
- id: setup3
if: steps.setup2.outcome == 'failure'
uses: ./.github/workflows/setup
with:
docker_hub_pat: ${{ inputs.docker_hub_pat }}
git_lfs: ${{ inputs.git_lfs }}
cache_key_prefix: ${{ inputs.cache_key_prefix }}
is_retried: true

@ -1,19 +1,51 @@
name: 'openpilot env setup'
inputs:
docker_hub_pat:
description: 'Auth token for Docker Hub, required for BuildJet jobs'
required: true
default: ''
git_lfs:
description: 'Whether or not to pull the git lfs'
required: false
required: true
default: 'true'
cache_key_prefix:
description: 'Prefix for caching key'
required: true
default: 'scons_x86_64'
is_retried:
description: 'A mock param that asserts that we use the setup-with-retry instead of this action directly'
required: false
default: 'false'
runs:
using: "composite"
steps:
# assert that this action is retried using the setup-with-retry
- shell: bash
if: ${{ inputs.is_retried == 'false' }}
run: |
echo "You should not run this action directly. Use setup-with-retry instead"
exit 1
# do this after checkout to ensure our custom LFS config is used to pull from GitLab
- shell: bash
if: ${{ inputs.git_lfs == 'true' }}
run: git lfs pull
# on BuildJet runners, must be logged into DockerHub to avoid rate limiting
# https://buildjet.com/for-github-actions/docs/guides/docker
- shell: bash
if: ${{ contains(runner.name, 'buildjet') && inputs.docker_hub_pat == '' }}
run: |
echo "Need to set the Docker Hub PAT secret as an input to this action"
exit 1
- name: Login to Docker Hub
if: contains(runner.name, 'buildjet')
shell: bash
run: |
docker login -u adeebshihadeh -p ${{ inputs.docker_hub_pat }}
# build cache
- id: date
shell: bash
@ -23,12 +55,29 @@ runs:
- id: restore-scons-cache
uses: actions/cache/restore@v3
with:
path: ~/scons_cache
key: scons-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
path: .ci_cache/scons_cache
key: ${{ inputs.cache_key_prefix }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
restore-keys: |
scons-${{ env.CACHE_COMMIT_DATE }}-
scons-
${{ inputs.cache_key_prefix }}-${{ env.CACHE_COMMIT_DATE }}-
${{ inputs.cache_key_prefix }}-
# if we didn't get a cache hit, make the directory manually so it doesn't fail on future steps
- id: scons-cache-setup
shell: bash
if: steps.restore-scons-cache.outputs.cache-hit != 'true'
run: mkdir -p $GITHUB_WORKSPACE/.ci_cache/scons_cache
# as suggested here: https://github.com/moby/moby/issues/32816#issuecomment-910030001
- id: normalize-file-permissions
shell: bash
name: Normalize file permissions to ensure a consistent docker build cache
run: |
find . -type f -executable -not -perm 755 -exec chmod 755 {} \;
find . -type f -not -executable -not -perm 644 -exec chmod 644 {} \;
- id: setup-buildx-action
if: contains(runner.name, 'buildjet')
name: Set up Docker Buildx on buildjet to ensure a consistent cache
uses: docker/setup-buildx-action@v2
with:
driver: docker-container
# build our docker image
- shell: bash
run: eval ${{ env.BUILD }}
run: eval ${{ env.BUILD }}

@ -13,17 +13,15 @@ concurrency:
env:
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
DOCKER_REGISTRY: ghcr.io/commaai
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: |
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
BUILD: selfdrive/test/docker_build.sh base
RUN: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v ~/scons_cache:/tmp/scons_cache -v ~/comma_download_cache:/tmp/comma_download_cache -v ~/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
RUN: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: |
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN_CL: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v ~/scons_cache:/tmp/scons_cache -v ~/comma_download_cache:/tmp/comma_download_cache -v ~/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
BUILD_CL: selfdrive/test/docker_build.sh cl
RUN_CL: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
jobs:
@ -35,57 +33,68 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build Docker image
run: eval "$BUILD"
- name: Unit test
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
timeout-minutes: 5
run: ${{ env.RUN }} "scons -j$(nproc) cereal/ common/ --minimal"
- name: Test PlotJuggler
timeout-minutes: 2
run: |
${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \
apt-get update && \
apt-get install -y libdw-dev libqt5svg5-dev libqt5x11extras5-dev && \
cd /tmp/openpilot/tools/plotjuggler && \
./test_plotjuggler.py"
${{ env.RUN }} "pytest tools/plotjuggler/"
simulator:
name: simulator
runs-on: ubuntu-20.04
env:
IMAGE_NAME: openpilot-sim
if: github.repository == 'commaai/openpilot'
timeout-minutes: 45
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
- name: Build base cl image
run: eval "$BUILD_CL"
- 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 .
- name: Push to container registry
- name: Setup to push to repo
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
- name: Build and push sim image
run: |
selfdrive/test/docker_build.sh sim
docs:
name: build docs
runs-on: ubuntu-20.04
timeout-minutes: 45
env:
BUILD: |
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 .
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 .
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- uses: ./.github/workflows/setup-with-retry
with:
git_lfs: false
- name: Push docker container
- name: Setup to push to repo
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/openpilot-docs-base:latest
docker push $DOCKER_REGISTRY/openpilot-docs:latest
- name: Build and push docs image
run: |
selfdrive/test/docker_build.sh docs
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

3
.gitignore vendored

@ -1,12 +1,12 @@
venv/
.venv/
.ci_cache
.env
.clang-format
.DS_Store
.tags
.ipynb_checkpoints
.idea
.moc_files
.overlay_init
.overlay_consistent
.sconsign.dblite
@ -48,6 +48,7 @@ selfdrive/mapd/default_speeds_by_region.json
system/proclogd/proclogd
selfdrive/ui/_ui
selfdrive/ui/translations/alerts_generated.h
selfdrive/ui/translations/tmp
selfdrive/test/longitudinal_maneuvers/out
selfdrive/car/tests/cars_dump
system/camerad/camerad

@ -1,3 +1,4 @@
exclude: '^(tinygrad_repo)'
repos:
- repo: meta
hooks:
@ -14,6 +15,8 @@ repos:
- id: check-yaml
- id: check-merge-conflict
- id: check-symlinks
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- id: check-added-large-files
args: ['--maxkb=100']
- repo: https://github.com/codespell-project/codespell
@ -35,7 +38,7 @@ repos:
args: ['--explicit-package-bases']
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
rev: v0.0.284
rev: v0.0.288
hooks:
- id: ruff
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
@ -53,6 +56,18 @@ repos:
- --quiet
- --force
- -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/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces
- repo: local
hooks:
- id: test_translations
@ -61,10 +76,14 @@ repos:
language: script
pass_filenames: false
- repo: https://github.com/python-poetry/poetry
rev: '1.5.0'
rev: '1.6.0'
hooks:
- id: poetry-check
- id: poetry-lock
name: validate poetry lock
args:
args:
- --check
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.26.3
hooks:
- id: check-github-workflows

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

@ -12,22 +12,28 @@ ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
RUN cd /tmp && \
tools/install_ubuntu_dependencies.sh && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/* && \
# remove unused architectures from gcc for panda
cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \
rm -rf arm/ && \
rm -rf thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
ENV POETRY_VIRTUALENVS_CREATE=false
ENV PYENV_VERSION=3.11.4
ENV PYENV_ROOT="/root/.pyenv"
ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
COPY pyproject.toml poetry.lock .python-version /tmp/
COPY tools/ubuntu_setup.sh tools/install_python_dependencies.sh /tmp/tools/
COPY tools/install_python_dependencies.sh /tmp/tools/
RUN cd /tmp && \
tools/ubuntu_setup.sh && \
rm -rf /var/lib/apt/lists/* && \
tools/install_python_dependencies.sh && \
rm -rf /tmp/* && \
rm -rf /root/.cache && \
pip uninstall -y poetry && \
# remove unused architectures from gcc for panda
cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \
rm -rf arm/ && \
rm -rf thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
pip uninstall -y poetry
RUN sudo git config --global --add safe.directory /tmp/openpilot

361
Jenkinsfile vendored

@ -1,4 +1,4 @@
def phone(String ip, String step_label, String cmd) {
def device(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
@ -51,225 +51,186 @@ END"""
}
}
def phone_steps(String device_type, steps) {
lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
timeout(time: 20, unit: 'MINUTES') {
phone(device_ip, "git checkout", readFile("selfdrive/test/setup_device_ci.sh"),)
steps.each { item ->
phone(device_ip, item[0], item[1])
}
def deviceStage(String stageName, String deviceType, List env, def steps) {
stage(stageName) {
if (currentBuild.result != null) {
return
}
}
}
pipeline {
agent none
environment {
CI = "1"
PYTHONWARNINGS = "error"
TEST_DIR = "/data/openpilot"
SOURCE_DIR = "/data/openpilot_source/"
AZURE_TOKEN = credentials('azure_token')
MAPBOX_TOKEN = credentials('mapbox_token')
}
options {
timeout(time: 3, unit: 'HOURS')
disableConcurrentBuilds(abortPrevious: env.BRANCH_NAME != 'master')
}
def extra = env.collect { "export ${it}" }.join('\n');
stages {
stage('build release3-staging') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
when {
branch 'devel-staging'
}
steps {
phone_steps("tici-needs-can", [
["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"],
])
docker.image('ghcr.io/commaai/alpine-ssh').inside('--user=root') {
lock(resource: "", label: deviceType, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
timeout(time: 20, unit: 'MINUTES') {
device(device_ip, "git checkout", extra + "\n" + readFile("selfdrive/test/setup_device_ci.sh"))
steps.each { item ->
device(device_ip, item[0], item[1])
}
}
}
}
}
}
stage('build nightly') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
when {
branch 'master-ci'
}
steps {
phone_steps("tici-needs-can", [
["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"],
])
}
def pcStage(String stageName, Closure body) {
node {
stage(stageName) {
if (currentBuild.result != null) {
return
}
stage('openpilot tests') {
when {
not {
anyOf {
branch 'master-ci'; branch 'devel'; branch 'devel-staging';
branch 'release3'; branch 'release3-staging'; branch 'dashcam3'; branch 'dashcam3-staging';
branch 'testing-closet*'; branch 'hotfix-*'
}
checkout scm
def dockerArgs = '--user=root -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/scons_cache:/tmp/scons_cache';
docker.build("openpilot-base:build-${env.GIT_COMMIT}", "-f Dockerfile.openpilot_base .").inside(dockerArgs) {
timeout(time: 20, unit: 'MINUTES') {
try {
sh "git config --global --add safe.directory '*'"
sh "git submodule update --init --recursive"
sh "git lfs pull"
body()
} finally {
sh "rm -rf ${env.WORKSPACE}/* || true"
sh "rm -rf .* || true"
}
}
}
}
}
}
parallel {
/*
stage('simulator') {
agent {
dockerfile {
filename 'Dockerfile.sim_nvidia'
dir 'tools/sim'
args '--user=root'
}
}
steps {
sh "git config --global --add safe.directory '*'"
sh "git submodule update --init --recursive"
sh "git lfs pull"
lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) {
sh "${WORKSPACE}/tools/sim/build_container.sh"
sh "DETACH=1 ${WORKSPACE}/tools/sim/start_carla.sh"
sh "${WORKSPACE}/tools/sim/start_openpilot_docker.sh"
}
}
def setupCredentials() {
withCredentials([
string(credentialsId: 'azure_token', variable: 'AZURE_TOKEN'),
string(credentialsId: 'mapbox_token', variable: 'MAPBOX_TOKEN')
]) {
env.AZURE_TOKEN = "${AZURE_TOKEN}"
env.MAPBOX_TOKEN = "${MAPBOX_TOKEN}"
}
}
post {
always {
sh "docker kill carla_sim || true"
sh "rm -rf ${WORKSPACE}/* || true"
sh "rm -rf .* || true"
}
}
}
*/
node {
env.CI = "1"
env.PYTHONWARNINGS = "error"
env.TEST_DIR = "/data/openpilot"
env.SOURCE_DIR = "/data/openpilot_source/"
setupCredentials()
stage('PC tests') {
agent {
dockerfile {
filename 'Dockerfile.openpilot_base'
args '--user=root -v /tmp/comma_download_cache:/tmp/comma_download_cache'
}
}
steps {
sh "git config --global --add safe.directory '*'"
sh "git submodule update --init --depth=1 --recursive"
sh "git lfs pull"
// 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 "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"
}
env.GIT_BRANCH = checkout(scm).GIT_BRANCH
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
post {
always {
sh "rm -rf ${WORKSPACE}/* || true"
sh "rm -rf .* || true"
}
}
}
def excludeBranches = ['master-ci', 'devel', 'devel-staging', 'release3', 'release3-staging',
'dashcam3', 'dashcam3-staging', 'testing-closet*', 'hotfix-*']
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
stage('tizi-tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tizi", [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
["test camerad", "pytest system/camerad/test/test_camerad.py"],
["test exposure", "pytest system/camerad/test/test_exposure.py"],
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.py"],
])
}
}
stage('build') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
environment {
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
}
steps {
phone_steps("tici-needs-can", [
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"],
["build openpilot", "cd selfdrive/manager && ./build.py"],
["check dirty", "release/check-dirty.sh"],
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"],
])
}
}
stage('loopback-tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-loopback", [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
])
}
}
if (env.BRANCH_NAME != 'master') {
properties([
disableConcurrentBuilds(abortPrevious: true)
])
}
stage('HW + Unit Tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-common", [
["build", "cd selfdrive/manager && ./build.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test power draw", "pytest system/hardware/tici/tests/test_power_draw.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"],
["test pigeond", "pytest system/sensord/tests/test_pigeond.py"],
["test manager", "pytest selfdrive/manager/test/test_manager.py"],
["test nav", "pytest selfdrive/navd/tests/"],
])
}
}
try {
if (env.BRANCH_NAME == 'devel-staging') {
deviceStage("build release3-staging", "tici-needs-can", [], [
["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"],
])
}
stage('camerad') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-ar0231", [
["build", "cd selfdrive/manager && ./build.py"],
["test camerad", "pytest system/camerad/test/test_camerad.py"],
["test exposure", "pytest system/camerad/test/test_exposure.py"],
])
phone_steps("tici-ox03c10", [
["build", "cd selfdrive/manager && ./build.py"],
["test camerad", "pytest system/camerad/test/test_camerad.py"],
["test exposure", "pytest system/camerad/test/test_exposure.py"],
])
}
}
if (env.BRANCH_NAME == 'master-ci') {
deviceStage("build nightly", "tici-needs-can", [], [
["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"],
])
}
stage('sensord') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-lsmc", [
["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
])
phone_steps("tici-bmx-lsm", [
["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
])
}
if (!env.BRANCH_NAME.matches(excludeRegex)) {
parallel (
// tici tests
'onroad tests': {
deviceStage("onroad", "tici-needs-can", ["SKIP_COPY=1"], [
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"],
["build openpilot", "cd selfdrive/manager && ./build.py"],
["check dirty", "release/check-dirty.sh"],
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"],
])
},
'HW + Unit Tests': {
deviceStage("tici", "tici-common", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test power draw", "./system/hardware/tici/tests/test_power_draw.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"],
["test pigeond", "pytest system/sensord/tests/test_pigeond.py"],
["test manager", "pytest selfdrive/manager/test/test_manager.py"],
["test nav", "pytest selfdrive/navd/tests/"],
])
},
'loopback': {
deviceStage("tici", "tici-loopback", ["UNSAFE=1"], [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
])
},
'camerad': {
deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test camerad", "pytest system/camerad/test/test_camerad.py"],
["test exposure", "pytest system/camerad/test/test_exposure.py"],
])
deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test camerad", "pytest system/camerad/test/test_camerad.py"],
["test exposure", "pytest system/camerad/test/test_exposure.py"],
])
},
'sensord': {
deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
])
deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
])
},
'replay': {
deviceStage("tici", "tici-replay", ["UNSAFE=1"], [
["build", "cd selfdrive/manager && ./build.py"],
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
])
},
'tizi': {
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.py"],
])
},
// *** PC tests ***
'PC tests': {
pcStage("PC tests") {
// tests that our build system's dependencies are configured properly,
// needs a machine with lots of cores
sh label: "test multi-threaded build", script: "scons --no-cache --random -j42"
}
stage('replay') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-replay", [
["build", "cd selfdrive/manager && ./build.py"],
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
])
}
},
'car tests': {
pcStage("car tests") {
sh "scons -j30"
sh label: "test_models.py", script: "INTERNAL_SEG_CNT=250 INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt FILEREADER_CACHE=1 \
pytest -n42 --dist=loadscope selfdrive/car/tests/test_models.py"
sh label: "test_car_interfaces.py", script: "MAX_EXAMPLES=100 pytest -n42 selfdrive/car/tests/test_car_interfaces.py"
}
},
}
)
}
} catch (Exception e) {
currentBuild.result = 'FAILED'
throw e
}
}
}

@ -39,13 +39,17 @@ Running on a dedicated device in a car
------
To use openpilot in a car, you need four things
* A supported device to run this software: a [comma 3X](https://comma.ai/shop/comma-3x) or comma three.
* This software. The setup procedure of the comma 3/3X allows the user to enter a URL for custom software.
The URL, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another GitHub username can install a fork.
* One of [the 250+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run openpilot.
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car.
We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
1. **Supported Device:** A comma 3/3X. You can purchase these devices from (https://comma.ai/shop/comma-3x)
2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software.
To install the release version of openpilot, use the URL `openpilot.comma.ai`.
To install openpilot master (for more advanced users), use the URL `installer.comma.ai/commaai/master`. You can replace "commaai" with another GitHub username to install a fork.
3. **Supported Car:** Ensure that you have one of [the 250+ supported cars](docs/CARS.md). openpilot supports a wide range of car makes including Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford, and many more.
If your car is not officially listed as supported but has adaptive cruise control and lane-keeping assist, it's likely capable of running openpilot.
4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma 3/3X to your car.
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup).
Running on PC
------

@ -1,5 +1,12 @@
Version 0.9.5 (202X-XX-XX)
Version 0.9.5 (2023-09-27)
========================
* New driving model
* Improved navigate on openpilot performance using navigation instructions as an additional model input
* Hyundai Azera 2022 support thanks to sunnyhaibin!
* Hyundai Ioniq 6 2023 support thanks to sunnyhaibin, alamo3, and sshane!
* Hyundai Kona Electric 2023 (Korean version) support thanks to sunnyhaibin and haram-KONA!
* Kia K8 Hybrid (with HDA II) 2023 support thanks to sunnyhaibin!
* Kia Sorento Hybrid 2023 support thanks to sunnyhaibin!
* Lexus IS 2023 support thanks to L3R5!
Version 0.9.4 (2023-07-27)

@ -1,5 +1,4 @@
import os
import shutil
import subprocess
import sys
import sysconfig
@ -15,10 +14,6 @@ AGNOS = TICI
Decider('MD5-timestamp')
AddOption('--extras',
action='store_true',
help='build misc extras, like setup and installer files')
AddOption('--kaitai',
action='store_true',
help='Regenerate kaitai struct parsers')
@ -49,21 +44,16 @@ AddOption('--external-sconscript',
dest='external_sconscript',
help='add an external SConscript to the build')
AddOption('--no-thneed',
action='store_true',
dest='no_thneed',
help='avoid using thneed')
AddOption('--pc-thneed',
action='store_true',
dest='pc_thneed',
help='use thneed on pc')
AddOption('--no-test',
AddOption('--minimal',
action='store_false',
dest='test',
dest='extras',
default=os.path.islink(Dir('#laika/').abspath),
help='skip building test files')
help='the minimum build to run openpilot. no tests, tools, etc.')
## Architecture name breakdown (arch)
## - larch64: linux tici aarch64
@ -254,18 +244,6 @@ def progress_function(node):
if os.environ.get('SCONS_PROGRESS'):
Progress(progress_function, interval=node_interval)
SHARED = False
# TODO: this can probably be removed
def abspath(x):
if arch == 'aarch64':
pth = os.path.join("/data/pythonpath", x[0].path)
env.Depends(pth, x)
return File(pth)
else:
# rpath works elsewhere
return x[0].path.rsplit("/", 1)[1][:-3]
# Cython build environment
py_include = sysconfig.get_paths()['include']
envCython = env.Clone()
@ -336,18 +314,6 @@ qt_env['CXXFLAGS'] += qt_flags
qt_env['LIBPATH'] += ['#selfdrive/ui']
qt_env['LIBS'] = qt_libs
# Have to respect cache-readonly
if GetOption('cache_readonly'):
local_moc_files_dir = Dir("#.moc_files").abspath
cache_moc_files_dir = cache_dir + "/moc_files"
if os.path.exists(local_moc_files_dir):
shutil.rmtree(local_moc_files_dir)
if os.path.exists(cache_moc_files_dir):
shutil.copytree(cache_moc_files_dir, local_moc_files_dir)
qt_env['QT3_MOCHPREFIX'] = local_moc_files_dir + "/moc_"
else:
qt_env['QT3_MOCHPREFIX'] = cache_dir + '/moc_files/moc_'
if GetOption("clazy"):
checks = [
"level0",
@ -359,33 +325,35 @@ if GetOption("clazy"):
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
Export('env', 'qt_env', 'arch', 'real_arch', 'SHARED')
Export('env', 'qt_env', 'arch', 'real_arch')
# Build common module
SConscript(['common/SConscript'])
Import('_common', '_gpucommon')
if SHARED:
common, gpucommon = abspath(common), abspath(gpucommon)
else:
common = [_common, 'json11']
gpucommon = [_gpucommon]
common = [_common, 'json11']
gpucommon = [_gpucommon]
Export('common', 'gpucommon')
# cereal and messaging are shared with the system
# Build cereal and messaging
SConscript(['cereal/SConscript'])
if SHARED:
cereal = abspath([File('cereal/libcereal_shared.so')])
messaging = abspath([File('cereal/libmessaging_shared.so')])
else:
cereal = [File('#cereal/libcereal.a')]
messaging = [File('#cereal/libmessaging.a')]
visionipc = [File('#cereal/libvisionipc.a')]
Export('cereal', 'messaging', 'visionipc')
cereal = [File('#cereal/libcereal.a')]
messaging = [File('#cereal/libmessaging.a')]
visionipc = [File('#cereal/libvisionipc.a')]
messaging_python = [File('#cereal/messaging/messaging_pyx.so')]
# Build rednose library and ekf models
Export('cereal', 'messaging', 'messaging_python', 'visionipc')
# Build other submodules
SConscript([
'body/board/SConscript',
'opendbc/can/SConscript',
'panda/SConscript',
])
# Build rednose library and ekf models
rednose_deps = [
"#selfdrive/locationd/models/constants.py",
"#selfdrive/locationd/models/gnss_helpers.py",
@ -427,20 +395,8 @@ if arch != "Darwin":
])
# Build openpilot
# build submodules
SConscript([
'body/board/SConscript',
'cereal/SConscript',
'opendbc/can/SConscript',
'panda/SConscript',
])
SConscript(['third_party/SConscript'])
SConscript(['common/kalman/SConscript'])
SConscript(['common/transformations/SConscript'])
SConscript(['selfdrive/boardd/SConscript'])
SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript'])
SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript'])
@ -449,7 +405,7 @@ SConscript(['selfdrive/navd/SConscript'])
SConscript(['selfdrive/modeld/SConscript'])
SConscript(['selfdrive/ui/SConscript'])
if (arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists()) or GetOption('extras'):
if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'):
SConscript(['tools/replay/SConscript'])
SConscript(['tools/cabana/SConscript'])

@ -1 +1 @@
Subproject commit 396fa7b923099640d6843f338950ed41300793ec
Subproject commit 6ff44357a3e416d29044b1d085a3e9223db9691a

@ -1 +1 @@
Subproject commit 1ee48e0110a46fbdd9db50ed89a38bb5a748cfcb
Subproject commit 4b334f6f10877e4a666b23983de2d27934ebf3b1

@ -1,9 +1,4 @@
Import('env', 'envCython', 'arch', 'SHARED')
if SHARED:
fxn = env.SharedLibrary
else:
fxn = env.Library
Import('env', 'envCython', 'arch')
common_libs = [
'params.cc',
@ -12,24 +7,35 @@ common_libs = [
'util.cc',
'i2c.cc',
'watchdog.cc',
'ratekeeper.cc'
]
if arch != "Darwin":
common_libs.append('gpio.cc')
_common = fxn('common', common_libs, LIBS="json11")
_common = env.Library('common', common_libs, LIBS="json11")
files = [
'clutil.cc',
]
_gpucommon = fxn('gpucommon', files)
_gpucommon = env.Library('gpucommon', files)
Export('_common', '_gpucommon')
if GetOption('test'):
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'])
if GetOption('extras'):
env.Program('tests/test_common',
['tests/test_runner.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc', 'tests/test_ratekeeper.cc'],
LIBS=[_common, 'json11', 'zmq', 'pthread'])
# Cython bindings
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
SConscript([
'kalman/SConscript',
'transformations/SConscript'
])
Import('simple_kalman_python', 'transformations_python')
common_python = [params_python, simple_kalman_python, transformations_python]
# Cython
envCython.Program('clock.so', 'clock.pyx')
envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
Export('common_python')

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

@ -1,7 +1,7 @@
import os
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__)), "../"))

@ -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;
}
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) {
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_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_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);

@ -1,5 +1,7 @@
#include "common/gpio.h"
#include <string>
#ifdef __APPLE__
int gpio_init(int pin_nr, bool output) {
return 0;
@ -29,7 +31,7 @@ int gpio_init(int pin_nr, bool output) {
char pin_dir_path[50];
int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
"/sys/class/gpio/gpio%d/direction", pin_nr);
if(pin_dir_path_len <= 0) {
if (pin_dir_path_len <= 0) {
return -1;
}
const char *value = output ? "out" : "in";
@ -40,7 +42,7 @@ int gpio_set(int pin_nr, bool high) {
char pin_val_path[50];
int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
"/sys/class/gpio/gpio%d/value", pin_nr);
if(pin_val_path_len <= 0) {
if (pin_val_path_len <= 0) {
return -1;
}
return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
@ -68,7 +70,7 @@ int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int p
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1);
int ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
if (ret == -1) {
LOGE("Unable to get line event from ioctl : %s", strerror(errno));
close(fd);

@ -5,7 +5,7 @@
#define GPIO_HUB_RST_N 30
#define GPIO_UBLOX_RST_N 32
#define GPIO_UBLOX_SAFEBOOT_N 33
#define GPIO_UBLOX_PWR_EN 34
#define GPIO_GNSS_PWR_EN 34 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
#define GPIO_STM_RST_N 124
#define GPIO_STM_BOOT0 134
#define GPIO_BMX_ACCEL_INT 21
@ -17,7 +17,7 @@
#define GPIO_HUB_RST_N 0
#define GPIO_UBLOX_RST_N 0
#define GPIO_UBLOX_SAFEBOOT_N 0
#define GPIO_UBLOX_PWR_EN 0
#define GPIO_GNSS_PWR_EN 0 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
#define GPIO_STM_RST_N 0
#define GPIO_STM_BOOT0 0
#define GPIO_BMX_ACCEL_INT 0

@ -8,7 +8,6 @@
#include <cstdio>
#include <stdexcept>
#include "common/util.h"
#include "common/swaglog.h"
#include "common/util.h"
@ -26,36 +25,42 @@ I2CBus::I2CBus(uint8_t bus_id) {
snprintf(bus_name, 20, "/dev/i2c-%d", bus_id);
i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR));
if(i2c_fd < 0) {
if (i2c_fd < 0) {
throw std::runtime_error("Failed to open I2C bus");
}
}
I2CBus::~I2CBus() {
if(i2c_fd >= 0) { close(i2c_fd); }
if (i2c_fd >= 0) {
close(i2c_fd);
}
}
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
std::lock_guard lk(m);
int ret = 0;
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
if(ret < 0) { goto fail; }
if (ret < 0) { goto fail; }
ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer);
if((ret < 0) || (ret != len)) { goto fail; }
if ((ret < 0) || (ret != len)) { goto fail; }
fail:
return ret;
}
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
std::lock_guard lk(m);
int ret = 0;
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
if(ret < 0) { goto fail; }
if (ret < 0) { goto fail; }
ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data);
if(ret < 0) { goto fail; }
if (ret < 0) { goto fail; }
fail:
return ret;

@ -1,12 +1,14 @@
#pragma once
#include <cstdint>
#include <mutex>
#include <sys/types.h>
class I2CBus {
private:
int i2c_fd;
std::mutex m;
public:
I2CBus(uint8_t bus_id);

@ -1,3 +1,5 @@
Import('envCython')
envCython.Program('simple_kalman_impl.so', 'simple_kalman_impl.pyx')
simple_kalman_python = envCython.Program('simple_kalman_impl.so', 'simple_kalman_impl.pyx')
Export('simple_kalman_python')

@ -1,3 +1,12 @@
# 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
import numpy as np
def get_kalman_gain(dt, A, C, Q, R, iterations=100):
P = np.zeros_like(Q)
for _ in range(iterations):
P = A.dot(P).dot(A.T) + dt * Q
S = C.dot(P).dot(C.T) + R
K = P.dot(C.T).dot(np.linalg.inv(S))
P = (np.eye(len(P)) - K.dot(C)).dot(P)
return K

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

@ -197,7 +197,7 @@ class SwagLogger(logging.Logger):
filename = os.path.normcase(co.co_filename)
# TODO: is this pylint exception correct?
if filename == _srcfile: # pylint: disable=comparison-with-callable
if filename == _srcfile:
f = f.f_back
continue
sinfo = None

@ -1,7 +1,7 @@
#pragma once
typedef struct vec3 {
float v[3];
float v[3];
} vec3;
typedef struct vec4 {
@ -9,7 +9,7 @@ typedef struct vec4 {
} vec4;
typedef struct mat3 {
float v[3*3];
float v[3*3];
} mat3;
typedef struct mat4 {

@ -64,7 +64,7 @@ bool create_params_path(const std::string &param_path, const std::string &key_pa
std::string ensure_params_path(const std::string &prefix, const std::string &path = {}) {
std::string params_path = path.empty() ? Path::params() : path;
if (!create_params_path(params_path, params_path + prefix)) {
throw std::runtime_error(util::string_format("Failed to ensure params path, errno=%d", errno));
throw std::runtime_error(util::string_format("Failed to ensure params path, errno=%d, path=%s", errno, params_path.c_str()));
}
return params_path;
}
@ -158,6 +158,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"LongitudinalPersonality", PERSISTENT},
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"NavPastDestinations", PERSISTENT},
{"NavSettingLeftSide", PERSISTENT},
{"NavSettingTime24h", PERSISTENT},
{"NavdRender", PERSISTENT},

@ -15,7 +15,11 @@ enum ParamKeyType {
class Params {
public:
Params(const std::string &path = {});
explicit Params(const std::string &path = {});
// Not copyable.
Params(const Params&) = delete;
Params& operator=(const Params&) = delete;
std::vector<std::string> allKeys() const;
bool checkKey(const std::string &key);
ParamKeyType getKeyType(const std::string &key);

@ -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
assert Params
assert ParamKeyType
assert UnknownKeyName

@ -14,7 +14,7 @@ cdef extern from "common/params.h":
ALL
cdef cppclass c_Params "Params":
c_Params(string) nogil
c_Params(string) nogil except +
string get(string, bool) nogil
bool getBool(string, bool) nogil
int remove(string) nogil

@ -2,13 +2,14 @@ import os
import shutil
import uuid
from typing import List, Optional
from typing import Optional
from common.params import Params
from openpilot.common.params import Params
from openpilot.system.hardware.hw import Paths
class OpenpilotPrefix(object):
class OpenpilotPrefix:
def __init__(self, prefix: Optional[str] = None, clean_dirs_on_exit: bool = True):
self.prefix = prefix if prefix else str(uuid.uuid4())
self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15])
self.msgq_path = os.path.join('/dev/shm', self.prefix)
self.clean_dirs_on_exit = clean_dirs_on_exit
@ -18,13 +19,17 @@ class OpenpilotPrefix(object):
os.mkdir(self.msgq_path)
except FileExistsError:
pass
os.makedirs(Paths.log_root(), exist_ok=True)
return self
def __exit__(self, exc_type, exc_obj, exc_tb):
if self.clean_dirs_on_exit:
self.clean_dirs()
del os.environ['OPENPILOT_PREFIX']
try:
del os.environ['OPENPILOT_PREFIX']
except KeyError:
pass
return False
def clean_dirs(self):
@ -33,17 +38,6 @@ class OpenpilotPrefix(object):
shutil.rmtree(os.path.realpath(symlink_path), ignore_errors=True)
os.remove(symlink_path)
shutil.rmtree(self.msgq_path, ignore_errors=True)
class DummySocket:
def __init__(self):
self.data: List[bytes] = []
def receive(self, non_blocking: bool = False) -> Optional[bytes]:
if non_blocking:
return None
return self.data.pop()
def send(self, data: bytes):
self.data.append(data)
shutil.rmtree(Paths.log_root(), ignore_errors=True)
shutil.rmtree(Paths.download_cache_root(), ignore_errors=True)
shutil.rmtree(Paths.comma_home(), ignore_errors=True)

@ -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 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 system.hardware import PC
from openpilot.system.hardware import PC
# time step for each process
@ -31,12 +30,12 @@ class Priority:
def set_realtime_priority(level: int) -> None:
if not PC:
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level)) # pylint: disable=no-member
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level))
def set_core_affinity(cores: List[int]) -> None:
if not PC:
os.sched_setaffinity(0, cores) # pylint: disable=no-member
os.sched_setaffinity(0, cores)
def config_realtime_process(cores: Union[int, List[int]], priority: int) -> None:
@ -50,13 +49,13 @@ class Ratekeeper:
def __init__(self, rate: float, print_delay_threshold: Optional[float] = 0.0) -> None:
"""Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
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._frame = 0
self._remaining = 0.0
self._process_name = getproctitle()
self._dts = deque([self._interval], maxlen=100)
self._last_monitor_time = sec_since_boot()
self._last_monitor_time = time.monotonic()
@property
def frame(self) -> int:
@ -82,11 +81,11 @@ class Ratekeeper:
# this only monitor the cumulative lag, but does not enforce a rate
def monitor_time(self) -> bool:
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)
lagged = False
remaining = self._next_frame_time - sec_since_boot()
remaining = self._next_frame_time - time.monotonic()
self._next_frame_time += self._interval
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")

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

@ -12,7 +12,7 @@
#include <string>
#include <zmq.h>
#include "json11.hpp"
#include "third_party/json11/json11.hpp"
#include "common/util.h"
#include "common/version.h"
@ -20,7 +20,7 @@
class SwaglogState : public LogState {
public:
SwaglogState() : LogState("ipc:///tmp/logmessage") {}
SwaglogState() : LogState(Path::swaglog_ipc().c_str()) {}
json11::Json::object ctx_j;
@ -64,8 +64,7 @@ static void log(int levelnum, const char* filename, int lineno, const char* func
if (levelnum >= s.print_level) {
printf("%s: %s\n", filename, msg);
}
char levelnum_c = levelnum;
zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK);
zmq_send(s.sock, log_s.data(), log_s.length(), ZMQ_NOBLOCK);
}
static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func,
@ -87,8 +86,11 @@ static void cloudlog_common(int levelnum, const char* filename, int lineno, cons
log_j["msg"] = msg_j;
}
std::string log_s = ((json11::Json)log_j).dump();
std::string log_s;
log_s += (char)levelnum;
((json11::Json)log_j).dump(log_s);
log(levelnum, filename, lineno, func, msg_buf, log_s);
free(msg_buf);
}

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

@ -1,2 +1 @@
test_util
test_swaglog
test_common

@ -2,8 +2,8 @@ import os
import unittest
from uuid import uuid4
from 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_on_fs_tmp
from openpilot.common.file_helpers import atomic_write_in_dir
class TestFileHelpers(unittest.TestCase):

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

@ -1,21 +1,14 @@
import os
import threading
import time
import tempfile
import shutil
import uuid
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):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
print("using", self.tmpdir)
self.params = Params(self.tmpdir)
def tearDown(self):
shutil.rmtree(self.tmpdir)
self.params = Params()
def test_params_put_and_get(self):
self.params.put("DongleId", "cb38263377b873ee")
@ -90,19 +83,19 @@ class TestParams(unittest.TestCase):
self.assertFalse(self.params.get_bool("IsMetric"))
def test_put_non_blocking_with_get_block(self):
q = Params(self.tmpdir)
q = Params()
def _delayed_writer():
time.sleep(0.1)
put_nonblocking("CarParams", "test", self.tmpdir)
put_nonblocking("CarParams", "test")
threading.Thread(target=_delayed_writer).start()
assert q.get("CarParams") is None
assert q.get("CarParams", True) == b"test"
def test_put_bool_non_blocking_with_get_block(self):
q = Params(self.tmpdir)
q = Params()
def _delayed_writer():
time.sleep(0.1)
put_bool_nonblocking("CarParams", True, self.tmpdir)
put_bool_nonblocking("CarParams", True)
threading.Thread(target=_delayed_writer).start()
assert q.get("CarParams") is None
assert q.get("CarParams", True) == b"1"

@ -0,0 +1,23 @@
#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);
int lags = 0;
int bad_keep_times = 0;
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();
lags += lagged;
bad_keep_times += (seconds_since_boot() - begin - (1 / freq)) > 1e-3;
}
// need a tolerance here due to scheduling
REQUIRE(lags < 5);
REQUIRE(bad_keep_times < 5);
}

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"

@ -1,15 +1,14 @@
#include <zmq.h>
#include <iostream>
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "json11.hpp"
#include "catch2/catch.hpp"
#include "common/swaglog.h"
#include "common/util.h"
#include "common/version.h"
#include "system/hardware/hw.h"
#include "third_party/json11/json11.hpp"
const char *SWAGLOG_ADDR = "ipc:///tmp/logmessage";
std::string daemon_name = "testy";
std::string dongle_id = "test_dongle_id";
int LINE_NO = 0;
@ -25,7 +24,7 @@ void log_thread(int thread_id, int msg_cnt) {
void recv_log(int thread_cnt, int thread_msg_cnt) {
void *zctx = zmq_ctx_new();
void *sock = zmq_socket(zctx, ZMQ_PULL);
zmq_bind(sock, SWAGLOG_ADDR);
zmq_bind(sock, Path::swaglog_ipc().c_str());
std::vector<int> thread_msgs(thread_cnt);
int total_count = 0;

@ -5,10 +5,10 @@
#include <algorithm>
#include <climits>
#include <fstream>
#include <random>
#include <string>
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "common/util.h"
@ -38,7 +38,8 @@ TEST_CASE("util::read_file") {
std::string content = random_bytes(64 * 1024);
write(fd, content.c_str(), content.size());
std::string ret = util::read_file(filename);
REQUIRE(ret == content);
bool equal = (ret == content);
REQUIRE(equal);
close(fd);
}
SECTION("read directory") {
@ -108,7 +109,8 @@ TEST_CASE("util::safe_fwrite") {
REQUIRE(ret == 0);
ret = fclose(f);
REQUIRE(ret == 0);
REQUIRE(dat == util::read_file(filename));
bool equal = (dat == util::read_file(filename));
REQUIRE(equal);
}
TEST_CASE("util::create_directories") {

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

@ -1,6 +1,5 @@
Import('env', 'envCython')
transformations = env.Library('transformations', ['orientation.cc', 'coordinates.cc'])
Export('transformations')
envCython.Program('transformations.so', 'transformations.pyx')
transformations_python = envCython.Program('transformations.so', 'transformations.pyx')
Export('transformations', 'transformations_python')

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

@ -1,13 +1,11 @@
#define _USE_MATH_DEFINES
#include "common/transformations/coordinates.hpp"
#include <iostream>
#include <cmath>
#include <eigen3/Eigen/Dense>
#include "coordinates.hpp"
double a = 6378137; // lgtm [cpp/short-global-name]
double b = 6356752.3142; // lgtm [cpp/short-global-name]
double esq = 6.69437999014 * 0.001; // lgtm [cpp/short-global-name]

@ -1,5 +1,7 @@
#pragma once
#include <eigen3/Eigen/Dense>
#define DEG2RAD(x) ((x) * M_PI / 180.0)
#define RAD2DEG(x) ((x) * 180.0 / M_PI)

@ -1,8 +1,7 @@
# pylint: skip-file
from common.transformations.orientation import numpy_wrap
from common.transformations.transformations import (ecef2geodetic_single,
from openpilot.common.transformations.orientation import numpy_wrap
from openpilot.common.transformations.transformations import (ecef2geodetic_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):

@ -1,7 +1,9 @@
import numpy as np
from common.transformations.camera import (FULL_FRAME_SIZE,
get_view_frame_from_calib_frame)
from openpilot.common.transformations.orientation import rot_from_euler
from openpilot.common.transformations.camera import (
FULL_FRAME_SIZE, get_view_frame_from_calib_frame, view_frame_from_device_frame,
eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics)
# segnet
SEGNET_SIZE = (512, 384)
@ -57,61 +59,20 @@ medmodel_frame_from_calib_frame = np.dot(medmodel_intrinsics,
medmodel_frame_from_bigmodel_frame = np.dot(medmodel_intrinsics, np.linalg.inv(bigmodel_intrinsics))
calib_from_medmodel = np.linalg.inv(medmodel_frame_from_calib_frame[:, :3])
calib_from_sbigmodel = np.linalg.inv(sbigmodel_frame_from_calib_frame[:, :3])
### This function mimics the update_calibration logic in modeld.cc
### 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):
from 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
if tici and wide_cam:
intrinsics = tici_ecam_intrinsics
elif tici:
intrinsics = tici_fcam_intrinsics
else:
intrinsics = eon_fcam_intrinsics
if big_model:
sbigmodel_from_calib = sbigmodel_frame_from_calib_frame[:, (0,1,2)]
calib_from_model = np.linalg.inv(sbigmodel_from_calib)
else:
medmodel_from_calib = medmodel_frame_from_calib_frame[:, (0,1,2)]
calib_from_model = np.linalg.inv(medmodel_from_calib)
device_from_calib = rot_from_euler(rpy_calib)
camera_from_calib = intrinsics.dot(view_frame_from_device_frame.dot(device_from_calib))
warp_matrix = camera_from_calib.dot(calib_from_model)
return warp_matrix
### This is old, just for debugging
def get_warp_matrix_old(rpy_calib, wide_cam=False, big_model=False, tici=True):
from 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
def get_view_frame_from_road_frame(roll, pitch, yaw, height):
device_from_road = rot_from_euler([roll, pitch, yaw]).dot(np.diag([1, -1, -1]))
view_from_road = view_frame_from_device_frame.dot(device_from_road)
return np.hstack((view_from_road, [[0], [height], [0]]))
if tici and wide_cam:
intrinsics = tici_ecam_intrinsics
# This function is verified to give similar results to xx.uncommon.utils.transform_img
def get_warp_matrix(device_from_calib_euler: np.ndarray, wide_camera: bool = False, bigmodel_frame: bool = False, tici: bool = True) -> np.ndarray:
if tici and wide_camera:
cam_intrinsics = tici_ecam_intrinsics
elif tici:
intrinsics = tici_fcam_intrinsics
cam_intrinsics = tici_fcam_intrinsics
else:
intrinsics = eon_fcam_intrinsics
cam_intrinsics = eon_fcam_intrinsics
model_height = 1.22
if big_model:
model_from_road = np.dot(sbigmodel_intrinsics,
get_view_frame_from_road_frame(0, 0, 0, model_height))
else:
model_from_road = np.dot(medmodel_intrinsics,
get_view_frame_from_road_frame(0, 0, 0, model_height))
ground_from_model = np.linalg.inv(model_from_road[:, (0, 1, 3)])
E = get_view_frame_from_road_frame(*rpy_calib, 1.22)
camera_frame_from_road_frame = intrinsics.dot(E)
camera_frame_from_ground = camera_frame_from_road_frame[:,(0,1,3)]
warp_matrix = camera_frame_from_ground .dot(ground_from_model)
calib_from_model = calib_from_sbigmodel if bigmodel_frame else calib_from_medmodel
device_from_calib = rot_from_euler(device_from_calib_euler)
camera_from_calib = cam_intrinsics @ view_frame_from_device_frame @ device_from_calib
warp_matrix: np.ndarray = camera_from_calib @ calib_from_model
return warp_matrix

@ -4,8 +4,8 @@
#include <cmath>
#include <eigen3/Eigen/Dense>
#include "orientation.hpp"
#include "coordinates.hpp"
#include "common/transformations/orientation.hpp"
#include "common/transformations/coordinates.hpp"
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat){
if (quat.w() > 0){

@ -1,6 +1,6 @@
#pragma once
#include <eigen3/Eigen/Dense>
#include "coordinates.hpp"
#include "common/transformations/coordinates.hpp"
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat);

@ -1,8 +1,7 @@
# pylint: skip-file
import numpy as np
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,
euler2rot_single,
ned_euler_from_ecef_single,

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

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

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

@ -3,7 +3,6 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <dirent.h>
#include <cassert>
#include <cerrno>
@ -253,6 +252,14 @@ std::string dir_name(std::string const &path) {
return path.substr(0, pos);
}
bool starts_with(const std::string &s1, const std::string &s2) {
return strncmp(s1.c_str(), s2.c_str(), s2.size()) == 0;
}
bool ends_with(const std::string &s1, const std::string &s2) {
return strcmp(s1.c_str() + (s1.size() - s2.size()), s2.c_str()) == 0;
}
std::string check_output(const std::string& command) {
char buffer[128];
std::string result;

@ -77,6 +77,8 @@ float getenv(const char* key, float default_val);
std::string hexdump(const uint8_t* in, const size_t size);
std::string dir_name(std::string const& path);
bool starts_with(const std::string &s1, const std::string &s2);
bool ends_with(const std::string &s1, const std::string &s2);
// ***** random helpers *****
int random_int(int min, int max);
@ -115,7 +117,7 @@ public:
#ifndef __APPLE__
std::signal(SIGPWR, (sighandler_t)set_do_exit);
#endif
};
}
inline static std::atomic<bool> power_failure = false;
inline static std::atomic<int> signal = 0;
inline operator bool() { return do_exit; }
@ -151,12 +153,18 @@ struct unique_fd {
class FirstOrderFilter {
public:
FirstOrderFilter(float x0, float ts, float dt) {
FirstOrderFilter(float x0, float ts, float dt, bool initialized = true) {
k_ = (dt / ts) / (1.0 + dt / ts);
x_ = x0;
initialized_ = initialized;
}
inline float update(float x) {
x_ = (1. - k_) * x_ + k_ * x;
if (initialized_) {
x_ = (1. - k_) * x_ + k_ * x;
} else {
initialized_ = true;
x_ = x;
}
return x_;
}
inline void reset(float x) { x_ = x; }
@ -164,12 +172,13 @@ public:
private:
float x_, k_;
bool initialized_;
};
template<typename T>
void update_max_atomic(std::atomic<T>& max, T const& value) {
T prev = max;
while(prev < value && !max.compare_exchange_weak(prev, value)) {}
while (prev < value && !max.compare_exchange_weak(prev, value)) {}
}
class LogState {
@ -179,9 +188,9 @@ class LogState {
void *zctx = nullptr;
void *sock = nullptr;
int print_level;
const char* endpoint;
std::string endpoint;
LogState(const char* _endpoint) {
LogState(std::string _endpoint) {
endpoint = _endpoint;
}
@ -193,7 +202,7 @@ class LogState {
int timeout = 100;
zmq_setsockopt(sock, ZMQ_LINGER, &timeout, sizeof(timeout));
zmq_connect(sock, endpoint);
zmq_connect(sock, endpoint.c_str());
initialized = true;
}

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

@ -1,6 +1,6 @@
import sys
import pygame # pylint: disable=import-error
import cv2 # pylint: disable=import-error
import pygame
import cv2
class Window:
def __init__(self, w, h, caption="window", double=False, halve=False):

@ -0,0 +1,10 @@
import pytest
from openpilot.common.prefix import OpenpilotPrefix
@pytest.fixture(scope="function", autouse=True)
def global_setup_and_teardown():
# setup a clean environment for each test
with OpenpilotPrefix():
yield

@ -2,9 +2,9 @@
# Supported Cars
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 device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 257 Supported Cars
# 262 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|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -71,6 +71,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Honda&model=Passport 2019-23">Buy Here</a></sub></details>||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Azera 2022">Buy Here</a></sub></details>||
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Elantra 2017-19">Buy Here</a></sub></details>||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai 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=Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Elantra GT 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 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=Hyundai&model=Elantra GT 2017-19">Buy Here</a></sub></details>||
@ -80,15 +81,17 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<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 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 (Southeast Asia only) 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 6 (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P 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 6 (with HDA II) 2023">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 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 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)|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 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 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 2020-22|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 Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 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 2020-22">Buy Here</a></sub></details>||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai 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=Hyundai&model=Kona 2020">Buy Here</a></sub></details>||
|Hyundai|Kona Electric 2018-21|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=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>||
|Hyundai|Kona Electric 2022|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 O 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=Kona Electric 2022">Buy Here</a></sub></details>||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 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=Kona Electric 2018-21">Buy Here</a></sub></details>||
|Hyundai|Kona Electric 2022-23|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 O 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=Kona Electric 2022-23">Buy Here</a></sub></details>||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 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=Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Santa Cruz 2022-23">Buy Here</a></sub></details>||
@ -107,7 +110,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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=Veloster 2019-20">Buy Here</a></sub></details>||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA 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=Jeep&model=Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA 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=Jeep&model=Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Carnival 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 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=Carnival 2023">Buy Here</a></sub></details>||
|Kia|Carnival 2023-24[<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=Carnival 2023-24">Buy Here</a></sub></details>||
|Kia|Carnival (China only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai 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=Carnival (China only) 2023">Buy Here</a></sub></details>||
|Kia|Ceed 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=Ceed 2019">Buy Here</a></sub></details>||
|Kia|EV6 (Southeast Asia only) 2022-23[<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 P 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=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
@ -117,6 +120,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Forte 2023|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 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=Forte 2023">Buy Here</a></sub></details>||
|Kia|K5 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 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=K5 2021-22">Buy Here</a></sub></details>||
|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai 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=K5 Hybrid 2020">Buy Here</a></sub></details>||
|Kia|K8 Hybrid (with HDA II) 2023[<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=Kia&model=K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai 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=Kia&model=Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Niro EV 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 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 EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 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 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@ -124,15 +128,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 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 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 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 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|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 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|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 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 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>|
@ -183,7 +188,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Škoda|Kamiq 2021[<sup>9,11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kamiq 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|Škoda|Karoq 2019-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Karoq 2019-23">Buy Here</a></sub></details>||
|Škoda|Kodiaq 2017-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|Škoda|Octavia 2015, 2018-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia 2015, 2018-19">Buy Here</a></sub></details>||
|Škoda|Octavia 2015-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia 2015-19">Buy Here</a></sub></details>||
|Škoda|Octavia RS 2016[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|Škoda|Scala 2020-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|Škoda|Superb 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
@ -267,7 +272,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
### Footnotes
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
<sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
<sup>4</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />

@ -1,5 +1,4 @@
# type: ignore
# pylint: skip-file
# Configuration file for the Sphinx documentation builder.
#
@ -17,8 +16,8 @@ import os
import sys
from os.path import exists
from common.basedir import BASEDIR
from system.version import get_version
from openpilot.common.basedir import BASEDIR
from openpilot.system.version import get_version
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))

@ -1,8 +1,8 @@
FROM ghcr.io/commaai/openpilot-base:latest as openpilot-docs-base
FROM ghcr.io/commaai/openpilot-base:latest
ENV PYTHONUNBUFFERED 1
ENV OPENPILOT_PATH /home/batman/openpilot/
ENV OPENPILOT_PATH /tmp/openpilot
ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
ENV POETRY_VIRUALENVS_CREATE false
@ -11,11 +11,12 @@ WORKDIR ${OPENPILOT_PATH}
COPY SConstruct ${OPENPILOT_PATH}
COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./body ${OPENPILOT_PATH}/body
COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./site_scons ${OPENPILOT_PATH}/site_scons
COPY ./laika ${OPENPILOT_PATH}/laika
COPY ./laika_repo ${OPENPILOT_PATH}/laika_repo
RUN ln -s ${OPENPILOT_PATH}/laika_repo/laika/ ${OPENPILOT_PATH}/laika
COPY ./rednose ${OPENPILOT_PATH}/rednose
COPY ./rednose_repo ${OPENPILOT_PATH}/rednose_repo
COPY ./tools ${OPENPILOT_PATH}/tools
@ -28,7 +29,7 @@ COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system
COPY ./*.md ${OPENPILOT_PATH}/
RUN scons -j$(nproc)
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly
RUN apt update && apt install doxygen -y
COPY ./docs ${OPENPILOT_PATH}/docs
@ -37,5 +38,5 @@ WORKDIR ${OPENPILOT_PATH}/docs
RUN make html
FROM nginx:1.21
COPY --from=0 /home/batman/openpilot/build/docs/html /usr/share/nginx/html
COPY --from=0 /tmp/openpilot/build/docs/html /usr/share/nginx/html
COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf

@ -1 +1 @@
Subproject commit ef21c612f4b0d68c790b4b1a6d637cd5fce7732e
Subproject commit 8861844c9b577ff7de7d03fab9f4d7f560415fc9

@ -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 a1582f5e28fe0df23b6821c907188be477aac11c
Subproject commit 4ab347baefb7473771ada0723c969c50d0c28d01

@ -1 +1 @@
Subproject commit 5681f84f4e79c45ac1387cedf70cf66be9729884
Subproject commit dfbee695affcc37378b57a2e9a96e157eb41b7ab

2186
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -1,8 +1,62 @@
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "--ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/"
addopts = "--ignore=openpilot/ --ignore=cereal/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers --durations=10"
#cpp_files = "test_*" # uncomment when agnos has pytest-cpp and remove from CI
python_files = "test_*.py"
timeout = "30" # you get this long by default
#timeout = "30" # you get this long by default
markers = [
"parallel: mark tests as parallelizable (tests with no global state, so can be run in parallel)"
]
testpaths = [
"common",
"selfdrive/athena",
"selfdrive/boardd",
"selfdrive/car",
"selfdrive/controls",
"selfdrive/locationd",
"selfdrive/monitoring",
"selfdrive/thermald",
"selfdrive/test/longitudinal_maneuvers",
"system/hardware/tici",
"system/loggerd",
"system/proclogd",
"system/tests",
"system/ubloxd",
"tools/lib/tests",
"tools/replay",
"tools/cabana"
]
[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]
name = "openpilot"
@ -22,6 +76,7 @@ aiohttp = "*"
aiortc = "*"
casadi = "==3.6.3"
cffi = "*"
control = "*"
crcmod = "*"
cryptography = "*"
Cython = "*"
@ -39,6 +94,7 @@ psutil = "*"
pyaudio = "*"
pycapnp = "*"
pycryptodome = "*"
pycurl = "*"
pydub = "*"
PyJWT = "*"
pyopencl = "*"
@ -64,7 +120,8 @@ sconscontrib = {git = "https://github.com/SCons/scons-contrib.git"}
[tool.poetry.group.dev.dependencies]
av = "*"
azure-storage-blob = "~2.1"
azure-identity = "*"
azure-storage-blob = "*"
breathe = "*"
carla = { url = "https://github.com/commaai/carla/releases/download/3.11.4/carla-0.9.14-cp311-cp311-linux_x86_64.whl", platform = "linux", markers = "platform_machine == 'x86_64'" }
coverage = "*"
@ -75,6 +132,7 @@ inputs = "*"
lru-dict = "*"
markdown-it-py = "*"
matplotlib = "*"
metadrive-simulator = { git = "https://github.com/metadriverse/metadrive.git", rev ="7d6f8ef707bfff67c6b88f3b4a98f8b1d58bf8e6", markers = "platform_machine != 'aarch64'" } # no linux/aarch64 wheels for certain dependencies
mpld3 = "*"
mypy = "*"
myst-parser = "*"
@ -84,11 +142,16 @@ pandas = "*"
parameterized = "^0.8"
pprofile = "*"
pre-commit = "*"
pycurl = "*"
pygame = "*"
pyprof2calltree = "*"
pytest = "*"
pytest-cov = "*"
pytest-cpp = "*"
pytest-subtests = "*"
pytest-xdist = "*"
pytest-timeout = "*"
pytest-timeouts = "*"
ruff = "*"
scipy = "*"
sphinx = "*"
sphinx-rtd-theme = "*"
@ -110,7 +173,7 @@ build-backend = "poetry.core.masonry.api"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff]
select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A", "B"]
select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"]
ignore = ["W292", "E741", "E402", "C408", "ISC003", "B027", "B024"]
line-length = 160
target-version="py311"
@ -123,3 +186,9 @@ exclude = [
"third_party",
]
flake8-implicit-str-concat.allow-multiline=false
[tool.ruff.flake8-tidy-imports.banned-api]
"selfdrive".msg = "Use openpilot.selfdrive"
"common".msg = "Use openpilot.common"
"system".msg = "Use openpilot.system"
"third_party".msg = "Use openpilot.third_party"
"tools".msg = "Use openpilot.tools"

@ -1 +1 @@
Subproject commit 22f02dd650361e091f09f1ae94c5c96a9419c2d4
Subproject commit 8658bed29686b2ddae191fd18302986c85542431

@ -15,12 +15,14 @@ docs/INTEGRATION.md
docs/LIMITATIONS.md
site_scons/site_tools/cython.py
openpilot/__init__.py
openpilot/**
common/.gitignore
common/__init__.py
common/conversions.py
common/gpio.py
common/realtime.py
common/clock.pyx
common/timeout.py
common/ffi_wrapper.py
common/file_helpers.py
@ -160,6 +162,8 @@ common/clutil.cc
common/clutil.h
common/params.h
common/params.cc
common/ratekeeper.cc
common/ratekeeper.h
common/watchdog.cc
common/watchdog.h
@ -205,6 +209,7 @@ system/hardware/__init__.py
system/hardware/base.h
system/hardware/base.py
system/hardware/hw.h
system/hardware/hw.py
system/hardware/tici/__init__.py
system/hardware/tici/hardware.h
system/hardware/tici/hardware.py
@ -314,6 +319,8 @@ selfdrive/ui/tests/test_translations.py
selfdrive/ui/qt/*.cc
selfdrive/ui/qt/*.h
selfdrive/ui/qt/network/*.cc
selfdrive/ui/qt/network/*.h
selfdrive/ui/qt/offroad/*.cc
selfdrive/ui/qt/offroad/*.h
selfdrive/ui/qt/offroad/*.qml
@ -350,15 +357,18 @@ selfdrive/manager/process.py
selfdrive/manager/test/__init__.py
selfdrive/manager/test/test_manager.py
selfdrive/modeld/.gitignore
selfdrive/modeld/__init__.py
selfdrive/modeld/SConscript
selfdrive/modeld/modeld.cc
selfdrive/modeld/navmodeld.cc
selfdrive/modeld/dmonitoringmodeld.cc
selfdrive/modeld/modeld.py
selfdrive/modeld/navmodeld.py
selfdrive/modeld/dmonitoringmodeld.py
selfdrive/modeld/constants.py
selfdrive/modeld/modeld
selfdrive/modeld/navmodeld
selfdrive/modeld/dmonitoringmodeld
selfdrive/modeld/models/__init__.py
selfdrive/modeld/models/*.pxd
selfdrive/modeld/models/*.pyx
selfdrive/modeld/models/commonmodel.cc
selfdrive/modeld/models/commonmodel.h
@ -367,12 +377,8 @@ selfdrive/modeld/models/driving.cc
selfdrive/modeld/models/driving.h
selfdrive/modeld/models/supercombo.onnx
selfdrive/modeld/models/dmonitoring.cc
selfdrive/modeld/models/dmonitoring.h
selfdrive/modeld/models/dmonitoring_model_q.dlc
selfdrive/modeld/models/nav.cc
selfdrive/modeld/models/nav.h
selfdrive/modeld/models/navmodel_q.dlc
selfdrive/modeld/transforms/loadyuv.cc
@ -388,12 +394,12 @@ selfdrive/modeld/thneed/thneed_common.cc
selfdrive/modeld/thneed/thneed_qcom2.cc
selfdrive/modeld/thneed/serialize.cc
selfdrive/modeld/runners/snpemodel.cc
selfdrive/modeld/runners/snpemodel.h
selfdrive/modeld/runners/thneedmodel.cc
selfdrive/modeld/runners/thneedmodel.h
selfdrive/modeld/runners/runmodel.h
selfdrive/modeld/runners/run.h
selfdrive/modeld/runners/__init__.py
selfdrive/modeld/runners/*.pxd
selfdrive/modeld/runners/*.pyx
selfdrive/modeld/runners/*.cc
selfdrive/modeld/runners/*.h
selfdrive/modeld/runners/*.py
selfdrive/monitoring/dmonitoringd.py
selfdrive/monitoring/driver_monitor.py
@ -565,6 +571,7 @@ opendbc/nissan_x_trail_2017_generated.dbc
opendbc/nissan_leaf_2018_generated.dbc
opendbc/subaru_global_2017_generated.dbc
opendbc/subaru_global_2020_hybrid_generated.dbc
opendbc/subaru_outback_2015_generated.dbc
opendbc/subaru_outback_2019_generated.dbc
opendbc/subaru_forester_2017_generated.dbc

@ -1,5 +1,3 @@
selfdrive/modeld/runners/onnx*
third_party/mapbox-gl-native-qt/x86_64/*.so
third_party/libyuv/x86_64/**

@ -2,7 +2,7 @@
from collections import Counter
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__":
cars = get_all_car_info()

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

@ -39,9 +39,9 @@ void hexdump(uint32_t *d, int l) {
int main() {
int fd = open("/dev/mem", O_RDWR);
volatile uint32_t *mb = (uint32_t*)mmap(0,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0x06400000);
volatile uint32_t *mc = (uint32_t*)mmap(0,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0x06480000);
volatile uint32_t *md = (uint32_t*)mmap(0,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0x09A20000);
volatile uint32_t *mb = (uint32_t *)mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x06400000);
volatile uint32_t *mc = (uint32_t *)mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x06480000);
volatile uint32_t *md = (uint32_t *)mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x09A20000);
while (1) {
printf("PLL MODE:%x L_VAL:%x ALPHA:%x USER_CTL:%x CONFIG_CTL:%x CONFIG_CTL_HI:%x STATUS:%x TEST_CTL_LO:%x TEST_CTL_HI:%x\n",
mb[C0_PLL_MODE/4], mb[C0_PLL_L_VAL/4], mb[C0_PLL_ALPHA/4],

@ -1,7 +1,7 @@
#!/usr/bin/env python3
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 PyQt5.QtWidgets import QApplication, QLabel
from openpilot.selfdrive.ui.qt.python_helpers import set_main_window
if __name__ == "__main__":

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

@ -1 +1,2 @@
*.cc
translations_assets.qrc

@ -14,5 +14,7 @@
<file>offroad/icon_wifi_strength_medium.svg</file>
<file>offroad/icon_wifi_strength_high.svg</file>
<file>offroad/icon_wifi_strength_full.svg</file>
<file alias="languages.json">../ui/translations/languages.json</file>
</qresource>
</RCC>

@ -30,17 +30,17 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
import cereal.messaging as messaging
from cereal import log
from cereal.services import service_list
from common.api import Api
from common.basedir import PERSIST
from common.file_helpers import CallbackReader
from common.params import Params
from common.realtime import sec_since_boot, set_core_affinity
from system.hardware import HARDWARE, PC, AGNOS
from system.loggerd.config import ROOT
from system.loggerd.xattr_cache import getxattr, setxattr
from selfdrive.statsd import STATS_DIR
from system.swaglog import SWAGLOG_DIR, cloudlog
from system.version import get_commit, get_origin, get_short_branch, get_version
from openpilot.common.api import Api
from openpilot.common.basedir import PERSIST
from openpilot.common.file_helpers import CallbackReader
from openpilot.common.params import Params
from openpilot.common.realtime import set_core_affinity
from openpilot.system.hardware import HARDWARE, PC, AGNOS
from openpilot.system.loggerd.xattr_cache import getxattr, setxattr
from openpilot.selfdrive.statsd import STATS_DIR
from openpilot.system.swaglog import cloudlog
from openpilot.system.version import get_commit, get_origin, get_short_branch, get_version
from openpilot.selfdrive.hardware.hw import Paths
# TODO: use socket constant when mypy recognizes this as a valid attribute
@ -75,7 +75,7 @@ class UploadFile:
allow_cellular: bool
@classmethod
def from_dict(cls, d: Dict) -> UploadFile:
def from_dict(cls, d: dict) -> UploadFile:
return cls(d.get("fn", ""), d.get("url", ""), d.get("headers", {}), d.get("allow_cellular", False))
@ -92,7 +92,7 @@ class UploadItem:
allow_cellular: bool = False
@classmethod
def from_dict(cls, d: Dict) -> UploadItem:
def from_dict(cls, d: dict) -> UploadItem:
return cls(d["path"], d["url"], d["headers"], d["created_at"], d["id"], d["retry_count"], d["current"],
d["progress"], d["allow_cellular"])
@ -119,12 +119,11 @@ class AbortTransferException(Exception):
class UploadQueueCache:
params = Params()
@staticmethod
def initialize(upload_queue: Queue[UploadItem]) -> None:
try:
upload_queue_json = UploadQueueCache.params.get("AthenadUploadQueue")
upload_queue_json = Params().get("AthenadUploadQueue")
if upload_queue_json is not None:
for item in json.loads(upload_queue_json):
upload_queue.put(UploadItem.from_dict(item))
@ -136,7 +135,7 @@ class UploadQueueCache:
try:
queue: List[Optional[UploadItem]] = list(upload_queue.queue)
items = [asdict(i) for i in queue if i is not None and (i.id not in cancelled_uploads)]
UploadQueueCache.params.put("AthenadUploadQueue", json.dumps(items))
Params().put("AthenadUploadQueue", json.dumps(items))
except Exception:
cloudlog.exception("athena.UploadQueueCache.cache.exception")
@ -309,7 +308,7 @@ def _do_upload(upload_item: UploadItem, callback: Optional[Callable] = None) ->
# security: user should be able to request any message from their car
@dispatcher.add_method
def getMessage(service: str, timeout: int = 1000) -> Dict:
def getMessage(service: str, timeout: int = 1000) -> dict:
if service is None or service not in service_list:
raise Exception("invalid service")
@ -320,7 +319,7 @@ def getMessage(service: str, timeout: int = 1000) -> Dict:
raise TimeoutError
# this is because capnp._DynamicStructReader doesn't have typing information
return cast(Dict, ret.to_dict())
return cast(dict, ret.to_dict())
@dispatcher.add_method
@ -352,7 +351,7 @@ def scan_dir(path: str, prefix: str) -> List[str]:
# (glob and friends traverse entire dir tree)
with os.scandir(path) as i:
for e in i:
rel_path = os.path.relpath(e.path, ROOT)
rel_path = os.path.relpath(e.path, Paths.log_root())
if e.is_dir(follow_symlinks=False):
# add trailing slash
rel_path = os.path.join(rel_path, '')
@ -367,7 +366,7 @@ def scan_dir(path: str, prefix: str) -> List[str]:
@dispatcher.add_method
def listDataDirectory(prefix='') -> List[str]:
return scan_dir(ROOT, prefix)
return scan_dir(Paths.log_root(), prefix)
@dispatcher.add_method
@ -408,7 +407,7 @@ def uploadFilesToUrls(files_data: List[UploadFileDict]) -> UploadFilesToUrlRespo
failed.append(file.fn)
continue
path = os.path.join(ROOT, file.fn)
path = os.path.join(Paths.log_root(), file.fn)
if not os.path.exists(path) and not os.path.exists(strip_bz2_extension(path)):
failed.append(file.fn)
continue
@ -552,7 +551,7 @@ def getNetworks():
@dispatcher.add_method
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()
if ret is not None:
def b64jpeg(x):
@ -572,8 +571,8 @@ def get_logs_to_send_sorted() -> List[str]:
# TODO: scan once then use inotify to detect file creation/deletion
curr_time = int(time.time())
logs = []
for log_entry in os.listdir(SWAGLOG_DIR):
log_path = os.path.join(SWAGLOG_DIR, log_entry)
for log_entry in os.listdir(Paths.swaglog_root()):
log_path = os.path.join(Paths.swaglog_root(), log_entry)
time_sent = 0
try:
value = getxattr(log_path, LOG_ATTR_NAME)
@ -593,10 +592,10 @@ def log_handler(end_event: threading.Event) -> None:
return
log_files = []
last_scan = 0
last_scan = 0.
while not end_event.is_set():
try:
curr_scan = sec_since_boot()
curr_scan = time.monotonic()
if curr_scan - last_scan > 10:
log_files = get_logs_to_send_sorted()
last_scan = curr_scan
@ -608,7 +607,7 @@ def log_handler(end_event: threading.Event) -> None:
cloudlog.debug(f"athena.log_handler.forward_request {log_entry}")
try:
curr_time = int(time.time())
log_path = os.path.join(SWAGLOG_DIR, log_entry)
log_path = os.path.join(Paths.swaglog_root(), log_entry)
setxattr(log_path, LOG_ATTR_NAME, int.to_bytes(curr_time, 4, sys.byteorder))
with open(log_path) as f:
jsonrpc = {
@ -635,7 +634,7 @@ def log_handler(end_event: threading.Event) -> None:
log_success = "result" in log_resp and log_resp["result"].get("success")
cloudlog.debug(f"athena.log_handler.forward_response {log_entry} {log_success}")
if log_entry and log_success:
log_path = os.path.join(SWAGLOG_DIR, log_entry)
log_path = os.path.join(Paths.swaglog_root(), log_entry)
try:
setxattr(log_path, LOG_ATTR_NAME, LOG_ATTR_VALUE_MAX_UNIX_TIME)
except OSError:
@ -652,8 +651,8 @@ def log_handler(end_event: threading.Event) -> None:
def stat_handler(end_event: threading.Event) -> None:
while not end_event.is_set():
last_scan = 0
curr_scan = sec_since_boot()
last_scan = 0.
curr_scan = time.monotonic()
try:
if curr_scan - last_scan > 10:
stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(STATS_DIR)))
@ -721,7 +720,7 @@ def ws_proxy_send(ws: WebSocket, local_sock: socket.socket, signal_sock: socket.
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():
try:
opcode, data = ws.recv_data(control_frame=True)
@ -730,10 +729,10 @@ def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
data = data.decode("utf-8")
recv_queue.put_nowait(data)
elif opcode == ABNF.OPCODE_PING:
last_ping = int(sec_since_boot() * 1e9)
last_ping = int(time.monotonic() * 1e9)
Params().put("LastAthenaPingTime", str(last_ping))
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:
cloudlog.exception("athenad.ws_recv.timeout")
end_event.set()

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

Loading…
Cancel
Save