Merge remote-tracking branch 'upstream/master' into camera-scc-long
# Conflicts: # docs/CARS.md # panda # selfdrive/car/hyundai/carcontroller.py # selfdrive/car/hyundai/interface.py
@@ -16,7 +16,6 @@ jobs:
|
||||
build_prebuilt:
|
||||
name: build prebuilt
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 60
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
env:
|
||||
IMAGE_NAME: openpilot-prebuilt
|
||||
|
||||
@@ -8,7 +8,6 @@ jobs:
|
||||
build_masterci:
|
||||
name: build master-ci
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 60
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- name: Wait for green check mark
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }}
|
||||
group: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
@@ -32,7 +32,6 @@ jobs:
|
||||
build_release:
|
||||
name: build release
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
STRIPPED_DIR: /tmp/releasepilot
|
||||
steps:
|
||||
@@ -61,12 +60,11 @@ jobs:
|
||||
cp .pylintrc $STRIPPED_DIR
|
||||
cp mypy.ini $STRIPPED_DIR
|
||||
cd $STRIPPED_DIR
|
||||
${{ env.RUN }} "SKIP=test_translations pre-commit run --all"
|
||||
${{ env.RUN }} "pre-commit run --all"
|
||||
|
||||
build_all:
|
||||
name: build all
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -84,7 +82,6 @@ jobs:
|
||||
#build_mac:
|
||||
# name: build macos
|
||||
# runs-on: macos-latest
|
||||
# timeout-minutes: 60
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# with:
|
||||
@@ -105,7 +102,7 @@ jobs:
|
||||
# /usr/local/Cellar
|
||||
# ~/github_brew_cache_entries.txt
|
||||
# /tmp/scons_cache
|
||||
# key: macos-${{ hashFiles('tools/mac_setup.sh', 'update_requirements.sh', 'Pipfile*') }}
|
||||
# key: macos-${{ hashFiles('tools/mac_setup.sh', 'update_requirements.sh', 'poetry.lock') }}
|
||||
# restore-keys: macos-
|
||||
# - name: Brew link restored dependencies
|
||||
# run: |
|
||||
@@ -121,11 +118,11 @@ jobs:
|
||||
# - name: Build openpilot
|
||||
# run: |
|
||||
# source tools/openpilot_env.sh
|
||||
# pipenv run selfdrive/manager/build.py
|
||||
# poetry run selfdrive/manager/build.py
|
||||
#
|
||||
# # cleanup scons cache
|
||||
# rm -rf /tmp/scons_cache/
|
||||
# pipenv run scons -j$(nproc) --cache-populate
|
||||
# poetry run scons -j$(nproc) --cache-populate
|
||||
# - name: Remove pre-existing Homebrew packages for caching
|
||||
# if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
# run: |
|
||||
@@ -141,7 +138,6 @@ jobs:
|
||||
docker_push:
|
||||
name: docker push
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 22
|
||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
|
||||
needs: static_analysis # hack to ensure slow tests run first since this and static_analysis are fast
|
||||
steps:
|
||||
@@ -150,14 +146,12 @@ jobs:
|
||||
submodules: true
|
||||
- name: Build Docker image
|
||||
run: eval "$BUILD"
|
||||
timeout-minutes: 13
|
||||
- name: Push to container registry
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
|
||||
- name: Build CL Docker image
|
||||
run: eval "$BUILD_CL"
|
||||
timeout-minutes: 4
|
||||
- name: Push to container registry
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
@@ -166,7 +160,6 @@ jobs:
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -174,13 +167,11 @@ jobs:
|
||||
- name: Build Docker image
|
||||
run: eval "$BUILD"
|
||||
- name: pre-commit
|
||||
timeout-minutes: 5
|
||||
run: ${{ env.RUN }} "pre-commit run --all"
|
||||
|
||||
valgrind:
|
||||
name: valgrind
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -198,7 +189,6 @@ jobs:
|
||||
unit_tests:
|
||||
name: unit tests
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -207,7 +197,6 @@ jobs:
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run unit tests
|
||||
timeout-minutes: 15
|
||||
run: |
|
||||
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
|
||||
$UNIT_TEST common && \
|
||||
@@ -215,10 +204,12 @@ jobs:
|
||||
$UNIT_TEST selfdrive/boardd && \
|
||||
$UNIT_TEST selfdrive/controls && \
|
||||
$UNIT_TEST selfdrive/monitoring && \
|
||||
$UNIT_TEST selfdrive/loggerd && \
|
||||
$UNIT_TEST system/loggerd && \
|
||||
$UNIT_TEST selfdrive/car && \
|
||||
$UNIT_TEST selfdrive/locationd && \
|
||||
$UNIT_TEST system/ubloxd && \
|
||||
selfdrive/locationd/test/_test_locationd_lib.py && \
|
||||
./system/ubloxd/tests/test_glonass_runner && \
|
||||
$UNIT_TEST selfdrive/athena && \
|
||||
$UNIT_TEST selfdrive/thermald && \
|
||||
$UNIT_TEST system/hardware/tici && \
|
||||
@@ -229,9 +220,10 @@ jobs:
|
||||
./common/tests/test_util && \
|
||||
./common/tests/test_swaglog && \
|
||||
./selfdrive/boardd/tests/test_boardd_usbprotocol && \
|
||||
./selfdrive/loggerd/tests/test_logger &&\
|
||||
./system/loggerd/tests/test_logger &&\
|
||||
./system/proclogd/tests/test_proclog && \
|
||||
./tools/replay/tests/test_replay && \
|
||||
./tools/cabana/tests/test_cabana && \
|
||||
./system/camerad/test/ae_gray_test && \
|
||||
coverage xml"
|
||||
- name: "Upload coverage to Codecov"
|
||||
@@ -240,7 +232,6 @@ jobs:
|
||||
process_replay:
|
||||
name: process replay
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -260,6 +251,7 @@ jobs:
|
||||
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||
coverage xml"
|
||||
- name: Print diff
|
||||
id: print-diff
|
||||
if: always()
|
||||
run: cat selfdrive/test/process_replay/diff.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
@@ -269,7 +261,7 @@ jobs:
|
||||
name: process_replay_diff.txt
|
||||
path: selfdrive/test/process_replay/diff.txt
|
||||
- name: Upload reference logs
|
||||
if: ${{ failure() && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
|
||||
if: ${{ failure() && steps.print-diff.outcome == 'success' && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
|
||||
run: |
|
||||
${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
||||
- name: "Upload coverage to Codecov"
|
||||
@@ -278,7 +270,6 @@ jobs:
|
||||
test_modeld:
|
||||
name: model tests
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -291,12 +282,10 @@ jobs:
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Run model replay with ONNX
|
||||
timeout-minutes: 2
|
||||
run: |
|
||||
${{ env.RUN_CL }} "ONNXCPU=1 CI=1 coverage run selfdrive/test/process_replay/model_replay.py && \
|
||||
${{ env.RUN_CL }} "ONNXCPU=1 CI=1 NO_NAV=1 coverage run selfdrive/test/process_replay/model_replay.py && \
|
||||
coverage xml"
|
||||
- name: Run unit tests
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
${{ env.RUN_CL }} "$UNIT_TEST selfdrive/modeld && \
|
||||
coverage xml"
|
||||
@@ -306,7 +295,6 @@ jobs:
|
||||
test_longitudinal:
|
||||
name: longitudinal
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -321,7 +309,6 @@ jobs:
|
||||
cd selfdrive/test/longitudinal_maneuvers && \
|
||||
coverage run ./test_longitudinal.py && \
|
||||
coverage xml"
|
||||
timeout-minutes: 2
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
- uses: actions/upload-artifact@v2
|
||||
@@ -334,7 +321,6 @@ jobs:
|
||||
test_cars:
|
||||
name: cars
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -353,7 +339,6 @@ jobs:
|
||||
- name: Build openpilot
|
||||
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||
- name: Test car models
|
||||
timeout-minutes: 12
|
||||
run: |
|
||||
${{ env.RUN }} "coverage run -m pytest selfdrive/car/tests/test_models.py && \
|
||||
coverage xml && \
|
||||
@@ -367,7 +352,6 @@ jobs:
|
||||
car_docs_diff:
|
||||
name: PR comments
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }}
|
||||
group: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
@@ -30,7 +30,6 @@ jobs:
|
||||
plotjuggler:
|
||||
name: plotjuggler
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -38,7 +37,6 @@ jobs:
|
||||
- name: Build Docker image
|
||||
run: eval "$BUILD"
|
||||
- name: Unit test
|
||||
timeout-minutes: 2
|
||||
run: |
|
||||
${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \
|
||||
apt-get update && \
|
||||
@@ -49,7 +47,6 @@ jobs:
|
||||
simulator:
|
||||
name: simulator
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
IMAGE_NAME: openpilot-sim
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
@@ -74,7 +71,6 @@ jobs:
|
||||
docs:
|
||||
name: build docs
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
@@ -46,12 +46,10 @@ selfdrive/mapd/default_speeds_by_region.json
|
||||
system/proclogd/proclogd
|
||||
selfdrive/ui/_ui
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/visiond/visiond
|
||||
selfdrive/sensord/_gpsd
|
||||
selfdrive/sensord/_sensord
|
||||
system/camerad/camerad
|
||||
system/camerad/test/ae_gray_test
|
||||
selfdrive/modeld/_modeld
|
||||
selfdrive/modeld/_navmodeld
|
||||
selfdrive/modeld/_dmonitoringmodeld
|
||||
/src/
|
||||
|
||||
@@ -83,3 +81,5 @@ selfdrive/modeld/models/*.thneed
|
||||
build/
|
||||
|
||||
!**/.gitkeep
|
||||
|
||||
poetry.toml
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
url = ../../commaai/body.git
|
||||
[submodule "tinygrad"]
|
||||
path = tinygrad_repo
|
||||
url = ../../commaai/tinygrad.git
|
||||
url = https://github.com/geohot/tinygrad.git
|
||||
|
||||
@@ -7,7 +7,7 @@ repos:
|
||||
rev: v4.1.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
exclude: '^(pyextra)/'
|
||||
exclude: '^(third_party)/'
|
||||
- id: check-json
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
@@ -19,7 +19,7 @@ repos:
|
||||
rev: v2.2.1
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: '^(pyextra/)|(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(include/)|(selfdrive/ui/translations/.*.ts)|(selfdrive/controls/lib/cluster)'
|
||||
exclude: '^(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)|(poetry.lock)'
|
||||
args:
|
||||
# if you've got a short variable name that's getting flagged, add it here
|
||||
- -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup
|
||||
@@ -31,12 +31,12 @@ repos:
|
||||
entry: mypy
|
||||
language: system
|
||||
types: [python]
|
||||
exclude: '^(pyextra/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
|
||||
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(selfdrive/debug/)/'
|
||||
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(selfdrive/debug/)/'
|
||||
additional_dependencies: ['flake8-no-implicit-concat']
|
||||
args:
|
||||
- --indent-size=2
|
||||
@@ -51,7 +51,7 @@ repos:
|
||||
entry: pylint
|
||||
language: system
|
||||
types: [python]
|
||||
exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
|
||||
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
|
||||
args:
|
||||
- -j0
|
||||
- -rn
|
||||
@@ -64,7 +64,7 @@ repos:
|
||||
entry: cppcheck
|
||||
language: system
|
||||
types: [c++]
|
||||
exclude: '^(third_party/)|(pyextra/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)|(installer/)'
|
||||
exclude: '^(third_party/)|(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++
|
||||
@@ -78,3 +78,7 @@ repos:
|
||||
entry: selfdrive/ui/tests/test_translations.py
|
||||
language: script
|
||||
pass_filenames: false
|
||||
- repo: https://github.com/python-poetry/poetry
|
||||
rev: '1.2.2'
|
||||
hooks:
|
||||
- id: poetry-check
|
||||
|
||||
@@ -10,7 +10,6 @@ WORKDIR ${OPENPILOT_PATH}
|
||||
|
||||
COPY SConstruct ${OPENPILOT_PATH}
|
||||
|
||||
COPY ./pyextra ${OPENPILOT_PATH}/pyextra
|
||||
COPY ./third_party ${OPENPILOT_PATH}/third_party
|
||||
COPY ./site_scons ${OPENPILOT_PATH}/site_scons
|
||||
COPY ./laika ${OPENPILOT_PATH}/laika
|
||||
|
||||
@@ -12,19 +12,19 @@ ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
ENV PIPENV_SYSTEM=1
|
||||
ENV POETRY_VIRTUALENVS_CREATE=false
|
||||
ENV PYENV_VERSION=3.8.10
|
||||
ENV PYENV_ROOT="/root/.pyenv"
|
||||
ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
|
||||
|
||||
COPY Pipfile Pipfile.lock .python-version update_requirements.sh /tmp/
|
||||
COPY pyproject.toml poetry.lock .python-version update_requirements.sh /tmp/
|
||||
COPY tools/ubuntu_setup.sh /tmp/tools/
|
||||
RUN cd /tmp && \
|
||||
tools/ubuntu_setup.sh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm -rf /tmp/* && \
|
||||
rm -rf /root/.cache && \
|
||||
pip uninstall -y pipenv && \
|
||||
pip uninstall -y poetry && \
|
||||
# remove unused architectures from gcc for panda
|
||||
cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \
|
||||
rm -rf arm/ && \
|
||||
|
||||
@@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends\
|
||||
|
||||
# Intel OpenCL driver
|
||||
ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz
|
||||
ARG INTEL_DRIVER_URL=http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532
|
||||
ARG INTEL_DRIVER_URL=https://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532
|
||||
RUN mkdir -p /tmp/opencl-driver-intel
|
||||
WORKDIR /tmp/opencl-driver-intel
|
||||
RUN echo INTEL_DRIVER is $INTEL_DRIVER && \
|
||||
|
||||
@@ -11,6 +11,9 @@ export SOURCE_DIR=${env.SOURCE_DIR}
|
||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||
export GIT_COMMIT=${env.GIT_COMMIT}
|
||||
export AZURE_TOKEN='${env.AZURE_TOKEN}'
|
||||
export MAPBOX_TOKEN='${env.MAPBOX_TOKEN}'
|
||||
|
||||
export GIT_SSH_COMMAND="ssh -i /data/gitkey"
|
||||
|
||||
source ~/.bash_profile
|
||||
if [ -f /TICI ]; then
|
||||
@@ -47,20 +50,34 @@ pipeline {
|
||||
TEST_DIR = "/data/openpilot"
|
||||
SOURCE_DIR = "/data/openpilot_source/"
|
||||
AZURE_TOKEN = credentials('azure_token')
|
||||
MAPBOX_TOKEN = credentials('mapbox_token')
|
||||
}
|
||||
options {
|
||||
timeout(time: 4, unit: 'HOURS')
|
||||
timeout(time: 3, unit: 'HOURS')
|
||||
disableConcurrentBuilds(abortPrevious: env.BRANCH_NAME != 'master')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('build release3') {
|
||||
stage('build release3-staging') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
when {
|
||||
branch 'devel-staging'
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici", [
|
||||
["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
|
||||
phone_steps("tici-needs-can", [
|
||||
["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('build nightly') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
when {
|
||||
branch 'master-ci'
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici-needs-can", [
|
||||
["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -78,6 +95,7 @@ pipeline {
|
||||
|
||||
parallel {
|
||||
|
||||
/*
|
||||
stage('simulator') {
|
||||
agent {
|
||||
dockerfile {
|
||||
@@ -87,7 +105,8 @@ pipeline {
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh "git config --global --add safe.directory ${WORKSPACE}"
|
||||
sh "git config --global --add safe.directory '*'"
|
||||
sh "git submodule update --init --recursive"
|
||||
sh "git lfs pull"
|
||||
lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) {
|
||||
sh "${WORKSPACE}/tools/sim/build_container.sh"
|
||||
@@ -104,6 +123,7 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
stage('build') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
@@ -111,36 +131,45 @@ pipeline {
|
||||
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici", [
|
||||
phone_steps("tici-needs-can", [
|
||||
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"],
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["check dirty", "release/check-dirty.sh"],
|
||||
["test manager", "python selfdrive/manager/test/test_manager.py"],
|
||||
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
|
||||
["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('loopback-tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-loopback", [
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('HW + Unit Tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici2", [
|
||||
phone_steps("tici-common", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test power draw", "python system/hardware/tici/test_power_draw.py"],
|
||||
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
|
||||
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"],
|
||||
["test sensord", "python selfdrive/sensord/tests/test_sensord.py"],
|
||||
["test pigeond", "python selfdrive/sensord/tests/test_pigeond.py"],
|
||||
["test loggerd", "python system/loggerd/tests/test_loggerd.py"],
|
||||
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python system/loggerd/tests/test_encoder.py"],
|
||||
["test pigeond", "python system/sensord/tests/test_pigeond.py"],
|
||||
["test manager", "python selfdrive/manager/test/test_manager.py"],
|
||||
["test pandad", "python selfdrive/boardd/tests/test_pandad.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('camerad') {
|
||||
stage('camerad-ar') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-party", [
|
||||
phone_steps("tici-ar0231", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "python system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python system/camerad/test/test_exposure.py"],
|
||||
@@ -148,17 +177,43 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage('replay') {
|
||||
stage('camerad-ox') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici3", [
|
||||
phone_steps("tici-ox03c10", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
|
||||
["test camerad", "python system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('sensord') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-lsmc", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && python -m unittest test_sensord.py"],
|
||||
])
|
||||
phone_steps("tici-bmx-lsm", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && python -m unittest test_sensord.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('replay') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-common", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "cd selfdrive/test/process_replay && NO_NAV=1 ./model_replay.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
av = "*"
|
||||
azure-storage-blob = "~=2.1"
|
||||
control = "*"
|
||||
coverage = "*"
|
||||
dictdiffer = "*"
|
||||
fastcluster = "*"
|
||||
hexdump = "*"
|
||||
hypothesis = "==6.46.7"
|
||||
inputs = "*"
|
||||
lru-dict = "*"
|
||||
markdown-it-py = "*"
|
||||
matplotlib = "*"
|
||||
mypy = "*"
|
||||
myst-parser = "*"
|
||||
natsort = "*"
|
||||
numpy = "*"
|
||||
opencv-python-headless = "*"
|
||||
parameterized = "*"
|
||||
paramiko = "*"
|
||||
pprofile = "*"
|
||||
pre-commit = "*"
|
||||
pycurl = "*"
|
||||
pygame = "*"
|
||||
pyprof2calltree = "*"
|
||||
pytest = "*"
|
||||
pytest-xdist = "*"
|
||||
reverse_geocoder = "*"
|
||||
scipy = "*"
|
||||
sphinx = "*"
|
||||
sphinx-sitemap = "*"
|
||||
sphinx-rtd-theme = "*"
|
||||
breathe = "*"
|
||||
subprocess32 = "*"
|
||||
tenacity = "*"
|
||||
mpld3 = "*"
|
||||
carla = {version = "==0.9.13", markers="platform_system != 'Darwin'"}
|
||||
ft4222 = "*"
|
||||
pandas = "*"
|
||||
tabulate = "*"
|
||||
types-pyyaml = "*"
|
||||
lxml = "*"
|
||||
types-atomicwrites = "*"
|
||||
types-pycurl = "*"
|
||||
types-requests = "*"
|
||||
types-certifi = "*"
|
||||
|
||||
[packages]
|
||||
atomicwrites = "*"
|
||||
casadi = {version = "*", markers="platform_system != 'Darwin'"}
|
||||
cffi = "*"
|
||||
crcmod = "*"
|
||||
cryptography = "*"
|
||||
Cython = "*"
|
||||
flake8 = "*"
|
||||
Flask = "*"
|
||||
future-fstrings = "*" # for acados
|
||||
gunicorn = "*"
|
||||
hexdump = "*"
|
||||
Jinja2 = "*"
|
||||
json-rpc = "*"
|
||||
libusb1 = "*"
|
||||
nose = "*"
|
||||
numpy = "*"
|
||||
protobuf = "==3.20.1"
|
||||
onnx = "*"
|
||||
onnxruntime-gpu = {version = "*", markers="platform_system != 'Darwin'"}
|
||||
pillow = "*"
|
||||
psutil = "*"
|
||||
pycapnp = "==1.1.0"
|
||||
pycryptodome = "*"
|
||||
PyJWT = "*"
|
||||
pylint = "*"
|
||||
pyopencl = "*"
|
||||
pyserial = "*"
|
||||
python-dateutil = "*"
|
||||
PyYAML = "*"
|
||||
pyzmq = "*"
|
||||
requests = "*"
|
||||
scons = "*"
|
||||
sentry-sdk = "*"
|
||||
setproctitle = "*"
|
||||
six = "*"
|
||||
smbus2 = "*"
|
||||
sympy = "!=1.6.1"
|
||||
timezonefinder = "*"
|
||||
tqdm = "*"
|
||||
urllib3 = "*"
|
||||
utm = "*"
|
||||
websocket_client = "*"
|
||||
hatanaka = "==2.4"
|
||||
PySide2 = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
@@ -103,13 +103,15 @@ Directory Structure
|
||||
├── opendbc # Files showing how to interpret data from cars
|
||||
├── panda # Code used to communicate on CAN
|
||||
├── third_party # External libraries
|
||||
├── pyextra # Extra python packages
|
||||
└── system # Generic services
|
||||
├── camerad # Driver to capture images from the camera sensors
|
||||
├── clocksd # Broadcasts current time
|
||||
├── hardware # Hardware abstraction classes
|
||||
├── logcatd # systemd journal as a service
|
||||
└── proclogd # Logs information from /proc
|
||||
├── loggerd # Logger and uploader of car data
|
||||
├── proclogd # Logs information from /proc
|
||||
├── sensord # IMU interface code
|
||||
└── ubloxd # u-blox GNSS module interface code
|
||||
└── selfdrive # Code needed to drive the car
|
||||
├── assets # Fonts, images, and sounds for UI
|
||||
├── athena # Allows communication with the app
|
||||
@@ -118,12 +120,10 @@ Directory Structure
|
||||
├── controls # Planning and controls
|
||||
├── debug # Tools to help you debug and do car ports
|
||||
├── locationd # Precise localization and vehicle parameter estimation
|
||||
├── loggerd # Logger and uploader of car data
|
||||
├── manager # Daemon that starts/stops all other daemons as needed
|
||||
├── modeld # Driving and monitoring model runners
|
||||
├── monitoring # Daemon to determine driver attention
|
||||
├── navd # Turn-by-turn navigation
|
||||
├── sensord # IMU interface code
|
||||
├── test # Unit tests, system tests, and a car simulator
|
||||
└── ui # The UI
|
||||
|
||||
@@ -143,7 +143,4 @@ NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/1061157-bc7e9bf3b246ece7322e6ffe653f6af8-medium_jpg.jpg?buster=1458363130" width="75"></img> <img src="https://cdn-images-1.medium.com/max/1600/1*C87EjxGeMPrkTuVRVWVg4w.png" width="225"></img>
|
||||
|
||||
[](https://github.com/commaai/openpilot/actions)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/alerts/)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/context:python)
|
||||
[](https://lgtm.com/projects/g/commaai/openpilot/context:cpp)
|
||||
[](https://codecov.io/gh/commaai/openpilot)
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
Version 0.8.17 (2022-XX-XX)
|
||||
Version 0.9.2 (2023-03-XX)
|
||||
========================
|
||||
* Draw MPC path instead of model predicted path, this is a more accurate representation of what the car will do.
|
||||
* Buick LaCrosse 2017-19 support thanks to koch-cf!
|
||||
* Škoda Fabia 2022-23 support thanks to jyoung8607!
|
||||
|
||||
Version 0.9.1 (2023-02-28)
|
||||
========================
|
||||
* New driving model
|
||||
* Internal feature space accuracy increased tenfold during training, this makes the model dramatically more accurate.
|
||||
* Self-tuning torque lateral controller parameters
|
||||
* Parameters learned live for each car
|
||||
* Enabled only on Toyota Corolla for now
|
||||
* 30% improved height estimation resulting in better driving performance for tall cars
|
||||
* Driver monitoring: removed timer resetting on user interaction if distracted
|
||||
* UI updates
|
||||
* Adjust alert volume using ambient noise level
|
||||
* Driver monitoring icon shows driver's head pose
|
||||
* German translation thanks to Vrabetz and CzokNorris!
|
||||
* Cadillac Escalade 2017 support thanks to rickygilleland!
|
||||
* Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler!
|
||||
* Genesis GV60 2023 support thanks to sunnyhaibin!
|
||||
* Hyundai Tucson 2022-23 support
|
||||
* Kia K5 Hybrid 2020 support thanks to sunnyhaibin!
|
||||
* Kia Niro Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Kia Sorento 2022-23 support thanks to sunnyhaibin!
|
||||
* Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin!
|
||||
* Toyota C-HR 2021 support thanks to eFiniLan!
|
||||
* Toyota C-HR Hybrid 2022 support thanks to Korben00!
|
||||
* Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607!
|
||||
|
||||
Version 0.9.0 (2022-11-21)
|
||||
========================
|
||||
* New driving model
|
||||
* Internal feature space information content increased tenfold during training to ~700 bits, which makes the model dramatically more accurate
|
||||
* Less reliance on previous frames makes model more reactive and snappy
|
||||
* Trained in new reprojective simulator
|
||||
* Trained in 36 hours from scratch, compared to one week for previous releases
|
||||
* Training now simulates both lateral and longitudinal behavior, which allows openpilot to slow down for turns, stop at traffic lights, and more in experimental mode
|
||||
* Experimental driving mode
|
||||
* End-to-end longitudinal control
|
||||
* Stops for traffic lights and stop signs
|
||||
* Slows down for turns
|
||||
* openpilot defaults to chill mode, enable experimental mode in settings
|
||||
* Driver monitoring updates
|
||||
* New bigger model with added end-to-end distracted trigger
|
||||
* Reduced false positives during driver calibration
|
||||
* Self-tuning torque controller: learns parameters live for each car
|
||||
* Torque controller used on all Toyota, Lexus, Hyundai, Kia, and Genesis models
|
||||
* UI updates
|
||||
* Multi-language in navigation
|
||||
* Matched speeds shown on car's dash
|
||||
* Multi-language in navigation
|
||||
* Improved update experience
|
||||
* Border turns grey while overriding steering
|
||||
* Added button to bookmark events while driving; view them later in comma connect
|
||||
* AGNOS 6
|
||||
* Bookmark events while driving; view them in comma connect
|
||||
* New onroad visualization for experimental mode
|
||||
* tools: new and improved cabana thanks to deanlee!
|
||||
* Experimental longitudinal support for Volkswagen, CAN-FD Hyundai, and new GM models
|
||||
* Genesis GV70 2022-23 support thanks to zunichky and sunnyhaibin!
|
||||
* Hyundai Santa Cruz 2021-22 support thanks to sunnyhaibin!
|
||||
* Kia Sportage 2023 support thanks to sunnyhaibin!
|
||||
* Kia Sportage Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Kia Stinger 2022 support thanks to sunnyhaibin!
|
||||
|
||||
Version 0.8.16 (2022-08-26)
|
||||
========================
|
||||
|
||||
@@ -71,10 +71,10 @@ if arch == "aarch64" and AGNOS:
|
||||
lenv = {
|
||||
"PATH": os.environ['PATH'],
|
||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||
"PYTHONPATH": Dir("#").abspath + ":" + Dir("#pyextra/").abspath,
|
||||
"PYTHONPATH": Dir("#").abspath,
|
||||
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados/include/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#pyextra/acados_template").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||
}
|
||||
|
||||
@@ -197,10 +197,6 @@ env = Environment(
|
||||
"#third_party/libyuv/include",
|
||||
"#third_party/json11",
|
||||
"#third_party/curl/include",
|
||||
"#third_party/libgralloc/include",
|
||||
"#third_party/android_frameworks_native/include",
|
||||
"#third_party/android_hardware_libhardware/include",
|
||||
"#third_party/android_system_core/include",
|
||||
"#third_party/linux/include",
|
||||
"#third_party/snpe/include",
|
||||
"#third_party/mapbox-gl-native-qt/include",
|
||||
@@ -282,7 +278,7 @@ Export('envCython')
|
||||
|
||||
# Qt build environment
|
||||
qt_env = env.Clone()
|
||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus"]
|
||||
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus", "Xml"]
|
||||
|
||||
qt_libs = []
|
||||
if arch == "Darwin":
|
||||
@@ -298,16 +294,20 @@ if arch == "Darwin":
|
||||
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
|
||||
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
|
||||
else:
|
||||
qt_env['QTDIR'] = "/usr"
|
||||
qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
|
||||
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
|
||||
|
||||
qt_env['QTDIR'] = qt_install_prefix
|
||||
qt_dirs = [
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.12.8/QtGui",
|
||||
f"{qt_install_headers}",
|
||||
f"{qt_install_headers}/QtGui/5.12.8/QtGui",
|
||||
]
|
||||
qt_dirs += [f"/usr/include/{real_arch}-linux-gnu/qt5/Qt{m}" for m in qt_modules]
|
||||
qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
|
||||
|
||||
qt_libs = [f"Qt5{m}" for m in qt_modules]
|
||||
if arch == "larch64":
|
||||
qt_libs += ["GLESv2", "wayland-client"]
|
||||
qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
|
||||
elif arch != "Darwin":
|
||||
qt_libs += ["GL"]
|
||||
|
||||
@@ -384,10 +384,10 @@ rednose_config = {
|
||||
if arch != "larch64":
|
||||
rednose_config['to_build'].update({
|
||||
'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, [], rednose_deps),
|
||||
'lane': ('#selfdrive/locationd/models/lane_kf.py', True, [], rednose_deps),
|
||||
'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, [], []),
|
||||
'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, [], []),
|
||||
'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, [], []),
|
||||
'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, [], rednose_deps),
|
||||
})
|
||||
|
||||
Export('rednose_config')
|
||||
@@ -398,6 +398,7 @@ SConscript([
|
||||
'system/camerad/SConscript',
|
||||
'system/clocksd/SConscript',
|
||||
'system/proclogd/SConscript',
|
||||
'system/ubloxd/SConscript',
|
||||
])
|
||||
if arch != "Darwin":
|
||||
SConscript(['system/logcatd/SConscript'])
|
||||
@@ -406,10 +407,10 @@ if arch != "Darwin":
|
||||
|
||||
# build submodules
|
||||
SConscript([
|
||||
'cereal/SConscript',
|
||||
'body/board/SConscript',
|
||||
'panda/board/SConscript',
|
||||
'cereal/SConscript',
|
||||
'opendbc/can/SConscript',
|
||||
'panda/SConscript',
|
||||
])
|
||||
|
||||
SConscript(['third_party/SConscript'])
|
||||
@@ -419,16 +420,15 @@ SConscript(['common/transformations/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/modeld/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/controls/lib/cluster/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/boardd/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/loggerd/SConscript'])
|
||||
SConscript(['system/loggerd/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/locationd/SConscript'])
|
||||
SConscript(['selfdrive/sensord/SConscript'])
|
||||
SConscript(['system/sensord/SConscript'])
|
||||
SConscript(['selfdrive/ui/SConscript'])
|
||||
SConscript(['selfdrive/navd/SConscript'])
|
||||
|
||||
@@ -439,9 +439,6 @@ if arch in ['x86_64', 'Darwin'] or GetOption('extras'):
|
||||
Export('opendbc')
|
||||
SConscript(['tools/cabana/SConscript'])
|
||||
|
||||
if GetOption('test'):
|
||||
SConscript('panda/tests/safety/SConscript')
|
||||
|
||||
external_sconscript = GetOption('external_sconscript')
|
||||
if external_sconscript:
|
||||
SConscript([external_sconscript])
|
||||
|
||||
@@ -10,11 +10,13 @@ common_libs = [
|
||||
'statlog.cc',
|
||||
'swaglog.cc',
|
||||
'util.cc',
|
||||
'gpio.cc',
|
||||
'i2c.cc',
|
||||
'watchdog.cc',
|
||||
]
|
||||
|
||||
if arch != "Darwin":
|
||||
common_libs.append('gpio.cc')
|
||||
|
||||
_common = fxn('common', common_libs, LIBS="json11")
|
||||
|
||||
files = [
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/swaglog.h"
|
||||
|
||||
namespace { // helper functions
|
||||
|
||||
@@ -31,14 +32,14 @@ void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
||||
case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break;
|
||||
}
|
||||
|
||||
std::cout << "vendor: " << get_platform_info(platform, CL_PLATFORM_VENDOR) << std::endl
|
||||
<< "platform version: " << get_platform_info(platform, CL_PLATFORM_VERSION) << std::endl
|
||||
<< "profile: " << get_platform_info(platform, CL_PLATFORM_PROFILE) << std::endl
|
||||
<< "extensions: " << get_platform_info(platform, CL_PLATFORM_EXTENSIONS) << std::endl
|
||||
<< "name :" << get_device_info(device, CL_DEVICE_NAME) << std::endl
|
||||
<< "device version :" << get_device_info(device, CL_DEVICE_VERSION) << std::endl
|
||||
<< "max work group size :" << work_group_size << std::endl
|
||||
<< "type = " << device_type << " = " << type_str << std::endl;
|
||||
LOGD("vendor: %s", get_platform_info(platform, CL_PLATFORM_VENDOR).c_str());
|
||||
LOGD("platform version: %s", get_platform_info(platform, CL_PLATFORM_VERSION).c_str());
|
||||
LOGD("profile: %s", get_platform_info(platform, CL_PLATFORM_PROFILE).c_str());
|
||||
LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str());
|
||||
LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str());
|
||||
LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str());
|
||||
LOGD("max work group size: %d", work_group_size);
|
||||
LOGD("type = %d = ", device_type, type_str);
|
||||
}
|
||||
|
||||
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||
@@ -49,7 +50,7 @@ void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||
std::string log(log_size, '\0');
|
||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL);
|
||||
|
||||
std::cout << "build failed; status=" << status << ", log:" << std::endl << log << std::endl;
|
||||
LOGE("build failed; status=%d, log: %s", status, log.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -61,14 +62,15 @@ cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
||||
|
||||
for (size_t i = 0; i < num_platforms; ++i) {
|
||||
std::cout << "platform[" << i << "] CL_PLATFORM_NAME: " << get_platform_info(platform_ids[i], CL_PLATFORM_NAME) << std::endl;
|
||||
LOGD("platform[%d] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
||||
|
||||
// Get first device
|
||||
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
||||
cl_print_info(platform_ids[i], device_id);
|
||||
return device_id;
|
||||
}
|
||||
}
|
||||
std::cout << "No valid openCL platform found" << std::endl;
|
||||
LOGE("No valid openCL platform found");
|
||||
assert(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
#include "common/gpio.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
int gpio_init(int pin_nr, bool output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set(int pin_nr, bool high) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -63,3 +78,5 @@ int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int p
|
||||
close(fd);
|
||||
return rq.fd;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -98,18 +98,20 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CarVin", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"CompletedTrainingVersion", PERSISTENT},
|
||||
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"CurrentBootlog", PERSISTENT},
|
||||
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"DashcamOverride", PERSISTENT},
|
||||
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"DisablePowerDown", PERSISTENT},
|
||||
{"EndToEndLong", PERSISTENT},
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT}, // WARNING: THIS MAY DISABLE AEB
|
||||
{"ExperimentalMode", PERSISTENT},
|
||||
{"ExperimentalModeConfirmed", PERSISTENT},
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT},
|
||||
{"DisableUpdates", PERSISTENT},
|
||||
{"DisengageOnAccelerator", PERSISTENT},
|
||||
{"DongleId", PERSISTENT},
|
||||
{"DoReboot", CLEAR_ON_MANAGER_START},
|
||||
{"DoShutdown", CLEAR_ON_MANAGER_START},
|
||||
{"DoUninstall", CLEAR_ON_MANAGER_START},
|
||||
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"ForcePowerDown", CLEAR_ON_MANAGER_START},
|
||||
{"GitBranch", PERSISTENT},
|
||||
{"GitCommit", PERSISTENT},
|
||||
@@ -133,9 +135,10 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"IsRhdDetected", PERSISTENT},
|
||||
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsUpdateAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
|
||||
{"LaikadEphemeris", PERSISTENT | DONT_LOG},
|
||||
{"LaikadEphemerisV3", PERSISTENT | DONT_LOG},
|
||||
{"LanguageSetting", PERSISTENT},
|
||||
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
|
||||
{"LastGPSPosition", PERSISTENT},
|
||||
@@ -148,9 +151,12 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LiveTorqueCarParams", PERSISTENT},
|
||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
|
||||
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
|
||||
{"NavSettingTime24h", PERSISTENT},
|
||||
{"NavSettingLeftSide", PERSISTENT},
|
||||
{"NavdRender", PERSISTENT},
|
||||
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"OpenpilotEnabledToggle", PERSISTENT},
|
||||
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
|
||||
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
||||
@@ -167,7 +173,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"Timezone", PERSISTENT},
|
||||
{"TrainingVersion", PERSISTENT},
|
||||
{"UbloxAvailable", PERSISTENT},
|
||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
|
||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "common/params.h"
|
||||
#include "common/util.h"
|
||||
|
||||
class OpenpilotPrefix {
|
||||
public:
|
||||
OpenpilotPrefix(std::string prefix = {}) {
|
||||
if (prefix.empty()) {
|
||||
prefix = util::random_string(15);
|
||||
}
|
||||
msgq_path = "/dev/shm/" + prefix;
|
||||
bool ret = util::create_directories(msgq_path, 0777);
|
||||
assert(ret);
|
||||
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
||||
}
|
||||
|
||||
~OpenpilotPrefix() {
|
||||
auto param_path = Params().getParamPath();
|
||||
if (util::file_exists(param_path)) {
|
||||
std::string real_path = util::readlink(param_path);
|
||||
system(util::string_format("rm %s -rf", real_path.c_str()).c_str());
|
||||
unlink(param_path.c_str());
|
||||
}
|
||||
system(util::string_format("rm %s -rf", msgq_path.c_str()).c_str());
|
||||
unsetenv("OPENPILOT_PREFIX");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string msgq_path;
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "common/util.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
@@ -9,6 +10,7 @@
|
||||
#include <dirent.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -149,6 +151,14 @@ int safe_fflush(FILE *stream) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int safe_ioctl(int fd, unsigned long request, void *argp) {
|
||||
int ret;
|
||||
do {
|
||||
ret = ioctl(fd, request, argp);
|
||||
} while ((ret == -1) && (errno == EINTR));
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string readlink(const std::string &path) {
|
||||
char buff[4096];
|
||||
ssize_t len = ::readlink(path.c_str(), buff, sizeof(buff)-1);
|
||||
@@ -219,6 +229,18 @@ std::string hexdump(const uint8_t* in, const size_t size) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string random_string(std::string::size_type length) {
|
||||
const char* chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
std::mt19937 rg{std::random_device{}()};
|
||||
std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);
|
||||
std::string s;
|
||||
s.reserve(length);
|
||||
while (length--) {
|
||||
s += chrs[pick(rg)];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string dir_name(std::string const &path) {
|
||||
size_t pos = path.find_last_of("/");
|
||||
if (pos == std::string::npos) return "";
|
||||
|
||||
@@ -75,6 +75,7 @@ int getenv(const char* key, int default_val);
|
||||
float getenv(const char* key, float default_val);
|
||||
|
||||
std::string hexdump(const uint8_t* in, const size_t size);
|
||||
std::string random_string(std::string::size_type length);
|
||||
std::string dir_name(std::string const& path);
|
||||
|
||||
// **** file fhelpers *****
|
||||
@@ -86,6 +87,7 @@ int write_file(const char* path, const void* data, size_t size, int flags = O_WR
|
||||
FILE* safe_fopen(const char* filename, const char* mode);
|
||||
size_t safe_fwrite(const void * ptr, size_t size, size_t count, FILE * stream);
|
||||
int safe_fflush(FILE *stream);
|
||||
int safe_ioctl(int fd, unsigned long request, void *argp);
|
||||
|
||||
std::string readlink(const std::string& path);
|
||||
bool file_exists(const std::string& fn);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMMA_VERSION "0.8.17"
|
||||
#define COMMA_VERSION "0.9.2"
|
||||
|
||||
@@ -4,228 +4,261 @@
|
||||
|
||||
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.
|
||||
|
||||
# 208 Supported Cars
|
||||
# 239 Supported Cars
|
||||
|
||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Acura|RDX 2019-22|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Audi|Q3 2020-21|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
|
||||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|3 mph|6 mph|[](##)|[](##)|GM|
|
||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|3 mph|6 mph|[](##)|[](##)|GM|
|
||||
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|FCA|
|
||||
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|FCA|
|
||||
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[](##)|[](##)|FCA|
|
||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|FCA|
|
||||
|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|FCA|
|
||||
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None|
|
||||
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai F|
|
||||
|Genesis|G70 2020|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai F|
|
||||
|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Genesis|G90 2017-18|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai C|
|
||||
|GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[](##)|[](##)|OBD-II|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|3 mph|6 mph|[](##)|[](##)|GM|
|
||||
|Honda|Accord 2018-22|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Accord Hybrid 2018-22|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Civic 2019-21|All|openpilot|0 mph|2 mph[<sup>2</sup>](#footnotes)|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Civic 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Honda Bosch B|
|
||||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Honda Bosch B|
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|e 2020|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Insight 2019-22|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Inspire 2018|All|openpilot|0 mph|3 mph|[](##)|[](##)|Honda Bosch A|
|
||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|Honda Nidec|
|
||||
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|Hyundai B|
|
||||
|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai K|
|
||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai K|
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|Hyundai J|
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai Q|
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022|Highway Driving Assist|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai K|
|
||||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|Hyundai C|
|
||||
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|Hyundai C|
|
||||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot|0 mph|32 mph|[](##)|[](##)|Hyundai C|
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai B|
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai G|
|
||||
|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai O|
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai I|
|
||||
|Hyundai|Palisade 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai D|
|
||||
|Hyundai|Santa Fe 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Hyundai|Santa Fe Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai E|
|
||||
|Hyundai|Sonata 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai A|
|
||||
|Hyundai|Sonata Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai A|
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot|19 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai N|
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|Hyundai E|
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|FCA|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|FCA|
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai E|
|
||||
|Kia|EV6 (with HDA II) 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai P|
|
||||
|Kia|EV6 (without HDA II) 2022|Highway Driving Assist|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai L|
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai G|
|
||||
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai A|
|
||||
|Kia|Niro EV 2019|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Kia|Niro EV 2020|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai F|
|
||||
|Kia|Niro EV 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai C|
|
||||
|Kia|Niro EV 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai F|
|
||||
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot|10 mph|32 mph|[](##)|[](##)|Hyundai C|
|
||||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|Hyundai B|
|
||||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai G|
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai A|
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai C|
|
||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai E|
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Hyundai C|
|
||||
|Kia|Telluride 2020|All|openpilot|0 mph|0 mph|[](##)|[](##)|Hyundai H|
|
||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|ES Hybrid 2017-18|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|NX 2018-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|NX Hybrid 2018-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX 2016|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX 2017-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX Hybrid 2016|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX Hybrid 2017-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Mazda|CX-5 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Mazda|
|
||||
|Mazda|CX-9 2021-22|All|Stock|0 mph|28 mph|[](##)|[](##)|Mazda|
|
||||
|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|Nissan B|
|
||||
|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|Nissan A|
|
||||
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|Nissan A|
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|Nissan A|
|
||||
|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|Ram|
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|Subaru B|
|
||||
|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|Subaru B|
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|Subaru A|
|
||||
|Škoda|Kamiq 2021[<sup>5</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Karoq 2019-21[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Škoda|Superb 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon 2017-18|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon 2019-21|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon Hybrid 2019-21|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>4</sup>](#footnotes)|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Camry 2021-22|All|openpilot|0 mph[<sup>4</sup>](#footnotes)|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Camry Hybrid 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla 2017-19|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla Cross (Non-US only) 2020-21|All|openpilot|17 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Highlander 2017-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Highlander 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Highlander Hybrid 2017-19|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius 2017-20|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius Prime 2017-20|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Prius v 2017|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 2017-18|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 Hybrid 2017-18|All|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Toyota|Sienna 2018-20|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|Toyota|
|
||||
|Volkswagen|Arteon 2018-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Arteon eHybrid 2020-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Arteon R 2020-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Atlas 2018-23[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Atlas Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|California 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|31 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Caravelle 2020[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|31 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|CC 2018-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf 2015-20[<sup>8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf R 2015-19[<sup>8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Volkswagen|Jetta 2018-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Jetta GLI 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Passat 2015-22[<sup>6,7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Passat Alltrack 2015-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Passat GTE 2015-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Polo 2020-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Polo GTI 2020-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|T-Cross 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|T-Roc 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Taos 2022[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Teramont 2018-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Teramont Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Teramont X 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Tiguan 2019-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|J533|
|
||||
|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[](##)|[](##)|VW|
|
||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Acura&model=ILX 2016-19">Honda Nidec</a>||
|
||||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2016-18">Honda Nidec</a>||
|
||||
|Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2019-22">Honda Bosch A</a>||
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 2014-19">J533</a>||
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 Sportback e-tron 2017-18">J533</a>||
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=Q2 2018">J533</a>||
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=Q3 2019-23">J533</a>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=RS3 2018">J533</a>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=S3 2015-17">J533</a>||
|
||||
|Buick|LaCrosse 2017-19[<sup>3</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Buick&model=LaCrosse 2017-19">OBD-II</a>||
|
||||
|Cadillac|Escalade 2017[<sup>3</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">OBD-II</a>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">OBD-II</a>||
|
||||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EUV 2022-23">GM</a>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EV 2022-23">GM</a>||
|
||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Silverado 1500 2020-21">GM</a>||
|
||||
|Chevrolet|Volt 2017-18[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Volt 2017-18">OBD-II</a>|<a href="https://youtu.be/QeMCN_4TFfQ" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2017-18">FCA</a>||
|
||||
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2019-20">FCA</a>||
|
||||
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2021">FCA</a>||
|
||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica Hybrid 2017-18">FCA</a>||
|
||||
|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica Hybrid 2019-22">FCA</a>||
|
||||
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None||
|
||||
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G70 2018-19">Hyundai F</a>||
|
||||
|Genesis|G70 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G70 2020">Hyundai F</a>||
|
||||
|Genesis|G80 2017|All|Stock|19 mph|37 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G80 2017">Hyundai J</a>||
|
||||
|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G80 2018-19">Hyundai H</a>||
|
||||
|Genesis|G90 2017-18|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G90 2017-18">Hyundai C</a>||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Advanced Trim) 2023">Hyundai A</a>||
|
||||
|Genesis|GV60 (Performance Trim) 2023[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Performance Trim) 2023">Hyundai K</a>||
|
||||
|Genesis|GV70 2022-23[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV70 2022-23">Hyundai L</a>||
|
||||
|GMC|Acadia 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=GMC&model=Acadia 2018">OBD-II</a>|<a href="https://www.youtube.com/watch?v=0ZN6DdsBUZo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=GMC&model=Sierra 1500 2020-21">GM</a>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord 2018-22">Honda Bosch A</a>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord Hybrid 2018-22">Honda Bosch A</a>||
|
||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2016-18">Honda Nidec</a>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>4</sup>](#footnotes)|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2019-21">Honda Bosch A</a>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2022">Honda Bosch B</a>||
|
||||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Honda Bosch A</a>||
|
||||
|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2022">Honda Bosch B</a>||
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Honda Nidec</a>||
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Honda Bosch A</a>||
|
||||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Honda Bosch A</a>||
|
||||
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=e 2020">Honda Bosch A</a>||
|
||||
|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Fit 2018-20">Honda Nidec</a>||
|
||||
|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Freed 2020">Honda Nidec</a>||
|
||||
|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=HR-V 2019-22">Honda Nidec</a>||
|
||||
|Honda|Insight 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Insight 2019-22">Honda Bosch A</a>||
|
||||
|Honda|Inspire 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Inspire 2018">Honda Bosch A</a>||
|
||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Odyssey 2018-20">Honda Nidec</a>||
|
||||
|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Passport 2019-21">Honda Nidec</a>||
|
||||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Pilot 2016-22">Honda Nidec</a>||
|
||||
|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Ridgeline 2017-22">Honda Nidec</a>||
|
||||
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2017-19">Hyundai B</a>||
|
||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2021-23">Hyundai K</a>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra GT 2017-19">Hyundai E</a>||
|
||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra Hybrid 2021-23">Hyundai K</a>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Genesis 2015-16">Hyundai J</a>||
|
||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=i30 2017-19">Hyundai E</a>||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (Southeast Asia only) 2022-23">Hyundai Q</a>||
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>5</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Hyundai Q</a>||
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>5</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Hyundai K</a>||
|
||||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Electric 2019">Hyundai C</a>||
|
||||
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Electric 2020">Hyundai H</a>||
|
||||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Hyundai C</a>||
|
||||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Hyundai H</a>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Hyundai C</a>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2020-21">Hyundai H</a>||
|
||||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona 2020">Hyundai B</a>||
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Electric 2018-21">Hyundai G</a>||
|
||||
|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Electric 2022">Hyundai O</a>||
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Hybrid 2020">Hyundai I</a>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Palisade 2020-22">Hyundai H</a>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Cruz 2022-23">Hyundai N</a>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe 2019-20">Hyundai D</a>||
|
||||
|Hyundai|Santa Fe 2021-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe 2021-22">Hyundai L</a>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe Hybrid 2022">Hyundai L</a>||
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe Plug-in Hybrid 2022">Hyundai L</a>||
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Sonata 2018-19">Hyundai E</a>||
|
||||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Sonata 2020-23">Hyundai A</a>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Sonata Hybrid 2020-22">Hyundai A</a>||
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2021">Hyundai L</a>||
|
||||
|Hyundai|Tucson 2022[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2022">Hyundai N</a>||
|
||||
|Hyundai|Tucson 2023[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2023">Hyundai N</a>||
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson Diesel 2019">Hyundai L</a>||
|
||||
|Hyundai|Tucson Hybrid 2022[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson Hybrid 2022">Hyundai N</a>||
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Veloster 2019-20">Hyundai E</a>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Jeep&model=Grand Cherokee 2016-18">FCA</a>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Jeep&model=Grand Cherokee 2019-21">FCA</a>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Ceed 2019">Hyundai E</a>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (Southeast Asia only) 2022-23">Hyundai P</a>||
|
||||
|Kia|EV6 (with HDA II) 2022[<sup>5</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (with HDA II) 2022">Hyundai P</a>||
|
||||
|Kia|EV6 (without HDA II) 2022[<sup>5</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (without HDA II) 2022">Hyundai L</a>||
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Forte 2019-21">Hyundai G</a>||
|
||||
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=K5 2021-22">Hyundai A</a>||
|
||||
|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=K5 Hybrid 2020">Hyundai A</a>||
|
||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2019">Hyundai H</a>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2020">Hyundai F</a>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2021">Hyundai C</a>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2022">Hyundai H</a>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2021">Hyundai F</a>||
|
||||
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2022">Hyundai H</a>||
|
||||
|Kia|Niro Hybrid 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2023">Hyundai A</a>||
|
||||
|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[<sup>1</sup>](#footnotes)|10 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Hyundai C</a>||
|
||||
|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Plug-in Hybrid 2020">Hyundai D</a>||
|
||||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Optima 2017">Hyundai B</a>||
|
||||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Optima 2019-20">Hyundai G</a>||
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Seltos 2021">Hyundai A</a>||
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2018">Hyundai C</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2019">Hyundai E</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2021-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2021-23">Hyundai K</a>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Hyundai A</a>||
|
||||
|Kia|Sportage 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage 2023">Hyundai N</a>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage Hybrid 2023">Hyundai N</a>||
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Stinger 2018-20">Hyundai C</a>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Stinger 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Stinger 2022">Hyundai K</a>||
|
||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Telluride 2020-22">Hyundai H</a>||
|
||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=CT Hybrid 2017-18">Toyota</a>||
|
||||
|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=ES 2019-22">Toyota</a>||
|
||||
|Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=ES Hybrid 2017-18">Toyota</a>||
|
||||
|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=ES Hybrid 2019-23">Toyota</a>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=IS 2017-19">Toyota</a>||
|
||||
|Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=NX 2018-19">Toyota</a>||
|
||||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=NX 2020-21">Toyota</a>||
|
||||
|Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=NX Hybrid 2018-19">Toyota</a>||
|
||||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=NX Hybrid 2020-21">Toyota</a>||
|
||||
|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RC 2018-20">Toyota</a>||
|
||||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX 2016">Toyota</a>||
|
||||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX 2017-19">Toyota</a>||
|
||||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX 2020-22">Toyota</a>||
|
||||
|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2016">Toyota</a>||
|
||||
|Lexus|RX Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2017-19">Toyota</a>||
|
||||
|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2020-21">Toyota</a>||
|
||||
|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=UX Hybrid 2019-22">Toyota</a>||
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=MAN&model=eTGE 2020-23">J533</a>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=MAN&model=TGE 2017-23">J533</a>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Mazda&model=CX-5 2022-23">Mazda</a>||
|
||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Mazda&model=CX-9 2021-23">Mazda</a>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=Altima 2019-20">Nissan B</a>||
|
||||
|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=Leaf 2018-22">Nissan A</a>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=Rogue 2018-20">Nissan A</a>||
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=X-Trail 2017">Nissan A</a>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Ram&model=1500 2019-23">Ram</a>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Ateca 2018">J533</a>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Leon 2014-20">J533</a>||
|
||||
|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Ascent 2019-21">Subaru A</a>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2018-19">Subaru A</a>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2020-23">Subaru A</a>||
|
||||
|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Forester 2019-21">Subaru A</a>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2017-19">Subaru A</a>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2020-22">Subaru A</a>||
|
||||
|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Legacy 2020-22">Subaru B</a>||
|
||||
|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Outback 2020-22">Subaru B</a>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2018-19">Subaru A</a>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2020-21">Subaru A</a>||
|
||||
|Škoda|Fabia 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Fabia 2022-23">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Kamiq 2021">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Karoq 2019-21">J533</a>||
|
||||
|Škoda|Kodiaq 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Kodiaq 2017-23">J533</a>||
|
||||
|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Octavia 2015, 2018-19">J533</a>||
|
||||
|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Octavia RS 2016">J533</a>||
|
||||
|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Scala 2020">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Superb 2015-22">J533</a>||
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Alphard 2019-20">Toyota</a>||
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Alphard Hybrid 2021">Toyota</a>||
|
||||
|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon 2016">Toyota</a>||
|
||||
|Toyota|Avalon 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon 2017-18">Toyota</a>||
|
||||
|Toyota|Avalon 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon 2019-21">Toyota</a>||
|
||||
|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon 2022">Toyota</a>||
|
||||
|Toyota|Avalon Hybrid 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon Hybrid 2019-21">Toyota</a>||
|
||||
|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Avalon Hybrid 2022">Toyota</a>||
|
||||
|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR 2017-20">Toyota</a>||
|
||||
|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR 2021">Toyota</a>||
|
||||
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2017-20">Toyota</a>||
|
||||
|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2021-22">Toyota</a>||
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>6</sup>](#footnotes)|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry 2021-23|All|openpilot|0 mph[<sup>6</sup>](#footnotes)|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2021-23">Toyota</a>||
|
||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2021-23">Toyota</a>||
|
||||
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla 2017-19">Toyota</a>||
|
||||
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla 2020-22">Toyota</a>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla Cross (Non-US only) 2020-23">Toyota</a>||
|
||||
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla Cross Hybrid (Non-US only) 2020-22">Toyota</a>||
|
||||
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla Hatchback 2019-22">Toyota</a>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla Hybrid 2020-22">Toyota</a>||
|
||||
|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla Hybrid (Non-US only) 2020-23">Toyota</a>||
|
||||
|Toyota|Highlander 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Highlander 2017-19">Toyota</a>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Highlander 2020-23">Toyota</a>||
|
||||
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Highlander Hybrid 2017-19">Toyota</a>||
|
||||
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Highlander Hybrid 2020-23">Toyota</a>||
|
||||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Mirai 2021">Toyota</a>||
|
||||
|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius 2016">Toyota</a>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius 2017-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius 2021-22">Toyota</a>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius Prime 2017-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius Prime 2021-22">Toyota</a>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Prius v 2017">Toyota</a>||
|
||||
|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 2016">Toyota</a>||
|
||||
|Toyota|RAV4 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 2017-18">Toyota</a>||
|
||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 2019-21">Toyota</a>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 2022">Toyota</a>||
|
||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 Hybrid 2016">Toyota</a>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 Hybrid 2017-18">Toyota</a>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 Hybrid 2019-21">Toyota</a>||
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 Hybrid 2022">Toyota</a>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Sienna 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon 2018-22">J533</a>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon eHybrid 2020-22">J533</a>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon R 2020-22">J533</a>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Atlas 2018-23">J533</a>||
|
||||
|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Atlas Cross Sport 2021-22">J533</a>||
|
||||
|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=California 2021">J533</a>||
|
||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Caravelle 2020">J533</a>||
|
||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=CC 2018-22">J533</a>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Crafter 2017-23">J533</a>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=e-Crafter 2018-23">J533</a>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=e-Golf 2014-20">J533</a>||
|
||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf 2015-20">J533</a>||
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf Alltrack 2015-19">J533</a>||
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf GTD 2015-20">J533</a>||
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf GTE 2015-20">J533</a>||
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf GTI 2015-21">J533</a>||
|
||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf R 2015-19">J533</a>||
|
||||
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Golf SportsVan 2015-20">J533</a>||
|
||||
|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Grand California 2019-23">J533</a>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Jetta 2018-22">J533</a>||
|
||||
|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Jetta GLI 2021-22">J533</a>||
|
||||
|Volkswagen|Passat 2015-22[<sup>8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Passat 2015-22">J533</a>||
|
||||
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Passat Alltrack 2015-22">J533</a>||
|
||||
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Passat GTE 2015-22">J533</a>||
|
||||
|Volkswagen|Polo 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Polo 2020-22">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Volkswagen|Polo GTI 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Polo GTI 2020-22">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=T-Cross 2021">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=T-Roc 2021">J533</a>[<sup>10</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Taos 2022">J533</a>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont 2018-22">J533</a>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">J533</a>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont X 2021-22">J533</a>||
|
||||
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Tiguan 2018-23">J533</a>||
|
||||
|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Touran 2017">J533</a>||
|
||||
|
||||
<a id="footnotes"></a>
|
||||
<sup>1</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>2</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>3</sup>When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>4</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>5</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>6</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
<sup>7</sup>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). 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.) <br />
|
||||
<sup>8</sup>Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.) <br />
|
||||
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>3</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>5</sup>Requires a <a href="https://comma.ai/shop/panda" target="_blank">red panda</a> for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. All the hardware needed is sold in the <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD kit</a>. <br />
|
||||
<sup>6</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>7</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>8</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
<sup>9</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
||||
<sup>10</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
||||
|
||||
## Community Maintained Cars
|
||||
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
|
||||
|
||||
@@ -8,4 +8,4 @@ Additionally, on specific supported cars (see ACC column in [supported cars](CAR
|
||||
* Stock ACC is replaced by openpilot ACC.
|
||||
* openpilot FCW operates in addition to stock FCW.
|
||||
|
||||
openpilot should preserve all other vehicle's stock features, including, but are not limited to: FCW, Automatic Emergency Braking (AEB), auto high-beam, blind spot warning, and side collision warning.
|
||||
openpilot should preserve all other vehicle's stock features, including, but not limited to: FCW, Automatic Emergency Braking (AEB), auto high-beam, blind spot warning, and side collision warning.
|
||||
|
||||
@@ -41,7 +41,7 @@ clean:
|
||||
|
||||
@echo "Building rst files..."
|
||||
sphinx-apidoc -o "$(DOCSBUILDDIR)" ../ \
|
||||
../xx ../laika_repo ../rednose_repo ../pyextra ../notebooks ../panda_jungle \
|
||||
../xx ../laika_repo ../rednose_repo ../notebooks ../panda_jungle \
|
||||
../third_party \
|
||||
../panda/examples \
|
||||
../scripts \
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="500.972 178.81 20 14.375" width="20" height="14.375" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com">
|
||||
<defs>
|
||||
<clipPath id="clip0_1674_1768">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#clip0_1674_1768)" transform="matrix(1, 0, 0, 1, 500.971771, 175.81041)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.582 5.2447C19.352 4.3611 18.6743 3.66531 17.8138 3.42916C16.2542 3 10 3 10 3C10 3 3.7458 3 2.18614 3.42916C1.32557 3.66531 0.647841 4.3611 0.417841 5.2447C0 6.84612 0 10.1875 0 10.1875C0 10.1875 0 13.5288 0.417841 15.1303C0.647841 16.0139 1.32557 16.7097 2.18614 16.946C3.7458 17.375 10 17.375 10 17.375C10 17.375 16.2542 17.375 17.8138 16.946C18.6743 16.7097 19.352 16.0139 19.582 15.1303C20 13.5288 20 10.1875 20 10.1875C20 10.1875 20 6.84612 19.582 5.2447ZM8.12509 13.6255V7.37549L13.1251 10.5006L8.12509 13.6255Z" fill="#FF0000"/>
|
||||
</g>
|
||||
<path d="M 425.503 122.113 L 428.63 127.117 L 422.376 127.117 L 425.503 122.113 Z" style="stroke: rgb(0, 0, 0); stroke-width: 0px; fill: rgb(255, 255, 255);" transform="matrix(0, 1, -1, 0, 636.212463, -239.192383)" bx:shape="triangle 422.376 122.113 6.254 5.004 0.5 0 1@278e08c4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -83,7 +83,7 @@ common
|
||||
sensorsd
|
||||
^^^^^^^^
|
||||
.. autodoxygenindex::
|
||||
:project: selfdrive_sensord_sensors
|
||||
:project: system_sensord_sensors
|
||||
|
||||
boardd
|
||||
^^^^^^
|
||||
|
||||
@@ -4,19 +4,13 @@ ENV PYTHONUNBUFFERED 1
|
||||
|
||||
ENV OPENPILOT_PATH /home/batman/openpilot/
|
||||
ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
|
||||
ENV POETRY_VIRUALENVS_CREATE false
|
||||
|
||||
RUN mkdir -p ${OPENPILOT_PATH}
|
||||
WORKDIR ${OPENPILOT_PATH}
|
||||
|
||||
COPY Pipfile Pipfile.lock $OPENPILOT_PATH
|
||||
RUN pip install --no-cache-dir pipenv==2021.5.29 pip==21.3.1 && \
|
||||
pipenv install --system --deploy --dev --clear && \
|
||||
pip uninstall -y pipenv
|
||||
|
||||
|
||||
COPY SConstruct ${OPENPILOT_PATH}
|
||||
|
||||
COPY ./pyextra ${OPENPILOT_PATH}/pyextra
|
||||
COPY ./third_party ${OPENPILOT_PATH}/third_party
|
||||
COPY ./site_scons ${OPENPILOT_PATH}/site_scons
|
||||
COPY ./laika ${OPENPILOT_PATH}/laika
|
||||
|
||||
@@ -5,7 +5,7 @@ openpilot
|
||||
:maxdepth: 4
|
||||
|
||||
Debugging <selfdrive/debug/README.md>
|
||||
selfdrive/loggerd/README.md
|
||||
system/loggerd/README.md
|
||||
Driver Monitoring <selfdrive/monitoring/README.md>
|
||||
Process Replay <selfdrive/test/process_replay/README.md>
|
||||
|
||||
@@ -77,3 +77,4 @@ tools
|
||||
Simulator <tools/sim/README.md>
|
||||
tools/ssh/README.md
|
||||
Webcam <tools/webcam/README.md>
|
||||
tools/cabana/README.md
|
||||
|
||||
@@ -74,7 +74,7 @@ function launch {
|
||||
|
||||
# handle pythonpath
|
||||
ln -sfn $(pwd) /data/pythonpath
|
||||
export PYTHONPATH="$PWD:$PWD/pyextra"
|
||||
export PYTHONPATH="$PWD"
|
||||
|
||||
# hardware specific init
|
||||
agnos_init
|
||||
|
||||
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
|
||||
export VECLIB_MAXIMUM_THREADS=1
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="6.1"
|
||||
export AGNOS_VERSION="6.2"
|
||||
fi
|
||||
|
||||
if [ -z "$PASSIVE" ]; then
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
path_classifiers:
|
||||
library:
|
||||
- external
|
||||
- third_party
|
||||
- pyextra
|
||||
- tools/lib/mkvparse
|
||||
extraction:
|
||||
cpp:
|
||||
after_prepare:
|
||||
- "pip3 install --upgrade --user pkgconfig cython setuptools wheel"
|
||||
- "pip3 install --upgrade --user jinja2 pyyaml cython pycapnp numpy sympy tqdm\
|
||||
\ cffi logentries zmq scons"
|
||||
- "export PATH=/opt/work/.local/bin:$PATH"
|
||||
index:
|
||||
build_command: "scons"
|
||||
javascript:
|
||||
index:
|
||||
filters:
|
||||
- exclude: "*"
|
||||
@@ -2,7 +2,7 @@
|
||||
python_version = 3.8
|
||||
plugins = numpy.typing.mypy_plugin
|
||||
files = body, common, docs, scripts, selfdrive, site_scons, system, tools
|
||||
exclude = ^(pyextra/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)
|
||||
exclude = ^(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)
|
||||
|
||||
; third-party packages
|
||||
ignore_missing_imports = True
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
[tool.poetry]
|
||||
name = "openpilot"
|
||||
version = "0.1.0"
|
||||
description = "an open source driver assistance system"
|
||||
authors = ["Vehicle Researcher <user@comma.ai>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/commaai/openpilot"
|
||||
documentation = "https://docs.comma.ai"
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "~3.8"
|
||||
atomicwrites = "^1.4.0"
|
||||
casadi = { version = "==3.5.5", platform = "linux" }
|
||||
cffi = "^1.15.1"
|
||||
crcmod = "^1.7"
|
||||
cryptography = "^37.0.4"
|
||||
Cython = "^0.29.30"
|
||||
flake8 = "^4.0.1"
|
||||
Flask = "^2.1.2"
|
||||
future-fstrings = "^1.2.0" # for acados
|
||||
gunicorn = "^20.1.0"
|
||||
hatanaka = "==2.4"
|
||||
hexdump = "^3.3"
|
||||
Jinja2 = "^3.1.2"
|
||||
json-rpc = "^1.13.0"
|
||||
libusb1 = "^3.0.0"
|
||||
nose = "^1.3.7"
|
||||
numpy = "^1.23.0"
|
||||
onnx = "^1.12.0"
|
||||
onnxruntime-gpu = { version = "^1.11.1", platform = "linux" }
|
||||
pillow = "^9.2.0"
|
||||
poetry = "==1.2.2"
|
||||
protobuf = "==3.20.1"
|
||||
psutil = "^5.9.1"
|
||||
pycapnp = "==1.1.0"
|
||||
pycryptodome = "^3.15.0"
|
||||
PyJWT = "^2.5.0"
|
||||
pylint = "^2.15.4"
|
||||
pyopencl = "^2022.2.4"
|
||||
pyserial = "^3.5"
|
||||
python-dateutil = "^2.8.2"
|
||||
PyYAML = "^6.0"
|
||||
pyzmq = "^23.2.0"
|
||||
requests = "^2.28.1"
|
||||
scons = "^4.3.0"
|
||||
sentry-sdk = "^1.6.0"
|
||||
setproctitle = "^1.2.3"
|
||||
six = "^1.16.0"
|
||||
smbus2 = "^0.4.2"
|
||||
sounddevice = "^0.4.5"
|
||||
spidev = { version = "^3.6", platform = "linux" }
|
||||
spidev2 = { version = "^0.9.0", platform = "linux" }
|
||||
sympy = "^1.10.1"
|
||||
timezonefinder = "^6.0.1"
|
||||
tqdm = "^4.64.0"
|
||||
urllib3 = "^1.26.10"
|
||||
utm = "^0.7.0"
|
||||
websocket_client = "^1.3.3"
|
||||
polyline = "^1.4.0"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
av = "^9.2.0"
|
||||
azure-storage-blob = "~2.1"
|
||||
breathe = "^4.34.0"
|
||||
carla = { version = "==0.9.13", platform = "linux" }
|
||||
control = "^0.9.2"
|
||||
coverage = "^6.4.1"
|
||||
dictdiffer = "^0.9.0"
|
||||
fastcluster = "^1.2.6"
|
||||
ft4222 = "^1.4.1"
|
||||
hexdump = "^3.3"
|
||||
hypothesis = "==6.46.7"
|
||||
inputs = "^0.5"
|
||||
lru-dict = "^1.1.7"
|
||||
lxml = "^4.9.1"
|
||||
markdown-it-py = "^2.1.0"
|
||||
matplotlib = "^3.5.2"
|
||||
mpld3 = "^0.5.8"
|
||||
mypy = "^0.961"
|
||||
myst-parser = "^0.18.0"
|
||||
natsort = "^8.1.0"
|
||||
numpy = "^1.23.0"
|
||||
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl", platform = "linux" }
|
||||
pandas = "^1.4.3"
|
||||
parameterized = "^0.8.1"
|
||||
paramiko = "^2.11.0"
|
||||
pprofile = "^2.1.0"
|
||||
pre-commit = "^2.19.0"
|
||||
pycurl = "^7.45.1"
|
||||
pygame = "^2.1.2"
|
||||
pyprof2calltree = "^1.4.5"
|
||||
pytest = "^7.1.2"
|
||||
pytest-xdist = "^2.5.0"
|
||||
reverse_geocoder = "^1.5.1"
|
||||
scipy = "^1.8.1"
|
||||
sphinx = "^5.0.2"
|
||||
sphinx-rtd-theme = "^1.0.0"
|
||||
sphinx-sitemap = "^2.2.0"
|
||||
subprocess32 = "^3.5.4"
|
||||
tabulate = "^0.8.10"
|
||||
tenacity = "^8.0.1"
|
||||
types-atomicwrites = "^1.4.5"
|
||||
types-certifi = "^2021.10.8"
|
||||
types-pycurl = "^7.45.1"
|
||||
types-PyYAML = "^6.0"
|
||||
types-requests = "^2.28.11"
|
||||
types-tabulate = "^0.8.10"
|
||||
|
||||
|
||||
[tool.poetry.group.xx]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.xx.dependencies]
|
||||
aenum = "^3.1.11"
|
||||
aiohttp = "^3.8.1"
|
||||
albumentations = "^1.2.1"
|
||||
apex = { url = "https://github.com/commaai/apex/releases/download/pytorch2.0.0%2Bcu11.8/apex-0.1-cp38-cp38-linux_x86_64.whl" }
|
||||
azure-cli-core = "^2.38.0"
|
||||
azure-common = "^1.1.28"
|
||||
azure-core = "^1.24.2"
|
||||
azure-nspkg = "~3.0"
|
||||
azure-storage-common = "~2.1"
|
||||
azure-storage-nspkg = "~3.1"
|
||||
blosc = "==1.9.2"
|
||||
cloudpickle = "^2.1.0"
|
||||
configargparse = "^1.5.3"
|
||||
cupy-cuda11x = "^11.6.0"
|
||||
datadog = "^0.44.0"
|
||||
dotmap = "^1.3.30"
|
||||
einops = "^0.5.0"
|
||||
elasticsearch = "^8.3.1"
|
||||
Flask-Cors = "^3.0.10"
|
||||
Flask-SocketIO = "^5.2.0"
|
||||
GeoAlchemy2 = "^0.12.1"
|
||||
imageio = "^2.19.5"
|
||||
influxdb-client = "^1.30.0"
|
||||
ipykernel = "^6.15.1"
|
||||
ipython = "^8.4.0"
|
||||
joblib = "^1.1.0"
|
||||
json-logging-py = "^0.2"
|
||||
jupyter = "^1.0.0"
|
||||
jupyterlab = "^3.4.4"
|
||||
jupyterlab-vim = "^0.15.1"
|
||||
Markdown = "^3.4.1"
|
||||
msgpack-python = "^0.5.6"
|
||||
networkx = "~2.8"
|
||||
nvidia-ml-py3 = "^7.352.0"
|
||||
onnx2torch = "^1.5.4"
|
||||
onnxoptimizer = "^0.3.1"
|
||||
osmium = "^3.3.0"
|
||||
pillow-avif-plugin = "^1.2.2"
|
||||
pipenv = "==2022.10.12"
|
||||
plotly = "^5.9.0"
|
||||
pycuda = "^2022.1"
|
||||
Pygments = "^2.12.0"
|
||||
PyMySQL = "~0.9"
|
||||
pyproj = "^3.3.1"
|
||||
python-logstash = "^0.4.8"
|
||||
redis = "^4.3.4"
|
||||
s2sphere = "^0.2.5"
|
||||
scikit-image = "^0.19.3"
|
||||
scikit-learn = "^1.1.1"
|
||||
segmentation-models-pytorch = "==0.2.1"
|
||||
simplejson = "^3.17.6"
|
||||
SQLAlchemy = "^1.4.39"
|
||||
torch = { url = "https://download.pytorch.org/whl/cu118/torch-2.0.0%2Bcu118-cp38-cp38-linux_x86_64.whl" }
|
||||
torchsummary = "^1.5.1"
|
||||
torchvision = { url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.1%2Bcu118-cp38-cp38-linux_x86_64.whl" }
|
||||
triton = "^2.0.0"
|
||||
Werkzeug = "^2.1.2"
|
||||
zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" }
|
||||
omegaconf = "^2.3.0"
|
||||
osmnx = "==1.2.2"
|
||||
tritonclient = {version = "2.28.0", extras = ["http"]}
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -11,15 +11,19 @@ SOURCE_DIR="$(git rev-parse --show-toplevel)"
|
||||
|
||||
if [ -f /TICI ]; then
|
||||
FILES_SRC="release/files_tici"
|
||||
RELEASE_BRANCH=release3-staging
|
||||
DASHCAM_BRANCH=dashcam3-staging
|
||||
else
|
||||
exit 0
|
||||
echo "no release files set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$RELEASE_BRANCH" ]; then
|
||||
echo "RELEASE_BRANCH is not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# set git identity
|
||||
source $DIR/identity.sh
|
||||
export GIT_SSH_COMMAND="ssh -i /data/gitkey"
|
||||
|
||||
echo "[-] Setting up repo T=$SECONDS"
|
||||
rm -rf $BUILD_DIR
|
||||
@@ -27,7 +31,6 @@ mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
git init
|
||||
git remote add origin git@github.com:commaai/openpilot.git
|
||||
git fetch origin $RELEASE_BRANCH
|
||||
git checkout --orphan $RELEASE_BRANCH
|
||||
|
||||
# do the files copy
|
||||
@@ -48,7 +51,6 @@ echo "#define COMMA_VERSION \"$VERSION-release\"" > common/version.h
|
||||
echo "[-] committing version $VERSION T=$SECONDS"
|
||||
git add -f .
|
||||
git commit -a -m "openpilot v$VERSION release"
|
||||
git branch --set-upstream-to=origin/$RELEASE_BRANCH
|
||||
|
||||
# Build panda firmware
|
||||
pushd panda/
|
||||
@@ -105,10 +107,12 @@ RELEASE=1 selfdrive/test/test_onroad.py
|
||||
selfdrive/car/tests/test_car_interfaces.py
|
||||
rm -rf $TEST_FILES
|
||||
|
||||
if [ ! -z "$PUSH" ]; then
|
||||
echo "[-] pushing T=$SECONDS"
|
||||
git push -f origin $RELEASE_BRANCH
|
||||
if [ ! -z "$RELEASE_BRANCH" ]; then
|
||||
echo "[-] pushing release T=$SECONDS"
|
||||
git push -f origin $RELEASE_BRANCH:$RELEASE_BRANCH
|
||||
fi
|
||||
|
||||
if [ ! -z "$DASHCAM_BRANCH" ]; then
|
||||
# Create dashcam
|
||||
git rm selfdrive/car/*/carcontroller.py
|
||||
git commit -m "create dashcam release from release"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
while read hash submodule ref; do
|
||||
git -C $submodule fetch --depth 100 origin master
|
||||
git -C $submodule fetch --depth 1000 origin master
|
||||
git -C $submodule branch -r --contains $hash | grep "origin/master"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "$submodule ok"
|
||||
|
||||
@@ -71,6 +71,7 @@ selfdrive/rtshield.py
|
||||
selfdrive/statsd.py
|
||||
|
||||
system/logmessaged.py
|
||||
system/micd.py
|
||||
system/swaglog.py
|
||||
system/version.py
|
||||
|
||||
@@ -90,8 +91,12 @@ selfdrive/boardd/boardd_api_impl.pyx
|
||||
selfdrive/boardd/can_list_to_can_capnp.cc
|
||||
selfdrive/boardd/panda.cc
|
||||
selfdrive/boardd/panda.h
|
||||
selfdrive/boardd/spi.cc
|
||||
selfdrive/boardd/panda_comms.h
|
||||
selfdrive/boardd/panda_comms.cc
|
||||
selfdrive/boardd/set_time.py
|
||||
selfdrive/boardd/pandad.py
|
||||
selfdrive/boardd/tests/test_boardd_loopback.py
|
||||
|
||||
selfdrive/car/__init__.py
|
||||
selfdrive/car/docs_definitions.py
|
||||
@@ -141,6 +146,7 @@ selfdrive/debug/vw_mqb_config.py
|
||||
common/SConscript
|
||||
common/version.h
|
||||
|
||||
common/prefix.h
|
||||
common/swaglog.h
|
||||
common/swaglog.cc
|
||||
common/statlog.h
|
||||
@@ -186,8 +192,6 @@ selfdrive/controls/lib/pid.py
|
||||
selfdrive/controls/lib/radar_helpers.py
|
||||
selfdrive/controls/lib/vehicle_model.py
|
||||
|
||||
selfdrive/controls/lib/cluster/*
|
||||
|
||||
selfdrive/controls/lib/lateral_mpc_lib/.gitignore
|
||||
selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore
|
||||
selfdrive/controls/lib/lateral_mpc_lib/*
|
||||
@@ -212,21 +216,19 @@ system/hardware/tici/amplifier.py
|
||||
system/hardware/tici/updater
|
||||
system/hardware/tici/iwlist.py
|
||||
system/hardware/pc/__init__.py
|
||||
system/hardware/pc/hardware.h
|
||||
system/hardware/pc/hardware.py
|
||||
|
||||
selfdrive/locationd/__init__.py
|
||||
selfdrive/locationd/.gitignore
|
||||
selfdrive/locationd/SConscript
|
||||
selfdrive/locationd/ubloxd.cc
|
||||
selfdrive/locationd/ublox_msg.cc
|
||||
selfdrive/locationd/ublox_msg.h
|
||||
selfdrive/locationd/generated/ubx.cpp
|
||||
selfdrive/locationd/generated/ubx.h
|
||||
selfdrive/locationd/generated/gps.cpp
|
||||
selfdrive/locationd/generated/gps.h
|
||||
system/ubloxd/.gitignore
|
||||
system/ubloxd/SConscript
|
||||
system/ubloxd/generated/*
|
||||
system/ubloxd/*.h
|
||||
system/ubloxd/*.cc
|
||||
|
||||
selfdrive/locationd/__init__.py
|
||||
selfdrive/locationd/SConscript
|
||||
selfdrive/locationd/.gitignore
|
||||
selfdrive/locationd/laikad.py
|
||||
selfdrive/locationd/laikad_helpers.py
|
||||
selfdrive/locationd/locationd.h
|
||||
selfdrive/locationd/locationd.cc
|
||||
selfdrive/locationd/paramsd.py
|
||||
@@ -252,36 +254,36 @@ system/proclogd/main.cc
|
||||
system/proclogd/proclog.cc
|
||||
system/proclogd/proclog.h
|
||||
|
||||
selfdrive/loggerd/.gitignore
|
||||
selfdrive/loggerd/SConscript
|
||||
selfdrive/loggerd/encoder/encoder.cc
|
||||
selfdrive/loggerd/encoder/encoder.h
|
||||
selfdrive/loggerd/encoder/v4l_encoder.cc
|
||||
selfdrive/loggerd/encoder/v4l_encoder.h
|
||||
selfdrive/loggerd/video_writer.cc
|
||||
selfdrive/loggerd/video_writer.h
|
||||
selfdrive/loggerd/logger.cc
|
||||
selfdrive/loggerd/logger.h
|
||||
selfdrive/loggerd/loggerd.cc
|
||||
selfdrive/loggerd/loggerd.h
|
||||
selfdrive/loggerd/encoderd.cc
|
||||
selfdrive/loggerd/bootlog.cc
|
||||
selfdrive/loggerd/encoder/ffmpeg_encoder.cc
|
||||
selfdrive/loggerd/encoder/ffmpeg_encoder.h
|
||||
system/loggerd/.gitignore
|
||||
system/loggerd/SConscript
|
||||
system/loggerd/encoder/encoder.cc
|
||||
system/loggerd/encoder/encoder.h
|
||||
system/loggerd/encoder/v4l_encoder.cc
|
||||
system/loggerd/encoder/v4l_encoder.h
|
||||
system/loggerd/video_writer.cc
|
||||
system/loggerd/video_writer.h
|
||||
system/loggerd/logger.cc
|
||||
system/loggerd/logger.h
|
||||
system/loggerd/loggerd.cc
|
||||
system/loggerd/loggerd.h
|
||||
system/loggerd/encoderd.cc
|
||||
system/loggerd/bootlog.cc
|
||||
system/loggerd/encoder/ffmpeg_encoder.cc
|
||||
system/loggerd/encoder/ffmpeg_encoder.h
|
||||
|
||||
selfdrive/loggerd/__init__.py
|
||||
selfdrive/loggerd/config.py
|
||||
selfdrive/loggerd/uploader.py
|
||||
selfdrive/loggerd/deleter.py
|
||||
selfdrive/loggerd/xattr_cache.py
|
||||
system/loggerd/__init__.py
|
||||
system/loggerd/config.py
|
||||
system/loggerd/uploader.py
|
||||
system/loggerd/deleter.py
|
||||
system/loggerd/xattr_cache.py
|
||||
|
||||
selfdrive/sensord/SConscript
|
||||
selfdrive/sensord/libdiag.h
|
||||
selfdrive/sensord/sensors_qcom2.cc
|
||||
selfdrive/sensord/sensors/*.cc
|
||||
selfdrive/sensord/sensors/*.h
|
||||
selfdrive/sensord/sensord
|
||||
selfdrive/sensord/pigeond.py
|
||||
system/sensord/.gitignore
|
||||
system/sensord/SConscript
|
||||
system/sensord/sensors_qcom2.cc
|
||||
system/sensord/sensors/*.cc
|
||||
system/sensord/sensors/*.h
|
||||
system/sensord/sensord
|
||||
system/sensord/pigeond.py
|
||||
|
||||
selfdrive/thermald/thermald.py
|
||||
selfdrive/thermald/power_monitoring.py
|
||||
@@ -317,12 +319,17 @@ selfdrive/ui/qt/widgets/*.cc
|
||||
selfdrive/ui/qt/widgets/*.h
|
||||
selfdrive/ui/qt/maps/*.cc
|
||||
selfdrive/ui/qt/maps/*.h
|
||||
selfdrive/ui/qt/setup/*.cc
|
||||
selfdrive/ui/qt/setup/*.h
|
||||
|
||||
selfdrive/ui/installer/*.cc
|
||||
selfdrive/ui/installer/*.h
|
||||
selfdrive/ui/installer/*.cc
|
||||
|
||||
system/camerad/SConscript
|
||||
system/camerad/main.cc
|
||||
|
||||
system/camerad/snapshot/*
|
||||
system/camerad/include/*
|
||||
system/camerad/cameras/camera_common.h
|
||||
system/camerad/cameras/camera_common.cc
|
||||
system/camerad/cameras/sensor2_i2c.h
|
||||
@@ -344,20 +351,28 @@ selfdrive/manager/test/test_manager.py
|
||||
selfdrive/modeld/__init__.py
|
||||
selfdrive/modeld/SConscript
|
||||
selfdrive/modeld/modeld.cc
|
||||
selfdrive/modeld/navmodeld.cc
|
||||
selfdrive/modeld/dmonitoringmodeld.cc
|
||||
selfdrive/modeld/constants.py
|
||||
selfdrive/modeld/modeld
|
||||
selfdrive/modeld/navmodeld
|
||||
selfdrive/modeld/dmonitoringmodeld
|
||||
|
||||
selfdrive/modeld/models/commonmodel.cc
|
||||
selfdrive/modeld/models/commonmodel.h
|
||||
|
||||
selfdrive/modeld/models/driving.cc
|
||||
selfdrive/modeld/models/driving.h
|
||||
selfdrive/modeld/models/supercombo.onnx
|
||||
|
||||
selfdrive/modeld/models/dmonitoring.cc
|
||||
selfdrive/modeld/models/dmonitoring.h
|
||||
selfdrive/modeld/models/supercombo.onnx
|
||||
selfdrive/modeld/models/dmonitoring_model_q.dlc
|
||||
|
||||
selfdrive/modeld/models/nav.cc
|
||||
selfdrive/modeld/models/nav.h
|
||||
selfdrive/modeld/models/navmodel_q.dlc
|
||||
|
||||
selfdrive/modeld/transforms/loadyuv.cc
|
||||
selfdrive/modeld/transforms/loadyuv.h
|
||||
selfdrive/modeld/transforms/loadyuv.cl
|
||||
@@ -370,7 +385,6 @@ selfdrive/modeld/thneed/thneed.h
|
||||
selfdrive/modeld/thneed/thneed_common.cc
|
||||
selfdrive/modeld/thneed/thneed_qcom2.cc
|
||||
selfdrive/modeld/thneed/serialize.cc
|
||||
selfdrive/modeld/thneed/include/*
|
||||
|
||||
selfdrive/modeld/runners/snpemodel.cc
|
||||
selfdrive/modeld/runners/snpemodel.h
|
||||
@@ -398,8 +412,11 @@ selfdrive/assets/offroad/*
|
||||
selfdrive/assets/sounds/*
|
||||
selfdrive/assets/training/*
|
||||
|
||||
third_party/.gitignore
|
||||
third_party/SConscript
|
||||
|
||||
third_party/cluster/*
|
||||
|
||||
third_party/linux/**
|
||||
third_party/opencl/**
|
||||
|
||||
@@ -422,15 +439,14 @@ third_party/snpe/dsp**
|
||||
third_party/acados/x86_64/**
|
||||
third_party/acados/larch64/**
|
||||
third_party/acados/include/**
|
||||
third_party/acados/acados_template/**
|
||||
|
||||
third_party/bootstrap/**
|
||||
third_party/qt5/larch64/bin/**
|
||||
|
||||
scripts/update_now.sh
|
||||
scripts/stop_updater.sh
|
||||
|
||||
pyextra/.gitignore
|
||||
pyextra/acados_template/**
|
||||
|
||||
rednose/.gitignore
|
||||
rednose/**
|
||||
laika/**
|
||||
@@ -480,6 +496,7 @@ cereal/visionipc/*.pxd
|
||||
|
||||
panda/.gitignore
|
||||
panda/__init__.py
|
||||
panda/SConscript
|
||||
panda/board/**
|
||||
panda/certs/**
|
||||
panda/crypto/**
|
||||
@@ -563,15 +580,13 @@ opendbc/tesla_radar.dbc
|
||||
opendbc/tesla_powertrain.dbc
|
||||
|
||||
tinygrad_repo/openpilot/compile.py
|
||||
tinygrad_repo/accel/opencl/*
|
||||
tinygrad_repo/extra/onnx.py
|
||||
tinygrad_repo/extra/onnx_ops.py
|
||||
tinygrad_repo/extra/thneed.py
|
||||
tinygrad_repo/extra/utils.py
|
||||
tinygrad_repo/tinygrad/llops/ops_gpu.py
|
||||
tinygrad_repo/tinygrad/llops/ops_opencl.py
|
||||
tinygrad_repo/tinygrad/helpers.py
|
||||
tinygrad_repo/tinygrad/mlops.py
|
||||
tinygrad_repo/tinygrad/ops.py
|
||||
tinygrad_repo/tinygrad/shapetracker.py
|
||||
tinygrad_repo/tinygrad/tensor.py
|
||||
tinygrad_repo/tinygrad/nn/__init__.py
|
||||
tinygrad_repo/tinygrad/nn/optim.py
|
||||
tinygrad_repo/tinygrad/codegen/ast.py
|
||||
tinygrad_repo/tinygrad/codegen/gpu.py
|
||||
tinygrad_repo/tinygrad/nn/*
|
||||
tinygrad_repo/tinygrad/runtime/ops_gpu.py
|
||||
tinygrad_repo/tinygrad/shape/*
|
||||
tinygrad_repo/tinygrad/*.py
|
||||
|
||||
@@ -13,7 +13,7 @@ system/camerad/cameras/camera_util.cc
|
||||
system/camerad/cameras/camera_util.h
|
||||
system/camerad/cameras/real_debayer.cl
|
||||
|
||||
selfdrive/sensord/rawgps/*
|
||||
system/sensord/rawgps/*
|
||||
|
||||
selfdrive/ui/qt/spinner_larch64
|
||||
selfdrive/ui/qt/text_larch64
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
nmcli connection modify --temporary lte ipv4.route-metric 1
|
||||
nmcli connection modify --temporary lte ipv6.route-metric 1
|
||||
nmcli con up lte
|
||||
nmcli connection modify --temporary lte ipv4.route-metric 1 ipv6.route-metric 1
|
||||
nmcli con up --wait --ask lte
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file alias="bootstrap-icons.svg">../../third_party/bootstrap/bootstrap-icons.svg</file>
|
||||
<file>img_continue_triangle.svg</file>
|
||||
<file>img_circled_check.svg</file>
|
||||
<file>img_circled_slash.svg</file>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 278.364 122.673 C 282.129 126.545 284.418 132.064 284.5 137.465 L 284.5 200.562 C 284.419 205.963 282.129 211.48 278.364 215.352 C 274.492 219.119 268.972 221.407 263.571 221.488 L 259.262 221.488 L 259.262 238.417 C 259.128 241.171 257.425 244.12 255.107 245.614 C 252.655 246.875 249.249 246.874 246.796 245.613 C 244.48 244.12 242.777 241.171 242.643 238.417 L 242.643 221.488 L 57.357 221.488 L 57.357 238.417 C 57.223 241.171 55.519 244.121 53.203 245.614 C 50.75 246.875 47.344 246.875 44.892 245.614 C 42.575 244.12 40.872 241.171 40.738 238.417 L 40.738 221.488 L 36.427 221.488 C 31.027 221.407 25.508 219.119 21.636 215.352 C 17.871 211.48 15.582 205.961 15.5 200.56 L 15.5 137.463 C 15.581 132.062 17.871 126.545 21.636 122.673 C 25.508 118.906 31.029 116.618 36.429 116.536 L 40.738 116.536 L 40.738 87.024 C 40.426 78.719 43.434 70.271 48.95 64.028 C 54.579 57.887 62.818 53.986 71.131 53.441 L 228.982 53.444 C 237.295 53.989 245.421 57.888 251.05 64.028 C 256.566 70.272 259.574 78.719 259.262 87.024 L 259.262 116.536 L 263.573 116.536 C 268.974 116.618 274.492 118.906 278.364 122.673 Z M 60.911 75.581 C 58.147 78.782 56.932 82.617 57.35 86.825 L 57.357 118.356 C 60.791 119.893 63.643 122.022 66.115 125.423 C 68.512 128.877 69.916 133.256 69.976 137.46 L 69.976 167.014 L 230.024 167.014 L 230.024 137.466 C 230.084 133.262 231.488 128.877 233.884 125.423 C 236.357 122.021 239.208 119.892 242.643 118.356 L 242.643 86.988 C 243.06 82.779 241.852 78.782 239.089 75.581 C 236.438 72.283 232.951 70.432 228.785 70.059 L 71.215 70.059 C 67.049 70.433 63.563 72.283 60.911 75.581 Z M 33.383 203.605 C 34.241 204.572 35.14 204.944 36.431 204.87 L 53.357 204.87 L 53.357 137.465 C 53.432 136.174 53.06 135.278 52.093 134.42 C 51.236 133.454 50.336 133.08 49.046 133.155 L 36.429 133.155 C 35.139 133.08 34.295 133.506 33.382 134.419 C 32.47 135.332 32.044 136.176 32.119 137.466 L 32.119 200.56 C 32.044 201.851 32.416 202.747 33.383 203.605 Z M 69.976 204.87 L 230.024 204.87 L 230.024 183.631 L 69.976 183.631 L 69.976 204.87 Z M 267.881 137.465 C 267.956 136.174 267.584 135.278 266.618 134.419 C 265.758 133.453 264.86 133.08 263.57 133.155 L 250.953 133.155 C 249.663 133.08 248.766 133.452 247.908 134.419 C 246.941 135.277 246.568 136.176 246.643 137.466 L 246.643 204.87 L 263.571 204.87 C 264.862 204.944 265.759 204.572 266.616 203.606 C 267.583 202.748 267.956 201.849 267.881 200.559 L 267.881 137.465 Z" fill="black" style="stroke-width: 4px; vector-effect: non-scaling-stroke; fill-opacity: 0.4;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="url(#paint0_radial_1352_397)"/>
|
||||
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="#E11C1C"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_1352_397" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(150 150) rotate(90) scale(125)">
|
||||
<stop offset="0.135417" stop-color="#E11C1C"/>
|
||||
<stop offset="1" stop-color="#FF8C22"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="black" fill-opacity="0.36"/>
|
||||
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="black" fill-opacity="0.36"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="white"/>
|
||||
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg fill="red" width="72" height="80" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg" id="marker">
|
||||
<path stroke="darkred" stroke-width="0.5" d="M7.5 1C5.42312 1 3 2.2883 3 5.56759C3 7.79276 6.46156 12.7117 7.5 14C8.42309 12.7117 12 7.90993 12 5.56759C12 2.2883 9.57688 1 7.5 1Z"/>
|
||||
<circle cx="7.5" cy="5.5" r="2" fill="darkred"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 389 B |
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import bz2
|
||||
import hashlib
|
||||
@@ -14,14 +16,15 @@ import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from dataclasses import asdict, dataclass, replace
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from typing import Any, Dict
|
||||
from queue import Queue
|
||||
from typing import BinaryIO, Callable, Dict, List, Optional, Set, Union, cast
|
||||
|
||||
import requests
|
||||
from jsonrpc import JSONRPCResponseManager, dispatcher
|
||||
from websocket import (ABNF, WebSocketException, WebSocketTimeoutException,
|
||||
from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException,
|
||||
create_connection)
|
||||
|
||||
import cereal.messaging as messaging
|
||||
@@ -33,8 +36,8 @@ from common.file_helpers import CallbackReader
|
||||
from common.params import Params
|
||||
from common.realtime import sec_since_boot, set_core_affinity
|
||||
from system.hardware import HARDWARE, PC, AGNOS
|
||||
from selfdrive.loggerd.config import ROOT
|
||||
from selfdrive.loggerd.xattr_cache import getxattr, setxattr
|
||||
from system.loggerd.config import ROOT
|
||||
from system.loggerd.xattr_cache import getxattr, setxattr
|
||||
from selfdrive.statsd import STATS_DIR
|
||||
from system.swaglog import SWAGLOG_DIR, cloudlog
|
||||
from system.version import get_commit, get_origin, get_short_branch, get_version
|
||||
@@ -54,19 +57,54 @@ WS_FRAME_SIZE = 4096
|
||||
|
||||
NetworkType = log.DeviceState.NetworkType
|
||||
|
||||
UploadFileDict = Dict[str, Union[str, int, float, bool]]
|
||||
UploadItemDict = Dict[str, Union[str, bool, int, float, Dict[str, str]]]
|
||||
|
||||
UploadFilesToUrlResponse = Dict[str, Union[int, List[UploadItemDict], List[str]]]
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadFile:
|
||||
fn: str
|
||||
url: str
|
||||
headers: Dict[str, str]
|
||||
allow_cellular: bool
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: Dict) -> UploadFile:
|
||||
return cls(d.get("fn", ""), d.get("url", ""), d.get("headers", {}), d.get("allow_cellular", False))
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadItem:
|
||||
path: str
|
||||
url: str
|
||||
headers: Dict[str, str]
|
||||
created_at: int
|
||||
id: Optional[str]
|
||||
retry_count: int = 0
|
||||
current: bool = False
|
||||
progress: float = 0
|
||||
allow_cellular: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: Dict) -> UploadItem:
|
||||
return cls(d["path"], d["url"], d["headers"], d["created_at"], d["id"], d["retry_count"], d["current"],
|
||||
d["progress"], d["allow_cellular"])
|
||||
|
||||
|
||||
dispatcher["echo"] = lambda s: s
|
||||
recv_queue: Any = queue.Queue()
|
||||
send_queue: Any = queue.Queue()
|
||||
upload_queue: Any = queue.Queue()
|
||||
low_priority_send_queue: Any = queue.Queue()
|
||||
log_recv_queue: Any = queue.Queue()
|
||||
cancelled_uploads: Any = set()
|
||||
UploadItem = namedtuple('UploadItem', ['path', 'url', 'headers', 'created_at', 'id', 'retry_count', 'current', 'progress', 'allow_cellular'], defaults=(0, False, 0, False))
|
||||
recv_queue: Queue[str] = queue.Queue()
|
||||
send_queue: Queue[str] = queue.Queue()
|
||||
upload_queue: Queue[UploadItem] = queue.Queue()
|
||||
low_priority_send_queue: Queue[str] = queue.Queue()
|
||||
log_recv_queue: Queue[str] = queue.Queue()
|
||||
cancelled_uploads: Set[str] = set()
|
||||
|
||||
cur_upload_items: Dict[int, Any] = {}
|
||||
cur_upload_items: Dict[int, Optional[UploadItem]] = {}
|
||||
|
||||
|
||||
def strip_bz2_extension(fn):
|
||||
def strip_bz2_extension(fn: str) -> str:
|
||||
if fn.endswith('.bz2'):
|
||||
return fn[:-4]
|
||||
return fn
|
||||
@@ -76,29 +114,30 @@ class AbortTransferException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UploadQueueCache():
|
||||
class UploadQueueCache:
|
||||
params = Params()
|
||||
|
||||
@staticmethod
|
||||
def initialize(upload_queue):
|
||||
def initialize(upload_queue: Queue[UploadItem]) -> None:
|
||||
try:
|
||||
upload_queue_json = UploadQueueCache.params.get("AthenadUploadQueue")
|
||||
if upload_queue_json is not None:
|
||||
for item in json.loads(upload_queue_json):
|
||||
upload_queue.put(UploadItem(**item))
|
||||
upload_queue.put(UploadItem.from_dict(item))
|
||||
except Exception:
|
||||
cloudlog.exception("athena.UploadQueueCache.initialize.exception")
|
||||
|
||||
@staticmethod
|
||||
def cache(upload_queue):
|
||||
def cache(upload_queue: Queue[UploadItem]) -> None:
|
||||
try:
|
||||
items = [i._asdict() for i in upload_queue.queue if i.id not in cancelled_uploads]
|
||||
queue: List[Optional[UploadItem]] = list(upload_queue.queue)
|
||||
items = [asdict(i) for i in queue if i is not None and (i.id not in cancelled_uploads)]
|
||||
UploadQueueCache.params.put("AthenadUploadQueue", json.dumps(items))
|
||||
except Exception:
|
||||
cloudlog.exception("athena.UploadQueueCache.cache.exception")
|
||||
|
||||
|
||||
def handle_long_poll(ws):
|
||||
def handle_long_poll(ws: WebSocket) -> None:
|
||||
end_event = threading.Event()
|
||||
|
||||
threads = [
|
||||
@@ -126,7 +165,7 @@ def handle_long_poll(ws):
|
||||
thread.join()
|
||||
|
||||
|
||||
def jsonrpc_handler(end_event):
|
||||
def jsonrpc_handler(end_event: threading.Event) -> None:
|
||||
dispatcher["startLocalProxy"] = partial(startLocalProxy, end_event)
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
@@ -147,11 +186,12 @@ def jsonrpc_handler(end_event):
|
||||
|
||||
|
||||
def retry_upload(tid: int, end_event: threading.Event, increase_count: bool = True) -> None:
|
||||
if cur_upload_items[tid].retry_count < MAX_RETRY_COUNT:
|
||||
item = cur_upload_items[tid]
|
||||
item = cur_upload_items[tid]
|
||||
if item is not None and item.retry_count < MAX_RETRY_COUNT:
|
||||
new_retry_count = item.retry_count + 1 if increase_count else item.retry_count
|
||||
|
||||
item = item._replace(
|
||||
item = replace(
|
||||
item,
|
||||
retry_count=new_retry_count,
|
||||
progress=0,
|
||||
current=False
|
||||
@@ -175,44 +215,44 @@ def upload_handler(end_event: threading.Event) -> None:
|
||||
cur_upload_items[tid] = None
|
||||
|
||||
try:
|
||||
cur_upload_items[tid] = upload_queue.get(timeout=1)._replace(current=True)
|
||||
cur_upload_items[tid] = item = replace(upload_queue.get(timeout=1), current=True)
|
||||
|
||||
if cur_upload_items[tid].id in cancelled_uploads:
|
||||
cancelled_uploads.remove(cur_upload_items[tid].id)
|
||||
if item.id in cancelled_uploads:
|
||||
cancelled_uploads.remove(item.id)
|
||||
continue
|
||||
|
||||
# Remove item if too old
|
||||
age = datetime.now() - datetime.fromtimestamp(cur_upload_items[tid].created_at / 1000)
|
||||
age = datetime.now() - datetime.fromtimestamp(item.created_at / 1000)
|
||||
if age.total_seconds() > MAX_AGE:
|
||||
cloudlog.event("athena.upload_handler.expired", item=cur_upload_items[tid], error=True)
|
||||
cloudlog.event("athena.upload_handler.expired", item=item, error=True)
|
||||
continue
|
||||
|
||||
# Check if uploading over metered connection is allowed
|
||||
sm.update(0)
|
||||
metered = sm['deviceState'].networkMetered
|
||||
network_type = sm['deviceState'].networkType.raw
|
||||
if metered and (not cur_upload_items[tid].allow_cellular):
|
||||
if metered and (not item.allow_cellular):
|
||||
retry_upload(tid, end_event, False)
|
||||
continue
|
||||
|
||||
try:
|
||||
def cb(sz, cur):
|
||||
def cb(sz: int, cur: int) -> None:
|
||||
# Abort transfer if connection changed to metered after starting upload
|
||||
sm.update(0)
|
||||
metered = sm['deviceState'].networkMetered
|
||||
if metered and (not cur_upload_items[tid].allow_cellular):
|
||||
if metered and (not item.allow_cellular):
|
||||
raise AbortTransferException
|
||||
|
||||
cur_upload_items[tid] = cur_upload_items[tid]._replace(progress=cur / sz if sz else 1)
|
||||
cur_upload_items[tid] = replace(item, progress=cur / sz if sz else 1)
|
||||
|
||||
fn = cur_upload_items[tid].path
|
||||
fn = item.path
|
||||
try:
|
||||
sz = os.path.getsize(fn)
|
||||
except OSError:
|
||||
sz = -1
|
||||
|
||||
cloudlog.event("athena.upload_handler.upload_start", fn=fn, sz=sz, network_type=network_type, metered=metered, retry_count=cur_upload_items[tid].retry_count)
|
||||
response = _do_upload(cur_upload_items[tid], cb)
|
||||
cloudlog.event("athena.upload_handler.upload_start", fn=fn, sz=sz, network_type=network_type, metered=metered, retry_count=item.retry_count)
|
||||
response = _do_upload(item, cb)
|
||||
|
||||
if response.status_code not in (200, 201, 401, 403, 412):
|
||||
cloudlog.event("athena.upload_handler.retry", status_code=response.status_code, fn=fn, sz=sz, network_type=network_type, metered=metered)
|
||||
@@ -234,7 +274,7 @@ def upload_handler(end_event: threading.Event) -> None:
|
||||
cloudlog.exception("athena.upload_handler.exception")
|
||||
|
||||
|
||||
def _do_upload(upload_item, callback=None):
|
||||
def _do_upload(upload_item: UploadItem, callback: Optional[Callable] = None) -> requests.Response:
|
||||
path = upload_item.path
|
||||
compress = False
|
||||
|
||||
@@ -244,27 +284,25 @@ def _do_upload(upload_item, callback=None):
|
||||
compress = True
|
||||
|
||||
with open(path, "rb") as f:
|
||||
data: BinaryIO
|
||||
if compress:
|
||||
cloudlog.event("athena.upload_handler.compress", fn=path, fn_orig=upload_item.path)
|
||||
data = bz2.compress(f.read())
|
||||
size = len(data)
|
||||
data = io.BytesIO(data)
|
||||
compressed = bz2.compress(f.read())
|
||||
size = len(compressed)
|
||||
data = io.BytesIO(compressed)
|
||||
else:
|
||||
size = os.fstat(f.fileno()).st_size
|
||||
data = f
|
||||
|
||||
if callback:
|
||||
data = CallbackReader(data, callback, size)
|
||||
|
||||
return requests.put(upload_item.url,
|
||||
data=data,
|
||||
data=CallbackReader(data, callback, size) if callback else data,
|
||||
headers={**upload_item.headers, 'Content-Length': str(size)},
|
||||
timeout=30)
|
||||
|
||||
|
||||
# security: user should be able to request any message from their car
|
||||
@dispatcher.add_method
|
||||
def getMessage(service=None, timeout=1000):
|
||||
def getMessage(service: str, timeout: int = 1000) -> Dict:
|
||||
if service is None or service not in service_list:
|
||||
raise Exception("invalid service")
|
||||
|
||||
@@ -274,7 +312,8 @@ def getMessage(service=None, timeout=1000):
|
||||
if ret is None:
|
||||
raise TimeoutError
|
||||
|
||||
return ret.to_dict()
|
||||
# this is because capnp._DynamicStructReader doesn't have typing information
|
||||
return cast(Dict, ret.to_dict())
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
@@ -288,7 +327,7 @@ def getVersion() -> Dict[str, str]:
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def setNavDestination(latitude=0, longitude=0, place_name=None, place_details=None):
|
||||
def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: Optional[str] = None, place_details: Optional[str] = None) -> Dict[str, int]:
|
||||
destination = {
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
@@ -300,8 +339,8 @@ def setNavDestination(latitude=0, longitude=0, place_name=None, place_details=No
|
||||
return {"success": 1}
|
||||
|
||||
|
||||
def scan_dir(path, prefix):
|
||||
files = list()
|
||||
def scan_dir(path: str, prefix: str) -> List[str]:
|
||||
files = []
|
||||
# only walk directories that match the prefix
|
||||
# (glob and friends traverse entire dir tree)
|
||||
with os.scandir(path) as i:
|
||||
@@ -320,18 +359,18 @@ def scan_dir(path, prefix):
|
||||
return files
|
||||
|
||||
@dispatcher.add_method
|
||||
def listDataDirectory(prefix=''):
|
||||
def listDataDirectory(prefix='') -> List[str]:
|
||||
return scan_dir(ROOT, prefix)
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def reboot():
|
||||
def reboot() -> Dict[str, int]:
|
||||
sock = messaging.sub_sock("deviceState", timeout=1000)
|
||||
ret = messaging.recv_one(sock)
|
||||
if ret is None or ret.deviceState.started:
|
||||
raise Exception("Reboot unavailable")
|
||||
|
||||
def do_reboot():
|
||||
def do_reboot() -> None:
|
||||
time.sleep(2)
|
||||
HARDWARE.reboot()
|
||||
|
||||
@@ -341,50 +380,53 @@ def reboot():
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def uploadFileToUrl(fn, url, headers):
|
||||
return uploadFilesToUrls([{
|
||||
def uploadFileToUrl(fn: str, url: str, headers: Dict[str, str]) -> UploadFilesToUrlResponse:
|
||||
# this is because mypy doesn't understand that the decorator doesn't change the return type
|
||||
response: UploadFilesToUrlResponse = uploadFilesToUrls([{
|
||||
"fn": fn,
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
}])
|
||||
return response
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def uploadFilesToUrls(files_data):
|
||||
items = []
|
||||
failed = []
|
||||
for file in files_data:
|
||||
fn = file.get('fn', '')
|
||||
if len(fn) == 0 or fn[0] == '/' or '..' in fn or 'url' not in file:
|
||||
failed.append(fn)
|
||||
def uploadFilesToUrls(files_data: List[UploadFileDict]) -> UploadFilesToUrlResponse:
|
||||
files = map(UploadFile.from_dict, files_data)
|
||||
|
||||
items: List[UploadItemDict] = []
|
||||
failed: List[str] = []
|
||||
for file in files:
|
||||
if len(file.fn) == 0 or file.fn[0] == '/' or '..' in file.fn or len(file.url) == 0:
|
||||
failed.append(file.fn)
|
||||
continue
|
||||
|
||||
path = os.path.join(ROOT, fn)
|
||||
path = os.path.join(ROOT, file.fn)
|
||||
if not os.path.exists(path) and not os.path.exists(strip_bz2_extension(path)):
|
||||
failed.append(fn)
|
||||
failed.append(file.fn)
|
||||
continue
|
||||
|
||||
# Skip item if already in queue
|
||||
url = file['url'].split('?')[0]
|
||||
url = file.url.split('?')[0]
|
||||
if any(url == item['url'].split('?')[0] for item in listUploadQueue()):
|
||||
continue
|
||||
|
||||
item = UploadItem(
|
||||
path=path,
|
||||
url=file['url'],
|
||||
headers=file.get('headers', {}),
|
||||
url=file.url,
|
||||
headers=file.headers,
|
||||
created_at=int(time.time() * 1000),
|
||||
id=None,
|
||||
allow_cellular=file.get('allow_cellular', False),
|
||||
allow_cellular=file.allow_cellular,
|
||||
)
|
||||
upload_id = hashlib.sha1(str(item).encode()).hexdigest()
|
||||
item = item._replace(id=upload_id)
|
||||
item = replace(item, id=upload_id)
|
||||
upload_queue.put_nowait(item)
|
||||
items.append(item._asdict())
|
||||
items.append(asdict(item))
|
||||
|
||||
UploadQueueCache.cache(upload_queue)
|
||||
|
||||
resp = {"enqueued": len(items), "items": items}
|
||||
resp: UploadFilesToUrlResponse = {"enqueued": len(items), "items": items}
|
||||
if failed:
|
||||
resp["failed"] = failed
|
||||
|
||||
@@ -392,32 +434,32 @@ def uploadFilesToUrls(files_data):
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def listUploadQueue():
|
||||
def listUploadQueue() -> List[UploadItemDict]:
|
||||
items = list(upload_queue.queue) + list(cur_upload_items.values())
|
||||
return [i._asdict() for i in items if (i is not None) and (i.id not in cancelled_uploads)]
|
||||
return [asdict(i) for i in items if (i is not None) and (i.id not in cancelled_uploads)]
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def cancelUpload(upload_id):
|
||||
def cancelUpload(upload_id: Union[str, List[str]]) -> Dict[str, Union[int, str]]:
|
||||
if not isinstance(upload_id, list):
|
||||
upload_id = [upload_id]
|
||||
|
||||
uploading_ids = {item.id for item in list(upload_queue.queue)}
|
||||
cancelled_ids = uploading_ids.intersection(upload_id)
|
||||
if len(cancelled_ids) == 0:
|
||||
return 404
|
||||
return {"success": 0, "error": "not found"}
|
||||
|
||||
cancelled_uploads.update(cancelled_ids)
|
||||
return {"success": 1}
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def primeActivated(activated):
|
||||
def primeActivated(activated: bool) -> Dict[str, int]:
|
||||
return {"success": 1}
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def setBandwithLimit(upload_speed_kbps, download_speed_kbps):
|
||||
def setBandwithLimit(upload_speed_kbps: int, download_speed_kbps: int) -> Dict[str, Union[int, str]]:
|
||||
if not AGNOS:
|
||||
return {"success": 0, "error": "only supported on AGNOS"}
|
||||
|
||||
@@ -428,7 +470,7 @@ def setBandwithLimit(upload_speed_kbps, download_speed_kbps):
|
||||
return {"success": 0, "error": "failed to set limit", "stdout": e.stdout, "stderr": e.stderr}
|
||||
|
||||
|
||||
def startLocalProxy(global_end_event, remote_ws_uri, local_port):
|
||||
def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> Dict[str, int]:
|
||||
try:
|
||||
if local_port not in LOCAL_PORT_WHITELIST:
|
||||
raise Exception("Requested local port not whitelisted")
|
||||
@@ -462,7 +504,7 @@ def startLocalProxy(global_end_event, remote_ws_uri, local_port):
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getPublicKey():
|
||||
def getPublicKey() -> Optional[str]:
|
||||
if not os.path.isfile(PERSIST + '/comma/id_rsa.pub'):
|
||||
return None
|
||||
|
||||
@@ -471,7 +513,7 @@ def getPublicKey():
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getSshAuthorizedKeys():
|
||||
def getSshAuthorizedKeys() -> str:
|
||||
return Params().get("GithubSshKeys", encoding='utf8') or ''
|
||||
|
||||
|
||||
@@ -486,7 +528,7 @@ def getNetworkType():
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getNetworkMetered():
|
||||
def getNetworkMetered() -> bool:
|
||||
network_type = HARDWARE.get_network_type()
|
||||
return HARDWARE.get_network_metered(network_type)
|
||||
|
||||
@@ -497,7 +539,7 @@ def getNetworks():
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def takeSnapshot():
|
||||
def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]:
|
||||
from system.camerad.snapshot.snapshot import jpeg_write, snapshot
|
||||
ret = snapshot()
|
||||
if ret is not None:
|
||||
@@ -514,16 +556,19 @@ def takeSnapshot():
|
||||
raise Exception("not available while camerad is started")
|
||||
|
||||
|
||||
def get_logs_to_send_sorted():
|
||||
def get_logs_to_send_sorted() -> List[str]:
|
||||
# TODO: scan once then use inotify to detect file creation/deletion
|
||||
curr_time = int(time.time())
|
||||
logs = []
|
||||
for log_entry in os.listdir(SWAGLOG_DIR):
|
||||
log_path = os.path.join(SWAGLOG_DIR, log_entry)
|
||||
time_sent = 0
|
||||
try:
|
||||
time_sent = int.from_bytes(getxattr(log_path, LOG_ATTR_NAME), sys.byteorder)
|
||||
value = getxattr(log_path, LOG_ATTR_NAME)
|
||||
if value is not None:
|
||||
time_sent = int.from_bytes(value, sys.byteorder)
|
||||
except (ValueError, TypeError):
|
||||
time_sent = 0
|
||||
pass
|
||||
# assume send failed and we lost the response if sent more than one hour ago
|
||||
if not time_sent or curr_time - time_sent > 3600:
|
||||
logs.append(log_entry)
|
||||
@@ -531,7 +576,7 @@ def get_logs_to_send_sorted():
|
||||
return sorted(logs)[:-1]
|
||||
|
||||
|
||||
def log_handler(end_event):
|
||||
def log_handler(end_event: threading.Event) -> None:
|
||||
if PC:
|
||||
return
|
||||
|
||||
@@ -593,7 +638,7 @@ def log_handler(end_event):
|
||||
cloudlog.exception("athena.log_handler.exception")
|
||||
|
||||
|
||||
def stat_handler(end_event):
|
||||
def stat_handler(end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
last_scan = 0
|
||||
curr_scan = sec_since_boot()
|
||||
@@ -619,7 +664,7 @@ def stat_handler(end_event):
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def ws_proxy_recv(ws, local_sock, ssock, end_event, global_end_event):
|
||||
def ws_proxy_recv(ws: WebSocket, local_sock: socket.socket, ssock: socket.socket, end_event: threading.Event, global_end_event: threading.Event) -> None:
|
||||
while not (end_event.is_set() or global_end_event.is_set()):
|
||||
try:
|
||||
data = ws.recv()
|
||||
@@ -638,7 +683,7 @@ def ws_proxy_recv(ws, local_sock, ssock, end_event, global_end_event):
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_proxy_send(ws, local_sock, signal_sock, end_event):
|
||||
def ws_proxy_send(ws: WebSocket, local_sock: socket.socket, signal_sock: socket.socket, end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
r, _, _ = select.select((local_sock, signal_sock), (), ())
|
||||
@@ -663,7 +708,7 @@ def ws_proxy_send(ws, local_sock, signal_sock, end_event):
|
||||
cloudlog.debug("athena.ws_proxy_send done closing sockets")
|
||||
|
||||
|
||||
def ws_recv(ws, end_event):
|
||||
def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
last_ping = int(sec_since_boot() * 1e9)
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
@@ -685,7 +730,7 @@ def ws_recv(ws, end_event):
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_send(ws, end_event):
|
||||
def ws_send(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
try:
|
||||
@@ -704,7 +749,7 @@ def ws_send(ws, end_event):
|
||||
end_event.set()
|
||||
|
||||
|
||||
def backoff(retries):
|
||||
def backoff(retries: int) -> int:
|
||||
return random.randrange(0, min(128, int(2 ** retries)))
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import time
|
||||
import threading
|
||||
import queue
|
||||
import unittest
|
||||
from dataclasses import asdict, replace
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
@@ -226,7 +227,7 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
"""When an upload times out or fails to connect it should be placed back in the queue"""
|
||||
fn = self._create_file('qlog.bz2')
|
||||
item = athenad.UploadItem(path=fn, url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
|
||||
item_no_retry = item._replace(retry_count=MAX_RETRY_COUNT)
|
||||
item_no_retry = replace(item, retry_count=MAX_RETRY_COUNT)
|
||||
|
||||
end_event = threading.Event()
|
||||
thread = threading.Thread(target=athenad.upload_handler, args=(end_event,))
|
||||
@@ -296,7 +297,7 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
self.assertEqual(len(items), 0)
|
||||
|
||||
@with_http_server
|
||||
def test_listUploadQueueCurrent(self, host):
|
||||
def test_listUploadQueueCurrent(self, host: str):
|
||||
fn = self._create_file('qlog.bz2')
|
||||
item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
|
||||
|
||||
@@ -321,7 +322,7 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
|
||||
items = dispatcher["listUploadQueue"]()
|
||||
self.assertEqual(len(items), 1)
|
||||
self.assertDictEqual(items[0], item._asdict())
|
||||
self.assertDictEqual(items[0], asdict(item))
|
||||
self.assertFalse(items[0]['current'])
|
||||
|
||||
athenad.cancelled_uploads.add(item.id)
|
||||
@@ -346,7 +347,7 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
athenad.UploadQueueCache.initialize(athenad.upload_queue)
|
||||
|
||||
self.assertEqual(athenad.upload_queue.qsize(), 1)
|
||||
self.assertDictEqual(athenad.upload_queue.queue[-1]._asdict(), item1._asdict())
|
||||
self.assertDictEqual(asdict(athenad.upload_queue.queue[-1]), asdict(item1))
|
||||
|
||||
@mock.patch('selfdrive.athena.athenad.create_connection')
|
||||
def test_startLocalProxy(self, mock_create_connection):
|
||||
@@ -417,5 +418,6 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
sl = athenad.get_logs_to_send_sorted()
|
||||
self.assertListEqual(sl, fl[:-1])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Import('env', 'envCython', 'common', 'cereal', 'messaging')
|
||||
|
||||
libs = ['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']
|
||||
env.Program('boardd', ['main.cc', 'boardd.cc', 'panda.cc'], LIBS=libs)
|
||||
env.Program('boardd', ['main.cc', 'boardd.cc', 'panda.cc', 'panda_comms.cc', 'spi.cc'], LIBS=libs)
|
||||
env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc'])
|
||||
|
||||
envCython.Program('boardd_api_impl.so', 'boardd_api_impl.pyx', LIBS=["can_list_to_can_capnp", 'capnp', 'kj'] + envCython["LIBS"])
|
||||
if GetOption('test'):
|
||||
env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc', 'panda.cc'], LIBS=libs)
|
||||
env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc', 'panda.cc', 'panda_comms.cc', 'spi.cc'], LIBS=libs)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
@@ -19,8 +20,6 @@
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "cereal/gen/cpp/car.capnp.h"
|
||||
#include "cereal/messaging/messaging.h"
|
||||
#include "common/params.h"
|
||||
@@ -50,8 +49,8 @@
|
||||
|
||||
#define MAX_IR_POWER 0.5f
|
||||
#define MIN_IR_POWER 0.0f
|
||||
#define CUTOFF_IL 200
|
||||
#define SATURATE_IL 1600
|
||||
#define CUTOFF_IL 400
|
||||
#define SATURATE_IL 1000
|
||||
#define NIBBLE_TO_HEX(n) ((n) < 10 ? (n) + '0' : ((n) - 10) + 'a')
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -67,7 +66,7 @@ static std::string get_time_str(const struct tm &time) {
|
||||
|
||||
bool check_all_connected(const std::vector<Panda *> &pandas) {
|
||||
for (const auto& panda : pandas) {
|
||||
if (!panda->connected) {
|
||||
if (!panda->connected()) {
|
||||
do_exit = true;
|
||||
return false;
|
||||
}
|
||||
@@ -107,40 +106,42 @@ void sync_time(Panda *panda, SyncTimeDir dir) {
|
||||
bool safety_setter_thread(std::vector<Panda *> pandas) {
|
||||
LOGD("Starting safety setter thread");
|
||||
|
||||
Params p;
|
||||
|
||||
// there should be at least one panda connected
|
||||
if (pandas.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set to ELM327 for fingerprinting
|
||||
// initialize to ELM327 without OBD multiplexing for fingerprinting
|
||||
bool obd_multiplexing_enabled = false;
|
||||
for (int i = 0; i < pandas.size(); i++) {
|
||||
const uint16_t safety_param = (i > 0) ? 1U : 0U;
|
||||
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
|
||||
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
|
||||
}
|
||||
|
||||
Params p = Params();
|
||||
|
||||
// wait for VIN to be read
|
||||
// openpilot can switch between multiplexing modes for different FW queries
|
||||
while (true) {
|
||||
if (do_exit || !check_all_connected(pandas) || !ignition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string value_vin = p.get("CarVin");
|
||||
if (value_vin.size() > 0) {
|
||||
// sanity check VIN format
|
||||
assert(value_vin.size() == 17);
|
||||
LOGW("got CarVin %s", value_vin.c_str());
|
||||
bool obd_multiplexing_requested = p.getBool("ObdMultiplexingEnabled");
|
||||
if (obd_multiplexing_requested != obd_multiplexing_enabled) {
|
||||
for (int i = 0; i < pandas.size(); i++) {
|
||||
const uint16_t safety_param = (i > 0 || !obd_multiplexing_requested) ? 1U : 0U;
|
||||
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
|
||||
}
|
||||
obd_multiplexing_enabled = obd_multiplexing_requested;
|
||||
p.putBool("ObdMultiplexingChanged", true);
|
||||
}
|
||||
|
||||
if (p.getBool("FirmwareQueryDone")) {
|
||||
LOGW("finished FW query");
|
||||
break;
|
||||
}
|
||||
util::sleep_for(20);
|
||||
}
|
||||
|
||||
// set to ELM327 for ECU knockouts
|
||||
for (Panda *panda : pandas) {
|
||||
panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
|
||||
}
|
||||
|
||||
std::string params;
|
||||
LOGW("waiting for params to set safety model");
|
||||
while (true) {
|
||||
@@ -184,7 +185,7 @@ bool safety_setter_thread(std::vector<Panda *> pandas) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Panda *usb_connect(std::string serial="", uint32_t index=0) {
|
||||
Panda *connect(std::string serial="", uint32_t index=0) {
|
||||
std::unique_ptr<Panda> panda;
|
||||
try {
|
||||
panda = std::make_unique<Panda>(serial, (index * PANDA_BUS_CNT));
|
||||
@@ -227,9 +228,9 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) {
|
||||
//Dont send if older than 1 second
|
||||
if ((nanos_since_boot() - event.getLogMonoTime() < 1e9) && !fake_send) {
|
||||
for (const auto& panda : pandas) {
|
||||
LOGT("sending sendcan to panda: %s", (panda->usb_serial).c_str());
|
||||
LOGT("sending sendcan to panda: %s", (panda->hw_serial()).c_str());
|
||||
panda->can_send(event.getSendcan());
|
||||
LOGT("sendcan sent to panda: %s", (panda->usb_serial).c_str());
|
||||
LOGT("sendcan sent to panda: %s", (panda->hw_serial()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +358,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!panda->comms_healthy) {
|
||||
if (!panda->comms_healthy()) {
|
||||
evt.setValid(false);
|
||||
}
|
||||
|
||||
@@ -433,7 +434,7 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) {
|
||||
// build msg
|
||||
MessageBuilder msg;
|
||||
auto evt = msg.initEvent();
|
||||
evt.setValid(panda->comms_healthy);
|
||||
evt.setValid(panda->comms_healthy());
|
||||
|
||||
auto ps = evt.initPeripheralState();
|
||||
ps.setPandaType(panda->hw_type);
|
||||
@@ -526,13 +527,10 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
|
||||
|
||||
FirstOrderFilter integ_lines_filter(0, 30.0, 0.05);
|
||||
|
||||
while (!do_exit && panda->connected) {
|
||||
while (!do_exit && panda->connected()) {
|
||||
cnt++;
|
||||
sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update?
|
||||
|
||||
// Other pandas don't have fan/IR to control
|
||||
if (panda->hw_type != cereal::PandaState::PandaType::UNO && panda->hw_type != cereal::PandaState::PandaType::DOS) continue;
|
||||
|
||||
if (sm.updated("deviceState") && !no_fan_control) {
|
||||
// Fan speed
|
||||
uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired();
|
||||
@@ -545,9 +543,8 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
|
||||
if (sm.updated("driverCameraState")) {
|
||||
auto event = sm["driverCameraState"];
|
||||
int cur_integ_lines = event.getDriverCameraState().getIntegLines();
|
||||
float cur_gain = event.getDriverCameraState().getGain();
|
||||
|
||||
cur_integ_lines = integ_lines_filter.update(cur_integ_lines * cur_gain);
|
||||
cur_integ_lines = integ_lines_filter.update(cur_integ_lines);
|
||||
last_front_frame_t = event.getLogMonoTime();
|
||||
|
||||
if (cur_integ_lines <= CUTOFF_IL) {
|
||||
@@ -595,7 +592,7 @@ void boardd_main_thread(std::vector<std::string> serials) {
|
||||
// connect to all provided serials
|
||||
std::vector<Panda *> pandas;
|
||||
for (int i = 0; i < serials.size() && !do_exit; /**/) {
|
||||
Panda *p = usb_connect(serials[i], i);
|
||||
Panda *p = connect(serials[i], i);
|
||||
if (!p) {
|
||||
// send empty pandaState & peripheralState and try again
|
||||
send_empty_panda_state(&pm);
|
||||
|
||||
@@ -4,75 +4,20 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "cereal/messaging/messaging.h"
|
||||
#include "panda/board/dlc_to_len.h"
|
||||
#include "common/gpio.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
static int init_usb_ctx(libusb_context **context) {
|
||||
assert(context != nullptr);
|
||||
|
||||
int err = libusb_init(context);
|
||||
if (err != 0) {
|
||||
LOGE("libusb initialization error");
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LIBUSB_API_VERSION >= 0x01000106
|
||||
libusb_set_option(*context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
|
||||
#else
|
||||
libusb_set_debug(*context, 3);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) {
|
||||
// init libusb
|
||||
ssize_t num_devices;
|
||||
libusb_device **dev_list = NULL;
|
||||
int err = init_usb_ctx(&ctx);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
// connect by serial
|
||||
num_devices = libusb_get_device_list(ctx, &dev_list);
|
||||
if (num_devices < 0) { goto fail; }
|
||||
for (size_t i = 0; i < num_devices; ++i) {
|
||||
libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(dev_list[i], &desc);
|
||||
if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) {
|
||||
int ret = libusb_open(dev_list[i], &dev_handle);
|
||||
if (dev_handle == NULL || ret < 0) { goto fail; }
|
||||
|
||||
unsigned char desc_serial[26] = { 0 };
|
||||
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, std::size(desc_serial));
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
usb_serial = std::string((char *)desc_serial, ret).c_str();
|
||||
if (serial.empty() || serial == usb_serial) {
|
||||
break;
|
||||
}
|
||||
libusb_close(dev_handle);
|
||||
dev_handle = NULL;
|
||||
}
|
||||
// try USB first, then SPI
|
||||
try {
|
||||
handle = std::make_unique<PandaUsbHandle>(serial);
|
||||
} catch (std::exception &e) {
|
||||
#ifndef __APPLE__
|
||||
handle = std::make_unique<PandaSpiHandle>(serial);
|
||||
#endif
|
||||
}
|
||||
if (dev_handle == NULL) goto fail;
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
dev_list = nullptr;
|
||||
|
||||
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
|
||||
libusb_detach_kernel_driver(dev_handle, 0);
|
||||
}
|
||||
|
||||
err = libusb_set_configuration(dev_handle, 1);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
err = libusb_claim_interface(dev_handle, 0);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
hw_type = get_hw_type();
|
||||
|
||||
@@ -80,197 +25,64 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) {
|
||||
(hw_type != cereal::PandaState::PandaType::GREY_PANDA));
|
||||
|
||||
has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) ||
|
||||
(hw_type == cereal::PandaState::PandaType::DOS);
|
||||
(hw_type == cereal::PandaState::PandaType::DOS) ||
|
||||
(hw_type == cereal::PandaState::PandaType::TRES);
|
||||
|
||||
can_reset_communications();
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (dev_list != NULL) {
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
}
|
||||
cleanup();
|
||||
throw std::runtime_error("Error connecting to panda");
|
||||
}
|
||||
|
||||
Panda::~Panda() {
|
||||
std::lock_guard lk(usb_lock);
|
||||
cleanup();
|
||||
connected = false;
|
||||
bool Panda::connected() {
|
||||
return handle->connected;
|
||||
}
|
||||
|
||||
void Panda::cleanup() {
|
||||
if (dev_handle) {
|
||||
libusb_release_interface(dev_handle, 0);
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
bool Panda::comms_healthy() {
|
||||
return handle->comms_healthy;
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
std::string Panda::hw_serial() {
|
||||
return handle->hw_serial;
|
||||
}
|
||||
|
||||
std::vector<std::string> Panda::list() {
|
||||
// init libusb
|
||||
ssize_t num_devices;
|
||||
libusb_context *context = NULL;
|
||||
libusb_device **dev_list = NULL;
|
||||
std::vector<std::string> serials;
|
||||
std::vector<std::string> serials = PandaUsbHandle::list();
|
||||
|
||||
int err = init_usb_ctx(&context);
|
||||
if (err != 0) { return serials; }
|
||||
|
||||
num_devices = libusb_get_device_list(context, &dev_list);
|
||||
if (num_devices < 0) {
|
||||
LOGE("libusb can't get device list");
|
||||
goto finish;
|
||||
}
|
||||
for (size_t i = 0; i < num_devices; ++i) {
|
||||
libusb_device *device = dev_list[i];
|
||||
libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(device, &desc);
|
||||
if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) {
|
||||
libusb_device_handle *handle = NULL;
|
||||
int ret = libusb_open(device, &handle);
|
||||
if (ret < 0) { goto finish; }
|
||||
|
||||
unsigned char desc_serial[26] = { 0 };
|
||||
ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, desc_serial, std::size(desc_serial));
|
||||
libusb_close(handle);
|
||||
if (ret < 0) { goto finish; }
|
||||
|
||||
serials.push_back(std::string((char *)desc_serial, ret).c_str());
|
||||
#ifndef __APPLE__
|
||||
for (auto s : PandaSpiHandle::list()) {
|
||||
if (std::find(serials.begin(), serials.end(), s) == serials.end()) {
|
||||
serials.push_back(s);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
finish:
|
||||
if (dev_list != NULL) {
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
}
|
||||
if (context) {
|
||||
libusb_exit(context);
|
||||
}
|
||||
return serials;
|
||||
}
|
||||
|
||||
void Panda::handle_usb_issue(int err, const char func[]) {
|
||||
LOGE_100("usb error %d \"%s\" in %s", err, libusb_strerror((enum libusb_error)err), func);
|
||||
if (err == LIBUSB_ERROR_NO_DEVICE) {
|
||||
LOGE("lost connection");
|
||||
connected = false;
|
||||
}
|
||||
// TODO: check other errors, is simply retrying okay?
|
||||
}
|
||||
|
||||
int Panda::usb_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) {
|
||||
int err;
|
||||
const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
|
||||
|
||||
if (!connected) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
std::lock_guard lk(usb_lock);
|
||||
do {
|
||||
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout);
|
||||
if (err < 0) handle_usb_issue(err, __func__);
|
||||
} while (err < 0 && connected);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int Panda::usb_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) {
|
||||
int err;
|
||||
const uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
|
||||
|
||||
if (!connected) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
std::lock_guard lk(usb_lock);
|
||||
do {
|
||||
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
|
||||
if (err < 0) handle_usb_issue(err, __func__);
|
||||
} while (err < 0 && connected);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int Panda::usb_bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
int err;
|
||||
int transferred = 0;
|
||||
|
||||
if (!connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard lk(usb_lock);
|
||||
do {
|
||||
// Try sending can messages. If the receive buffer on the panda is full it will NAK
|
||||
// and libusb will try again. After 5ms, it will time out. We will drop the messages.
|
||||
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);
|
||||
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
LOGW("Transmit buffer full");
|
||||
break;
|
||||
} else if (err != 0 || length != transferred) {
|
||||
handle_usb_issue(err, __func__);
|
||||
}
|
||||
} while(err != 0 && connected);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int Panda::usb_bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
int err;
|
||||
int transferred = 0;
|
||||
|
||||
if (!connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard lk(usb_lock);
|
||||
|
||||
do {
|
||||
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);
|
||||
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
break; // timeout is okay to exit, recv still happened
|
||||
} else if (err == LIBUSB_ERROR_OVERFLOW) {
|
||||
comms_healthy = false;
|
||||
LOGE_100("overflow got 0x%x", transferred);
|
||||
} else if (err != 0) {
|
||||
handle_usb_issue(err, __func__);
|
||||
}
|
||||
|
||||
} while(err != 0 && connected);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
void Panda::set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param) {
|
||||
usb_write(0xdc, (uint16_t)safety_model, safety_param);
|
||||
handle->control_write(0xdc, (uint16_t)safety_model, safety_param);
|
||||
}
|
||||
|
||||
void Panda::set_alternative_experience(uint16_t alternative_experience) {
|
||||
usb_write(0xdf, alternative_experience, 0);
|
||||
handle->control_write(0xdf, alternative_experience, 0);
|
||||
}
|
||||
|
||||
cereal::PandaState::PandaType Panda::get_hw_type() {
|
||||
unsigned char hw_query[1] = {0};
|
||||
|
||||
usb_read(0xc1, 0, 0, hw_query, 1);
|
||||
handle->control_read(0xc1, 0, 0, hw_query, 1);
|
||||
return (cereal::PandaState::PandaType)(hw_query[0]);
|
||||
}
|
||||
|
||||
void Panda::set_rtc(struct tm sys_time) {
|
||||
// tm struct has year defined as years since 1900
|
||||
usb_write(0xa1, (uint16_t)(1900 + sys_time.tm_year), 0);
|
||||
usb_write(0xa2, (uint16_t)(1 + sys_time.tm_mon), 0);
|
||||
usb_write(0xa3, (uint16_t)sys_time.tm_mday, 0);
|
||||
// usb_write(0xa4, (uint16_t)(1 + sys_time.tm_wday), 0);
|
||||
usb_write(0xa5, (uint16_t)sys_time.tm_hour, 0);
|
||||
usb_write(0xa6, (uint16_t)sys_time.tm_min, 0);
|
||||
usb_write(0xa7, (uint16_t)sys_time.tm_sec, 0);
|
||||
handle->control_write(0xa1, (uint16_t)(1900 + sys_time.tm_year), 0);
|
||||
handle->control_write(0xa2, (uint16_t)(1 + sys_time.tm_mon), 0);
|
||||
handle->control_write(0xa3, (uint16_t)sys_time.tm_mday, 0);
|
||||
// handle->control_write(0xa4, (uint16_t)(1 + sys_time.tm_wday), 0);
|
||||
handle->control_write(0xa5, (uint16_t)sys_time.tm_hour, 0);
|
||||
handle->control_write(0xa6, (uint16_t)sys_time.tm_min, 0);
|
||||
handle->control_write(0xa7, (uint16_t)sys_time.tm_sec, 0);
|
||||
}
|
||||
|
||||
struct tm Panda::get_rtc() {
|
||||
@@ -284,7 +96,7 @@ struct tm Panda::get_rtc() {
|
||||
uint8_t second;
|
||||
} rtc_time = {0};
|
||||
|
||||
usb_read(0xa0, 0, 0, (unsigned char*)&rtc_time, sizeof(rtc_time));
|
||||
handle->control_read(0xa0, 0, 0, (unsigned char*)&rtc_time, sizeof(rtc_time));
|
||||
|
||||
struct tm new_time = { 0 };
|
||||
new_time.tm_year = rtc_time.year - 1900; // tm struct has year defined as years since 1900
|
||||
@@ -298,70 +110,70 @@ struct tm Panda::get_rtc() {
|
||||
}
|
||||
|
||||
void Panda::set_fan_speed(uint16_t fan_speed) {
|
||||
usb_write(0xb1, fan_speed, 0);
|
||||
handle->control_write(0xb1, fan_speed, 0);
|
||||
}
|
||||
|
||||
uint16_t Panda::get_fan_speed() {
|
||||
uint16_t fan_speed_rpm = 0;
|
||||
usb_read(0xb2, 0, 0, (unsigned char*)&fan_speed_rpm, sizeof(fan_speed_rpm));
|
||||
handle->control_read(0xb2, 0, 0, (unsigned char*)&fan_speed_rpm, sizeof(fan_speed_rpm));
|
||||
return fan_speed_rpm;
|
||||
}
|
||||
|
||||
void Panda::set_ir_pwr(uint16_t ir_pwr) {
|
||||
usb_write(0xb0, ir_pwr, 0);
|
||||
handle->control_write(0xb0, ir_pwr, 0);
|
||||
}
|
||||
|
||||
std::optional<health_t> Panda::get_state() {
|
||||
health_t health {0};
|
||||
int err = usb_read(0xd2, 0, 0, (unsigned char*)&health, sizeof(health));
|
||||
int err = handle->control_read(0xd2, 0, 0, (unsigned char*)&health, sizeof(health));
|
||||
return err >= 0 ? std::make_optional(health) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<can_health_t> Panda::get_can_state(uint16_t can_number) {
|
||||
can_health_t can_health {0};
|
||||
int err = usb_read(0xc2, can_number, 0, (unsigned char*)&can_health, sizeof(can_health));
|
||||
int err = handle->control_read(0xc2, can_number, 0, (unsigned char*)&can_health, sizeof(can_health));
|
||||
return err >= 0 ? std::make_optional(can_health) : std::nullopt;
|
||||
}
|
||||
|
||||
void Panda::set_loopback(bool loopback) {
|
||||
usb_write(0xe5, loopback, 0);
|
||||
handle->control_write(0xe5, loopback, 0);
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8_t>> Panda::get_firmware_version() {
|
||||
std::vector<uint8_t> fw_sig_buf(128);
|
||||
int read_1 = usb_read(0xd3, 0, 0, &fw_sig_buf[0], 64);
|
||||
int read_2 = usb_read(0xd4, 0, 0, &fw_sig_buf[64], 64);
|
||||
int read_1 = handle->control_read(0xd3, 0, 0, &fw_sig_buf[0], 64);
|
||||
int read_2 = handle->control_read(0xd4, 0, 0, &fw_sig_buf[64], 64);
|
||||
return ((read_1 == 64) && (read_2 == 64)) ? std::make_optional(fw_sig_buf) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Panda::get_serial() {
|
||||
char serial_buf[17] = {'\0'};
|
||||
int err = usb_read(0xd0, 0, 0, (uint8_t*)serial_buf, 16);
|
||||
int err = handle->control_read(0xd0, 0, 0, (uint8_t*)serial_buf, 16);
|
||||
return err >= 0 ? std::make_optional(serial_buf) : std::nullopt;
|
||||
}
|
||||
|
||||
void Panda::set_power_saving(bool power_saving) {
|
||||
usb_write(0xe7, power_saving, 0);
|
||||
handle->control_write(0xe7, power_saving, 0);
|
||||
}
|
||||
|
||||
void Panda::enable_deepsleep() {
|
||||
usb_write(0xfb, 0, 0);
|
||||
handle->control_write(0xfb, 0, 0);
|
||||
}
|
||||
|
||||
void Panda::send_heartbeat(bool engaged) {
|
||||
usb_write(0xf3, engaged, 0);
|
||||
handle->control_write(0xf3, engaged, 0);
|
||||
}
|
||||
|
||||
void Panda::set_can_speed_kbps(uint16_t bus, uint16_t speed) {
|
||||
usb_write(0xde, bus, (speed * 10));
|
||||
handle->control_write(0xde, bus, (speed * 10));
|
||||
}
|
||||
|
||||
void Panda::set_data_speed_kbps(uint16_t bus, uint16_t speed) {
|
||||
usb_write(0xf9, bus, (speed * 10));
|
||||
handle->control_write(0xf9, bus, (speed * 10));
|
||||
}
|
||||
|
||||
void Panda::set_canfd_non_iso(uint16_t bus, bool non_iso) {
|
||||
usb_write(0xfc, bus, non_iso);
|
||||
handle->control_write(0xfc, bus, non_iso);
|
||||
}
|
||||
|
||||
static uint8_t len_to_dlc(uint8_t len) {
|
||||
@@ -375,17 +187,6 @@ static uint8_t len_to_dlc(uint8_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
static void write_packet(uint8_t *dest, int *write_pos, const uint8_t *src, size_t size) {
|
||||
for (int i = 0, &pos = *write_pos; i < size; ++i, ++pos) {
|
||||
// Insert counter every 64 bytes (first byte of 64 bytes USB packet)
|
||||
if (pos % USBPACKET_MAX_SIZE == 0) {
|
||||
dest[pos] = pos / USBPACKET_MAX_SIZE;
|
||||
pos++;
|
||||
}
|
||||
dest[pos] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data_list,
|
||||
std::function<void(uint8_t *, size_t)> write_func) {
|
||||
int32_t pos = 0;
|
||||
@@ -399,8 +200,7 @@ void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data
|
||||
}
|
||||
auto can_data = cmsg.getDat();
|
||||
uint8_t data_len_code = len_to_dlc(can_data.size());
|
||||
assert(can_data.size() <= ((hw_type == cereal::PandaState::PandaType::RED_PANDA ||
|
||||
hw_type == cereal::PandaState::PandaType::RED_PANDA_V2) ? 64 : 8));
|
||||
assert(can_data.size() <= 64);
|
||||
assert(can_data.size() == dlc_to_len[data_len_code]);
|
||||
|
||||
can_header header;
|
||||
@@ -408,9 +208,17 @@ void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data
|
||||
header.extended = (cmsg.getAddress() >= 0x800) ? 1 : 0;
|
||||
header.data_len_code = data_len_code;
|
||||
header.bus = bus - bus_offset;
|
||||
header.checksum = 0;
|
||||
|
||||
memcpy(&send_buf[pos], (uint8_t *)&header, sizeof(can_header));
|
||||
memcpy(&send_buf[pos + sizeof(can_header)], (uint8_t *)can_data.begin(), can_data.size());
|
||||
uint32_t msg_size = sizeof(can_header) + can_data.size();
|
||||
|
||||
// set checksum
|
||||
((can_header *) &send_buf[pos])->checksum = calculate_checksum(&send_buf[pos], msg_size);
|
||||
|
||||
pos += msg_size;
|
||||
|
||||
write_packet(send_buf, &pos, (uint8_t *)&header, sizeof(can_header));
|
||||
write_packet(send_buf, &pos, (uint8_t *)can_data.begin(), can_data.size());
|
||||
if (pos >= USB_TX_SOFT_LIMIT) {
|
||||
write_func(send_buf, pos);
|
||||
pos = 0;
|
||||
@@ -423,51 +231,76 @@ void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data
|
||||
|
||||
void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
|
||||
pack_can_buffer(can_data_list, [=](uint8_t* data, size_t size) {
|
||||
usb_bulk_write(3, data, size, 5);
|
||||
handle->bulk_write(3, data, size, 5);
|
||||
});
|
||||
}
|
||||
|
||||
bool Panda::can_receive(std::vector<can_frame>& out_vec) {
|
||||
uint8_t data[RECV_SIZE];
|
||||
int recv = usb_bulk_read(0x81, (uint8_t*)data, RECV_SIZE);
|
||||
if (!comms_healthy) {
|
||||
// Check if enough space left in buffer to store RECV_SIZE data
|
||||
assert(receive_buffer_size + RECV_SIZE <= sizeof(receive_buffer));
|
||||
|
||||
int recv = handle->bulk_read(0x81, &receive_buffer[receive_buffer_size], RECV_SIZE);
|
||||
if (!comms_healthy()) {
|
||||
return false;
|
||||
}
|
||||
if (recv == RECV_SIZE) {
|
||||
LOGW("Panda receive buffer full");
|
||||
}
|
||||
receive_buffer_size += recv;
|
||||
|
||||
return (recv <= 0) ? true : unpack_can_buffer(data, recv, out_vec);
|
||||
return (recv <= 0) ? true : unpack_can_buffer(receive_buffer, receive_buffer_size, out_vec);
|
||||
}
|
||||
|
||||
bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec) {
|
||||
recv_buf.clear();
|
||||
for (int i = 0; i < size; i += USBPACKET_MAX_SIZE) {
|
||||
if (data[i] != i / USBPACKET_MAX_SIZE) {
|
||||
LOGE("CAN: MALFORMED USB RECV PACKET");
|
||||
comms_healthy = false;
|
||||
return false;
|
||||
}
|
||||
int chunk_len = std::min(USBPACKET_MAX_SIZE, (size - i));
|
||||
recv_buf.insert(recv_buf.end(), &data[i + 1], &data[i + chunk_len]);
|
||||
}
|
||||
void Panda::can_reset_communications() {
|
||||
handle->control_write(0xc0, 0, 0);
|
||||
}
|
||||
|
||||
bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector<can_frame> &out_vec) {
|
||||
int pos = 0;
|
||||
while (pos < recv_buf.size()) {
|
||||
|
||||
while (pos <= size - sizeof(can_header)) {
|
||||
can_header header;
|
||||
memcpy(&header, &recv_buf[pos], CANPACKET_HEAD_SIZE);
|
||||
memcpy(&header, &data[pos], sizeof(can_header));
|
||||
|
||||
const uint8_t data_len = dlc_to_len[header.data_len_code];
|
||||
if (pos + sizeof(can_header) + data_len > size) {
|
||||
// we don't have all the data for this message yet
|
||||
break;
|
||||
}
|
||||
|
||||
can_frame &canData = out_vec.emplace_back();
|
||||
canData.busTime = 0;
|
||||
canData.address = header.addr;
|
||||
canData.src = header.bus + bus_offset;
|
||||
if (header.rejected) { canData.src += CANPACKET_REJECTED; }
|
||||
if (header.returned) { canData.src += CANPACKET_RETURNED; }
|
||||
if (header.rejected) {
|
||||
canData.src += CAN_REJECTED_BUS_OFFSET;
|
||||
}
|
||||
if (header.returned) {
|
||||
canData.src += CAN_RETURNED_BUS_OFFSET;
|
||||
}
|
||||
|
||||
const uint8_t data_len = dlc_to_len[header.data_len_code];
|
||||
canData.dat.assign((char *)&recv_buf[pos + CANPACKET_HEAD_SIZE], data_len);
|
||||
if (calculate_checksum(&data[pos], sizeof(can_header) + data_len) != 0) {
|
||||
LOGE("Panda CAN checksum failed");
|
||||
size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += CANPACKET_HEAD_SIZE + data_len;
|
||||
canData.dat.assign((char *)&data[pos + sizeof(can_header)], data_len);
|
||||
|
||||
pos += sizeof(can_header) + data_len;
|
||||
}
|
||||
|
||||
// move the overflowing data to the beginning of the buffer for the next round
|
||||
memmove(data, &data[pos], size - pos);
|
||||
size -= pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Panda::calculate_checksum(uint8_t *data, uint32_t len) {
|
||||
uint8_t checksum = 0U;
|
||||
for (uint32_t i = 0U; i < len; i++) {
|
||||
checksum ^= data[i];
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "cereal/gen/cpp/car.capnp.h"
|
||||
#include "cereal/gen/cpp/log.capnp.h"
|
||||
#include "panda/board/health.h"
|
||||
#include "panda/board/can_definitions.h"
|
||||
#include "selfdrive/boardd/panda_comms.h"
|
||||
|
||||
#define TIMEOUT 0
|
||||
#define PANDA_CAN_CNT 3
|
||||
#define PANDA_BUS_CNT 4
|
||||
#define RECV_SIZE (0x4000U)
|
||||
#define USB_TX_SOFT_LIMIT (0x100U)
|
||||
#define USBPACKET_MAX_SIZE (0x40)
|
||||
#define CANPACKET_HEAD_SIZE 5U
|
||||
#define CANPACKET_MAX_SIZE 72U
|
||||
#define CANPACKET_REJECTED (0xC0U)
|
||||
#define CANPACKET_RETURNED (0x80U)
|
||||
|
||||
#define RECV_SIZE (0x4000U)
|
||||
|
||||
#define CAN_REJECTED_BUS_OFFSET 0xC0U
|
||||
#define CAN_RETURNED_BUS_OFFSET 0x80U
|
||||
|
||||
struct __attribute__((packed)) can_header {
|
||||
uint8_t reserved : 1;
|
||||
@@ -34,44 +30,35 @@ struct __attribute__((packed)) can_header {
|
||||
uint8_t returned : 1;
|
||||
uint8_t extended : 1;
|
||||
uint32_t addr : 29;
|
||||
uint8_t checksum : 8;
|
||||
};
|
||||
|
||||
struct can_frame {
|
||||
long address;
|
||||
std::string dat;
|
||||
long busTime;
|
||||
long src;
|
||||
long address;
|
||||
std::string dat;
|
||||
long busTime;
|
||||
long src;
|
||||
};
|
||||
|
||||
|
||||
class Panda {
|
||||
private:
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device_handle *dev_handle = NULL;
|
||||
std::mutex usb_lock;
|
||||
std::vector<uint8_t> recv_buf;
|
||||
void handle_usb_issue(int err, const char func[]);
|
||||
void cleanup();
|
||||
private:
|
||||
std::unique_ptr<PandaCommsHandle> handle;
|
||||
|
||||
public:
|
||||
public:
|
||||
Panda(std::string serial="", uint32_t bus_offset=0);
|
||||
~Panda();
|
||||
|
||||
std::string usb_serial;
|
||||
std::atomic<bool> connected = true;
|
||||
std::atomic<bool> comms_healthy = true;
|
||||
cereal::PandaState::PandaType hw_type = cereal::PandaState::PandaType::UNKNOWN;
|
||||
bool has_rtc = false;
|
||||
const uint32_t bus_offset;
|
||||
|
||||
bool connected();
|
||||
bool comms_healthy();
|
||||
std::string hw_serial();
|
||||
|
||||
// Static functions
|
||||
static std::vector<std::string> list();
|
||||
|
||||
// HW communication
|
||||
int usb_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout=TIMEOUT);
|
||||
int usb_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout=TIMEOUT);
|
||||
int usb_bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
int usb_bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
|
||||
// Panda functionality
|
||||
cereal::PandaState::PandaType get_hw_type();
|
||||
void set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param=0U);
|
||||
@@ -94,11 +81,16 @@ class Panda {
|
||||
void set_canfd_non_iso(uint16_t bus, bool non_iso);
|
||||
void can_send(capnp::List<cereal::CanData>::Reader can_data_list);
|
||||
bool can_receive(std::vector<can_frame>& out_vec);
|
||||
void can_reset_communications();
|
||||
|
||||
protected:
|
||||
// for unit tests
|
||||
uint8_t receive_buffer[RECV_SIZE + sizeof(can_header) + 64];
|
||||
uint32_t receive_buffer_size = 0;
|
||||
|
||||
Panda(uint32_t bus_offset) : bus_offset(bus_offset) {}
|
||||
void pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data_list,
|
||||
std::function<void(uint8_t *, size_t)> write_func);
|
||||
bool unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec);
|
||||
bool unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector<can_frame> &out_vec);
|
||||
uint8_t calculate_checksum(uint8_t *data, uint32_t len);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "selfdrive/boardd/panda.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
|
||||
static int init_usb_ctx(libusb_context **context) {
|
||||
assert(context != nullptr);
|
||||
|
||||
int err = libusb_init(context);
|
||||
if (err != 0) {
|
||||
LOGE("libusb initialization error");
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LIBUSB_API_VERSION >= 0x01000106
|
||||
libusb_set_option(*context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
|
||||
#else
|
||||
libusb_set_debug(*context, 3);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PandaUsbHandle::PandaUsbHandle(std::string serial) : PandaCommsHandle(serial) {
|
||||
// init libusb
|
||||
ssize_t num_devices;
|
||||
libusb_device **dev_list = NULL;
|
||||
int err = init_usb_ctx(&ctx);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
// connect by serial
|
||||
num_devices = libusb_get_device_list(ctx, &dev_list);
|
||||
if (num_devices < 0) { goto fail; }
|
||||
for (size_t i = 0; i < num_devices; ++i) {
|
||||
libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(dev_list[i], &desc);
|
||||
if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) {
|
||||
int ret = libusb_open(dev_list[i], &dev_handle);
|
||||
if (dev_handle == NULL || ret < 0) { goto fail; }
|
||||
|
||||
unsigned char desc_serial[26] = { 0 };
|
||||
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, std::size(desc_serial));
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
hw_serial = std::string((char *)desc_serial, ret);
|
||||
if (serial.empty() || serial == hw_serial) {
|
||||
break;
|
||||
}
|
||||
libusb_close(dev_handle);
|
||||
dev_handle = NULL;
|
||||
}
|
||||
}
|
||||
if (dev_handle == NULL) goto fail;
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
dev_list = nullptr;
|
||||
|
||||
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
|
||||
libusb_detach_kernel_driver(dev_handle, 0);
|
||||
}
|
||||
|
||||
err = libusb_set_configuration(dev_handle, 1);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
err = libusb_claim_interface(dev_handle, 0);
|
||||
if (err != 0) { goto fail; }
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (dev_list != NULL) {
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
}
|
||||
cleanup();
|
||||
throw std::runtime_error("Error connecting to panda");
|
||||
}
|
||||
|
||||
PandaUsbHandle::~PandaUsbHandle() {
|
||||
std::lock_guard lk(hw_lock);
|
||||
cleanup();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void PandaUsbHandle::cleanup() {
|
||||
if (dev_handle) {
|
||||
libusb_release_interface(dev_handle, 0);
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> PandaUsbHandle::list() {
|
||||
// init libusb
|
||||
ssize_t num_devices;
|
||||
libusb_context *context = NULL;
|
||||
libusb_device **dev_list = NULL;
|
||||
std::vector<std::string> serials;
|
||||
|
||||
int err = init_usb_ctx(&context);
|
||||
if (err != 0) { return serials; }
|
||||
|
||||
num_devices = libusb_get_device_list(context, &dev_list);
|
||||
if (num_devices < 0) {
|
||||
LOGE("libusb can't get device list");
|
||||
goto finish;
|
||||
}
|
||||
for (size_t i = 0; i < num_devices; ++i) {
|
||||
libusb_device *device = dev_list[i];
|
||||
libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(device, &desc);
|
||||
if (desc.idVendor == 0xbbaa && desc.idProduct == 0xddcc) {
|
||||
libusb_device_handle *handle = NULL;
|
||||
int ret = libusb_open(device, &handle);
|
||||
if (ret < 0) { goto finish; }
|
||||
|
||||
unsigned char desc_serial[26] = { 0 };
|
||||
ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, desc_serial, std::size(desc_serial));
|
||||
libusb_close(handle);
|
||||
if (ret < 0) { goto finish; }
|
||||
|
||||
serials.push_back(std::string((char *)desc_serial, ret).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (dev_list != NULL) {
|
||||
libusb_free_device_list(dev_list, 1);
|
||||
}
|
||||
if (context) {
|
||||
libusb_exit(context);
|
||||
}
|
||||
return serials;
|
||||
}
|
||||
|
||||
void PandaUsbHandle::handle_usb_issue(int err, const char func[]) {
|
||||
LOGE_100("usb error %d \"%s\" in %s", err, libusb_strerror((enum libusb_error)err), func);
|
||||
if (err == LIBUSB_ERROR_NO_DEVICE) {
|
||||
LOGE("lost connection");
|
||||
connected = false;
|
||||
}
|
||||
// TODO: check other errors, is simply retrying okay?
|
||||
}
|
||||
|
||||
int PandaUsbHandle::control_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) {
|
||||
int err;
|
||||
const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
|
||||
|
||||
if (!connected) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
std::lock_guard lk(hw_lock);
|
||||
do {
|
||||
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout);
|
||||
if (err < 0) handle_usb_issue(err, __func__);
|
||||
} while (err < 0 && connected);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::control_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) {
|
||||
int err;
|
||||
const uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
|
||||
|
||||
if (!connected) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
std::lock_guard lk(hw_lock);
|
||||
do {
|
||||
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
|
||||
if (err < 0) handle_usb_issue(err, __func__);
|
||||
} while (err < 0 && connected);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
int err;
|
||||
int transferred = 0;
|
||||
|
||||
if (!connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard lk(hw_lock);
|
||||
do {
|
||||
// Try sending can messages. If the receive buffer on the panda is full it will NAK
|
||||
// and libusb will try again. After 5ms, it will time out. We will drop the messages.
|
||||
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);
|
||||
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
LOGW("Transmit buffer full");
|
||||
break;
|
||||
} else if (err != 0 || length != transferred) {
|
||||
handle_usb_issue(err, __func__);
|
||||
}
|
||||
} while(err != 0 && connected);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
int err;
|
||||
int transferred = 0;
|
||||
|
||||
if (!connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard lk(hw_lock);
|
||||
|
||||
do {
|
||||
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);
|
||||
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
break; // timeout is okay to exit, recv still happened
|
||||
} else if (err == LIBUSB_ERROR_OVERFLOW) {
|
||||
comms_healthy = false;
|
||||
LOGE_100("overflow got 0x%x", transferred);
|
||||
} else if (err != 0) {
|
||||
handle_usb_issue(err, __func__);
|
||||
}
|
||||
|
||||
} while(err != 0 && connected);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <linux/spi/spidev.h>
|
||||
#endif
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
|
||||
#define TIMEOUT 0
|
||||
#define SPI_BUF_SIZE 1024
|
||||
|
||||
|
||||
// comms base class
|
||||
class PandaCommsHandle {
|
||||
public:
|
||||
PandaCommsHandle(std::string serial) {};
|
||||
virtual ~PandaCommsHandle() {};
|
||||
virtual void cleanup() = 0;
|
||||
|
||||
std::string hw_serial;
|
||||
std::atomic<bool> connected = true;
|
||||
std::atomic<bool> comms_healthy = true;
|
||||
static std::vector<std::string> list();
|
||||
|
||||
// HW communication
|
||||
virtual int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0;
|
||||
};
|
||||
|
||||
class PandaUsbHandle : public PandaCommsHandle {
|
||||
public:
|
||||
PandaUsbHandle(std::string serial);
|
||||
~PandaUsbHandle();
|
||||
int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT);
|
||||
int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT);
|
||||
int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
void cleanup();
|
||||
|
||||
static std::vector<std::string> list();
|
||||
|
||||
private:
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device_handle *dev_handle = NULL;
|
||||
std::recursive_mutex hw_lock;
|
||||
void handle_usb_issue(int err, const char func[]);
|
||||
};
|
||||
|
||||
#ifndef __APPLE__
|
||||
class PandaSpiHandle : public PandaCommsHandle {
|
||||
public:
|
||||
PandaSpiHandle(std::string serial);
|
||||
~PandaSpiHandle();
|
||||
int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT);
|
||||
int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT);
|
||||
int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
|
||||
void cleanup();
|
||||
|
||||
static std::vector<std::string> list();
|
||||
|
||||
private:
|
||||
int spi_fd = -1;
|
||||
uint8_t tx_buf[SPI_BUF_SIZE];
|
||||
uint8_t rx_buf[SPI_BUF_SIZE];
|
||||
inline static std::recursive_mutex hw_lock;
|
||||
|
||||
int wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack);
|
||||
int bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len);
|
||||
int spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len);
|
||||
int spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len);
|
||||
};
|
||||
#endif
|
||||
@@ -7,7 +7,7 @@ import subprocess
|
||||
from typing import List, NoReturn
|
||||
from functools import cmp_to_key
|
||||
|
||||
from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU
|
||||
from panda import Panda, PandaDFU
|
||||
from common.basedir import BASEDIR
|
||||
from common.params import Params
|
||||
from system.hardware import HARDWARE
|
||||
@@ -15,10 +15,8 @@ from system.swaglog import cloudlog
|
||||
|
||||
|
||||
def get_expected_signature(panda: Panda) -> bytes:
|
||||
fn = DEFAULT_H7_FW_FN if (panda.get_mcu_type() == MCU_TYPE_H7) else DEFAULT_FW_FN
|
||||
|
||||
try:
|
||||
return Panda.get_signature_from_firmware(fn)
|
||||
return Panda.get_signature_from_firmware(panda.get_mcu_type().config.app_path)
|
||||
except Exception:
|
||||
cloudlog.exception("Error computing expected signature")
|
||||
return b""
|
||||
@@ -86,9 +84,9 @@ def main() -> NoReturn:
|
||||
params.remove("PandaSignatures")
|
||||
|
||||
# Flash all Pandas in DFU mode
|
||||
for p in PandaDFU.list():
|
||||
cloudlog.info(f"Panda in DFU mode found, flashing recovery {p}")
|
||||
PandaDFU(p).recover()
|
||||
for serial in PandaDFU.list():
|
||||
cloudlog.info(f"Panda in DFU mode found, flashing recovery {serial}")
|
||||
PandaDFU(serial).recover()
|
||||
time.sleep(1)
|
||||
|
||||
panda_serials = Panda.list()
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
#ifndef __APPLE__
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/timing.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "panda/board/comms_definitions.h"
|
||||
#include "selfdrive/boardd/panda_comms.h"
|
||||
|
||||
|
||||
#define SPI_SYNC 0x5AU
|
||||
#define SPI_HACK 0x79U
|
||||
#define SPI_DACK 0x85U
|
||||
#define SPI_NACK 0x1FU
|
||||
#define SPI_CHECKSUM_START 0xABU
|
||||
|
||||
struct __attribute__((packed)) spi_header {
|
||||
uint8_t sync;
|
||||
uint8_t endpoint;
|
||||
uint16_t tx_len;
|
||||
uint16_t max_rx_len;
|
||||
};
|
||||
|
||||
const int SPI_MAX_RETRIES = 5;
|
||||
const int SPI_ACK_TIMEOUT = 50; // milliseconds
|
||||
const std::string SPI_DEVICE = "/dev/spidev0.0";
|
||||
|
||||
class LockEx {
|
||||
public:
|
||||
LockEx(int fd, std::recursive_mutex &m) : fd(fd), m(m) {
|
||||
m.lock();
|
||||
flock(fd, LOCK_EX);
|
||||
};
|
||||
|
||||
~LockEx() {
|
||||
m.unlock();
|
||||
flock(fd, LOCK_UN);
|
||||
}
|
||||
|
||||
private:
|
||||
int fd;
|
||||
std::recursive_mutex &m;
|
||||
};
|
||||
|
||||
|
||||
PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) {
|
||||
int ret;
|
||||
const int uid_len = 12;
|
||||
uint8_t uid[uid_len] = {0};
|
||||
|
||||
uint32_t spi_mode = SPI_MODE_0;
|
||||
uint32_t spi_speed = 30000000;
|
||||
uint8_t spi_bits_per_word = 8;
|
||||
|
||||
spi_fd = open(SPI_DEVICE.c_str(), O_RDWR);
|
||||
if (spi_fd < 0) {
|
||||
LOGE("failed opening SPI device %d", spi_fd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// SPI settings
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode);
|
||||
if (ret < 0) {
|
||||
LOGE("failed setting SPI mode %d", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
|
||||
if (ret < 0) {
|
||||
LOGE("failed setting SPI speed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits_per_word);
|
||||
if (ret < 0) {
|
||||
LOGE("failed setting SPI bits per word");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// get hw UID/serial
|
||||
ret = control_read(0xc3, 0, 0, uid, uid_len);
|
||||
if (ret == uid_len) {
|
||||
std::stringstream stream;
|
||||
for (int i = 0; i < uid_len; i++) {
|
||||
stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]);
|
||||
}
|
||||
hw_serial = stream.str();
|
||||
} else {
|
||||
LOGD("failed to get serial %d", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!serial.empty() && (serial != hw_serial)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
cleanup();
|
||||
throw std::runtime_error("Error connecting to panda");
|
||||
}
|
||||
|
||||
PandaSpiHandle::~PandaSpiHandle() {
|
||||
std::lock_guard lk(hw_lock);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void PandaSpiHandle::cleanup() {
|
||||
if (spi_fd != -1) {
|
||||
close(spi_fd);
|
||||
spi_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int PandaSpiHandle::control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout) {
|
||||
LockEx lock(spi_fd, hw_lock);
|
||||
ControlPacket_t packet = {
|
||||
.request = request,
|
||||
.param1 = param1,
|
||||
.param2 = param2,
|
||||
.length = 0
|
||||
};
|
||||
return spi_transfer_retry(0, (uint8_t *) &packet, sizeof(packet), NULL, 0);
|
||||
}
|
||||
|
||||
int PandaSpiHandle::control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout) {
|
||||
LockEx lock(spi_fd, hw_lock);
|
||||
ControlPacket_t packet = {
|
||||
.request = request,
|
||||
.param1 = param1,
|
||||
.param2 = param2,
|
||||
.length = length
|
||||
};
|
||||
return spi_transfer_retry(0, (uint8_t *) &packet, sizeof(packet), data, length);
|
||||
}
|
||||
|
||||
int PandaSpiHandle::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
LockEx lock(spi_fd, hw_lock);
|
||||
return bulk_transfer(endpoint, data, length, NULL, 0);
|
||||
}
|
||||
int PandaSpiHandle::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
|
||||
LockEx lock(spi_fd, hw_lock);
|
||||
return bulk_transfer(endpoint, NULL, 0, data, length);
|
||||
}
|
||||
|
||||
int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len) {
|
||||
const int xfer_size = 0x40 * 15;
|
||||
|
||||
int ret = 0;
|
||||
uint16_t length = (tx_data != NULL) ? tx_len : rx_len;
|
||||
for (int i = 0; i < (int)std::ceil((float)length / xfer_size); i++) {
|
||||
int d;
|
||||
if (tx_data != NULL) {
|
||||
int len = std::min(xfer_size, tx_len - (xfer_size * i));
|
||||
d = spi_transfer_retry(endpoint, tx_data + (xfer_size * i), len, NULL, 0);
|
||||
} else {
|
||||
uint16_t to_read = std::min(xfer_size, rx_len - ret);
|
||||
d = spi_transfer_retry(endpoint, NULL, 0, rx_data + (xfer_size * i), to_read);
|
||||
}
|
||||
|
||||
if (d < 0) {
|
||||
LOGE("SPI: bulk transfer failed with %d", d);
|
||||
comms_healthy = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret += d;
|
||||
if ((rx_data != NULL) && d < xfer_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> PandaSpiHandle::list() {
|
||||
try {
|
||||
PandaSpiHandle sh("");
|
||||
return {sh.hw_serial};
|
||||
} catch (std::exception &e) {
|
||||
// no panda on SPI
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void add_checksum(uint8_t *data, int data_len) {
|
||||
data[data_len] = SPI_CHECKSUM_START;
|
||||
for (int i=0; i < data_len; i++) {
|
||||
data[data_len] ^= data[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool check_checksum(uint8_t *data, int data_len) {
|
||||
uint8_t checksum = SPI_CHECKSUM_START;
|
||||
for (uint16_t i = 0U; i < data_len; i++) {
|
||||
checksum ^= data[i];
|
||||
}
|
||||
return checksum == 0U;
|
||||
}
|
||||
|
||||
|
||||
int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len) {
|
||||
int ret;
|
||||
|
||||
int count = SPI_MAX_RETRIES;
|
||||
do {
|
||||
// TODO: handle error
|
||||
ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len);
|
||||
count--;
|
||||
} while (ret < 0 && connected && count > 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) {
|
||||
double start_millis = millis_since_boot();
|
||||
while (true) {
|
||||
int ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to send ACK request");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rx_buf[0] == ack) {
|
||||
break;
|
||||
} else if (rx_buf[0] == SPI_NACK) {
|
||||
LOGW("SPI: got NACK");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// handle timeout
|
||||
if (millis_since_boot() - start_millis > SPI_ACK_TIMEOUT) {
|
||||
LOGD("SPI: timed out waiting for ACK");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len) {
|
||||
int ret;
|
||||
uint16_t rx_data_len;
|
||||
|
||||
// needs to be less, since we need to have space for the checksum
|
||||
assert(tx_len < SPI_BUF_SIZE);
|
||||
assert(max_rx_len < SPI_BUF_SIZE);
|
||||
|
||||
spi_header header = {
|
||||
.sync = SPI_SYNC,
|
||||
.endpoint = endpoint,
|
||||
.tx_len = tx_len,
|
||||
.max_rx_len = max_rx_len
|
||||
};
|
||||
|
||||
spi_ioc_transfer transfer = {
|
||||
.tx_buf = (uint64_t)tx_buf,
|
||||
.rx_buf = (uint64_t)rx_buf
|
||||
};
|
||||
|
||||
// Send header
|
||||
memcpy(tx_buf, &header, sizeof(header));
|
||||
add_checksum(tx_buf, sizeof(header));
|
||||
transfer.len = sizeof(header) + 1;
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to send header");
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Wait for (N)ACK
|
||||
tx_buf[0] = 0x12;
|
||||
transfer.len = 1;
|
||||
ret = wait_for_ack(transfer, SPI_HACK);
|
||||
if (ret < 0) {
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Send data
|
||||
if (tx_data != NULL) {
|
||||
memcpy(tx_buf, tx_data, tx_len);
|
||||
}
|
||||
add_checksum(tx_buf, tx_len);
|
||||
transfer.len = tx_len + 1;
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to send data");
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Wait for (N)ACK
|
||||
tx_buf[0] = 0xab;
|
||||
transfer.len = 1;
|
||||
ret = wait_for_ack(transfer, SPI_DACK);
|
||||
if (ret < 0) {
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Read data len
|
||||
transfer.len = 2;
|
||||
transfer.rx_buf = (uint64_t)(rx_buf + 1);
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to read rx data len");
|
||||
goto transfer_fail;
|
||||
}
|
||||
rx_data_len = *(uint16_t *)(rx_buf+1);
|
||||
if (rx_data_len >= SPI_BUF_SIZE) {
|
||||
LOGE("SPI: RX data len larger than buf size %d", rx_data_len);
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Read data
|
||||
transfer.len = rx_data_len + 1;
|
||||
transfer.rx_buf = (uint64_t)(rx_buf + 2 + 1);
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to read rx data");
|
||||
goto transfer_fail;
|
||||
}
|
||||
if (!check_checksum(rx_buf, rx_data_len + 4)) {
|
||||
LOGE("SPI: bad checksum");
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
if (rx_data != NULL) {
|
||||
memcpy(rx_data, rx_buf + 3, rx_data_len);
|
||||
}
|
||||
|
||||
return rx_data_len;
|
||||
|
||||
transfer_fail:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -51,7 +51,7 @@ class TestBoardd(unittest.TestCase):
|
||||
cp.safetyConfigs = [safety_config]*num_pandas
|
||||
|
||||
params = Params()
|
||||
params.put("CarVin", b"0"*17)
|
||||
params.put_bool("FirmwareQueryDone", True)
|
||||
params.put_bool("ControlsReady", True)
|
||||
params.put("CarParams", cp.to_bytes())
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "cereal/messaging/messaging.h"
|
||||
#include "selfdrive/boardd/panda.h"
|
||||
|
||||
const unsigned char dlc_to_len[] = {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 12U, 16U, 20U, 24U, 32U, 48U, 64U};
|
||||
|
||||
int random_int(int min, int max) {
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
@@ -18,7 +16,8 @@ int random_int(int min, int max) {
|
||||
struct PandaTest : public Panda {
|
||||
PandaTest(uint32_t bus_offset, int can_list_size, cereal::PandaState::PandaType hw_type);
|
||||
void test_can_send();
|
||||
void test_can_recv();
|
||||
void test_can_recv(uint32_t chunk_size = 0);
|
||||
void test_chunked_can_recv();
|
||||
|
||||
std::map<int, std::string> test_data;
|
||||
int can_list_size = 0;
|
||||
@@ -50,7 +49,7 @@ PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState
|
||||
can.setAddress(i);
|
||||
can.setSrc(random_int(0, 3) + bus_offset);
|
||||
can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size()));
|
||||
total_pakets_size += CANPACKET_HEAD_SIZE + dat.size();
|
||||
total_pakets_size += sizeof(can_header) + dat.size();
|
||||
}
|
||||
|
||||
can_data_list = can_list.asReader();
|
||||
@@ -60,14 +59,7 @@ PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState
|
||||
void PandaTest::test_can_send() {
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) {
|
||||
int size_left = size;
|
||||
for (int i = 0, counter = 0; i < size; i += USBPACKET_MAX_SIZE, counter++) {
|
||||
REQUIRE(chunk[i] == counter);
|
||||
|
||||
const int len = std::min(USBPACKET_MAX_SIZE, size_left);
|
||||
unpacked_data.insert(unpacked_data.end(), &chunk[i + 1], &chunk[i + len]);
|
||||
size_left -= len;
|
||||
}
|
||||
unpacked_data.insert(unpacked_data.end(), chunk, &chunk[size]);
|
||||
});
|
||||
REQUIRE(unpacked_data.size() == total_pakets_size);
|
||||
|
||||
@@ -75,23 +67,37 @@ void PandaTest::test_can_send() {
|
||||
INFO("test can message integrity");
|
||||
for (int pos = 0, pckt_len = 0; pos < unpacked_data.size(); pos += pckt_len) {
|
||||
can_header header;
|
||||
memcpy(&header, &unpacked_data[pos], CANPACKET_HEAD_SIZE);
|
||||
memcpy(&header, &unpacked_data[pos], sizeof(can_header));
|
||||
const uint8_t data_len = dlc_to_len[header.data_len_code];
|
||||
pckt_len = CANPACKET_HEAD_SIZE + data_len;
|
||||
pckt_len = sizeof(can_header) + data_len;
|
||||
|
||||
REQUIRE(header.addr == cnt);
|
||||
REQUIRE(test_data.find(data_len) != test_data.end());
|
||||
const std::string &dat = test_data[data_len];
|
||||
REQUIRE(memcmp(dat.data(), &unpacked_data[pos + 5], dat.size()) == 0);
|
||||
REQUIRE(memcmp(dat.data(), &unpacked_data[pos + sizeof(can_header)], dat.size()) == 0);
|
||||
++cnt;
|
||||
}
|
||||
REQUIRE(cnt == can_list_size);
|
||||
}
|
||||
|
||||
void PandaTest::test_can_recv() {
|
||||
void PandaTest::test_can_recv(uint32_t rx_chunk_size) {
|
||||
std::vector<can_frame> frames;
|
||||
this->pack_can_buffer(can_data_list, [&](uint8_t *data, size_t size) {
|
||||
this->unpack_can_buffer(data, size, frames);
|
||||
this->pack_can_buffer(can_data_list, [&](uint8_t *data, uint32_t size) {
|
||||
if (rx_chunk_size == 0) {
|
||||
REQUIRE(this->unpack_can_buffer(data, size, frames));
|
||||
} else {
|
||||
this->receive_buffer_size = 0;
|
||||
uint32_t pos = 0;
|
||||
|
||||
while(pos < size) {
|
||||
uint32_t chunk_size = std::min(rx_chunk_size, size - pos);
|
||||
memcpy(&this->receive_buffer[this->receive_buffer_size], &data[pos], chunk_size);
|
||||
this->receive_buffer_size += chunk_size;
|
||||
pos += chunk_size;
|
||||
|
||||
REQUIRE(this->unpack_can_buffer(this->receive_buffer, this->receive_buffer_size, frames));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
REQUIRE(frames.size() == can_list_size);
|
||||
@@ -114,6 +120,9 @@ TEST_CASE("send/recv CAN 2.0 packets") {
|
||||
SECTION("can_receive") {
|
||||
test.test_can_recv();
|
||||
}
|
||||
SECTION("chunked_can_receive") {
|
||||
test.test_can_recv(0x40);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("send/recv CAN FD packets") {
|
||||
@@ -127,4 +136,7 @@ TEST_CASE("send/recv CAN FD packets") {
|
||||
SECTION("can_receive") {
|
||||
test.test_can_recv();
|
||||
}
|
||||
SECTION("chunked_can_receive") {
|
||||
test.test_can_recv(0x40);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from panda import Panda
|
||||
from selfdrive.test.helpers import phone_only
|
||||
from selfdrive.manager.process_config import managed_processes
|
||||
from system.hardware import HARDWARE
|
||||
|
||||
|
||||
class TestPandad(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
managed_processes['pandad'].stop()
|
||||
|
||||
def _wait_for_boardd(self):
|
||||
sm = messaging.SubMaster(['peripheralState'])
|
||||
for _ in range(30):
|
||||
sm.update(1000)
|
||||
if sm.updated['peripheralState']:
|
||||
break
|
||||
|
||||
if not sm.updated['peripheralState']:
|
||||
raise Exception("boardd failed to start")
|
||||
|
||||
@phone_only
|
||||
def test_in_dfu(self):
|
||||
HARDWARE.recover_internal_panda()
|
||||
time.sleep(1)
|
||||
|
||||
managed_processes['pandad'].start()
|
||||
self._wait_for_boardd()
|
||||
|
||||
@phone_only
|
||||
def test_in_bootstub(self):
|
||||
with Panda() as p:
|
||||
p.reset(enter_bootstub=True)
|
||||
assert p.bootstub
|
||||
managed_processes['pandad'].start()
|
||||
self._wait_for_boardd()
|
||||
|
||||
#def test_out_of_date_fw(self):
|
||||
# pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,5 +1,6 @@
|
||||
{% set footnote_tag = '[<sup>{}</sup>](#footnotes)' -%}
|
||||
{% set star_icon = '[](##)' -%}
|
||||
{% set video_icon = '<a href="{}" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>' -%}
|
||||
|
||||
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
|
||||
|
||||
@@ -12,7 +13,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|{{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 %}
|
||||
|{% for column in Column %}{{car_info.get_column(column, star_icon, video_icon, footnote_tag)}}|{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# functions common among cars
|
||||
import capnp
|
||||
from collections import namedtuple
|
||||
|
||||
from cereal import car
|
||||
from common.numpy_fast import clip
|
||||
from common.numpy_fast import clip, interp
|
||||
from typing import Dict
|
||||
|
||||
# kg of standard extra cargo to count for drive, gas, etc...
|
||||
@@ -10,6 +11,7 @@ STD_CARGO_KG = 136.
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
EventName = car.CarEvent.EventName
|
||||
AngleRateLimit = namedtuple('AngleRateLimit', ['speed_bp', 'angle_v'])
|
||||
|
||||
|
||||
def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
|
||||
@@ -71,7 +73,7 @@ def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> Dict[str, st
|
||||
return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc}
|
||||
|
||||
|
||||
def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS):
|
||||
def apply_driver_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS):
|
||||
|
||||
# limits due to driver torque
|
||||
driver_max_torque = LIMITS.STEER_MAX + (LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER
|
||||
@@ -91,24 +93,41 @@ def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque
|
||||
return int(round(float(apply_torque)))
|
||||
|
||||
|
||||
def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS):
|
||||
# limits due to comparison of commanded torque VS motor reported torque
|
||||
max_lim = min(max(motor_torque + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_ERROR_MAX), LIMITS.STEER_MAX)
|
||||
min_lim = max(min(motor_torque - LIMITS.STEER_ERROR_MAX, -LIMITS.STEER_ERROR_MAX), -LIMITS.STEER_MAX)
|
||||
def apply_dist_to_meas_limits(val, val_last, val_meas,
|
||||
STEER_DELTA_UP, STEER_DELTA_DOWN,
|
||||
STEER_ERROR_MAX, STEER_MAX):
|
||||
# limits due to comparison of commanded val VS measured val (torque/angle/curvature)
|
||||
max_lim = min(max(val_meas + STEER_ERROR_MAX, STEER_ERROR_MAX), STEER_MAX)
|
||||
min_lim = max(min(val_meas - STEER_ERROR_MAX, -STEER_ERROR_MAX), -STEER_MAX)
|
||||
|
||||
apply_torque = clip(apply_torque, min_lim, max_lim)
|
||||
val = clip(val, min_lim, max_lim)
|
||||
|
||||
# slow rate if steer torque increases in magnitude
|
||||
if apply_torque_last > 0:
|
||||
apply_torque = clip(apply_torque,
|
||||
max(apply_torque_last - LIMITS.STEER_DELTA_DOWN, -LIMITS.STEER_DELTA_UP),
|
||||
apply_torque_last + LIMITS.STEER_DELTA_UP)
|
||||
# slow rate if val increases in magnitude
|
||||
if val_last > 0:
|
||||
val = clip(val,
|
||||
max(val_last - STEER_DELTA_DOWN, -STEER_DELTA_UP),
|
||||
val_last + STEER_DELTA_UP)
|
||||
else:
|
||||
apply_torque = clip(apply_torque,
|
||||
apply_torque_last - LIMITS.STEER_DELTA_UP,
|
||||
min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP))
|
||||
val = clip(val,
|
||||
val_last - STEER_DELTA_UP,
|
||||
min(val_last + STEER_DELTA_DOWN, STEER_DELTA_UP))
|
||||
|
||||
return int(round(float(apply_torque)))
|
||||
return float(val)
|
||||
|
||||
|
||||
def apply_meas_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS):
|
||||
return int(round(apply_dist_to_meas_limits(apply_torque, apply_torque_last, motor_torque,
|
||||
LIMITS.STEER_DELTA_UP, LIMITS.STEER_DELTA_DOWN,
|
||||
LIMITS.STEER_ERROR_MAX, LIMITS.STEER_MAX)))
|
||||
|
||||
|
||||
def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, LIMITS):
|
||||
# pick angle rate limits based on wind up/down
|
||||
steer_up = apply_angle_last * apply_angle >= 0. and abs(apply_angle) > abs(apply_angle_last)
|
||||
rate_limits = LIMITS.ANGLE_RATE_LIMIT_UP if steer_up else LIMITS.ANGLE_RATE_LIMIT_DOWN
|
||||
|
||||
angle_rate_lim = interp(v_ego, rate_limits.speed_bp, rate_limits.angle_v)
|
||||
return clip(apply_angle, apply_angle_last - angle_rate_lim, apply_angle_last + angle_rate_lim)
|
||||
|
||||
|
||||
def crc8_pedal(data):
|
||||
|
||||
@@ -35,7 +35,7 @@ class CarController:
|
||||
torque -= deadband
|
||||
return torque
|
||||
|
||||
def update(self, CC, CS):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
|
||||
torque_l = 0
|
||||
torque_r = 0
|
||||
@@ -85,6 +85,7 @@ class CarController:
|
||||
new_actuators = CC.actuators.copy()
|
||||
new_actuators.accel = torque_l
|
||||
new_actuators.steer = torque_r
|
||||
new_actuators.steerOutputCan = torque_r
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
import math
|
||||
from cereal import car
|
||||
from common.realtime import DT_CTRL
|
||||
from selfdrive.car import scale_rot_inertia, scale_tire_stiffness, get_safety_config
|
||||
from selfdrive.car import get_safety_config
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
from selfdrive.car.body.values import SPEED_FROM_RPM
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_params(candidate, fingerprint=None, car_fw=None, experimental_long=False):
|
||||
|
||||
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
|
||||
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
|
||||
ret.notCar = True
|
||||
ret.carName = "body"
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.body)]
|
||||
@@ -27,14 +24,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelSpeedFactor = SPEED_FROM_RPM
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
|
||||
ret.radarOffCan = True
|
||||
ret.radarUnavailable = True
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
|
||||
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
|
||||
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
|
||||
|
||||
return ret
|
||||
|
||||
def _update(self, c):
|
||||
@@ -50,5 +43,5 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c):
|
||||
return self.CC.update(c, self.CS)
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -17,6 +17,9 @@ class CarControllerParams:
|
||||
LKAS_MAX_TORQUE = 1 # A value of 1 is easy to overpower
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
class CAR:
|
||||
BODY = "COMMA BODY"
|
||||
@@ -26,6 +29,12 @@ CAR_INFO: Dict[str, CarInfo] = {
|
||||
CAR.BODY: CarInfo("comma body", package="All"),
|
||||
}
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.BODY: [{
|
||||
513: 8, 516: 8, 514: 3, 515: 4,
|
||||
}],
|
||||
}
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
Request(
|
||||
@@ -40,10 +49,13 @@ FW_VERSIONS = {
|
||||
CAR.BODY: {
|
||||
(Ecu.engine, 0x720, None): [
|
||||
b'0.0.01',
|
||||
b'02/27/2022'
|
||||
b'02/27/2022',
|
||||
b'0.3.00a',
|
||||
],
|
||||
# git hash of the firmware used
|
||||
(Ecu.debug, 0x721, None): [
|
||||
b'166bd860' # git hash of the firmware used
|
||||
b'166bd860',
|
||||
b'dc780f85',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ from system.version import is_comma_remote, is_tested_branch
|
||||
from selfdrive.car.interfaces import get_interface_attr
|
||||
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
|
||||
from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
|
||||
from selfdrive.car.fw_versions import get_fw_versions_ordered, match_fw_to_car, get_present_ecus
|
||||
from selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
|
||||
from system.swaglog import cloudlog
|
||||
import cereal.messaging as messaging
|
||||
from selfdrive.car import gen_empty_fingerprint
|
||||
@@ -80,12 +80,13 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
fixed_fingerprint = os.environ.get('FINGERPRINT', "")
|
||||
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
|
||||
ecu_rx_addrs = set()
|
||||
params = Params()
|
||||
|
||||
if not skip_fw_query:
|
||||
# Vin query only reliably works through OBDII
|
||||
bus = 1
|
||||
|
||||
cached_params = Params().get("CarParamsCache")
|
||||
cached_params = params.get("CarParamsCache")
|
||||
if cached_params is not None:
|
||||
cached_params = car.CarParams.from_bytes(cached_params)
|
||||
if cached_params.carName == "mock":
|
||||
@@ -98,8 +99,9 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
cached = True
|
||||
else:
|
||||
cloudlog.warning("Getting VIN & FW versions")
|
||||
set_obd_multiplexing(params, True)
|
||||
vin_rx_addr, vin = get_vin(logcan, sendcan, bus)
|
||||
ecu_rx_addrs = get_present_ecus(logcan, sendcan)
|
||||
ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
cached = False
|
||||
|
||||
@@ -113,7 +115,11 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
cloudlog.event("Malformed VIN", vin=vin, error=True)
|
||||
vin = VIN_UNKNOWN
|
||||
cloudlog.warning("VIN %s", vin)
|
||||
Params().put("CarVin", vin)
|
||||
params.put("CarVin", vin)
|
||||
|
||||
# disable OBD multiplexing for potential ECU knockouts
|
||||
set_obd_multiplexing(params, False)
|
||||
params.put_bool("FirmwareQueryDone", True)
|
||||
|
||||
finger = gen_empty_fingerprint()
|
||||
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
|
||||
@@ -173,17 +179,15 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
return car_fingerprint, finger, vin, car_fw, source, exact_match
|
||||
|
||||
|
||||
def get_car(logcan, sendcan, num_pandas=1):
|
||||
def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1):
|
||||
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
|
||||
|
||||
if candidate is None:
|
||||
cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints)
|
||||
cloudlog.event("car doesn't match any fingerprints", fingerprints=fingerprints, error=True)
|
||||
candidate = "mock"
|
||||
|
||||
experimental_long = Params().get_bool("ExperimentalLongitudinalEnabled")
|
||||
|
||||
CarInterface, CarController, CarState = interfaces[candidate]
|
||||
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long)
|
||||
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed)
|
||||
CP.carVin = vin
|
||||
CP.carFw = car_fw
|
||||
CP.fingerprintSource = source
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from opendbc.can.packer import CANPacker
|
||||
from common.realtime import DT_CTRL
|
||||
from selfdrive.car import apply_toyota_steer_torque_limits
|
||||
from selfdrive.car import apply_meas_steer_torque_limits
|
||||
from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons
|
||||
from selfdrive.car.chrysler.values import CAR, RAM_CARS, CarControllerParams
|
||||
from selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
|
||||
|
||||
|
||||
class CarController:
|
||||
@@ -19,7 +19,7 @@ class CarController:
|
||||
self.packer = CANPacker(dbc_name)
|
||||
self.params = CarControllerParams(CP)
|
||||
|
||||
def update(self, CC, CS):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
can_sends = []
|
||||
|
||||
lkas_active = CC.latActive and self.lkas_control_bit_prev
|
||||
@@ -45,13 +45,13 @@ class CarController:
|
||||
self.hud_count += 1
|
||||
|
||||
# steering
|
||||
if self.frame % 2 == 0:
|
||||
if self.frame % self.params.STEER_STEP == 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):
|
||||
elif self.CP.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
|
||||
if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0):
|
||||
lkas_control_bit = False
|
||||
elif self.CP.carFingerprint in RAM_CARS:
|
||||
@@ -67,7 +67,7 @@ class CarController:
|
||||
|
||||
# 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)
|
||||
apply_steer = apply_meas_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params)
|
||||
if not lkas_active or not lkas_control_bit:
|
||||
apply_steer = 0
|
||||
self.apply_steer_last = apply_steer
|
||||
@@ -78,5 +78,6 @@ class CarController:
|
||||
|
||||
new_actuators = CC.actuators.copy()
|
||||
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -81,8 +81,9 @@ class CarState(CarStateBase):
|
||||
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON'] # Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message
|
||||
ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1
|
||||
ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1
|
||||
else:
|
||||
ret.steerFaultTemporary = cp.vl["EPS_2"]["LKAS_TEMPORARY_FAULT"] == 1
|
||||
ret.steerFaultPermanent = cp.vl["EPS_2"]["LKAS_STATE"] == 4
|
||||
|
||||
# blindspot sensors
|
||||
@@ -135,6 +136,7 @@ class CarState(CarStateBase):
|
||||
("COUNTER", "EPS_2",),
|
||||
("COLUMN_TORQUE", "EPS_2"),
|
||||
("EPS_TORQUE_MOTOR", "EPS_2"),
|
||||
("LKAS_TEMPORARY_FAULT", "EPS_2"),
|
||||
("LKAS_STATE", "EPS_2"),
|
||||
("COUNTER", "CRUISE_BUTTONS"),
|
||||
]
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
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_HD, RAM_DT
|
||||
from selfdrive.car import STD_CARGO_KG, get_safety_config
|
||||
from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False):
|
||||
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
|
||||
ret.carName = "chrysler"
|
||||
|
||||
ret.dashcamOnly = candidate in RAM_HD
|
||||
|
||||
ret.radarOffCan = DBC[candidate]['radar'] is None
|
||||
|
||||
# radar parsing needs some work, see https://github.com/commaai/openpilot/issues/26842
|
||||
ret.radarUnavailable = True # DBC[candidate]['radar'] is None
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerLimitTimer = 0.4
|
||||
|
||||
@@ -27,15 +25,21 @@ class CarInterface(CarInterfaceBase):
|
||||
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.
|
||||
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if candidate not in RAM_CARS:
|
||||
# Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed.
|
||||
new_eps_platform = candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019)
|
||||
new_eps_firmware = any(fw.ecu == 'eps' and fw.fwVersion[:4] >= b"6841" for fw in car_fw)
|
||||
if new_eps_platform or new_eps_firmware:
|
||||
ret.flags |= ChryslerFlags.HIGHER_MIN_STEERING_SPEED.value
|
||||
|
||||
# Chrysler
|
||||
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020):
|
||||
ret.mass = 2242. + STD_CARGO_KG
|
||||
ret.wheelbase = 3.089
|
||||
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
|
||||
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
@@ -46,6 +50,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelbase = 2.71
|
||||
ret.steerRatio = 16.7
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
@@ -56,12 +62,10 @@ class CarInterface(CarInterfaceBase):
|
||||
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[:8] in (b"68312176", b"68273275"):
|
||||
ret.minSteerSpeed = 0.
|
||||
# Older EPS FW allow steer to zero
|
||||
if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw):
|
||||
ret.minSteerSpeed = 0.
|
||||
|
||||
elif candidate == CAR.RAM_HD:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
@@ -74,15 +78,11 @@ class CarInterface(CarInterfaceBase):
|
||||
else:
|
||||
raise ValueError(f"Unsupported car: {candidate}")
|
||||
|
||||
if ret.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
|
||||
# TODO: allow these cars to steer down to 13 m/s if already engaged.
|
||||
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
|
||||
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
|
||||
# starting with reasonable value for civic and scaling by mass and wheelbase
|
||||
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
|
||||
|
||||
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
|
||||
# mass and CG position, so all cars will have approximately similar dyn behaviors
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
|
||||
|
||||
ret.enableBsm = 720 in fingerprint[0]
|
||||
|
||||
return ret
|
||||
@@ -105,5 +105,5 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c):
|
||||
return self.CC.update(c, self.CS)
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -45,12 +45,13 @@ def _address_to_track(address):
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.CP = CP
|
||||
self.rcp = _create_radar_can_parser(CP.carFingerprint)
|
||||
self.updated_messages = set()
|
||||
self.trigger_msg = LAST_MSG
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.rcp is None:
|
||||
if self.rcp is None or self.CP.radarUnavailable:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update_strings(can_strings)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from enum import IntFlag
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional, Union
|
||||
@@ -11,6 +12,10 @@ from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
|
||||
class ChryslerFlags(IntFlag):
|
||||
HIGHER_MIN_STEERING_SPEED = 1
|
||||
|
||||
|
||||
class CAR:
|
||||
# Chrysler
|
||||
PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017"
|
||||
@@ -30,6 +35,7 @@ class CAR:
|
||||
|
||||
class CarControllerParams:
|
||||
def __init__(self, CP):
|
||||
self.STEER_STEP = 2 # 50 Hz
|
||||
self.STEER_ERROR_MAX = 80
|
||||
if CP.carFingerprint in RAM_HD:
|
||||
self.STEER_DELTA_UP = 14
|
||||
@@ -44,17 +50,20 @@ class CarControllerParams:
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
self.STEER_MAX = 261 # higher than this faults the EPS
|
||||
|
||||
|
||||
STEER_THRESHOLD = 120
|
||||
|
||||
RAM_DT = {CAR.RAM_1500, }
|
||||
RAM_HD = {CAR.RAM_HD, }
|
||||
RAM_CARS = RAM_DT | RAM_HD
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChryslerCarInfo(CarInfo):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
harness: Enum = Harness.fca
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
|
||||
CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"),
|
||||
CAR.PACIFICA_2018_HYBRID: None, # same platforms
|
||||
@@ -66,10 +75,10 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
|
||||
],
|
||||
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_1500: ChryslerCarInfo("Ram 1500 2019-23", harness=Harness.ram),
|
||||
CAR.RAM_HD: [
|
||||
ChryslerCarInfo("Ram 2500 2020-22", harness=Harness.ram),
|
||||
ChryslerCarInfo("Ram 3500 2020-22", harness=Harness.ram),
|
||||
ChryslerCarInfo("Ram 3500 2019-22", harness=Harness.ram),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -86,13 +95,13 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.PACIFICA_2017_HYBRID: [{
|
||||
168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 788:3, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 3, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1892: 8, 2016: 8, 2024: 8
|
||||
168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 840: 8, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 3, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1892: 8, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.PACIFICA_2018: [{
|
||||
55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 8, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8
|
||||
},
|
||||
{
|
||||
55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 3, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8
|
||||
55: 8, 58: 6, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 3, 926: 3, 937: 8, 947: 8, 948: 8, 956: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8
|
||||
}],
|
||||
CAR.PACIFICA_2020: [{
|
||||
55: 8, 179: 8, 181: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 536: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 650: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 711: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 776: 8, 779: 8, 782: 8, 784: 8, 792: 8, 793: 8, 794: 8, 795: 8, 799: 8, 800: 8, 801: 8, 802: 8, 803: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 886: 8, 897: 8, 906: 8, 924: 8, 926: 3, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 7, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1284: 8, 1543: 8, 1568: 8, 1570: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 2015: 8, 2016: 8, 2017:8, 2024: 8, 2025: 8
|
||||
@@ -128,7 +137,7 @@ FINGERPRINTS = {
|
||||
}],
|
||||
CAR.JEEP_CHEROKEE_2019: [{
|
||||
# Jeep Grand Cherokee 2019, including most 2020 models
|
||||
55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
|
||||
55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1538: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
|
||||
}],
|
||||
}
|
||||
|
||||
@@ -173,78 +182,153 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
)
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.RAM_1500: {
|
||||
CAR.JEEP_CHEROKEE_2019: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68294063AH',
|
||||
b'68294063AG',
|
||||
b'68434860AC',
|
||||
b'68527375AD',
|
||||
b'68453503AC',
|
||||
b'68402971AD',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68441329AB',
|
||||
b'68490898AA',
|
||||
b'68428609AB',
|
||||
b'68500728AA',
|
||||
b'68355363AB',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68432418AD',
|
||||
b'68432418AB',
|
||||
b'68436004AE',
|
||||
b'68438454AD',
|
||||
b'68436004AD',
|
||||
b'68535469AB',
|
||||
b'68438454AC',
|
||||
b'68408639AD',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'68320950AL',
|
||||
b'68320950AJ',
|
||||
b'68454268AB',
|
||||
b'68475160AG',
|
||||
b'04672892AB',
|
||||
b'68475160AE',
|
||||
b'68456722AC',
|
||||
],
|
||||
(Ecu.eps, 0x75A, None): [
|
||||
b'68273275AG',
|
||||
b'68469901AA',
|
||||
b'68552788AA',
|
||||
b'68453431AA',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'68448163AJ',
|
||||
b'68500630AD',
|
||||
b'68539650AD',
|
||||
b'05035674AB ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035707AA',
|
||||
],
|
||||
},
|
||||
|
||||
CAR.RAM_1500: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68294051AG',
|
||||
b'68294051AI',
|
||||
b'68294052AG',
|
||||
b'68294063AG',
|
||||
b'68294063AH',
|
||||
b'68294063AI',
|
||||
b'68434846AC',
|
||||
b'68434858AC',
|
||||
b'68434860AC',
|
||||
b'68453503AC',
|
||||
b'68453505AC',
|
||||
b'68453511AC',
|
||||
b'68453513AD',
|
||||
b'68453514AD',
|
||||
b'68510283AG',
|
||||
b'68527375AD',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68428609AB',
|
||||
b'68441329AB',
|
||||
b'68473844AB',
|
||||
b'68490898AA',
|
||||
b'68500728AA',
|
||||
b'68615033AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68292406AH',
|
||||
b'68432418AB',
|
||||
b'68432418AD',
|
||||
b'68436004AD',
|
||||
b'68436004AE',
|
||||
b'68438454AC',
|
||||
b'68438454AD',
|
||||
b'68438456AE',
|
||||
b'68438456AF',
|
||||
b'68535469AB',
|
||||
b'68535470AC',
|
||||
b'68586307AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672892AB',
|
||||
b'04672932AB',
|
||||
b'68320950AH',
|
||||
b'68320950AI',
|
||||
b'68320950AJ',
|
||||
b'68320950AL',
|
||||
b'68320950AM',
|
||||
b'68454268AB',
|
||||
b'68475160AE',
|
||||
b'68475160AF',
|
||||
b'68475160AG',
|
||||
],
|
||||
(Ecu.eps, 0x75A, None): [
|
||||
b'68273275AF',
|
||||
b'68273275AG',
|
||||
b'68312176AE',
|
||||
b'68312176AG',
|
||||
b'68440789AC',
|
||||
b'68466110AB',
|
||||
b'68469901AA',
|
||||
b'68522583AB',
|
||||
b'68522585AB',
|
||||
b'68552788AA',
|
||||
b'68552790AA',
|
||||
b'68585112AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05036065AE ',
|
||||
b'05036066AE ',
|
||||
b'68378701AI ',
|
||||
b'68378758AM ',
|
||||
b'68448163AJ',
|
||||
b'68448165AK',
|
||||
b'68500630AD',
|
||||
b'68500630AE',
|
||||
b'68539650AD',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'68360078AL',
|
||||
b'68384328AD',
|
||||
b'68360085AL',
|
||||
b'68360080AM',
|
||||
b'68360081AM',
|
||||
b'68502994AD',
|
||||
b'68360085AL',
|
||||
b'68384328AD',
|
||||
b'68384332AD',
|
||||
b'68445533AB',
|
||||
b'68540431AB',
|
||||
b'68484467AC',
|
||||
b'68502994AD',
|
||||
b'68540431AB',
|
||||
],
|
||||
},
|
||||
|
||||
CAR.RAM_HD: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68361606AH',
|
||||
b'68437735AC',
|
||||
b'68492693AD',
|
||||
b'68525485AB',
|
||||
b'68525487AB',
|
||||
b'68525498AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68399794AC',
|
||||
b'68428503AA',
|
||||
b'68428505AA',
|
||||
b'68428507AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68334977AH',
|
||||
b'68455481AC',
|
||||
b'68504022AA',
|
||||
b'68504022AB',
|
||||
b'68504022AC',
|
||||
b'68530686AB',
|
||||
b'68530686AC',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672895AB',
|
||||
b'56029827AG',
|
||||
b'56029827AH',
|
||||
b'68462657AE',
|
||||
b'68484694AD',
|
||||
b'68484694AE',
|
||||
],
|
||||
(Ecu.eps, 0x761, None): [
|
||||
@@ -252,7 +336,13 @@ FW_VERSIONS = {
|
||||
b'68507906AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'52370131AF',
|
||||
b'52370231AF',
|
||||
b'52370231AG',
|
||||
b'52370931CT',
|
||||
b'52401032AE',
|
||||
b'52421132AF',
|
||||
b'68527616AD ',
|
||||
b'M2370131MB',
|
||||
b'M2421132MB',
|
||||
],
|
||||
|
||||
@@ -7,14 +7,15 @@ from enum import Enum
|
||||
from natsort import natsorted
|
||||
from typing import Dict, List
|
||||
|
||||
from cereal import car
|
||||
from common.basedir import BASEDIR
|
||||
from selfdrive.car import gen_empty_fingerprint
|
||||
from selfdrive.car.docs_definitions import CarInfo, Column
|
||||
from selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote
|
||||
from selfdrive.car.car_helpers import interfaces, get_interface_attr
|
||||
|
||||
|
||||
def get_all_footnotes() -> Dict[Enum, int]:
|
||||
all_footnotes = []
|
||||
all_footnotes = list(CommonFootnote)
|
||||
for footnotes in get_interface_attr("Footnote", ignore_none=True).values():
|
||||
all_footnotes.extend(footnotes)
|
||||
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}
|
||||
@@ -28,7 +29,7 @@ def get_all_car_info() -> List[CarInfo]:
|
||||
all_car_info: List[CarInfo] = []
|
||||
footnotes = get_all_footnotes()
|
||||
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
|
||||
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), experimental_long=True)
|
||||
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=False)
|
||||
|
||||
if CP.dashcamOnly or car_info is None:
|
||||
continue
|
||||
@@ -39,6 +40,7 @@ def get_all_car_info() -> List[CarInfo]:
|
||||
|
||||
for _car_info in car_info:
|
||||
if not hasattr(_car_info, "row"):
|
||||
_car_info.init_make(CP)
|
||||
_car_info.init(CP, footnotes)
|
||||
all_car_info.append(_car_info)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class Column(Enum):
|
||||
STEERING_TORQUE = "Steering Torque"
|
||||
AUTO_RESUME = "Resume from stop"
|
||||
HARNESS = "Harness"
|
||||
VIDEO = "Video"
|
||||
|
||||
|
||||
class Star(Enum):
|
||||
@@ -68,7 +69,17 @@ class Harness(Enum):
|
||||
none = "None"
|
||||
|
||||
|
||||
CarFootnote = namedtuple("CarFootnote", ["text", "column"], defaults=[None])
|
||||
CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only", "shop_footnote"], defaults=(False, False))
|
||||
|
||||
|
||||
class CommonFootnote(Enum):
|
||||
EXP_LONG_AVAIL = CarFootnote(
|
||||
"Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. ",
|
||||
Column.LONGITUDINAL, docs_only=True)
|
||||
EXP_LONG_DSU = CarFootnote(
|
||||
"By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace " +
|
||||
"stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b>",
|
||||
Column.LONGITUDINAL)
|
||||
|
||||
|
||||
def get_footnotes(footnotes: List[Enum], column: Column) -> List[Enum]:
|
||||
@@ -115,6 +126,22 @@ class CarInfo:
|
||||
harness: Enum = Harness.none
|
||||
|
||||
def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
|
||||
self.car_name = CP.carName
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.make, self.model, self.years = split_name(self.name)
|
||||
|
||||
# longitudinal column
|
||||
op_long = "Stock"
|
||||
if CP.openpilotLongitudinalControl and not CP.enableDsu:
|
||||
op_long = "openpilot"
|
||||
elif CP.experimentalLongitudinalAvailable or CP.enableDsu:
|
||||
op_long = "openpilot available"
|
||||
if CP.enableDsu:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_DSU)
|
||||
else:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_AVAIL)
|
||||
|
||||
# min steer & enable speed columns
|
||||
# TODO: set all the min steer speeds in carParams and remove this
|
||||
if self.min_steer_speed is not None:
|
||||
assert CP.minSteerSpeed == 0, f"{CP.carFingerprint}: Minimum steer speed set in both CarInfo and CarParams"
|
||||
@@ -125,19 +152,23 @@ class CarInfo:
|
||||
if self.min_enable_speed is None:
|
||||
self.min_enable_speed = CP.minEnableSpeed
|
||||
|
||||
self.car_name = CP.carName
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.make, self.model, self.years = split_name(self.name)
|
||||
# harness column
|
||||
harness_col = self.harness.value
|
||||
if self.harness is not Harness.none:
|
||||
model_years = self.model + (' ' + self.years if self.years else '')
|
||||
harness_col = f'<a href="https://comma.ai/shop/comma-three.html?make={self.make}&model={model_years}">{harness_col}</a>'
|
||||
|
||||
self.row = {
|
||||
Column.MAKE: self.make,
|
||||
Column.MODEL: self.model,
|
||||
Column.PACKAGE: self.package,
|
||||
Column.LONGITUDINAL: "openpilot" if CP.openpilotLongitudinalControl or CP.experimentalLongitudinalAvailable else "Stock",
|
||||
Column.LONGITUDINAL: op_long,
|
||||
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.AUTO_RESUME: Star.FULL if CP.autoResumeSng else Star.EMPTY,
|
||||
Column.HARNESS: self.harness.value,
|
||||
Column.HARNESS: harness_col,
|
||||
Column.VIDEO: self.video_link if self.video_link is not None else "", # replaced with an image and link from template in get_column
|
||||
}
|
||||
|
||||
# Set steering torque star from max lateral acceleration
|
||||
@@ -151,6 +182,9 @@ class CarInfo:
|
||||
|
||||
return self
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
"""CarInfo subclasses can add make-specific logic for harness selection, footnotes, etc."""
|
||||
|
||||
def get_detail_sentence(self, CP):
|
||||
if not CP.notCar:
|
||||
sentence_builder = "openpilot upgrades your <strong>{car_model}</strong> with automated lane centering{alc} and adaptive cruise control{acc}."
|
||||
@@ -170,6 +204,13 @@ class CarInfo:
|
||||
if self.row[Column.STEERING_TORQUE] != Star.FULL:
|
||||
sentence_builder += " This car may not be able to take tight turns on its own."
|
||||
|
||||
# experimental mode
|
||||
exp_link = "<a href='https://blog.comma.ai/090release/#experimental-mode' target='_blank' class='link-light-new-regular-text'>Experimental mode</a>"
|
||||
if CP.openpilotLongitudinalControl or CP.experimentalLongitudinalAvailable:
|
||||
sentence_builder += f" Traffic light and stop sign handling is also available in {exp_link}."
|
||||
else:
|
||||
sentence_builder += f" {exp_link}, with traffic light and stop sign handling, is not currently available for this car, but may be added in a future software update."
|
||||
|
||||
return sentence_builder.format(car_model=f"{self.make} {self.model}", alc=alc, acc=acc)
|
||||
|
||||
else:
|
||||
@@ -178,12 +219,14 @@ class CarInfo:
|
||||
else:
|
||||
raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}")
|
||||
|
||||
def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str:
|
||||
def get_column(self, column: Column, star_icon: str, video_icon: str, footnote_tag: str) -> str:
|
||||
item: Union[str, Star] = self.row[column]
|
||||
if isinstance(item, Star):
|
||||
item = star_icon.format(item.value)
|
||||
elif column == Column.MODEL and len(self.years):
|
||||
item += f" {self.years}"
|
||||
elif column == Column.VIDEO and len(item) > 0:
|
||||
item = video_icon.format(item)
|
||||
|
||||
footnotes = get_footnotes(self.footnotes, column)
|
||||
if len(footnotes):
|
||||
|
||||
@@ -9,6 +9,8 @@ from selfdrive.car import make_can_msg
|
||||
from selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||
from system.swaglog import cloudlog
|
||||
|
||||
EcuAddrBusType = Tuple[int, Optional[int], int]
|
||||
|
||||
|
||||
def make_tester_present_msg(addr, bus, subaddr=None):
|
||||
dat = [0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0]
|
||||
@@ -33,16 +35,16 @@ def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subadd
|
||||
return False
|
||||
|
||||
|
||||
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[Tuple[int, Optional[int], int]]:
|
||||
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[EcuAddrBusType]:
|
||||
addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)]
|
||||
queries: Set[Tuple[int, Optional[int], int]] = {(addr, None, bus) for addr in addr_list}
|
||||
queries: Set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list}
|
||||
responses = queries
|
||||
return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug)
|
||||
|
||||
|
||||
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[Tuple[int, Optional[int], int]],
|
||||
responses: Set[Tuple[int, Optional[int], int]], timeout: float = 1, debug: bool = False) -> Set[Tuple[int, Optional[int], int]]:
|
||||
ecu_responses: Set[Tuple[int, Optional[int], int]] = set() # set((addr, subaddr, bus),)
|
||||
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[EcuAddrBusType],
|
||||
responses: Set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> Set[EcuAddrBusType]:
|
||||
ecu_responses: Set[EcuAddrBusType] = set() # set((addr, subaddr, bus),)
|
||||
try:
|
||||
msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries]
|
||||
|
||||
@@ -87,5 +89,5 @@ if __name__ == "__main__":
|
||||
for addr, subaddr, bus in ecu_addrs:
|
||||
msg = f" 0x{hex(addr)}"
|
||||
if subaddr is not None:
|
||||
msg += f" (sub-address: 0x{hex(subaddr)})"
|
||||
msg += f" (sub-address: {hex(subaddr)})"
|
||||
print(msg)
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
import math
|
||||
from cereal import car
|
||||
from common.numpy_fast import clip, interp
|
||||
from common.numpy_fast import clip
|
||||
from opendbc.can.packer import CANPacker
|
||||
from selfdrive.car.ford import fordcan
|
||||
from selfdrive.car.ford.values import CANBUS, CarControllerParams
|
||||
from selfdrive.car import apply_std_steer_angle_limits
|
||||
from selfdrive.car.ford.fordcan import create_acc_command, create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, \
|
||||
create_lat_ctl2_msg, create_lka_msg, create_lkas_ui_msg
|
||||
from selfdrive.car.ford.values import CANBUS, CANFD_CARS, CarControllerParams
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
|
||||
def apply_ford_steer_angle_limits(apply_angle, apply_angle_last, vEgo):
|
||||
# rate limit
|
||||
steer_up = apply_angle_last * apply_angle > 0. and abs(apply_angle) > abs(apply_angle_last)
|
||||
rate_limit = CarControllerParams.RATE_LIMIT_UP if steer_up else CarControllerParams.RATE_LIMIT_DOWN
|
||||
max_angle_diff = interp(vEgo, rate_limit.speed_points, rate_limit.max_angle_diff_points)
|
||||
apply_angle = clip(apply_angle, (apply_angle_last - max_angle_diff), (apply_angle_last + max_angle_diff))
|
||||
|
||||
# absolute limit (LatCtlPath_An_Actl)
|
||||
apply_path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO
|
||||
apply_path_angle = clip(apply_path_angle, -0.5, 0.5235)
|
||||
apply_angle = math.degrees(apply_path_angle) * CarControllerParams.LKAS_STEER_RATIO
|
||||
|
||||
return apply_angle
|
||||
|
||||
|
||||
class CarController:
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
@@ -30,12 +16,12 @@ class CarController:
|
||||
self.packer = CANPacker(dbc_name)
|
||||
self.frame = 0
|
||||
|
||||
self.apply_angle_last = 0
|
||||
self.apply_curvature_last = 0
|
||||
self.main_on_last = False
|
||||
self.lkas_enabled_last = False
|
||||
self.steer_alert_last = False
|
||||
|
||||
def update(self, CC, CS):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
can_sends = []
|
||||
|
||||
actuators = CC.actuators
|
||||
@@ -46,66 +32,69 @@ class CarController:
|
||||
|
||||
### acc buttons ###
|
||||
if CC.cruiseControl.cancel:
|
||||
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True))
|
||||
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True, bus=CANBUS.main))
|
||||
can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, cancel=True))
|
||||
can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, cancel=True, bus=CANBUS.main))
|
||||
elif CC.cruiseControl.resume and (self.frame % CarControllerParams.BUTTONS_STEP) == 0:
|
||||
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True))
|
||||
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True, bus=CANBUS.main))
|
||||
can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, resume=True))
|
||||
can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, resume=True, bus=CANBUS.main))
|
||||
# if stock lane centering isn't off, send a button press to toggle it off
|
||||
# the stock system checks for steering pressed, and eventually disengages cruise control
|
||||
elif CS.acc_tja_status_stock_values["Tja_D_Stat"] != 0 and (self.frame % CarControllerParams.ACC_UI_STEP) == 0:
|
||||
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, tja_toggle=True))
|
||||
|
||||
can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, tja_toggle=True))
|
||||
|
||||
### lateral control ###
|
||||
if CC.latActive:
|
||||
lca_rq = 1
|
||||
apply_angle = apply_ford_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgo)
|
||||
else:
|
||||
lca_rq = 0
|
||||
apply_angle = 0.
|
||||
|
||||
# send steering commands at 20Hz
|
||||
if (self.frame % CarControllerParams.LKAS_STEER_STEP) == 0:
|
||||
# use LatCtlPath_An_Actl to actuate steering
|
||||
path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO
|
||||
|
||||
# set slower ramp type when small steering angle change
|
||||
# 0=Slow, 1=Medium, 2=Fast, 3=Immediately
|
||||
steer_change = abs(CS.out.steeringAngleDeg - actuators.steeringAngleDeg)
|
||||
if steer_change < 2.0:
|
||||
ramp_type = 0
|
||||
elif steer_change < 4.0:
|
||||
ramp_type = 1
|
||||
elif steer_change < 6.0:
|
||||
ramp_type = 2
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
if CC.latActive:
|
||||
# apply limits to curvature and clip to signal range
|
||||
apply_curvature = apply_std_steer_angle_limits(actuators.curvature, self.apply_curvature_last, CS.out.vEgo, CarControllerParams)
|
||||
apply_curvature = clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX)
|
||||
else:
|
||||
ramp_type = 3
|
||||
precision = 1 # 0=Comfortable, 1=Precise (the stock system always uses comfortable)
|
||||
apply_curvature = 0.
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
can_sends.append(fordcan.create_lka_command(self.packer, 0, 0))
|
||||
can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision,
|
||||
0, path_angle, 0, 0))
|
||||
self.apply_curvature_last = apply_curvature
|
||||
can_sends.append(create_lka_msg(self.packer))
|
||||
|
||||
if self.CP.carFingerprint in CANFD_CARS:
|
||||
# TODO: extended mode
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = self.frame // CarControllerParams.STEER_STEP
|
||||
can_sends.append(create_lat_ctl2_msg(self.packer, mode, 0., 0., -apply_curvature, 0., counter))
|
||||
else:
|
||||
can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.))
|
||||
|
||||
### longitudinal control ###
|
||||
# send acc command at 50Hz
|
||||
if self.CP.openpilotLongitudinalControl and (self.frame % CarControllerParams.ACC_CONTROL_STEP) == 0:
|
||||
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
|
||||
|
||||
precharge_brake = accel < -0.1
|
||||
if accel > -0.5:
|
||||
gas = accel
|
||||
decel = False
|
||||
else:
|
||||
gas = -5.0
|
||||
decel = True
|
||||
|
||||
can_sends.append(create_acc_command(self.packer, CC.longActive, gas, accel, precharge_brake, decel))
|
||||
|
||||
### ui ###
|
||||
send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert)
|
||||
|
||||
# send lkas ui command at 1Hz or if ui state changes
|
||||
if (self.frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui:
|
||||
can_sends.append(fordcan.create_lkas_ui_command(self.packer, main_on, CC.latActive, steer_alert, hud_control, CS.lkas_status_stock_values))
|
||||
can_sends.append(create_lkas_ui_msg(self.packer, main_on, CC.latActive, steer_alert, hud_control, CS.lkas_status_stock_values))
|
||||
|
||||
# send acc ui command at 20Hz or if ui state changes
|
||||
if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
|
||||
can_sends.append(fordcan.create_acc_ui_command(self.packer, main_on, CC.latActive, hud_control, CS.acc_tja_status_stock_values))
|
||||
can_sends.append(create_acc_ui_msg(self.packer, main_on, CC.latActive, hud_control, CS.acc_tja_status_stock_values))
|
||||
|
||||
self.main_on_last = main_on
|
||||
self.lkas_enabled_last = CC.latActive
|
||||
self.steer_alert_last = steer_alert
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last
|
||||
new_actuators.curvature = self.apply_curvature_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -14,7 +14,7 @@ class CarState(CarStateBase):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnGear_D_RqDrv"]
|
||||
self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"]
|
||||
|
||||
def update(self, cp, cp_cam):
|
||||
ret = car.CarState.new_message()
|
||||
@@ -37,7 +37,7 @@ class CarState(CarStateBase):
|
||||
# steering wheel
|
||||
ret.steeringAngleDeg = cp.vl["SteeringPinion_Data"]["StePinComp_An_Est"]
|
||||
ret.steeringTorque = cp.vl["EPAS_INFO"]["SteeringColumnTorque"]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE, 5)
|
||||
ret.steerFaultTemporary = cp.vl["EPAS_INFO"]["EPAS_Failure"] == 1
|
||||
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
|
||||
# ret.espDisabled = False # TODO: find traction control signal
|
||||
@@ -51,7 +51,7 @@ class CarState(CarStateBase):
|
||||
|
||||
# gear
|
||||
if self.CP.transmissionType == TransmissionType.automatic:
|
||||
gear = self.shifter_values.get(cp.vl["Gear_Shift_by_Wire_FD1"]["TrnGear_D_RqDrv"], None)
|
||||
gear = self.shifter_values.get(cp.vl["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"])
|
||||
ret.gearShifter = self.parse_gear_shifter(gear)
|
||||
elif self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.clutchPressed = cp.vl["Engine_Clutch_Data"]["CluPdlPos_Pc_Meas"] > 0
|
||||
@@ -118,7 +118,7 @@ class CarState(CarStateBase):
|
||||
("DrStatRl_B_Actl", "BodyInfo_3_FD1"), # BCM Door open, rear left
|
||||
("DrStatRr_B_Actl", "BodyInfo_3_FD1"), # BCM Door open, rear right
|
||||
("FirstRowBuckleDriver", "RCMStatusMessage2_FD1"), # RCM Seatbelt status, driver
|
||||
("HeadLghtHiFlash_D_Stat", "Steering_Data_FD1"), # SCCM Passthru the remaining buttons
|
||||
("HeadLghtHiFlash_D_Stat", "Steering_Data_FD1"), # SCCM Passthrough the remaining buttons
|
||||
("WiprFront_D_Stat", "Steering_Data_FD1"),
|
||||
("LghtAmb_D_Sns", "Steering_Data_FD1"),
|
||||
("AccButtnGapDecPress", "Steering_Data_FD1"),
|
||||
@@ -171,7 +171,7 @@ class CarState(CarStateBase):
|
||||
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
signals += [
|
||||
("TrnGear_D_RqDrv", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position
|
||||
("TrnRng_D_RqGsm", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position
|
||||
]
|
||||
checks += [
|
||||
("Gear_Shift_by_Wire_FD1", 10),
|
||||
|
||||
@@ -4,64 +4,120 @@ from selfdrive.car.ford.values import CANBUS
|
||||
HUDControl = car.CarControl.HUDControl
|
||||
|
||||
|
||||
def create_lka_command(packer, angle_deg: float, curvature: float):
|
||||
def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray):
|
||||
checksum = mode + counter
|
||||
checksum += dat[2] + ((dat[3] & 0xE0) >> 5) # curvature
|
||||
checksum += dat[6] + ((dat[7] & 0xE0) >> 5) # curvature rate
|
||||
checksum += (dat[3] & 0x1F) + ((dat[4] & 0xFC) >> 2) # path angle
|
||||
checksum += (dat[4] & 0x3) + dat[5] # path offset
|
||||
return 0xFF - (checksum & 0xFF)
|
||||
|
||||
|
||||
def create_lka_msg(packer):
|
||||
"""
|
||||
Creates a CAN message for the Ford LKAS Command.
|
||||
Creates an empty CAN message for the Ford LKA Command.
|
||||
|
||||
This command can apply "Lane Keeping Aid" manoeuvres, which are subject to the PSCM lockout.
|
||||
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LkaDrvOvrrd_D_Rq": 0, # driver override level? [0|3]
|
||||
"LkaActvStats_D2_Req": 0, # action [0|7]
|
||||
"LaRefAng_No_Req": angle_deg, # angle [-102.4|102.3] degrees
|
||||
"LaRampType_B_Req": 0, # Ramp speed: 0=Smooth, 1=Quick
|
||||
"LaCurvature_No_Calc": curvature, # curvature [-0.01024|0.01023] 1/meter
|
||||
"LdwActvStats_D_Req": 0, # LDW status [0|7]
|
||||
"LdwActvIntns_D_Req": 0, # LDW intensity [0|3], shake alert strength
|
||||
}
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, values)
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, {})
|
||||
|
||||
|
||||
def create_tja_command(packer, lca_rq: int, ramp_type: int, precision: int, path_offset: float, path_angle: float, curvature_rate: float, curvature: float):
|
||||
def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float):
|
||||
"""
|
||||
Creates a CAN message for the Ford TJA/LCA Command.
|
||||
|
||||
This command can apply "Lane Centering" manoeuvres: continuous lane centering for traffic jam
|
||||
assist and highway driving. It is not subject to the PSCM lockout.
|
||||
This command can apply "Lane Centering" manoeuvres: continuous lane centering for traffic jam assist and highway
|
||||
driving. It is not subject to the PSCM lockout.
|
||||
|
||||
Ford lane centering command uses a third order polynomial to describe the road centerline. The
|
||||
polynomial is defined by the following coefficients:
|
||||
c0: lateral offset between the vehicle and the centerline
|
||||
c1: heading angle between the vehicle and the centerline
|
||||
c2: curvature of the centerline
|
||||
Ford lane centering command uses a third order polynomial to describe the road centerline. The polynomial is defined
|
||||
by the following coefficients:
|
||||
c0: lateral offset between the vehicle and the centerline (positive is right)
|
||||
c1: heading angle between the vehicle and the centerline (positive is right)
|
||||
c2: curvature of the centerline (positive is left)
|
||||
c3: rate of change of curvature of the centerline
|
||||
As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and
|
||||
speed, the steering angle cannot be easily controlled.
|
||||
As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and speed, the steering
|
||||
angle cannot be easily controlled.
|
||||
|
||||
The PSCM should be configured to accept TJA/LCA commands before these commands will be processed.
|
||||
This can be done using tools such as Forscan.
|
||||
The PSCM should be configured to accept TJA/LCA commands before these commands will be processed. This can be done
|
||||
using tools such as Forscan.
|
||||
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": lca_rq, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
"LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
return packer.make_can_msg("LateralMotionControl", CANBUS.main, values)
|
||||
|
||||
|
||||
def create_lkas_ui_command(packer, main_on: bool, enabled: bool, steer_alert: bool, hud_control, stock_values: dict):
|
||||
def create_lat_ctl2_msg(packer, mode: int, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float, counter: int):
|
||||
"""
|
||||
Create a CAN message for the new Ford Lane Centering command.
|
||||
|
||||
This message is used on the CAN FD platform and replaces the old LateralMotionControl message. It is similar but has
|
||||
additional signals for a counter and checksum.
|
||||
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtl_D2_Rq": mode, # Mode: 0=None, 1=PathFollowingLimitedMode, 2=PathFollowingExtendedMode,
|
||||
# 3=SafeRampOut, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
"LatCtlPrecision_D_Rq": 1, # 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # [-0.5|0.5235] radians
|
||||
"LatCtlCurv_No_Actl": curvature, # [-0.02|0.02094] 1/meter
|
||||
"LatCtlCrv_NoRate2_Actl": curvature_rate, # [-0.001024|0.001023] 1/meter^2
|
||||
"HandsOffCnfm_B_Rq": 0, # 0=Inactive, 1=Active [0|1]
|
||||
"LatCtlPath_No_Cnt": counter, # [0|15]
|
||||
"LatCtlPath_No_Cs": 0, # [0|255]
|
||||
}
|
||||
|
||||
# calculate checksum
|
||||
dat = packer.make_can_msg("LateralMotionControl2", CANBUS.main, values)[2]
|
||||
values["LatCtlPath_No_Cs"] = calculate_lat_ctl2_checksum(mode, counter, dat)
|
||||
|
||||
return packer.make_can_msg("LateralMotionControl2", CANBUS.main, values)
|
||||
|
||||
|
||||
def create_acc_command(packer, long_active: bool, gas: float, accel: float, precharge_brake: bool, decel: bool):
|
||||
"""
|
||||
Creates a CAN message for the Ford ACC Command.
|
||||
|
||||
This command can be used to enable ACC, to set the ACC gas/brake/decel values
|
||||
and to disable ACC.
|
||||
|
||||
Frequency is 50Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"AccBrkTot_A_Rq": accel, # Brake total accel request: [-20|11.9449] m/s^2
|
||||
"Cmbb_B_Enbl": 1 if long_active else 0, # Enabled: 0=No, 1=Yes
|
||||
"AccPrpl_A_Rq": gas, # Acceleration request: [-5|5.23] m/s^2
|
||||
"AccBrkPrchg_B_Rq": 1 if precharge_brake else 0, # Pre-charge brake request: 0=No, 1=Yes
|
||||
"AccBrkDecel_B_Rq": 1 if decel else 0, # Deceleration request: 0=Inactive, 1=Active
|
||||
}
|
||||
return packer.make_can_msg("ACCDATA", CANBUS.main, values)
|
||||
|
||||
|
||||
def create_lkas_ui_msg(packer, main_on: bool, enabled: bool, steer_alert: bool, hud_control, stock_values: dict):
|
||||
"""
|
||||
Creates a CAN message for the Ford IPC IPMA/LKAS status.
|
||||
|
||||
@@ -107,16 +163,15 @@ def create_lkas_ui_command(packer, main_on: bool, enabled: bool, steer_alert: bo
|
||||
|
||||
values = {
|
||||
**stock_values,
|
||||
"LaActvStats_D_Dsply": lines, # LKAS status (lines) [0|31]
|
||||
"LaHandsOff_D_Dsply": hands_on_wheel_dsply, # 0=HandsOn, 1=Level1 (w/o chime), 2=Level2 (w/ chime), 3=Suppressed
|
||||
"LaActvStats_D_Dsply": lines, # LKAS status (lines) [0|31]
|
||||
"LaHandsOff_D_Dsply": hands_on_wheel_dsply, # 0=HandsOn, 1=Level1 (w/o chime), 2=Level2 (w/ chime), 3=Suppressed
|
||||
}
|
||||
return packer.make_can_msg("IPMA_Data", CANBUS.main, values)
|
||||
|
||||
|
||||
def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, stock_values: dict):
|
||||
def create_acc_ui_msg(packer, main_on: bool, enabled: bool, hud_control, stock_values: dict):
|
||||
"""
|
||||
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam
|
||||
assist status.
|
||||
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam assist status.
|
||||
|
||||
Stock functionality is maintained by passing through unmodified signals.
|
||||
|
||||
@@ -148,7 +203,8 @@ def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, sto
|
||||
return packer.make_can_msg("ACCDATA_3", CANBUS.main, values)
|
||||
|
||||
|
||||
def create_button_command(packer, stock_values: dict, cancel = False, resume = False, tja_toggle = False, bus: int = CANBUS.camera):
|
||||
def create_button_msg(packer, stock_values: dict, cancel=False, resume=False, tja_toggle=False,
|
||||
bus: int = CANBUS.camera):
|
||||
"""
|
||||
Creates a CAN message for the Ford SCCM buttons/switches.
|
||||
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
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.ford.values import CAR, Ecu, TransmissionType, GearShifter
|
||||
from selfdrive.car import STD_CARGO_KG, get_safety_config
|
||||
from selfdrive.car.ford.values import CAR, Ecu
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
CarParams = car.CarParams
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
GearShifter = car.CarState.GearShifter
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False):
|
||||
if car_fw is None:
|
||||
car_fw = []
|
||||
|
||||
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
|
||||
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
|
||||
ret.carName = "ford"
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.ford)]
|
||||
|
||||
# These cars are dashcam only until the port is finished
|
||||
ret.dashcamOnly = True
|
||||
ret.safetyConfigs = [get_safety_config(CarParams.SafetyModel.ford)]
|
||||
|
||||
# Angle-based steering
|
||||
ret.steerControlType = CarParams.SteerControlType.angle
|
||||
ret.steerActuatorDelay = 0.4
|
||||
ret.radarUnavailable = True
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.steerLimitTimer = 1.0
|
||||
tire_stiffness_factor = 1.0
|
||||
|
||||
if candidate == CAR.ESCAPE_MK4:
|
||||
if candidate == CAR.BRONCO_SPORT_MK1:
|
||||
ret.wheelbase = 2.67
|
||||
ret.steerRatio = 17.7
|
||||
ret.mass = 1625 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.ESCAPE_MK4:
|
||||
ret.wheelbase = 2.71
|
||||
ret.steerRatio = 14.3 # Copied from Focus
|
||||
ret.steerRatio = 16.7
|
||||
ret.mass = 1750 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.EXPLORER_MK6:
|
||||
ret.wheelbase = 3.025
|
||||
ret.steerRatio = 16.8 # learned
|
||||
ret.steerRatio = 16.8
|
||||
ret.mass = 2050 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.FOCUS_MK4:
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 13.8 # learned
|
||||
ret.steerRatio = 15.0
|
||||
ret.mass = 1350 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.MAVERICK_MK1:
|
||||
ret.wheelbase = 3.076
|
||||
ret.steerRatio = 17.0
|
||||
ret.mass = 1650 + STD_CARGO_KG
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported car: {candidate}")
|
||||
|
||||
@@ -60,10 +67,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.minSteerSpeed = 0.
|
||||
|
||||
ret.autoResumeSng = ret.minEnableSpeed == -1.
|
||||
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
|
||||
tire_stiffness_factor=tire_stiffness_factor)
|
||||
return ret
|
||||
|
||||
def _update(self, c):
|
||||
@@ -74,5 +78,5 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c):
|
||||
return self.CC.update(c, self.CS)
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -47,7 +47,7 @@ class RadarInterface(RadarInterfaceBase):
|
||||
self.updated_messages = set()
|
||||
self.track_id = 0
|
||||
self.radar = DBC[CP.carFingerprint]['radar']
|
||||
if self.radar is None:
|
||||
if self.radar is None or CP.radarUnavailable:
|
||||
self.rcp = None
|
||||
elif self.radar == RADAR.DELPHI_ESR:
|
||||
self.rcp = _create_delphi_esr_radar_can_parser()
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
from collections import defaultdict, namedtuple
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Union
|
||||
from typing import Dict, List, Set, Union
|
||||
|
||||
from cereal import car
|
||||
from selfdrive.car import dbc_dict
|
||||
from selfdrive.car import AngleRateLimit, dbc_dict
|
||||
from selfdrive.car.docs_definitions import CarInfo, Harness
|
||||
from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
GearShifter = car.CarState.GearShifter
|
||||
|
||||
AngleRateLimit = namedtuple('AngleRateLimit', ['speed_points', 'max_angle_diff_points'])
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
# Messages: Lane_Assist_Data1, LateralMotionControl
|
||||
LKAS_STEER_STEP = 5
|
||||
STEER_STEP = 5
|
||||
# Message: ACCDATA
|
||||
ACC_CONTROL_STEP = 2
|
||||
# Message: IPMA_Data
|
||||
LKAS_UI_STEP = 100
|
||||
# Message: ACCDATA_3
|
||||
@@ -25,12 +23,20 @@ class CarControllerParams:
|
||||
# Message: Steering_Data_FD1, but send twice as fast
|
||||
BUTTONS_STEP = 10 / 2
|
||||
|
||||
LKAS_STEER_RATIO = 2.75 # Approximate ratio between LatCtlPath_An_Actl and steering angle in radians
|
||||
# TODO: remove this once we understand how the EPS calculates the steering angle better
|
||||
STEER_DRIVER_ALLOWANCE = 0.8 # Driver intervention threshold in Nm
|
||||
CURVATURE_MAX = 0.02 # Max curvature for steering command, m^-1
|
||||
STEER_DRIVER_ALLOWANCE = 1.0 # Driver intervention threshold, Nm
|
||||
|
||||
RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15])
|
||||
RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
|
||||
# Curvature rate limits
|
||||
# TODO: unify field names used by curvature and angle control cars
|
||||
# ~2 m/s^3 up, ~-3 m/s^3 down
|
||||
ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.00016])
|
||||
ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.006, 0.00066, 0.00024])
|
||||
|
||||
ACCEL_MAX = 2.0 # m/s^s max acceleration
|
||||
ACCEL_MIN = -3.5 # m/s^s max deceleration
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
class CANBUS:
|
||||
@@ -40,9 +46,14 @@ class CANBUS:
|
||||
|
||||
|
||||
class CAR:
|
||||
BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN"
|
||||
ESCAPE_MK4 = "FORD ESCAPE 4TH GEN"
|
||||
EXPLORER_MK6 = "FORD EXPLORER 6TH GEN"
|
||||
FOCUS_MK4 = "FORD FOCUS 4TH GEN"
|
||||
MAVERICK_MK1 = "FORD MAVERICK 1ST GEN"
|
||||
|
||||
|
||||
CANFD_CARS: Set[str] = set()
|
||||
|
||||
|
||||
class RADAR:
|
||||
@@ -60,12 +71,20 @@ class FordCarInfo(CarInfo):
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
|
||||
CAR.BRONCO_SPORT_MK1: FordCarInfo("Ford Bronco Sport 2021-22"),
|
||||
CAR.ESCAPE_MK4: [
|
||||
FordCarInfo("Ford Escape 2020"),
|
||||
FordCarInfo("Ford Kuga EU", "Driver Assistance Pack"),
|
||||
FordCarInfo("Ford Escape 2020-22"),
|
||||
FordCarInfo("Ford Escape Plug-in Hybrid 2020-22"),
|
||||
FordCarInfo("Ford Kuga 2020-21", "Driver Assistance Pack"),
|
||||
FordCarInfo("Ford Kuga Plug-in Hybrid 2020-22", "Driver Assistance Pack"),
|
||||
],
|
||||
CAR.EXPLORER_MK6: [
|
||||
FordCarInfo("Ford Explorer 2020-22"),
|
||||
FordCarInfo("Lincoln Aviator 2021", "Co-Pilot360 Plus"),
|
||||
FordCarInfo("Lincoln Aviator Plug-in Hybrid 2021", "Co-Pilot360 Plus"),
|
||||
],
|
||||
CAR.EXPLORER_MK6: FordCarInfo("Ford Explorer 2020-21"),
|
||||
CAR.FOCUS_MK4: FordCarInfo("Ford Focus EU 2019", "Driver Assistance Pack"),
|
||||
CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022", "Co-Pilot360 Assist"),
|
||||
}
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
@@ -85,33 +104,71 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
)
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.ESCAPE_MK4: {
|
||||
CAR.BRONCO_SPORT_MK1: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LX6C-2D053-RD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-RE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'M1PT-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'M1PA-14C204-GF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'N1PA-14C204-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x732, None): [
|
||||
b'LX6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PZ1P-14G395-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ESCAPE_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'LX6C-14D003-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LX6C-2D053-NS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-NY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-SA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'LJ6T-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ6T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'LX6A-14C204-BJV\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6A-14C204-ESG\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MX6A-14C204-BEF\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NX6A-14C204-BLE\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x732, None): [
|
||||
b'LX6P-14G395-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PZ1P-14G395-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.EXPLORER_MK6: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'L1MC-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'L1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-KB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -119,14 +176,20 @@ FW_VERSIONS = {
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'LB5T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LB5T-14F397-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'LB5A-14C204-BUJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MB5A-14C204-MD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x732, None): [
|
||||
b'L1MP-14C561-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MP-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MP-14G395-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MP-14G395-JB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FOCUS_MK4: {
|
||||
@@ -148,4 +211,26 @@ FW_VERSIONS = {
|
||||
(Ecu.shiftByWire, 0x732, None): [
|
||||
],
|
||||
},
|
||||
CAR.MAVERICK_MK1: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'NZ6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'NZ6C-2D053-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'NZ6T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'NZ6A-14C204-AAA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x732, None): [
|
||||
b'NZ6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import capnp
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
import struct
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
@@ -57,12 +58,26 @@ class Request:
|
||||
whitelist_ecus: List[int] = field(default_factory=list)
|
||||
rx_offset: int = 0x8
|
||||
bus: int = 1
|
||||
# Whether this query should be run on the first auxiliary panda (CAN FD cars for example)
|
||||
auxiliary: bool = False
|
||||
# FW responses from these queries will not be used for fingerprinting
|
||||
logging: bool = False
|
||||
# boardd toggles OBD multiplexing on/off as needed
|
||||
obd_multiplexing: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class FwQueryConfig:
|
||||
requests: List[Request]
|
||||
# TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus
|
||||
# Overrides and removes from essential ecus for specific models and ecus (exact matching)
|
||||
non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict)
|
||||
# Ecus added for data collection, not to be fingerprinted on
|
||||
extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self):
|
||||
for i in range(len(self.requests)):
|
||||
if self.requests[i].auxiliary:
|
||||
new_request = copy.deepcopy(self.requests[i])
|
||||
new_request.bus += 4
|
||||
self.requests.append(new_request)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import defaultdict
|
||||
from typing import Any, Optional, Set, Tuple
|
||||
from typing import Any, Dict, List, Set
|
||||
from tqdm import tqdm
|
||||
|
||||
import panda.python.uds as uds
|
||||
from cereal import car
|
||||
from selfdrive.car.ecu_addrs import get_ecu_addrs
|
||||
from common.params import Params
|
||||
from selfdrive.car.ecu_addrs import EcuAddrBusType, get_ecu_addrs
|
||||
from selfdrive.car.interfaces import get_interface_attr
|
||||
from selfdrive.car.fingerprints import FW_VERSIONS
|
||||
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
@@ -18,7 +19,7 @@ FW_QUERY_CONFIGS = get_interface_attr('FW_QUERY_CONFIG', ignore_none=True)
|
||||
VERSIONS = get_interface_attr('FW_VERSIONS', ignore_none=True)
|
||||
|
||||
MODEL_TO_BRAND = {c: b for b, e in VERSIONS.items() for c in e}
|
||||
REQUESTS = [(brand, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests]
|
||||
REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests]
|
||||
|
||||
|
||||
def chunks(l, n=128):
|
||||
@@ -29,16 +30,17 @@ def chunks(l, n=128):
|
||||
def build_fw_dict(fw_versions, filter_brand=None):
|
||||
fw_versions_dict = defaultdict(set)
|
||||
for fw in fw_versions:
|
||||
if filter_brand is None or fw.brand == filter_brand:
|
||||
addr = fw.address
|
||||
if (filter_brand is None or fw.brand == filter_brand) and not fw.logging:
|
||||
sub_addr = fw.subAddress if fw.subAddress != 0 else None
|
||||
fw_versions_dict[(addr, sub_addr)].add(fw.fwVersion)
|
||||
fw_versions_dict[(fw.address, sub_addr)].add(fw.fwVersion)
|
||||
return dict(fw_versions_dict)
|
||||
|
||||
|
||||
def get_brand_addrs():
|
||||
brand_addrs = defaultdict(set)
|
||||
for brand, cars in VERSIONS.items():
|
||||
# Add ecus in database + extra ecus to match against
|
||||
brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in FW_QUERY_CONFIGS[brand].extra_ecus}
|
||||
for fw in cars.values():
|
||||
brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()}
|
||||
return brand_addrs
|
||||
@@ -90,7 +92,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
|
||||
return set()
|
||||
|
||||
|
||||
def match_fw_to_car_exact(fw_versions_dict):
|
||||
def match_fw_to_car_exact(fw_versions_dict) -> Set[str]:
|
||||
"""Do an exact FW match. Returns all cars that match the given
|
||||
FW versions for a list of "essential" ECUs. If an ECU is not considered
|
||||
essential the FW version can be missing to get a fingerprint, but if it's present it
|
||||
@@ -146,34 +148,43 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True):
|
||||
return True, set()
|
||||
|
||||
|
||||
def get_present_ecus(logcan, sendcan):
|
||||
queries = list()
|
||||
parallel_queries = list()
|
||||
def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]:
|
||||
params = Params()
|
||||
# queries are split by OBD multiplexing mode
|
||||
queries: Dict[bool, List[List[EcuAddrBusType]]] = {True: [], False: []}
|
||||
parallel_queries: Dict[bool, List[EcuAddrBusType]] = {True: [], False: []}
|
||||
responses = set()
|
||||
|
||||
for brand, r in REQUESTS:
|
||||
for brand, config, r in REQUESTS:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
for brand_versions in VERSIONS[brand].values():
|
||||
for ecu_type, addr, sub_addr in brand_versions:
|
||||
for ecu_type, addr, sub_addr in list(brand_versions) + config.extra_ecus:
|
||||
# Only query ecus in whitelist if whitelist is not empty
|
||||
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
|
||||
a = (addr, sub_addr, r.bus)
|
||||
# Build set of queries
|
||||
if sub_addr is None:
|
||||
if a not in parallel_queries:
|
||||
parallel_queries.append(a)
|
||||
if a not in parallel_queries[r.obd_multiplexing]:
|
||||
parallel_queries[r.obd_multiplexing].append(a)
|
||||
else: # subaddresses must be queried one by one
|
||||
if [a] not in queries:
|
||||
queries.append([a])
|
||||
if [a] not in queries[r.obd_multiplexing]:
|
||||
queries[r.obd_multiplexing].append([a])
|
||||
|
||||
# Build set of expected responses to filter
|
||||
response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset)
|
||||
responses.add((response_addr, sub_addr, r.bus))
|
||||
|
||||
queries.insert(0, parallel_queries)
|
||||
for obd_multiplexing in queries:
|
||||
queries[obd_multiplexing].insert(0, parallel_queries[obd_multiplexing])
|
||||
|
||||
ecu_responses: Set[Tuple[int, Optional[int], int]] = set()
|
||||
for query in queries:
|
||||
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1))
|
||||
ecu_responses = set()
|
||||
for obd_multiplexing in queries:
|
||||
set_obd_multiplexing(params, obd_multiplexing)
|
||||
for query in queries[obd_multiplexing]:
|
||||
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1))
|
||||
return ecu_responses
|
||||
|
||||
|
||||
@@ -181,9 +192,9 @@ def get_brand_ecu_matches(ecu_rx_addrs):
|
||||
"""Returns dictionary of brands and matches with ECUs in their FW versions"""
|
||||
|
||||
brand_addrs = get_brand_addrs()
|
||||
brand_matches = {brand: set() for brand, _ in REQUESTS}
|
||||
brand_matches = {brand: set() for brand, _, _ in REQUESTS}
|
||||
|
||||
brand_rx_offsets = set((brand, r.rx_offset) for brand, r in REQUESTS)
|
||||
brand_rx_offsets = set((brand, r.rx_offset) for brand, _, r in REQUESTS)
|
||||
for addr, sub_addr, _ in ecu_rx_addrs:
|
||||
# Since we can't know what request an ecu responded to, add matches for all possible rx offsets
|
||||
for brand, rx_offset in brand_rx_offsets:
|
||||
@@ -194,6 +205,15 @@ def get_brand_ecu_matches(ecu_rx_addrs):
|
||||
return brand_matches
|
||||
|
||||
|
||||
def set_obd_multiplexing(params: Params, obd_multiplexing: bool):
|
||||
if params.get_bool("ObdMultiplexingEnabled") != obd_multiplexing:
|
||||
cloudlog.warning(f"Setting OBD multiplexing to {obd_multiplexing}")
|
||||
params.remove("ObdMultiplexingChanged")
|
||||
params.put_bool("ObdMultiplexingEnabled", obd_multiplexing)
|
||||
params.get_bool("ObdMultiplexingChanged", block=True)
|
||||
cloudlog.warning("OBD multiplexing set successfully")
|
||||
|
||||
|
||||
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False):
|
||||
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
|
||||
|
||||
@@ -213,6 +233,7 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
|
||||
|
||||
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False):
|
||||
versions = VERSIONS.copy()
|
||||
params = Params()
|
||||
|
||||
# Each brand can define extra ECUs to query for data collection
|
||||
for brand, config in FW_QUERY_CONFIGS.items():
|
||||
@@ -231,8 +252,8 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
|
||||
ecu_types = {}
|
||||
|
||||
for brand, brand_versions in versions.items():
|
||||
for c in brand_versions.values():
|
||||
for ecu_type, addr, sub_addr in c.keys():
|
||||
for ecu in brand_versions.values():
|
||||
for ecu_type, addr, sub_addr in ecu.keys():
|
||||
a = (brand, addr, sub_addr)
|
||||
if a not in ecu_types:
|
||||
ecu_types[a] = ecu_type
|
||||
@@ -248,14 +269,18 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
|
||||
|
||||
# Get versions and build capnp list to put into CarParams
|
||||
car_fw = []
|
||||
requests = [(brand, r) for brand, r in REQUESTS if query_brand is None or brand == query_brand]
|
||||
requests = [(brand, config, r) for brand, config, r in REQUESTS if query_brand is None or brand == query_brand]
|
||||
for addr in tqdm(addrs, disable=not progress):
|
||||
for addr_chunk in chunks(addr):
|
||||
for brand, r in requests:
|
||||
for brand, config, r in requests:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
# Toggle OBD multiplexing for each request
|
||||
if r.bus % 4 == 1:
|
||||
set_obd_multiplexing(params, r.obd_multiplexing)
|
||||
|
||||
try:
|
||||
addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and
|
||||
(len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)]
|
||||
@@ -272,6 +297,8 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
|
||||
f.request = r.request
|
||||
f.brand = brand
|
||||
f.bus = r.bus
|
||||
f.logging = r.logging or (f.ecu, tx_addr, sub_addr) in config.extra_ecus
|
||||
f.obdMultiplexing = r.obd_multiplexing
|
||||
|
||||
if sub_addr is not None:
|
||||
f.subAddress = sub_addr
|
||||
|
||||
@@ -3,12 +3,18 @@ from common.conversions import Conversions as CV
|
||||
from common.numpy_fast import interp
|
||||
from common.realtime import DT_CTRL
|
||||
from opendbc.can.packer import CANPacker
|
||||
from selfdrive.car import apply_std_steer_torque_limits
|
||||
from selfdrive.car import apply_driver_steer_torque_limits
|
||||
from selfdrive.car.gm import gmcan
|
||||
from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, EV_CAR
|
||||
from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = car.CarParams.NetworkLocation
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
|
||||
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
|
||||
CAMERA_CANCEL_DELAY_FRAMES = 10
|
||||
# Enforce a minimum interval between steering messages to avoid a fault
|
||||
MIN_STEER_MSG_INTERVAL_MS = 15
|
||||
|
||||
|
||||
class CarController:
|
||||
@@ -19,19 +25,20 @@ class CarController:
|
||||
self.apply_gas = 0
|
||||
self.apply_brake = 0
|
||||
self.frame = 0
|
||||
self.last_steer_frame = 0
|
||||
self.last_button_frame = 0
|
||||
self.cancel_counter = 0
|
||||
|
||||
self.lka_steering_cmd_counter = 0
|
||||
self.sent_lka_steering_cmd = False
|
||||
self.lka_icon_status_last = (False, False)
|
||||
|
||||
self.params = CarControllerParams()
|
||||
self.params = CarControllerParams(self.CP)
|
||||
|
||||
self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt'])
|
||||
self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar'])
|
||||
self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis'])
|
||||
|
||||
def update(self, CC, CS):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
hud_alert = hud_control.visualAlert
|
||||
@@ -42,23 +49,35 @@ class CarController:
|
||||
# Send CAN commands.
|
||||
can_sends = []
|
||||
|
||||
# Steering (50Hz)
|
||||
# Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just received the
|
||||
# next Panda loopback confirmation in the current CS frame.
|
||||
if CS.loopback_lka_steering_cmd_updated:
|
||||
self.lka_steering_cmd_counter += 1
|
||||
self.sent_lka_steering_cmd = True
|
||||
elif (self.frame % self.params.STEER_STEP) == 0:
|
||||
# Initialize ASCMLKASteeringCmd counter using the camera
|
||||
if not self.sent_lka_steering_cmd and self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
self.lka_steering_cmd_counter = CS.camera_lka_steering_cmd_counter + 1
|
||||
# Steering (Active: 50Hz, inactive: 10Hz)
|
||||
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Also send at 50Hz:
|
||||
# - on startup, first few msgs are blocked
|
||||
# - until we're in sync with camera so counters align when relay closes, preventing a fault.
|
||||
# openpilot can subtly drift, so this is activated throughout a drive to stay synced
|
||||
out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.cam_lka_steering_cmd_counter + 1) % 4
|
||||
if CS.loopback_lka_steering_cmd_ts_nanos == 0 or out_of_sync:
|
||||
steer_step = self.params.STEER_STEP
|
||||
|
||||
self.lka_steering_cmd_counter += 1 if CS.loopback_lka_steering_cmd_updated else 0
|
||||
|
||||
# Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we
|
||||
# received the ASCMLKASteeringCmd loopback confirmation too recently
|
||||
last_lka_steer_msg_ms = (now_nanos - CS.loopback_lka_steering_cmd_ts_nanos) * 1e-6
|
||||
if (self.frame - self.last_steer_frame) >= steer_step and last_lka_steer_msg_ms > MIN_STEER_MSG_INTERVAL_MS:
|
||||
# Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus
|
||||
if CS.loopback_lka_steering_cmd_ts_nanos == 0:
|
||||
self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1
|
||||
|
||||
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)
|
||||
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
|
||||
else:
|
||||
apply_steer = 0
|
||||
|
||||
self.last_steer_frame = self.frame
|
||||
self.apply_steer_last = apply_steer
|
||||
idx = self.lka_steering_cmd_counter % 4
|
||||
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive))
|
||||
@@ -67,24 +86,27 @@ class CarController:
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
if not CC.longActive:
|
||||
# Stock ECU sends max regen when not enabled
|
||||
self.apply_gas = self.params.MAX_ACC_REGEN
|
||||
# ASCM sends max regen when not enabled
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = 0
|
||||
else:
|
||||
if self.CP.carFingerprint in EV_CAR:
|
||||
self.apply_gas = int(round(interp(actuators.accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(actuators.accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
else:
|
||||
self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
|
||||
idx = (self.frame // 4) % 4
|
||||
|
||||
at_full_stop = CC.longActive and CS.out.standstill
|
||||
near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
|
||||
friction_brake_bus = CanBus.CHASSIS
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and actuators.longControlState == LongCtrlState.stopping
|
||||
friction_brake_bus = CanBus.POWERTRAIN
|
||||
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake, idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
|
||||
# Send dashboard UI commands (ACC status)
|
||||
send_fcw = hud_alert == VisualAlert.fcw
|
||||
@@ -93,7 +115,7 @@ class CarController:
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
if not self.CP.radarOffCan:
|
||||
if not self.CP.radarUnavailable:
|
||||
tt = self.frame * DT_CTRL
|
||||
time_and_headlights_step = 10
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
@@ -111,12 +133,21 @@ class CarController:
|
||||
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
|
||||
|
||||
else:
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
# A delayed cancellation allows camera to cancel and avoids a fault when user depresses brake quickly
|
||||
self.cancel_counter = self.cancel_counter + 1 if CC.cruiseControl.cancel else 0
|
||||
|
||||
# Stock longitudinal, integrated at camera
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
if CC.cruiseControl.cancel:
|
||||
if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
|
||||
|
||||
# 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.
|
||||
@@ -133,6 +164,7 @@ class CarController:
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
new_actuators.gas = self.apply_gas
|
||||
new_actuators.brake = self.apply_brake
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import copy
|
||||
from cereal import car
|
||||
from common.conversions import Conversions as CV
|
||||
from common.numpy_fast import mean
|
||||
@@ -16,8 +17,13 @@ class CarState(CarStateBase):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
|
||||
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
|
||||
self.cluster_speed_hyst_gap = CV.KPH_TO_MS / 2.
|
||||
self.cluster_min_speed = CV.KPH_TO_MS / 2.
|
||||
|
||||
self.loopback_lka_steering_cmd_updated = False
|
||||
self.camera_lka_steering_cmd_counter = 0
|
||||
self.loopback_lka_steering_cmd_ts_nanos = 0
|
||||
self.pt_lka_steering_cmd_counter = 0
|
||||
self.cam_lka_steering_cmd_counter = 0
|
||||
self.buttons_counter = 0
|
||||
|
||||
def update(self, pt_cp, cam_cp, loopback_cp):
|
||||
@@ -26,11 +32,16 @@ class CarState(CarStateBase):
|
||||
self.prev_cruise_buttons = self.cruise_buttons
|
||||
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
|
||||
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
|
||||
self.moving_backward = pt_cp.vl["EBCMWheelSpdRear"]["MovingBackward"] != 0
|
||||
|
||||
# Variables used for avoiding LKAS faults
|
||||
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]) > 0
|
||||
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0
|
||||
if self.loopback_lka_steering_cmd_updated:
|
||||
self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
self.camera_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
|
||||
@@ -48,21 +59,19 @@ class CarState(CarStateBase):
|
||||
else:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
|
||||
|
||||
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
|
||||
# that the brake is being intermittently pressed without user interaction.
|
||||
# To avoid a cruise fault we need to match the ECM's brake pressed signal and threshold
|
||||
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
|
||||
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
|
||||
if self.CP.networkLocation != NetworkLocation.fwdCamera:
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
|
||||
else:
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
# Match ECM threshold at a standstill to allow the camera to cancel earlier
|
||||
ret.brakePressed = ret.brake >= 20
|
||||
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
|
||||
# that the brake is being intermittently pressed without user interaction.
|
||||
# To avoid a cruise fault we need to use a conservative brake position threshold
|
||||
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
|
||||
# Regen braking is braking
|
||||
if self.CP.transmissionType == TransmissionType.direct:
|
||||
ret.brakePressed = ret.brakePressed or pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
|
||||
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
|
||||
|
||||
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
@@ -92,13 +101,17 @@ class CarState(CarStateBase):
|
||||
ret.parkingBrake = pt_cp.vl["VehicleIgnitionAlt"]["ParkBrake"] == 1
|
||||
ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0
|
||||
ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1
|
||||
ret.accFaulted = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED
|
||||
ret.accFaulted = (pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED or
|
||||
pt_cp.vl["EBCMFrictionBrakeStatus"]["FrictionBrakeUnavailable"] == 1)
|
||||
|
||||
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
|
||||
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
|
||||
# openpilot controls nonAdaptive when not pcmCruise
|
||||
if self.CP.pcmCruise:
|
||||
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
|
||||
|
||||
return ret
|
||||
|
||||
@@ -111,6 +124,7 @@ class CarState(CarStateBase):
|
||||
("AEBCmdActive", "AEBCmd"),
|
||||
("RollingCounter", "ASCMLKASteeringCmd"),
|
||||
("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"),
|
||||
("ACCCruiseState", "ASCMActiveCruiseControlStatus"),
|
||||
]
|
||||
checks += [
|
||||
("AEBCmd", 10),
|
||||
@@ -142,14 +156,22 @@ class CarState(CarStateBase):
|
||||
("FRWheelSpd", "EBCMWheelSpdFront"),
|
||||
("RLWheelSpd", "EBCMWheelSpdRear"),
|
||||
("RRWheelSpd", "EBCMWheelSpdRear"),
|
||||
("MovingBackward", "EBCMWheelSpdRear"),
|
||||
("FrictionBrakeUnavailable", "EBCMFrictionBrakeStatus"),
|
||||
("PRNDL2", "ECMPRDNL2"),
|
||||
("ManualMode", "ECMPRDNL2"),
|
||||
("LKADriverAppldTrq", "PSCMStatus"),
|
||||
("LKATorqueDelivered", "PSCMStatus"),
|
||||
("LKATorqueDeliveredStatus", "PSCMStatus"),
|
||||
("HandsOffSWlDetectionStatus", "PSCMStatus"),
|
||||
("HandsOffSWDetectionMode", "PSCMStatus"),
|
||||
("LKATotalTorqueDelivered", "PSCMStatus"),
|
||||
("PSCMStatusChecksum", "PSCMStatus"),
|
||||
("RollingCounter", "PSCMStatus"),
|
||||
("TractionControlOn", "ESPStatus"),
|
||||
("ParkBrake", "VehicleIgnitionAlt"),
|
||||
("CruiseMainOn", "ECMEngineStatus"),
|
||||
("BrakePressed", "ECMEngineStatus"),
|
||||
]
|
||||
|
||||
checks = [
|
||||
@@ -161,6 +183,7 @@ class CarState(CarStateBase):
|
||||
("VehicleIgnitionAlt", 10),
|
||||
("EBCMWheelSpdFront", 20),
|
||||
("EBCMWheelSpdRear", 20),
|
||||
("EBCMFrictionBrakeStatus", 20),
|
||||
("AcceleratorPedal2", 33),
|
||||
("ASCMSteeringButton", 33),
|
||||
("ECMEngineStatus", 100),
|
||||
@@ -168,6 +191,15 @@ class CarState(CarStateBase):
|
||||
("ECMAcceleratorPos", 80),
|
||||
]
|
||||
|
||||
# Used to read back last counter sent to PT by camera
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
signals += [
|
||||
("RollingCounter", "ASCMLKASteeringCmd"),
|
||||
]
|
||||
checks += [
|
||||
("ASCMLKASteeringCmd", 0),
|
||||
]
|
||||
|
||||
if CP.transmissionType == TransmissionType.direct:
|
||||
signals.append(("RegenPaddle", "EBCMRegenPaddle"))
|
||||
checks.append(("EBCMRegenPaddle", 50))
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
from selfdrive.car import make_can_msg
|
||||
from selfdrive.car.gm.values import CAR
|
||||
|
||||
|
||||
def create_buttons(packer, bus, idx, button):
|
||||
values = {
|
||||
"ACCButtons": button,
|
||||
"RollingCounter": idx,
|
||||
"ACCAlwaysOne": 1,
|
||||
"DistanceButton": 0,
|
||||
}
|
||||
|
||||
checksum = 240 + int(values["ACCAlwaysOne"] * 0xf)
|
||||
checksum += values["RollingCounter"] * (0x4ef if values["ACCAlwaysOne"] != 0 else 0x3f0)
|
||||
checksum -= int(values["ACCButtons"] - 1) << 4 # not correct if value is 0
|
||||
checksum -= 2 * values["DistanceButton"]
|
||||
|
||||
values["SteeringButtonChecksum"] = checksum
|
||||
return packer.make_can_msg("ASCMSteeringButton", bus, values)
|
||||
|
||||
def create_steering_control(packer, bus, apply_steer, idx, lkas_active):
|
||||
|
||||
def create_pscm_status(packer, bus, pscm_status):
|
||||
checksum_mod = int(1 - pscm_status["HandsOffSWlDetectionStatus"]) << 5
|
||||
pscm_status["HandsOffSWlDetectionStatus"] = 1
|
||||
pscm_status["PSCMStatusChecksum"] += checksum_mod
|
||||
return packer.make_can_msg("PSCMStatus", bus, pscm_status)
|
||||
|
||||
|
||||
def create_steering_control(packer, bus, apply_steer, idx, lkas_active):
|
||||
values = {
|
||||
"LKASteeringCmdActive": lkas_active,
|
||||
"LKASteeringCmd": apply_steer,
|
||||
@@ -18,15 +36,17 @@ def create_steering_control(packer, bus, apply_steer, idx, lkas_active):
|
||||
|
||||
return packer.make_can_msg("ASCMLKASteeringCmd", bus, values)
|
||||
|
||||
|
||||
def create_adas_keepalive(bus):
|
||||
dat = b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
return [make_can_msg(0x409, dat, bus), make_can_msg(0x40a, dat, bus)]
|
||||
|
||||
def create_gas_regen_command(packer, bus, throttle, idx, acc_engaged, at_full_stop):
|
||||
|
||||
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
|
||||
values = {
|
||||
"GasRegenCmdActive": acc_engaged,
|
||||
"GasRegenCmdActive": enabled,
|
||||
"RollingCounter": idx,
|
||||
"GasRegenCmdActiveInv": 1 - acc_engaged,
|
||||
"GasRegenCmdActiveInv": 1 - enabled,
|
||||
"GasRegenCmd": throttle,
|
||||
"GasRegenFullStopActive": at_full_stop,
|
||||
"GasRegenAlwaysOne": 1,
|
||||
@@ -41,15 +61,21 @@ def create_gas_regen_command(packer, bus, throttle, idx, acc_engaged, at_full_st
|
||||
|
||||
return packer.make_can_msg("ASCMGasRegenCmd", bus, values)
|
||||
|
||||
def create_friction_brake_command(packer, bus, apply_brake, idx, near_stop, at_full_stop):
|
||||
|
||||
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
|
||||
mode = 0x1
|
||||
|
||||
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
|
||||
if enabled and CP.carFingerprint in (CAR.BOLT_EUV,):
|
||||
mode = 0x9
|
||||
|
||||
if apply_brake > 0:
|
||||
mode = 0xa
|
||||
if at_full_stop:
|
||||
mode = 0xd
|
||||
|
||||
# TODO: this is to have GM bringing the car to complete stop,
|
||||
# but currently it conflicts with OP controls, so turned off.
|
||||
# but currently it conflicts with OP controls, so turned off. Not set by all cars
|
||||
#elif near_stop:
|
||||
# mode = 0xb
|
||||
|
||||
@@ -57,44 +83,48 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, near_stop, at_f
|
||||
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
|
||||
|
||||
values = {
|
||||
"RollingCounter" : idx,
|
||||
"FrictionBrakeMode" : mode,
|
||||
"RollingCounter": idx,
|
||||
"FrictionBrakeMode": mode,
|
||||
"FrictionBrakeChecksum": checksum,
|
||||
"FrictionBrakeCmd" : -apply_brake
|
||||
"FrictionBrakeCmd": -apply_brake
|
||||
}
|
||||
|
||||
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):
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, lead_car_in_sight, fcw):
|
||||
target_speed = min(target_speed_kph, 255)
|
||||
|
||||
values = {
|
||||
"ACCAlwaysOne" : 1,
|
||||
"ACCResumeButton" : 0,
|
||||
"ACCSpeedSetpoint" : target_speed,
|
||||
"ACCGapLevel" : 3 * acc_engaged, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive" : acc_engaged,
|
||||
"ACCAlwaysOne2" : 1,
|
||||
"ACCLeadCar" : lead_car_in_sight,
|
||||
"ACCAlwaysOne": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": target_speed,
|
||||
"ACCGapLevel": 3 * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCLeadCar": lead_car_in_sight,
|
||||
"FCWAlert": 0x3 if fcw else 0
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
|
||||
|
||||
|
||||
def create_adas_time_status(bus, tt, idx):
|
||||
dat = [(tt >> 20) & 0xff, (tt >> 12) & 0xff, (tt >> 4) & 0xff,
|
||||
((tt & 0xf) << 4) + (idx << 2)]
|
||||
((tt & 0xf) << 4) + (idx << 2)]
|
||||
chksum = 0x1000 - dat[0] - dat[1] - dat[2] - dat[3]
|
||||
chksum = chksum & 0xfff
|
||||
dat += [0x40 + (chksum >> 8), chksum & 0xff, 0x12]
|
||||
return make_can_msg(0xa1, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_steering_status(bus, idx):
|
||||
dat = [idx << 6, 0xf0, 0x20, 0, 0, 0]
|
||||
chksum = 0x60 + sum(dat)
|
||||
dat += [chksum >> 8, chksum & 0xff]
|
||||
return make_can_msg(0x306, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_accelerometer_speed_status(bus, speed_ms, idx):
|
||||
spd = int(speed_ms * 16) & 0xfff
|
||||
accel = 0 & 0xfff
|
||||
@@ -108,6 +138,7 @@ def create_adas_accelerometer_speed_status(bus, speed_ms, idx):
|
||||
dat += [(idx << 5) + (far_range_mode << 4) + (near_range_mode << 3) + (chksum >> 8), chksum & 0xff]
|
||||
return make_can_msg(0x308, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_headlights_status(packer, bus):
|
||||
values = {
|
||||
"Always42": 0x42,
|
||||
@@ -115,6 +146,7 @@ def create_adas_headlights_status(packer, bus):
|
||||
}
|
||||
return packer.make_can_msg("ASCMHeadlight", bus, values)
|
||||
|
||||
|
||||
def create_lka_icon_command(bus, active, critical, steer):
|
||||
if active and steer == 1:
|
||||
if critical:
|
||||
|
||||
@@ -4,8 +4,9 @@ from math import fabs
|
||||
from panda import Panda
|
||||
|
||||
from common.conversions import Conversions as CV
|
||||
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 import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config
|
||||
from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
|
||||
from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
@@ -20,8 +21,7 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
params = CarControllerParams()
|
||||
return params.ACCEL_MIN, params.ACCEL_MAX
|
||||
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
|
||||
# Determined by iteratively plotting and minimizing error for f(angle, speed) = steer.
|
||||
@staticmethod
|
||||
@@ -45,53 +45,74 @@ class CarInterface(CarInterfaceBase):
|
||||
return CarInterfaceBase.get_steer_feedforward_default
|
||||
|
||||
@staticmethod
|
||||
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False):
|
||||
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
|
||||
ret.carName = "gm"
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
|
||||
ret.autoResumeSng = False
|
||||
use_off_car_defaults = len(fingerprint[0]) == 0 # Pick sensible carParams during offline doc generation/CI jobs
|
||||
|
||||
if candidate in EV_CAR:
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
|
||||
ret.longitudinalTuning.deadzoneBP = [0.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.15]
|
||||
|
||||
ret.longitudinalTuning.kpBP = [5., 35.]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
|
||||
if candidate in CAMERA_ACC_CAR:
|
||||
ret.openpilotLongitudinalControl = False
|
||||
ret.experimentalLongitudinalAvailable = True
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarOffCan = True # no radar
|
||||
ret.radarUnavailable = True # no radar
|
||||
ret.pcmCruise = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kpV = [2.0, 1.5]
|
||||
ret.longitudinalTuning.kiV = [0.72]
|
||||
ret.stopAccel = -2.0
|
||||
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
|
||||
if experimental_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
|
||||
else: # ASCM, OBD-II harness
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.networkLocation = NetworkLocation.gateway
|
||||
ret.radarOffCan = False
|
||||
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint[CanBus.OBSTACLE] and not use_off_car_defaults
|
||||
ret.pcmCruise = False # stock non-adaptive cruise control is kept off
|
||||
# supports stop and go, but initial engage must (conservatively) be above 18mph
|
||||
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
|
||||
# Tuning
|
||||
ret.longitudinalTuning.kpV = [2.4, 1.5]
|
||||
ret.longitudinalTuning.kiV = [0.36]
|
||||
|
||||
# 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, CAR.EQUINOX, CAR.BOLT_EV}
|
||||
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX} or \
|
||||
(ret.networkLocation == NetworkLocation.gateway and ret.radarUnavailable)
|
||||
|
||||
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
|
||||
# Some GMs need some tolerance above 10 kph to avoid a fault
|
||||
ret.minSteerSpeed = 10.1 * CV.KPH_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]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.36]
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
|
||||
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
|
||||
|
||||
if candidate == CAR.VOLT:
|
||||
ret.mass = 1607. + STD_CARGO_KG
|
||||
@@ -127,7 +148,13 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerRatio = 14.4 # end to end is 13.46
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia()
|
||||
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
|
||||
|
||||
elif candidate == CAR.BUICK_LACROSSE:
|
||||
ret.mass = 1712. + STD_CARGO_KG
|
||||
ret.wheelbase = 2.91
|
||||
ret.steerRatio = 15.8
|
||||
ret.centerToFront = ret.wheelbase * 0.4 # wild guess
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_REGAL:
|
||||
ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2
|
||||
@@ -141,6 +168,14 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerRatio = 15.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
|
||||
elif candidate == CAR.ESCALADE:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.mass = 5653. * CV.LB_TO_KG + STD_CARGO_KG # (5552+5815)/2
|
||||
ret.wheelbase = 2.95 # 116 inches in meters
|
||||
ret.steerRatio = 17.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.ESCALADE_ESV:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.mass = 2739. + STD_CARGO_KG
|
||||
@@ -152,11 +187,11 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kf = 0.000045
|
||||
tire_stiffness_factor = 1.0
|
||||
|
||||
elif candidate in (CAR.BOLT_EV, CAR.BOLT_EUV):
|
||||
elif candidate == CAR.BOLT_EUV:
|
||||
ret.mass = 1669. + STD_CARGO_KG
|
||||
ret.wheelbase = 2.63779
|
||||
ret.steerRatio = 16.8
|
||||
ret.centerToFront = 2.15 # measured
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
tire_stiffness_factor = 1.0
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
@@ -176,10 +211,6 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
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)
|
||||
|
||||
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
|
||||
# mass and CG position, so all cars will have approximately similar dyn behaviors
|
||||
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
|
||||
@@ -192,22 +223,26 @@ class CarInterface(CarInterfaceBase):
|
||||
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)
|
||||
buttonEvents = [create_button_event(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, CruiseButtons.UNPRESS)]
|
||||
# Handle ACCButtons changing buttons mid-press
|
||||
if self.CS.cruise_buttons != CruiseButtons.UNPRESS and self.CS.prev_cruise_buttons != CruiseButtons.UNPRESS:
|
||||
buttonEvents.append(create_button_event(CruiseButtons.UNPRESS, self.CS.prev_cruise_buttons, BUTTONS_DICT, CruiseButtons.UNPRESS))
|
||||
|
||||
# Suppress resume button if we're resuming from stop so we don't adjust speed.
|
||||
if be.type == ButtonType.accelCruise and (ret.cruiseState.enabled and ret.standstill):
|
||||
be.type = ButtonType.unknown
|
||||
|
||||
ret.buttonEvents = [be]
|
||||
ret.buttonEvents = buttonEvents
|
||||
|
||||
# The ECM allows enabling on falling edge of set, but only rising edge of resume
|
||||
events = self.create_common_events(ret, extra_gears=[GearShifter.sport, GearShifter.low,
|
||||
GearShifter.eco, GearShifter.manumatic],
|
||||
pcm_enable=self.CP.pcmCruise)
|
||||
pcm_enable=self.CP.pcmCruise, enable_buttons=(ButtonType.decelCruise,))
|
||||
if not self.CP.pcmCruise:
|
||||
if any(b.type == ButtonType.accelCruise and b.pressed for b in ret.buttonEvents):
|
||||
events.add(EventName.buttonEnable)
|
||||
|
||||
# Enabling at a standstill with brake is allowed
|
||||
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
|
||||
if ret.vEgo < self.CP.minEnableSpeed and not (ret.standstill and ret.brake >= 20 and
|
||||
self.CP.networkLocation == NetworkLocation.fwdCamera):
|
||||
below_min_enable_speed = ret.vEgo < self.CP.minEnableSpeed or self.CS.moving_backward
|
||||
if below_min_enable_speed and not (ret.standstill and ret.brake >= 20 and
|
||||
self.CP.networkLocation == NetworkLocation.fwdCamera):
|
||||
events.add(EventName.belowEngageSpeed)
|
||||
if ret.cruiseState.standstill:
|
||||
events.add(EventName.resumeRequired)
|
||||
@@ -218,5 +253,5 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c):
|
||||
return self.CC.update(c, self.CS)
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -3,7 +3,7 @@ import math
|
||||
from cereal import car
|
||||
from common.conversions import Conversions as CV
|
||||
from opendbc.can.parser import CANParser
|
||||
from selfdrive.car.gm.values import DBC, CAR, CanBus
|
||||
from selfdrive.car.gm.values import DBC, CanBus
|
||||
from selfdrive.car.interfaces import RadarInterfaceBase
|
||||
|
||||
RADAR_HEADER_MSG = 1120
|
||||
@@ -16,9 +16,6 @@ LAST_RADAR_MSG = RADAR_HEADER_MSG + NUM_SLOTS
|
||||
|
||||
|
||||
def create_radar_can_parser(car_fingerprint):
|
||||
if car_fingerprint not in (CAR.VOLT, CAR.MALIBU, CAR.HOLDEN_ASTRA, CAR.ACADIA, CAR.CADILLAC_ATS, CAR.ESCALADE_ESV):
|
||||
return None
|
||||
|
||||
# C1A-ARS3-A by Continental
|
||||
radar_targets = list(range(SLOT_1_MSG, SLOT_1_MSG + NUM_SLOTS))
|
||||
signals = list(zip(['FLRRNumValidTargets',
|
||||
@@ -34,11 +31,12 @@ def create_radar_can_parser(car_fingerprint):
|
||||
|
||||
return CANParser(DBC[car_fingerprint]['radar'], signals, checks, CanBus.OBSTACLE)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
|
||||
self.rcp = create_radar_can_parser(CP.carFingerprint)
|
||||
self.rcp = None if CP.radarUnavailable else create_radar_can_parser(CP.carFingerprint)
|
||||
|
||||
self.trigger_msg = LAST_RADAR_MSG
|
||||
self.updated_messages = set()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Union
|
||||
|
||||
@@ -11,9 +11,10 @@ Ecu = car.CarParams.Ecu
|
||||
|
||||
class CarControllerParams:
|
||||
STEER_MAX = 300 # GM limit is 3Nm. Used by carcontroller to generate LKA output
|
||||
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
|
||||
STEER_STEP = 3 # Active control frames per command (~33hz)
|
||||
INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz)
|
||||
STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness
|
||||
STEER_DELTA_DOWN = 25
|
||||
STEER_DRIVER_ALLOWANCE = 50
|
||||
STEER_DRIVER_MULTIPLIER = 4
|
||||
STEER_DRIVER_FACTOR = 100
|
||||
@@ -23,14 +24,6 @@ class CarControllerParams:
|
||||
ADAS_KEEPALIVE_STEP = 100
|
||||
CAMERA_KEEPALIVE_STEP = 100
|
||||
|
||||
# Volt gas/brake lookups
|
||||
# TODO: These values should be confirmed on non-Volt vehicles.
|
||||
# MAX_GAS should achieve 2 m/s^2 and MAX_BRAKE with regen should achieve -4.0 m/s^2
|
||||
MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
|
||||
ZERO_GAS = 2048 # Coasting
|
||||
MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
|
||||
MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
|
||||
|
||||
# Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we
|
||||
# perform the closed loop control, and might need some
|
||||
# to apply some more braking if we're on a downhill slope.
|
||||
@@ -39,15 +32,32 @@ class CarControllerParams:
|
||||
ACCEL_MAX = 2. # m/s^2
|
||||
ACCEL_MIN = -4. # m/s^2
|
||||
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
GAS_LOOKUP_BP = [-0.1, 0., ACCEL_MAX]
|
||||
EV_GAS_LOOKUP_BP = [-1., 0., ACCEL_MAX]
|
||||
GAS_LOOKUP_V = [MAX_ACC_REGEN, ZERO_GAS, MAX_GAS]
|
||||
def __init__(self, CP):
|
||||
# Gas/brake lookups
|
||||
self.ZERO_GAS = 2048 # Coasting
|
||||
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
|
||||
|
||||
BRAKE_LOOKUP_BP = [ACCEL_MIN, -0.1]
|
||||
EV_BRAKE_LOOKUP_BP = [ACCEL_MIN, -1.]
|
||||
BRAKE_LOOKUP_V = [MAX_BRAKE, 0.]
|
||||
if CP.carFingerprint in CAMERA_ACC_CAR:
|
||||
self.MAX_GAS = 3400
|
||||
self.MAX_ACC_REGEN = 1514
|
||||
self.INACTIVE_REGEN = 1554
|
||||
# Camera ACC vehicles have no regen while enabled.
|
||||
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
|
||||
max_regen_acceleration = 0.
|
||||
|
||||
else:
|
||||
self.MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
|
||||
self.MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
|
||||
self.INACTIVE_REGEN = 1404
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
|
||||
|
||||
self.GAS_LOOKUP_BP = [max_regen_acceleration, 0., self.ACCEL_MAX]
|
||||
self.GAS_LOOKUP_V = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS]
|
||||
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
|
||||
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
|
||||
|
||||
|
||||
class CAR:
|
||||
@@ -56,9 +66,10 @@ class CAR:
|
||||
CADILLAC_ATS = "CADILLAC ATS Premium Performance 2018"
|
||||
MALIBU = "CHEVROLET MALIBU PREMIER 2017"
|
||||
ACADIA = "GMC ACADIA DENALI 2018"
|
||||
BUICK_LACROSSE = "BUICK LACROSSE 2017"
|
||||
BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
|
||||
ESCALADE = "CADILLAC ESCALADE 2017"
|
||||
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
|
||||
BOLT_EV = "CHEVROLET BOLT EV 2022"
|
||||
BOLT_EUV = "CHEVROLET BOLT EUV 2022"
|
||||
SILVERADO = "CHEVROLET SILVERADO 1500 2020"
|
||||
EQUINOX = "CHEVROLET EQUINOX 2019"
|
||||
@@ -66,7 +77,7 @@ class CAR:
|
||||
|
||||
class Footnote(Enum):
|
||||
OBD_II = CarFootnote(
|
||||
'Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. ' +
|
||||
'Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. ' +
|
||||
'<b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b>',
|
||||
Column.MODEL)
|
||||
|
||||
@@ -74,25 +85,34 @@ class Footnote(Enum):
|
||||
@dataclass
|
||||
class GMCarInfo(CarInfo):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
harness: Enum = Harness.obd_ii
|
||||
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II])
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
if CP.networkLocation == car.CarParams.NetworkLocation.fwdCamera:
|
||||
self.harness = Harness.gm
|
||||
else:
|
||||
self.harness = Harness.obd_ii
|
||||
self.footnotes.append(Footnote.OBD_II)
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
|
||||
CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017"),
|
||||
CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0),
|
||||
CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"),
|
||||
CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"),
|
||||
CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"),
|
||||
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
|
||||
CAR.BUICK_LACROSSE: GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"),
|
||||
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
|
||||
CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"),
|
||||
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
|
||||
CAR.BOLT_EV: GMCarInfo("Chevrolet Bolt EV 2022-23", footnotes=[], harness=Harness.gm),
|
||||
CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "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),
|
||||
CAR.BOLT_EUV: [
|
||||
GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "https://youtu.be/xvwzGMUA210"),
|
||||
GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
|
||||
],
|
||||
CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22", footnotes=[], harness=Harness.gm),
|
||||
CAR.SILVERADO: [
|
||||
GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
|
||||
GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", "https://youtu.be/5HbNoBLzRwE"),
|
||||
],
|
||||
CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22"),
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +153,15 @@ FINGERPRINTS = {
|
||||
# Volt Premier w/ ACC 2018
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 578: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1516: 8, 1601: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2018: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Volt Premier 2018 w/ flashed firmware, no radar
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 8, 381: 2, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 5, 844: 8, 853: 8, 866: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7
|
||||
}],
|
||||
CAR.BUICK_LACROSSE: [
|
||||
# LaCrosse Premium AWD 2017
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.BUICK_REGAL : [
|
||||
# Regal TourX Essence w/ ACC 2018
|
||||
@@ -158,6 +187,10 @@ FINGERPRINTS = {
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 338: 6, 340: 6, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.ESCALADE: [
|
||||
{
|
||||
170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 4, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1917: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.ESCALADE_ESV: [
|
||||
{
|
||||
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
|
||||
@@ -178,9 +211,9 @@ FINGERPRINTS = {
|
||||
|
||||
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_EV, CAR.BOLT_EUV}
|
||||
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_EV, CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX}
|
||||
CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX}
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
@@ -99,6 +99,12 @@ HUDData = namedtuple("HUDData",
|
||||
"lanes_visible", "fcw", "acc_alert", "steer_required"])
|
||||
|
||||
|
||||
def rate_limit_steer(new_steer, last_steer):
|
||||
# TODO just hardcoded ramp to min/max in 0.33s for all Honda
|
||||
MAX_DELTA = 3 * DT_CTRL
|
||||
return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA)
|
||||
|
||||
|
||||
class CarController:
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
@@ -116,8 +122,9 @@ class CarController:
|
||||
self.speed = 0.0
|
||||
self.gas = 0.0
|
||||
self.brake = 0.0
|
||||
self.last_steer = 0.0
|
||||
|
||||
def update(self, CC, CS):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
hud_v_cruise = hud_control.setSpeed * CV.MS_TO_KPH if hud_control.speedVisible else 255
|
||||
@@ -130,6 +137,10 @@ class CarController:
|
||||
accel = 0.0
|
||||
gas, brake = 0.0, 0.0
|
||||
|
||||
# *** rate limit steer ***
|
||||
limited_steer = rate_limit_steer(actuators.steer, self.last_steer)
|
||||
self.last_steer = limited_steer
|
||||
|
||||
# *** apply brake hysteresis ***
|
||||
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady,
|
||||
CS.out.vEgo, self.CP.carFingerprint)
|
||||
@@ -143,7 +154,7 @@ class CarController:
|
||||
# **** process the car messages ****
|
||||
|
||||
# steer torque is converted back to CAN reference (positive when steering right)
|
||||
apply_steer = int(interp(-actuators.steer * self.params.STEER_MAX,
|
||||
apply_steer = int(interp(-limited_steer * self.params.STEER_MAX,
|
||||
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
|
||||
|
||||
# Send CAN commands
|
||||
@@ -178,14 +189,14 @@ class CarController:
|
||||
clip(CS.out.vEgo + 0.0, 0.0, 100.0),
|
||||
clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V)
|
||||
pcm_accel = int(1.0 * 0xc6)
|
||||
pcm_accel = int(1.0 * self.params.NIDEC_GAS_MAX)
|
||||
else:
|
||||
pcm_speed_V = [0.0,
|
||||
clip(CS.out.vEgo - 2.0, 0.0, 100.0),
|
||||
clip(CS.out.vEgo + 2.0, 0.0, 100.0),
|
||||
clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V)
|
||||
pcm_accel = int(clip((accel / 1.44) / max_accel, 0.0, 1.0) * 0xc6)
|
||||
pcm_accel = int(clip((accel / 1.44) / max_accel, 0.0, 1.0) * self.params.NIDEC_GAS_MAX)
|
||||
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message
|
||||
@@ -243,13 +254,15 @@ class CarController:
|
||||
self.speed = pcm_speed
|
||||
|
||||
if not self.CP.enableGasInterceptor:
|
||||
self.gas = pcm_accel / 0xc6
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
new_actuators.brake = self.brake
|
||||
new_actuators.steer = self.last_steer
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -274,7 +274,7 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.available = bool(cp.vl[self.main_on_sig_msg]["MAIN_ON"])
|
||||
|
||||
# Gets rid of Pedal Grinding noise when brake is pressed at slow speeds for some models
|
||||
if self.CP.carFingerprint in (CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE):
|
||||
if self.CP.carFingerprint in (CAR.PILOT, CAR.RIDGELINE):
|
||||
if ret.brake > 0.1:
|
||||
ret.brakePressed = True
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ def get_pt_bus(car_fingerprint):
|
||||
|
||||
|
||||
def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False):
|
||||
if radar_disabled:
|
||||
no_radar = car_fingerprint in HONDA_BOSCH_RADARLESS
|
||||
if radar_disabled or no_radar:
|
||||
# when radar is disabled, steering commands are sent directly to powertrain bus
|
||||
return get_pt_bus(car_fingerprint)
|
||||
# normally steering commands are sent to radar, which forwards them to powertrain bus
|
||||
@@ -104,13 +105,13 @@ def create_bosch_supplemental_1(packer, car_fingerprint):
|
||||
def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud):
|
||||
commands = []
|
||||
bus_pt = get_pt_bus(CP.carFingerprint)
|
||||
radar_disabled = CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl
|
||||
radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl
|
||||
bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled)
|
||||
|
||||
if CP.openpilotLongitudinalControl:
|
||||
acc_hud_values = {
|
||||
'CRUISE_SPEED': hud.v_cruise,
|
||||
'ENABLE_MINI_CAR': 1,
|
||||
'ENABLE_MINI_CAR': 1 if enabled else 0,
|
||||
'HUD_DISTANCE': 0, # max distance setting on display
|
||||
'IMPERIAL_UNIT': int(not is_metric),
|
||||
'HUD_LEAD': 2 if enabled and hud.lead_visible else 1 if enabled else 0,
|
||||
@@ -153,7 +154,7 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
|
||||
else:
|
||||
commands.append(packer.make_can_msg('LKAS_HUD', bus_lkas, lkas_hud_values))
|
||||
|
||||
if radar_disabled and CP.carFingerprint in HONDA_BOSCH:
|
||||
if radar_disabled:
|
||||
radar_hud_values = {
|
||||
'CMBS_OFF': 0x01,
|
||||
'SET_TO_1': 0x01,
|
||||
|
||||