diff --git a/.github/workflows/badges.yaml b/.github/workflows/badges.yaml
index 223c7348631f8f..16edb45c21cf6b 100644
--- a/.github/workflows/badges.yaml
+++ b/.github/workflows/badges.yaml
@@ -7,12 +7,7 @@ on:
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai
-
- BUILD: |
- docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true
- docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
- docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
- 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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/sh -c
jobs:
badges:
@@ -23,23 +18,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
-
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
-
- - name: Build Docker image
- run: eval "$BUILD"
-
+ - uses: ./.github/workflows/setup
- name: Push badges
run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/ui/translations/create_badges.py"
@@ -49,6 +28,6 @@ jobs:
git config user.email "badge-researcher@comma.ai"
git config user.name "Badge Researcher"
- git add translation_badge_*.svg
+ git add translation_badge.svg
git commit -m "Add/Update badges"
git push -f origin HEAD
diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml
index 7397e30f832971..8529da8e89fb1f 100644
--- a/.github/workflows/selfdrive_tests.yaml
+++ b/.github/workflows/selfdrive_tests.yaml
@@ -17,7 +17,7 @@ env:
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
- 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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
+ RUN: docker run --shm-size 1G -v $PWD:/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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: |
docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true
@@ -27,11 +27,10 @@ env:
UNIT_TEST: coverage run --append -m unittest discover
jobs:
- # TODO: once actions/cache supports read only mode, use the cache for all jobs
build_release:
name: build release
runs-on: ubuntu-20.04
- timeout-minutes: 50
+ timeout-minutes: 30
env:
STRIPPED_DIR: /tmp/releasepilot
steps:
@@ -39,44 +38,29 @@ jobs:
with:
fetch-depth: 0
submodules: true
- - name: Pull LFS
- run: git lfs pull
+ - name: Build devel
+ run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
+ - uses: ./.github/workflows/setup
- name: Check submodules
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: release/check-submodules.sh
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build devel
+ - name: Build openpilot and run checks
+ run: |
+ cd $STRIPPED_DIR
+ ${{ env.RUN }} "CI=1 python selfdrive/manager/build.py"
+ - name: Run tests
run: |
- TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
- cp Dockerfile.openpilot_base $STRIPPED_DIR
+ cd $STRIPPED_DIR
+ ${{ env.RUN }} "release/check-dirty.sh && \
+ python -m unittest discover selfdrive/car"
+ - name: pre-commit
+ run: |
+ cd $GITHUB_WORKSPACE
cp .pre-commit-config.yaml $STRIPPED_DIR
cp .pylintrc $STRIPPED_DIR
cp mypy.ini $STRIPPED_DIR
- - name: Build Docker image
- run: |
- eval "$BUILD"
- rm $STRIPPED_DIR/Dockerfile.openpilot_base
- - name: Build openpilot and run checks
- run: |
- cd $STRIPPED_DIR
- ${{ env.RUN }} "CI=1 python selfdrive/manager/build.py && \
- pre-commit run --all && \
- rm .pre-commit-config.yaml && \
- rm .pylintrc && \
- rm mypy.ini && \
- release/check-dirty.sh && \
- python -m unittest discover selfdrive/car"
+ cd $STRIPPED_DIR
+ ${{ env.RUN }} "pre-commit run --all"
build_all:
name: build all
@@ -86,26 +70,14 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }}
+ - uses: ./.github/workflows/setup
with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }}
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
+ save-cache: true
- name: Build openpilot with all flags
run: ${{ env.RUN }} "scons -j$(nproc) --extras --test && release/check-dirty.sh"
- name: Cleanup scons cache
run: |
- ${{ env.RUN }} "scons -j$(nproc) --extras --test && \
- rm -rf /tmp/scons_cache/* && \
+ ${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --extras --test --cache-populate"
#build_mac:
@@ -175,21 +147,9 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
+ - uses: ./.github/workflows/setup
- name: Build Docker image
run: |
- eval "$BUILD"
docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true
docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/webcam/Dockerfile .
- name: Build openpilot
@@ -244,30 +204,11 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache dependencies
- id: dependency-cache
- uses: actions/cache@v2
- with:
- path: /tmp/comma_download_cache
- key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/test_valgrind_replay.py') }}
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
+ - uses: ./.github/workflows/setup
- name: Run valgrind
run: |
${{ env.RUN }} "scons -j$(nproc) && \
- FILEREADER_CACHE=1 python selfdrive/test/test_valgrind_replay.py"
+ python selfdrive/test/test_valgrind_replay.py"
- name: Print logs
if: always()
run: cat selfdrive/test/valgrind_logs.txt
@@ -277,28 +218,10 @@ jobs:
runs-on: ubuntu-20.04
timeout-minutes: 50
steps:
- - name: Get current date
- id: date
- run: echo "::set-output name=time::$(date +'%s')"
- - name: Output timestamp
- run: echo $TIMESTAMP
- env:
- TIMESTAMP: ${{ steps.date.outputs.time }}
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }}
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }}
- restore-keys: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- - name: Build Docker image
- run: eval "$BUILD"
+ - uses: ./.github/workflows/setup
- name: Run unit tests
run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
@@ -339,30 +262,17 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache dependencies
+ - uses: ./.github/workflows/setup
+ - name: Cache test routes
id: dependency-cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: /tmp/comma_download_cache
- key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/test_processes.py') }}
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
+ key: proc-replay-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/ref_commit') }}
- name: Run replay
run: |
${{ env.RUN }} "scons -j$(nproc) && \
- FILEREADER_CACHE=1 CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
+ CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
coverage xml"
- name: Print diff
if: always()
@@ -389,35 +299,16 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Pull LFS
- run: git lfs pull
- - name: Cache dependencies
- id: dependency-cache
- uses: actions/cache@v2
- with:
- path: /tmp/comma_download_cache
- key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/model_replay.py') }}
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
+ - uses: ./.github/workflows/setup
- name: Build Docker image
- # Sim docker is needed to get the intel OPENCL drivers
+ # Sim docker is needed to get the OpenCL drivers
run: eval "$BUILD_CL"
- name: Run replay
run: |
${{ env.RUN_CL }} "scons -j$(nproc) && \
- ONNXCPU=1 FILEREADER_CACHE=1 CI=1 coverage run \
- selfdrive/test/process_replay/model_replay.py -j$(nproc) && \
- coverage xml"
+ ONNXCPU=1 CI=1 coverage run \
+ selfdrive/test/process_replay/model_replay.py -j$(nproc) && \
+ coverage xml"
test_longitudinal:
name: longitudinal
@@ -427,20 +318,7 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
+ - uses: ./.github/workflows/setup
- name: Test longitudinal
run: |
${{ env.RUN }} "mkdir -p selfdrive/test/out && \
@@ -469,30 +347,17 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- - name: Cache dependencies
+ - uses: ./.github/workflows/setup
+ - name: Cache test routes
id: dependency-cache
- uses: actions/cache@v2
+ uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
with:
path: /tmp/comma_download_cache
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }}
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
- name: Test car models
run: |
${{ env.RUN }} "scons -j$(nproc) --test && \
- FILEREADER_CACHE=1 coverage run -m pytest selfdrive/car/tests/test_models.py && \
+ coverage run -m pytest selfdrive/car/tests/test_models.py && \
coverage xml && \
chmod -R 777 /tmp/comma_download_cache"
env:
@@ -530,20 +395,7 @@ jobs:
with:
submodules: true
ref: ${{ github.event.pull_request.base.ref }}
- - name: Cache scons
- id: scons-cache
- # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
- uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
- env:
- CACHE_SKIP_SAVE: true
- with:
- path: /tmp/scons_cache
- key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- restore-keys: |
- scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- scons-
- - name: Build Docker image
- run: eval "$BUILD"
+ - uses: ./.github/workflows/setup
- 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"
diff --git a/.github/workflows/setup/action.yaml b/.github/workflows/setup/action.yaml
new file mode 100644
index 00000000000000..79ec9212354c26
--- /dev/null
+++ b/.github/workflows/setup/action.yaml
@@ -0,0 +1,45 @@
+name: 'openpilot env setup'
+
+env:
+ BASE_IMAGE: openpilot-base
+ DOCKER_REGISTRY: ghcr.io/commaai
+ BUILD: |
+ docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true
+ docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
+ docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
+
+inputs:
+ save-cache:
+ default: false
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ # do this after checkout to ensure our custom LFS config is used to pull from GitLab
+ - shell: bash
+ run: git lfs pull
+
+ # build cache
+ - id: date
+ shell: bash
+ run: echo "::set-output name=date::$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d')"
+ - shell: bash
+ run: echo "${{ steps.date.outputs.date }}"
+ - shell: bash
+ run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV
+ if: github.ref != 'refs/heads/master' || inputs.save-cache == 'false'
+ - id: scons-cache
+ # TODO: change the version to the released version
+ # when https://github.com/actions/cache/pull/489 (or 571) is merged.
+ uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
+ with:
+ path: /tmp/scons_cache
+ key: scons-${{ steps.date.outputs.date }}-${{ github.sha }}
+ restore-keys: |
+ scons-${{ steps.date.outputs.date }}-
+ scons-
+
+ # build our docker image
+ - shell: bash
+ run: eval "$BUILD"
diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml
index e93ce2bb39a146..173e2083847264 100644
--- a/.github/workflows/tools_tests.yaml
+++ b/.github/workflows/tools_tests.yaml
@@ -21,46 +21,6 @@ env:
GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c
jobs:
- build_latest_ubuntu:
- name: build latest ubuntu
- runs-on: ubuntu-20.04
- timeout-minutes: 60
- steps:
- - uses: actions/checkout@v3
- with:
- submodules: true
- - name: Cache pyenv
- id: ubuntu-latest-pyenv
- uses: actions/cache@v3
- with:
- path: |
- ~/.pyenv
- ~/.local/share/virtualenvs/
- key: ubuntu-latest-python-${{ hashFiles('tools/ubuntu_setup.sh') }}-
- - name: Cache scons
- id: ubuntu-latest-scons
- uses: actions/cache@v3
- with:
- path: /tmp/scons_cache
- key: ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}-
- restore-keys: |
- ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}-
- ubuntu-latest-scons-
-
- - name: tools/ubuntu_setup.sh
- run: |
- source tools/openpilot_env.sh
- tools/ubuntu_setup.sh
- - name: Build openpilot
- run: |
- source tools/openpilot_env.sh
- pipenv run scons -j$(nproc) --extras --test
- - name: Cleanup scons cache
- run: |
- source tools/openpilot_env.sh
- rm -rf /tmp/scons_cache/*
- pipenv run scons -j$(nproc) --extras --test --cache-populate
-
plotjuggler:
name: plotjuggler
runs-on: ubuntu-20.04
diff --git a/.gitignore b/.gitignore
index 07626f4e285ab2..6aee0ed8e0b077 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,7 +55,6 @@ selfdrive/modeld/_dmonitoringmodeld
/src/
one
-/body/
openpilot
notebooks
xx
diff --git a/.gitmodules b/.gitmodules
index 1e6110b7a6941e..bc439b451c4583 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,15 +1,18 @@
[submodule "panda"]
- path = panda
- url = ../../commaai/panda.git
+ path = panda
+ url = ../../commaai/panda.git
[submodule "opendbc"]
- path = opendbc
- url = ../../commaai/opendbc.git
+ path = opendbc
+ url = ../../commaai/opendbc.git
[submodule "laika_repo"]
- path = laika_repo
- url = ../../commaai/laika.git
+ path = laika_repo
+ url = ../../commaai/laika.git
[submodule "cereal"]
- path = cereal
- url = ../../commaai/cereal.git
+ path = cereal
+ url = ../../commaai/cereal.git
[submodule "rednose_repo"]
- path = rednose_repo
- url = ../../commaai/rednose.git
+ path = rednose_repo
+ url = ../../commaai/rednose.git
+[submodule "body"]
+ path = body
+ url = ../../commaai/body.git
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8b8bc1f1b9ce67..e273cd9aeeab91 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -54,7 +54,7 @@ repos:
entry: cppcheck
language: system
types: [c++]
- exclude: '^(third_party/)|(pyextra/)|(cereal/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)/|(installer/)'
+ exclude: '^(third_party/)|(pyextra/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)|(installer/)'
args:
- --error-exitcode=1
- --language=c++
diff --git a/README.md b/README.md
index 94837575b456e9..0b6fc20105ba26 100755
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Table of Contents
What is openpilot?
------
-[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW) and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md).
+[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW), and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models, and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera-based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md).
@@ -40,9 +40,9 @@ 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 three](https://comma.ai/shop/products/three).
-* This software. The setup procedure of the comma three 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 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot.
+* This software. The setup procedure of the comma three 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 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, 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).
@@ -52,11 +52,11 @@ Running on PC
All of openpilot's services can run as normal on a PC, even without special hardware or a car. To develop or experiment with openpilot you can run openpilot on recorded or simulated data.
-With openpilot's tools you can plot logs, replay drives and watch the full-res camera streams. See [the tools README](tools/README.md) for more information.
+With openpilot's tools, you can plot logs, replay drives, and watch the full-res camera streams. See [the tools README](tools/README.md) for more information.
-You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes, but does require a decent GPU.
+You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes but does require a decent GPU.
-A PC running openpilot can also control your vehicle if it is connected to a [a webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and [a harness](https://comma.ai/shop/products/car-harness).
+A PC running openpilot can also control your vehicle if it is connected to a [webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and a [harness](https://comma.ai/shop/products/car-harness).
Community and Contributing
------
@@ -78,8 +78,8 @@ By default, openpilot uploads the driving data to our servers. You can also acce
openpilot is open source software: the user is free to disable data collection if they wish to do so.
-openpilot logs the road facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
-The driver facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
+openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
+The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
@@ -87,11 +87,11 @@ Safety and Testing
----
* openpilot observes ISO26262 guidelines, see [SAFETY.md](docs/SAFETY.md) for more details.
-* openpilot has software in the loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit.
+* openpilot has software-in-the-loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit.
* The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details.
-* panda has software in the loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
-* Internally, we have a hardware in the loop Jenkins test suite that builds and unit tests the various processes.
-* panda has additional hardware in the loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
+* panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
+* Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes.
+* panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
* We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes.
Directory Structure
diff --git a/RELEASES.md b/RELEASES.md
index e69d7461b7bb14..3ac6c8e41dfe69 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,6 +1,22 @@
-Version 0.8.16 (2022-XX-XX)
+Version 0.8.17 (2022-XX-XX)
========================
+
+Version 0.8.16 (2022-08-26)
+========================
+* New driving model
+ * Reduced turn cutting
+* Auto-detect right hand drive setting with driver monitoring model
+* Improved fan controller for comma three
+* New translations
+ * Japanese thanks to cydia2020!
+ * Brazilian Portuguese thanks to AlexandreSato!
+* Chevrolet Bolt EUV 2022-23 support thanks to JasonJShuler!
+* Chevrolet Silverado 1500 2020-21 support thanks to JasonJShuler!
+* GMC Sierra 1500 2020-21 support thanks to JasonJShuler!
+* Hyundai Ioniq 5 2022 support thanks to sunnyhaibin!
* Hyundai Kona Electric 2022 support thanks to sunnyhaibin!
+* Hyundai Tucson Hybrid 2022 support thanks to sunnyhaibin!
+* Subaru Legacy 2020-22 support thanks to martinl!
* Subaru Outback 2020-22 support
Version 0.8.15 (2022-07-20)
diff --git a/SConstruct b/SConstruct
index 940b2367fa4b35..49685b19b20e29 100644
--- a/SConstruct
+++ b/SConstruct
@@ -393,9 +393,13 @@ if arch != "Darwin":
# Build openpilot
-SConscript(['cereal/SConscript'])
-SConscript(['panda/board/SConscript'])
-SConscript(['opendbc/can/SConscript'])
+# build submodules
+SConscript([
+ 'cereal/SConscript',
+ 'body/board/SConscript',
+ 'panda/board/SConscript',
+ 'opendbc/can/SConscript',
+])
SConscript(['third_party/SConscript'])
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000000000..8b66082bf241e6
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+Suspected vulnerabilities can be reported to both `adeeb@comma.ai` and `security@comma.ai`.
diff --git a/body b/body
new file mode 160000
index 00000000000000..04aeb30ce0bb14
--- /dev/null
+++ b/body
@@ -0,0 +1 @@
+Subproject commit 04aeb30ce0bb14759989cd374158233877e1e151
diff --git a/cereal b/cereal
index fbd45de6e6bc71..589ef049a7b0ba 160000
--- a/cereal
+++ b/cereal
@@ -1 +1 @@
-Subproject commit fbd45de6e6bc71b4561eaef65dd86fce952c5d55
+Subproject commit 589ef049a7b0bac31f4c8987c0fc539839fae489
diff --git a/common/params.cc b/common/params.cc
index 11b24fe9c120df..f89e7ff002b572 100644
--- a/common/params.cc
+++ b/common/params.cc
@@ -151,6 +151,7 @@ std::unordered_map keys = {
{"RecordFront", PERSISTENT},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"ReleaseNotes", PERSISTENT},
+ {"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"ShouldDoUpdate", CLEAR_ON_MANAGER_START},
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"SshEnabled", PERSISTENT},
diff --git a/common/version.h b/common/version.h
index bf1c58df1e2920..0a109c1faac060 100644
--- a/common/version.h
+++ b/common/version.h
@@ -1 +1 @@
-#define COMMA_VERSION "0.8.16"
+#define COMMA_VERSION "0.8.17"
diff --git a/docs/CARS.md b/docs/CARS.md
index 7fe9441a822ac5..78b4d3b168305e 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -2,226 +2,217 @@
# Supported Cars
-A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system.
+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.
-## How We Rate The Cars
+# 205 Supported Cars
-### Stop and Go
-- [![star](assets/icon-star-full.svg)](##) - openpilot operates down to 0 mph.
-- [![star](assets/icon-star-empty.svg)](##) - openpilot operates only above a minimum speed. See your car's manual for the minimum speed.
-
-### Steer to 0
-- [![star](assets/icon-star-full.svg)](##) - openpilot can control the steering wheel down to 0 mph.
-- [![star](assets/icon-star-empty.svg)](##) - No steering control below certain speeds. See your car's manual for the minimum speed.
-
-### Steering Torque
-- [![star](assets/icon-star-full.svg)](##) - Car has enough steering torque to comfortably take most highway turns.
-- [![star](assets/icon-star-empty.svg)](##) - Limited ability to make tighter turns.
-
-
-# 199 Supported Cars
-
-|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|
-|---|---|---|:---:|:---:|:---:|:---:|
-|Acura|ILX 2016-19|AcuraWatch Plus|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Acura|RDX 2016-18|AcuraWatch Plus|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Acura|RDX 2019-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Audi|A3 2014-19|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Audi|Q2 2018|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Audi|Q3 2020-21|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Audi|RS3 2018|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Audi|S3 2015-17|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Cadillac|Escalade ESV 2016[1](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise Control|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chrysler|Pacifica 2021|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|comma|body|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Genesis|G70 2018-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Genesis|G70 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Genesis|G80 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Genesis|G90 2017-18|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise Control|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Honda|Accord 2018-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Accord Hybrid 2018-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Civic 2016-18|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Civic 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)[2](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Civic 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Honda|Civic Hatchback 2017-21|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Civic Hatchback 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Honda|CR-V 2015-16|Touring Trim|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|CR-V 2017-22|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|CR-V Hybrid 2017-19|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|e 2020|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Fit 2018-20|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Freed 2020|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|HR-V 2019-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Insight 2019-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Inspire 2018|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Odyssey 2018-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Passport 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Pilot 2016-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Honda|Ridgeline 2017-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Elantra Hybrid 2021-22|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Electric 2020|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC) & LFA|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Ioniq Plug-in Hybrid 2020-21|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Kona 2020|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Palisade 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Santa Fe 2019-20|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Santa Fe 2021-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Santa Fe Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Santa Fe Plug-in Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Sonata 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Sonata Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|EV6 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Forte 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|K5 2021-22|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Electric 2019|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Electric 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Electric 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Electric 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Niro Plug-in Hybrid 2018-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Optima 2017|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Optima 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Seltos 2021|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Sorento 2018|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Sorento 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Stinger 2018-20|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Kia|Telluride 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|ES 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|ES Hybrid 2017-18|Lexus Safety System+|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|ES Hybrid 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|IS 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|NX 2018-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|NX 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|NX Hybrid 2018-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|NX Hybrid 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|RC 2017-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|RX 2016-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|RX 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|RX Hybrid 2016-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|RX Hybrid 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Lexus|UX Hybrid 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Mazda|CX-5 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Mazda|CX-9 2021-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Nissan|Altima 2019-20|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Nissan|Leaf 2018-22|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Nissan|Rogue 2018-20|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Nissan|X-Trail 2017|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Ram|1500 2019-22|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|SEAT|Ateca 2018|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|SEAT|Leon 2014-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|Ascent 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|Forester 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|Impreza 2017-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Subaru|Impreza 2020-22|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|Outback 2020-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Subaru|XV 2018-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
-|Subaru|XV 2020-21|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Kamiq 2021[5](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Karoq 2019-21|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Kodiaq 2018-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Octavia 2015, 2018-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Octavia RS 2016|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Scala 2020|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Škoda|Superb 2015-18|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Alphard 2019-20|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Alphard Hybrid 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon 2019-21|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon Hybrid 2019-21|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Avalon Hybrid 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|C-HR 2017-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|C-HR Hybrid 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Camry 2018-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Camry 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Camry Hybrid 2018-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Camry Hybrid 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla Cross (Non-US only) 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla Hatchback 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Corolla Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Highlander 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Highlander 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Highlander Hybrid 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Highlander Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Mirai 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius 2017-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius Prime 2017-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius Prime 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Prius v 2017|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 Hybrid 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 Hybrid 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|RAV4 Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Toyota|Sienna 2018-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Arteon 2018-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Arteon eHybrid 2020-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Arteon R 2020-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Atlas 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Atlas Cross Sport 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|California 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Caravelle 2020[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|CC 2018-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|e-Golf 2014-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf 2015-20[8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf Alltrack 2015-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf GTD 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf GTE 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf GTI 2015-21|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf R 2015-19[8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Golf SportsVan 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Jetta 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Jetta GLI 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Passat 2015-22[6,7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Passat Alltrack 2015-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Passat GTE 2015-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Polo 2020-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Polo GTI 2020-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|T-Cross 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|T-Roc 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Taos 2022[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Teramont 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Teramont Cross Sport 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Teramont X 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Tiguan 2019-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
-|Volkswagen|Touran 2017|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
+|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Harness|
+|---|---|---|:---:|:---:|:---:|:---:|:---:|
+|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Acura|RDX 2019-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Audi|A3 2014-19|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Audi|Q2 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Audi|Q3 2020-21|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Audi|RS3 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Audi|S3 2015-17|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Cadillac|Escalade ESV 2016[1](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
+|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
+|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
+|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
+|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|comma|body|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|None|
+|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
+|Genesis|G70 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
+|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Genesis|G90 2017-18|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
+|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
+|Honda|Accord 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Accord Hybrid 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Civic 2019-21|All|Stock|0 mph|2 mph[2](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|
+|Honda|Civic Hatchback 2017-21|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|
+|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|CR-V 2017-22|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|CR-V Hybrid 2017-19|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|e 2020|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Insight 2019-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Inspire 2018|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
+|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
+|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC) & LKAS|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
+|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
+|Hyundai|Elantra Hybrid 2021-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
+|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC) & LKAS|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai J|
+|Hyundai|Ioniq 5 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|
+|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Hyundai|Ioniq Electric 2020|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC) & LFA|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Hyundai|Ioniq Plug-in Hybrid 2020-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Hyundai|Kona 2020|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
+|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G|
+|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai O|
+|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai I|
+|Hyundai|Palisade 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Hyundai|Santa Fe 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai D|
+|Hyundai|Santa Fe 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
+|Hyundai|Santa Fe Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
+|Hyundai|Santa Fe Plug-in Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
+|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
+|Hyundai|Sonata 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
+|Hyundai|Sonata Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
+|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
+|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
+|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
+|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
+|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
+|Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
+|Kia|EV6 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai P|
+|Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
+|Kia|Forte 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G|
+|Kia|K5 2021-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
+|Kia|Niro Electric 2019|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Kia|Niro Electric 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
+|Kia|Niro Electric 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Kia|Niro Electric 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
+|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Kia|Niro Plug-in Hybrid 2018-19|Smart Cruise Control (SCC) & LKAS|openpilot|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Kia|Optima 2017|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
+|Kia|Optima 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G|
+|Kia|Seltos 2021|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
+|Kia|Sorento 2018|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Kia|Sorento 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
+|Kia|Stinger 2018-20|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
+|Kia|Telluride 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
+|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|ES Hybrid 2017-18|Lexus Safety System+|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|NX 2018-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|NX Hybrid 2018-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|RX 2016-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|RX Hybrid 2016-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Mazda|CX-5 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Mazda|
+|Mazda|CX-9 2021-22|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|Mazda|
+|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan B|
+|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A|
+|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A|
+|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A|
+|Ram|1500 2019-22|Adaptive Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Ram|
+|SEAT|Ateca 2018|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|SEAT|Leon 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A|
+|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
+|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A|
+|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A|
+|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
+|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A|
+|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru B|
+|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru B|
+|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
+|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A|
+|Škoda|Kamiq 2021[5](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Karoq 2019-21[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Kodiaq 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Octavia 2015, 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Octavia RS 2016|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Scala 2020|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Škoda|Superb 2015-18|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon 2016|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon 2017-18|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon 2019-21|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon Hybrid 2019-21|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Camry 2018-20|All|Stock|0 mph[4](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Camry 2021-22|All|openpilot|0 mph[4](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Camry Hybrid 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla 2017-19|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla Cross (Non-US only) 2020-21|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Highlander 2017-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Highlander 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Highlander Hybrid 2017-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius 2016|Toyota Safety Sense P|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius 2017-20|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius Prime 2017-20|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Prius v 2017|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 2016|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 2017-18|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 Hybrid 2017-18|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Toyota|Sienna 2018-20|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota|
+|Volkswagen|Arteon 2018-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Arteon eHybrid 2020-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Arteon R 2020-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Atlas 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Atlas Cross Sport 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|California 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Caravelle 2020[7](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|CC 2018-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|e-Golf 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf 2015-20[8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf Alltrack 2015-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf GTD 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf GTE 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf GTI 2015-21|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf R 2015-19[8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Golf SportsVan 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
+|Volkswagen|Jetta 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Jetta GLI 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Passat 2015-22[6,7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Passat Alltrack 2015-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Passat GTE 2015-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Polo 2020-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Polo GTI 2020-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|T-Cross 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|T-Roc 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Taos 2022[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Teramont 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Teramont Cross Sport 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Teramont X 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Tiguan 2019-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533|
+|Volkswagen|Touran 2017|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
1Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
@@ -229,8 +220,8 @@ A supported vehicle is one that just works when you install a comma device. Ever
3When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
4openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
5Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-6Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.
-7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). For the newer design, in the interim, choose "VW J533 Development" from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.
+6Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
+7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). In the interim, if your car has a J533 connector CAN gateway inside the dashboard, choose "VW J533 Development" from the vehicle drop-down for a suitable harness. (Some newer models are also observed to not have a J533 connector.)
8Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.)
## Community Maintained Cars
@@ -239,13 +230,13 @@ Although they're not upstream, the community has openpilot running on other make
# Don't see your car here?
**openpilot can support many more cars than it currently does.** There are a few reasons your car may not be supported.
-If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. We don't have a roadmap for car support, and in fact, most car support comes from users like you!
+If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car support comes from users like you!
### Which cars are able to be supported?
-openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering) and [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control), then it almost certainly has these interfaces. These interfaces generally started shipping on cars around 2016.
+openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control.
-If your car has the following packages or features, then it's a good candidate for support. If it does not, then it's unlikely able to be supported.
+If your car has the following packages or features, then it's a good candidate for support.
| Make | Required Package/Features |
| ---- | ------------------------- |
@@ -263,8 +254,9 @@ All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wi
### Toyota Security
-Specific new Toyota models are shipping with a new message authentication method that openpilot does not yet support.
-So far, this list includes:
+openpilot does not yet support these Toyota models due to a new message authentication method.
+[Vote](https://comma.ai/shop/products/vote) if you'd like to see openpilot support on these models.
+
* Toyota RAV4 Prime 2021+
* Toyota Sienna 2021+
* Toyota Venza 2021+
@@ -273,4 +265,4 @@ So far, this list includes:
* Toyota Corolla Cross 2022+ (only US model)
* Lexus NX 2022+
* Toyota bZ4x 2023+
-* Subaru Solterra 2023+
\ No newline at end of file
+* Subaru Solterra 2023+
diff --git a/opendbc b/opendbc
index 48856851716219..b913296c912344 160000
--- a/opendbc
+++ b/opendbc
@@ -1 +1 @@
-Subproject commit 488568517162194fbb2aa45d3dba4c6af663a883
+Subproject commit b913296c9123441b2b271c00239929ed388169b5
diff --git a/panda b/panda
index 89989abca5def3..9d6496ece8465d 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit 89989abca5def32c73b8c9e66da804acb12be357
+Subproject commit 9d6496ece8465dfe30997b31dfb352e1e51dde6c
diff --git a/release/files_common b/release/files_common
index 0980f7929dfda9..663d6d35529551 100644
--- a/release/files_common
+++ b/release/files_common
@@ -434,6 +434,19 @@ rednose/.gitignore
rednose/**
laika/**
+body/.gitignore
+body/board/SConscript
+body/board/*.h
+body/board/*.c
+body/board/*.s
+body/board/*.ld
+body/board/inc/**
+body/board/obj/
+body/board/bldc/**
+body/board/drivers/**
+body/certs/**
+body/crypto/**
+
cereal/.gitignore
cereal/__init__.py
cereal/car.capnp
@@ -491,6 +504,7 @@ opendbc/can/parser_pyx.pyx
opendbc/comma_body.dbc
+opendbc/chrysler_ram_hd_generated.dbc
opendbc/chrysler_ram_dt_generated.dbc
opendbc/chrysler_pacifica_2017_hybrid_generated.dbc
opendbc/chrysler_pacifica_2017_hybrid_private_fusion.dbc
@@ -499,6 +513,8 @@ opendbc/gm_global_a_powertrain_generated.dbc
opendbc/gm_global_a_object.dbc
opendbc/gm_global_a_chassis.dbc
+opendbc/FORD_CADS.dbc
+opendbc/ford_fusion_2018_adas.dbc
opendbc/ford_lincoln_base_pt.dbc
opendbc/honda_accord_2018_can_generated.dbc
@@ -518,7 +534,7 @@ opendbc/honda_insight_ex_2019_can_generated.dbc
opendbc/acura_ilx_2016_nidec.dbc
opendbc/honda_civic_ex_2022_can_generated.dbc
-opendbc/kia_ev6.dbc
+opendbc/hyundai_canfd.dbc
opendbc/hyundai_kia_generic.dbc
opendbc/hyundai_kia_mando_front_radar.dbc
@@ -538,6 +554,7 @@ opendbc/toyota_nodsu_pt_generated.dbc
opendbc/toyota_adas.dbc
opendbc/toyota_tss2_adas.dbc
+opendbc/vw_golf_mk4.dbc
opendbc/vw_mqb_2010.dbc
opendbc/tesla_can.dbc
diff --git a/release/files_tici b/release/files_tici
index f8c0a279593806..8e3249cdfd55fb 100644
--- a/release/files_tici
+++ b/release/files_tici
@@ -11,5 +11,7 @@ system/camerad/cameras/camera_qcom2.cc
system/camerad/cameras/camera_qcom2.h
system/camerad/cameras/real_debayer.cl
+selfdrive/sensord/rawgps/*
+
selfdrive/ui/qt/spinner_larch64
selfdrive/ui/qt/text_larch64
diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc
index 240935175bf4bd..23bf8f292989e8 100644
--- a/selfdrive/boardd/boardd.cc
+++ b/selfdrive/boardd/boardd.cc
@@ -197,7 +197,7 @@ Panda *usb_connect(std::string serial="", uint32_t index=0) {
if (getenv("BOARDD_LOOPBACK")) {
panda->set_loopback(true);
}
- panda->enable_deepsleep();
+ //panda->enable_deepsleep();
// power on charging, only the first time. Panda can also change mode and it causes a brief disconneciton
#ifndef __x86_64__
@@ -372,6 +372,7 @@ std::optional send_panda_states(PubMaster *pm, const std::vector
ps.setAlternativeExperience(health.alternative_experience_pkt);
ps.setHarnessStatus(cereal::PandaState::HarnessStatus(health.car_harness_status_pkt));
ps.setInterruptLoad(health.interrupt_load);
+ ps.setFanPower(health.fan_power);
// Convert faults bitset to capnp list
std::bitset fault_bits(health.faults_pkt);
diff --git a/selfdrive/boardd/tests/boardd_old.py b/selfdrive/boardd/tests/boardd_old.py
deleted file mode 100755
index fad29f6f34ae37..00000000000000
--- a/selfdrive/boardd/tests/boardd_old.py
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/usr/bin/env python3
-# pylint: skip-file
-
-# This file is not used by openpilot. Only boardd.cc is used.
-# The python version is slower, but has more options for development.
-
-# TODO: merge the extra functionalities of this file (like MOCK) in boardd.c and
-# delete this python version of boardd
-
-import os
-import struct
-import time
-
-import cereal.messaging as messaging
-from common.realtime import Ratekeeper
-from system.swaglog import cloudlog
-from selfdrive.boardd.boardd import can_capnp_to_can_list
-from cereal import car
-
-SafetyModel = car.CarParams.SafetyModel
-
-# USB is optional
-try:
- import usb1
- from usb1 import USBErrorIO, USBErrorOverflow # pylint: disable=no-name-in-module
-except Exception:
- pass
-
-# *** serialization functions ***
-def can_list_to_can_capnp(can_msgs, msgtype='can'):
- dat = messaging.new_message(msgtype, len(can_msgs))
- for i, can_msg in enumerate(can_msgs):
- if msgtype == 'sendcan':
- cc = dat.sendcan[i]
- else:
- cc = dat.can[i]
- cc.address = can_msg[0]
- cc.busTime = can_msg[1]
- cc.dat = bytes(can_msg[2])
- cc.src = can_msg[3]
- return dat
-
-
-# *** can driver ***
-def can_health():
- while 1:
- try:
- dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x16)
- break
- except (USBErrorIO, USBErrorOverflow):
- cloudlog.exception("CAN: BAD HEALTH, RETRYING")
- v, i = struct.unpack("II", dat[0:8])
- ign_line, ign_can = struct.unpack("BB", dat[20:22])
- return {"voltage": v, "current": i, "ignition_line": bool(ign_line), "ignition_can": bool(ign_can)}
-
-def __parse_can_buffer(dat):
- ret = []
- for j in range(0, len(dat), 0x10):
- ddat = dat[j:j+0x10]
- f1, f2 = struct.unpack("II", ddat[0:8])
- ret.append((f1 >> 21, f2 >> 16, ddat[8:8 + (f2 & 0xF)], (f2 >> 4) & 0xFF))
- return ret
-
-def can_send_many(arr):
- snds = []
- for addr, _, dat, alt in arr:
- if addr < 0x800: # only support 11 bit addr
- snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
- snd = snd.ljust(0x10, b'\x00')
- snds.append(snd)
- while 1:
- try:
- handle.bulkWrite(3, b''.join(snds))
- break
- except (USBErrorIO, USBErrorOverflow):
- cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
-
-def can_recv():
- dat = b""
- while 1:
- try:
- dat = handle.bulkRead(1, 0x10*256)
- break
- except (USBErrorIO, USBErrorOverflow):
- cloudlog.exception("CAN: BAD RECV, RETRYING")
- return __parse_can_buffer(dat)
-
-def can_init():
- global handle, context
- handle = None
- cloudlog.info("attempting can init")
-
- context = usb1.USBContext()
- #context.setDebug(9)
-
- for device in context.getDeviceList(skip_on_error=True):
- if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
- handle = device.open()
- handle.claimInterface(0)
- handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
-
- if handle is None:
- cloudlog.warning("CAN NOT FOUND")
- exit(-1)
-
- cloudlog.info("got handle")
- cloudlog.info("can init done")
-
-def boardd_mock_loop():
- can_init()
- handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
-
- logcan = messaging.sub_sock('can')
- sendcan = messaging.pub_sock('sendcan')
-
- while 1:
- tsc = messaging.drain_sock(logcan, wait_for_one=True)
- snds = map(lambda x: can_capnp_to_can_list(x.can), tsc)
- snd = []
- for s in snds:
- snd += s
- snd = list(filter(lambda x: x[-1] <= 2, snd))
- snd_0 = len(list(filter(lambda x: x[-1] == 0, snd)))
- snd_1 = len(list(filter(lambda x: x[-1] == 1, snd)))
- snd_2 = len(list(filter(lambda x: x[-1] == 2, snd)))
- can_send_many(snd)
-
- # recv @ 100hz
- can_msgs = can_recv()
- got_0 = len(list(filter(lambda x: x[-1] == 0+0x80, can_msgs)))
- got_1 = len(list(filter(lambda x: x[-1] == 1+0x80, can_msgs)))
- got_2 = len(list(filter(lambda x: x[-1] == 2+0x80, can_msgs)))
- print("sent %3d (%3d/%3d/%3d) got %3d (%3d/%3d/%3d)" %
- (len(snd), snd_0, snd_1, snd_2, len(can_msgs), got_0, got_1, got_2))
- m = can_list_to_can_capnp(can_msgs, msgtype='sendcan')
- sendcan.send(m.to_bytes())
-
-def boardd_test_loop():
- can_init()
- cnt = 0
- while 1:
- can_send_many([[0xbb, 0, "\xaa\xaa\xaa\xaa", 0], [0xaa, 0, "\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt), 1]])
- #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]])
- #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]])
- # recv @ 100hz
- can_msgs = can_recv()
- print("got %d" % (len(can_msgs)))
- time.sleep(0.01)
- cnt += 1
-
-# *** main loop ***
-def boardd_loop(rate=100):
- rk = Ratekeeper(rate)
-
- can_init()
-
- # *** publishes can and health
- logcan = messaging.pub_sock('can')
- health_sock = messaging.pub_sock('pandaStates')
-
- # *** subscribes to can send
- sendcan = messaging.sub_sock('sendcan')
-
- # drain sendcan to delete any stale messages from previous runs
- messaging.drain_sock(sendcan)
-
- while 1:
- # health packet @ 2hz
- if (rk.frame % (rate // 2)) == 0:
- health = can_health()
- msg = messaging.new_message('pandaStates', 1)
-
- # store the health to be logged
- msg.pandaState[0].voltage = health['voltage']
- msg.pandaState[0].current = health['current']
- msg.pandaState[0].ignitionLine = health['ignition_line']
- msg.pandaState[0].ignitionCan = health['ignition_can']
- msg.pandaState[0].controlsAllowed = True
-
- health_sock.send(msg.to_bytes())
-
- # recv @ 100hz
- can_msgs = can_recv()
-
- # publish to logger
- # TODO: refactor for speed
- if len(can_msgs) > 0:
- dat = can_list_to_can_capnp(can_msgs).to_bytes()
- logcan.send(dat)
-
- # send can if we have a packet
- tsc = messaging.recv_sock(sendcan)
- if tsc is not None:
- can_send_many(can_capnp_to_can_list(tsc.sendcan))
-
- rk.keep_time()
-
-# *** main loop ***
-def boardd_proxy_loop(rate=100, address="192.168.2.251"):
- rk = Ratekeeper(rate)
-
- can_init()
-
- # *** subscribes can
- logcan = messaging.sub_sock('can', addr=address)
- # *** publishes to can send
- sendcan = messaging.pub_sock('sendcan')
-
- # drain sendcan to delete any stale messages from previous runs
- messaging.drain_sock(sendcan)
-
- while 1:
- # recv @ 100hz
- can_msgs = can_recv()
- #for m in can_msgs:
- # print("R: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
-
- # publish to logger
- # TODO: refactor for speed
- if len(can_msgs) > 0:
- dat = can_list_to_can_capnp(can_msgs, "sendcan")
- sendcan.send(dat)
-
- # send can if we have a packet
- tsc = messaging.recv_sock(logcan)
- if tsc is not None:
- cl = can_capnp_to_can_list(tsc.can)
- #for m in cl:
- # print("S: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
- can_send_many(cl)
-
- rk.keep_time()
-
-def main():
- if os.getenv("MOCK") is not None:
- boardd_mock_loop()
- elif os.getenv("PROXY") is not None:
- boardd_proxy_loop()
- elif os.getenv("BOARDTEST") is not None:
- boardd_test_loop()
- else:
- boardd_loop()
-
-if __name__ == "__main__":
- main()
diff --git a/selfdrive/boardd/tests/replay_many.py b/selfdrive/boardd/tests/replay_many.py
deleted file mode 100755
index 78aef07497ad14..00000000000000
--- a/selfdrive/boardd/tests/replay_many.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python3
-import os
-import sys
-import time
-import signal
-import traceback
-import usb1
-from panda import Panda, PandaDFU
-from multiprocessing import Pool
-
-jungle = "JUNGLE" in os.environ
-if jungle:
- from panda_jungle import PandaJungle # pylint: disable=import-error
-
-import cereal.messaging as messaging
-from selfdrive.boardd.boardd import can_capnp_to_can_list
-
-def initializer():
- """Ignore CTRL+C in the worker process.
- source: https://stackoverflow.com/a/44869451 """
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-def send_thread(sender_serial):
- while True:
- try:
- if jungle:
- sender = PandaJungle(sender_serial)
- else:
- sender = Panda(sender_serial)
- sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
-
- sender.set_can_loopback(False)
- can_sock = messaging.sub_sock('can')
-
- while True:
- tsc = messaging.recv_one(can_sock)
- snd = can_capnp_to_can_list(tsc.can)
- snd = list(filter(lambda x: x[-1] <= 2, snd))
-
- try:
- sender.can_send_many(snd)
- except usb1.USBErrorTimeout:
- pass
-
- # Drain panda message buffer
- sender.can_recv()
- except Exception:
- traceback.print_exc()
- time.sleep(1)
-
-if __name__ == "__main__":
- if jungle:
- serials = PandaJungle.list()
- else:
- serials = Panda.list()
- num_senders = len(serials)
-
- if num_senders == 0:
- print("No senders found. Exiting")
- sys.exit(1)
- else:
- print("%d senders found. Starting broadcast" % num_senders)
-
- if "FLASH" in os.environ:
- for s in PandaDFU.list():
- PandaDFU(s).recover()
-
- time.sleep(1)
- for s in serials:
- Panda(s).recover()
- Panda(s).flash()
-
- pool = Pool(num_senders, initializer=initializer)
- pool.map_async(send_thread, serials)
-
- while True:
- try:
- time.sleep(10)
- except KeyboardInterrupt:
- pool.terminate()
- pool.join()
- raise
-
diff --git a/selfdrive/boardd/tests/test_boardd_api.py b/selfdrive/boardd/tests/test_boardd_api.py
deleted file mode 100644
index f2bcd57a6bf27d..00000000000000
--- a/selfdrive/boardd/tests/test_boardd_api.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import random
-import numpy as np
-
-import selfdrive.boardd.tests.boardd_old as boardd_old
-import selfdrive.boardd.boardd as boardd
-
-from common.realtime import sec_since_boot
-from cereal import log
-import unittest
-
-
-def generate_random_can_data_list():
- can_list = []
- cnt = random.randint(1, 64)
- for _ in range(cnt):
- can_data = np.random.bytes(random.randint(1, 8))
- can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)])
- return can_list, cnt
-
-
-class TestBoarddApiMethods(unittest.TestCase):
- def test_correctness(self):
- for i in range(1000):
- can_list, _ = generate_random_can_data_list()
-
- # Sendcan
- # Old API
- m_old = boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
- # new API
- m = boardd.can_list_to_can_capnp(can_list, 'sendcan')
-
- ev_old = log.Event.from_bytes(m_old)
- ev = log.Event.from_bytes(m)
-
- self.assertEqual(ev_old.which(), ev.which())
- self.assertEqual(len(ev.sendcan), len(ev_old.sendcan))
- for i in range(len(ev.sendcan)):
- attrs = ['address', 'busTime', 'dat', 'src']
- for attr in attrs:
- self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old'))
-
- # Can
- m_old = boardd_old.can_list_to_can_capnp(can_list, 'can').to_bytes()
- # new API
- m = boardd.can_list_to_can_capnp(can_list, 'can')
-
- ev_old = log.Event.from_bytes(m_old)
- ev = log.Event.from_bytes(m)
- self.assertEqual(ev_old.which(), ev.which())
- self.assertEqual(len(ev.can), len(ev_old.can))
- for i in range(len(ev.can)):
- attrs = ['address', 'busTime', 'dat', 'src']
- for attr in attrs:
- self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old'))
-
- def test_performance(self):
- can_list, _ = generate_random_can_data_list()
- recursions = 1000
-
- n1 = sec_since_boot()
- for _ in range(recursions):
- boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
- n2 = sec_since_boot()
- elapsed_old = n2 - n1
-
- # print('Old API, elapsed time: {} secs'.format(elapsed_old))
- n1 = sec_since_boot()
- for _ in range(recursions):
- boardd.can_list_to_can_capnp(can_list)
- n2 = sec_since_boot()
- elapsed_new = n2 - n1
- # print('New API, elapsed time: {} secs'.format(elapsed_new))
- self.assertTrue(elapsed_new < elapsed_old / 2)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/selfdrive/car/CARS_template.md b/selfdrive/car/CARS_template.md
index e448c19d353f81..2fce0f9036176e 100644
--- a/selfdrive/car/CARS_template.md
+++ b/selfdrive/car/CARS_template.md
@@ -5,24 +5,12 @@
# Supported Cars
-A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system.
-
-## How We Rate The Cars
-
-{% for star_row in STAR_DESCRIPTIONS.values() %}
-{% for name, stars in star_row.items() %}
-### {{name}}
-{% for star, description in stars %}
-- {{star_icon.format(star)}} - {{description}}
-{% endfor %}
-
-{% endfor %}
-{% endfor %}
+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.
# {{all_car_info | length}} Supported Cars
|{{Column | map(attribute='value') | join('|')}}|
-|---|---|---|:---:|:---:|:---:|:---:|
+|---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%}
{% for car_info in all_car_info %}
|{% for column in Column %}{{car_info.get_column(column, star_icon, footnote_tag)}}|{% endfor %}
@@ -39,13 +27,13 @@ Although they're not upstream, the community has openpilot running on other make
# Don't see your car here?
**openpilot can support many more cars than it currently does.** There are a few reasons your car may not be supported.
-If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. We don't have a roadmap for car support, and in fact, most car support comes from users like you!
+If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car support comes from users like you!
### Which cars are able to be supported?
-openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering) and [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control), then it almost certainly has these interfaces. These interfaces generally started shipping on cars around 2016.
+openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control.
-If your car has the following packages or features, then it's a good candidate for support. If it does not, then it's unlikely able to be supported.
+If your car has the following packages or features, then it's a good candidate for support.
| Make | Required Package/Features |
| ---- | ------------------------- |
@@ -63,8 +51,9 @@ All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wi
### Toyota Security
-Specific new Toyota models are shipping with a new message authentication method that openpilot does not yet support.
-So far, this list includes:
+openpilot does not yet support these Toyota models due to a new message authentication method.
+[Vote](https://comma.ai/shop/products/vote) if you'd like to see openpilot support on these models.
+
* Toyota RAV4 Prime 2021+
* Toyota Sienna 2021+
* Toyota Venza 2021+
@@ -74,3 +63,4 @@ So far, this list includes:
* Lexus NX 2022+
* Toyota bZ4x 2023+
* Subaru Solterra 2023+
+
diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py
index c77e40daaf28bb..f2d198338d81f4 100644
--- a/selfdrive/car/__init__.py
+++ b/selfdrive/car/__init__.py
@@ -38,7 +38,7 @@ def create_button_enable_events(buttonEvents: capnp.lib.capnp._DynamicListBuilde
def gen_empty_fingerprint():
- return {i: {} for i in range(0, 4)}
+ return {i: {} for i in range(0, 8)}
# FIXME: hardcoding honda civic 2016 touring params so they can be used to
diff --git a/selfdrive/car/body/values.py b/selfdrive/car/body/values.py
index 0caf93b619952a..61b72d5ade21b6 100644
--- a/selfdrive/car/body/values.py
+++ b/selfdrive/car/body/values.py
@@ -2,7 +2,7 @@
from cereal import car
from selfdrive.car import dbc_dict
-from selfdrive.car.docs_definitions import CarInfo, Harness
+from selfdrive.car.docs_definitions import CarInfo
Ecu = car.CarParams.Ecu
SPEED_FROM_RPM = 0.008587
@@ -18,7 +18,7 @@ class CAR:
BODY = "COMMA BODY"
CAR_INFO: Dict[str, CarInfo] = {
- CAR.BODY: CarInfo("comma body", package="All", harness=Harness.none),
+ CAR.BODY: CarInfo("comma body", package="All"),
}
FW_VERSIONS = {
diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py
index 22abf1b677d5fa..5a2d90c64c2410 100644
--- a/selfdrive/car/chrysler/carcontroller.py
+++ b/selfdrive/car/chrysler/carcontroller.py
@@ -22,23 +22,8 @@ def __init__(self, dbc_name, CP, VM):
def update(self, CC, CS):
can_sends = []
- # TODO: can we make this more sane? why is it different for all the cars?
- lkas_control_bit = self.lkas_control_bit_prev
- if CS.out.vEgo > self.CP.minSteerSpeed:
- lkas_control_bit = True
- elif self.CP.carFingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019):
- if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0):
- lkas_control_bit = False
- elif self.CP.carFingerprint in RAM_CARS:
- if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5):
- lkas_control_bit = False
-
- # EPS faults if LKAS re-enables too quickly
- lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200)
lkas_active = CC.latActive and self.lkas_control_bit_prev
- # *** control msgs ***
-
# cruise buttons
if (self.frame - self.last_button_frame)*DT_CTRL > 0.05:
das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0
@@ -61,19 +46,35 @@ def update(self, CC, CS):
# steering
if self.frame % 2 == 0:
+
+ # TODO: can we make this more sane? why is it different for all the cars?
+ lkas_control_bit = self.lkas_control_bit_prev
+ if CS.out.vEgo > self.CP.minSteerSpeed:
+ lkas_control_bit = True
+ elif self.CP.carFingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019):
+ if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0):
+ lkas_control_bit = False
+ elif self.CP.carFingerprint in RAM_CARS:
+ if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5):
+ lkas_control_bit = False
+
+ # EPS faults if LKAS re-enables too quickly
+ lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200)
+
+ if not lkas_control_bit and self.lkas_control_bit_prev:
+ self.last_lkas_falling_edge = self.frame
+ self.lkas_control_bit_prev = lkas_control_bit
+
# steer torque
new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX))
apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params)
- if not lkas_active:
+ if not lkas_active or not lkas_control_bit:
apply_steer = 0
self.apply_steer_last = apply_steer
can_sends.append(create_lkas_command(self.packer, self.CP, int(apply_steer), lkas_control_bit))
self.frame += 1
- if not lkas_control_bit and self.lkas_control_bit_prev:
- self.last_lkas_falling_edge = self.frame
- self.lkas_control_bit_prev = lkas_control_bit
new_actuators = CC.actuators.copy()
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py
index 3c2bc3131a4b3c..9bb22b607ce87a 100755
--- a/selfdrive/car/chrysler/interface.py
+++ b/selfdrive/car/chrysler/interface.py
@@ -2,7 +2,7 @@
from cereal import car
from panda import Panda
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
-from selfdrive.car.chrysler.values import CAR, DBC, RAM_CARS
+from selfdrive.car.chrysler.values import CAR, DBC, RAM_HD, RAM_DT
from selfdrive.car.interfaces import CarInterfaceBase
@@ -12,14 +12,20 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "chrysler"
- ret.radarOffCan = DBC[candidate]['radar'] is None
+ ret.dashcamOnly = candidate in RAM_HD
- param = Panda.FLAG_CHRYSLER_RAM_DT if candidate in RAM_CARS else None
- ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler, param)]
+ ret.radarOffCan = DBC[candidate]['radar'] is None
ret.steerActuatorDelay = 0.1
ret.steerLimitTimer = 0.4
+ # safety config
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)]
+ if candidate in RAM_HD:
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_HD
+ elif candidate in RAM_DT:
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT
+
ret.minSteerSpeed = 3.8 # m/s
if candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019):
# TODO: allow 2019 cars to steer down to 13 m/s if already engaged.
@@ -47,18 +53,24 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
# Ram
elif candidate == CAR.RAM_1500:
ret.steerActuatorDelay = 0.2
-
ret.wheelbase = 3.88
ret.steerRatio = 16.3
ret.mass = 2493. + STD_CARGO_KG
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
-
ret.minSteerSpeed = 14.5
if car_fw is not None:
for fw in car_fw:
if fw.ecu == 'eps' and fw.fwVersion in (b"68312176AE", b"68312176AG", b"68273275AG"):
ret.minSteerSpeed = 0.
+ elif candidate == CAR.RAM_HD:
+ ret.steerActuatorDelay = 0.2
+ ret.wheelbase = 3.785
+ ret.steerRatio = 15.61
+ ret.mass = 3405. + STD_CARGO_KG
+ ret.minSteerSpeed = 16
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False)
+
else:
raise ValueError(f"Unsupported car: {candidate}")
diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py
index c45a81a0ace509..10478efa89ed49 100644
--- a/selfdrive/car/chrysler/values.py
+++ b/selfdrive/car/chrysler/values.py
@@ -22,23 +22,30 @@ class CAR:
# Ram
RAM_1500 = "RAM 1500 5TH GEN"
+ RAM_HD = "RAM HD 5TH GEN"
class CarControllerParams:
def __init__(self, CP):
- self.STEER_MAX = 261 # higher than this faults the EPS on Chrysler/Jeep. Ram DT allows more
self.STEER_ERROR_MAX = 80
-
- if CP.carFingerprint in RAM_CARS:
+ if CP.carFingerprint in RAM_HD:
+ self.STEER_DELTA_UP = 14
+ self.STEER_DELTA_DOWN = 14
+ self.STEER_MAX = 361 # higher than this faults the EPS
+ elif CP.carFingerprint in RAM_DT:
self.STEER_DELTA_UP = 6
self.STEER_DELTA_DOWN = 6
+ self.STEER_MAX = 261 # EPS allows more, up to 350?
else:
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 3
+ self.STEER_MAX = 261 # higher than this faults the EPS
STEER_THRESHOLD = 120
-RAM_CARS = {CAR.RAM_1500, }
+RAM_DT = {CAR.RAM_1500, }
+RAM_HD = {CAR.RAM_HD, }
+RAM_CARS = RAM_DT | RAM_HD
@dataclass
class ChryslerCarInfo(CarInfo):
@@ -57,6 +64,10 @@ class ChryslerCarInfo(CarInfo):
CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-22", harness=Harness.ram),
+ CAR.RAM_HD: [
+ ChryslerCarInfo("Ram 2500 2020-22", harness=Harness.ram),
+ ChryslerCarInfo("Ram 3500 2020-22", harness=Harness.ram),
+ ],
}
# Unique CAN messages:
@@ -177,6 +188,41 @@ class ChryslerCarInfo(CarInfo):
b'68500483AB',
],
},
+
+ CAR.RAM_HD: {
+ (Ecu.combinationMeter, 0x742, None): [
+ b'68361606AH',
+ b'68492693AD',
+ ],
+ (Ecu.srs, 0x744, None): [
+ b'68399794AC',
+ b'68428503AA',
+ b'68428505AA',
+ ],
+ (Ecu.esp, 0x747, None): [
+ b'68334977AH',
+ b'68504022AB',
+ b'68530686AB',
+ ],
+ (Ecu.fwdRadar, 0x753, None): [
+ b'04672895AB',
+ b'56029827AG',
+ b'68484694AE',
+ ],
+ (Ecu.eps, 0x761, None): [
+ b'68421036AC',
+ b'68507906AB',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'52421132AF',
+ b'M2370131MB',
+ b'M2421132MB',
+ ],
+ (Ecu.gateway, 0x18DACBF1, None): [
+ b'68488419AB',
+ b'68535476AB',
+ ],
+ },
}
DBC = {
@@ -188,4 +234,5 @@ class ChryslerCarInfo(CarInfo):
CAR.JEEP_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.JEEP_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None),
+ CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None),
}
diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py
index 5793596a437009..4d79cbfc765b42 100755
--- a/selfdrive/car/docs.py
+++ b/selfdrive/car/docs.py
@@ -8,16 +8,16 @@
from typing import Dict, List
from common.basedir import BASEDIR
-from selfdrive.car.docs_definitions import STAR_DESCRIPTIONS, StarColumns, TierColumns, CarInfo, Column, Star
+from selfdrive.car import gen_empty_fingerprint
+from selfdrive.car.docs_definitions import CarInfo, Column
from selfdrive.car.car_helpers import interfaces, get_interface_attr
from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR as HKG_RADAR_START_ADDR
-def get_all_footnotes(only_tier_cols: bool = False) -> Dict[Enum, int]:
+def get_all_footnotes() -> Dict[Enum, int]:
all_footnotes = []
- hide_cols = set(StarColumns) - set(TierColumns) if only_tier_cols else []
for footnotes in get_interface_attr("Footnote", ignore_none=True).values():
- all_footnotes.extend([fn for fn in footnotes if fn.value.column not in hide_cols])
+ all_footnotes.extend(footnotes)
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}
@@ -25,12 +25,13 @@ def get_all_footnotes(only_tier_cols: bool = False) -> Dict[Enum, int]:
CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md")
-def get_all_car_info(only_tier_cols: bool = False) -> List[CarInfo]:
+def get_all_car_info() -> List[CarInfo]:
all_car_info: List[CarInfo] = []
- footnotes = get_all_footnotes(only_tier_cols)
+ footnotes = get_all_footnotes()
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
# Hyundai exception: those with radar have openpilot longitudinal
- fingerprint = {0: {}, 1: {HKG_RADAR_START_ADDR: 8}, 2: {}, 3: {}}
+ fingerprint = gen_empty_fingerprint()
+ fingerprint[1] = {HKG_RADAR_START_ADDR: 8}
CP = interfaces[model][0].get_params(model, fingerprint=fingerprint, disable_radar=True)
if CP.dashcamOnly or car_info is None:
@@ -57,21 +58,13 @@ def group_by_make(all_car_info: List[CarInfo]) -> Dict[str, List[CarInfo]]:
return dict(sorted_car_info)
-def generate_cars_md(all_car_info: List[CarInfo], template_fn: str, only_tier_cols: bool) -> str:
+def generate_cars_md(all_car_info: List[CarInfo], template_fn: str) -> str:
with open(template_fn, "r") as f:
template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True)
- cols = list(Column)
- if only_tier_cols:
- hide_cols = set(StarColumns) - set(TierColumns)
- cols = [c for c in cols if c not in hide_cols]
- for car in all_car_info:
- for c in hide_cols:
- del car.row[c]
-
- footnotes = [fn.value.text for fn in get_all_footnotes(only_tier_cols)]
+ footnotes = [fn.value.text for fn in get_all_footnotes()]
cars_md: str = template.render(all_car_info=all_car_info, group_by_make=group_by_make,
- footnotes=footnotes, Star=Star, Column=cols, STAR_DESCRIPTIONS=STAR_DESCRIPTIONS)
+ footnotes=footnotes, Column=Column)
return cars_md
@@ -79,11 +72,10 @@ def generate_cars_md(all_car_info: List[CarInfo], template_fn: str, only_tier_co
parser = argparse.ArgumentParser(description="Auto generates supported cars documentation",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
- parser.add_argument("--tier-columns", action="store_true", help="Include only columns that count in the tier")
parser.add_argument("--template", default=CARS_MD_TEMPLATE, help="Override default template filename")
parser.add_argument("--out", default=CARS_MD_OUT, help="Override default generated filename")
args = parser.parse_args()
with open(args.out, 'w') as f:
- f.write(generate_cars_md(get_all_car_info(args.tier_columns), args.template, args.tier_columns))
+ f.write(generate_cars_md(get_all_car_info(), args.template))
print(f"Generated and written to {args.out}")
diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py
index 46222d10040fc1..52d6388d7edf6b 100644
--- a/selfdrive/car/docs_definitions.py
+++ b/selfdrive/car/docs_definitions.py
@@ -2,7 +2,7 @@
from collections import namedtuple
from dataclasses import dataclass, field
from enum import Enum
-from typing import Dict, List, Optional, Tuple, Union, no_type_check
+from typing import Dict, List, Optional, Tuple, Union
from cereal import car
from common.conversions import Conversions as CV
@@ -10,21 +10,20 @@
GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2
MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)"
-
-class Tier(Enum):
- GOLD = 0
- SILVER = 1
- BRONZE = 2
+# Makes that lack auto-resume with stock long, and auto resume in any configuration
+NO_AUTO_RESUME_STOCK_LONG = {"toyota", "gm"}
+NO_AUTO_RESUME = NO_AUTO_RESUME_STOCK_LONG | {"nissan", "subaru"}
class Column(Enum):
MAKE = "Make"
MODEL = "Model"
PACKAGE = "Supported Package"
- LONGITUDINAL = "openpilot ACC"
- FSR_LONGITUDINAL = "Stop and Go"
- FSR_STEERING = "Steer to 0"
+ LONGITUDINAL = "ACC"
+ FSR_LONGITUDINAL = "No ACC accel below"
+ FSR_STEERING = "No ALC below"
STEERING_TORQUE = "Steering Torque"
+ HARNESS = "Harness"
class Star(Enum):
@@ -33,9 +32,46 @@ class Star(Enum):
EMPTY = "empty"
-StarColumns = list(Column)[3:]
-TierColumns = (Column.FSR_LONGITUDINAL, Column.FSR_STEERING, Column.STEERING_TORQUE)
-CarFootnote = namedtuple("CarFootnote", ["text", "column", "star"], defaults=[None])
+class Harness(Enum):
+ nidec = "Honda Nidec"
+ bosch_a = "Honda Bosch A"
+ bosch_b = "Honda Bosch B"
+ toyota = "Toyota"
+ subaru_a = "Subaru A"
+ subaru_b = "Subaru B"
+ fca = "FCA"
+ ram = "Ram"
+ vw = "VW"
+ j533 = "J533"
+ hyundai_a = "Hyundai A"
+ hyundai_b = "Hyundai B"
+ hyundai_c = "Hyundai C"
+ hyundai_d = "Hyundai D"
+ hyundai_e = "Hyundai E"
+ hyundai_f = "Hyundai F"
+ hyundai_g = "Hyundai G"
+ hyundai_h = "Hyundai H"
+ hyundai_i = "Hyundai I"
+ hyundai_j = "Hyundai J"
+ hyundai_k = "Hyundai K"
+ hyundai_l = "Hyundai L"
+ hyundai_m = "Hyundai M"
+ hyundai_n = "Hyundai N"
+ hyundai_o = "Hyundai O"
+ hyundai_p = "Hyundai P"
+ hyundai_q = "Hyundai Q"
+ custom = "Developer"
+ obd_ii = "OBD-II"
+ gm = "GM"
+ nissan_a = "Nissan A"
+ nissan_b = "Nissan B"
+ mazda = "Mazda"
+ ford_q3 = "Ford Q3"
+ ford_q4 = "Ford Q4"
+ none = "None"
+
+
+CarFootnote = namedtuple("CarFootnote", ["text", "column"], defaults=[None])
def get_footnotes(footnotes: List[Enum], column: Column) -> List[Enum]:
@@ -79,7 +115,7 @@ class CarInfo:
footnotes: List[Enum] = field(default_factory=list)
min_steer_speed: Optional[float] = None
min_enable_speed: Optional[float] = None
- harness: Optional[Enum] = None
+ harness: Enum = Harness.none
def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
# TODO: set all the min steer speeds in carParams and remove this
@@ -88,8 +124,6 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
else:
self.min_steer_speed = CP.minSteerSpeed
- assert self.harness is not None, f"{CP.carFingerprint}: Need to specify car harness"
-
# TODO: set all the min enable speeds in carParams correctly and remove this
if self.min_enable_speed is None:
self.min_enable_speed = CP.minEnableSpeed
@@ -101,11 +135,11 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
Column.MAKE: self.make,
Column.MODEL: self.model,
Column.PACKAGE: self.package,
- # StarColumns
- Column.LONGITUDINAL: Star.FULL if CP.openpilotLongitudinalControl and not CP.radarOffCan else Star.EMPTY,
- Column.FSR_LONGITUDINAL: Star.FULL if self.min_enable_speed <= 0. else Star.EMPTY,
- Column.FSR_STEERING: Star.FULL if self.min_steer_speed <= 0. else Star.EMPTY,
+ Column.LONGITUDINAL: "openpilot" if CP.openpilotLongitudinalControl and not CP.radarOffCan else "Stock",
+ Column.FSR_LONGITUDINAL: f"{max(self.min_enable_speed * CV.MS_TO_MPH, 0):.0f} mph",
+ Column.FSR_STEERING: f"{max(self.min_steer_speed * CV.MS_TO_MPH, 0):.0f} mph",
Column.STEERING_TORQUE: Star.EMPTY,
+ Column.HARNESS: self.harness.value,
}
# Set steering torque star from max lateral acceleration
@@ -113,26 +147,7 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
if CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD:
self.row[Column.STEERING_TORQUE] = Star.FULL
- if CP.notCar:
- for col in StarColumns:
- self.row[col] = Star.FULL
-
self.all_footnotes = all_footnotes
- for column in StarColumns:
- # Demote if footnote specifies a star
- for fn in get_footnotes(self.footnotes, column):
- if fn.value.star is not None:
- self.row[column] = fn.value.star
-
- # openpilot ACC star doesn't count for tiers
- full_stars = [s for col, s in self.row.items() if col in TierColumns].count(Star.FULL)
- if full_stars == len(TierColumns):
- self.tier = Tier.GOLD
- elif full_stars == len(TierColumns) - 1:
- self.tier = Tier.SILVER
- else:
- self.tier = Tier.BRONZE
-
self.year_list = get_year_list(self.years)
self.detail_sentence = self.get_detail_sentence(CP)
@@ -147,11 +162,11 @@ def get_detail_sentence(self, CP):
else:
alc = ""
- # Exception for Nissan, Subaru, and stock long Toyota which do not auto-resume yet
+ # Exception for cars which do not auto-resume yet
acc = ""
if self.min_enable_speed > 0:
acc = f" while driving above {self.min_enable_speed * CV.MS_TO_MPH:.0f} mph"
- elif CP.carName not in ("nissan", "subaru", "toyota") or (CP.carName == "toyota" and CP.openpilotLongitudinalControl):
+ elif CP.carName not in NO_AUTO_RESUME or (CP.carName in NO_AUTO_RESUME_STOCK_LONG and CP.openpilotLongitudinalControl):
acc = " that automatically resumes from a stop"
if self.row[Column.STEERING_TORQUE] != Star.FULL:
@@ -165,10 +180,9 @@ def get_detail_sentence(self, CP):
else:
raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}")
- @no_type_check
def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str:
item: Union[str, Star] = self.row[column]
- if column in StarColumns:
+ if isinstance(item, Star):
item = star_icon.format(item.value)
elif column == Column.MODEL and len(self.years):
item += f" {self.years}"
@@ -180,56 +194,3 @@ def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str:
return item
-
-class Harness(Enum):
- nidec = "Honda Nidec"
- bosch_a = "Honda Bosch A"
- bosch_b = "Honda Bosch B"
- toyota = "Toyota"
- subaru = "Subaru"
- fca = "FCA"
- ram = "Ram"
- vw = "VW"
- j533 = "J533"
- hyundai_a = "Hyundai A"
- hyundai_b = "Hyundai B"
- hyundai_c = "Hyundai C"
- hyundai_d = "Hyundai D"
- hyundai_e = "Hyundai E"
- hyundai_f = "Hyundai F"
- hyundai_g = "Hyundai G"
- hyundai_h = "Hyundai H"
- hyundai_i = "Hyundai I"
- hyundai_j = "Hyundai J"
- hyundai_k = "Hyundai K"
- hyundai_l = "Hyundai L"
- hyundai_m = "Hyundai M"
- hyundai_n = "Hyundai N"
- hyundai_o = "Hyundai O"
- hyundai_p = "Hyundai P"
- custom = "Developer"
- obd_ii = "OBD-II"
- nissan_a = "Nissan A"
- nissan_b = "Nissan B"
- mazda = "Mazda"
- none = "None"
-
-
-STAR_DESCRIPTIONS = {
- "Gas & Brakes": { # icon and row name
- Column.FSR_LONGITUDINAL.value: [
- [Star.FULL.value, "openpilot operates down to 0 mph."],
- [Star.EMPTY.value, "openpilot operates only above a minimum speed. See your car's manual for the minimum speed."],
- ],
- },
- "Steering": {
- Column.FSR_STEERING.value: [
- [Star.FULL.value, "openpilot can control the steering wheel down to 0 mph."],
- [Star.EMPTY.value, "No steering control below certain speeds. See your car's manual for the minimum speed."],
- ],
- Column.STEERING_TORQUE.value: [
- [Star.FULL.value, "Car has enough steering torque to comfortably take most highway turns."],
- [Star.EMPTY.value, "Limited ability to make tighter turns."],
- ],
- },
-}
diff --git a/selfdrive/car/ford/radar_interface.py b/selfdrive/car/ford/radar_interface.py
index 866602cf09888b..c942703002ff29 100644
--- a/selfdrive/car/ford/radar_interface.py
+++ b/selfdrive/car/ford/radar_interface.py
@@ -1,33 +1,63 @@
#!/usr/bin/env python3
+from math import cos, sin
from cereal import car
-from common.conversions import Conversions as CV
from opendbc.can.parser import CANParser
-from selfdrive.car.ford.values import CANBUS, DBC
+from common.conversions import Conversions as CV
+from selfdrive.car.ford.values import CANBUS, DBC, RADAR
from selfdrive.car.interfaces import RadarInterfaceBase
-RADAR_MSGS = list(range(0x500, 0x540))
+DELPHI_ESR_RADAR_MSGS = list(range(0x500, 0x540))
+DELPHI_MRR_RADAR_START_ADDR = 0x120
+DELPHI_MRR_RADAR_MSG_COUNT = 64
-def _create_radar_can_parser(car_fingerprint):
- if DBC[car_fingerprint]['radar'] is None:
- return None
- msg_n = len(RADAR_MSGS)
+def _create_delphi_esr_radar_can_parser():
+ msg_n = len(DELPHI_ESR_RADAR_MSGS)
signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n,
- RADAR_MSGS * 3))
- checks = list(zip(RADAR_MSGS, [20] * msg_n))
+ DELPHI_ESR_RADAR_MSGS * 3))
+ checks = list(zip(DELPHI_ESR_RADAR_MSGS, [20] * msg_n))
+
+ return CANParser(RADAR.DELPHI_ESR, signals, checks, CANBUS.radar)
+
+
+def _create_delphi_mrr_radar_can_parser():
+ signals = []
+ checks = []
+
+ for i in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
+ msg = f"MRR_Detection_{i:03d}"
+ signals += [
+ (f"CAN_DET_VALID_LEVEL_{i:02d}", msg),
+ (f"CAN_DET_AZIMUTH_{i:02d}", msg),
+ (f"CAN_DET_RANGE_{i:02d}", msg),
+ (f"CAN_DET_RANGE_RATE_{i:02d}", msg),
+ (f"CAN_DET_AMPLITUDE_{i:02d}", msg),
+ (f"CAN_SCAN_INDEX_2LSB_{i:02d}", msg),
+ ]
+ checks += [(msg, 20)]
+
+ return CANParser(RADAR.DELPHI_MRR, signals, checks, CANBUS.radar)
- return CANParser(DBC[car_fingerprint]['radar'], signals, checks, CANBUS.radar)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
- self.validCnt = {key: 0 for key in RADAR_MSGS}
- self.track_id = 0
- self.rcp = _create_radar_can_parser(CP.carFingerprint)
- self.trigger_msg = 0x53f
self.updated_messages = set()
+ self.track_id = 0
+ self.radar = DBC[CP.carFingerprint]['radar']
+ if self.radar is None:
+ self.rcp = None
+ elif self.radar == RADAR.DELPHI_ESR:
+ self.rcp = _create_delphi_esr_radar_can_parser()
+ self.trigger_msg = DELPHI_ESR_RADAR_MSGS[-1]
+ self.valid_cnt = {key: 0 for key in DELPHI_ESR_RADAR_MSGS}
+ elif self.radar == RADAR.DELPHI_MRR:
+ self.rcp = _create_delphi_mrr_radar_can_parser()
+ self.trigger_msg = DELPHI_MRR_RADAR_START_ADDR + DELPHI_MRR_RADAR_MSG_COUNT - 1
+ else:
+ raise ValueError(f"Unsupported radar: {self.radar}")
def update(self, can_strings):
if self.rcp is None:
@@ -45,20 +75,30 @@ def update(self, can_strings):
errors.append("canError")
ret.errors = errors
+ if self.radar == RADAR.DELPHI_ESR:
+ self._update_delphi_esr()
+ elif self.radar == RADAR.DELPHI_MRR:
+ self._update_delphi_mrr()
+
+ ret.points = list(self.pts.values())
+ self.updated_messages.clear()
+ return ret
+
+ def _update_delphi_esr(self):
for ii in sorted(self.updated_messages):
cpt = self.rcp.vl[ii]
if cpt['X_Rel'] > 0.00001:
- self.validCnt[ii] = 0 # reset counter
+ self.valid_cnt[ii] = 0 # reset counter
if cpt['X_Rel'] > 0.00001:
- self.validCnt[ii] += 1
+ self.valid_cnt[ii] += 1
else:
- self.validCnt[ii] = max(self.validCnt[ii] - 1, 0)
- #print ii, self.validCnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle']
+ self.valid_cnt[ii] = max(self.valid_cnt[ii] - 1, 0)
+ #print ii, self.valid_cnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle']
# radar point only valid if there have been enough valid measurements
- if self.validCnt[ii] > 0:
+ if self.valid_cnt[ii] > 0:
if ii not in self.pts:
self.pts[ii] = car.RadarData.RadarPoint.new_message()
self.pts[ii].trackId = self.track_id
@@ -73,6 +113,36 @@ def update(self, can_strings):
if ii in self.pts:
del self.pts[ii]
- ret.points = list(self.pts.values())
- self.updated_messages.clear()
- return ret
+ def _update_delphi_mrr(self):
+ for ii in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
+ msg = self.rcp.vl[f"MRR_Detection_{ii:03d}"]
+
+ # SCAN_INDEX rotates through 0..3 on each message
+ # treat these as separate points
+ scanIndex = msg[f"CAN_SCAN_INDEX_2LSB_{ii:02d}"]
+ i = (ii - 1) * 4 + scanIndex
+
+ if i not in self.pts:
+ self.pts[i] = car.RadarData.RadarPoint.new_message()
+ self.pts[i].trackId = self.track_id
+ self.pts[i].aRel = float('nan')
+ self.pts[i].yvRel = float('nan')
+ self.track_id += 1
+
+ valid = bool(msg[f"CAN_DET_VALID_LEVEL_{ii:02d}"])
+ amplitude = msg[f"CAN_DET_AMPLITUDE_{ii:02d}"] # dBsm [-64|63]
+
+ if valid and 0 < amplitude <= 15:
+ azimuth = msg[f"CAN_DET_AZIMUTH_{ii:02d}"] # rad [-3.1416|3.13964]
+ dist = msg[f"CAN_DET_RANGE_{ii:02d}"] # m [0|255.984]
+ distRate = msg[f"CAN_DET_RANGE_RATE_{ii:02d}"] # m/s [-128|127.984]
+
+ # *** openpilot radar point ***
+ self.pts[i].dRel = cos(azimuth) * dist # m from front of car
+ self.pts[i].yRel = -sin(azimuth) * dist # in car frame's y axis, left is positive
+ self.pts[i].vRel = distRate # m/s
+
+ self.pts[i].measured = True
+
+ else:
+ del self.pts[i]
diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py
index 6b8396cf072ae6..825d515da9cbd6 100644
--- a/selfdrive/car/ford/values.py
+++ b/selfdrive/car/ford/values.py
@@ -24,6 +24,11 @@ class CarControllerParams:
STEER_RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
+class RADAR:
+ DELPHI_ESR = 'ford_fusion_2018_adas'
+ DELPHI_MRR = 'FORD_CADS'
+
+
class CANBUS:
main = 0
radar = 1
@@ -80,6 +85,6 @@ class CAR:
DBC = {
- CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', None),
- CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', None),
+ CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
+ CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
}
diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py
index 0ed8cc5425d14d..66a19a610d640d 100755
--- a/selfdrive/car/fw_versions.py
+++ b/selfdrive/car/fw_versions.py
@@ -208,12 +208,14 @@ class Request:
"chrysler",
[CHRYSLER_VERSION_REQUEST],
[CHRYSLER_VERSION_RESPONSE],
+ whitelist_ecus=[Ecu.esp, Ecu.eps, Ecu.srs, Ecu.gateway, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.combinationMeter],
rx_offset=CHRYSLER_RX_OFFSET,
),
Request(
"chrysler",
[CHRYSLER_VERSION_REQUEST],
[CHRYSLER_VERSION_RESPONSE],
+ whitelist_ecus=[Ecu.engine, Ecu.transmission],
),
# Ford
Request(
@@ -337,17 +339,21 @@ def match_fw_to_car_exact(fw_versions_dict):
return set(candidates.keys()) - set(invalid)
-def match_fw_to_car(fw_versions, allow_fuzzy=True):
+def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True):
# Try exact matching first
- exact_matches = [(True, match_fw_to_car_exact)]
+ exact_matches = []
+ if allow_exact:
+ exact_matches = [(True, match_fw_to_car_exact)]
if allow_fuzzy:
exact_matches.append((False, match_fw_to_car_fuzzy))
+ brands = get_interface_attr('FW_VERSIONS', ignore_none=True).keys()
for exact_match, match_func in exact_matches:
- # TODO: For each brand, attempt to fingerprint using only FW returned from its queries
+ # For each brand, attempt to fingerprint using all FW returned from its queries
matches = set()
- fw_versions_dict = build_fw_dict(fw_versions, filter_brand=None)
- matches |= match_func(fw_versions_dict)
+ for brand in brands:
+ fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand)
+ matches |= match_func(fw_versions_dict)
if len(matches):
return exact_match, matches
@@ -416,9 +422,8 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa
for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True):
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress)
all_car_fw.extend(car_fw)
-
- # TODO: Until erroneous FW versions are removed, try to fingerprint on all possible combinations so far
- _, matches = match_fw_to_car(all_car_fw, allow_fuzzy=False)
+ # Try to match using FW returned from this brand only
+ matches = match_fw_to_car_exact(build_fw_dict(car_fw))
if len(matches) == 1:
break
diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py
index 0e7406514315fe..977d20c5b3c2e1 100644
--- a/selfdrive/car/gm/carcontroller.py
+++ b/selfdrive/car/gm/carcontroller.py
@@ -5,7 +5,7 @@
from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.gm import gmcan
-from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, EV_CAR
+from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, EV_CAR
VisualAlert = car.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation
@@ -19,6 +19,7 @@ def __init__(self, dbc_name, CP, VM):
self.apply_gas = 0
self.apply_brake = 0
self.frame = 0
+ self.last_button_frame = 0
self.lka_steering_cmd_counter_last = -1
self.lka_icon_status_last = (False, False)
@@ -46,8 +47,7 @@ def update(self, CC, CS):
if CS.lka_steering_cmd_counter != self.lka_steering_cmd_counter_last:
self.lka_steering_cmd_counter_last = CS.lka_steering_cmd_counter
elif (self.frame % self.params.STEER_STEP) == 0:
- lkas_enabled = CC.latActive and CS.out.vEgo > self.params.MIN_STEER_SPEED
- if lkas_enabled:
+ if CC.latActive:
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
else:
@@ -58,7 +58,7 @@ def update(self, CC, CS):
# moment of disengaging, increment the counter based on the last message known to pass Panda safety checks.
idx = (CS.lka_steering_cmd_counter + 1) % 4
- can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, lkas_enabled))
+ can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive))
if self.CP.openpilotLongitudinalControl:
# Gas/regen, brakes, and UI commands - all at 25Hz
@@ -107,6 +107,13 @@ def update(self, CC, CS):
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0:
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
+ else:
+ # Stock longitudinal, integrated at camera
+ if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
+ if CC.cruiseControl.cancel:
+ self.last_button_frame = self.frame
+ can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL))
+
# Show green icon when LKA torque is applied, and
# alarming orange icon when approaching torque limit.
# If not sent again, LKA icon disappears in about 5 seconds.
@@ -114,6 +121,8 @@ def update(self, CC, CS):
lka_active = CS.lkas_status == 1
lka_critical = lka_active and abs(actuators.steer) > 0.9
lka_icon_status = (lka_active, lka_critical)
+
+ # SW_GMLAN not yet on cam harness, no HUD alerts
if self.CP.networkLocation != NetworkLocation.fwdCamera and (self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last):
steer_alert = hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw)
can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert))
diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py
index b174d0327ed7c4..0bba1d29b8c01f 100644
--- a/selfdrive/car/gm/carstate.py
+++ b/selfdrive/car/gm/carstate.py
@@ -1,4 +1,5 @@
from cereal import car
+from common.conversions import Conversions as CV
from common.numpy_fast import mean
from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser
@@ -6,6 +7,7 @@
from selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD
TransmissionType = car.CarParams.TransmissionType
+NetworkLocation = car.CarParams.NetworkLocation
class CarState(CarStateBase):
@@ -14,12 +16,14 @@ def __init__(self, CP):
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
self.lka_steering_cmd_counter = 0
+ self.buttons_counter = 0
- def update(self, pt_cp, loopback_cp):
+ def update(self, pt_cp, cam_cp, loopback_cp):
ret = car.CarState.new_message()
self.prev_cruise_buttons = self.cruise_buttons
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
+ self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
ret.wheelSpeeds = self.get_wheel_speeds(
pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
@@ -77,9 +81,21 @@ def update(self, pt_cp, loopback_cp):
ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF
ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL
+ if self.CP.networkLocation == NetworkLocation.fwdCamera:
+ ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
return ret
+ @staticmethod
+ def get_cam_can_parser(CP):
+ signals = []
+ checks = []
+ if CP.networkLocation == NetworkLocation.fwdCamera:
+ signals.append(("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"))
+ checks.append(("ASCMActiveCruiseControlStatus", 25))
+
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.CAMERA)
+
@staticmethod
def get_can_parser(CP):
signals = [
@@ -95,6 +111,7 @@ def get_can_parser(CP):
("AcceleratorPedal2", "AcceleratorPedal2"),
("CruiseState", "AcceleratorPedal2"),
("ACCButtons", "ASCMSteeringButton"),
+ ("RollingCounter", "ASCMSteeringButton"),
("SteeringWheelAngle", "PSCMSteeringAngle"),
("SteeringWheelRate", "PSCMSteeringAngle"),
("FLWheelSpd", "EBCMWheelSpdFront"),
diff --git a/selfdrive/car/gm/gmcan.py b/selfdrive/car/gm/gmcan.py
index 47e6090a03a32a..20e4c4ab6efb69 100644
--- a/selfdrive/car/gm/gmcan.py
+++ b/selfdrive/car/gm/gmcan.py
@@ -1,5 +1,12 @@
from selfdrive.car import make_can_msg
+def create_buttons(packer, bus, idx, button):
+ values = {
+ "ACCButtons": button,
+ "RollingCounter": idx,
+ }
+ return packer.make_can_msg("ASCMSteeringButton", bus, values)
+
def create_steering_control(packer, bus, apply_steer, idx, lkas_active):
values = {
@@ -59,8 +66,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, near_stop, at_f
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
def create_acc_dashboard_command(packer, bus, acc_engaged, target_speed_kph, lead_car_in_sight, fcw):
- # Not a bit shift, dash can round up based on low 4 bits.
- target_speed = int(target_speed_kph * 16) & 0xfff
+ target_speed = min(target_speed_kph, 255)
values = {
"ACCAlwaysOne" : 1,
diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py
index 10ae670ac8e5b5..09d950ea8d0072 100755
--- a/selfdrive/car/gm/interface.py
+++ b/selfdrive/car/gm/interface.py
@@ -1,10 +1,11 @@
#!/usr/bin/env python3
from cereal import car
from math import fabs
+from panda import Panda
from common.conversions import Conversions as CV
-from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
-from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams
+from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
+from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR
from selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type
@@ -48,29 +49,36 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "gm"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
- ret.pcmCruise = False # For ASCM, stock non-adaptive cruise control is kept off
- ret.radarOffCan = False # For ASCM, radar exists
- ret.transmissionType = TransmissionType.automatic
- # NetworkLocation.gateway: OBD-II harness (typically ASCM), NetworkLocation.fwdCamera: non-ASCM
- ret.networkLocation = NetworkLocation.gateway
+
+ if candidate in EV_CAR:
+ ret.transmissionType = TransmissionType.direct
+ else:
+ ret.transmissionType = TransmissionType.automatic
+
+ if candidate in CAMERA_ACC_CAR:
+ ret.openpilotLongitudinalControl = False
+ ret.networkLocation = NetworkLocation.fwdCamera
+ ret.radarOffCan = True # no radar
+ ret.pcmCruise = True
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
+ else: # ASCM, OBD-II harness
+ ret.openpilotLongitudinalControl = True
+ ret.networkLocation = NetworkLocation.gateway
+ ret.radarOffCan = False
+ ret.pcmCruise = False # stock non-adaptive cruise control is kept off
# These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL}
- # Presence of a camera on the object bus is ok.
- # Have to go to read_only if ASCM is online (ACC-enabled cars),
- # or camera is on powertrain bus (LKA cars without ACC).
- ret.openpilotLongitudinalControl = True
- tire_stiffness_factor = 0.444 # not optimized yet
-
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]]
ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
+ tire_stiffness_factor = 0.444 # not optimized yet
ret.longitudinalTuning.kpBP = [5., 35.]
ret.longitudinalTuning.kpV = [2.4, 1.5]
@@ -84,7 +92,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
if candidate == CAR.VOLT:
- ret.transmissionType = TransmissionType.direct
ret.mass = 1607. + STD_CARGO_KG
ret.wheelbase = 2.69
ret.steerRatio = 17.7 # Stock 15.7, LiveParameters
@@ -143,6 +150,25 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.lateralTuning.pid.kf = 0.000045
tire_stiffness_factor = 1.0
+ elif candidate == CAR.BOLT_EUV:
+ ret.minEnableSpeed = -1
+ ret.mass = 1669. + STD_CARGO_KG
+ ret.wheelbase = 2.675
+ ret.steerRatio = 16.8
+ ret.centerToFront = ret.wheelbase * 0.4
+ tire_stiffness_factor = 1.0
+ ret.steerActuatorDelay = 0.2
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
+
+ elif candidate == CAR.SILVERADO:
+ ret.minEnableSpeed = -1
+ ret.mass = 2200. + STD_CARGO_KG
+ ret.wheelbase = 3.75
+ ret.steerRatio = 16.3
+ ret.centerToFront = ret.wheelbase * 0.5
+ tire_stiffness_factor = 1.0
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
+
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
@@ -156,7 +182,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
# returns a car.CarState
def _update(self, c):
- ret = self.CS.update(self.cp, self.cp_loopback)
+ ret = self.CS.update(self.cp, self.cp_cam, self.cp_loopback)
if self.CS.cruise_buttons != self.CS.prev_cruise_buttons and self.CS.prev_cruise_buttons != CruiseButtons.INIT:
be = create_button_event(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, CruiseButtons.UNPRESS)
@@ -178,9 +204,6 @@ def _update(self, c):
if ret.vEgo < self.CP.minSteerSpeed:
events.add(car.CarEvent.EventName.belowSteerSpeed)
- # handle button presses
- events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise))
-
ret.events = events.to_msg()
return ret
diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py
index 016910a40fafde..21ede171e04202 100644
--- a/selfdrive/car/gm/values.py
+++ b/selfdrive/car/gm/values.py
@@ -14,7 +14,6 @@ class CarControllerParams:
STEER_STEP = 2 # Control frames per command (50hz)
STEER_DELTA_UP = 7 # Delta rates require review due to observed EPS weakness
STEER_DELTA_DOWN = 17
- MIN_STEER_SPEED = 3. # m/s
STEER_DRIVER_ALLOWANCE = 50
STEER_DRIVER_MULTIPLIER = 4
STEER_DRIVER_FACTOR = 100
@@ -59,10 +58,8 @@ class CAR:
ACADIA = "GMC ACADIA DENALI 2018"
BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
-
-
-EV_CAR = {CAR.VOLT}
-STEER_THRESHOLD = 1.0
+ BOLT_EUV = "CHEVROLET BOLT EUV 2022"
+ SILVERADO = "CHEVROLET SILVERADO 1500 2020"
class Footnote(Enum):
@@ -87,6 +84,11 @@ class GMCarInfo(CarInfo):
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
+ CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210", footnotes=[], harness=Harness.gm),
+ CAR.SILVERADO: [
+ GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II", footnotes=[], harness=Harness.gm),
+ GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", footnotes=[], harness=Harness.gm),
+ ],
}
@@ -107,6 +109,7 @@ class AccState:
class CanBus:
POWERTRAIN = 0
OBSTACLE = 1
+ CAMERA = 2
CHASSIS = 2
SW_GMLAN = 3
LOOPBACK = 128
@@ -155,6 +158,21 @@ class CanBus:
{
309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8
}],
+ CAR.BOLT_EUV: [
+ {
+ 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
+ }],
+ CAR.SILVERADO: [
+ {
+ 190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
+ }],
}
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))
+
+EV_CAR = {CAR.VOLT, CAR.BOLT_EUV}
+
+# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
+CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO}
+
+STEER_THRESHOLD = 1.0
diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py
index a78189b697448c..a235ba6cda7306 100755
--- a/selfdrive/car/honda/interface.py
+++ b/selfdrive/car/honda/interface.py
@@ -4,7 +4,7 @@
from common.conversions import Conversions as CV
from common.numpy_fast import interp
from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS
-from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
+from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu
@@ -86,7 +86,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
eps_modified = True
if candidate == CAR.CIVIC:
- stop_and_go = True
ret.mass = CivicParams.MASS
ret.wheelbase = CivicParams.WHEELBASE
ret.centerToFront = CivicParams.CENTER_TO_FRONT
@@ -106,7 +105,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
tire_stiffness_factor = 1.
elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022):
- stop_and_go = True
ret.mass = CivicParams.MASS
ret.wheelbase = CivicParams.WHEELBASE
ret.centerToFront = CivicParams.CENTER_TO_FRONT
@@ -116,7 +114,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.ACCORD, CAR.ACCORDH):
- stop_and_go = True
ret.mass = 3279. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.83
ret.centerToFront = ret.wheelbase * 0.39
@@ -130,7 +127,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.ACURA_ILX:
- stop_and_go = False
ret.mass = 3095. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.37
@@ -140,7 +136,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.CRV, CAR.CRV_EU):
- stop_and_go = False
ret.mass = 3572. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.62
ret.centerToFront = ret.wheelbase * 0.41
@@ -151,7 +146,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_5G:
- stop_and_go = True
ret.mass = 3410. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.66
ret.centerToFront = ret.wheelbase * 0.41
@@ -169,7 +163,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_HYBRID:
- stop_and_go = True
ret.mass = 1667. + STD_CARGO_KG # mean of 4 models in kg
ret.wheelbase = 2.66
ret.centerToFront = ret.wheelbase * 0.41
@@ -180,7 +173,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.FIT:
- stop_and_go = False
ret.mass = 2644. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.53
ret.centerToFront = ret.wheelbase * 0.39
@@ -190,7 +182,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
elif candidate == CAR.FREED:
- stop_and_go = False
ret.mass = 3086. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.74
# the remaining parameters were copied from FIT
@@ -201,7 +192,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
elif candidate == CAR.HRV:
- stop_and_go = False
ret.mass = 3125 * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.61
ret.centerToFront = ret.wheelbase * 0.41
@@ -212,7 +202,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.ACURA_RDX:
- stop_and_go = False
ret.mass = 3935. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.68
ret.centerToFront = ret.wheelbase * 0.38
@@ -222,7 +211,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate == CAR.ACURA_RDX_3G:
- stop_and_go = True
ret.mass = 4068. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.75
ret.centerToFront = ret.wheelbase * 0.41
@@ -232,7 +220,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
tire_stiffness_factor = 0.677
elif candidate == CAR.ODYSSEY:
- stop_and_go = False
ret.mass = 4471. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 3.00
ret.centerToFront = ret.wheelbase * 0.41
@@ -242,7 +229,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
elif candidate == CAR.ODYSSEY_CHN:
- stop_and_go = False
ret.mass = 1849.2 + STD_CARGO_KG # mean of 4 models in kg
ret.wheelbase = 2.90
ret.centerToFront = ret.wheelbase * 0.41 # from CAR.ODYSSEY
@@ -252,7 +238,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
elif candidate in (CAR.PILOT, CAR.PASSPORT):
- stop_and_go = False
ret.mass = 4204. * CV.LB_TO_KG + STD_CARGO_KG # average weight
ret.wheelbase = 2.82
ret.centerToFront = ret.wheelbase * 0.428
@@ -262,7 +247,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.RIDGELINE:
- stop_and_go = False
ret.mass = 4515. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 3.18
ret.centerToFront = ret.wheelbase * 0.41
@@ -272,7 +256,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.INSIGHT:
- stop_and_go = True
ret.mass = 2987. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.7
ret.centerToFront = ret.wheelbase * 0.39
@@ -282,7 +265,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.HONDA_E:
- stop_and_go = True
ret.mass = 3338.8 * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.5
ret.centerToFront = ret.wheelbase * 0.5
@@ -311,7 +293,8 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
# min speed to enable ACC. if car can do stop and go, then set enabling speed
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc
- ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else 25.5 * CV.MPH_TO_MS
+ stop_and_go = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor
+ ret.minEnableSpeed = -1. if stop_and_go else 25.5 * CV.MPH_TO_MS
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
@@ -369,9 +352,6 @@ def _update(self, c):
if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001:
events.add(EventName.manualRestart)
- # handle button presses
- events.events.extend(create_button_enable_events(ret.buttonEvents, self.CP.pcmCruise))
-
ret.events = events.to_msg()
return ret
diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py
index 88e118cc3d5a1d..ad4f73c011d4b2 100644
--- a/selfdrive/car/honda/values.py
+++ b/selfdrive/car/honda/values.py
@@ -113,7 +113,7 @@ class HondaCarInfo(CarInfo):
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
- CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec),
+ CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a),
HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a),
@@ -131,7 +131,7 @@ class HondaCarInfo(CarInfo):
CAR.FIT: HondaCarInfo("Honda Fit 2018-20", harness=Harness.nidec),
CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec),
CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", harness=Harness.nidec),
- CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-22", min_steer_speed=0., harness=Harness.nidec),
+ CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", min_steer_speed=0., harness=Harness.nidec),
CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey
CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec),
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py
index 4d181e3821b012..971330a33516ce 100644
--- a/selfdrive/car/hyundai/carcontroller.py
+++ b/selfdrive/car/hyundai/carcontroller.py
@@ -4,8 +4,8 @@
from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits
-from selfdrive.car.hyundai import hda2can, hyundaican
-from selfdrive.car.hyundai.values import Buttons, CarControllerParams, HDA2_CAR, CAR
+from selfdrive.car.hyundai import hyundaicanfd, hyundaican
+from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
@@ -69,24 +69,35 @@ def update(self, CC, CS):
can_sends = []
- if self.CP.carFingerprint in HDA2_CAR:
+ if self.CP.carFingerprint in CANFD_CAR:
# steering control
- can_sends.append(hda2can.create_lkas(self.packer, CC.enabled, CC.latActive, apply_steer))
+ can_sends.append(hyundaicanfd.create_lkas(self.packer, self.CP, CC.enabled, CC.latActive, apply_steer))
- if self.frame % 5 == 0:
- can_sends.append(hda2can.create_cam_0x2a4(self.packer, CS.cam_0x2a4))
+ # block LFA on HDA2
+ if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.CANFD_HDA2):
+ can_sends.append(hyundaicanfd.create_cam_0x2a4(self.packer, CS.cam_0x2a4))
- # cruise cancel
+ # LFA and HDA icons
+ if self.frame % 2 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_HDA2):
+ can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, CC.enabled))
+
+ # button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
+ # cruise cancel
if CC.cruiseControl.cancel:
- for _ in range(20):
- can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL))
- self.last_button_frame = self.frame
+ if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
+ can_sends.append(hyundaicanfd.create_cruise_info(self.packer, CS.cruise_info_copy, True))
+ self.last_button_frame = self.frame
+ else:
+ for _ in range(20):
+ can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL))
+ self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
- can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL))
- self.last_button_frame = self.frame
+ if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
+ can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL))
+ self.last_button_frame = self.frame
else:
# tester present - w/ no response (keeps radar disabled)
diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py
index f1428555a88f52..eab6e73f1f429b 100644
--- a/selfdrive/car/hyundai/carstate.py
+++ b/selfdrive/car/hyundai/carstate.py
@@ -5,7 +5,7 @@
from common.conversions import Conversions as CV
from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
-from selfdrive.car.hyundai.values import DBC, FEATURES, CAMERA_SCC_CAR, HDA2_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
+from selfdrive.car.hyundai.values import HyundaiFlags, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from selfdrive.car.interfaces import CarStateBase
PREV_BUTTON_SAMPLES = 8
@@ -19,8 +19,8 @@ def __init__(self, CP):
self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
- if CP.carFingerprint in HDA2_CAR:
- self.shifter_values = can_define.dv["ACCELERATOR"]["GEAR"]
+ if CP.carFingerprint in CANFD_CAR:
+ self.shifter_values = can_define.dv["GEAR_SHIFTER"]["GEAR"]
elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in FEATURES["use_tcu_gears"]:
@@ -35,8 +35,8 @@ def __init__(self, CP):
self.params = CarControllerParams(CP)
def update(self, cp, cp_cam):
- if self.CP.carFingerprint in HDA2_CAR:
- return self.update_hda2(cp, cp_cam)
+ if self.CP.carFingerprint in CANFD_CAR:
+ return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message()
@@ -133,17 +133,20 @@ def update(self, cp, cp_cam):
return ret
- def update_hda2(self, cp, cp_cam):
+ def update_canfd(self, cp, cp_cam):
ret = car.CarState.new_message()
- ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255.
- ret.gasPressed = ret.gas > 1e-3
+ if self.CP.flags & HyundaiFlags.CANFD_HDA2:
+ ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255.
+ else:
+ ret.gas = cp.vl["ACCELERATOR_ALT"]["ACCELERATOR_PEDAL"] / 1023.
+ ret.gasPressed = ret.gas > 1e-5
ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0
- gear = cp.vl["ACCELERATOR"]["GEAR"]
+ gear = cp.vl["GEAR_SHIFTER"]["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
# TODO: figure out positions
@@ -168,23 +171,26 @@ def update_hda2(self, cp, cp_cam):
ret.cruiseState.available = True
ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1
- ret.cruiseState.standstill = cp.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1
-
+ cp_cruise_info = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS
- ret.cruiseState.speed = cp.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor
+ ret.cruiseState.speed = cp_cruise_info.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor
+ ret.cruiseState.standstill = cp_cruise_info.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1
- self.cruise_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["CRUISE_BUTTONS"])
- self.main_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["ADAPTIVE_CRUISE_MAIN_BTN"])
- self.buttons_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"]
+ cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
+ self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"])
+ self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"])
+ self.buttons_counter = cp.vl[cruise_btn_msg]["COUNTER"]
+ self.cruise_info_copy = copy.copy(cp_cruise_info.vl["CRUISE_INFO"])
- self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"])
+ if self.CP.flags & HyundaiFlags.CANFD_HDA2:
+ self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"])
return ret
@staticmethod
def get_can_parser(CP):
- if CP.carFingerprint in HDA2_CAR:
- return CarState.get_can_parser_hda2(CP)
+ if CP.carFingerprint in CANFD_CAR:
+ return CarState.get_can_parser_canfd(CP)
signals = [
# signal_name, signal_address
@@ -318,10 +324,8 @@ def get_can_parser(CP):
@staticmethod
def get_cam_can_parser(CP):
- if CP.carFingerprint in HDA2_CAR:
- signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)]
- checks = [("CAM_0x2a4", 20)]
- return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6)
+ if CP.carFingerprint in CANFD_CAR:
+ return CarState.get_cam_can_parser_canfd(CP)
signals = [
# signal_name, signal_address
@@ -373,15 +377,16 @@ def get_cam_can_parser(CP):
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2)
@staticmethod
- def get_can_parser_hda2(CP):
+ def get_can_parser_canfd(CP):
+
+ cruise_btn_msg = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
signals = [
("WHEEL_SPEED_1", "WHEEL_SPEEDS"),
("WHEEL_SPEED_2", "WHEEL_SPEEDS"),
("WHEEL_SPEED_3", "WHEEL_SPEEDS"),
("WHEEL_SPEED_4", "WHEEL_SPEEDS"),
- ("ACCELERATOR_PEDAL", "ACCELERATOR"),
- ("GEAR", "ACCELERATOR"),
+ ("GEAR", "GEAR_SHIFTER"),
("BRAKE_PRESSED", "BRAKE"),
("STEERING_RATE", "STEERING_SENSORS"),
@@ -390,11 +395,9 @@ def get_can_parser_hda2(CP):
("STEERING_OUT_TORQUE", "MDPS"),
("CRUISE_ACTIVE", "SCC1"),
- ("SET_SPEED", "CRUISE_INFO"),
- ("CRUISE_STANDSTILL", "CRUISE_INFO"),
- ("COUNTER", "CRUISE_BUTTONS"),
- ("CRUISE_BUTTONS", "CRUISE_BUTTONS"),
- ("ADAPTIVE_CRUISE_MAIN_BTN", "CRUISE_BUTTONS"),
+ ("COUNTER", cruise_btn_msg),
+ ("CRUISE_BUTTONS", cruise_btn_msg),
+ ("ADAPTIVE_CRUISE_MAIN_BTN", cruise_btn_msg),
("DISTANCE_UNIT", "CLUSTER_INFO"),
@@ -407,16 +410,64 @@ def get_can_parser_hda2(CP):
checks = [
("WHEEL_SPEEDS", 100),
- ("ACCELERATOR", 100),
+ ("GEAR_SHIFTER", 100),
("BRAKE", 100),
("STEERING_SENSORS", 100),
("MDPS", 100),
("SCC1", 50),
- ("CRUISE_INFO", 50),
- ("CRUISE_BUTTONS", 50),
+ (cruise_btn_msg, 50),
("CLUSTER_INFO", 4),
("BLINKERS", 4),
("DOORS_SEATBELTS", 4),
]
- return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 5)
+ if CP.flags & HyundaiFlags.CANFD_HDA2:
+ signals += [
+ ("ACCELERATOR_PEDAL", "ACCELERATOR"),
+ ("GEAR", "ACCELERATOR"),
+ ("SET_SPEED", "CRUISE_INFO"),
+ ("CRUISE_STANDSTILL", "CRUISE_INFO"),
+ ]
+ checks += [
+ ("CRUISE_INFO", 50),
+ ("ACCELERATOR", 100),
+ ]
+ else:
+ signals += [
+ ("ACCELERATOR_PEDAL", "ACCELERATOR_ALT"),
+ ]
+ checks += [
+ ("ACCELERATOR_ALT", 100),
+ ]
+
+ bus = 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus)
+
+ @staticmethod
+ def get_cam_can_parser_canfd(CP):
+ if CP.flags & HyundaiFlags.CANFD_HDA2:
+ signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)]
+ checks = [("CAM_0x2a4", 20)]
+ else:
+ signals = [
+ ("COUNTER", "CRUISE_INFO"),
+ ("NEW_SIGNAL_1", "CRUISE_INFO"),
+ ("CRUISE_MAIN", "CRUISE_INFO"),
+ ("CRUISE_STATUS", "CRUISE_INFO"),
+ ("CRUISE_INACTIVE", "CRUISE_INFO"),
+ ("NEW_SIGNAL_2", "CRUISE_INFO"),
+ ("CRUISE_STANDSTILL", "CRUISE_INFO"),
+ ("NEW_SIGNAL_3", "CRUISE_INFO"),
+ ("BYTE11", "CRUISE_INFO"),
+ ("SET_SPEED", "CRUISE_INFO"),
+ ("NEW_SIGNAL_4", "CRUISE_INFO"),
+ ]
+
+ signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(3, 7)]
+ signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(13, 31)]
+
+ checks = [
+ ("CRUISE_INFO", 50),
+ ]
+
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6)
diff --git a/selfdrive/car/hyundai/hda2can.py b/selfdrive/car/hyundai/hda2can.py
deleted file mode 100644
index 36207aa113280d..00000000000000
--- a/selfdrive/car/hyundai/hda2can.py
+++ /dev/null
@@ -1,27 +0,0 @@
-def create_lkas(packer, enabled, lat_active, apply_steer):
- values = {
- "LKA_MODE": 2,
- "LKA_ICON": 2 if enabled else 1,
- "TORQUE_REQUEST": apply_steer,
- "LKA_ASSIST": 0,
- "STEER_REQ": 1 if lat_active else 0,
- "STEER_MODE": 0,
- "SET_ME_1": 0,
- "NEW_SIGNAL_1": 0,
- "NEW_SIGNAL_2": 0,
- }
- return packer.make_can_msg("LKAS", 4, values)
-
-def create_cam_0x2a4(packer, camera_values):
- camera_values.update({
- "BYTE7": 0,
- })
- return packer.make_can_msg("CAM_0x2a4", 4, camera_values)
-
-def create_buttons(packer, cnt, btn):
- values = {
- "COUNTER": cnt,
- "SET_ME_1": 1,
- "CRUISE_BUTTONS": btn,
- }
- return packer.make_can_msg("CRUISE_BUTTONS", 5, values)
diff --git a/selfdrive/car/hyundai/hyundaicanfd.py b/selfdrive/car/hyundai/hyundaicanfd.py
new file mode 100644
index 00000000000000..a53be7627db2d8
--- /dev/null
+++ b/selfdrive/car/hyundai/hyundaicanfd.py
@@ -0,0 +1,46 @@
+from selfdrive.car.hyundai.values import HyundaiFlags
+
+
+def create_lkas(packer, CP, enabled, lat_active, apply_steer):
+ values = {
+ "LKA_MODE": 2,
+ "LKA_ICON": 2 if enabled else 1,
+ "TORQUE_REQUEST": apply_steer,
+ "LKA_ASSIST": 0,
+ "STEER_REQ": 1 if lat_active else 0,
+ "STEER_MODE": 0,
+ "SET_ME_1": 0,
+ "NEW_SIGNAL_1": 0,
+ "NEW_SIGNAL_2": 0,
+ }
+
+ msg = "LKAS" if CP.flags & HyundaiFlags.CANFD_HDA2 else "LFA"
+ return packer.make_can_msg(msg, 4, values)
+
+def create_cam_0x2a4(packer, camera_values):
+ camera_values.update({
+ "BYTE7": 0,
+ })
+ return packer.make_can_msg("CAM_0x2a4", 4, camera_values)
+
+def create_buttons(packer, cnt, btn):
+ values = {
+ "COUNTER": cnt,
+ "SET_ME_1": 1,
+ "CRUISE_BUTTONS": btn,
+ }
+ return packer.make_can_msg("CRUISE_BUTTONS", 5, values)
+
+def create_cruise_info(packer, cruise_info_copy, cancel):
+ values = cruise_info_copy
+ if cancel:
+ values["CRUISE_STATUS"] = 0
+ values["CRUISE_INACTIVE"] = 1
+ return packer.make_can_msg("CRUISE_INFO", 4, values)
+
+def create_lfahda_cluster(packer, enabled):
+ values = {
+ "HDA_ICON": 1 if enabled else 0,
+ "LFA_ICON": 2 if enabled else 0,
+ }
+ return packer.make_can_msg("LFAHDA_CLUSTER", 4, values)
diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py
index 892f62c4279873..fbf2334980ef44 100644
--- a/selfdrive/car/hyundai/interface.py
+++ b/selfdrive/car/hyundai/interface.py
@@ -2,9 +2,9 @@
from cereal import car
from panda import Panda
from common.conversions import Conversions as CV
-from selfdrive.car.hyundai.values import CAR, DBC, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams
+from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams
from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
-from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
+from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu
@@ -25,7 +25,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "hyundai"
- ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)]
ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
# WARNING: disabling radar also disables AEB (and we show the same warning on the instrument cluster as if you manually disabled AEB)
@@ -116,13 +115,11 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.indi.actuatorEffectivenessV = [2.3]
ret.minSteerSpeed = 60 * CV.KPH_TO_MS
elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
- ret.lateralTuning.pid.kf = 0.00005
ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743.}.get(candidate, 1275.) + STD_CARGO_KG
ret.wheelbase = 2.6
ret.steerRatio = 13.42 # Spec
tire_stiffness_factor = 0.385
- ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
- ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022):
ret.lateralTuning.pid.kf = 0.00006
ret.mass = 1490. + STD_CARGO_KG # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx
@@ -163,6 +160,12 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
tire_stiffness_factor = 0.385
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
+ elif candidate == CAR.TUCSON_HYBRID_4TH_GEN:
+ ret.mass = 1680. + STD_CARGO_KG # average of all 3 trims
+ ret.wheelbase = 2.756
+ ret.steerRatio = 16.
+ tire_stiffness_factor = 0.385
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Kia
elif candidate == CAR.KIA_SORENTO:
@@ -172,7 +175,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
- elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021):
+ elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021):
ret.lateralTuning.pid.kf = 0.00006
ret.mass = 1737. + STD_CARGO_KG
ret.wheelbase = 2.7
@@ -180,7 +183,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
tire_stiffness_factor = 0.385
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
- if candidate == CAR.KIA_NIRO_HEV:
+ if candidate == CAR.KIA_NIRO_PHEV:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.KIA_SELTOS:
ret.mass = 1337. + STD_CARGO_KG
@@ -238,8 +241,12 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.mass = 2055 + STD_CARGO_KG
ret.wheelbase = 2.9
ret.steerRatio = 16.
- ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput),
- get_safety_config(car.CarParams.SafetyModel.hyundaiHDA2)]
+ tire_stiffness_factor = 0.65
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
+ elif candidate == CAR.IONIQ_5:
+ ret.mass = 2012 + STD_CARGO_KG
+ ret.wheelbase = 3.0
+ ret.steerRatio = 16.
tire_stiffness_factor = 0.65
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@@ -279,15 +286,39 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.01]]
- # these cars require a special panda safety mode due to missing counters and checksums in the messages
- if candidate in LEGACY_SAFETY_MODE_CAR:
- ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
-
- # set appropriate safety param for gas signal
- if candidate in HYBRID_CAR:
- ret.safetyConfigs[0].safetyParam = 2
- elif candidate in EV_CAR:
- ret.safetyConfigs[0].safetyParam = 1
+ # panda safety config
+ if candidate in CANFD_CAR:
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput),
+ get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd)]
+
+ # detect HDA2 with LKAS message
+ if 0x50 in fingerprint[6]:
+ ret.flags |= HyundaiFlags.CANFD_HDA2.value
+ ret.safetyConfigs[1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
+ else:
+ # non-HDA2
+ if 0x1cf not in fingerprint[4]:
+ ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
+ else:
+ ret.enableBsm = 0x58b in fingerprint[0]
+
+ if candidate in LEGACY_SAFETY_MODE_CAR:
+ # these cars require a special panda safety mode due to missing counters and checksums in the messages
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
+ else:
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)]
+
+ # set appropriate safety param for gas signal
+ if candidate in HYBRID_CAR:
+ ret.safetyConfigs[0].safetyParam = 2
+ elif candidate in EV_CAR:
+ ret.safetyConfigs[0].safetyParam = 1
+
+ if ret.openpilotLongitudinalControl:
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LONG
+
+ if candidate in CAMERA_SCC_CAR:
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
ret.centerToFront = ret.wheelbase * 0.4
@@ -300,14 +331,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor)
- ret.enableBsm = 0x58b in fingerprint[0]
-
- if ret.openpilotLongitudinalControl:
- ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LONG
-
- if candidate in CAMERA_SCC_CAR:
- ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
-
return ret
@staticmethod
@@ -318,6 +341,14 @@ def init(CP, logcan, sendcan):
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam)
+ if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons:
+ buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)]
+ # Handle CF_Clu_CruiseSwState changing buttons mid-press
+ if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0:
+ buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT))
+
+ ret.buttonEvents = buttonEvents
+
# On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state
# To avoid re-engaging when openpilot cancels, check user engagement intention via buttons
# Main button also can trigger an engagement on these cars
@@ -327,15 +358,6 @@ def _update(self, c):
if self.CS.brake_error:
events.add(EventName.brakeUnavailable)
- if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons:
- buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)]
- # Handle CF_Clu_CruiseSwState changing buttons mid-press
- if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0:
- buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT))
-
- ret.buttonEvents = buttonEvents
- events.events.extend(create_button_enable_events(ret.buttonEvents))
-
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.:
self.low_speed_alert = True
diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py
index ec38123fa1a03b..aa18235223a6eb 100644
--- a/selfdrive/car/hyundai/values.py
+++ b/selfdrive/car/hyundai/values.py
@@ -1,3 +1,4 @@
+from enum import IntFlag
from dataclasses import dataclass
from typing import Dict, List, Optional, Union
@@ -20,7 +21,7 @@ def __init__(self, CP):
self.STEER_DRIVER_FACTOR = 1
self.STEER_THRESHOLD = 150
- if CP.carFingerprint in HDA2_CAR:
+ if CP.carFingerprint in CANFD_CAR:
self.STEER_MAX = 270
self.STEER_DRIVER_ALLOWANCE = 250
self.STEER_DRIVER_MULTIPLIER = 2
@@ -29,7 +30,7 @@ def __init__(self, CP):
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ,
- CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_HEV,
+ CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER):
self.STEER_MAX = 255
@@ -38,6 +39,11 @@ def __init__(self, CP):
self.STEER_MAX = 384
+class HyundaiFlags(IntFlag):
+ CANFD_HDA2 = 1
+ CANFD_ALT_BUTTONS = 2
+
+
class CAR:
# Hyundai
ELANTRA = "HYUNDAI ELANTRA 2017"
@@ -65,12 +71,14 @@ class CAR:
PALISADE = "HYUNDAI PALISADE 2020"
VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
+ IONIQ_5 = "HYUNDAI IONIQ 5 2022"
+ TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN"
# Kia
KIA_FORTE = "KIA FORTE E 2018 & GT 2021"
KIA_K5_2021 = "KIA K5 2021"
KIA_NIRO_EV = "KIA NIRO EV 2020"
- KIA_NIRO_HEV = "KIA NIRO HYBRID 2019"
+ KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019"
KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021"
KIA_OPTIMA = "KIA OPTIMA SX 2019 & 2016"
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
@@ -111,7 +119,7 @@ class HyundaiCarInfo(CarInfo):
CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", "Smart Cruise Control (SCC)", harness=Harness.hyundai_o),
CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", harness=Harness.hyundai_i),
CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", harness=Harness.hyundai_d),
- CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", harness=Harness.hyundai_l),
+ CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", video_link="https://youtu.be/VnHzSTygTS4", harness=Harness.hyundai_l),
CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022", "All", harness=Harness.hyundai_l),
CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", harness=Harness.hyundai_l),
CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-22", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a),
@@ -126,6 +134,8 @@ class HyundaiCarInfo(CarInfo):
],
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", "Smart Cruise Control (SCC)", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a),
+ CAR.IONIQ_5: HyundaiCarInfo("Hyundai Ioniq 5 2022", "Highway Driving Assist II", harness=Harness.hyundai_q),
+ CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n),
# Kia
CAR.KIA_FORTE: [
@@ -139,7 +149,7 @@ class HyundaiCarInfo(CarInfo):
HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c),
HyundaiCarInfo("Kia Niro Electric 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h),
],
- CAR.KIA_NIRO_HEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c),
+ CAR.KIA_NIRO_PHEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c),
CAR.KIA_NIRO_HEV_2021: [
HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify
HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h),
@@ -156,7 +166,7 @@ class HyundaiCarInfo(CarInfo):
],
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c),
CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e),
- CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "All", harness=Harness.hyundai_p),
+ CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "Highway Driving Assist II", harness=Harness.hyundai_p),
# Genesis
CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f),
@@ -611,6 +621,7 @@ class Buttons:
b'\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ',
b'\xf1\x8799110S1500\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ',
b'\xf1\x8799110S1500\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ',
+ b'\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ',
],
(Ecu.esp, 0x7d1, None): [
b'\xf1\x00TM ESC \x02 101 \x08\x04 58910-S2GA0',
@@ -619,6 +630,7 @@ class Buttons:
b'\xf1\x8758910-S2GA0\xf1\x00TM ESC \x02 101 \x08\x04 58910-S2GA0',
b'\xf1\x8758910-S1DA0\xf1\x00TM ESC \x1e 102 \x08\x08 58910-S1DA0',
b'\xf1\x8758910-S2GA0\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
+ b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x82TACVN5GMI3XXXH0A',
@@ -627,6 +639,7 @@ class Buttons:
b'\xf1\x82TMCFD5MMCXXXXG0A',
b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82TMDWN5TMD3TXXJ1A',
b'\xf1\x81HM6M2_0a0_G00',
+ b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M1_0a0_J10',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00TM MDPS C 1.00 1.02 56370-S2AA0 0B19',
@@ -646,6 +659,8 @@ class Buttons:
b'\xf1\x87KMMYBU034207SB72x\x89\x88\x98h\x88\x98\x89\x87fhvvfWf33_\xff\x87\xff\x8f\xfa\x81\xe5\xf1\x89HT6TAF00A1\xf1\x82STM0M25GS1\x00\x00\x00\x00\x00\x00',
b'\xf1\x87954A02N250\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 VTMPT25XXX730NS2\xa6',
b'\xf1\x00HT6TA290BLHT6TAF00A1STM0M25GS1\x00\x00\x00\x00\x00\x006\xd8\x97\x15',
+ b'\xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc',
+ b'\xf1\x87954A02N250\x00\x00\x00\x00\x00\xf1\x81T02900A1 \xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc',
],
},
CAR.SANTA_FE_HEV_2022: {
@@ -688,6 +703,7 @@ class Buttons:
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.03 96400-J5100 ',
+ b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x81606DE051\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -705,6 +721,7 @@ class Buttons:
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504',
+ b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x87VCJLE17622572DK0vd6D\x99\x98y\x97vwVffUfvfC%CuT&Dx\x87o\xff{\x1c\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0',
@@ -714,6 +731,7 @@ class Buttons:
b'\xf1\x87VDHLG17118862DK2\x8awWwgu\x96wVfUVwv\x97xWvfvUTGTx\x87o\xff\xc9\xed\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0',
b'\xf1\x87VDKLJ18675252DK6\x89vhgwwwwveVU\x88w\x87w\x99vgf\x97vXfgw_\xff\xc2\xfb\xf1\x89E25\x00\x00\x00\x00\x00\x00\x00\xf1\x82TCK0T33NB2',
b'\xf1\x87WAJTE17552812CH4vfFffvfVeT5DwvvVVdFeegeg\x88\x88o\xff\x1a]\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00TCK2T20NB1\x19\xd2\x00\x94',
+ b'\xf1\x87VDHLG17274082DK2wfFf\x89x\x98wUT5T\x88v\x97xgeGefTGTVvO\xff\x1c\x14\xf1\x81E19\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E19\x00\x00\x00\x00\x00\x00\x00SCK0T33UB2\xee[\x97S',
],
},
CAR.PALISADE: {
@@ -808,6 +826,7 @@ class Buttons:
b"\xf1\x87LBLUFN622950KF36\xa8\x88\x88\x88\x87w\x87xh\x99\x96\x89\x88\x99\x98\x89\x88\x99\x98\x89\x87o\xf6\xff\x98\x88o\xffx'\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB3\xd1\xc3\xf8\xa8",
b'\xf1\x87LDMVBN950669KF37\x97www\x96fffy\x99\xa7\x99\xa9\x99\xaa\x99g\x88\x96x\xb8\x8f\xf9\xffTD/\xff\xa7\xcb\xf1\x81U922\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX4G38NB5\xb9\x94\xe8\x89',
b'\xf1\x87LDLVAA4478824HH1\x87wwwvfvg\x89\x99\xa8\x99w\x88\x87x\x89\x99\xa8\x99\xa6o\xfa\xfffU/\xffu\x92\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00TON4G38NB2[v\\\xb6',
+ b'\xf1\x87LDMVBN871852KF37\xb9\x99\x99\x99\xa8\x88\x88\x88y\x99\xa7\x99x\x99\xa7\x89\x88\x88\x98\x88\x89o\xf7\xff\xaa\x88o\xff\x0e\xed\xf1\x81U922\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX4G38NB5\xb9\x94\xe8\x89',
],
},
CAR.VELOSTER: {
@@ -1008,6 +1027,7 @@ class Buttons:
b'\xf1\x00DEev SCC F-CUP 1.00 1.02 96400-Q4000 ',
b'\xf1\x00DEev SCC F-CUP 1.00 1.02 96400-Q4100 ',
b'\xf1\x00DEev SCC F-CUP 1.00 1.03 96400-Q4100 ',
+ b'\xf1\x00DEev SCC FHCUP 1.00 1.03 96400-Q4000 ',
b'\xf1\x8799110Q4000\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ',
b'\xf1\x8799110Q4100\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4100 ',
b'\xf1\x8799110Q4500\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4500 ',
@@ -1027,9 +1047,10 @@ class Buttons:
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821',
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.01 99211-Q4500 210428',
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.03 95740-Q4000 180821',
+ b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821',
],
},
- CAR.KIA_NIRO_HEV: {
+ CAR.KIA_NIRO_PHEV: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -1135,11 +1156,13 @@ class Buttons:
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-AB000 200819',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA000 200819',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205',
+ b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.06 99210-AA000 220111',
],
(Ecu.esp, 0x7d1, None): [
b'\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800',
b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 104 \x08\x03 58910-AA800',
b'\xf1\x8758910-AB800\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800',
+ b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00HT6WA280BLHT6VA640A1CCN0N20NS5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -1154,7 +1177,7 @@ class Buttons:
b'\xf1\x82CNCWD0AMFCXCSFFA',
b'\xf1\x82CNCWD0AMFCXCSFFB',
b'\xf1\x82CNCVD0AMFCXCSFFB',
- b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82CNDWD0AMFCXCSG8A',
+ b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M2_0a0_G80',
],
},
CAR.ELANTRA_HEV_2021: {
@@ -1263,6 +1286,38 @@ class Buttons:
b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.05 99210-CV000 211027',
],
},
+ CAR.IONIQ_5: {
+ (Ecu.esp, 0x7d1, None): [
+ b'\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010',
+ b'\xf1\x8758520GI010\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010',
+ ],
+ (Ecu.eps, 0x7d4, None): [
+ b'\xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106',
+ b'\xf1\x8757700GI000 \xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106',
+ ],
+ (Ecu.fwdRadar, 0x7d0, None): [
+ b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
+ b'\xf1\x8799110GI000\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
+ ],
+ (Ecu.fwdCamera, 0x7c4, None): [
+ b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206',
+ ],
+ },
+ CAR.TUCSON_HYBRID_4TH_GEN: {
+ (Ecu.fwdCamera, 0x7c4, None): [
+ b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q',
+ ],
+ (Ecu.eps, 0x7d4, None): [
+ b'\xf1\x00NX4 MDPS C 1.00 1.01 56300-P0100 2228',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x87391312MND0',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa',
+ b'\xf1\x8795441-3D220\x00\xf1\x81G19_Rev\x00\x00\x00\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa',
+ ],
+ },
}
CHECKSUM = {
@@ -1274,18 +1329,18 @@ class Buttons:
# which message has the gear
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
- "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022},
+ "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
"use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022},
}
-HDA2_CAR = {CAR.KIA_EV6, }
+CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN}
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
-HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal
+HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
@@ -1312,7 +1367,7 @@ class Buttons:
CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
- CAR.KIA_NIRO_HEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
+ CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None),
@@ -1333,6 +1388,8 @@ class Buttons:
CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None),
- CAR.KIA_EV6: dbc_dict('kia_ev6', None),
+ CAR.KIA_EV6: dbc_dict('hyundai_canfd', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
+ CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
+ CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
}
diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py
index 1e55e72ac4bec8..9da6aabd67b7cf 100644
--- a/selfdrive/car/interfaces.py
+++ b/selfdrive/car/interfaces.py
@@ -9,7 +9,7 @@
from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D
from common.realtime import DT_CTRL
-from selfdrive.car import gen_empty_fingerprint
+from selfdrive.car import create_button_enable_events, gen_empty_fingerprint
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel
@@ -213,6 +213,9 @@ def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_
if cs_out.accFaulted:
events.add(EventName.accFaulted)
+ # Handle button presses
+ events.events.extend(create_button_enable_events(cs_out.buttonEvents, pcm_cruise=self.CP.pcmCruise))
+
# Handle permanent and temporary steering faults
self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1
if cs_out.steerFaultTemporary:
diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py
index bb96572c33e9a2..65122ab8971b9b 100644
--- a/selfdrive/car/isotp_parallel_query.py
+++ b/selfdrive/car/isotp_parallel_query.py
@@ -135,6 +135,7 @@ def get_data(self, timeout, total_timeout=60.):
if self.debug:
cloudlog.warning(f"iso-tp query response pending: {tx_addr}")
else:
+ response_timeouts[tx_addr] = 0
request_done[tx_addr] = True
cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}")
diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py
index 7db401e1617779..ed246f15da54db 100644
--- a/selfdrive/car/mazda/values.py
+++ b/selfdrive/car/mazda/values.py
@@ -40,7 +40,7 @@ class MazdaCarInfo(CarInfo):
CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"),
CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"),
CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"),
- CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22"),
+ CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22", video_link="https://youtu.be/dA3duO4a0O4"),
CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"),
}
@@ -67,6 +67,7 @@ class Buttons:
b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH54-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PXFG-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -80,6 +81,7 @@ class Buttons:
(Ecu.transmission, 0x7e1, None): [
b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PXFG-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.CX5: {
diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py
index de5e62cb7e91ab..7fc5456d98f92f 100644
--- a/selfdrive/car/subaru/carstate.py
+++ b/selfdrive/car/subaru/carstate.py
@@ -4,7 +4,7 @@
from common.conversions import Conversions as CV
from selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
-from selfdrive.car.subaru.values import DBC, STEER_THRESHOLD, CAR, GLOBAL_GEN2, PREGLOBAL_CARS
+from selfdrive.car.subaru.values import DBC, CAR, GLOBAL_GEN2, PREGLOBAL_CARS
class CarState(CarStateBase):
@@ -50,7 +50,9 @@ def update(self, cp, cp_cam, cp_body):
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
- ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD[self.car_fingerprint]
+
+ steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80
+ ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
ret.cruiseState.enabled = cp_cruise.vl["CruiseControl"]["Cruise_Activated"] != 0
diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py
index 049da10a16b184..a464734a30b9bb 100644
--- a/selfdrive/car/subaru/interface.py
+++ b/selfdrive/car/subaru/interface.py
@@ -71,7 +71,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
- elif candidate == CAR.OUTBACK:
+ elif candidate in (CAR.OUTBACK, CAR.LEGACY):
ret.mass = 1568. + STD_CARGO_KG
ret.wheelbase = 2.67
ret.centerToFront = ret.wheelbase * 0.5
diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py
index 9465f75edf807d..7d8365a11f3435 100644
--- a/selfdrive/car/subaru/values.py
+++ b/selfdrive/car/subaru/values.py
@@ -35,6 +35,7 @@ class CAR:
IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
FORESTER = "SUBARU FORESTER 2019"
OUTBACK = "SUBARU OUTBACK 6TH GEN"
+ LEGACY = "SUBARU LEGACY 7TH GEN"
# Pre-global
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
@@ -46,12 +47,13 @@ class CAR:
@dataclass
class SubaruCarInfo(CarInfo):
package: str = "EyeSight Driver Assistance"
- harness: Enum = Harness.subaru
+ harness: Enum = Harness.subaru_a
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"),
- CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", harness=Harness.none),
+ CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", harness=Harness.subaru_b),
+ CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", harness=Harness.subaru_b),
CAR.IMPREZA: [
SubaruCarInfo("Subaru Impreza 2017-19"),
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
@@ -99,7 +101,23 @@ class SubaruCarInfo(CarInfo):
b'\x00\xfe\xf7\x00\x00',
b'\001\xfe\xf9\000\000',
b'\x01\xfe\xf7\x00\x00',
- b'\xf1\x00\xa4\x10@',
+ ],
+ },
+ CAR.LEGACY: {
+ (Ecu.esp, 0x7b0, None): [
+ b'\xa1\\ x04\x01',
+ ],
+ (Ecu.eps, 0x746, None): [
+ b'\x9b\xc0\x11\x00',
+ ],
+ (Ecu.fwdCamera, 0x787, None): [
+ b'\x00\x00e\x80\x00\x1f@ \x19\x00',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'\xde\"a0\x07',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xa5\xf6\x05@\x00',
],
},
CAR.IMPREZA: {
@@ -116,6 +134,7 @@ class SubaruCarInfo(CarInfo):
b'z\x94\x08\x90\x00',
b'z\x84\x19\x90\x00',
b'\xf1\x00\xb2\x06\x04',
+ b'z\x94\x0c\x90\x00',
],
(Ecu.eps, 0x746, None): [
b'\x7a\xc0\x0c\x00',
@@ -139,6 +158,7 @@ class SubaruCarInfo(CarInfo):
b'\x00\x00c\xf4\x00\x00\x00\x00',
b'\x00\x00d\xdc\x00\x00\x00\x00',
b'\x00\x00dd\x00\x00\x00\x00',
+ b'\x00\x00c\xf4\x1f@ \x07',
],
(Ecu.engine, 0x7e0, None): [
b'\xaa\x61\x66\x73\x07',
@@ -148,7 +168,6 @@ class SubaruCarInfo(CarInfo):
b'\xaa!`u\a',
b'\xaa!dq\a',
b'\xaa!dt\a',
- b'\xf1\x00\xa2\x10\t',
b'\xc5!ar\a',
b'\xbe!as\a',
b'\xc5!ds\a',
@@ -158,6 +177,7 @@ class SubaruCarInfo(CarInfo):
b'\xaa\x00Bu\x07',
b'\xc5!dr\x07',
b'\xaa!aw\x07',
+ b'\xaa!av\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xe3\xe5\x46\x31\x00',
@@ -173,7 +193,6 @@ class SubaruCarInfo(CarInfo):
b'\xe4\xf5\002\000\000',
b'\xe3\xd0\x081\x00',
b'\xe3\xf5\x06\x00\x00',
- b'\xf1\x00\xa4\x10@',
],
},
CAR.IMPREZA_2020: {
@@ -202,7 +221,6 @@ class SubaruCarInfo(CarInfo):
b'\xca!`0\a',
b'\xcc\"f0\a',
b'\xcc!fp\a',
- b'\xf1\x00\xa2\x10\t',
b'\xca!f@\x07',
b'\xca!fp\x07',
],
@@ -247,7 +265,6 @@ class SubaruCarInfo(CarInfo):
b'\x1a\xf6F`\x00',
b'\032\xf6b`\000',
b'\x1a\xf6B`\x00',
- b'\xf1\x00\xa4\x10@',
b'\x1a\xf6b0\x00',
],
},
@@ -450,29 +467,18 @@ class SubaruCarInfo(CarInfo):
},
}
-STEER_THRESHOLD = {
- CAR.ASCENT: 80,
- CAR.IMPREZA: 80,
- CAR.IMPREZA_2020: 80,
- CAR.FORESTER: 80,
- CAR.OUTBACK: 80,
- CAR.FORESTER_PREGLOBAL: 75,
- CAR.LEGACY_PREGLOBAL: 75,
- CAR.OUTBACK_PREGLOBAL: 75,
- CAR.OUTBACK_PREGLOBAL_2018: 75,
-}
-
DBC = {
CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None),
CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None),
CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None),
+ CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None),
}
-GLOBAL_GEN2 = (CAR.OUTBACK, )
-PREGLOBAL_CARS = (CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018)
\ No newline at end of file
+GLOBAL_GEN2 = (CAR.OUTBACK, CAR.LEGACY)
+PREGLOBAL_CARS = (CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018)
diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py
index 7b6355d73f7137..b769cbe5f7086a 100644
--- a/selfdrive/car/tests/routes.py
+++ b/selfdrive/car/tests/routes.py
@@ -26,195 +26,201 @@
HYUNDAI.KIA_OPTIMA_H,
]
-TestRoute = namedtuple('TestRoute', ['route', 'car_model', 'segment'], defaults=(None,))
+CarTestRoute = namedtuple('CarTestRoute', ['route', 'car_model', 'segment'], defaults=(None,))
routes = [
- TestRoute("efdf9af95e71cd84|2022-05-13--19-03-31", COMMA.BODY),
-
- TestRoute("0c94aa1e1296d7c6|2021-05-05--19-48-37", CHRYSLER.JEEP_CHEROKEE),
- TestRoute("91dfedae61d7bd75|2021-05-22--20-07-52", CHRYSLER.JEEP_CHEROKEE_2019),
- TestRoute("420a8e183f1aed48|2020-03-05--07-15-29", CHRYSLER.PACIFICA_2017_HYBRID),
- TestRoute("43a685a66291579b|2021-05-27--19-47-29", CHRYSLER.PACIFICA_2018),
- TestRoute("378472f830ee7395|2021-05-28--07-38-43", CHRYSLER.PACIFICA_2018_HYBRID),
- TestRoute("8190c7275a24557b|2020-01-29--08-33-58", CHRYSLER.PACIFICA_2019_HYBRID),
- TestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.PACIFICA_2020),
- TestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500),
+ CarTestRoute("efdf9af95e71cd84|2022-05-13--19-03-31", COMMA.BODY),
+
+ CarTestRoute("0c94aa1e1296d7c6|2021-05-05--19-48-37", CHRYSLER.JEEP_CHEROKEE),
+ CarTestRoute("91dfedae61d7bd75|2021-05-22--20-07-52", CHRYSLER.JEEP_CHEROKEE_2019),
+ CarTestRoute("420a8e183f1aed48|2020-03-05--07-15-29", CHRYSLER.PACIFICA_2017_HYBRID),
+ CarTestRoute("43a685a66291579b|2021-05-27--19-47-29", CHRYSLER.PACIFICA_2018),
+ CarTestRoute("378472f830ee7395|2021-05-28--07-38-43", CHRYSLER.PACIFICA_2018_HYBRID),
+ CarTestRoute("8190c7275a24557b|2020-01-29--08-33-58", CHRYSLER.PACIFICA_2019_HYBRID),
+ CarTestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.PACIFICA_2020),
+ CarTestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500),
+ CarTestRoute("8fb5eabf914632ae|2022-08-04--17-28-53", CHRYSLER.RAM_HD, segment=6),
#TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION),
- TestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA),
- TestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
- TestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
- TestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
-
- TestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G),
- TestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC),
- TestRoute("a859a044a447c2b0|2020-03-03--18-42-45", HONDA.CRV_EU),
- TestRoute("68aac44ad69f838e|2021-05-18--20-40-52", HONDA.CRV),
- TestRoute("14fed2e5fa0aa1a5|2021-05-25--14-59-42", HONDA.CRV_HYBRID),
- TestRoute("52f3e9ae60c0d886|2021-05-23--15-59-43", HONDA.FIT),
- TestRoute("2c4292a5cd10536c|2021-08-19--21-32-15", HONDA.FREED),
- TestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HRV),
- TestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX),
- TestRoute("81722949a62ea724|2019-04-06--15-19-25", HONDA.ODYSSEY_CHN),
- TestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T
- TestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T
- TestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs
- TestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH),
- TestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs
- TestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G),
- TestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY),
- TestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL),
- TestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.INSIGHT),
- TestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.PILOT),
- TestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PASSPORT),
- TestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.CIVIC_BOSCH),
- TestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX),
- TestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE),
- TestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E),
- TestRoute("f44aa96ace22f34a|2021-12-22--06-22-31", HONDA.CIVIC_2022),
-
- TestRoute("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS),
- TestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70),
- TestRoute("6b301bf83f10aa90|2020-11-22--16-45-07", HYUNDAI.GENESIS_G80),
- TestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE),
- TestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022),
- TestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
- TestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022),
- TestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
- TestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA),
- TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
- TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
- TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
- TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
- TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
- TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
- TestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019),
- TestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV),
- TestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020),
- TestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD),
- TestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ),
- TestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022),
- TestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA),
- TestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV),
- TestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11),
- TestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV),
- TestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
- TestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER),
- TestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6),
- TestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
- TestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
- TestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_HEV),
- TestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021),
- TestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
- TestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA),
- TestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021),
- TestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021),
- TestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID),
- TestRoute("715ac05b594e9c59|2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
-
- TestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2),
- TestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2),
- TestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON),
- TestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019),
- TestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019),
- TestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2),
- TestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2),
- TestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY),
- TestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2),
- TestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2),
- TestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH),
- TestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA),
- TestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2),
- TestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLAH_TSS2),
- TestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.PRIUS),
- TestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.RAV4),
- TestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.RAV4H),
- TestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2),
- TestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022),
- TestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4H_TSS2),
- TestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022),
- TestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2),
- TestRoute("25057fa6a5a63dfb|2020-03-04--08-44-23", TOYOTA.LEXUS_CTH),
- TestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ESH_TSS2),
- TestRoute("37041c500fd30100|2020-12-30--12-17-24", TOYOTA.LEXUS_ESH),
- TestRoute("32696cea52831b02|2021-11-19--18-13-30", TOYOTA.LEXUS_RC),
- TestRoute("886fcd8408d570e9|2020-01-29--05-11-22", TOYOTA.LEXUS_RX),
- TestRoute("886fcd8408d570e9|2020-01-29--02-18-55", TOYOTA.LEXUS_RX),
- TestRoute("d27ad752e9b08d4f|2021-05-26--19-39-51", TOYOTA.LEXUS_RXH),
- TestRoute("01b22eb2ed121565|2020-02-02--11-25-51", TOYOTA.LEXUS_RX_TSS2),
- TestRoute("b74758c690a49668|2020-05-20--15-58-57", TOYOTA.LEXUS_RXH_TSS2),
- TestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH),
- TestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX),
- TestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2),
- TestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NXH_TSS2),
- TestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2),
- TestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2),
- TestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER),
- TestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH),
- TestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA),
- TestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS),
- TestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2),
- TestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI),
- TestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR),
- TestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH),
- TestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V),
-
- TestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1),
- TestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1),
- TestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7),
- TestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7),
- TestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.PASSAT_MK8),
- TestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.POLO_MK6),
- TestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.TAOS_MK1),
- TestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.TCROSS_MK1),
- TestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2),
- TestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2),
- TestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61),
- TestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1),
- TestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3),
- TestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1),
- TestRoute("0cd0b7f7e31a3853|2021-12-03--03-12-05", VOLKSWAGEN.AUDI_Q3_MK2),
- TestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1),
- TestRoute("fc6b6c9a3471c846|2021-05-27--13-39-56", VOLKSWAGEN.SEAT_LEON_MK3),
- TestRoute("12d6ae3057c04b0d|2021-09-15--00-04-07", VOLKSWAGEN.SKODA_KAMIQ_MK1),
- TestRoute("12d6ae3057c04b0d|2021-09-04--21-21-21", VOLKSWAGEN.SKODA_KAROQ_MK1),
- TestRoute("90434ff5d7c8d603|2021-03-15--12-07-31", VOLKSWAGEN.SKODA_KODIAQ_MK1),
- TestRoute("66e5edc3a16459c5|2021-05-25--19-00-29", VOLKSWAGEN.SKODA_OCTAVIA_MK3),
- TestRoute("026b6d18fba6417f|2021-03-26--09-17-04", VOLKSWAGEN.SKODA_SCALA_MK1),
- TestRoute("b2e9858e29db492b|2021-03-26--16-58-42", VOLKSWAGEN.SKODA_SUPERB_MK3),
-
- TestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.ASCENT),
- TestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER),
- TestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA),
- TestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020),
- TestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3),
+ CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA),
+ CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
+ CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
+ CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
+ CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV),
+ CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.SILVERADO),
+
+ CarTestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G),
+ CarTestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC),
+ CarTestRoute("a859a044a447c2b0|2020-03-03--18-42-45", HONDA.CRV_EU),
+ CarTestRoute("68aac44ad69f838e|2021-05-18--20-40-52", HONDA.CRV),
+ CarTestRoute("14fed2e5fa0aa1a5|2021-05-25--14-59-42", HONDA.CRV_HYBRID),
+ CarTestRoute("52f3e9ae60c0d886|2021-05-23--15-59-43", HONDA.FIT),
+ CarTestRoute("2c4292a5cd10536c|2021-08-19--21-32-15", HONDA.FREED),
+ CarTestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HRV),
+ CarTestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX),
+ CarTestRoute("81722949a62ea724|2019-04-06--15-19-25", HONDA.ODYSSEY_CHN),
+ CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T
+ CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T
+ CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs
+ CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH),
+ CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs
+ CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G),
+ CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY),
+ CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL),
+ CarTestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.INSIGHT),
+ CarTestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.PILOT),
+ CarTestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PASSPORT),
+ CarTestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.CIVIC_BOSCH),
+ CarTestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX),
+ CarTestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE),
+ CarTestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E),
+ CarTestRoute("f44aa96ace22f34a|2021-12-22--06-22-31", HONDA.CIVIC_2022),
+
+ CarTestRoute("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS),
+ CarTestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70),
+ CarTestRoute("6b301bf83f10aa90|2020-11-22--16-45-07", HYUNDAI.GENESIS_G80),
+ CarTestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE),
+ CarTestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022),
+ CarTestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
+ CarTestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022, segment=1),
+ CarTestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
+ CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA),
+ CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
+ CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
+ CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
+ CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
+ CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN),
+ CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
+ CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
+ CarTestRoute("22de8111a8c5463c|2022-07-29--13-34-49", HYUNDAI.IONIQ_5),
+ CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019),
+ CarTestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV),
+ CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020),
+ CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD),
+ CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ),
+ CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022),
+ CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA),
+ CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV),
+ CarTestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11),
+ CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV),
+ CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
+ CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER),
+ CarTestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6),
+ CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
+ CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
+ CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV),
+ CarTestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021),
+ CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
+ CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA),
+ CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021),
+ CarTestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021),
+ CarTestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID),
+ CarTestRoute("715ac05b594e9c59|2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
+
+ CarTestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2),
+ CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2),
+ CarTestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON),
+ CarTestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019),
+ CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019),
+ CarTestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2),
+ CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2),
+ CarTestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY),
+ CarTestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2),
+ CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2),
+ CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH),
+ CarTestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA),
+ CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2),
+ CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLAH_TSS2),
+ CarTestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.PRIUS),
+ CarTestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.RAV4),
+ CarTestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.RAV4H),
+ CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2),
+ CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022),
+ CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4H_TSS2),
+ CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022),
+ CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2),
+ CarTestRoute("da23c367491f53e2|2021-05-21--09-09-11", TOYOTA.LEXUS_CTH, segment=3),
+ CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ESH_TSS2),
+ CarTestRoute("37041c500fd30100|2020-12-30--12-17-24", TOYOTA.LEXUS_ESH),
+ CarTestRoute("32696cea52831b02|2021-11-19--18-13-30", TOYOTA.LEXUS_RC),
+ CarTestRoute("886fcd8408d570e9|2020-01-29--02-18-55", TOYOTA.LEXUS_RX),
+ CarTestRoute("d27ad752e9b08d4f|2021-05-26--19-39-51", TOYOTA.LEXUS_RXH),
+ CarTestRoute("01b22eb2ed121565|2020-02-02--11-25-51", TOYOTA.LEXUS_RX_TSS2),
+ CarTestRoute("b74758c690a49668|2020-05-20--15-58-57", TOYOTA.LEXUS_RXH_TSS2),
+ CarTestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH),
+ CarTestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX),
+ CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2),
+ CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NXH_TSS2),
+ CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2),
+ CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2),
+ CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER),
+ CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH),
+ CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA),
+ CarTestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS),
+ CarTestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2),
+ CarTestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI),
+ CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR),
+ CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH),
+ CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V),
+
+ CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1),
+ CarTestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1),
+ CarTestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7),
+ CarTestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7),
+ CarTestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.PASSAT_MK8),
+ CarTestRoute("3cfdec54aa035f3f|2022-07-19--23-45-10", VOLKSWAGEN.PASSAT_NMS),
+ CarTestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.POLO_MK6),
+ CarTestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.TAOS_MK1),
+ CarTestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.TCROSS_MK1),
+ CarTestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2),
+ CarTestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2),
+ CarTestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61),
+ CarTestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1),
+ CarTestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3),
+ CarTestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1),
+ CarTestRoute("0cd0b7f7e31a3853|2021-12-03--03-12-05", VOLKSWAGEN.AUDI_Q3_MK2),
+ CarTestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1),
+ CarTestRoute("fc6b6c9a3471c846|2021-05-27--13-39-56", VOLKSWAGEN.SEAT_LEON_MK3),
+ CarTestRoute("12d6ae3057c04b0d|2021-09-15--00-04-07", VOLKSWAGEN.SKODA_KAMIQ_MK1),
+ CarTestRoute("12d6ae3057c04b0d|2021-09-04--21-21-21", VOLKSWAGEN.SKODA_KAROQ_MK1),
+ CarTestRoute("90434ff5d7c8d603|2021-03-15--12-07-31", VOLKSWAGEN.SKODA_KODIAQ_MK1),
+ CarTestRoute("66e5edc3a16459c5|2021-05-25--19-00-29", VOLKSWAGEN.SKODA_OCTAVIA_MK3),
+ CarTestRoute("026b6d18fba6417f|2021-03-26--09-17-04", VOLKSWAGEN.SKODA_SCALA_MK1),
+ CarTestRoute("b2e9858e29db492b|2021-03-26--16-58-42", VOLKSWAGEN.SKODA_SUPERB_MK3),
+
+ CarTestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.ASCENT),
+ CarTestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER),
+ CarTestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA),
+ CarTestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020),
+ CarTestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3),
+ CarTestRoute("c56e69bbc74b8fad|2022-08-18--09-43-51", SUBARU.LEGACY, segment=3),
# Pre-global, dashcam
- TestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL),
- TestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL),
- TestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL),
- TestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018),
-
- TestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL),
- TestRoute("5b7c365c50084530|2020-03-25--22-10-13", NISSAN.LEAF),
- TestRoute("22c3dcce2dd627eb|2020-12-30--16-38-48", NISSAN.LEAF_IC),
- TestRoute("059ab9162e23198e|2020-05-30--09-41-01", NISSAN.ROGUE),
- TestRoute("b72d3ec617c0a90f|2020-12-11--15-38-17", NISSAN.ALTIMA),
-
- TestRoute("32a319f057902bb3|2020-04-27--15-18-58", MAZDA.CX5),
- TestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9),
- TestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3),
- TestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6),
- TestRoute("f6d5b1a9d7a1c92e|2021-07-08--06-56-59", MAZDA.CX9_2021),
- TestRoute("a4af1602d8e668ac|2022-02-03--12-17-07", MAZDA.CX5_2022),
-
- TestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS),
- TestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS),
+ CarTestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL),
+ CarTestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL),
+ CarTestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL),
+ CarTestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018),
+
+ CarTestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL),
+ CarTestRoute("5b7c365c50084530|2020-03-25--22-10-13", NISSAN.LEAF),
+ CarTestRoute("22c3dcce2dd627eb|2020-12-30--16-38-48", NISSAN.LEAF_IC),
+ CarTestRoute("059ab9162e23198e|2020-05-30--09-41-01", NISSAN.ROGUE),
+ CarTestRoute("b72d3ec617c0a90f|2020-12-11--15-38-17", NISSAN.ALTIMA),
+
+ CarTestRoute("32a319f057902bb3|2020-04-27--15-18-58", MAZDA.CX5),
+ CarTestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9),
+ CarTestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3),
+ CarTestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6),
+ CarTestRoute("f6d5b1a9d7a1c92e|2021-07-08--06-56-59", MAZDA.CX9_2021),
+ CarTestRoute("a4af1602d8e668ac|2022-02-03--12-17-07", MAZDA.CX5_2022),
+
+ CarTestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS),
+ CarTestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS),
# Segments that test specific issues
# Controls mismatch due to interceptor threshold
- TestRoute("cfb32f0fb91b173b|2022-04-06--14-54-45", HONDA.CIVIC, segment=21),
- TestRoute("5a8762b91fc70467|2022-04-14--21-26-20", TOYOTA.RAV4, segment=2),
+ CarTestRoute("cfb32f0fb91b173b|2022-04-06--14-54-45", HONDA.CIVIC, segment=21),
+ CarTestRoute("5a8762b91fc70467|2022-04-14--21-26-20", TOYOTA.RAV4, segment=2),
# Controls mismatch due to standstill threshold
- TestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.CRV_HYBRID, segment=22),
+ CarTestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.CRV_HYBRID, segment=22),
]
diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py
index 412874c813e661..aabf652c8ccfce 100755
--- a/selfdrive/car/tests/test_car_interfaces.py
+++ b/selfdrive/car/tests/test_car_interfaces.py
@@ -5,6 +5,7 @@
from parameterized import parameterized
from cereal import car
+from selfdrive.car import gen_empty_fingerprint
from selfdrive.car.fingerprints import all_known_cars
from selfdrive.car.car_helpers import interfaces
from selfdrive.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
@@ -19,11 +20,8 @@ def test_car_interfaces(self, car_name):
fingerprint = {}
CarInterface, CarController, CarState = interfaces[car_name]
- fingerprints = {
- 0: fingerprint,
- 1: fingerprint,
- 2: fingerprint,
- }
+ fingerprints = gen_empty_fingerprint()
+ fingerprints.update({k: fingerprint for k in fingerprints.keys()})
car_fw = []
diff --git a/selfdrive/car/tests/test_docs.py b/selfdrive/car/tests/test_docs.py
index e31ad0d5c1325c..af58bb5e5931fd 100755
--- a/selfdrive/car/tests/test_docs.py
+++ b/selfdrive/car/tests/test_docs.py
@@ -4,7 +4,7 @@
from selfdrive.car.car_helpers import interfaces, get_interface_attr
from selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info
-from selfdrive.car.docs_definitions import Column, Star
+from selfdrive.car.docs_definitions import Column, Harness, Star
from selfdrive.car.honda.values import CAR as HONDA
@@ -13,7 +13,7 @@ def setUp(self):
self.all_cars = get_all_car_info()
def test_generator(self):
- generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE, False)
+ generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
with open(CARS_MD_OUT, "r") as f:
current_cars_md = f.read()
@@ -56,6 +56,14 @@ def test_year_format(self):
with self.subTest(car=car):
self.assertIsNone(re.search(r"\d{4}-\d{4}", car.name), f"Format years correctly: {car.name}")
+ def test_harnesses(self):
+ for car in self.all_cars:
+ with self.subTest(car=car):
+ if car.name == "comma body":
+ raise unittest.SkipTest
+
+ self.assertNotIn(car.harness, [None, Harness.none], f"Need to specify car harness: {car.name}")
+
if __name__ == "__main__":
unittest.main()
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 929c539842bca1..502df3fe3f1524 100755
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -15,7 +15,7 @@
from selfdrive.car.gm.values import CAR as GM
from selfdrive.car.honda.values import CAR as HONDA, HONDA_BOSCH
from selfdrive.car.hyundai.values import CAR as HYUNDAI
-from selfdrive.car.tests.routes import non_tested_cars, routes, TestRoute
+from selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute
from selfdrive.test.openpilotci import get_url
from tools.lib.logreader import LogReader
from tools.lib.route import Route
@@ -38,7 +38,7 @@
for r in routes:
routes_by_car[r.car_model].add(r)
-test_cases: List[Tuple[str, Optional[TestRoute]]] = []
+test_cases: List[Tuple[str, Optional[CarTestRoute]]] = []
for i, c in enumerate(sorted(all_known_cars())):
if i % NUM_JOBS == JOB_ID:
test_cases.extend((c, r) for r in routes_by_car.get(c, (None, )))
@@ -58,7 +58,6 @@ def setUpClass(cls):
raise unittest.SkipTest
if 'FILTER' in os.environ:
- print(tuple(os.environ.get('FILTER').split(', ')))
if not cls.car_model.startswith(tuple(os.environ.get('FILTER').split(','))):
raise unittest.SkipTest
@@ -82,6 +81,7 @@ def setUpClass(cls):
except Exception:
continue
+ car_fw = []
can_msgs = []
fingerprint = defaultdict(dict)
for msg in lr:
@@ -91,6 +91,7 @@ def setUpClass(cls):
fingerprint[m.src][m.address] = len(m.dat)
can_msgs.append(msg)
elif msg.which() == "carParams":
+ car_fw = msg.carParams.carFw
if msg.carParams.openpilotLongitudinalControl:
disable_radar = True
if cls.car_model is None and not cls.ci:
@@ -104,7 +105,7 @@ def setUpClass(cls):
cls.can_msgs = sorted(can_msgs, key=lambda msg: msg.logMonoTime)
cls.CarInterface, cls.CarController, cls.CarState = interfaces[cls.car_model]
- cls.CP = cls.CarInterface.get_params(cls.car_model, fingerprint, [], disable_radar)
+ cls.CP = cls.CarInterface.get_params(cls.car_model, fingerprint, car_fw, disable_radar)
assert cls.CP
assert cls.CP.carFingerprint == cls.car_model
@@ -139,19 +140,23 @@ def test_car_params(self):
raise Exception("unkown tuning")
def test_car_interface(self):
- # TODO: also check for checkusm and counter violations from can parser
+ # TODO: also check for checksum violations from can parser
can_invalid_cnt = 0
+ can_valid = False
CC = car.CarControl.new_message()
for i, msg in enumerate(self.can_msgs):
CS = self.CI.update(CC, (msg.as_builder().to_bytes(),))
self.CI.apply(CC)
- # wait 2s for low frequency msgs to be seen
- if i > 200:
+ if CS.canValid:
+ can_valid = True
+
+ # wait max of 2s for low frequency msgs to be seen
+ if i > 200 or can_valid:
can_invalid_cnt += not CS.canValid
- self.assertLess(can_invalid_cnt, 50)
+ self.assertEqual(can_invalid_cnt, 0)
def test_radar_interface(self):
os.environ['NO_RADAR_SLEEP'] = "1"
@@ -219,6 +224,8 @@ def test_panda_safety_carstate(self):
for can in self.can_msgs:
CS = self.CI.update(CC, (can.as_builder().to_bytes(), ))
for msg in can_capnp_to_can_list(can.can, src_filter=range(64)):
+ msg = list(msg)
+ msg[3] %= 4
to_send = package_can_msg(msg)
ret = self.safety.safety_rx_hook(to_send)
self.assertEqual(1, ret, f"safety rx failed ({ret=}): {to_send}")
diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml
index 27a8c26fd65b6b..14ee7e223962f8 100644
--- a/selfdrive/car/torque_data/override.yaml
+++ b/selfdrive/car/torque_data/override.yaml
@@ -22,7 +22,12 @@ COMMA BODY: [.nan, 1000, .nan]
# Totally new cars
KIA EV6 2022: [3.5, 2.5, 0.0]
RAM 1500 5TH GEN: [2.0, 2.0, 0.0]
+RAM HD 5TH GEN: [1.4, 1.4, 0.0]
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]
+CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
+CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
+VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1]
+HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0]
# Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0]
diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml
index f7288ec731cf46..de64a5544c111f 100644
--- a/selfdrive/car/torque_data/substitute.yaml
+++ b/selfdrive/car/torque_data/substitute.yaml
@@ -29,6 +29,7 @@ HYUNDAI I30 N LINE 2019 & GT 2018 DCT: HYUNDAI SONATA 2019
HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019
HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019
HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019
+HYUNDAI IONIQ 5 2022: KIA EV6 2022
HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019
@@ -68,6 +69,8 @@ VOLKSWAGEN POLO 6TH GEN: VOLKSWAGEN GOLF 7TH GEN
SEAT LEON 3RD GEN: VOLKSWAGEN GOLF 7TH GEN
SEAT ATECA 1ST GEN: VOLKSWAGEN GOLF 7TH GEN
+SUBARU LEGACY 7TH GEN: SUBARU OUTBACK 6TH GEN
+
# Old subarus don't have much data guessing it's like low torque impreza
SUBARU OUTBACK 2018 - 2019: SUBARU IMPREZA LIMITED 2019
SUBARU OUTBACK 2015 - 2017: SUBARU IMPREZA LIMITED 2019
diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py
index 281d01026f3a40..843e7806dc9bba 100644
--- a/selfdrive/car/toyota/carstate.py
+++ b/selfdrive/car/toyota/carstate.py
@@ -94,12 +94,11 @@ def update(self, cp, cp_cam):
ret.cruiseState.available = cp.vl["PCM_CRUISE_2"]["MAIN_ON"] != 0
ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]["SET_SPEED"] * CV.KPH_TO_MS
- if self.CP.carFingerprint in RADAR_ACC_CAR:
- self.acc_type = cp.vl["ACC_CONTROL"]["ACC_TYPE"]
- ret.stockFcw = bool(cp.vl["ACC_HUD"]["FCW"])
- elif self.CP.carFingerprint in TSS2_CAR:
- self.acc_type = cp_cam.vl["ACC_CONTROL"]["ACC_TYPE"]
- ret.stockFcw = bool(cp_cam.vl["ACC_HUD"]["FCW"])
+ cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp
+
+ if self.CP.carFingerprint in (TSS2_CAR | RADAR_ACC_CAR):
+ self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"]
+ ret.stockFcw = bool(cp_acc.vl["ACC_HUD"]["FCW"])
# some TSS2 cars have low speed lockout permanently set, so ignore on those cars
# these cars are identified by an ACC_TYPE value of 2.
@@ -120,10 +119,11 @@ def update(self, cp, cp_cam):
ret.cruiseState.nonAdaptive = cp.vl["PCM_CRUISE"]["CRUISE_STATE"] in (1, 2, 3, 4, 5, 6)
ret.genericToggle = bool(cp.vl["LIGHT_STALK"]["AUTO_HIGH_BEAM"])
- ret.stockAeb = bool(cp_cam.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_cam.vl["PRE_COLLISION"]["FORCE"] < -1e-5)
-
ret.espDisabled = cp.vl["ESP_CONTROL"]["TC_DISABLED"] != 0
+ if not self.CP.enableDsu:
+ ret.stockAeb = bool(cp_acc.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_acc.vl["PRE_COLLISION"]["FORCE"] < -1e-5)
+
if self.CP.enableBsm:
ret.leftBlindspot = (cp.vl["BSM"]["L_ADJACENT"] == 1) or (cp.vl["BSM"]["L_APPROACHING"] == 1)
ret.rightBlindspot = (cp.vl["BSM"]["R_ADJACENT"] == 1) or (cp.vl["BSM"]["R_APPROACHING"] == 1)
@@ -219,27 +219,31 @@ def get_can_parser(CP):
("ACC_HUD", 1),
]
+ if CP.carFingerprint not in (TSS2_CAR - RADAR_ACC_CAR) and not CP.enableDsu:
+ signals += [
+ ("FORCE", "PRE_COLLISION"),
+ ("PRECOLLISION_ACTIVE", "PRE_COLLISION"),
+ ]
+ checks += [
+ ("PRE_COLLISION", 33),
+ ]
+
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 0)
@staticmethod
def get_cam_can_parser(CP):
- signals = [
- ("FORCE", "PRE_COLLISION"),
- ("PRECOLLISION_ACTIVE", "PRE_COLLISION"),
- ]
-
- # use steering message to check if panda is connected to frc
- checks = [
- ("STEERING_LKA", 42),
- ("PRE_COLLISION", 0), # TODO: figure out why freq is inconsistent
- ]
+ signals = []
+ checks = []
if CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR):
signals += [
+ ("PRECOLLISION_ACTIVE", "PRE_COLLISION"),
+ ("FORCE", "PRE_COLLISION"),
("ACC_TYPE", "ACC_CONTROL"),
("FCW", "ACC_HUD"),
]
checks += [
+ ("PRE_COLLISION", 33),
("ACC_CONTROL", 33),
("ACC_HUD", 1),
]
diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py
index 9457eaf68b941d..7b4031ca64ebcc 100644
--- a/selfdrive/car/toyota/values.py
+++ b/selfdrive/car/toyota/values.py
@@ -6,7 +6,7 @@
from cereal import car
from common.conversions import Conversions as CV
from selfdrive.car import dbc_dict
-from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, Star
+from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness
Ecu = car.CarParams.Ecu
MIN_ACC_SPEED = 19. * CV.MPH_TO_MS
@@ -89,7 +89,7 @@ class Footnote(Enum):
DSU = CarFootnote(
"When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace " +
"stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).",
- Column.LONGITUDINAL, star=Star.HALF)
+ Column.LONGITUDINAL)
CAMRY = CarFootnote(
"openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.",
Column.FSR_LONGITUDINAL)
@@ -114,7 +114,7 @@ class ToyotaCarInfo(CarInfo):
CAR.AVALON_TSS2: ToyotaCarInfo("Toyota Avalon 2022"),
CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
- CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk", footnotes=[Footnote.CAMRY]),
+ CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-22"),
CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-21"),
@@ -170,7 +170,7 @@ class ToyotaCarInfo(CarInfo):
CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"),
CAR.LEXUS_NXH_TSS2: ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2017-20"),
- CAR.LEXUS_RX: ToyotaCarInfo("Lexus RX 2016-18", footnotes=[Footnote.DSU]),
+ CAR.LEXUS_RX: ToyotaCarInfo("Lexus RX 2016-19", footnotes=[Footnote.DSU]),
CAR.LEXUS_RXH: ToyotaCarInfo("Lexus RX Hybrid 2016-19", footnotes=[Footnote.DSU]),
CAR.LEXUS_RX_TSS2: ToyotaCarInfo("Lexus RX 2020-22"),
CAR.LEXUS_RXH_TSS2: ToyotaCarInfo("Lexus RX Hybrid 2020-21"),
@@ -687,6 +687,7 @@ class ToyotaCarInfo(CarInfo):
b'\x01896630ZP2000\x00\x00\x00\x00',
b'\x01896630ZQ5000\x00\x00\x00\x00',
b'\x01896630ZU9000\x00\x00\x00\x00',
+ b'\x01896630ZX4000\x00\x00\x00\x00',
b'\x018966312L8000\x00\x00\x00\x00',
b'\x018966312M0000\x00\x00\x00\x00',
b'\x018966312M9000\x00\x00\x00\x00',
@@ -782,6 +783,7 @@ class ToyotaCarInfo(CarInfo):
b'\x01896637626000\x00\x00\x00\x00',
b'\x01896637648000\x00\x00\x00\x00',
b'\x01896637643000\x00\x00\x00\x00',
+ b'\x02896630A21000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZJ5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZN8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZQ3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
@@ -826,6 +828,7 @@ class ToyotaCarInfo(CarInfo):
b'F152612A10\x00\x00\x00\x00\x00\x00',
b'F152612D00\x00\x00\x00\x00\x00\x00',
b'F152616011\x00\x00\x00\x00\x00\x00',
+ b'F152616060\x00\x00\x00\x00\x00\x00',
b'F152642540\x00\x00\x00\x00\x00\x00',
b'F152676293\x00\x00\x00\x00\x00\x00',
b'F152676303\x00\x00\x00\x00\x00\x00',
@@ -1823,15 +1826,18 @@ class ToyotaCarInfo(CarInfo):
b'\x02348Y3000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0234D14000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0234D16000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'\x02348X4000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.esp, 0x7b0, None): [
b'F152648831\x00\x00\x00\x00\x00\x00',
b'F152648891\x00\x00\x00\x00\x00\x00',
b'F152648D00\x00\x00\x00\x00\x00\x00',
b'F152648D60\x00\x00\x00\x00\x00\x00',
+ b'F152648811\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B48271\x00\x00\x00\x00\x00\x00',
+ b'8965B48261\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00',
diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py
index 99a3ce55f4621b..f14c119892c95a 100644
--- a/selfdrive/car/volkswagen/carcontroller.py
+++ b/selfdrive/car/volkswagen/carcontroller.py
@@ -1,8 +1,10 @@
from cereal import car
from opendbc.can.packer import CANPacker
+from common.numpy_fast import clip
+from common.conversions import Conversions as CV
from selfdrive.car import apply_std_steer_torque_limits
-from selfdrive.car.volkswagen import volkswagencan
-from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, MQB_LDW_MESSAGES, CarControllerParams as P
+from selfdrive.car.volkswagen import mqbcan, pqcan
+from selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams
VisualAlert = car.CarControl.HUDControl.VisualAlert
@@ -10,11 +12,12 @@
class CarController:
def __init__(self, dbc_name, CP, VM):
self.CP = CP
+ self.CCP = CarControllerParams(CP)
+ self.CCS = pqcan if CP.carFingerprint in PQ_CARS else mqbcan
+ self.packer_pt = CANPacker(dbc_name)
+
self.apply_steer_last = 0
self.frame = 0
-
- self.packer_pt = CANPacker(DBC_FILES.mqb)
-
self.hcaSameTorqueCount = 0
self.hcaEnabledFrameCount = 0
@@ -26,7 +29,7 @@ def update(self, CC, CS, ext_bus):
# **** Steering Controls ************************************************ #
- if self.frame % P.HCA_STEP == 0:
+ if self.frame % self.CCP.HCA_STEP == 0:
# Logic to avoid HCA state 4 "refused":
# * Don't steer unless HCA is in state 3 "ready" or 5 "active"
# * Don't steer at standstill
@@ -38,21 +41,21 @@ def update(self, CC, CS, ext_bus):
# when exceeding ~1/3 the 360 second timer.
if CC.latActive:
- new_steer = int(round(actuators.steer * P.STEER_MAX))
- apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, P)
+ new_steer = int(round(actuators.steer * self.CCP.STEER_MAX))
+ apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP)
if apply_steer == 0:
hcaEnabled = False
self.hcaEnabledFrameCount = 0
else:
self.hcaEnabledFrameCount += 1
- if self.hcaEnabledFrameCount >= 118 * (100 / P.HCA_STEP): # 118s
+ if self.hcaEnabledFrameCount >= 118 * (100 / self.CCP.HCA_STEP): # 118s
hcaEnabled = False
self.hcaEnabledFrameCount = 0
else:
hcaEnabled = True
if self.apply_steer_last == apply_steer:
self.hcaSameTorqueCount += 1
- if self.hcaSameTorqueCount > 1.9 * (100 / P.HCA_STEP): # 1.9s
+ if self.hcaSameTorqueCount > 1.9 * (100 / self.CCP.HCA_STEP): # 1.9s
apply_steer -= (1, -1)[apply_steer < 0]
self.hcaSameTorqueCount = 0
else:
@@ -62,32 +65,41 @@ def update(self, CC, CS, ext_bus):
apply_steer = 0
self.apply_steer_last = apply_steer
- can_sends.append(volkswagencan.create_mqb_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled))
+ can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled))
+
+ # **** Acceleration Controls ******************************************** #
+
+ if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl:
+ tsk_status = self.CCS.tsk_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive)
+ accel = clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.longActive else 0
+ can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, tsk_status, accel))
# **** HUD Controls ***************************************************** #
- if self.frame % P.LDW_STEP == 0:
+ if self.frame % self.CCP.LDW_STEP == 0:
+ hud_alert = 0
if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw):
- hud_alert = MQB_LDW_MESSAGES["laneAssistTakeOverSilent"]
- else:
- hud_alert = MQB_LDW_MESSAGES["none"]
+ hud_alert = self.CCP.LDW_MESSAGES["laneAssistTakeOver"]
+ can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, CANBUS.pt, CS.ldw_stock_values, CC.enabled,
+ CS.out.steeringPressed, hud_alert, hud_control))
- can_sends.append(volkswagencan.create_mqb_hud_control(self.packer_pt, CANBUS.pt, CC.enabled,
- CS.out.steeringPressed, hud_alert, hud_control.leftLaneVisible,
- hud_control.rightLaneVisible, CS.ldw_stock_values,
- hud_control.leftLaneDepart, hud_control.rightLaneDepart))
+ if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl:
+ acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive)
+ set_speed = hud_control.setSpeed * CV.MS_TO_KPH # FIXME: follow the recent displayed-speed updates, also use mph_kmh toggle to fix display rounding problem?
+ can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, CANBUS.pt, acc_hud_status, set_speed,
+ hud_control.leadVisible))
- # **** ACC Button Controls ********************************************** #
+ # **** Stock ACC Button Controls **************************************** #
- if self.CP.pcmCruise and self.frame % P.GRA_ACC_STEP == 0:
+ if self.CP.pcmCruise and self.frame % self.CCP.GRA_ACC_STEP == 0:
idx = (CS.gra_stock_values["COUNTER"] + 1) % 16
if CC.cruiseControl.cancel:
- can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True))
+ can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True))
elif CC.cruiseControl.resume:
- can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True))
+ can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True))
new_actuators = actuators.copy()
- new_actuators.steer = self.apply_steer_last / P.STEER_MAX
+ new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX
self.frame += 1
return new_actuators, can_sends
diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py
index 052a1262e43d7c..facc740a153a15 100644
--- a/selfdrive/car/volkswagen/carstate.py
+++ b/selfdrive/car/volkswagen/carstate.py
@@ -3,22 +3,34 @@
from common.conversions import Conversions as CV
from selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser
-from opendbc.can.can_define import CANDefine
-from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, NetworkLocation, TransmissionType, GearShifter, \
- CarControllerParams, MQB_BUTTONS
+from selfdrive.car.volkswagen.values import DBC, CANBUS, PQ_CARS, NetworkLocation, TransmissionType, GearShifter, \
+ CarControllerParams
+
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
- self.button_states = {button.event_type: False for button in MQB_BUTTONS}
- can_define = CANDefine(DBC_FILES.mqb)
- if CP.transmissionType == TransmissionType.automatic:
- self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"]
- elif CP.transmissionType == TransmissionType.direct:
- self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"]
- self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"]
+ self.CCP = CarControllerParams(CP)
+ self.button_states = {button.event_type: False for button in self.CCP.BUTTONS}
+
+ def create_button_events(self, pt_cp, buttons):
+ button_events = []
+
+ for button in buttons:
+ state = pt_cp.vl[button.can_addr][button.can_msg] in button.values
+ if self.button_states[button.event_type] != state:
+ event = car.CarState.ButtonEvent.new_message()
+ event.type = button.event_type
+ event.pressed = state
+ button_events.append(event)
+ self.button_states[button.event_type] = state
+
+ return button_events
def update(self, pt_cp, cam_cp, ext_cp, trans_type):
+ if self.CP.carFingerprint in PQ_CARS:
+ return self.update_pq(pt_cp, cam_cp, ext_cp, trans_type)
+
ret = car.CarState.new_message()
# Update vehicle speed and acceleration from ABS wheel speeds.
ret.wheelSpeeds = self.get_wheel_speeds(
@@ -37,11 +49,11 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type):
ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])]
ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])]
ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])]
- ret.steeringPressed = abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE
+ ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD
# Verify EPS readiness to accept steering commands
- hca_status = self.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"])
+ hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"])
ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT")
ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED")
@@ -54,9 +66,9 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type):
# Update gear and/or clutch position data.
if trans_type == TransmissionType.automatic:
- ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None))
+ ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None))
elif trans_type == TransmissionType.direct:
- ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None))
+ ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None))
elif trans_type == TransmissionType.manual:
ret.clutchPressed = not pt_cp.vl["Motor_14"]["MO_Kuppl_schalter"]
if bool(pt_cp.vl["Gateway_72"]["BCM1_Rueckfahrlicht_Schalter"]):
@@ -118,25 +130,119 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type):
# Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough
ret.leftBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Left"])
ret.rightBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Right"])
+ ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
self.gra_stock_values = pt_cp.vl["GRA_ACC_01"]
- buttonEvents = []
- for button in MQB_BUTTONS:
- state = pt_cp.vl[button.can_addr][button.can_msg] in button.values
- if self.button_states[button.event_type] != state:
- event = car.CarState.ButtonEvent.new_message()
- event.type = button.event_type
- event.pressed = state
- buttonEvents.append(event)
- self.button_states[button.event_type] = state
- ret.buttonEvents = buttonEvents
# Additional safety checks performed in CarInterface.
ret.espDisabled = pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"] != 0
return ret
+ def update_pq(self, pt_cp, cam_cp, ext_cp, trans_type):
+ ret = car.CarState.new_message()
+ # Update vehicle speed and acceleration from ABS wheel speeds.
+ ret.wheelSpeeds = self.get_wheel_speeds(
+ pt_cp.vl["Bremse_3"]["Radgeschw__VL_4_1"],
+ pt_cp.vl["Bremse_3"]["Radgeschw__VR_4_1"],
+ pt_cp.vl["Bremse_3"]["Radgeschw__HL_4_1"],
+ pt_cp.vl["Bremse_3"]["Radgeschw__HR_4_1"],
+ )
+
+ # vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF
+ ret.vEgoRaw = pt_cp.vl["Bremse_1"]["Geschwindigkeit_neu__Bremse_1_"] * CV.KPH_TO_MS
+ ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
+ ret.standstill = ret.vEgo < 0.1
+
+ # Update steering angle, rate, yaw rate, and driver input torque. VW send
+ # the sign/direction in a separate signal so they must be recombined.
+ ret.steeringAngleDeg = pt_cp.vl["Lenkhilfe_3"]["LH3_BLW"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_BLWSign"])]
+ ret.steeringRateDeg = pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit_S"])]
+ ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])]
+ ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
+ ret.yawRate = pt_cp.vl["Bremse_5"]["Giergeschwindigkeit"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["Vorzeichen_der_Giergeschwindigk"])] * CV.DEG_TO_RAD
+
+ # Verify EPS readiness to accept steering commands
+ hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
+ ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT")
+ ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED")
+
+ # Update gas, brakes, and gearshift.
+ ret.gas = pt_cp.vl["Motor_3"]["Fahrpedal_Rohsignal"] / 100.0
+ ret.gasPressed = ret.gas > 0
+ ret.brake = pt_cp.vl["Bremse_5"]["Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
+ ret.brakePressed = bool(pt_cp.vl["Motor_2"]["Bremstestschalter"])
+ ret.parkingBrake = bool(pt_cp.vl["Kombi_1"]["Bremsinfo"])
+
+ # Update gear and/or clutch position data.
+ if trans_type == TransmissionType.automatic:
+ ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"], None))
+ elif trans_type == TransmissionType.manual:
+ ret.clutchPressed = not pt_cp.vl["Motor_1"]["Kupplungsschalter"]
+ reverse_light = bool(pt_cp.vl["Gate_Komf_1"]["GK1_Rueckfahr"])
+ if reverse_light:
+ ret.gearShifter = GearShifter.reverse
+ else:
+ ret.gearShifter = GearShifter.drive
+
+ # Update door and trunk/hatch lid open status.
+ ret.doorOpen = any([pt_cp.vl["Gate_Komf_1"]["GK1_Fa_Tuerkont"],
+ pt_cp.vl["Gate_Komf_1"]["BSK_BT_geoeffnet"],
+ pt_cp.vl["Gate_Komf_1"]["BSK_HL_geoeffnet"],
+ pt_cp.vl["Gate_Komf_1"]["BSK_HR_geoeffnet"],
+ pt_cp.vl["Gate_Komf_1"]["BSK_HD_Hauptraste"]])
+
+ # Update seatbelt fastened status.
+ ret.seatbeltUnlatched = not bool(pt_cp.vl["Airbag_1"]["Gurtschalter_Fahrer"])
+
+ # Consume blind-spot monitoring info/warning LED states, if available.
+ # Infostufe: BSM LED on, Warnung: BSM LED flashing
+ if self.CP.enableBsm:
+ ret.leftBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
+ ret.rightBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
+
+ # Consume factory LDW data relevant for factory SWA (Lane Change Assist)
+ # and capture it for forwarding to the blind spot radar controller
+ self.ldw_stock_values = cam_cp.vl["LDW_Status"] if self.CP.networkLocation == NetworkLocation.fwdCamera else {}
+
+ # Stock FCW is considered active if the release bit for brake-jerk warning
+ # is set. Stock AEB considered active if the partial braking or target
+ # braking release bits are set.
+ # Refer to VW Self Study Program 890253: Volkswagen Driver Assistance
+ # Systems, chapters on Front Assist with Braking and City Emergency
+ # Braking for the 2016 Passat NMS
+ # TODO: deferred until we can collect data on pre-MY2016 behavior, AWV message may be shorter with fewer signals
+ ret.stockFcw = False
+ ret.stockAeb = False
+
+ # Update ACC radar status.
+ ret.cruiseState.available = bool(pt_cp.vl["Motor_5"]["GRA_Hauptschalter"])
+ ret.cruiseState.enabled = bool(pt_cp.vl["Motor_2"]["GRA_Status"])
+ if self.CP.pcmCruise:
+ ret.accFaulted = ext_cp.vl["ACC_GRA_Anziege"]["ACA_StaACC"] in (6, 7)
+ # TODO: update opendbc with PQ TSK state for OP long accFaulted
+
+ # Update ACC setpoint. When the setpoint reads as 255, the driver has not
+ # yet established an ACC setpoint, so treat it as zero.
+ ret.cruiseState.speed = ext_cp.vl["ACC_GRA_Anziege"]["ACA_V_Wunsch"] * CV.KPH_TO_MS
+ if ret.cruiseState.speed > 70: # 255 kph in m/s == no current setpoint
+ ret.cruiseState.speed = 0
+
+ # Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough
+ ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"],
+ pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_re"])
+ ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
+ self.gra_stock_values = pt_cp.vl["GRA_Neu"]
+
+ # Additional safety checks performed in CarInterface.
+ ret.espDisabled = bool(pt_cp.vl["Bremse_1"]["ESP_Passiv_getastet"])
+
+ return ret
+
@staticmethod
def get_can_parser(CP):
+ if CP.carFingerprint in PQ_CARS:
+ return CarState.get_can_parser_pq(CP)
+
signals = [
# sig_name, sig_address
("LWI_Lenkradwinkel", "LWI_01"), # Absolute steering angle
@@ -218,10 +324,13 @@ def get_can_parser(CP):
signals += MqbExtraSignals.bsm_radar_signals
checks += MqbExtraSignals.bsm_radar_checks
- return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.pt)
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.pt)
@staticmethod
def get_cam_can_parser(CP):
+ if CP.carFingerprint in PQ_CARS:
+ return CarState.get_cam_can_parser_pq(CP)
+
signals = []
checks = []
@@ -246,7 +355,124 @@ def get_cam_can_parser(CP):
signals += MqbExtraSignals.bsm_radar_signals
checks += MqbExtraSignals.bsm_radar_checks
- return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.cam)
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.cam)
+
+ @staticmethod
+ def get_can_parser_pq(CP):
+ signals = [
+ # sig_name, sig_address, default
+ ("LH3_BLW", "Lenkhilfe_3"), # Absolute steering angle
+ ("LH3_BLWSign", "Lenkhilfe_3"), # Steering angle sign
+ ("LH3_LM", "Lenkhilfe_3"), # Absolute driver torque input
+ ("LH3_LMSign", "Lenkhilfe_3"), # Driver torque input sign
+ ("LH2_Sta_HCA", "Lenkhilfe_2"), # Steering rack HCA status
+ ("Lenkradwinkel_Geschwindigkeit", "Lenkwinkel_1"), # Absolute steering rate
+ ("Lenkradwinkel_Geschwindigkeit_S", "Lenkwinkel_1"), # Steering rate sign
+ ("Geschwindigkeit_neu__Bremse_1_", "Bremse_1"), # Vehicle speed from ABS
+ ("Radgeschw__VL_4_1", "Bremse_3"), # ABS wheel speed, front left
+ ("Radgeschw__VR_4_1", "Bremse_3"), # ABS wheel speed, front right
+ ("Radgeschw__HL_4_1", "Bremse_3"), # ABS wheel speed, rear left
+ ("Radgeschw__HR_4_1", "Bremse_3"), # ABS wheel speed, rear right
+ ("Giergeschwindigkeit", "Bremse_5"), # Absolute yaw rate
+ ("Vorzeichen_der_Giergeschwindigk", "Bremse_5"), # Yaw rate sign
+ ("Gurtschalter_Fahrer", "Airbag_1"), # Seatbelt status, driver
+ ("Gurtschalter_Beifahrer", "Airbag_1"), # Seatbelt status, passenger
+ ("Bremstestschalter", "Motor_2"), # Brake pedal pressed (brake light test switch)
+ ("Bremslichtschalter", "Motor_2"), # Brakes applied (brake light switch)
+ ("Bremsdruck", "Bremse_5"), # Brake pressure applied
+ ("Vorzeichen_Bremsdruck", "Bremse_5"), # Brake pressure applied sign (???)
+ ("Fahrpedal_Rohsignal", "Motor_3"), # Accelerator pedal value
+ ("ESP_Passiv_getastet", "Bremse_1"), # Stability control disabled
+ ("GRA_Hauptschalter", "Motor_5"), # ACC main switch
+ ("GRA_Status", "Motor_2"), # ACC engagement status
+ ("GK1_Fa_Tuerkont", "Gate_Komf_1"), # Door open, driver
+ ("BSK_BT_geoeffnet", "Gate_Komf_1"), # Door open, passenger
+ ("BSK_HL_geoeffnet", "Gate_Komf_1"), # Door open, rear left
+ ("BSK_HR_geoeffnet", "Gate_Komf_1"), # Door open, rear right
+ ("BSK_HD_Hauptraste", "Gate_Komf_1"), # Trunk or hatch open
+ ("GK1_Blinker_li", "Gate_Komf_1"), # Left turn signal on
+ ("GK1_Blinker_re", "Gate_Komf_1"), # Right turn signal on
+ ("Bremsinfo", "Kombi_1"), # Manual handbrake applied
+ ("GRA_Hauptschalt", "GRA_Neu"), # ACC button, on/off
+ ("GRA_Typ_Hauptschalt", "GRA_Neu"), # ACC button, momentary vs latching
+ ("GRA_Kodierinfo", "GRA_Neu"), # ACC button, configuration
+ ("GRA_Abbrechen", "GRA_Neu"), # ACC button, cancel
+ ("GRA_Neu_Setzen", "GRA_Neu"), # ACC button, set
+ ("GRA_Up_lang", "GRA_Neu"), # ACC button, increase or accel, long press
+ ("GRA_Down_lang", "GRA_Neu"), # ACC button, decrease or decel, long press
+ ("GRA_Up_kurz", "GRA_Neu"), # ACC button, increase or accel, short press
+ ("GRA_Down_kurz", "GRA_Neu"), # ACC button, decrease or decel, short press
+ ("GRA_Recall", "GRA_Neu"), # ACC button, resume
+ ("GRA_Zeitluecke", "GRA_Neu"), # ACC button, time gap adj
+ ("COUNTER", "GRA_Neu"), # ACC button, message counter
+ ("GRA_Sender", "GRA_Neu"), # ACC button, CAN message originator
+ ]
+
+ checks = [
+ # sig_address, frequency
+ ("Bremse_1", 100), # From J104 ABS/ESP controller
+ ("Bremse_3", 100), # From J104 ABS/ESP controller
+ ("Lenkhilfe_3", 100), # From J500 Steering Assist with integrated sensors
+ ("Lenkwinkel_1", 100), # From J500 Steering Assist with integrated sensors
+ ("Motor_3", 100), # From J623 Engine control module
+ ("Airbag_1", 50), # From J234 Airbag control module
+ ("Bremse_5", 50), # From J104 ABS/ESP controller
+ ("GRA_Neu", 50), # From J??? steering wheel control buttons
+ ("Kombi_1", 50), # From J285 Instrument cluster
+ ("Motor_2", 50), # From J623 Engine control module
+ ("Motor_5", 50), # From J623 Engine control module
+ ("Lenkhilfe_2", 20), # From J500 Steering Assist with integrated sensors
+ ("Gate_Komf_1", 10), # From J533 CAN gateway
+ ]
+
+ if CP.transmissionType == TransmissionType.automatic:
+ signals += [("Waehlhebelposition__Getriebe_1_", "Getriebe_1", 0)] # Auto trans gear selector position
+ checks += [("Getriebe_1", 100)] # From J743 Auto transmission control module
+ elif CP.transmissionType == TransmissionType.manual:
+ signals += [("Kupplungsschalter", "Motor_1", 0), # Clutch switch
+ ("GK1_Rueckfahr", "Gate_Komf_1", 0)] # Reverse light from BCM
+ checks += [("Motor_1", 100)] # From J623 Engine control module
+
+ if CP.networkLocation == NetworkLocation.fwdCamera:
+ # Extended CAN devices other than the camera are here on CANBUS.pt
+ signals += PqExtraSignals.fwd_radar_signals
+ checks += PqExtraSignals.fwd_radar_checks
+ if CP.enableBsm:
+ signals += PqExtraSignals.bsm_radar_signals
+ checks += PqExtraSignals.bsm_radar_checks
+
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.pt)
+
+ @staticmethod
+ def get_cam_can_parser_pq(CP):
+
+ signals = []
+ checks = []
+
+ if CP.networkLocation == NetworkLocation.fwdCamera:
+ signals += [
+ # sig_name, sig_address
+ ("LDW_SW_Warnung_links", "LDW_Status"), # Blind spot in warning mode on left side due to lane departure
+ ("LDW_SW_Warnung_rechts", "LDW_Status"), # Blind spot in warning mode on right side due to lane departure
+ ("LDW_Seite_DLCTLC", "LDW_Status"), # Direction of most likely lane departure (left or right)
+ ("LDW_DLC", "LDW_Status"), # Lane departure, distance to line crossing
+ ("LDW_TLC", "LDW_Status"), # Lane departure, time to line crossing
+ ]
+ checks += [
+ # sig_address, frequency
+ ("LDW_Status", 10) # From R242 Driver assistance camera
+ ]
+
+ if CP.networkLocation == NetworkLocation.gateway:
+ # Radars are here on CANBUS.cam
+ signals += PqExtraSignals.fwd_radar_signals
+ checks += PqExtraSignals.fwd_radar_checks
+ if CP.enableBsm:
+ signals += PqExtraSignals.bsm_radar_signals
+ checks += PqExtraSignals.bsm_radar_checks
+
+ return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.cam)
+
class MqbExtraSignals:
# Additional signal and message lists for optional or bus-portable controllers
@@ -269,3 +495,22 @@ class MqbExtraSignals:
bsm_radar_checks = [
("SWA_01", 20), # From J1086 Lane Change Assist
]
+
+class PqExtraSignals:
+ # Additional signal and message lists for optional or bus-portable controllers
+ fwd_radar_signals = [
+ ("ACA_StaACC", "ACC_GRA_Anziege", 0), # ACC drivetrain coordinator status
+ ("ACA_V_Wunsch", "ACC_GRA_Anziege", 0), # ACC set speed
+ ]
+ fwd_radar_checks = [
+ ("ACC_GRA_Anziege", 25), # From J428 ACC radar control module
+ ]
+ bsm_radar_signals = [
+ ("SWA_Infostufe_SWA_li", "SWA_1", 0), # Blind spot object info, left
+ ("SWA_Warnung_SWA_li", "SWA_1", 0), # Blind spot object warning, left
+ ("SWA_Infostufe_SWA_re", "SWA_1", 0), # Blind spot object info, right
+ ("SWA_Warnung_SWA_re", "SWA_1", 0), # Blind spot object warning, right
+ ]
+ bsm_radar_checks = [
+ ("SWA_1", 20), # From J1086 Lane Change Assist
+ ]
diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py
index 8bab93d4a3dac5..7e686b970712b3 100644
--- a/selfdrive/car/volkswagen/interface.py
+++ b/selfdrive/car/volkswagen/interface.py
@@ -1,7 +1,10 @@
from cereal import car
-from selfdrive.car.volkswagen.values import CAR, CANBUS, NetworkLocation, TransmissionType, GearShifter
-from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
+from panda import Panda
+from common.conversions import Conversions as CV
+from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, \
+ gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase
+from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter
EventName = car.CarEvent.EventName
@@ -23,7 +26,36 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.carName = "volkswagen"
ret.radarOffCan = True
- if True: # pylint: disable=using-constant-test
+ if candidate in PQ_CARS:
+ # Set global PQ35/PQ46/NMS parameters
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagenPq)]
+ ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
+
+ if 0x440 in fingerprint[0]: # Getriebe_1
+ ret.transmissionType = TransmissionType.automatic
+ else:
+ ret.transmissionType = TransmissionType.manual
+
+ if any(msg in fingerprint[1] for msg in (0x1A0, 0xC2)): # Bremse_1, Lenkwinkel_1
+ ret.networkLocation = NetworkLocation.gateway
+ else:
+ ret.networkLocation = NetworkLocation.fwdCamera
+
+ # The PQ port is in dashcam-only mode due to a fixed six-minute maximum timer on HCA steering. An unsupported
+ # EPS flash update to work around this timer, and enable steering down to zero, is available from:
+ # https://github.com/pd0wm/pq-flasher
+ # It is documented in a four-part blog series:
+ # https://blog.willemmelching.nl/carhacking/2022/01/02/vw-part1/
+ # Panda ALLOW_DEBUG firmware required.
+ ret.dashcamOnly = True
+
+ if disable_radar and ret.networkLocation == NetworkLocation.gateway:
+ # Proof-of-concept, prep for E2E only. No radar points available. Follow-to-stop not yet supported, but should
+ # be simple to add when a suitable test car becomes available. Panda ALLOW_DEBUG firmware required.
+ ret.openpilotLongitudinalControl = True
+ ret.safetyConfigs[0].safetyParam |= Panda.FLAG_VOLKSWAGEN_LONG_CONTROL
+
+ else:
# Set global MQB parameters
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagen)]
ret.enableBsm = 0x30F in fingerprint[0] # SWA_01
@@ -52,6 +84,13 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.lateralTuning.pid.kpV = [0.6]
ret.lateralTuning.pid.kiV = [0.2]
+ # Global longitudinal tuning defaults, can be overridden per-vehicle
+
+ ret.pcmCruise = not ret.openpilotLongitudinalControl
+ ret.longitudinalActuatorDelayUpperBound = 0.5 # s
+ ret.longitudinalTuning.kpV = [0.1]
+ ret.longitudinalTuning.kiV = [0.0]
+
# Per-chassis tuning values, override tuning defaults here if desired
if candidate == CAR.ARTEON_MK1:
@@ -74,6 +113,14 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
ret.mass = 1551 + STD_CARGO_KG
ret.wheelbase = 2.79
+ elif candidate == CAR.PASSAT_NMS:
+ ret.mass = 1503 + STD_CARGO_KG
+ ret.wheelbase = 2.80
+ ret.minEnableSpeed = 20 * CV.KPH_TO_MS # ACC "basic", no FtS
+ ret.minSteerSpeed = 50 * CV.KPH_TO_MS
+ ret.steerActuatorDelay = 0.2
+ CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
+
elif candidate == CAR.POLO_MK6:
ret.mass = 1230 + STD_CARGO_KG
ret.wheelbase = 2.55
@@ -160,7 +207,8 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam, self.cp_ext, self.CP.transmissionType)
- events = self.create_common_events(ret, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic])
+ events = self.create_common_events(ret, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic],
+ pcm_enable=not self.CS.CP.openpilotLongitudinalControl)
# Low speed steer alert hysteresis logic
if self.CP.minSteerSpeed > 0. and ret.vEgo < (self.CP.minSteerSpeed + 1.):
@@ -170,6 +218,12 @@ def _update(self, c):
if self.low_speed_alert:
events.add(EventName.belowSteerSpeed)
+ if self.CS.CP.openpilotLongitudinalControl:
+ if ret.vEgo < self.CP.minEnableSpeed + 2.:
+ events.add(EventName.belowEngageSpeed)
+ if c.enabled and ret.vEgo < self.CP.minEnableSpeed:
+ events.add(EventName.speedTooLow)
+
ret.events = events.to_msg()
return ret
diff --git a/selfdrive/car/volkswagen/mqbcan.py b/selfdrive/car/volkswagen/mqbcan.py
new file mode 100644
index 00000000000000..1a4ca339ab4f1e
--- /dev/null
+++ b/selfdrive/car/volkswagen/mqbcan.py
@@ -0,0 +1,38 @@
+def create_steering_control(packer, bus, apply_steer, lkas_enabled):
+ values = {
+ "SET_ME_0X3": 0x3,
+ "Assist_Torque": abs(apply_steer),
+ "Assist_Requested": lkas_enabled,
+ "Assist_VZ": 1 if apply_steer < 0 else 0,
+ "HCA_Available": 1,
+ "HCA_Standby": not lkas_enabled,
+ "HCA_Active": lkas_enabled,
+ "SET_ME_0XFE": 0xFE,
+ "SET_ME_0X07": 0x07,
+ }
+ return packer.make_can_msg("HCA_01", bus, values)
+
+
+def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, steering_pressed, hud_alert, hud_control):
+ values = ldw_stock_values.copy()
+
+ values.update({
+ "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0,
+ "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0,
+ "LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible,
+ "LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible,
+ "LDW_Texte": hud_alert,
+ })
+ return packer.make_can_msg("LDW_02", bus, values)
+
+
+def create_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False):
+ values = gra_stock_values.copy()
+
+ values.update({
+ "COUNTER": idx,
+ "GRA_Abbrechen": cancel,
+ "GRA_Tip_Wiederaufnahme": resume,
+ })
+
+ return packer.make_can_msg("GRA_ACC_01", bus, values)
diff --git a/selfdrive/car/volkswagen/pqcan.py b/selfdrive/car/volkswagen/pqcan.py
new file mode 100644
index 00000000000000..1e7ed21342b54b
--- /dev/null
+++ b/selfdrive/car/volkswagen/pqcan.py
@@ -0,0 +1,84 @@
+def create_steering_control(packer, bus, apply_steer, lkas_enabled):
+ values = {
+ "LM_Offset": abs(apply_steer),
+ "LM_OffSign": 1 if apply_steer < 0 else 0,
+ "HCA_Status": 5 if (lkas_enabled and apply_steer != 0) else 3,
+ "Vib_Freq": 16,
+ }
+
+ return packer.make_can_msg("HCA_1", bus, values)
+
+
+def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, steering_pressed, hud_alert, hud_control):
+ values = ldw_stock_values.copy()
+
+ values.update({
+ "LDW_Lampe_gelb": 1 if enabled and steering_pressed else 0,
+ "LDW_Lampe_gruen": 1 if enabled and not steering_pressed else 0,
+ "LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible,
+ "LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible,
+ "LDW_Textbits": hud_alert,
+ })
+
+ return packer.make_can_msg("LDW_Status", bus, values)
+
+
+def create_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False):
+ values = gra_stock_values.copy()
+
+ values.update({
+ "COUNTER": idx,
+ "GRA_Abbrechen": cancel,
+ "GRA_Recall": resume,
+ })
+
+ return packer.make_can_msg("GRA_Neu", bus, values)
+
+
+def tsk_status_value(main_switch_on, acc_faulted, long_active):
+ if long_active:
+ tsk_status = 1
+ elif main_switch_on:
+ tsk_status = 2
+ else:
+ tsk_status = 0
+
+ return tsk_status
+
+
+def acc_hud_status_value(main_switch_on, acc_faulted, long_active):
+ if acc_faulted:
+ hud_status = 6
+ elif long_active:
+ hud_status = 3
+ elif main_switch_on:
+ hud_status = 2
+ else:
+ hud_status = 0
+
+ return hud_status
+
+
+def create_acc_accel_control(packer, bus, adr_status, accel):
+ values = {
+ "ACS_Sta_ADR": adr_status,
+ "ACS_StSt_Info": adr_status != 1,
+ "ACS_Typ_ACC": 0, # TODO: this is ACC "basic", find a way to detect FtS support (1)
+ "ACS_Sollbeschl": accel if adr_status == 1 else 3.01,
+ "ACS_zul_Regelabw": 0.2 if adr_status == 1 else 1.27,
+ "ACS_max_AendGrad": 3.0 if adr_status == 1 else 5.08,
+ }
+
+ return packer.make_can_msg("ACC_System", bus, values)
+
+
+def create_acc_hud_control(packer, bus, acc_status, set_speed, lead_visible):
+ values = {
+ "ACA_StaACC": acc_status,
+ "ACA_Zeitluecke": 2,
+ "ACA_V_Wunsch": set_speed,
+ "ACA_gemZeitl": 8 if lead_visible else 0,
+ }
+ # TODO: ACA_ID_StaACC, ACA_AnzDisplay, ACA_kmh_mph, ACA_PrioDisp, ACA_Aend_Zeitluecke
+
+ return packer.make_can_msg("ACC_GRA_Anziege", bus, values)
diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py
index 9f58d68f81bb8b..58f071901b990e 100755
--- a/selfdrive/car/volkswagen/values.py
+++ b/selfdrive/car/volkswagen/values.py
@@ -4,6 +4,7 @@
from typing import Dict, List, Union
from cereal import car
+from opendbc.can.can_define import CANDefine
from selfdrive.car import dbc_dict
from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness
@@ -15,54 +16,89 @@
class CarControllerParams:
- HCA_STEP = 2 # HCA_01 message frequency 50Hz
- LDW_STEP = 10 # LDW_02 message frequency 10Hz
- GRA_ACC_STEP = 3 # GRA_ACC_01 message frequency 33Hz
+ HCA_STEP = 2 # HCA_01/HCA_1 message frequency 50Hz
+ GRA_ACC_STEP = 3 # GRA_ACC_01/GRA_Neu message frequency 33Hz
+ ACC_CONTROL_STEP = 2 # ACC_06/ACC_07/ACC_System frequency 50Hz
+ ACC_HUD_STEP = 4 # ACC_GRA_Anziege frequency 25Hz
- # Observed documented MQB limits: 3.00 Nm max, rate of change 5.00 Nm/sec.
- # Limiting rate-of-change based on real-world testing and Comma's safety
- # requirements for minimum time to lane departure.
- STEER_MAX = 300 # Max heading control assist torque 3.00 Nm
- STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50))
- STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
- STEER_DRIVER_ALLOWANCE = 80
- STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily
- STEER_DRIVER_FACTOR = 1 # from dbc
+ ACCEL_MAX = 2.0 # 2.0 m/s max acceleration
+ ACCEL_MIN = -3.5 # 3.5 m/s max deceleration
+ def __init__(self, CP):
+ # Documented lateral limits: 3.00 Nm max, rate of change 5.00 Nm/sec.
+ # MQB vs PQ maximums are shared, but rate-of-change limited differently
+ # based on safety requirements driven by lateral accel testing.
+ self.STEER_MAX = 300 # Max heading control assist torque 3.00 Nm
+ self.STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily
+ self.STEER_DRIVER_FACTOR = 1 # from dbc
-class CANBUS:
- pt = 0
- cam = 2
+ can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
+ if CP.carFingerprint in PQ_CARS:
+ self.LDW_STEP = 5 # LDW_1 message frequency 20Hz
+ self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
+ self.STEER_DELTA_UP = 6 # Max HCA reached in 1.00s (STEER_MAX / (50Hz * 1.00))
+ self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
-class DBC_FILES:
- mqb = "vw_mqb_2010" # Used for all cars with MQB-style CAN messaging
+ if CP.transmissionType == TransmissionType.automatic:
+ self.shifter_values = can_define.dv["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"]
+ self.hca_status_values = can_define.dv["Lenkhilfe_2"]["LH2_Sta_HCA"]
+ self.BUTTONS = [
+ Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_Neu", "GRA_Neu_Setzen", [1]),
+ Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_Neu", "GRA_Recall", [1]),
+ Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_Neu", "GRA_Up_kurz", [1]),
+ Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_Neu", "GRA_Down_kurz", [1]),
+ Button(car.CarState.ButtonEvent.Type.cancel, "GRA_Neu", "GRA_Abbrechen", [1]),
+ Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_Neu", "GRA_Zeitluecke", [1]),
+ ]
-DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict(DBC_FILES.mqb, None))
+ self.LDW_MESSAGES = {
+ "none": 0, # Nothing to display
+ "laneAssistUnavail": 1, # "Lane Assist currently not available."
+ "laneAssistUnavailSysError": 2, # "Lane Assist system error"
+ "laneAssistUnavailNoSensorView": 3, # "Lane Assist not available. No sensor view."
+ "laneAssistTakeOver": 4, # "Lane Assist: Please Take Over Steering"
+ "laneAssistDeactivTrailer": 5, # "Lane Assist: no function with trailer"
+ }
+ else:
+ self.LDW_STEP = 10 # LDW_02 message frequency 10Hz
+ self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
+ self.STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50))
+ self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
-MQB_BUTTONS = [
- Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]),
- Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]),
- Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]),
- Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]),
- Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]),
- Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]),
-]
+ if CP.transmissionType == TransmissionType.automatic:
+ self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"]
+ elif CP.transmissionType == TransmissionType.direct:
+ self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"]
+ self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"]
+ self.BUTTONS = [
+ Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]),
+ Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]),
+ Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]),
+ Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]),
+ Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]),
+ Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]),
+ ]
-MQB_LDW_MESSAGES = {
- "none": 0, # Nothing to display
- "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime
- "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime
- "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep
- "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep
- "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime
- "laneAssistTakeOverSilent": 8, # "Lane Assist: Please Take Over Steering" silent
- "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep
- "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward
-}
+ self.LDW_MESSAGES = {
+ "none": 0, # Nothing to display
+ "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime
+ "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime
+ "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep
+ "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep
+ "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime
+ "laneAssistTakeOver": 8, # "Lane Assist: Please Take Over Steering" silent
+ "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep
+ "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward
+ }
+
+
+class CANBUS:
+ pt = 0
+ cam = 2
# Check the 7th and 8th characters of the VIN before adding a new CAR. If the
@@ -76,6 +112,7 @@ class CAR:
GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants
JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 VW Jetta
PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants
+ PASSAT_NMS = "VOLKSWAGEN PASSAT NMS" # Chassis A3, North America/China/Mideast NMS Passat, incl. facelift
POLO_MK6 = "VOLKSWAGEN POLO 6TH GEN" # Chassis AW, Mk6 VW Polo
TAOS_MK1 = "VOLKSWAGEN TAOS 1ST GEN" # Chassis B2, Mk1 VW Taos and Tharu
TCROSS_MK1 = "VOLKSWAGEN T-CROSS 1ST GEN" # Chassis C1, Mk1 VW T-Cross SWB and LWB variants
@@ -96,18 +133,27 @@ class CAR:
SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants
+PQ_CARS = {CAR.PASSAT_NMS}
+
+
+DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("vw_mqb_2010", None))
+for car_type in PQ_CARS:
+ DBC[car_type] = dbc_dict("vw_golf_mk4", None)
+
+
class Footnote(Enum):
KAMIQ = CarFootnote(
"Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.",
Column.MODEL)
PASSAT = CarFootnote(
- "Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.",
+ "Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.",
Column.MODEL)
VW_HARNESS = CarFootnote(
"Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma " +
"store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black " +
- "(older design) or light brown (newer design). For the newer design, in the interim, choose \"VW J533 Development\" " +
- "from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.",
+ "(older design) or light brown (newer design). In the interim, if your car has a J533 connector CAN gateway " +
+ "inside the dashboard, choose \"VW J533 Development\" from the vehicle drop-down for a suitable harness. " +
+ "(Some newer models are also observed to not have a J533 connector.)",
Column.MODEL)
VW_VARIANT = CarFootnote(
"Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.)",
@@ -153,6 +199,7 @@ class VWCarInfo(CarInfo):
VWCarInfo("Volkswagen Passat Alltrack 2015-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
VWCarInfo("Volkswagen Passat GTE 2015-22", footnotes=[Footnote.VW_HARNESS, Footnote.VW_VARIANT], harness=Harness.j533),
],
+ CAR.PASSAT_NMS: VWCarInfo("Volkswagen Passat NMS 2017-22", harness=Harness.j533),
CAR.POLO_MK6: [
VWCarInfo("Volkswagen Polo 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
VWCarInfo("Volkswagen Polo GTI 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
@@ -177,7 +224,7 @@ class VWCarInfo(CarInfo):
CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"),
CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"),
CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.KAMIQ]),
- CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21"),
+ CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21", footnotes=[Footnote.VW_HARNESS]),
CAR.SKODA_KODIAQ_MK1: VWCarInfo("Škoda Kodiaq 2018-19"),
CAR.SKODA_SCALA_MK1: VWCarInfo("Škoda Scala 2020"),
CAR.SKODA_SUPERB_MK3: VWCarInfo("Škoda Superb 2015-18"),
@@ -507,6 +554,26 @@ class VWCarInfo(CarInfo):
b'\xf1\x875Q0907572R \xf1\x890771',
],
},
+ CAR.PASSAT_NMS: {
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x8706K906016C \xf1\x899609',
+ b'\xf1\x8706K906016G \xf1\x891124',
+ b'\xf1\x8706K906071BJ\xf1\x894891',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x8709G927158AB\xf1\x893318',
+ b'\xf1\x8709G927158BD\xf1\x893121',
+ b'\xf1\x8709G927158FQ\xf1\x893745',
+ ],
+ (Ecu.srs, 0x715, None): [
+ b'\xf1\x87561959655 \xf1\x890210\xf1\x82\02212121111113000102011--121012--101312',
+ b'\xf1\x87561959655C \xf1\x890508\xf1\x82\02215141111121100314919--153015--304831',
+ ],
+ (Ecu.fwdRadar, 0x757, None): [
+ b'\xf1\x87561907567A \xf1\x890132',
+ b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\00152',
+ ],
+ },
CAR.POLO_MK6: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704C906025H \xf1\x895177',
diff --git a/selfdrive/car/volkswagen/volkswagencan.py b/selfdrive/car/volkswagen/volkswagencan.py
deleted file mode 100644
index f4c21eeed4410a..00000000000000
--- a/selfdrive/car/volkswagen/volkswagencan.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# CAN controls for MQB platform Volkswagen, Audi, Skoda, and SEAT.
-# PQ35/PQ46/NMS, and any future MLB, to come later.
-
-def create_mqb_steering_control(packer, bus, apply_steer, lkas_enabled):
- values = {
- "SET_ME_0X3": 0x3,
- "Assist_Torque": abs(apply_steer),
- "Assist_Requested": lkas_enabled,
- "Assist_VZ": 1 if apply_steer < 0 else 0,
- "HCA_Available": 1,
- "HCA_Standby": not lkas_enabled,
- "HCA_Active": lkas_enabled,
- "SET_ME_0XFE": 0xFE,
- "SET_ME_0X07": 0x07,
- }
- return packer.make_can_msg("HCA_01", bus, values)
-
-def create_mqb_hud_control(packer, bus, enabled, steering_pressed, hud_alert, left_lane_visible, right_lane_visible,
- ldw_stock_values, left_lane_depart, right_lane_depart):
- # Lane color reference:
- # 0 (LKAS disabled) - off
- # 1 (LKAS enabled, no lane detected) - dark gray
- # 2 (LKAS enabled, lane detected) - light gray on VW, green or white on Audi depending on year or virtual cockpit. On a color MFD on a 2015 A3 TDI it is white, virtual cockpit on a 2018 A3 e-Tron its green.
- # 3 (LKAS enabled, lane departure detected) - white on VW, red on Audi
- values = ldw_stock_values.copy()
- values.update({
- "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0,
- "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0,
- "LDW_Lernmodus_links": 3 if left_lane_depart else 1 + left_lane_visible,
- "LDW_Lernmodus_rechts": 3 if right_lane_depart else 1 + right_lane_visible,
- "LDW_Texte": hud_alert,
- })
- return packer.make_can_msg("LDW_02", bus, values)
-
-def create_mqb_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False):
- values = gra_stock_values.copy()
-
- values["COUNTER"] = idx
- values["GRA_Abbrechen"] = cancel
- values["GRA_Tip_Wiederaufnahme"] = resume
-
- return packer.make_can_msg("GRA_ACC_01", bus, values)
diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py
index 2f7c85aa7d3324..1f394d487ada59 100755
--- a/selfdrive/controls/controlsd.py
+++ b/selfdrive/controls/controlsd.py
@@ -16,7 +16,7 @@
from selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can
from selfdrive.controls.lib.lane_planner import CAMERA_OFFSET
-from selfdrive.controls.lib.drive_helpers import update_v_cruise, initialize_v_cruise
+from selfdrive.controls.lib.drive_helpers import V_CRUISE_INITIAL, update_v_cruise, initialize_v_cruise
from selfdrive.controls.lib.drive_helpers import get_lag_adjusted_curvature
from selfdrive.controls.lib.latcontrol import LatControl
from selfdrive.controls.lib.longcontrol import LongControl
@@ -50,6 +50,7 @@
LaneChangeDirection = log.LateralPlan.LaneChangeDirection
EventName = car.CarEvent.EventName
ButtonEvent = car.CarState.ButtonEvent
+ButtonType = car.CarState.ButtonEvent.Type
SafetyModel = car.CarParams.SafetyModel
IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
@@ -162,8 +163,8 @@ def __init__(self, sm=None, pm=None, can_sock=None, CI=None):
self.active = False
self.can_rcv_error = False
self.soft_disable_timer = 0
- self.v_cruise_kph = 255
- self.v_cruise_cluster_kph = 255
+ self.v_cruise_kph = V_CRUISE_INITIAL
+ self.v_cruise_cluster_kph = V_CRUISE_INITIAL
self.v_cruise_kph_last = 0
self.mismatch_counter = 0
self.cruise_mismatch_counter = 0
@@ -203,6 +204,16 @@ def __init__(self, sm=None, pm=None, can_sock=None, CI=None):
self.rk = Ratekeeper(100, print_delay_threshold=None)
self.prof = Profiler(False) # off by default
+ def set_initial_state(self):
+ if REPLAY:
+ controls_state = Params().get("ReplayControlsState")
+ if controls_state is not None:
+ controls_state = log.ControlsState.from_bytes(controls_state)
+ self.v_cruise_kph = controls_state.vCruise
+
+ if self.sm['pandaStates'][0].controlsAllowed:
+ self.state = State.enabled
+
def update_events(self, CS):
"""Compute carEvents from carState"""
@@ -218,6 +229,11 @@ def update_events(self, CS):
self.events.add(EventName.controlsInitializing)
return
+ # Block resume if cruise never previously enabled
+ resume_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in CS.buttonEvents)
+ if not self.CP.pcmCruise and self.v_cruise_kph == V_CRUISE_INITIAL and resume_pressed:
+ self.events.add(EventName.resumeBlocked)
+
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
if (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator) or \
(CS.brakePressed and (not self.CS_prev.brakePressed or not CS.standstill)):
@@ -417,11 +433,9 @@ def data_sample(self):
if all_valid or timed_out or SIMULATION:
if not self.read_only:
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
- self.initialized = True
-
- if REPLAY and self.sm['pandaStates'][0].controlsAllowed:
- self.state = State.enabled
+ self.initialized = True
+ self.set_initial_state()
Params().put_bool("ControlsReady", True)
# Check for CAN timeout
@@ -452,18 +466,18 @@ def state_transition(self, CS):
self.v_cruise_kph_last = self.v_cruise_kph
- # if stock cruise is completely disabled, then we can use our own set speed logic
- if not self.CP.pcmCruise:
- self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.vEgo, CS.gasPressed, CS.buttonEvents,
- self.button_timers, self.enabled, self.is_metric)
- self.v_cruise_cluster_kph = self.v_cruise_kph
- else:
- if CS.cruiseState.available:
+ if CS.cruiseState.available:
+ # if stock cruise is completely disabled, then we can use our own set speed logic
+ if not self.CP.pcmCruise:
+ self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.vEgo, CS.gasPressed, CS.buttonEvents,
+ self.button_timers, self.enabled, self.is_metric)
+ self.v_cruise_cluster_kph = self.v_cruise_kph
+ else:
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH
- else:
- self.v_cruise_kph = 0
- self.v_cruise_cluster_kph = 0
+ else:
+ self.v_cruise_kph = V_CRUISE_INITIAL
+ self.v_cruise_cluster_kph = V_CRUISE_INITIAL
# decrement the soft disable timer at every step, as it's reset on
# entrance in SOFT_DISABLING state
@@ -626,8 +640,13 @@ def state_control(self, CS):
if len(dpath_points):
# Check if we deviated from the path
# TODO use desired vs actual curvature
- left_deviation = actuators.steer > 0 and dpath_points[0] < -0.20
- right_deviation = actuators.steer < 0 and dpath_points[0] > 0.20
+ if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
+ steering_value = actuators.steeringAngleDeg
+ else:
+ steering_value = actuators.steer
+
+ left_deviation = steering_value > 0 and dpath_points[0] < -0.20
+ right_deviation = steering_value < 0 and dpath_points[0] > 0.20
if left_deviation or right_deviation:
self.events.add(EventName.steerSaturated)
diff --git a/selfdrive/controls/lib/desire_helper.py b/selfdrive/controls/lib/desire_helper.py
index 978c386530aa24..19ab2da214caef 100644
--- a/selfdrive/controls/lib/desire_helper.py
+++ b/selfdrive/controls/lib/desire_helper.py
@@ -40,12 +40,12 @@ def __init__(self):
self.prev_one_blinker = False
self.desire = log.LateralPlan.Desire.none
- def update(self, carstate, active, lane_change_prob):
+ def update(self, carstate, lateral_active, lane_change_prob):
v_ego = carstate.vEgo
one_blinker = carstate.leftBlinker != carstate.rightBlinker
below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN
- if not active or self.lane_change_timer > LANE_CHANGE_TIME_MAX:
+ if not lateral_active or self.lane_change_timer > LANE_CHANGE_TIME_MAX:
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
else:
diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py
index d79f94bbfdad82..ffa83738344825 100644
--- a/selfdrive/controls/lib/drive_helpers.py
+++ b/selfdrive/controls/lib/drive_helpers.py
@@ -11,6 +11,7 @@
V_CRUISE_MAX = 145 # kph
V_CRUISE_MIN = 8 # kph
V_CRUISE_ENABLE_MIN = 40 # kph
+V_CRUISE_INITIAL = 255 # kph
LAT_MPC_N = 16
LON_MPC_N = 32
diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py
index de666fd507c975..5139ead84bf195 100644
--- a/selfdrive/controls/lib/events.py
+++ b/selfdrive/controls/lib/events.py
@@ -635,6 +635,10 @@ def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
ET.NO_ENTRY: wrong_car_mode_alert,
},
+ EventName.resumeBlocked: {
+ ET.NO_ENTRY: NoEntryAlert("Press Set to Engage"),
+ },
+
EventName.wrongCruiseMode: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: NoEntryAlert("Adaptive Cruise Disabled"),
diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
index a1037f040bfaf3..c0e7358160fd0c 100755
--- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
+++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
@@ -89,7 +89,7 @@ def gen_lat_ocp():
# TODO hacky weights to keep behavior the same
ocp.model.cost_y_expr = vertcat(y_ego,
((v_ego +5.0) * psi_ego),
- ((v_ego +5.0) * 4 * curv_rate))
+ ((v_ego + 5.0) * 4.0 * curv_rate))
ocp.model.cost_y_expr_e = vertcat(y_ego,
((v_ego +5.0) * psi_ego))
@@ -147,7 +147,7 @@ def set_weights(self, path_weight, heading_weight, steer_rate_weight):
#TODO hacky weights to keep behavior the same
self.solver.cost_set(N, 'W', (3/20.)*W[:2,:2])
- def run(self, x0, p, y_pts, heading_pts):
+ def run(self, x0, p, y_pts, heading_pts, curv_rate_pts):
x0_cp = np.copy(x0)
p_cp = np.copy(p)
self.solver.constraints_set(0, "lbx", x0_cp)
@@ -156,6 +156,7 @@ def run(self, x0, p, y_pts, heading_pts):
v_ego = p_cp[0]
# rotation_radius = p_cp[1]
self.yref[:,1] = heading_pts*(v_ego+5.0)
+ self.yref[:,2] = curv_rate_pts * (v_ego+5.0) * 4.0
for i in range(N):
self.solver.cost_set(i, "yref", self.yref[i])
self.solver.set(i, "p", p_cp)
diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py
index 019a19fb1dfe66..29dfc771111c92 100644
--- a/selfdrive/controls/lib/lateral_planner.py
+++ b/selfdrive/controls/lib/lateral_planner.py
@@ -3,7 +3,7 @@
from common.numpy_fast import interp
from system.swaglog import cloudlog
from selfdrive.controls.lib.lateral_mpc_lib.lat_mpc import LateralMpc
-from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N, CAR_ROTATION_RADIUS
+from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N
from selfdrive.controls.lib.lane_planner import LanePlanner, TRAJECTORY_SIZE
from selfdrive.controls.lib.desire_helper import DesireHelper
import cereal.messaging as messaging
@@ -11,17 +11,21 @@
class LateralPlanner:
- def __init__(self, use_lanelines=True, wide_camera=False):
+ def __init__(self, CP, use_lanelines=True, wide_camera=False):
self.use_lanelines = use_lanelines
self.LP = LanePlanner(wide_camera)
self.DH = DesireHelper()
+ # Vehicle model parameters used to calculate lateral movement of car
+ self.factor1 = CP.wheelbase - CP.centerToFront
+ self.factor2 = (CP.centerToFront * CP.mass) / (CP.wheelbase * CP.tireStiffnessRear)
self.last_cloudlog_t = 0
self.solution_invalid_cnt = 0
self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3))
self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3))
self.plan_yaw = np.zeros((TRAJECTORY_SIZE,))
+ self.plan_curv_rate = np.zeros((TRAJECTORY_SIZE,))
self.t_idxs = np.arange(TRAJECTORY_SIZE)
self.y_pts = np.zeros(TRAJECTORY_SIZE)
@@ -42,13 +46,13 @@ def update(self, sm):
if len(md.position.x) == TRAJECTORY_SIZE and len(md.orientation.x) == TRAJECTORY_SIZE:
self.path_xyz = np.column_stack([md.position.x, md.position.y, md.position.z])
self.t_idxs = np.array(md.position.t)
- self.plan_yaw = list(md.orientation.z)
+ self.plan_yaw = np.array(md.orientation.z)
if len(md.position.xStd) == TRAJECTORY_SIZE:
self.path_xyz_stds = np.column_stack([md.position.xStd, md.position.yStd, md.position.zStd])
# Lane change logic
lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob
- self.DH.update(sm['carState'], sm['controlsState'].active, lane_change_prob)
+ self.DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob)
# Turn off lanes during lane change
if self.DH.desire == log.LateralPlan.Desire.laneChangeRight or self.DH.desire == log.LateralPlan.Desire.laneChangeLeft:
@@ -67,16 +71,19 @@ def update(self, sm):
y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1])
heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw)
+ curv_rate_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_curv_rate)
self.y_pts = y_pts
assert len(y_pts) == LAT_MPC_N + 1
assert len(heading_pts) == LAT_MPC_N + 1
- # self.x0[4] = v_ego
- p = np.array([v_ego, CAR_ROTATION_RADIUS])
+ assert len(curv_rate_pts) == LAT_MPC_N + 1
+ lateral_factor = max(0, self.factor1 - (self.factor2 * v_ego**2))
+ p = np.array([v_ego, lateral_factor])
self.lat_mpc.run(self.x0,
p,
y_pts,
- heading_pts)
+ heading_pts,
+ curv_rate_pts)
# init state for next
# mpc.u_sol is the desired curvature rate given x0 curv state.
# with x0[3] = measured_curvature, this would be the actual desired rate.
diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py
index 9356a55d8433f8..ca7523f2eeffe3 100755
--- a/selfdrive/controls/plannerd.py
+++ b/selfdrive/controls/plannerd.py
@@ -22,10 +22,10 @@ def plannerd_thread(sm=None, pm=None):
cloudlog.event("e2e mode", on=use_lanelines)
longitudinal_planner = Planner(CP)
- lateral_planner = LateralPlanner(use_lanelines=use_lanelines, wide_camera=wide_camera)
+ lateral_planner = LateralPlanner(CP, use_lanelines=use_lanelines, wide_camera=wide_camera)
if sm is None:
- sm = messaging.SubMaster(['carState', 'controlsState', 'radarState', 'modelV2'],
+ sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2'],
poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState'])
if pm is None:
diff --git a/selfdrive/controls/tests/test_lateral_mpc.py b/selfdrive/controls/tests/test_lateral_mpc.py
index 4630e28a6154f5..4864dbdc068328 100644
--- a/selfdrive/controls/tests/test_lateral_mpc.py
+++ b/selfdrive/controls/tests/test_lateral_mpc.py
@@ -13,6 +13,7 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur
y_pts = poly_shift * np.ones(LAT_MPC_N + 1)
heading_pts = np.zeros(LAT_MPC_N + 1)
+ curv_rate_pts = np.zeros(LAT_MPC_N + 1)
x0 = np.array([x_init, y_init, psi_init, curvature_init])
p = np.array([v_ref, CAR_ROTATION_RADIUS])
@@ -20,7 +21,7 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur
# converge in no more than 10 iterations
for _ in range(10):
lat_mpc.run(x0, p,
- y_pts, heading_pts)
+ y_pts, heading_pts, curv_rate_pts)
return lat_mpc.x_sol
diff --git a/selfdrive/debug/test_car_model.py b/selfdrive/debug/test_car_model.py
index 9082dbe3d6b4e2..4de5b267628810 100755
--- a/selfdrive/debug/test_car_model.py
+++ b/selfdrive/debug/test_car_model.py
@@ -4,11 +4,11 @@
from typing import List, Tuple
import unittest
-from selfdrive.car.tests.routes import TestRoute
+from selfdrive.car.tests.routes import CarTestRoute
from selfdrive.car.tests.test_models import TestCarModel
-def create_test_models_suite(routes: List[Tuple[str, TestRoute]], ci=False) -> unittest.TestSuite:
+def create_test_models_suite(routes: List[Tuple[str, CarTestRoute]], ci=False) -> unittest.TestSuite:
test_suite = unittest.TestSuite()
for car_model, test_route in routes:
# create new test case and discover tests
@@ -30,7 +30,7 @@ def create_test_models_suite(routes: List[Tuple[str, TestRoute]], ci=False) -> u
parser.print_help()
sys.exit()
- test_route = TestRoute(args.route, args.car, segment=args.segment)
+ test_route = CarTestRoute(args.route, args.car, segment=args.segment)
test_suite = create_test_models_suite([(args.car, test_route)], ci=args.ci)
unittest.TextTestRunner().run(test_suite)
diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py
index 0cc674cfc8220f..868813b0feba29 100755
--- a/selfdrive/debug/test_fw_query_on_routes.py
+++ b/selfdrive/debug/test_fw_query_on_routes.py
@@ -10,7 +10,7 @@
from tools.lib.route import Route
from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.car_helpers import interface_names
-from selfdrive.car.fw_versions import match_fw_to_car_exact, match_fw_to_car_fuzzy, build_fw_dict
+from selfdrive.car.fw_versions import match_fw_to_car
NO_API = "NO_API" in os.environ
@@ -89,9 +89,8 @@
print("not in supported cars")
break
- fw_versions_dict = build_fw_dict(car_fw)
- exact_matches = match_fw_to_car_exact(fw_versions_dict)
- fuzzy_matches = match_fw_to_car_fuzzy(fw_versions_dict)
+ _, exact_matches = match_fw_to_car(car_fw, allow_exact=True, allow_fuzzy=False)
+ _, fuzzy_matches = match_fw_to_car(car_fw, allow_exact=False, allow_fuzzy=True)
if (len(exact_matches) == 1) and (list(exact_matches)[0] == live_fingerprint):
good_exact += 1
diff --git a/selfdrive/loggerd/bootlog.cc b/selfdrive/loggerd/bootlog.cc
index 882b749fe9d7b6..90ba487ff0984e 100644
--- a/selfdrive/loggerd/bootlog.cc
+++ b/selfdrive/loggerd/bootlog.cc
@@ -7,12 +7,6 @@
static kj::Array build_boot_log() {
- std::vector bootlog_commands;
- if (Hardware::AGNOS()) {
- bootlog_commands.push_back("journalctl");
- bootlog_commands.push_back("sudo nvme smart-log --output-format=json /dev/nvme0");
- }
-
MessageBuilder msg;
auto boot = msg.initEvent().initBoot();
@@ -31,17 +25,19 @@ static kj::Array build_boot_log() {
}
// Gather output of commands
- i = 0;
+ std::vector bootlog_commands = {
+ "[ -e /dev/nvme0 ] && sudo nvme smart-log --output-format=json /dev/nvme0",
+ "[ -x \"$(command -v journalctl)\" ] && journalctl",
+ };
+
auto commands = boot.initCommands().initEntries(bootlog_commands.size());
- for (auto &command : bootlog_commands) {
- auto lentry = commands[i];
+ for (int j = 0; j < bootlog_commands.size(); j++) {
+ auto lentry = commands[j];
- lentry.setKey(command);
+ lentry.setKey(bootlog_commands[j]);
- const std::string result = util::check_output(command);
+ const std::string result = util::check_output(bootlog_commands[j]);
lentry.setValue(capnp::Data::Reader((const kj::byte*)result.data(), result.size()));
-
- i++;
}
boot.setLaunchLog(util::read_file("/tmp/launch_log"));
diff --git a/selfdrive/loggerd/encoder/v4l_encoder.cc b/selfdrive/loggerd/encoder/v4l_encoder.cc
index b3bd692b1639bd..016e6c57063f73 100644
--- a/selfdrive/loggerd/encoder/v4l_encoder.cc
+++ b/selfdrive/loggerd/encoder/v4l_encoder.cc
@@ -303,4 +303,10 @@ V4LEncoder::~V4LEncoder() {
checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
close(fd);
+
+ for (int i = 0; i < BUF_OUT_COUNT; i++) {
+ if (buf_out[i].free() != 0) {
+ LOGE("Failed to free buffer");
+ }
+ }
}
diff --git a/selfdrive/loggerd/logger.cc b/selfdrive/loggerd/logger.cc
index 8038f1926c8d47..da3710cc72a69b 100644
--- a/selfdrive/loggerd/logger.cc
+++ b/selfdrive/loggerd/logger.cc
@@ -24,9 +24,11 @@ kj::Array logger_build_init_data() {
MessageBuilder msg;
auto init = msg.initEvent().initInitData();
- init.setDeviceType(Hardware::get_device_type());
init.setVersion(COMMA_VERSION);
+ init.setDirty(!getenv("CLEAN"));
+ init.setDeviceType(Hardware::get_device_type());
+ // log kernel args
std::ifstream cmdline_stream("/proc/cmdline");
std::vector kernel_args;
std::string buf;
@@ -42,8 +44,6 @@ kj::Array logger_build_init_data() {
init.setKernelVersion(util::read_file("/proc/version"));
init.setOsVersion(util::read_file("/VERSION"));
- init.setDirty(!getenv("CLEAN"));
-
// log params
auto params = Params();
std::map params_map = params.readAll();
@@ -55,16 +55,31 @@ kj::Array logger_build_init_data() {
init.setDongleId(params_map["DongleId"]);
auto lparams = init.initParams().initEntries(params_map.size());
- int i = 0;
+ int j = 0;
for (auto& [key, value] : params_map) {
- auto lentry = lparams[i];
+ auto lentry = lparams[j];
lentry.setKey(key);
if ( !(params.getKeyType(key) & DONT_LOG) ) {
lentry.setValue(capnp::Data::Reader((const kj::byte*)value.data(), value.size()));
}
- i++;
+ j++;
+ }
+
+ // log commands
+ std::vector log_commands = {
+ "df -h", // usage for all filesystems
+ };
+
+ auto commands = init.initCommands().initEntries(log_commands.size());
+ for (int i = 0; i < log_commands.size(); i++) {
+ auto lentry = commands[i];
+ lentry.setKey(log_commands[i]);
+
+ const std::string result = util::check_output(log_commands[i]);
+ lentry.setValue(capnp::Data::Reader((const kj::byte*)result.data(), result.size()));
}
+
return capnp::messageToFlatArray(msg);
}
diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py
index dec51966a41dc8..927de563c5cb6f 100644
--- a/selfdrive/manager/process_config.py
+++ b/selfdrive/manager/process_config.py
@@ -2,7 +2,7 @@
from cereal import car
from common.params import Params
-from system.hardware import PC
+from system.hardware import PC, TICI
from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess
WEBCAM = os.getenv("USE_WEBCAM") is not None
@@ -57,7 +57,7 @@ def logging(started, params, CP: car.CarParams) -> bool:
PythonProcess("webjoystick", "tools.joystick.web", onroad=False, callback=notcar),
# Experimental
- PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=os.path.isfile("/persist/comma/use-quectel-rawgps")),
+ PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=(TICI and os.path.isfile("/persist/comma/use-quectel-rawgps"))),
]
managed_processes = {p.name: p for p in procs}
diff --git a/selfdrive/modeld/models/supercombo.dlc b/selfdrive/modeld/models/supercombo.dlc
index 23f6d904fb238b..fe133523fc20b8 100644
--- a/selfdrive/modeld/models/supercombo.dlc
+++ b/selfdrive/modeld/models/supercombo.dlc
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:027cbb1fabae369878271cb0e3505071a8bdaa07473fad9a0b2e8d695c5dc1ff
-size 76725611
+oid sha256:93d265fc88f05746ce47257e15fc2afe43b250b44715313049f829e8aa30a9d6
+size 94302331
diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx
index 9023c18dd7f970..7b11edbe0800ad 100644
--- a/selfdrive/modeld/models/supercombo.onnx
+++ b/selfdrive/modeld/models/supercombo.onnx
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:484976ea5bd4ddcabc82e95faf30d7311a27802c1e337472558699fa2395a499
-size 77472267
+oid sha256:50c7fc8565ac69a4b9a0de122e961326820e78bf13659255a89d0ed04be030d5
+size 95167481
diff --git a/selfdrive/modeld/thneed/optimizer.cc b/selfdrive/modeld/thneed/optimizer.cc
index 03d20ff3861332..39737d3d7623ba 100644
--- a/selfdrive/modeld/thneed/optimizer.cc
+++ b/selfdrive/modeld/thneed/optimizer.cc
@@ -9,7 +9,7 @@
extern map g_program_source;
-static int is_same_size_image(cl_mem a, cl_mem b) {
+/*static int is_same_size_image(cl_mem a, cl_mem b) {
size_t a_width, a_height, a_depth, a_array_size, a_row_pitch, a_slice_pitch;
clGetImageInfo(a, CL_IMAGE_WIDTH, sizeof(a_width), &a_width, NULL);
clGetImageInfo(a, CL_IMAGE_HEIGHT, sizeof(a_height), &a_height, NULL);
@@ -29,7 +29,7 @@ static int is_same_size_image(cl_mem a, cl_mem b) {
return (a_width == b_width) && (a_height == b_height) &&
(a_depth == b_depth) && (a_array_size == b_array_size) &&
(a_row_pitch == b_row_pitch) && (a_slice_pitch == b_slice_pitch);
-}
+}*/
static cl_mem make_image_like(cl_context context, cl_mem val) {
cl_image_format format;
@@ -138,7 +138,7 @@ int Thneed::optimize() {
// delete useless copy layers
// saves ~0.7 ms
- if (kq[i]->name == "concatenation" || kq[i]->name == "flatten") {
+ /*if (kq[i]->name == "concatenation" || kq[i]->name == "flatten") {
string in = kq[i]->args[kq[i]->get_arg_num("input")];
string out = kq[i]->args[kq[i]->get_arg_num("output")];
if (is_same_size_image(*(cl_mem*)in.data(), *(cl_mem*)out.data())) {
@@ -148,7 +148,7 @@ int Thneed::optimize() {
kq.erase(kq.begin()+i); --i;
}
- }
+ }*/
// NOTE: if activations/accumulation are done in the wrong order, this will be wrong
diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py
index 8b41d6df1b66ba..d2090aa76aa625 100755
--- a/selfdrive/test/process_replay/model_replay.py
+++ b/selfdrive/test/process_replay/model_replay.py
@@ -22,7 +22,7 @@
TEST_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"
SEGMENT = 0
-MAX_FRAMES = 20 if PC else 1300
+MAX_FRAMES = 10 if PC else 1300
SEND_EXTRA_INPUTS = bool(os.getenv("SEND_EXTRA_INPUTS", "0"))
@@ -176,7 +176,7 @@ def model_replay(lr, frs):
# TODO this tolerence is absurdly large
tolerance = 5e-1 if PC else None
results: Any = {TEST_ROUTE: {}}
- log_paths: Any = {TEST_ROUTE: {'ref': BASE_URL + log_fn, 'new': log_fn}}
+ log_paths: Any = {TEST_ROUTE: {"models": {'ref': BASE_URL + log_fn, 'new': log_fn}}}
results[TEST_ROUTE]["models"] = compare_logs(cmp_log, log_msgs, tolerance=tolerance, ignore_fields=ignore)
diff1, diff2, failed = format_diff(results, log_paths, ref_commit)
diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit
index 8b9483419308ca..80be9b464f5ca5 100644
--- a/selfdrive/test/process_replay/model_replay_ref_commit
+++ b/selfdrive/test/process_replay/model_replay_ref_commit
@@ -1 +1 @@
-5c2cb8fb9787584a1eb553968cb87e7e6782dac5
+ca90e11f8d59902af38d3785ddd91a27d0fbb411
diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py
index 0c642cde178999..8b9c77625f9ba1 100755
--- a/selfdrive/test/process_replay/process_replay.py
+++ b/selfdrive/test/process_replay/process_replay.py
@@ -280,7 +280,7 @@ def laika_rcv_callback(msg, CP, cfg, fsm):
proc_name="plannerd",
pub_sub={
"modelV2": ["lateralPlan", "longitudinalPlan"],
- "carState": [], "controlsState": [], "radarState": [],
+ "carControl": [], "carState": [], "controlsState": [], "radarState": [],
},
ignore=["logMonoTime", "valid", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime", "lateralPlan.solverExecutionTime"],
init_callback=get_car_params,
@@ -385,7 +385,7 @@ def replay_process(cfg, lr, fingerprint=None):
return cpp_replay_process(cfg, lr, fingerprint)
-def setup_env(simulation=False, CP=None, cfg=None):
+def setup_env(simulation=False, CP=None, cfg=None, controlsState=None):
params = Params()
params.clear_all()
params.put_bool("OpenpilotEnabledToggle", True)
@@ -414,6 +414,12 @@ def setup_env(simulation=False, CP=None, cfg=None):
elif "SIMULATION" in os.environ:
del os.environ["SIMULATION"]
+ # Initialize controlsd with a controlsState packet
+ if controlsState is not None:
+ params.put("ReplayControlsState", controlsState.as_builder().to_bytes())
+ else:
+ params.delete("ReplayControlsState")
+
# Regen or python process
if CP is not None:
if CP.alternativeExperience == ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS:
@@ -440,13 +446,25 @@ def python_replay_process(cfg, lr, fingerprint=None):
all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime)
pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())]
+ controlsState = None
+ initialized = False
+ for msg in lr:
+ if msg.which() == 'controlsState':
+ controlsState = msg.controlsState
+ if initialized:
+ break
+ elif msg.which() == 'carEvents':
+ initialized = car.CarEvent.EventName.controlsInitializing not in [e.name for e in msg.carEvents]
+
+ assert controlsState is not None and initialized, "controlsState never initialized"
+
if fingerprint is not None:
os.environ['SKIP_FW_QUERY'] = "1"
os.environ['FINGERPRINT'] = fingerprint
- setup_env(cfg=cfg)
+ setup_env(cfg=cfg, controlsState=controlsState)
else:
CP = [m for m in lr if m.which() == 'carParams'][0].carParams
- setup_env(CP=CP, cfg=cfg)
+ setup_env(CP=CP, cfg=cfg, controlsState=controlsState)
assert(type(managed_processes[cfg.proc_name]) is PythonProcess)
managed_processes[cfg.proc_name].prepare()
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index 27bea557408bef..f77f244594f725 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-c556add24a92c9d8593d9d3ebbeed9434b751d66
\ No newline at end of file
+656daeb9de3680258527500ecae4ddff323b2e59
\ No newline at end of file
diff --git a/selfdrive/test/process_replay/regen.py b/selfdrive/test/process_replay/regen.py
index 39f75ce428aac6..4e1cbfd30d8684 100755
--- a/selfdrive/test/process_replay/regen.py
+++ b/selfdrive/test/process_replay/regen.py
@@ -201,14 +201,15 @@ def regen_segment(lr, frs=None, outdir=FAKEDATA, disable_tqdm=False):
params = Params()
os.environ["LOG_ROOT"] = outdir
- for msg in lr:
- if msg.which() == 'carParams':
- setup_env(CP=msg.carParams)
- elif msg.which() == 'liveCalibration':
- params.put("CalibrationParams", msg.as_builder().to_bytes())
+ # Get and setup initial state
+ CP = [m for m in lr if m.which() == 'carParams'][0].carParams
+ controlsState = [m for m in lr if m.which() == 'controlsState'][0].controlsState
+ liveCalibration = [m for m in lr if m.which() == 'liveCalibration'][0]
- vs, cam_procs = replay_cameras(lr, frs, disable_tqdm=disable_tqdm)
+ setup_env(CP=CP, controlsState=controlsState)
+ params.put("CalibrationParams", liveCalibration.as_builder().to_bytes())
+ vs, cam_procs = replay_cameras(lr, frs, disable_tqdm=disable_tqdm)
fake_daemons = {
'sensord': [
multiprocessing.Process(target=replay_sensor_events, args=('sensorEvents', lr)),
diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py
index 9e6fee0de0c26f..e8c2e1dc9494e6 100755
--- a/selfdrive/test/process_replay/test_processes.py
+++ b/selfdrive/test/process_replay/test_processes.py
@@ -18,7 +18,7 @@
original_segments = [
("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY
("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA
- ("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"), # HYUNDAI.KIA_EV6
+ ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"), # HYUNDAI.KIA_EV6
("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI)
("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR)
("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2
@@ -32,14 +32,15 @@
("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.GOLF
("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--2"), # MAZDA.CX9_2021
- # Enable when port is tested and dascamOnly is no longer set
+ # Enable when port is tested and dashcamOnly is no longer set
#("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS
+ #("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.PASSAT_NMS
]
segments = [
("BODY", "regen660D86654BA|2022-07-06--14-27-15--0"),
("HYUNDAI", "regen114E5FF24D8|2022-07-14--17-08-47--0"),
- ("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"),
+ ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"),
("TOYOTA", "regenBA97410FBEC|2022-07-06--14-26-49--0"),
("TOYOTA2", "regenDEDB1D9C991|2022-07-06--14-54-08--0"),
("TOYOTA3", "regenDDC1FE60734|2022-07-06--14-32-06--0"),
@@ -48,7 +49,7 @@
("CHRYSLER", "regen38346FB33D0|2022-07-14--18-05-26--0"),
("RAM", "2f4452b03ccb98f0|2022-07-07--08-01-56--3"),
("SUBARU", "regen54A1E2BE5AA|2022-07-14--18-07-50--0"),
- ("GM", "regen01D09D915B5|2022-07-06--14-36-20--0"),
+ ("GM", "regen76027B408B7|2022-08-16--19-56-58--0"),
("NISSAN", "regenCA0B0DC946E|2022-07-14--18-10-17--0"),
("VOLKSWAGEN", "regen007098CA0EF|2022-07-06--15-01-26--0"),
("MAZDA", "regen61BA413D53B|2022-07-06--14-39-42--0"),
@@ -117,19 +118,19 @@ def format_diff(results, log_paths, ref_commit):
for proc, diff in list(result.items()):
# long diff
diff2 += f"*** process: {proc} ***\n"
- diff2 += f"\tref: {log_paths[segment]['ref']}\n\n"
- diff2 += f"\tnew: {log_paths[segment]['new']}\n\n"
+ diff2 += f"\tref: {log_paths[segment][proc]['ref']}\n"
+ diff2 += f"\tnew: {log_paths[segment][proc]['new']}\n\n"
# short diff
diff1 += f" {proc}\n"
if isinstance(diff, str):
- diff1 += f" ref: {log_paths[segment]['ref']}\n"
- diff1 += f" new: {log_paths[segment]['new']}\n\n"
+ diff1 += f" ref: {log_paths[segment][proc]['ref']}\n"
+ diff1 += f" new: {log_paths[segment][proc]['new']}\n\n"
diff1 += f" {diff}\n"
failed = True
elif len(diff):
- diff1 += f" ref: {log_paths[segment]['ref']}\n"
- diff1 += f" new: {log_paths[segment]['new']}\n\n"
+ diff1 += f" ref: {log_paths[segment][proc]['ref']}\n"
+ diff1 += f" new: {log_paths[segment][proc]['new']}\n\n"
cnt: Dict[str, int] = {}
for d in diff:
@@ -196,8 +197,7 @@ def format_diff(results, log_paths, ref_commit):
untested = (set(interface_names) - set(excluded_interfaces)) - {c.lower() for c in tested_cars}
assert len(untested) == 0, f"Cars missing routes: {str(untested)}"
-
- log_paths: DefaultDict[str, Dict[str, str]] = defaultdict(dict)
+ log_paths: DefaultDict[str, Dict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict))
with concurrent.futures.ProcessPoolExecutor(max_workers=args.jobs) as pool:
if not args.upload_only:
download_segments = [seg for car, seg in segments if car in tested_cars]
@@ -225,8 +225,8 @@ def format_diff(results, log_paths, ref_commit):
dat = None if args.upload_only else log_data[segment]
pool_args.append((segment, cfg, args, cur_log_fn, ref_log_path, dat))
- log_paths[segment]['ref'] = ref_log_path
- log_paths[segment]['new'] = cur_log_fn
+ log_paths[segment][cfg.proc_name + cfg.subtest_name]['ref'] = ref_log_path
+ log_paths[segment][cfg.proc_name + cfg.subtest_name]['new'] = cur_log_fn
results: Any = defaultdict(dict)
p2 = pool.map(run_test_process, pool_args)
diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py
index 31651706c698a5..93598a1b066c4f 100755
--- a/selfdrive/test/test_onroad.py
+++ b/selfdrive/test/test_onroad.py
@@ -37,7 +37,7 @@
"selfdrive.thermald.thermald": 3.87,
"selfdrive.locationd.calibrationd": 2.0,
"./_soundd": 1.0,
- "selfdrive.monitoring.dmonitoringd": 1.90,
+ "selfdrive.monitoring.dmonitoringd": 4.0,
"./proclogd": 1.54,
"system.logmessaged": 0.2,
"./clocksd": 0.02,
diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc
index f81c3dd8f07f20..046814d517d3e2 100644
--- a/selfdrive/ui/qt/maps/map.cc
+++ b/selfdrive/ui/qt/maps/map.cc
@@ -361,7 +361,7 @@ void MapWindow::offroadTransition(bool offroad) {
}
MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) {
- is_rhd = Params().getBool("IsRHD");
+ is_rhd = Params().getBool("IsRhdDetected");
QHBoxLayout *main_layout = new QHBoxLayout(this);
main_layout->setContentsMargins(11, 50, 11, 11);
{
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index e6e21bd8d3a4a6..5e0e87e64d0deb 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -62,7 +62,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
#ifdef ENABLE_MAPS
{
"NavSettingTime24h",
- tr("Show ETA in 24h format"),
+ tr("Show ETA in 24h Format"),
tr("Use 24h format instead of am/pm"),
"../assets/offroad/icon_metric.png",
},
diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc
index accab67bf071eb..a84542d29107b6 100644
--- a/selfdrive/ui/qt/sidebar.cc
+++ b/selfdrive/ui/qt/sidebar.cc
@@ -33,7 +33,7 @@ void Sidebar::drawMetric(QPainter &p, const QPair &label, QCol
}
Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
- home_img = loadPixmap("../assets/images/button_home.png", {180, 180});
+ home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
connect(this, &Sidebar::valueChanged, [=] { update(); });
@@ -43,9 +43,16 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
setFixedWidth(300);
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
+
+ pm = std::make_unique>({"userFlag"});
}
void Sidebar::mouseReleaseEvent(QMouseEvent *event) {
+ if (home_btn.contains(event->pos())) {
+ MessageBuilder msg;
+ msg.initEvent().initUserFlag();
+ pm->send("userFlag", msg);
+ }
if (settings_btn.contains(event->pos())) {
emit openSettings();
}
@@ -99,7 +106,7 @@ void Sidebar::paintEvent(QPaintEvent *event) {
p.setOpacity(0.65);
p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img);
p.setOpacity(1.0);
- p.drawPixmap(60, 1080 - 180 - 40, home_img);
+ p.drawPixmap(home_btn.x(), home_btn.y(), home_img);
// network
int x = 58;
diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h
index 4c6d8f47e5bc35..0140673aac3fb1 100644
--- a/selfdrive/ui/qt/sidebar.h
+++ b/selfdrive/ui/qt/sidebar.h
@@ -43,6 +43,7 @@ public slots:
{cereal::DeviceState::NetworkType::CELL5_G, tr("5G")}
};
+ const QRect home_btn = QRect(60, 860, 180, 180);
const QRect settings_btn = QRect(50, 35, 200, 117);
const QColor good_color = QColor(255, 255, 255);
const QColor warning_color = QColor(218, 202, 37);
@@ -52,4 +53,7 @@ public slots:
ItemStatus connect_status, panda_status, temp_status;
QString net_type;
int net_strength = 0;
+
+private:
+ std::unique_ptr pm;
};
diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py
index 526dde93e85f75..fead288dfcad13 100755
--- a/selfdrive/ui/tests/test_translations.py
+++ b/selfdrive/ui/tests/test_translations.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
import json
import os
+import re
import shutil
import unittest
+import xml.etree.ElementTree as ET
from selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations
@@ -64,6 +66,30 @@ def test_vanished_translations(self):
self.assertTrue(b"" not in cur_translations,
f"{file} ({name}) translation file has obsolete translations. Run selfdrive/ui/update_translations.py --vanish to remove them")
+ def test_plural_translations(self):
+ """
+ Tests:
+ - that any numerus (plural) translations marked "finished" have all plural forms non-empty
+ - that the correct format specifier is used (%n)
+ """
+ for name, file in self.translation_files.items():
+ with self.subTest(name=name, file=file):
+ tr_xml = ET.parse(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"))
+
+ for context in tr_xml.getroot():
+ for message in context.iterfind("message"):
+ if message.get("numerus") == "yes":
+ translation = message.find("translation")
+ numerusform = [t.text for t in translation.findall("numerusform")]
+
+ # Do not assert finished translations
+ if translation.get("type") == "unfinished":
+ continue
+
+ self.assertNotIn(None, numerusform, "Ensure all plural translation forms are completed.")
+ self.assertTrue(all([re.search("%[0-9]+", t) is None for t in numerusform]),
+ "Plural translations must use %n, not %1, %2, etc.: {}".format(numerusform))
+
if __name__ == "__main__":
unittest.main()
diff --git a/selfdrive/ui/translations/README.md b/selfdrive/ui/translations/README.md
index 07f1a4825c3c64..91a6c3ca2f40c4 100644
--- a/selfdrive/ui/translations/README.md
+++ b/selfdrive/ui/translations/README.md
@@ -1,15 +1,19 @@
# Multilanguage
-[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_en.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_en.ts)
-[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHT.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHT.ts)
-[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHS.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHS.ts)
-[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ko.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ko.ts)
-[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ja.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ja.ts)
+[![languages](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge.svg)](#)
## Contributing
Before getting started, make sure you have set up the openpilot Ubuntu development environment by reading the [tools README.md](/tools/README.md).
+### Policy
+
+Most of the languages supported by openpilot come from and are maintained by the community via pull requests. A pull request likely to be merged is one that [fixes a translation or adds missing translations.](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/README.md#improving-an-existing-language)
+
+We also generally merge pull requests adding support for a new language if there are community members willing to maintain it. Maintaining a language is ensuring quality and completion of translations before each openpilot release.
+
+comma may remove or hide language support from releases depending on translation quality and completeness.
+
### Adding a New Language
openpilot provides a few tools to help contributors manage their translations and to ensure quality. To get started:
@@ -38,7 +42,7 @@ Any time you edit source code in the UI, you need to update the translations to
### Testing
-openpilot has a few unit tests to make sure all translations are up to date and that all strings are wrapped in a translation marker. They are run in CI, but you can also run them locally.
+openpilot has a few unit tests to make sure all translations are up-to-date and that all strings are wrapped in a translation marker. They are run in CI, but you can also run them locally.
Tests translation files up to date:
diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py
index d9e2d443b969d4..451fbb23eb12b3 100755
--- a/selfdrive/ui/translations/create_badges.py
+++ b/selfdrive/ui/translations/create_badges.py
@@ -8,20 +8,17 @@
TRANSLATION_TAG = "']
+ for idx, (name, file) in enumerate(translation_files.items()):
with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"), "r") as tr_f:
tr_file = tr_f.read()
- print(f"[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/{TRANSLATION_BADGE.format(file)})]({TRANSLATION_LINK.format(file)}.ts)")
-
total_translations = 0
unfinished_translations = 0
for line in tr_file.splitlines():
@@ -35,6 +32,16 @@
r = requests.get(f"https://img.shields.io/badge/LANGUAGE {name}-{percent_finished}%25 complete-{color}")
assert r.status_code == 200, "Error downloading badge"
+ content_svg = r.content.decode("utf-8")
+
+ # make tag ids in each badge unique
+ for tag in ("r", "s"):
+ content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"')
+ content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"')
+
+ badge_svg.extend([f'', content_svg, ""])
+
+ badge_svg.append("")
- with open(os.path.join(BASEDIR, TRANSLATION_BADGE.format(file)), "wb") as badge_f:
- badge_f.write(r.content)
+ with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f:
+ badge_f.write("\n".join(badge_svg))
diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json
index f52a28ef9a2294..2559b2d26d502b 100644
--- a/selfdrive/ui/translations/languages.json
+++ b/selfdrive/ui/translations/languages.json
@@ -1,5 +1,6 @@
{
"English": "main_en",
+ "Português": "main_pt",
"中文(繁體)": "main_zh-CHT",
"中文(简体)": "main_zh-CHS",
"한국어": "main_ko",
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index 20f96b8f4089be..dd9f933dcf75ff 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -876,71 +876,71 @@ location set
Sidebar
-
-
+
+
接続
-
+
オフライン
-
-
+
+
オンライン
-
+
エラー
-
-
-
+
+
+
温度
-
+
高温
-
+
最適
-
+
OK
-
+
車両
-
+
NO
-
+
PANDA
-
+
GPS
-
+
検索
@@ -1025,23 +1025,23 @@ location set
-
+ ブランチの切り替え
-
+ 切替
-
+ updater を実行する時にブランチを切り替えます。
-
+ ブランチ名を入力
@@ -1204,7 +1204,7 @@ location set
-
+
24時間表示
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index cd9c73c34d5810..d4abe861962c3a 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -876,71 +876,71 @@ location set
Sidebar
-
-
+
+
연결
-
+
오프라인
-
-
+
+
온라인
-
+
오류
-
-
-
+
+
+
온도
-
+
높음
-
+
좋음
-
+
경고
-
+
차량
-
+
NO
-
+
PANDA
-
+
GPS
-
+
검색중
@@ -1204,7 +1204,7 @@ location set
-
+
24시간 형식으로 도착예정시간 표시
diff --git a/selfdrive/ui/translations/main_pt.ts b/selfdrive/ui/translations/main_pt.ts
new file mode 100644
index 00000000000000..3d5eda118ce042
--- /dev/null
+++ b/selfdrive/ui/translations/main_pt.ts
@@ -0,0 +1,1307 @@
+
+
+
+
+ AbstractAlert
+
+
+
+ Fechar
+
+
+
+
+ Adiar Atualização
+
+
+
+
+ Reiniciar e Atualizar
+
+
+
+ AdvancedNetworking
+
+
+
+ Voltar
+
+
+
+
+ Ativar Theter
+
+
+
+
+ Senha Thetering
+
+
+
+
+
+ EDITAR
+
+
+
+
+ Insira nova senha thetering
+
+
+
+
+ IP Endereço
+
+
+
+
+ Ativar Roaming
+
+
+
+
+ APN Config
+
+
+
+
+ Insira APN
+
+
+
+
+ deixe em branco para configuração automática
+
+
+
+ ConfirmationDialog
+
+
+
+
+ OK
+
+
+
+
+ Cancelar
+
+
+
+ DeclinePage
+
+
+
+ Você precisa aceitar os Termos e Condições para utilizar openpilot.
+
+
+
+
+ Voltar
+
+
+
+
+ Rejeitar, desintalar %1
+
+
+
+ DevicePanel
+
+
+
+ Dongle ID
+
+
+
+
+ N/A
+
+
+
+
+ Serial
+
+
+
+
+ Câmera Motorista
+
+
+
+
+ PREVISUAL
+
+
+
+
+ Pré-visualizar a câmera voltada para o motorista para garantir que monitor tem uma boa visibilidade (veículo precisa estar desligado)
+
+
+
+
+ Limpar Calibragem
+
+
+
+
+ RESET
+
+
+
+
+ Tem certeza que quer limpar calibragem?
+
+
+
+
+ Revisar o Treinamento
+
+
+
+
+ REVISAR
+
+
+
+
+ Revisar regras, features e limitações do openpilot
+
+
+
+
+ Tem certeza que quer rever o treinamento?
+
+
+
+
+ Regulatório
+
+
+
+
+ VER
+
+
+
+
+ Mudar Língua
+
+
+
+
+ MUDAR
+
+
+
+
+ Selecione uma linguagem
+
+
+
+
+ Reiniciar
+
+
+
+
+ Desligar
+
+
+
+
+ o openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 8° para baixo. o openpilot está continuamente calibrando, a redefinição raramente é necessária.
+
+
+
+
+ Seu dispositivo está montado %1° %2 e %3° %4.
+
+
+
+
+ baixo
+
+
+
+
+ cima
+
+
+
+
+ esquerda
+
+
+
+
+ direita
+
+
+
+
+ Tem certeza que quer reiniciar?
+
+
+
+
+ Desacione para Reiniciar
+
+
+
+
+ Tem certeza que quer desligar?
+
+
+
+
+ Desacione para Desligar
+
+
+
+ DriveStats
+
+
+
+ Dirigidas
+
+
+
+
+ Horas
+
+
+
+
+ TOTAL
+
+
+
+
+ SEMANA PASSADA
+
+
+
+
+ KM
+
+
+
+
+ Milhas
+
+
+
+ DriverViewScene
+
+
+
+ camera iniciando
+
+
+
+ InputDialog
+
+
+
+ Cancelar
+
+
+
+
+
+ Necessita no mínimo %n caractere!
+ Necessita no mínimo %n caracteres!
+
+
+
+
+ Installer
+
+
+
+ Instalando...
+
+
+
+
+ Recebendo objetos:
+
+
+
+
+ Resolvendo deltas:
+
+
+
+
+ Atualizando arquivos:
+
+
+
+ MapETA
+
+
+
+ eta
+
+
+
+
+ min
+
+
+
+
+ hr
+
+
+
+
+ km
+
+
+
+
+ mi
+
+
+
+ MapInstructions
+
+
+
+ km
+
+
+
+
+ m
+
+
+
+
+ milha
+
+
+
+
+ pés
+
+
+
+ MapPanel
+
+
+
+ Destino Atual
+
+
+
+
+ LIMPAR
+
+
+
+
+ Destinos Recentes
+
+
+
+
+ Experimente a Navegação Beta
+
+
+
+
+ Obtenha instruções passo a passo exibidas e muito mais com
+uma assinatura prime Increva-se agora:https://connect.comma.ai
+
+
+
+
+ Sem local
+residência definido
+
+
+
+
+ Sem local
+trabalho definido
+
+
+
+
+ sem destinos recentes
+
+
+
+ MapWindow
+
+
+
+ Carregando Mapa
+
+
+
+
+ Esperando por GPS
+
+
+
+ MultiOptionDialog
+
+
+
+ Selecione
+
+
+
+
+ Cancelar
+
+
+
+ Networking
+
+
+
+ Avançado
+
+
+
+
+ Insira a senha
+
+
+
+
+
+ para "%1"
+
+
+
+
+ Senha incorreta
+
+
+
+ NvgWindow
+
+
+
+ km/h
+
+
+
+
+ mph
+
+
+
+
+
+ LIMITE
+
+
+
+
+
+ MAX
+
+
+
+
+
+ VELO
+
+
+
+ OffroadHome
+
+
+
+ ATUALIZAÇÃO
+
+
+
+
+ ALERTAS
+
+
+
+
+ ALERTA
+
+
+
+ PairingPopup
+
+
+
+ Pareie seu dispositivo a sua conta comma
+
+
+
+
+ navegue até https://connect.comma.ai no seu telefone
+
+
+
+
+ Clique "add new device" e escaneie o QR code a seguir
+
+
+
+
+ Salve connect.comma.ai como sua página inicial para utilizar com um app
+
+
+
+ PrimeAdWidget
+
+
+
+ Atualizar Agora
+
+
+
+
+ Torne-se um membro comma prime em connect.comma.ai
+
+
+
+
+ PRIME FEATURES:
+
+
+
+
+ Acesso remoto
+
+
+
+
+ 1 ano de armazenamento
+
+
+
+
+ Benefícios para desenvolvedor
+
+
+
+ PrimeUserWidget
+
+
+
+ ✓ INSCRITO
+
+
+
+
+ comma prime
+
+
+
+
+ CONNECT.COMMA.AI
+
+
+
+
+ PONTOS COMMA
+
+
+
+ QObject
+
+
+
+ Reiniciar
+
+
+
+
+ Sair
+
+
+
+
+ dashcam
+
+
+
+
+ openpilot
+
+
+
+
+
+ há %n minuto
+ há %n minutos
+
+
+
+
+
+
+ há %n hora
+ há %n horas
+
+
+
+
+
+
+ há %n dia
+ há %n dias
+
+
+
+
+ Reset
+
+
+
+ Reset falhou. Reinicie para tentar novamente.
+
+
+
+
+ Tem certeza que quer resetar seu dispositivo?
+
+
+
+
+ Resetando dispositivo...
+
+
+
+
+ Resetar Sistema
+
+
+
+
+ Solicitado reset do sistema. Confirme para apagar todo conteúdo e configurações. Aperte cancelar para continuar boot.
+
+
+
+
+ Cancelar
+
+
+
+
+ Reiniciar
+
+
+
+
+ Confirmar
+
+
+
+
+ Não foi possível montar a partição de dados. Pressione confirmar para resetar seu dispositivo.
+
+
+
+ RichTextDialog
+
+
+
+ Ok
+
+
+
+ SettingsWindow
+
+
+
+ ×
+
+
+
+
+ Dispositivo
+
+
+
+
+
+ Rede
+
+
+
+
+ Ajustes
+
+
+
+
+ Software
+
+
+
+
+ Navegação
+
+
+
+ Setup
+
+
+
+ ALERTA: Baixa Voltagem
+
+
+
+
+ Ligue seu dispositivo em um carro com um chicote ou prossiga por sua conta e risco.
+
+
+
+
+ Desligar
+
+
+
+
+
+
+ Continuar
+
+
+
+
+ Começando
+
+
+
+
+ Antes de pegarmos a estrada, vamos terminar a instalação e cobrir alguns detalhes.
+
+
+
+
+ Conectar ao Wi-Fi
+
+
+
+
+
+ Voltar
+
+
+
+
+ Continuar sem Wi-Fi
+
+
+
+
+ Esperando pela internet
+
+
+
+
+ Escolher Software para Instalar
+
+
+
+
+ Dashcam
+
+
+
+
+ Sofware Customizado
+
+
+
+
+ Preencher URL
+
+
+
+
+ para o Software Customizado
+
+
+
+
+ Baixando...
+
+
+
+
+ Download Falhou
+
+
+
+
+ Garanta que a URL inserida é valida, e uma boa conexão à internet.
+
+
+
+
+ Reiniciar Dispositivo
+
+
+
+
+ Inicializar
+
+
+
+ SetupWidget
+
+
+
+ Terminar Configuração
+
+
+
+
+ Pareie seu dispositivo com comma connect (connect.comma.ai) e reivindique sua oferta de comma prime.
+
+
+
+
+ Parear dispositivo
+
+
+
+ Sidebar
+
+
+
+
+ CONEXÃO
+
+
+
+
+ DESCONEC
+
+
+
+
+
+ CONECTADO
+
+
+
+
+ ERRO
+
+
+
+
+
+
+ TEMP
+
+
+
+
+ ALTA
+
+
+
+
+ BOA
+
+
+
+
+ OK
+
+
+
+
+ VEÍCULO
+
+
+
+
+ SEM
+
+
+
+
+ PANDA
+
+
+
+
+ GPS
+
+
+
+
+ PROCURA
+
+
+
+
+ --
+
+
+
+
+ Wi-Fi
+
+
+
+
+ ETH
+
+
+
+
+ 2G
+
+
+
+
+ 3G
+
+
+
+
+ LTE
+
+
+
+
+ 5G
+
+
+
+ SoftwarePanel
+
+
+
+ Ramo Git
+
+
+
+
+ Commit Git
+
+
+
+
+ Versão do Sistema
+
+
+
+
+ Versão
+
+
+
+
+ Verificação da última atualização
+
+
+
+
+ A última vez que o openpilot verificou com sucesso uma atualização. O atualizador só funciona com o carro desligado.
+
+
+
+
+ Verifique atualizações
+
+
+
+
+ VERIFICANDO
+
+
+
+
+ Trocar Ramo
+
+
+
+
+ INSERIR
+
+
+
+
+
+ O novo ramo será aplicado na próxima execução do atualizador.
+
+
+
+
+ Inserir o nome do ramo
+
+
+
+
+ DESINSTALAR
+
+
+
+
+ Desintalando %1
+
+
+
+
+ Tem certeza que quer desinstalar?
+
+
+
+
+ falha ao buscar atualização
+
+
+
+
+
+ VERIFICAR
+
+
+
+ SshControl
+
+
+
+ Chave SSH
+
+
+
+
+ Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub.
+
+
+
+
+
+ ADICIONAR
+
+
+
+
+ Insira seu nome de usuário do GitHub
+
+
+
+
+ CARREGANDO
+
+
+
+
+ REMOVER
+
+
+
+
+ Usuário "%1” não possui chaves no GitHub
+
+
+
+
+ A solicitação expirou
+
+
+
+
+ Usuário '%1' não existe no GitHub
+
+
+
+ SshToggle
+
+
+
+ Habilitar SSH
+
+
+
+ TermsPage
+
+
+
+ Termos & Condições
+
+
+
+
+ Declinar
+
+
+
+
+ Role para aceitar
+
+
+
+
+ Concordo
+
+
+
+ TogglesPanel
+
+
+
+ Ativar openpilot
+
+
+
+
+ Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. A alteração desta configuração tem efeito quando o carro é desligado.
+
+
+
+
+ Ativar Avisos de Saída de Faixa
+
+
+
+
+ Receba alertas para voltar para a pista se o seu veículo sair da faixa e a seta não tiver sido acionada previamente quando em velocidades superiores a 50 km/h.
+
+
+
+
+ Usar Sistema Métrico
+
+
+
+
+ Exibir velocidade em km/h invés de mph.
+
+
+
+
+ Gravar e Upload Câmera Motorista
+
+
+
+
+ Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor.
+
+
+
+
+ Desacionar Com Pedal Do Acelerador
+
+
+
+
+ Quando ativado, pressionar o pedal do acelerador desacionará o openpilot.
+
+
+
+
+ Mostrar ETA em formato 24h
+
+
+
+
+ Use o formato 24h em vez de am/pm
+
+
+
+
+ Exibir Mapa no Lado Esquerdo
+
+
+
+
+ Exibir mapa do lado esquerdo quando a tela for dividida.
+
+
+
+
+ openpilot Controle Longitudinal
+
+
+
+
+ openpilot desativará o radar do carro e assumirá o controle do acelerador e freios. Atenção: isso desativa AEB!
+
+
+
+ Updater
+
+
+
+ Atualização Necessária
+
+
+
+
+ Uma atualização do sistema operacional é necessária. Conecte seu dispositivo ao Wi-Fi para a experiência de atualização mais rápida. O tamanho do download é de aproximadamente 1GB.
+
+
+
+
+ Conecte-se ao Wi-Fi
+
+
+
+
+ Instalar
+
+
+
+
+ Voltar
+
+
+
+
+ Carregando...
+
+
+
+
+ Reiniciar
+
+
+
+
+ Falha na atualização
+
+
+
+ WifiUI
+
+
+
+
+ Procurando redes...
+
+
+
+
+ CONECTANDO...
+
+
+
+
+ ESQUECER
+
+
+
+
+ Esquecer Rede Wi-Fi "%1"?
+
+
+
diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts
new file mode 100644
index 00000000000000..3277f537404ff0
--- /dev/null
+++ b/selfdrive/ui/translations/main_th.ts
@@ -0,0 +1,1303 @@
+
+
+
+
+ AbstractAlert
+
+
+
+ ปิด
+
+
+
+
+ เลื่อนการอัปเดต
+
+
+
+
+ รีบูตและอัปเดต
+
+
+
+ AdvancedNetworking
+
+
+
+ ย้อนกลับ
+
+
+
+
+ ปล่อยฮอตสปอต
+
+
+
+
+ รหัสผ่านฮอตสปอต
+
+
+
+
+
+ แก้ไข
+
+
+
+
+ ป้อนรหัสผ่านฮอตสปอตใหม่
+
+
+
+
+ หมายเลขไอพี
+
+
+
+
+ เปิดใช้งานโรมมิ่ง
+
+
+
+
+ ตั้งค่า APN
+
+
+
+
+ ป้อนค่า APN
+
+
+
+
+ เว้นว่างเพื่อตั้งค่าอัตโนมัติ
+
+
+
+ ConfirmationDialog
+
+
+
+
+ ตกลง
+
+
+
+
+ ยกเลิก
+
+
+
+ DeclinePage
+
+
+
+ คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot
+
+
+
+
+ ย้อนกลับ
+
+
+
+
+ ปฏิเสธ และถอนการติดตั้ง %1
+
+
+
+ DevicePanel
+
+
+
+ Dongle ID
+
+
+
+
+ ไม่มี
+
+
+
+
+ ซีเรียล
+
+
+
+
+ กล้องฝั่งคนขับ
+
+
+
+
+ แสดงภาพ
+
+
+
+
+ ดูภาพตัวอย่างกล้องที่หันเข้าหาคนขับเพื่อให้แน่ใจว่าการตรวจสอบคนขับมีทัศนวิสัยที่ดี (รถต้องดับเครื่องยนต์)
+
+
+
+
+ รีเซ็ตการคาลิเบรท
+
+
+
+
+ รีเซ็ต
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตการคาลิเบรท?
+
+
+
+
+ ทบทวนคู่มือการใช้งาน
+
+
+
+
+ ทบทวน
+
+
+
+
+ ตรวจสอบกฎ คุณสมบัติ และข้อจำกัดของ openpilot
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการทบทวนคู่มือการใช้งาน?
+
+
+
+
+ ระเบียบข้อบังคับ
+
+
+
+
+ ดู
+
+
+
+
+ เปลี่ยนภาษา
+
+
+
+
+ เปลี่ยน
+
+
+
+
+ เลือกภาษา
+
+
+
+
+ รีบูต
+
+
+
+
+ ปิดเครื่อง
+
+
+
+
+ openpilot กำหนดให้ติดตั้งอุปกรณ์ โดยสามารถเอียงด้านซ้ายหรือขวาไม่เกิน 4° และเอียงขึ้นด้านบนไม่เกิน 5° หรือเอียงลงด้านล่างไม่เกิน 8° openpilot ทำการคาลิเบรทอย่างต่อเนื่อง แทบจะไม่จำเป็นต้องทำการรีเซ็ตการคาลิเบรท
+
+
+
+
+ อุปกรณ์ของคุณเอียงไปทาง %2 %1° และ %4 %3°
+
+
+
+
+ ด้านล่าง
+
+
+
+
+ ด้านบน
+
+
+
+
+ ด้านซ้าย
+
+
+
+
+ ด้านขวา
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการรีบูต?
+
+
+
+
+ ยกเลิกระบบช่วยขับเพื่อรีบูต
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการปิดเครื่อง?
+
+
+
+
+ ยกเลิกระบบช่วยขับเพื่อปิดเครื่อง
+
+
+
+ DriveStats
+
+
+
+ การขับขี่
+
+
+
+
+ ชั่วโมง
+
+
+
+
+ ทั้งหมด
+
+
+
+
+ สัปดาห์ที่ผ่านมา
+
+
+
+
+ กิโลเมตร
+
+
+
+
+ ไมล์
+
+
+
+ DriverViewScene
+
+
+
+ กำลังเปิดกล้อง
+
+
+
+ InputDialog
+
+
+
+ ยกเลิก
+
+
+
+
+
+ ต้องการอย่างน้อย %n ตัวอักษร!
+
+
+
+
+ Installer
+
+
+
+ กำลังติดตั้ง...
+
+
+
+
+ กำลังรับข้อมูล:
+
+
+
+
+ การแก้ไขเดลต้า:
+
+
+
+
+ กำลังอัปเดตไฟล์:
+
+
+
+ MapETA
+
+
+
+ eta
+
+
+
+
+ นาที
+
+
+
+
+ ชม.
+
+
+
+
+ กม.
+
+
+
+
+ ไมล์
+
+
+
+ MapInstructions
+
+
+
+ กม.
+
+
+
+
+ ม.
+
+
+
+
+ ไมล์
+
+
+
+
+ ฟุต
+
+
+
+ MapPanel
+
+
+
+ ปลายทางปัจจุบัน
+
+
+
+
+ ล้างข้อมูล
+
+
+
+
+ ปลายทางล่าสุด
+
+
+
+
+ ลองใช้ระบบนำทาง (เบต้า)
+
+
+
+
+ รับการแสดงเส้นทางแบบเลี้ยวต่อเลี้ยว และอื่นๆ ด้วยการสมัครบริการ
+comma prime สมัครเลย: https://connect.comma.ai
+
+
+
+
+ ยังไม่ได้กำหนด
+ตำแหน่งของบ้าน
+
+
+
+
+ ยังไม่ได้กำหนด
+ตำแหน่งของที่ทำงาน
+
+
+
+
+ ไม่พบปลายทางล่าสุด
+
+
+
+ MapWindow
+
+
+
+ กำลังโหลดแผนที่
+
+
+
+
+ กำลังรอสัญญาณ GPS
+
+
+
+ MultiOptionDialog
+
+
+
+ เลือก
+
+
+
+
+ ยกเลิก
+
+
+
+ Networking
+
+
+
+ ขั้นสูง
+
+
+
+
+ ใส่รหัสผ่าน
+
+
+
+
+
+ สำหรับ "%1"
+
+
+
+
+ รหัสผ่านผิด
+
+
+
+ NvgWindow
+
+
+
+ กม./ชม.
+
+
+
+
+ ไมล์/ชม.
+
+
+
+
+
+ สูงสุด
+
+
+
+
+
+ ความเร็ว
+
+
+
+
+
+ จำกัด
+
+
+
+ OffroadHome
+
+
+
+ อัปเดต
+
+
+
+
+ การแจ้งเตือน
+
+
+
+
+ การแจ้งเตือน
+
+
+
+ PairingPopup
+
+
+
+ จับคู่อุปกรณ์ของคุณกับบัญชี comma ของคุณ
+
+
+
+
+ ไปที่ https://connect.comma.ai ด้วยโทรศัพท์ของคุณ
+
+
+
+
+ กดที่ "add new device" และสแกนคิวอาร์โค้ดทางด้านขวา
+
+
+
+
+ จดจำ connect.comma.ai โดยการเพิ่มไปยังหน้าจอโฮม เพื่อใช้งานเหมือนเป็นแอปพลิเคชัน
+
+
+
+ PrimeAdWidget
+
+
+
+ อัพเกรดเดี๋ยวนี้
+
+
+
+
+ สมัครสมาชิก comma prime ได้ที่ connect.comma.ai
+
+
+
+
+ คุณสมบัติของ PRIME:
+
+
+
+
+ การเข้าถึงระยะไกล
+
+
+
+
+ จัดเก็บข้อมูลนาน 1 ปี
+
+
+
+
+ สิทธิพิเศษสำหรับนักพัฒนา
+
+
+
+ PrimeUserWidget
+
+
+
+ ✓ สมัครสำเร็จ
+
+
+
+
+ comma prime
+
+
+
+
+ CONNECT.COMMA.AI
+
+
+
+
+ คะแนน COMMA
+
+
+
+ QObject
+
+
+
+ รีบูต
+
+
+
+
+ ปิด
+
+
+
+
+ กล้องติดรถยนต์
+
+
+
+
+ openpilot
+
+
+
+
+
+ %n นาทีที่แล้ว
+
+
+
+
+
+
+ %n ชั่วโมงที่แล้ว
+
+
+
+
+
+
+ %n วันที่แล้ว
+
+
+
+
+ Reset
+
+
+
+ การรีเซ็ตล้มเหลว รีบูตเพื่อลองอีกครั้ง
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตอุปกรณ์?
+
+
+
+
+ กำลังรีเซ็ตอุปกรณ์...
+
+
+
+
+ รีเซ็ตระบบ
+
+
+
+
+ มีการสั่งรีเซ็ตระบบ กดยืนยันเพื่อลบข้อมูลและการตั้งค่าทั้งหมด กดยกเลิกเพื่อบูตเข้าระบบตามปกติ
+
+
+
+
+ ยกเลิก
+
+
+
+
+ รีบูต
+
+
+
+
+ ยืนยัน
+
+
+
+
+ ไม่สามารถเมานต์พาร์ติชั่นข้อมูล กดยืนยันเพื่อรีเซ็ตอุปกรณ์ของคุณ
+
+
+
+ RichTextDialog
+
+
+
+ ตกลง
+
+
+
+ SettingsWindow
+
+
+
+ ×
+
+
+
+
+ อุปกรณ์
+
+
+
+
+
+ เครือข่าย
+
+
+
+
+ ตัวเลือก
+
+
+
+
+ ซอฟต์แวร์
+
+
+
+
+ การนำทาง
+
+
+
+ Setup
+
+
+
+ คำเตือน: แรงดันไฟฟ้าต่ำ
+
+
+
+
+ โปรดต่ออุปกรณ์ของคุณเข้ากับสายควบคุมในรถยนต์ หรือดำเนินการด้วยความเสี่ยงของคุณเอง
+
+
+
+
+ ปิดเครื่อง
+
+
+
+
+
+
+ ดำเนินการต่อ
+
+
+
+
+ เริ่มกันเลย
+
+
+
+
+ ก่อนออกเดินทาง เรามาทำการติดตั้งซอฟต์แวร์ และตรวจสอบการตั้งค่า
+
+
+
+
+ เชื่อมต่อ Wi-Fi
+
+
+
+
+
+ ย้อนกลับ
+
+
+
+
+ ดำเนินการต่อโดยไม่ใช้ Wi-Fi
+
+
+
+
+ กำลังรอสัญญาณอินเตอร์เน็ต
+
+
+
+
+ เลือกซอฟต์แวร์ที่จะติดตั้ง
+
+
+
+
+ กล้องติดรถยนต์
+
+
+
+
+ ซอฟต์แวร์ที่กำหนดเอง
+
+
+
+
+ ป้อน URL
+
+
+
+
+ สำหรับซอฟต์แวร์ที่กำหนดเอง
+
+
+
+
+ กำลังดาวน์โหลด...
+
+
+
+
+ ดาวน์โหลดล้มเหลว
+
+
+
+
+ ตรวจสอบให้แน่ใจว่า URL ที่ป้อนนั้นถูกต้อง และอุปกรณ์เชื่อมต่ออินเทอร์เน็ตอยู่
+
+
+
+
+ รีบูตอุปกรณ์
+
+
+
+
+ เริ่มต้นใหม่
+
+
+
+ SetupWidget
+
+
+
+ ตั้งค่าเสร็จสิ้น
+
+
+
+
+ จับคู่อุปกรณ์ของคุณกับ comma connect (connect.comma.ai) และรับข้อเสนอ comma prime ของคุณ
+
+
+
+
+ จับคู่อุปกรณ์
+
+
+
+ Sidebar
+
+
+
+
+ เชื่อมต่อ
+
+
+
+
+ ออฟไลน์
+
+
+
+
+
+ ออนไลน์
+
+
+
+
+ เกิดข้อผิดพลาด
+
+
+
+
+
+
+ อุณหภูมิ
+
+
+
+
+ สูง
+
+
+
+
+ ดี
+
+
+
+
+ พอใช้
+
+
+
+
+ รถยนต์
+
+
+
+
+ ไม่พบ
+
+
+
+
+ PANDA
+
+
+
+
+ จีพีเอส
+
+
+
+
+ ค้นหา
+
+
+
+
+ --
+
+
+
+
+ Wi-Fi
+
+
+
+
+ ETH
+
+
+
+
+ 2G
+
+
+
+
+ 3G
+
+
+
+
+ LTE
+
+
+
+
+ 5G
+
+
+
+ SoftwarePanel
+
+
+
+ Git Branch
+
+
+
+
+ Git Commit
+
+
+
+
+ เวอร์ชันระบบปฏิบัติการ
+
+
+
+
+ เวอร์ชั่น
+
+
+
+
+ ตรวจสอบการอัปเดตล่าสุด
+
+
+
+
+ ครั้งสุดท้ายที่ openpilot ตรวจสอบการอัปเดตสำเร็จ ตัวอัปเดตจะทำงานในขณะที่รถดับเครื่องอยู่เท่านั้น
+
+
+
+
+ ตรวจสอบการอัปเดต
+
+
+
+
+ กำลังตรวจสอบ
+
+
+
+
+ เปลี่ยน Branch
+
+
+
+
+ เปลี่ยน
+
+
+
+
+
+ Branch ใหม่จะถูกติดตั้งในครั้งต่อไปที่ตัวอัปเดตทำงาน
+
+
+
+
+ ใส่ชื่อ Branch
+
+
+
+
+ ถอนการติดตั้ง %1
+
+
+
+
+ ถอนการติดตั้ง
+
+
+
+
+ คุณแน่ใจหรือไม่ว่าต้องการถอนการติดตั้ง?
+
+
+
+
+ โหลดข้อมูลอัปเดตไม่สำเร็จ
+
+
+
+
+
+ ตรวจสอบ
+
+
+
+ SshControl
+
+
+
+ คีย์ SSH
+
+
+
+
+ คำเตือน: สิ่งนี้ให้สิทธิ์ SSH เข้าถึงคีย์สาธารณะทั้งหมดใน GitHub ของคุณ อย่าป้อนชื่อผู้ใช้ GitHub อื่นนอกเหนือจากของคุณเอง พนักงาน comma จะไม่ขอให้คุณเพิ่มชื่อผู้ใช้ GitHub ของพวกเขา
+
+
+
+
+
+ เพิ่ม
+
+
+
+
+ ป้อนชื่อผู้ใช้ GitHub ของคุณ
+
+
+
+
+ กำลังโหลด
+
+
+
+
+ ลบ
+
+
+
+
+ ชื่อผู้ใช้ '%1' ไม่มีคีย์บน GitHub
+
+
+
+
+ ตรวจสอบไม่สำเร็จ เนื่องจากใช้เวลามากเกินไป
+
+
+
+
+ ไม่พบชื่อผู้ใช้ '%1' บน GitHub
+
+
+
+ SshToggle
+
+
+
+ เปิดใช้งาน SSH
+
+
+
+ TermsPage
+
+
+
+ ข้อตกลงและเงื่อนไข
+
+
+
+
+ ปฏิเสธ
+
+
+
+
+ เลื่อนเพื่อตอบรับข้อตกลง
+
+
+
+
+ ยอมรับ
+
+
+
+ TogglesPanel
+
+
+
+ เปิดใช้งาน openpilot
+
+
+
+
+ ใช้ระบบ openpilot สำหรับระบบควบคุมความเร็วอัตโนมัติ และระบบช่วยควบคุมรถให้อยู่ในเลน คุณจำเป็นต้องให้ความสนใจตลอดเวลาที่ใช้คุณสมบัตินี้ การเปลี่ยนการตั้งค่านี้จะมีผลเมื่อคุณดับเครื่องยนต์
+
+
+
+
+ เปิดใช้งานการเตือนการออกนอกเลน
+
+
+
+
+ รับการแจ้งเตือนให้เลี้ยวกลับเข้าเลนเมื่อรถของคุณตรวจพบการข้ามช่องจราจรโดยไม่เปิดสัญญาณไฟเลี้ยวในขณะขับขี่ที่ความเร็วเกิน 31 ไมล์ต่อชั่วโมง (50 กม./ชม)
+
+
+
+
+ ใช้ระบบเมตริก
+
+
+
+
+ แสดงความเร็วเป็น กม./ชม. แทน ไมล์/ชั่วโมง
+
+
+
+
+ บันทึกและอัปโหลดภาพจากกล้องคนขับ
+
+
+
+
+ อัปโหลดข้อมูลจากกล้องที่หันหน้าไปทางคนขับ และช่วยปรับปรุงอัลกอริธึมการตรวจสอบผู้ขับขี่
+
+
+
+
+ ยกเลิกระบบช่วยขับเมื่อเหยียบคันเร่ง
+
+
+
+
+ เมื่อเปิดใช้งาน การกดแป้นคันเร่งจะเป็นการยกเลิกระบบช่วยขับโดย openpilot
+
+
+
+
+ แสดงเวลา ETA ในรูปแบบ 24 ชั่วโมง
+
+
+
+
+ ใช้รูปแบบเวลา 24 ชั่วโมง แทน am/pm
+
+
+
+
+ แสดงแผนที่ที่ด้านซ้ายของหน้าจอ
+
+
+
+
+ แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ
+
+
+
+
+ openpilot การควบคุมการเร่งและลดความเร็ว
+
+
+
+
+ openpilot จะปิดการใช้งานเรดาร์ของรถ และจะเข้าควบคุมการเร่งและเบรก คำเตือน: สิ่งนี้จะปิดระบบ AEB!
+
+
+
+ Updater
+
+
+
+ จำเป็นต้องอัปเดต
+
+
+
+
+ จำเป็นต้องมีการอัปเดตระบบปฏิบัติการ เชื่อมต่ออุปกรณ์ของคุณกับ Wi-Fi เพื่อประสบการณ์การอัปเดตที่เร็วที่สุด ขนาดดาวน์โหลดประมาณ 1GB
+
+
+
+
+ เชื่อมต่อกับ Wi-Fi
+
+
+
+
+ ติดตั้ง
+
+
+
+
+ ย้อนกลับ
+
+
+
+
+ กำลังโหลด...
+
+
+
+
+ รีบูต
+
+
+
+
+ การอัปเดตล้มเหลว
+
+
+
+ WifiUI
+
+
+
+
+ กำลังสแกนหาเครือข่าย...
+
+
+
+
+ กำลังเชื่อมต่อ...
+
+
+
+
+ เลิกใช้
+
+
+
+
+ เลิกใช้เครือข่าย Wi-Fi "%1"?
+
+
+
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index cb09e8c2effc93..a26cddc0c34859 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -874,71 +874,71 @@ location set
Sidebar
-
-
+
+
CONNECT
-
+
离线
-
-
+
+
在线
-
+
连接出错
-
-
-
+
+
+
设备温度
-
+
过热
-
+
良好
-
+
一般
-
+
车辆连接
-
+
无
-
+
PANDA
-
+
GPS
-
+
搜索中
@@ -1202,7 +1202,7 @@ location set
-
+
以24小时格式显示预计到达时间
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index 58e921b313562f..f4681f85d940c2 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -876,71 +876,71 @@ location set
Sidebar
-
-
+
+
雲端服務
-
+
已離線
-
-
+
+
已連線
-
+
錯誤
-
-
-
+
+
+
溫度
-
+
偏高
-
+
正常
-
+
一般
-
+
車輛通訊
-
+
未連線
-
+
車輛通訊
-
+
GPS
-
+
車輛通訊
@@ -1204,7 +1204,7 @@ location set
-
+
預計到達時間單位改用 24 小時制
diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h
index 2a56052559be95..198efca0bb3f8f 100644
--- a/system/camerad/cameras/camera_common.h
+++ b/system/camerad/cameras/camera_common.h
@@ -94,7 +94,6 @@ class CameraBuf {
public:
cl_command_queue q;
FrameMetadata cur_frame_data;
- VisionBuf *cur_rgb_buf;
VisionBuf *cur_yuv_buf;
VisionBuf *cur_camera_buf;
std::unique_ptr camera_bufs;
diff --git a/tools/joystick/joystickd.py b/tools/joystick/joystickd.py
index 6226038d516ed9..35ecca351d1250 100755
--- a/tools/joystick/joystickd.py
+++ b/tools/joystick/joystickd.py
@@ -41,7 +41,7 @@ class Joystick:
def __init__(self):
# TODO: find a way to get this from API, perhaps "inputs" doesn't support it
self.min_axis_value = {'ABS_Y': 0., 'ABS_RZ': 0.}
- self.max_axis_value = {'ABS_Y': 1023., 'ABS_RZ': 255.}
+ self.max_axis_value = {'ABS_Y': 255., 'ABS_RZ': 255.}
self.cancel_button = 'BTN_TRIGGER'
self.axes_values = {'ABS_Y': 0., 'ABS_RZ': 0.} # gb, steer
self.axes_order = ['ABS_Y', 'ABS_RZ']
diff --git a/tools/plotjuggler/layouts/max-torque-debug.xml b/tools/plotjuggler/layouts/max-torque-debug.xml
index 05aa208e260808..6903ca8b1532b8 100644
--- a/tools/plotjuggler/layouts/max-torque-debug.xml
+++ b/tools/plotjuggler/layouts/max-torque-debug.xml
@@ -51,12 +51,6 @@
-
-
-
-
-
-
diff --git a/tools/plotjuggler/layouts/torque-controller.xml b/tools/plotjuggler/layouts/torque-controller.xml
index 661bfe094de445..443255968ad31e 100644
--- a/tools/plotjuggler/layouts/torque-controller.xml
+++ b/tools/plotjuggler/layouts/torque-controller.xml
@@ -1,12 +1,12 @@
-
+
-
+
-
+
@@ -14,7 +14,7 @@
-
+
@@ -22,33 +22,26 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -64,19 +57,29 @@
-
+
+
+ return value * 3.6
+ /carState/vEgo
+
+
+
+ return value * 2.23694
+ /carState/vEgo
+
+
return (value * v1 ^ 2) - (v2 * 9.81)
- /controlsState/curvature
+ /controlsState/desiredCurvature
/carState/vEgo
/liveParameters/roll
-
+
return (value * v1 ^ 2) - (v2 * 9.81)
- /controlsState/desiredCurvature
+ /controlsState/curvature
/carState/vEgo
/liveParameters/roll
@@ -86,3 +89,4 @@
+
diff --git a/tools/replay/camera.cc b/tools/replay/camera.cc
index 87afe63a2af64e..1a577b40cefa00 100644
--- a/tools/replay/camera.cc
+++ b/tools/replay/camera.cc
@@ -37,7 +37,8 @@ void CameraServer::startVipcServer() {
void CameraServer::cameraThread(Camera &cam) {
auto read_frame = [&](FrameReader *fr, int frame_id) {
VisionBuf *yuv_buf = vipc_server_->get_buffer(cam.stream_type);
- bool ret = fr->get(frame_id, yuv_buf ? (uint8_t *)yuv_buf->addr : nullptr);
+ assert(yuv_buf);
+ bool ret = fr->get(frame_id, (uint8_t *)yuv_buf->addr);
return ret ? yuv_buf : nullptr;
};
diff --git a/tools/replay/consoleui.cc b/tools/replay/consoleui.cc
index e4a3146a69c02c..6b419ca9d80908 100644
--- a/tools/replay/consoleui.cc
+++ b/tools/replay/consoleui.cc
@@ -18,6 +18,7 @@ const std::initializer_list> keyboard_shortc
{"space", "Pause/Resume"},
{"e", "Next Engagement"},
{"d", "Next Disengagement"},
+ {"t", "Next User Tag"}
},
{
{"enter", "Enter seek request"},
@@ -32,6 +33,7 @@ enum Color {
Yellow,
Green,
Red,
+ Cyan,
BrightWhite,
Engaged,
Disengaged,
@@ -70,6 +72,7 @@ ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"car
init_pair(Color::Debug, 246, COLOR_BLACK); // #949494
init_pair(Color::Yellow, 184, COLOR_BLACK);
init_pair(Color::Red, COLOR_RED, COLOR_BLACK);
+ init_pair(Color::Cyan, COLOR_CYAN, COLOR_BLACK);
init_pair(Color::BrightWhite, 15, COLOR_BLACK);
init_pair(Color::Disengaged, COLOR_BLUE, COLOR_BLUE);
init_pair(Color::Engaged, 28, 28);
@@ -205,6 +208,7 @@ void ConsoleUI::displayTimelineDesc() {
{Color::Green, " Info ", true},
{Color::Yellow, " Warning ", true},
{Color::Red, " Critical ", true},
+ {Color::Cyan, " User Tag ", true},
};
for (auto [color, name, bold] : indicators) {
add_str(w[Win::TimelineDesc], "__", color, bold);
@@ -263,6 +267,8 @@ void ConsoleUI::updateTimeline() {
if (type == TimelineType::Engaged) {
mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
+ } else if (type == TimelineType::UserFlag) {
+ mvwchgat(win, 3, start_pos, end_pos - start_pos + 1, ACS_S3, Color::Cyan, NULL);
} else {
auto color_id = Color::Green;
if (type != TimelineType::AlertInfo) {
@@ -336,6 +342,8 @@ void ConsoleUI::handleKey(char c) {
replay->seekToFlag(FindFlag::nextEngagement);
} else if (c == 'd') {
replay->seekToFlag(FindFlag::nextDisEngagement);
+ } else if (c == 't') {
+ replay->seekToFlag(FindFlag::nextUserFlag);
} else if (c == 'm') {
replay->seekTo(+60, true);
} else if (c == 'M') {
diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc
index c886a7e1862ae3..4b983fff85969d 100644
--- a/tools/replay/replay.cc
+++ b/tools/replay/replay.cc
@@ -149,6 +149,9 @@ void Replay::buildTimeline() {
timeline.push_back({toSeconds(alert_begin), toSeconds(e->mono_time), alert_type});
alert_begin = 0;
}
+ } else if (e->which == cereal::Event::Which::USER_FLAG) {
+ std::lock_guard lk(timeline_lock);
+ timeline.push_back({toSeconds(e->mono_time), toSeconds(e->mono_time), TimelineType::UserFlag});
}
}
}
@@ -163,6 +166,10 @@ std::optional Replay::find(FindFlag flag) {
} else if (flag == FindFlag::nextDisEngagement && end_ts > cur_ts) {
return end_ts;
}
+ } else if (type == TimelineType::UserFlag) {
+ if (flag == FindFlag::nextUserFlag && start_ts > cur_ts) {
+ return start_ts;
+ }
}
}
return std::nullopt;
@@ -360,7 +367,7 @@ void Replay::stream() {
setCurrentSegment(toSeconds(cur_mono_time_) / 60);
// migration for pandaState -> pandaStates to keep UI working for old segments
- if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D &&
+ if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D &&
sockets_[cereal::Event::Which::PANDA_STATES] != nullptr) {
MessageBuilder msg;
auto ps = msg.initEvent().initPandaStates(1);
diff --git a/tools/replay/replay.h b/tools/replay/replay.h
index 13269d3ec9a269..86d609683a5034 100644
--- a/tools/replay/replay.h
+++ b/tools/replay/replay.h
@@ -24,10 +24,11 @@ enum REPLAY_FLAGS {
enum class FindFlag {
nextEngagement,
- nextDisEngagement
+ nextDisEngagement,
+ nextUserFlag,
};
-enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical };
+enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag };
class Replay : public QObject {
Q_OBJECT
diff --git a/tools/sim/bridge.py b/tools/sim/bridge.py
index fa4ce2b41d9742..f008b9e716cde9 100755
--- a/tools/sim/bridge.py
+++ b/tools/sim/bridge.py
@@ -179,7 +179,7 @@ def gps_callback(gps, vehicle_state):
]
dat.gpsLocationExternal = {
- "timestamp": int(time.time() * 1000),
+ "unixTimestampMillis": int(time.time() * 1000),
"flags": 1, # valid fix
"accuracy": 1.0,
"verticalAccuracy": 1.0,
diff --git a/tools/sim/tests/test_carla_integration.py b/tools/sim/tests/test_carla_integration.py
index 43db783f4e38d4..f4673919674d29 100755
--- a/tools/sim/tests/test_carla_integration.py
+++ b/tools/sim/tests/test_carla_integration.py
@@ -77,7 +77,7 @@ def test_engage(self):
while time.monotonic() < start_time + max_time_per_step:
sm.update()
- q.put("cruise_up") # Try engaging
+ q.put("cruise_down") # Try engaging
if sm.all_alive() and sm['controlsState'].active:
control_active += 1
diff --git a/tools/ssh/README.md b/tools/ssh/README.md
index 29d33493282076..67b5271a42ac48 100644
--- a/tools/ssh/README.md
+++ b/tools/ssh/README.md
@@ -7,11 +7,11 @@ In order to SSH into your device, you'll need a GitHub account with SSH keys. Se
* Enable SSH in your device's settings
* Enter your GitHub username in the device's settings
* Connect to your device
- * Username: `comma` (comma three)
+ * Username: `comma`
* Port: `22` or `8022`
-Here's an example command for connecting to your device using its tethered connection:
-`ssh root@192.168.43.1`
+Here's an example command for connecting to your device using its tethered connection:
+`ssh comma@192.168.43.1`
For doing development work on device, it's recommended to use [SSH agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding).
@@ -34,7 +34,8 @@ Requires [comma SIM with comma prime](https://comma.ai/shop) activated with comm
## Recommended .ssh/config
-With the below ssh configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`. For example, `ssh comma-ffffffffffffffff`.
+With the below SSH configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`.
+For example: `ssh comma-ffffffffffffffff`
```
Host comma-*
diff --git a/tools/ubuntu_setup.sh b/tools/ubuntu_setup.sh
index 379df37adb6c84..863b853718fa56 100755
--- a/tools/ubuntu_setup.sh
+++ b/tools/ubuntu_setup.sh
@@ -108,7 +108,11 @@ if [ -f "/etc/os-release" ]; then
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
- install_ubuntu_focal_requirements
+ if [ "$UBUNTU_CODENAME" = "jammy" ]; then
+ install_ubuntu_jammy_requirements
+ else
+ install_ubuntu_focal_requirements
+ fi
esac
else
echo "No /etc/os-release in the system"
diff --git a/update_requirements.sh b/update_requirements.sh
index 94b14496f151a9..719a28c3599ead 100755
--- a/update_requirements.sh
+++ b/update_requirements.sh
@@ -4,11 +4,26 @@ set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR
+RC_FILE="${HOME}/.$(basename ${SHELL})rc"
+if [ "$(uname)" == "Darwin" ] && [ $SHELL == "/bin/bash" ]; then
+ RC_FILE="$HOME/.bash_profile"
+fi
+
if ! command -v "pyenv" > /dev/null 2>&1; then
echo "pyenv install ..."
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
- export PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
+
+ echo -e "\n. ~/.pyenvrc" >> $RC_FILE
+ cat < "${HOME}/.pyenvrc"
+if [ -z "\$PYENV_ROOT" ]; then
+ export PATH=\$HOME/.pyenv/bin:\$HOME/.pyenv/shims:\$PATH
+ export PYENV_ROOT="\$HOME/.pyenv"
+ eval "\$(pyenv init -)"
+ eval "\$(pyenv virtualenv-init -)"
+fi
+EOF
fi
+source $RC_FILE
export MAKEFLAGS="-j$(nproc)"