Merge remote-tracking branch 'upstream/master' into log-stock-aeb-tssp

pull/25489/head
Shane Smiskol 3 years ago
commit 5899ce1517
  1. 27
      .github/workflows/badges.yaml
  2. 230
      .github/workflows/selfdrive_tests.yaml
  3. 45
      .github/workflows/setup/action.yaml
  4. 40
      .github/workflows/tools_tests.yaml
  5. 26
      README.md
  6. 9
      RELEASES.md
  7. 2
      cereal
  8. 7
      docs/CARS.md
  9. 2
      opendbc
  10. 2
      panda
  11. 2
      release/files_common
  12. 245
      selfdrive/boardd/tests/boardd_old.py
  13. 83
      selfdrive/boardd/tests/replay_many.py
  14. 77
      selfdrive/boardd/tests/test_boardd_api.py
  15. 4
      selfdrive/car/docs.py
  16. 114
      selfdrive/car/ford/radar_interface.py
  17. 9
      selfdrive/car/ford/values.py
  18. 4
      selfdrive/car/gm/carcontroller.py
  19. 3
      selfdrive/car/gm/carstate.py
  20. 7
      selfdrive/car/gm/gmcan.py
  21. 14
      selfdrive/car/gm/interface.py
  22. 13
      selfdrive/car/gm/values.py
  23. 5
      selfdrive/car/honda/interface.py
  24. 2
      selfdrive/car/honda/values.py
  25. 29
      selfdrive/car/hyundai/carcontroller.py
  26. 103
      selfdrive/car/hyundai/carstate.py
  27. 23
      selfdrive/car/hyundai/hyundaicanfd.py
  28. 44
      selfdrive/car/hyundai/interface.py
  29. 43
      selfdrive/car/hyundai/values.py
  30. 5
      selfdrive/car/interfaces.py
  31. 2
      selfdrive/car/mazda/values.py
  32. 6
      selfdrive/car/tests/routes.py
  33. 12
      selfdrive/car/tests/test_models.py
  34. 2
      selfdrive/car/torque_data/override.yaml
  35. 2
      selfdrive/car/toyota/values.py
  36. 4
      selfdrive/car/volkswagen/interface.py
  37. 6
      selfdrive/loggerd/encoder/v4l_encoder.cc
  38. 2
      selfdrive/test/process_replay/ref_commit
  39. 4
      selfdrive/test/process_replay/test_processes.py
  40. 2
      selfdrive/test/test_onroad.py
  41. 11
      selfdrive/ui/qt/sidebar.cc
  42. 4
      selfdrive/ui/qt/sidebar.h
  43. 12
      selfdrive/ui/tests/test_translations.py
  44. 7
      selfdrive/ui/translations/README.md
  45. 23
      selfdrive/ui/translations/create_badges.py
  46. 34
      selfdrive/ui/translations/main_ja.ts
  47. 34
      selfdrive/ui/translations/main_ko.ts
  48. 34
      selfdrive/ui/translations/main_pt.ts
  49. 34
      selfdrive/ui/translations/main_zh-CHS.ts
  50. 34
      selfdrive/ui/translations/main_zh-CHT.ts
  51. 2
      tools/joystick/joystickd.py
  52. 8
      tools/replay/consoleui.cc
  53. 7
      tools/replay/replay.cc
  54. 5
      tools/replay/replay.h
  55. 2
      tools/sim/bridge.py
  56. 17
      update_requirements.sh

@ -7,12 +7,7 @@ on:
env: env:
BASE_IMAGE: openpilot-base BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai DOCKER_REGISTRY: ghcr.io/commaai
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/sh -c
BUILD: |
docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
jobs: jobs:
badges: badges:
@ -23,23 +18,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup
- name: Cache scons
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Push badges - name: Push badges
run: | run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/ui/translations/create_badges.py" ${{ env.RUN }} "scons -j$(nproc) && python selfdrive/ui/translations/create_badges.py"
@ -49,6 +28,6 @@ jobs:
git config user.email "badge-researcher@comma.ai" git config user.email "badge-researcher@comma.ai"
git config user.name "Badge Researcher" git config user.name "Badge Researcher"
git add translation_badge_*.svg git add translation_badge.svg
git commit -m "Add/Update badges" git commit -m "Add/Update badges"
git push -f origin HEAD git push -f origin HEAD

@ -17,7 +17,7 @@ env:
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base . docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: | BUILD_CL: |
docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true
@ -27,11 +27,10 @@ env:
UNIT_TEST: coverage run --append -m unittest discover UNIT_TEST: coverage run --append -m unittest discover
jobs: jobs:
# TODO: once actions/cache supports read only mode, use the cache for all jobs
build_release: build_release:
name: build release name: build release
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 50 timeout-minutes: 30
env: env:
STRIPPED_DIR: /tmp/releasepilot STRIPPED_DIR: /tmp/releasepilot
steps: steps:
@ -39,44 +38,29 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- name: Pull LFS - name: Build devel
run: git lfs pull run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
- uses: ./.github/workflows/setup
- name: Check submodules - name: Check submodules
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: release/check-submodules.sh run: release/check-submodules.sh
- name: Cache scons - name: Build openpilot and run checks
id: scons-cache run: |
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. cd $STRIPPED_DIR
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b ${{ env.RUN }} "CI=1 python selfdrive/manager/build.py"
env: - name: Run tests
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build devel
run: | run: |
TARGET_DIR=$STRIPPED_DIR release/build_devel.sh cd $STRIPPED_DIR
cp Dockerfile.openpilot_base $STRIPPED_DIR ${{ env.RUN }} "release/check-dirty.sh && \
python -m unittest discover selfdrive/car"
- name: pre-commit
run: |
cd $GITHUB_WORKSPACE
cp .pre-commit-config.yaml $STRIPPED_DIR cp .pre-commit-config.yaml $STRIPPED_DIR
cp .pylintrc $STRIPPED_DIR cp .pylintrc $STRIPPED_DIR
cp mypy.ini $STRIPPED_DIR cp mypy.ini $STRIPPED_DIR
- name: Build Docker image cd $STRIPPED_DIR
run: | ${{ env.RUN }} "pre-commit run --all"
eval "$BUILD"
rm $STRIPPED_DIR/Dockerfile.openpilot_base
- name: Build openpilot and run checks
run: |
cd $STRIPPED_DIR
${{ env.RUN }} "CI=1 python selfdrive/manager/build.py && \
pre-commit run --all && \
rm .pre-commit-config.yaml && \
rm .pylintrc && \
rm mypy.ini && \
release/check-dirty.sh && \
python -m unittest discover selfdrive/car"
build_all: build_all:
name: build all name: build all
@ -86,26 +70,14 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache scons - uses: ./.github/workflows/setup
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }}
with: with:
path: /tmp/scons_cache save-cache: true
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }}
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Build openpilot with all flags - name: Build openpilot with all flags
run: ${{ env.RUN }} "scons -j$(nproc) --extras --test && release/check-dirty.sh" run: ${{ env.RUN }} "scons -j$(nproc) --extras --test && release/check-dirty.sh"
- name: Cleanup scons cache - name: Cleanup scons cache
run: | run: |
${{ env.RUN }} "scons -j$(nproc) --extras --test && \ ${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --extras --test --cache-populate" scons -j$(nproc) --extras --test --cache-populate"
#build_mac: #build_mac:
@ -175,21 +147,9 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache scons - uses: ./.github/workflows/setup
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image - name: Build Docker image
run: | run: |
eval "$BUILD"
docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true
docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/webcam/Dockerfile . docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/webcam/Dockerfile .
- name: Build openpilot - name: Build openpilot
@ -244,30 +204,11 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache dependencies - uses: ./.github/workflows/setup
id: dependency-cache
uses: actions/cache@v2
with:
path: /tmp/comma_download_cache
key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/test_valgrind_replay.py') }}
- name: Cache scons
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Run valgrind - name: Run valgrind
run: | run: |
${{ env.RUN }} "scons -j$(nproc) && \ ${{ env.RUN }} "scons -j$(nproc) && \
FILEREADER_CACHE=1 python selfdrive/test/test_valgrind_replay.py" python selfdrive/test/test_valgrind_replay.py"
- name: Print logs - name: Print logs
if: always() if: always()
run: cat selfdrive/test/valgrind_logs.txt run: cat selfdrive/test/valgrind_logs.txt
@ -277,28 +218,10 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
timeout-minutes: 50 timeout-minutes: 50
steps: steps:
- name: Get current date
id: date
run: echo "::set-output name=time::$(date +'%s')"
- name: Output timestamp
run: echo $TIMESTAMP
env:
TIMESTAMP: ${{ steps.date.outputs.time }}
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache scons - uses: ./.github/workflows/setup
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }}
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }}
restore-keys: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
- name: Build Docker image
run: eval "$BUILD"
- name: Run unit tests - name: Run unit tests
run: | run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \ ${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
@ -339,30 +262,17 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache dependencies - uses: ./.github/workflows/setup
- name: Cache test routes
id: dependency-cache id: dependency-cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: /tmp/comma_download_cache path: /tmp/comma_download_cache
key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/test_processes.py') }} key: proc-replay-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/ref_commit') }}
- name: Cache scons
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Run replay - name: Run replay
run: | run: |
${{ env.RUN }} "scons -j$(nproc) && \ ${{ env.RUN }} "scons -j$(nproc) && \
FILEREADER_CACHE=1 CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
coverage xml" coverage xml"
- name: Print diff - name: Print diff
if: always() if: always()
@ -389,35 +299,16 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Pull LFS - uses: ./.github/workflows/setup
run: git lfs pull
- name: Cache dependencies
id: dependency-cache
uses: actions/cache@v2
with:
path: /tmp/comma_download_cache
key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/model_replay.py') }}
- name: Cache scons
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image - name: Build Docker image
# Sim docker is needed to get the intel OPENCL drivers # Sim docker is needed to get the OpenCL drivers
run: eval "$BUILD_CL" run: eval "$BUILD_CL"
- name: Run replay - name: Run replay
run: | run: |
${{ env.RUN_CL }} "scons -j$(nproc) && \ ${{ env.RUN_CL }} "scons -j$(nproc) && \
ONNXCPU=1 FILEREADER_CACHE=1 CI=1 coverage run \ ONNXCPU=1 CI=1 coverage run \
selfdrive/test/process_replay/model_replay.py -j$(nproc) && \ selfdrive/test/process_replay/model_replay.py -j$(nproc) && \
coverage xml" coverage xml"
test_longitudinal: test_longitudinal:
name: longitudinal name: longitudinal
@ -427,20 +318,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache scons - uses: ./.github/workflows/setup
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Test longitudinal - name: Test longitudinal
run: | run: |
${{ env.RUN }} "mkdir -p selfdrive/test/out && \ ${{ env.RUN }} "mkdir -p selfdrive/test/out && \
@ -469,30 +347,17 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true
- name: Cache dependencies - uses: ./.github/workflows/setup
- name: Cache test routes
id: dependency-cache id: dependency-cache
uses: actions/cache@v2 uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
with: with:
path: /tmp/comma_download_cache path: /tmp/comma_download_cache
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }} key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }}
- name: Cache scons
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Test car models - name: Test car models
run: | run: |
${{ env.RUN }} "scons -j$(nproc) --test && \ ${{ env.RUN }} "scons -j$(nproc) --test && \
FILEREADER_CACHE=1 coverage run -m pytest selfdrive/car/tests/test_models.py && \ coverage run -m pytest selfdrive/car/tests/test_models.py && \
coverage xml && \ coverage xml && \
chmod -R 777 /tmp/comma_download_cache" chmod -R 777 /tmp/comma_download_cache"
env: env:
@ -530,20 +395,7 @@ jobs:
with: with:
submodules: true submodules: true
ref: ${{ github.event.pull_request.base.ref }} ref: ${{ github.event.pull_request.base.ref }}
- name: Cache scons - uses: ./.github/workflows/setup
id: scons-cache
# TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
env:
CACHE_SKIP_SAVE: true
with:
path: /tmp/scons_cache
key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
restore-keys: |
scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-
scons-
- name: Build Docker image
run: eval "$BUILD"
- name: Get base car info - name: Get base car info
run: | run: |
${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info" ${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info"

@ -0,0 +1,45 @@
name: 'openpilot env setup'
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai
BUILD: |
docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
inputs:
save-cache:
default: false
required: false
runs:
using: "composite"
steps:
# do this after checkout to ensure our custom LFS config is used to pull from GitLab
- shell: bash
run: git lfs pull
# build cache
- id: date
shell: bash
run: echo "::set-output name=date::$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d')"
- shell: bash
run: echo "${{ steps.date.outputs.date }}"
- shell: bash
run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV
if: github.ref != 'refs/heads/master' || inputs.save-cache == 'false'
- id: scons-cache
# TODO: change the version to the released version
# when https://github.com/actions/cache/pull/489 (or 571) is merged.
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
with:
path: /tmp/scons_cache
key: scons-${{ steps.date.outputs.date }}-${{ github.sha }}
restore-keys: |
scons-${{ steps.date.outputs.date }}-
scons-
# build our docker image
- shell: bash
run: eval "$BUILD"

@ -21,46 +21,6 @@ env:
GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c
jobs: jobs:
build_latest_ubuntu:
name: build latest ubuntu
runs-on: ubuntu-20.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Cache pyenv
id: ubuntu-latest-pyenv
uses: actions/cache@v3
with:
path: |
~/.pyenv
~/.local/share/virtualenvs/
key: ubuntu-latest-python-${{ hashFiles('tools/ubuntu_setup.sh') }}-
- name: Cache scons
id: ubuntu-latest-scons
uses: actions/cache@v3
with:
path: /tmp/scons_cache
key: ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}-
restore-keys: |
ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}-
ubuntu-latest-scons-
- name: tools/ubuntu_setup.sh
run: |
source tools/openpilot_env.sh
tools/ubuntu_setup.sh
- name: Build openpilot
run: |
source tools/openpilot_env.sh
pipenv run scons -j$(nproc) --extras --test
- name: Cleanup scons cache
run: |
source tools/openpilot_env.sh
rm -rf /tmp/scons_cache/*
pipenv run scons -j$(nproc) --extras --test --cache-populate
plotjuggler: plotjuggler:
name: plotjuggler name: plotjuggler
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

@ -17,7 +17,7 @@ Table of Contents
What is openpilot? What is openpilot?
------ ------
[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW) and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md). [openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW), and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models, and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera-based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md).
<table> <table>
<tr> <tr>
@ -40,9 +40,9 @@ Running on a dedicated device in a car
To use openpilot in a car, you need four things To use openpilot in a car, you need four things
* A supported device to run this software: a [comma three](https://comma.ai/shop/products/three). * A supported device to run this software: a [comma three](https://comma.ai/shop/products/three).
* This software. The setup procedure of the comma three allows the user to enter a url for custom software. * This software. The setup procedure of the comma three allows the user to enter a URL for custom software.
The url, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another github username can install a fork. The URL, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another GitHub username can install a fork.
* One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot. * One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run openpilot.
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car. * A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car.
We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup). We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
@ -52,11 +52,11 @@ Running on PC
All of openpilot's services can run as normal on a PC, even without special hardware or a car. To develop or experiment with openpilot you can run openpilot on recorded or simulated data. All of openpilot's services can run as normal on a PC, even without special hardware or a car. To develop or experiment with openpilot you can run openpilot on recorded or simulated data.
With openpilot's tools you can plot logs, replay drives and watch the full-res camera streams. See [the tools README](tools/README.md) for more information. With openpilot's tools, you can plot logs, replay drives, and watch the full-res camera streams. See [the tools README](tools/README.md) for more information.
You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes, but does require a decent GPU. You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes but does require a decent GPU.
A PC running openpilot can also control your vehicle if it is connected to a [a webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and [a harness](https://comma.ai/shop/products/car-harness). A PC running openpilot can also control your vehicle if it is connected to a [webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and a [harness](https://comma.ai/shop/products/car-harness).
Community and Contributing Community and Contributing
------ ------
@ -78,8 +78,8 @@ By default, openpilot uploads the driving data to our servers. You can also acce
openpilot is open source software: the user is free to disable data collection if they wish to do so. openpilot is open source software: the user is free to disable data collection if they wish to do so.
openpilot logs the road facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs. openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
The driver facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded. The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded.
By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data. By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data.
@ -87,11 +87,11 @@ Safety and Testing
---- ----
* openpilot observes ISO26262 guidelines, see [SAFETY.md](docs/SAFETY.md) for more details. * openpilot observes ISO26262 guidelines, see [SAFETY.md](docs/SAFETY.md) for more details.
* openpilot has software in the loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit. * openpilot has software-in-the-loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit.
* The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details. * The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details.
* panda has software in the loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety). * panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety).
* Internally, we have a hardware in the loop Jenkins test suite that builds and unit tests the various processes. * Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes.
* panda has additional hardware in the loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile). * panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile).
* We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes. * We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes.
Directory Structure Directory Structure

@ -1,11 +1,18 @@
Version 0.8.16 (2022-XX-XX) Version 0.8.16 (2022-08-26)
======================== ========================
* New driving model * New driving model
* Reduced turn cutting * Reduced turn cutting
* Auto-detect right hand drive setting with driver monitoring model * Auto-detect right hand drive setting with driver monitoring model
* Improved fan controller for comma three
* New translations
* Japanese thanks to cydia2020!
* Brazilian Portuguese thanks to AlexandreSato!
* Chevrolet Bolt EUV 2022-23 support thanks to JasonJShuler! * Chevrolet Bolt EUV 2022-23 support thanks to JasonJShuler!
* Chevrolet Silverado 1500 2020-21 support thanks to JasonJShuler!
* GMC Sierra 1500 2020-21 support thanks to JasonJShuler!
* Hyundai Ioniq 5 2022 support thanks to sunnyhaibin! * Hyundai Ioniq 5 2022 support thanks to sunnyhaibin!
* Hyundai Kona Electric 2022 support thanks to sunnyhaibin! * Hyundai Kona Electric 2022 support thanks to sunnyhaibin!
* Hyundai Tucson Hybrid 2022 support thanks to sunnyhaibin!
* Subaru Legacy 2020-22 support thanks to martinl! * Subaru Legacy 2020-22 support thanks to martinl!
* Subaru Outback 2020-22 support * Subaru Outback 2020-22 support

@ -1 +1 @@
Subproject commit ecff0a284a2aa9879c4d04ea797356e4ef024dc4 Subproject commit 589ef049a7b0bac31f4c8987c0fc539839fae489

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system. A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system.
# 202 Supported Cars # 205 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Harness| |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Harness|
|---|---|---|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|
@ -18,7 +18,8 @@ A supported vehicle is one that just works when you install a comma device. Ever
|Audi|RS3 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|RS3 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|S3 2015-17|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|S3 2015-17|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW|
|Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| |Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
|Chevrolet|Bolt EUV 2022-23|Premier/Premier Redline Trim|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| |Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2019-20|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
@ -31,6 +32,7 @@ A supported vehicle is one that just works when you install a comma device. Ever
|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Genesis|G90 2017-18|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Genesis|G90 2017-18|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| |GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM|
|Honda|Accord 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| |Honda|Accord 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
|Honda|Accord Hybrid 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| |Honda|Accord Hybrid 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A|
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec|
@ -76,6 +78,7 @@ A supported vehicle is one that just works when you install a comma device. Ever
|Hyundai|Sonata Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Hyundai|Sonata Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson 2021|Smart Cruise Control (SCC)|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA|

@ -1 +1 @@
Subproject commit 7e095a90af3d36e6db9128a80f6f3b0cca01efa2 Subproject commit b913296c9123441b2b271c00239929ed388169b5

@ -1 +1 @@
Subproject commit ba8772123f13123c797234660c56adb70795cebb Subproject commit 9d6496ece8465dfe30997b31dfb352e1e51dde6c

@ -513,6 +513,8 @@ opendbc/gm_global_a_powertrain_generated.dbc
opendbc/gm_global_a_object.dbc opendbc/gm_global_a_object.dbc
opendbc/gm_global_a_chassis.dbc opendbc/gm_global_a_chassis.dbc
opendbc/FORD_CADS.dbc
opendbc/ford_fusion_2018_adas.dbc
opendbc/ford_lincoln_base_pt.dbc opendbc/ford_lincoln_base_pt.dbc
opendbc/honda_accord_2018_can_generated.dbc opendbc/honda_accord_2018_can_generated.dbc

@ -1,245 +0,0 @@
#!/usr/bin/env python3
# pylint: skip-file
# This file is not used by openpilot. Only boardd.cc is used.
# The python version is slower, but has more options for development.
# TODO: merge the extra functionalities of this file (like MOCK) in boardd.c and
# delete this python version of boardd
import os
import struct
import time
import cereal.messaging as messaging
from common.realtime import Ratekeeper
from system.swaglog import cloudlog
from selfdrive.boardd.boardd import can_capnp_to_can_list
from cereal import car
SafetyModel = car.CarParams.SafetyModel
# USB is optional
try:
import usb1
from usb1 import USBErrorIO, USBErrorOverflow # pylint: disable=no-name-in-module
except Exception:
pass
# *** serialization functions ***
def can_list_to_can_capnp(can_msgs, msgtype='can'):
dat = messaging.new_message(msgtype, len(can_msgs))
for i, can_msg in enumerate(can_msgs):
if msgtype == 'sendcan':
cc = dat.sendcan[i]
else:
cc = dat.can[i]
cc.address = can_msg[0]
cc.busTime = can_msg[1]
cc.dat = bytes(can_msg[2])
cc.src = can_msg[3]
return dat
# *** can driver ***
def can_health():
while 1:
try:
dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x16)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD HEALTH, RETRYING")
v, i = struct.unpack("II", dat[0:8])
ign_line, ign_can = struct.unpack("BB", dat[20:22])
return {"voltage": v, "current": i, "ignition_line": bool(ign_line), "ignition_can": bool(ign_can)}
def __parse_can_buffer(dat):
ret = []
for j in range(0, len(dat), 0x10):
ddat = dat[j:j+0x10]
f1, f2 = struct.unpack("II", ddat[0:8])
ret.append((f1 >> 21, f2 >> 16, ddat[8:8 + (f2 & 0xF)], (f2 >> 4) & 0xFF))
return ret
def can_send_many(arr):
snds = []
for addr, _, dat, alt in arr:
if addr < 0x800: # only support 11 bit addr
snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
snd = snd.ljust(0x10, b'\x00')
snds.append(snd)
while 1:
try:
handle.bulkWrite(3, b''.join(snds))
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
def can_recv():
dat = b""
while 1:
try:
dat = handle.bulkRead(1, 0x10*256)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD RECV, RETRYING")
return __parse_can_buffer(dat)
def can_init():
global handle, context
handle = None
cloudlog.info("attempting can init")
context = usb1.USBContext()
#context.setDebug(9)
for device in context.getDeviceList(skip_on_error=True):
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
handle = device.open()
handle.claimInterface(0)
handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
if handle is None:
cloudlog.warning("CAN NOT FOUND")
exit(-1)
cloudlog.info("got handle")
cloudlog.info("can init done")
def boardd_mock_loop():
can_init()
handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
logcan = messaging.sub_sock('can')
sendcan = messaging.pub_sock('sendcan')
while 1:
tsc = messaging.drain_sock(logcan, wait_for_one=True)
snds = map(lambda x: can_capnp_to_can_list(x.can), tsc)
snd = []
for s in snds:
snd += s
snd = list(filter(lambda x: x[-1] <= 2, snd))
snd_0 = len(list(filter(lambda x: x[-1] == 0, snd)))
snd_1 = len(list(filter(lambda x: x[-1] == 1, snd)))
snd_2 = len(list(filter(lambda x: x[-1] == 2, snd)))
can_send_many(snd)
# recv @ 100hz
can_msgs = can_recv()
got_0 = len(list(filter(lambda x: x[-1] == 0+0x80, can_msgs)))
got_1 = len(list(filter(lambda x: x[-1] == 1+0x80, can_msgs)))
got_2 = len(list(filter(lambda x: x[-1] == 2+0x80, can_msgs)))
print("sent %3d (%3d/%3d/%3d) got %3d (%3d/%3d/%3d)" %
(len(snd), snd_0, snd_1, snd_2, len(can_msgs), got_0, got_1, got_2))
m = can_list_to_can_capnp(can_msgs, msgtype='sendcan')
sendcan.send(m.to_bytes())
def boardd_test_loop():
can_init()
cnt = 0
while 1:
can_send_many([[0xbb, 0, "\xaa\xaa\xaa\xaa", 0], [0xaa, 0, "\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt), 1]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]])
# recv @ 100hz
can_msgs = can_recv()
print("got %d" % (len(can_msgs)))
time.sleep(0.01)
cnt += 1
# *** main loop ***
def boardd_loop(rate=100):
rk = Ratekeeper(rate)
can_init()
# *** publishes can and health
logcan = messaging.pub_sock('can')
health_sock = messaging.pub_sock('pandaStates')
# *** subscribes to can send
sendcan = messaging.sub_sock('sendcan')
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# health packet @ 2hz
if (rk.frame % (rate // 2)) == 0:
health = can_health()
msg = messaging.new_message('pandaStates', 1)
# store the health to be logged
msg.pandaState[0].voltage = health['voltage']
msg.pandaState[0].current = health['current']
msg.pandaState[0].ignitionLine = health['ignition_line']
msg.pandaState[0].ignitionCan = health['ignition_can']
msg.pandaState[0].controlsAllowed = True
health_sock.send(msg.to_bytes())
# recv @ 100hz
can_msgs = can_recv()
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs).to_bytes()
logcan.send(dat)
# send can if we have a packet
tsc = messaging.recv_sock(sendcan)
if tsc is not None:
can_send_many(can_capnp_to_can_list(tsc.sendcan))
rk.keep_time()
# *** main loop ***
def boardd_proxy_loop(rate=100, address="192.168.2.251"):
rk = Ratekeeper(rate)
can_init()
# *** subscribes can
logcan = messaging.sub_sock('can', addr=address)
# *** publishes to can send
sendcan = messaging.pub_sock('sendcan')
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# recv @ 100hz
can_msgs = can_recv()
#for m in can_msgs:
# print("R: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs, "sendcan")
sendcan.send(dat)
# send can if we have a packet
tsc = messaging.recv_sock(logcan)
if tsc is not None:
cl = can_capnp_to_can_list(tsc.can)
#for m in cl:
# print("S: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
can_send_many(cl)
rk.keep_time()
def main():
if os.getenv("MOCK") is not None:
boardd_mock_loop()
elif os.getenv("PROXY") is not None:
boardd_proxy_loop()
elif os.getenv("BOARDTEST") is not None:
boardd_test_loop()
else:
boardd_loop()
if __name__ == "__main__":
main()

@ -1,83 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import time
import signal
import traceback
import usb1
from panda import Panda, PandaDFU
from multiprocessing import Pool
jungle = "JUNGLE" in os.environ
if jungle:
from panda_jungle import PandaJungle # pylint: disable=import-error
import cereal.messaging as messaging
from selfdrive.boardd.boardd import can_capnp_to_can_list
def initializer():
"""Ignore CTRL+C in the worker process.
source: https://stackoverflow.com/a/44869451 """
signal.signal(signal.SIGINT, signal.SIG_IGN)
def send_thread(sender_serial):
while True:
try:
if jungle:
sender = PandaJungle(sender_serial)
else:
sender = Panda(sender_serial)
sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
sender.set_can_loopback(False)
can_sock = messaging.sub_sock('can')
while True:
tsc = messaging.recv_one(can_sock)
snd = can_capnp_to_can_list(tsc.can)
snd = list(filter(lambda x: x[-1] <= 2, snd))
try:
sender.can_send_many(snd)
except usb1.USBErrorTimeout:
pass
# Drain panda message buffer
sender.can_recv()
except Exception:
traceback.print_exc()
time.sleep(1)
if __name__ == "__main__":
if jungle:
serials = PandaJungle.list()
else:
serials = Panda.list()
num_senders = len(serials)
if num_senders == 0:
print("No senders found. Exiting")
sys.exit(1)
else:
print("%d senders found. Starting broadcast" % num_senders)
if "FLASH" in os.environ:
for s in PandaDFU.list():
PandaDFU(s).recover()
time.sleep(1)
for s in serials:
Panda(s).recover()
Panda(s).flash()
pool = Pool(num_senders, initializer=initializer)
pool.map_async(send_thread, serials)
while True:
try:
time.sleep(10)
except KeyboardInterrupt:
pool.terminate()
pool.join()
raise

@ -1,77 +0,0 @@
import random
import numpy as np
import selfdrive.boardd.tests.boardd_old as boardd_old
import selfdrive.boardd.boardd as boardd
from common.realtime import sec_since_boot
from cereal import log
import unittest
def generate_random_can_data_list():
can_list = []
cnt = random.randint(1, 64)
for _ in range(cnt):
can_data = np.random.bytes(random.randint(1, 8))
can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)])
return can_list, cnt
class TestBoarddApiMethods(unittest.TestCase):
def test_correctness(self):
for i in range(1000):
can_list, _ = generate_random_can_data_list()
# Sendcan
# Old API
m_old = boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
# new API
m = boardd.can_list_to_can_capnp(can_list, 'sendcan')
ev_old = log.Event.from_bytes(m_old)
ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.sendcan), len(ev_old.sendcan))
for i in range(len(ev.sendcan)):
attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs:
self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old'))
# Can
m_old = boardd_old.can_list_to_can_capnp(can_list, 'can').to_bytes()
# new API
m = boardd.can_list_to_can_capnp(can_list, 'can')
ev_old = log.Event.from_bytes(m_old)
ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.can), len(ev_old.can))
for i in range(len(ev.can)):
attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs:
self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old'))
def test_performance(self):
can_list, _ = generate_random_can_data_list()
recursions = 1000
n1 = sec_since_boot()
for _ in range(recursions):
boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
n2 = sec_since_boot()
elapsed_old = n2 - n1
# print('Old API, elapsed time: {} secs'.format(elapsed_old))
n1 = sec_since_boot()
for _ in range(recursions):
boardd.can_list_to_can_capnp(can_list)
n2 = sec_since_boot()
elapsed_new = n2 - n1
# print('New API, elapsed time: {} secs'.format(elapsed_new))
self.assertTrue(elapsed_new < elapsed_old / 2)
if __name__ == '__main__':
unittest.main()

@ -8,6 +8,7 @@ from natsort import natsorted
from typing import Dict, List from typing import Dict, List
from common.basedir import BASEDIR 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
from selfdrive.car.car_helpers import interfaces, get_interface_attr from selfdrive.car.car_helpers import interfaces, get_interface_attr
from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR as HKG_RADAR_START_ADDR from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR as HKG_RADAR_START_ADDR
@ -29,7 +30,8 @@ def get_all_car_info() -> List[CarInfo]:
footnotes = get_all_footnotes() footnotes = get_all_footnotes()
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
# Hyundai exception: those with radar have openpilot longitudinal # Hyundai exception: those with radar have openpilot longitudinal
fingerprint = {0: {}, 1: {HKG_RADAR_START_ADDR: 8}, 2: {}, 3: {}} fingerprint = gen_empty_fingerprint()
fingerprint[1] = {HKG_RADAR_START_ADDR: 8}
CP = interfaces[model][0].get_params(model, fingerprint=fingerprint, disable_radar=True) CP = interfaces[model][0].get_params(model, fingerprint=fingerprint, disable_radar=True)
if CP.dashcamOnly or car_info is None: if CP.dashcamOnly or car_info is None:

@ -1,33 +1,63 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from math import cos, sin
from cereal import car from cereal import car
from common.conversions import Conversions as CV
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.ford.values import CANBUS, DBC from common.conversions import Conversions as CV
from selfdrive.car.ford.values import CANBUS, DBC, RADAR
from selfdrive.car.interfaces import RadarInterfaceBase from selfdrive.car.interfaces import RadarInterfaceBase
RADAR_MSGS = list(range(0x500, 0x540)) DELPHI_ESR_RADAR_MSGS = list(range(0x500, 0x540))
DELPHI_MRR_RADAR_START_ADDR = 0x120
DELPHI_MRR_RADAR_MSG_COUNT = 64
def _create_radar_can_parser(car_fingerprint):
if DBC[car_fingerprint]['radar'] is None:
return None
msg_n = len(RADAR_MSGS) def _create_delphi_esr_radar_can_parser():
msg_n = len(DELPHI_ESR_RADAR_MSGS)
signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n, signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n,
RADAR_MSGS * 3)) DELPHI_ESR_RADAR_MSGS * 3))
checks = list(zip(RADAR_MSGS, [20] * msg_n)) checks = list(zip(DELPHI_ESR_RADAR_MSGS, [20] * msg_n))
return CANParser(RADAR.DELPHI_ESR, signals, checks, CANBUS.radar)
def _create_delphi_mrr_radar_can_parser():
signals = []
checks = []
for i in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
msg = f"MRR_Detection_{i:03d}"
signals += [
(f"CAN_DET_VALID_LEVEL_{i:02d}", msg),
(f"CAN_DET_AZIMUTH_{i:02d}", msg),
(f"CAN_DET_RANGE_{i:02d}", msg),
(f"CAN_DET_RANGE_RATE_{i:02d}", msg),
(f"CAN_DET_AMPLITUDE_{i:02d}", msg),
(f"CAN_SCAN_INDEX_2LSB_{i:02d}", msg),
]
checks += [(msg, 20)]
return CANParser(RADAR.DELPHI_MRR, signals, checks, CANBUS.radar)
return CANParser(DBC[car_fingerprint]['radar'], signals, checks, CANBUS.radar)
class RadarInterface(RadarInterfaceBase): class RadarInterface(RadarInterfaceBase):
def __init__(self, CP): def __init__(self, CP):
super().__init__(CP) super().__init__(CP)
self.validCnt = {key: 0 for key in RADAR_MSGS}
self.track_id = 0
self.rcp = _create_radar_can_parser(CP.carFingerprint)
self.trigger_msg = 0x53f
self.updated_messages = set() self.updated_messages = set()
self.track_id = 0
self.radar = DBC[CP.carFingerprint]['radar']
if self.radar is None:
self.rcp = None
elif self.radar == RADAR.DELPHI_ESR:
self.rcp = _create_delphi_esr_radar_can_parser()
self.trigger_msg = DELPHI_ESR_RADAR_MSGS[-1]
self.valid_cnt = {key: 0 for key in DELPHI_ESR_RADAR_MSGS}
elif self.radar == RADAR.DELPHI_MRR:
self.rcp = _create_delphi_mrr_radar_can_parser()
self.trigger_msg = DELPHI_MRR_RADAR_START_ADDR + DELPHI_MRR_RADAR_MSG_COUNT - 1
else:
raise ValueError(f"Unsupported radar: {self.radar}")
def update(self, can_strings): def update(self, can_strings):
if self.rcp is None: if self.rcp is None:
@ -45,20 +75,30 @@ class RadarInterface(RadarInterfaceBase):
errors.append("canError") errors.append("canError")
ret.errors = errors ret.errors = errors
if self.radar == RADAR.DELPHI_ESR:
self._update_delphi_esr()
elif self.radar == RADAR.DELPHI_MRR:
self._update_delphi_mrr()
ret.points = list(self.pts.values())
self.updated_messages.clear()
return ret
def _update_delphi_esr(self):
for ii in sorted(self.updated_messages): for ii in sorted(self.updated_messages):
cpt = self.rcp.vl[ii] cpt = self.rcp.vl[ii]
if cpt['X_Rel'] > 0.00001: if cpt['X_Rel'] > 0.00001:
self.validCnt[ii] = 0 # reset counter self.valid_cnt[ii] = 0 # reset counter
if cpt['X_Rel'] > 0.00001: if cpt['X_Rel'] > 0.00001:
self.validCnt[ii] += 1 self.valid_cnt[ii] += 1
else: else:
self.validCnt[ii] = max(self.validCnt[ii] - 1, 0) self.valid_cnt[ii] = max(self.valid_cnt[ii] - 1, 0)
#print ii, self.validCnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle'] #print ii, self.valid_cnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle']
# radar point only valid if there have been enough valid measurements # radar point only valid if there have been enough valid measurements
if self.validCnt[ii] > 0: if self.valid_cnt[ii] > 0:
if ii not in self.pts: if ii not in self.pts:
self.pts[ii] = car.RadarData.RadarPoint.new_message() self.pts[ii] = car.RadarData.RadarPoint.new_message()
self.pts[ii].trackId = self.track_id self.pts[ii].trackId = self.track_id
@ -73,6 +113,36 @@ class RadarInterface(RadarInterfaceBase):
if ii in self.pts: if ii in self.pts:
del self.pts[ii] del self.pts[ii]
ret.points = list(self.pts.values()) def _update_delphi_mrr(self):
self.updated_messages.clear() for ii in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
return ret msg = self.rcp.vl[f"MRR_Detection_{ii:03d}"]
# SCAN_INDEX rotates through 0..3 on each message
# treat these as separate points
scanIndex = msg[f"CAN_SCAN_INDEX_2LSB_{ii:02d}"]
i = (ii - 1) * 4 + scanIndex
if i not in self.pts:
self.pts[i] = car.RadarData.RadarPoint.new_message()
self.pts[i].trackId = self.track_id
self.pts[i].aRel = float('nan')
self.pts[i].yvRel = float('nan')
self.track_id += 1
valid = bool(msg[f"CAN_DET_VALID_LEVEL_{ii:02d}"])
amplitude = msg[f"CAN_DET_AMPLITUDE_{ii:02d}"] # dBsm [-64|63]
if valid and 0 < amplitude <= 15:
azimuth = msg[f"CAN_DET_AZIMUTH_{ii:02d}"] # rad [-3.1416|3.13964]
dist = msg[f"CAN_DET_RANGE_{ii:02d}"] # m [0|255.984]
distRate = msg[f"CAN_DET_RANGE_RATE_{ii:02d}"] # m/s [-128|127.984]
# *** openpilot radar point ***
self.pts[i].dRel = cos(azimuth) * dist # m from front of car
self.pts[i].yRel = -sin(azimuth) * dist # in car frame's y axis, left is positive
self.pts[i].vRel = distRate # m/s
self.pts[i].measured = True
else:
del self.pts[i]

@ -24,6 +24,11 @@ class CarControllerParams:
STEER_RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4]) STEER_RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
class RADAR:
DELPHI_ESR = 'ford_fusion_2018_adas'
DELPHI_MRR = 'FORD_CADS'
class CANBUS: class CANBUS:
main = 0 main = 0
radar = 1 radar = 1
@ -80,6 +85,6 @@ FW_VERSIONS = {
DBC = { DBC = {
CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', None), CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', None), CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
} }

@ -109,10 +109,10 @@ class CarController:
else: else:
# Stock longitudinal, integrated at camera # Stock longitudinal, integrated at camera
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1: if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
if CC.cruiseControl.cancel: if CC.cruiseControl.cancel:
self.last_button_frame = self.frame self.last_button_frame = self.frame
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CruiseButtons.CANCEL)) can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL))
# Show green icon when LKA torque is applied, and # Show green icon when LKA torque is applied, and
# alarming orange icon when approaching torque limit. # alarming orange icon when approaching torque limit.

@ -16,12 +16,14 @@ class CarState(CarStateBase):
can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
self.lka_steering_cmd_counter = 0 self.lka_steering_cmd_counter = 0
self.buttons_counter = 0
def update(self, pt_cp, cam_cp, loopback_cp): def update(self, pt_cp, cam_cp, loopback_cp):
ret = car.CarState.new_message() ret = car.CarState.new_message()
self.prev_cruise_buttons = self.cruise_buttons self.prev_cruise_buttons = self.cruise_buttons
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"] self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
ret.wheelSpeeds = self.get_wheel_speeds( ret.wheelSpeeds = self.get_wheel_speeds(
pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"], pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
@ -109,6 +111,7 @@ class CarState(CarStateBase):
("AcceleratorPedal2", "AcceleratorPedal2"), ("AcceleratorPedal2", "AcceleratorPedal2"),
("CruiseState", "AcceleratorPedal2"), ("CruiseState", "AcceleratorPedal2"),
("ACCButtons", "ASCMSteeringButton"), ("ACCButtons", "ASCMSteeringButton"),
("RollingCounter", "ASCMSteeringButton"),
("SteeringWheelAngle", "PSCMSteeringAngle"), ("SteeringWheelAngle", "PSCMSteeringAngle"),
("SteeringWheelRate", "PSCMSteeringAngle"), ("SteeringWheelRate", "PSCMSteeringAngle"),
("FLWheelSpd", "EBCMWheelSpdFront"), ("FLWheelSpd", "EBCMWheelSpdFront"),

@ -1,7 +1,10 @@
from selfdrive.car import make_can_msg from selfdrive.car import make_can_msg
def create_buttons(packer, bus, button): def create_buttons(packer, bus, idx, button):
values = {"ACCButtons": button} values = {
"ACCButtons": button,
"RollingCounter": idx,
}
return packer.make_can_msg("ASCMSteeringButton", bus, values) return packer.make_can_msg("ASCMSteeringButton", bus, values)
def create_steering_control(packer, bus, apply_steer, idx, lkas_active): def create_steering_control(packer, bus, apply_steer, idx, lkas_active):

@ -4,7 +4,7 @@ from math import fabs
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car 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.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
@ -160,6 +160,15 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.SILVERADO:
ret.minEnableSpeed = -1
ret.mass = 2200. + STD_CARGO_KG
ret.wheelbase = 3.75
ret.steerRatio = 16.3
ret.centerToFront = ret.wheelbase * 0.5
tire_stiffness_factor = 1.0
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# TODO: get actual value, for now starting with reasonable value for # TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase # civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
@ -195,9 +204,6 @@ class CarInterface(CarInterfaceBase):
if ret.vEgo < self.CP.minSteerSpeed: if ret.vEgo < self.CP.minSteerSpeed:
events.add(car.CarEvent.EventName.belowSteerSpeed) events.add(car.CarEvent.EventName.belowSteerSpeed)
# handle button presses
events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise))
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret

@ -59,6 +59,7 @@ class CAR:
BUICK_REGAL = "BUICK REGAL ESSENCE 2018" BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
BOLT_EUV = "CHEVROLET BOLT EUV 2022" BOLT_EUV = "CHEVROLET BOLT EUV 2022"
SILVERADO = "CHEVROLET SILVERADO 1500 2020"
class Footnote(Enum): class Footnote(Enum):
@ -83,7 +84,11 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier/Premier Redline Trim", video_link="https://youtu.be/xvwzGMUA210", footnotes=[], harness=Harness.gm), CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210", footnotes=[], harness=Harness.gm),
CAR.SILVERADO: [
GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II", footnotes=[], harness=Harness.gm),
GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", footnotes=[], harness=Harness.gm),
],
} }
@ -157,6 +162,10 @@ FINGERPRINTS = {
{ {
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
}], }],
CAR.SILVERADO: [
{
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
}],
} }
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) 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'))
@ -164,6 +173,6 @@ DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_power
EV_CAR = {CAR.VOLT, 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) # We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
CAMERA_ACC_CAR = {CAR.BOLT_EUV} CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO}
STEER_THRESHOLD = 1.0 STEER_THRESHOLD = 1.0

@ -4,7 +4,7 @@ from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.numpy_fast import interp from common.numpy_fast import interp
from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS
from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
@ -352,9 +352,6 @@ class CarInterface(CarInterfaceBase):
if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001: if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001:
events.add(EventName.manualRestart) events.add(EventName.manualRestart)
# handle button presses
events.events.extend(create_button_enable_events(ret.buttonEvents, self.CP.pcmCruise))
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret

@ -113,7 +113,7 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
], ],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [ CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a),
HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a), HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a),

@ -5,7 +5,7 @@ from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.hyundai import hyundaicanfd, hyundaican from selfdrive.car.hyundai import hyundaicanfd, hyundaican
from selfdrive.car.hyundai.values import Buttons, CarControllerParams, CANFD_CAR, CAR from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
@ -71,22 +71,33 @@ class CarController:
if self.CP.carFingerprint in CANFD_CAR: if self.CP.carFingerprint in CANFD_CAR:
# steering control # steering control
can_sends.append(hyundaicanfd.create_lkas(self.packer, CC.enabled, CC.latActive, apply_steer)) can_sends.append(hyundaicanfd.create_lkas(self.packer, self.CP, CC.enabled, CC.latActive, apply_steer))
if self.frame % 5 == 0: # block LFA on HDA2
if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.CANFD_HDA2):
can_sends.append(hyundaicanfd.create_cam_0x2a4(self.packer, CS.cam_0x2a4)) can_sends.append(hyundaicanfd.create_cam_0x2a4(self.packer, CS.cam_0x2a4))
# cruise cancel # LFA and HDA icons
if self.frame % 2 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_HDA2):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, CC.enabled))
# button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25: if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel: if CC.cruiseControl.cancel:
for _ in range(20): if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL)) can_sends.append(hyundaicanfd.create_cruise_info(self.packer, CS.cruise_info_copy, True))
self.last_button_frame = self.frame self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume # cruise standstill resume
elif CC.cruiseControl.resume: elif CC.cruiseControl.resume:
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL)) if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
self.last_button_frame = self.frame can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
else: else:
# tester present - w/ no response (keeps radar disabled) # tester present - w/ no response (keeps radar disabled)

@ -5,7 +5,7 @@ from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from selfdrive.car.hyundai.values import DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.values import HyundaiFlags, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
PREV_BUTTON_SAMPLES = 8 PREV_BUTTON_SAMPLES = 8
@ -20,7 +20,7 @@ class CarState(CarStateBase):
self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
if CP.carFingerprint in CANFD_CAR: if CP.carFingerprint in CANFD_CAR:
self.shifter_values = can_define.dv["ACCELERATOR"]["GEAR"] self.shifter_values = can_define.dv["GEAR_SHIFTER"]["GEAR"]
elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]: elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"] self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in FEATURES["use_tcu_gears"]: elif self.CP.carFingerprint in FEATURES["use_tcu_gears"]:
@ -136,14 +136,17 @@ class CarState(CarStateBase):
def update_canfd(self, cp, cp_cam): def update_canfd(self, cp, cp_cam):
ret = car.CarState.new_message() ret = car.CarState.new_message()
ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255. if self.CP.flags & HyundaiFlags.CANFD_HDA2:
ret.gasPressed = ret.gas > 1e-3 ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255.
else:
ret.gas = cp.vl["ACCELERATOR_ALT"]["ACCELERATOR_PEDAL"] / 1023.
ret.gasPressed = ret.gas > 1e-5
ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1 ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1 ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0 ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0
gear = cp.vl["ACCELERATOR"]["GEAR"] gear = cp.vl["GEAR_SHIFTER"]["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
# TODO: figure out positions # TODO: figure out positions
@ -168,16 +171,19 @@ class CarState(CarStateBase):
ret.cruiseState.available = True ret.cruiseState.available = True
ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1 ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1
ret.cruiseState.standstill = cp.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1 cp_cruise_info = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS
ret.cruiseState.speed = cp.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor ret.cruiseState.speed = cp_cruise_info.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor
ret.cruiseState.standstill = cp_cruise_info.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1
self.cruise_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["CRUISE_BUTTONS"]) cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
self.main_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["ADAPTIVE_CRUISE_MAIN_BTN"]) self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"])
self.buttons_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"] self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"])
self.buttons_counter = cp.vl[cruise_btn_msg]["COUNTER"]
self.cruise_info_copy = copy.copy(cp_cruise_info.vl["CRUISE_INFO"])
self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"]) if self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"])
return ret return ret
@ -319,9 +325,7 @@ class CarState(CarStateBase):
@staticmethod @staticmethod
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
if CP.carFingerprint in CANFD_CAR: if CP.carFingerprint in CANFD_CAR:
signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)] return CarState.get_cam_can_parser_canfd(CP)
checks = [("CAM_0x2a4", 20)]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6)
signals = [ signals = [
# signal_name, signal_address # signal_name, signal_address
@ -374,14 +378,15 @@ class CarState(CarStateBase):
@staticmethod @staticmethod
def get_can_parser_canfd(CP): def get_can_parser_canfd(CP):
cruise_btn_msg = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
signals = [ signals = [
("WHEEL_SPEED_1", "WHEEL_SPEEDS"), ("WHEEL_SPEED_1", "WHEEL_SPEEDS"),
("WHEEL_SPEED_2", "WHEEL_SPEEDS"), ("WHEEL_SPEED_2", "WHEEL_SPEEDS"),
("WHEEL_SPEED_3", "WHEEL_SPEEDS"), ("WHEEL_SPEED_3", "WHEEL_SPEEDS"),
("WHEEL_SPEED_4", "WHEEL_SPEEDS"), ("WHEEL_SPEED_4", "WHEEL_SPEEDS"),
("ACCELERATOR_PEDAL", "ACCELERATOR"), ("GEAR", "GEAR_SHIFTER"),
("GEAR", "ACCELERATOR"),
("BRAKE_PRESSED", "BRAKE"), ("BRAKE_PRESSED", "BRAKE"),
("STEERING_RATE", "STEERING_SENSORS"), ("STEERING_RATE", "STEERING_SENSORS"),
@ -390,11 +395,9 @@ class CarState(CarStateBase):
("STEERING_OUT_TORQUE", "MDPS"), ("STEERING_OUT_TORQUE", "MDPS"),
("CRUISE_ACTIVE", "SCC1"), ("CRUISE_ACTIVE", "SCC1"),
("SET_SPEED", "CRUISE_INFO"), ("COUNTER", cruise_btn_msg),
("CRUISE_STANDSTILL", "CRUISE_INFO"), ("CRUISE_BUTTONS", cruise_btn_msg),
("COUNTER", "CRUISE_BUTTONS"), ("ADAPTIVE_CRUISE_MAIN_BTN", cruise_btn_msg),
("CRUISE_BUTTONS", "CRUISE_BUTTONS"),
("ADAPTIVE_CRUISE_MAIN_BTN", "CRUISE_BUTTONS"),
("DISTANCE_UNIT", "CLUSTER_INFO"), ("DISTANCE_UNIT", "CLUSTER_INFO"),
@ -407,16 +410,64 @@ class CarState(CarStateBase):
checks = [ checks = [
("WHEEL_SPEEDS", 100), ("WHEEL_SPEEDS", 100),
("ACCELERATOR", 100), ("GEAR_SHIFTER", 100),
("BRAKE", 100), ("BRAKE", 100),
("STEERING_SENSORS", 100), ("STEERING_SENSORS", 100),
("MDPS", 100), ("MDPS", 100),
("SCC1", 50), ("SCC1", 50),
("CRUISE_INFO", 50), (cruise_btn_msg, 50),
("CRUISE_BUTTONS", 50),
("CLUSTER_INFO", 4), ("CLUSTER_INFO", 4),
("BLINKERS", 4), ("BLINKERS", 4),
("DOORS_SEATBELTS", 4), ("DOORS_SEATBELTS", 4),
] ]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 5) if CP.flags & HyundaiFlags.CANFD_HDA2:
signals += [
("ACCELERATOR_PEDAL", "ACCELERATOR"),
("GEAR", "ACCELERATOR"),
("SET_SPEED", "CRUISE_INFO"),
("CRUISE_STANDSTILL", "CRUISE_INFO"),
]
checks += [
("CRUISE_INFO", 50),
("ACCELERATOR", 100),
]
else:
signals += [
("ACCELERATOR_PEDAL", "ACCELERATOR_ALT"),
]
checks += [
("ACCELERATOR_ALT", 100),
]
bus = 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus)
@staticmethod
def get_cam_can_parser_canfd(CP):
if CP.flags & HyundaiFlags.CANFD_HDA2:
signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)]
checks = [("CAM_0x2a4", 20)]
else:
signals = [
("COUNTER", "CRUISE_INFO"),
("NEW_SIGNAL_1", "CRUISE_INFO"),
("CRUISE_MAIN", "CRUISE_INFO"),
("CRUISE_STATUS", "CRUISE_INFO"),
("CRUISE_INACTIVE", "CRUISE_INFO"),
("NEW_SIGNAL_2", "CRUISE_INFO"),
("CRUISE_STANDSTILL", "CRUISE_INFO"),
("NEW_SIGNAL_3", "CRUISE_INFO"),
("BYTE11", "CRUISE_INFO"),
("SET_SPEED", "CRUISE_INFO"),
("NEW_SIGNAL_4", "CRUISE_INFO"),
]
signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(3, 7)]
signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(13, 31)]
checks = [
("CRUISE_INFO", 50),
]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6)

@ -1,4 +1,7 @@
def create_lkas(packer, enabled, lat_active, apply_steer): from selfdrive.car.hyundai.values import HyundaiFlags
def create_lkas(packer, CP, enabled, lat_active, apply_steer):
values = { values = {
"LKA_MODE": 2, "LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1, "LKA_ICON": 2 if enabled else 1,
@ -10,7 +13,9 @@ def create_lkas(packer, enabled, lat_active, apply_steer):
"NEW_SIGNAL_1": 0, "NEW_SIGNAL_1": 0,
"NEW_SIGNAL_2": 0, "NEW_SIGNAL_2": 0,
} }
return packer.make_can_msg("LKAS", 4, values)
msg = "LKAS" if CP.flags & HyundaiFlags.CANFD_HDA2 else "LFA"
return packer.make_can_msg(msg, 4, values)
def create_cam_0x2a4(packer, camera_values): def create_cam_0x2a4(packer, camera_values):
camera_values.update({ camera_values.update({
@ -25,3 +30,17 @@ def create_buttons(packer, cnt, btn):
"CRUISE_BUTTONS": btn, "CRUISE_BUTTONS": btn,
} }
return packer.make_can_msg("CRUISE_BUTTONS", 5, values) return packer.make_can_msg("CRUISE_BUTTONS", 5, values)
def create_cruise_info(packer, cruise_info_copy, cancel):
values = cruise_info_copy
if cancel:
values["CRUISE_STATUS"] = 0
values["CRUISE_INACTIVE"] = 1
return packer.make_can_msg("CRUISE_INFO", 4, values)
def create_lfahda_cluster(packer, enabled):
values = {
"HDA_ICON": 1 if enabled else 0,
"LFA_ICON": 2 if enabled else 0,
}
return packer.make_can_msg("LFAHDA_CLUSTER", 4, values)

@ -2,9 +2,9 @@
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car.hyundai.values import CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams
from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
@ -160,6 +160,12 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.TUCSON_HYBRID_4TH_GEN:
ret.mass = 1680. + STD_CARGO_KG # average of all 3 trims
ret.wheelbase = 2.756
ret.steerRatio = 16.
tire_stiffness_factor = 0.385
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Kia # Kia
elif candidate == CAR.KIA_SORENTO: elif candidate == CAR.KIA_SORENTO:
@ -169,7 +175,7 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021): elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021):
ret.lateralTuning.pid.kf = 0.00006 ret.lateralTuning.pid.kf = 0.00006
ret.mass = 1737. + STD_CARGO_KG ret.mass = 1737. + STD_CARGO_KG
ret.wheelbase = 2.7 ret.wheelbase = 2.7
@ -177,7 +183,7 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
if candidate == CAR.KIA_NIRO_HEV: if candidate == CAR.KIA_NIRO_PHEV:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.KIA_SELTOS: elif candidate == CAR.KIA_SELTOS:
ret.mass = 1337. + STD_CARGO_KG ret.mass = 1337. + STD_CARGO_KG
@ -284,7 +290,18 @@ class CarInterface(CarInterfaceBase):
if candidate in CANFD_CAR: if candidate in CANFD_CAR:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput),
get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd)] get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd)]
# detect HDA2 with LKAS message
if 0x50 in fingerprint[6]:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
ret.safetyConfigs[1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
else:
# non-HDA2
if 0x1cf not in fingerprint[4]:
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
else: else:
ret.enableBsm = 0x58b in fingerprint[0]
if candidate in LEGACY_SAFETY_MODE_CAR: if candidate in LEGACY_SAFETY_MODE_CAR:
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
@ -314,8 +331,6 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor) tire_stiffness_factor=tire_stiffness_factor)
ret.enableBsm = 0x58b in fingerprint[0]
return ret return ret
@staticmethod @staticmethod
@ -326,6 +341,14 @@ class CarInterface(CarInterfaceBase):
def _update(self, c): def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam) ret = self.CS.update(self.cp, self.cp_cam)
if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons:
buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)]
# Handle CF_Clu_CruiseSwState changing buttons mid-press
if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0:
buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT))
ret.buttonEvents = buttonEvents
# On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state # On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state
# To avoid re-engaging when openpilot cancels, check user engagement intention via buttons # To avoid re-engaging when openpilot cancels, check user engagement intention via buttons
# Main button also can trigger an engagement on these cars # Main button also can trigger an engagement on these cars
@ -335,15 +358,6 @@ class CarInterface(CarInterfaceBase):
if self.CS.brake_error: if self.CS.brake_error:
events.add(EventName.brakeUnavailable) events.add(EventName.brakeUnavailable)
if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons:
buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)]
# Handle CF_Clu_CruiseSwState changing buttons mid-press
if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0:
buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT))
ret.buttonEvents = buttonEvents
events.events.extend(create_button_enable_events(ret.buttonEvents))
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.: if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.:
self.low_speed_alert = True self.low_speed_alert = True

@ -1,3 +1,4 @@
from enum import IntFlag
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, List, Optional, Union from typing import Dict, List, Optional, Union
@ -29,7 +30,7 @@ class CarControllerParams:
# To determine the limit for your car, find the maximum value that the stock LKAS will request. # To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list. # If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ,
CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_HEV, CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER):
self.STEER_MAX = 255 self.STEER_MAX = 255
@ -38,6 +39,11 @@ class CarControllerParams:
self.STEER_MAX = 384 self.STEER_MAX = 384
class HyundaiFlags(IntFlag):
CANFD_HDA2 = 1
CANFD_ALT_BUTTONS = 2
class CAR: class CAR:
# Hyundai # Hyundai
ELANTRA = "HYUNDAI ELANTRA 2017" ELANTRA = "HYUNDAI ELANTRA 2017"
@ -66,12 +72,13 @@ class CAR:
VELOSTER = "HYUNDAI VELOSTER 2019" VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
IONIQ_5 = "HYUNDAI IONIQ 5 2022" IONIQ_5 = "HYUNDAI IONIQ 5 2022"
TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN"
# Kia # Kia
KIA_FORTE = "KIA FORTE E 2018 & GT 2021" KIA_FORTE = "KIA FORTE E 2018 & GT 2021"
KIA_K5_2021 = "KIA K5 2021" KIA_K5_2021 = "KIA K5 2021"
KIA_NIRO_EV = "KIA NIRO EV 2020" KIA_NIRO_EV = "KIA NIRO EV 2020"
KIA_NIRO_HEV = "KIA NIRO HYBRID 2019" KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019"
KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021" KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021"
KIA_OPTIMA = "KIA OPTIMA SX 2019 & 2016" KIA_OPTIMA = "KIA OPTIMA SX 2019 & 2016"
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019" KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
@ -128,6 +135,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", "Smart Cruise Control (SCC)", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e), CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", "Smart Cruise Control (SCC)", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a), CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a),
CAR.IONIQ_5: HyundaiCarInfo("Hyundai Ioniq 5 2022", "Highway Driving Assist II", harness=Harness.hyundai_q), CAR.IONIQ_5: HyundaiCarInfo("Hyundai Ioniq 5 2022", "Highway Driving Assist II", harness=Harness.hyundai_q),
CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n),
# Kia # Kia
CAR.KIA_FORTE: [ CAR.KIA_FORTE: [
@ -141,7 +149,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c),
HyundaiCarInfo("Kia Niro Electric 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h), HyundaiCarInfo("Kia Niro Electric 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h),
], ],
CAR.KIA_NIRO_HEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c), CAR.KIA_NIRO_PHEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c),
CAR.KIA_NIRO_HEV_2021: [ CAR.KIA_NIRO_HEV_2021: [
HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify
HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h), HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h),
@ -695,6 +703,7 @@ FW_VERSIONS = {
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ', b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.03 96400-J5100 ', b'\xf1\x00CK__ SCC F_CUP 1.00 1.03 96400-J5100 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x81606DE051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x81606DE051\x00\x00\x00\x00\x00\x00\x00\x00',
@ -712,6 +721,7 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822', b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504', b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504',
b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x87VCJLE17622572DK0vd6D\x99\x98y\x97vwVffUfvfC%CuT&Dx\x87o\xff{\x1c\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x87VCJLE17622572DK0vd6D\x99\x98y\x97vwVffUfvfC%CuT&Dx\x87o\xff{\x1c\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0',
@ -721,6 +731,7 @@ FW_VERSIONS = {
b'\xf1\x87VDHLG17118862DK2\x8awWwgu\x96wVfUVwv\x97xWvfvUTGTx\x87o\xff\xc9\xed\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x87VDHLG17118862DK2\x8awWwgu\x96wVfUVwv\x97xWvfvUTGTx\x87o\xff\xc9\xed\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0',
b'\xf1\x87VDKLJ18675252DK6\x89vhgwwwwveVU\x88w\x87w\x99vgf\x97vXfgw_\xff\xc2\xfb\xf1\x89E25\x00\x00\x00\x00\x00\x00\x00\xf1\x82TCK0T33NB2', b'\xf1\x87VDKLJ18675252DK6\x89vhgwwwwveVU\x88w\x87w\x99vgf\x97vXfgw_\xff\xc2\xfb\xf1\x89E25\x00\x00\x00\x00\x00\x00\x00\xf1\x82TCK0T33NB2',
b'\xf1\x87WAJTE17552812CH4vfFffvfVeT5DwvvVVdFeegeg\x88\x88o\xff\x1a]\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00TCK2T20NB1\x19\xd2\x00\x94', b'\xf1\x87WAJTE17552812CH4vfFffvfVeT5DwvvVVdFeegeg\x88\x88o\xff\x1a]\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00TCK2T20NB1\x19\xd2\x00\x94',
b'\xf1\x87VDHLG17274082DK2wfFf\x89x\x98wUT5T\x88v\x97xgeGefTGTVvO\xff\x1c\x14\xf1\x81E19\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E19\x00\x00\x00\x00\x00\x00\x00SCK0T33UB2\xee[\x97S',
], ],
}, },
CAR.PALISADE: { CAR.PALISADE: {
@ -1039,7 +1050,7 @@ FW_VERSIONS = {
b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821', b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821',
], ],
}, },
CAR.KIA_NIRO_HEV: { CAR.KIA_NIRO_PHEV: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1292,6 +1303,21 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206',
], ],
}, },
CAR.TUCSON_HYBRID_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00NX4 MDPS C 1.00 1.01 56300-P0100 2228',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x87391312MND0',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa',
b'\xf1\x8795441-3D220\x00\xf1\x81G19_Rev\x00\x00\x00\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa',
],
},
} }
CHECKSUM = { CHECKSUM = {
@ -1303,18 +1329,18 @@ FEATURES = {
# which message has the gear # which message has the gear
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, "use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
"use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022}, "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
"use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022}, "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022},
} }
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5} CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN}
# The camera does SCC on these cars, rather than the radar # The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022} EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022}
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
@ -1341,7 +1367,7 @@ DBC = {
CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None), CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None), CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.KIA_NIRO_HEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None), CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA: dbc_dict('hyundai_kia_generic', None), CAR.KIA_OPTIMA: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None), CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None),
@ -1364,5 +1390,6 @@ DBC = {
CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None), CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_EV6: dbc_dict('hyundai_canfd', None), CAR.KIA_EV6: dbc_dict('hyundai_canfd', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None), CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
} }

@ -9,7 +9,7 @@ from common.basedir import BASEDIR
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import gen_empty_fingerprint from selfdrive.car import create_button_enable_events, gen_empty_fingerprint
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
@ -213,6 +213,9 @@ class CarInterfaceBase(ABC):
if cs_out.accFaulted: if cs_out.accFaulted:
events.add(EventName.accFaulted) events.add(EventName.accFaulted)
# Handle button presses
events.events.extend(create_button_enable_events(cs_out.buttonEvents, pcm_cruise=self.CP.pcmCruise))
# Handle permanent and temporary steering faults # Handle permanent and temporary steering faults
self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1 self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1
if cs_out.steerFaultTemporary: if cs_out.steerFaultTemporary:

@ -40,7 +40,7 @@ CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = {
CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"), CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"),
CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"), CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"),
CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"), CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"),
CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22"), CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22", video_link="https://youtu.be/dA3duO4a0O4"),
CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"), CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"),
} }

@ -48,6 +48,7 @@ routes = [
TestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), TestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
TestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), TestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
TestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV), TestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV),
TestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.SILVERADO),
TestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G), TestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G),
TestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC), TestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC),
@ -82,13 +83,14 @@ routes = [
TestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE), TestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE),
TestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022), TestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022),
TestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022), TestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
TestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022), TestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022, segment=1),
TestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED), TestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
TestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA), TestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA),
TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS), TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON), TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
TestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN),
TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
TestRoute("22de8111a8c5463c|2022-07-29--13-34-49", HYUNDAI.IONIQ_5), TestRoute("22de8111a8c5463c|2022-07-29--13-34-49", HYUNDAI.IONIQ_5),
@ -107,7 +109,7 @@ routes = [
TestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6), TestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6),
TestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021), TestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
TestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), TestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
TestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_HEV), TestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV),
TestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021), TestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021),
TestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE), TestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
TestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA), TestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA),

@ -138,19 +138,23 @@ class TestCarModelBase(unittest.TestCase):
raise Exception("unkown tuning") raise Exception("unkown tuning")
def test_car_interface(self): def test_car_interface(self):
# TODO: also check for checkusm and counter violations from can parser # TODO: also check for checksum violations from can parser
can_invalid_cnt = 0 can_invalid_cnt = 0
can_valid = False
CC = car.CarControl.new_message() CC = car.CarControl.new_message()
for i, msg in enumerate(self.can_msgs): for i, msg in enumerate(self.can_msgs):
CS = self.CI.update(CC, (msg.as_builder().to_bytes(),)) CS = self.CI.update(CC, (msg.as_builder().to_bytes(),))
self.CI.apply(CC) self.CI.apply(CC)
# wait 2s for low frequency msgs to be seen if CS.canValid:
if i > 200: can_valid = True
# wait max of 2s for low frequency msgs to be seen
if i > 200 or can_valid:
can_invalid_cnt += not CS.canValid can_invalid_cnt += not CS.canValid
self.assertLess(can_invalid_cnt, 50) self.assertEqual(can_invalid_cnt, 0)
def test_radar_interface(self): def test_radar_interface(self):
os.environ['NO_RADAR_SLEEP'] = "1" os.environ['NO_RADAR_SLEEP'] = "1"

@ -25,7 +25,9 @@ RAM 1500 5TH GEN: [2.0, 2.0, 0.0]
RAM HD 5TH GEN: [1.4, 1.4, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0]
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]
CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05] CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1] VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1]
HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0]
# Dashcam or fallback configured as ideal car # Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0] mock: [10.0, 10, 0.0]

@ -783,6 +783,7 @@ FW_VERSIONS = {
b'\x01896637626000\x00\x00\x00\x00', b'\x01896637626000\x00\x00\x00\x00',
b'\x01896637648000\x00\x00\x00\x00', b'\x01896637648000\x00\x00\x00\x00',
b'\x01896637643000\x00\x00\x00\x00', b'\x01896637643000\x00\x00\x00\x00',
b'\x02896630A21000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZJ5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZJ5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZN8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZN8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x02896630ZQ3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZQ3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
@ -827,6 +828,7 @@ FW_VERSIONS = {
b'F152612A10\x00\x00\x00\x00\x00\x00', b'F152612A10\x00\x00\x00\x00\x00\x00',
b'F152612D00\x00\x00\x00\x00\x00\x00', b'F152612D00\x00\x00\x00\x00\x00\x00',
b'F152616011\x00\x00\x00\x00\x00\x00', b'F152616011\x00\x00\x00\x00\x00\x00',
b'F152616060\x00\x00\x00\x00\x00\x00',
b'F152642540\x00\x00\x00\x00\x00\x00', b'F152642540\x00\x00\x00\x00\x00\x00',
b'F152676293\x00\x00\x00\x00\x00\x00', b'F152676293\x00\x00\x00\x00\x00\x00',
b'F152676303\x00\x00\x00\x00\x00\x00', b'F152676303\x00\x00\x00\x00\x00\x00',

@ -1,12 +1,11 @@
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, create_button_enable_events, scale_rot_inertia, scale_tire_stiffness, \ from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, \
gen_empty_fingerprint, get_safety_config gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -225,7 +224,6 @@ class CarInterface(CarInterfaceBase):
if c.enabled and ret.vEgo < self.CP.minEnableSpeed: if c.enabled and ret.vEgo < self.CP.minEnableSpeed:
events.add(EventName.speedTooLow) events.add(EventName.speedTooLow)
events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise))
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret

@ -303,4 +303,10 @@ V4LEncoder::~V4LEncoder() {
checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type); checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0); request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
close(fd); close(fd);
for (int i = 0; i < BUF_OUT_COUNT; i++) {
if (buf_out[i].free() != 0) {
LOGE("Failed to free buffer");
}
}
} }

@ -1 +1 @@
93a136f91739847afe1e1a4a1e98bc7c0adb5ec8 656daeb9de3680258527500ecae4ddff323b2e59

@ -18,7 +18,7 @@ from tools.lib.logreader import LogReader
original_segments = [ original_segments = [
("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY ("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY
("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA
("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"), # HYUNDAI.KIA_EV6 ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"), # HYUNDAI.KIA_EV6
("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI) ("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI)
("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR) ("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR)
("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2 ("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2
@ -40,7 +40,7 @@ original_segments = [
segments = [ segments = [
("BODY", "regen660D86654BA|2022-07-06--14-27-15--0"), ("BODY", "regen660D86654BA|2022-07-06--14-27-15--0"),
("HYUNDAI", "regen114E5FF24D8|2022-07-14--17-08-47--0"), ("HYUNDAI", "regen114E5FF24D8|2022-07-14--17-08-47--0"),
("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"), ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"),
("TOYOTA", "regenBA97410FBEC|2022-07-06--14-26-49--0"), ("TOYOTA", "regenBA97410FBEC|2022-07-06--14-26-49--0"),
("TOYOTA2", "regenDEDB1D9C991|2022-07-06--14-54-08--0"), ("TOYOTA2", "regenDEDB1D9C991|2022-07-06--14-54-08--0"),
("TOYOTA3", "regenDDC1FE60734|2022-07-06--14-32-06--0"), ("TOYOTA3", "regenDDC1FE60734|2022-07-06--14-32-06--0"),

@ -37,7 +37,7 @@ PROCS = {
"selfdrive.thermald.thermald": 3.87, "selfdrive.thermald.thermald": 3.87,
"selfdrive.locationd.calibrationd": 2.0, "selfdrive.locationd.calibrationd": 2.0,
"./_soundd": 1.0, "./_soundd": 1.0,
"selfdrive.monitoring.dmonitoringd": 1.90, "selfdrive.monitoring.dmonitoringd": 4.0,
"./proclogd": 1.54, "./proclogd": 1.54,
"system.logmessaged": 0.2, "system.logmessaged": 0.2,
"./clocksd": 0.02, "./clocksd": 0.02,

@ -33,7 +33,7 @@ void Sidebar::drawMetric(QPainter &p, const QPair<QString, QString> &label, QCol
} }
Sidebar::Sidebar(QWidget *parent) : QFrame(parent) { Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
home_img = loadPixmap("../assets/images/button_home.png", {180, 180}); home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio); settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
connect(this, &Sidebar::valueChanged, [=] { update(); }); connect(this, &Sidebar::valueChanged, [=] { update(); });
@ -43,9 +43,16 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
setFixedWidth(300); setFixedWidth(300);
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"});
} }
void Sidebar::mouseReleaseEvent(QMouseEvent *event) { void Sidebar::mouseReleaseEvent(QMouseEvent *event) {
if (home_btn.contains(event->pos())) {
MessageBuilder msg;
msg.initEvent().initUserFlag();
pm->send("userFlag", msg);
}
if (settings_btn.contains(event->pos())) { if (settings_btn.contains(event->pos())) {
emit openSettings(); emit openSettings();
} }
@ -99,7 +106,7 @@ void Sidebar::paintEvent(QPaintEvent *event) {
p.setOpacity(0.65); p.setOpacity(0.65);
p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img); p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img);
p.setOpacity(1.0); p.setOpacity(1.0);
p.drawPixmap(60, 1080 - 180 - 40, home_img); p.drawPixmap(home_btn.x(), home_btn.y(), home_img);
// network // network
int x = 58; int x = 58;

@ -43,6 +43,7 @@ protected:
{cereal::DeviceState::NetworkType::CELL5_G, tr("5G")} {cereal::DeviceState::NetworkType::CELL5_G, tr("5G")}
}; };
const QRect home_btn = QRect(60, 860, 180, 180);
const QRect settings_btn = QRect(50, 35, 200, 117); const QRect settings_btn = QRect(50, 35, 200, 117);
const QColor good_color = QColor(255, 255, 255); const QColor good_color = QColor(255, 255, 255);
const QColor warning_color = QColor(218, 202, 37); const QColor warning_color = QColor(218, 202, 37);
@ -52,4 +53,7 @@ protected:
ItemStatus connect_status, panda_status, temp_status; ItemStatus connect_status, panda_status, temp_status;
QString net_type; QString net_type;
int net_strength = 0; int net_strength = 0;
private:
std::unique_ptr<PubMaster> pm;
}; };

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json import json
import os import os
import re
import shutil import shutil
import unittest import unittest
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -66,6 +67,11 @@ class TestTranslations(unittest.TestCase):
f"{file} ({name}) translation file has obsolete translations. Run selfdrive/ui/update_translations.py --vanish to remove them") f"{file} ({name}) translation file has obsolete translations. Run selfdrive/ui/update_translations.py --vanish to remove them")
def test_plural_translations(self): def test_plural_translations(self):
"""
Tests:
- that any numerus (plural) translations marked "finished" have all plural forms non-empty
- that the correct format specifier is used (%n)
"""
for name, file in self.translation_files.items(): for name, file in self.translation_files.items():
with self.subTest(name=name, file=file): with self.subTest(name=name, file=file):
tr_xml = ET.parse(os.path.join(TRANSLATIONS_DIR, f"{file}.ts")) tr_xml = ET.parse(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"))
@ -74,13 +80,15 @@ class TestTranslations(unittest.TestCase):
for message in context.iterfind("message"): for message in context.iterfind("message"):
if message.get("numerus") == "yes": if message.get("numerus") == "yes":
translation = message.find("translation") translation = message.find("translation")
numerusform = translation.findall("numerusform") numerusform = [t.text for t in translation.findall("numerusform")]
# Do not assert finished translations # Do not assert finished translations
if translation.get("type") == "unfinished": if translation.get("type") == "unfinished":
continue continue
self.assertNotIn(None, [x.text for x in numerusform], "Ensure all plural translation forms are completed.") self.assertNotIn(None, numerusform, "Ensure all plural translation forms are completed.")
self.assertTrue(all([re.search("%[0-9]+", t) is None for t in numerusform]),
"Plural translations must use %n, not %1, %2, etc.: {}".format(numerusform))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,11 +1,6 @@
# Multilanguage # Multilanguage
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_en.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_en.ts) [![languages](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge.svg)](#)
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_pt.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_pt.ts)
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHT.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHT.ts)
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHS.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHS.ts)
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ko.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ko.ts)
[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ja.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ja.ts)
## Contributing ## Contributing

@ -8,20 +8,17 @@ from selfdrive.ui.update_translations import LANGUAGES_FILE, TRANSLATIONS_DIR
TRANSLATION_TAG = "<translation" TRANSLATION_TAG = "<translation"
UNFINISHED_TRANSLATION_TAG = "<translation type=\"unfinished\"" UNFINISHED_TRANSLATION_TAG = "<translation type=\"unfinished\""
TRANSLATION_BADGE = "translation_badge_{}.svg" BADGE_HEIGHT = 20 + 8
TRANSLATION_LINK = "https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/{}"
if __name__ == "__main__": if __name__ == "__main__":
with open(LANGUAGES_FILE, "r") as f: with open(LANGUAGES_FILE, "r") as f:
translation_files = json.load(f) translation_files = json.load(f)
print("Copy into selfdrive/ui/translations/README.md:\n") badge_svg = [f'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="{len(translation_files) * BADGE_HEIGHT}">']
for name, file in translation_files.items(): for idx, (name, file) in enumerate(translation_files.items()):
with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"), "r") as tr_f: with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"), "r") as tr_f:
tr_file = tr_f.read() tr_file = tr_f.read()
print(f"[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/{TRANSLATION_BADGE.format(file)})]({TRANSLATION_LINK.format(file)}.ts)")
total_translations = 0 total_translations = 0
unfinished_translations = 0 unfinished_translations = 0
for line in tr_file.splitlines(): for line in tr_file.splitlines():
@ -35,6 +32,16 @@ if __name__ == "__main__":
r = requests.get(f"https://img.shields.io/badge/LANGUAGE {name}-{percent_finished}%25 complete-{color}") r = requests.get(f"https://img.shields.io/badge/LANGUAGE {name}-{percent_finished}%25 complete-{color}")
assert r.status_code == 200, "Error downloading badge" assert r.status_code == 200, "Error downloading badge"
content_svg = r.content.decode("utf-8")
# make tag ids in each badge unique
for tag in ("r", "s"):
content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"')
content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"')
badge_svg.extend([f'<g transform="translate(0, {idx * BADGE_HEIGHT})">', content_svg, "</g>"])
badge_svg.append("</svg>")
with open(os.path.join(BASEDIR, TRANSLATION_BADGE.format(file)), "wb") as badge_f: with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f:
badge_f.write(r.content) badge_f.write("\n".join(badge_svg))

@ -876,71 +876,71 @@ location set</source>
<context> <context>
<name>Sidebar</name> <name>Sidebar</name>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>CONNECT</source> <source>CONNECT</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<source>OFFLINE</source> <source>OFFLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>ONLINE</source> <source>ONLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>ERROR</source> <source>ERROR</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>TEMP</source> <source>TEMP</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<source>HIGH</source> <source>HIGH</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<source>GOOD</source> <source>GOOD</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>OK</source> <source>OK</source>
<translation>OK</translation> <translation>OK</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>VEHICLE</source> <source>VEHICLE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>NO</source> <source>NO</source>
<translation>NO</translation> <translation>NO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>PANDA</source> <source>PANDA</source>
<translation>PANDA</translation> <translation>PANDA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>GPS</source> <source>GPS</source>
<translation>GPS</translation> <translation>GPS</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>SEARCH</source> <source>SEARCH</source>
<translation></translation> <translation></translation>
</message> </message>

@ -876,71 +876,71 @@ location set</source>
<context> <context>
<name>Sidebar</name> <name>Sidebar</name>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>CONNECT</source> <source>CONNECT</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<source>OFFLINE</source> <source>OFFLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>ONLINE</source> <source>ONLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>ERROR</source> <source>ERROR</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>TEMP</source> <source>TEMP</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<source>HIGH</source> <source>HIGH</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<source>GOOD</source> <source>GOOD</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>OK</source> <source>OK</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>VEHICLE</source> <source>VEHICLE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>NO</source> <source>NO</source>
<translation>NO</translation> <translation>NO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>PANDA</source> <source>PANDA</source>
<translation>PANDA</translation> <translation>PANDA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>GPS</source> <source>GPS</source>
<translation>GPS</translation> <translation>GPS</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>SEARCH</source> <source>SEARCH</source>
<translation></translation> <translation></translation>
</message> </message>

@ -880,71 +880,71 @@ trabalho definido</translation>
<context> <context>
<name>Sidebar</name> <name>Sidebar</name>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>CONNECT</source> <source>CONNECT</source>
<translation>CONEXÃO</translation> <translation>CONEXÃO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<source>OFFLINE</source> <source>OFFLINE</source>
<translation>DESCONEC</translation> <translation>DESCONEC</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>ONLINE</source> <source>ONLINE</source>
<translation>CONECTADO</translation> <translation>CONECTADO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>ERROR</source> <source>ERROR</source>
<translation>ERRO</translation> <translation>ERRO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>TEMP</source> <source>TEMP</source>
<translation>TEMP</translation> <translation>TEMP</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<source>HIGH</source> <source>HIGH</source>
<translation>ALTA</translation> <translation>ALTA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<source>GOOD</source> <source>GOOD</source>
<translation>BOA</translation> <translation>BOA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>OK</source> <source>OK</source>
<translation>OK</translation> <translation>OK</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>VEHICLE</source> <source>VEHICLE</source>
<translation>VEÍCULO</translation> <translation>VEÍCULO</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>NO</source> <source>NO</source>
<translation>SEM</translation> <translation>SEM</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>PANDA</source> <source>PANDA</source>
<translation>PANDA</translation> <translation>PANDA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>GPS</source> <source>GPS</source>
<translation>GPS</translation> <translation>GPS</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>SEARCH</source> <source>SEARCH</source>
<translation>PROCURA</translation> <translation>PROCURA</translation>
</message> </message>

@ -874,71 +874,71 @@ location set</source>
<context> <context>
<name>Sidebar</name> <name>Sidebar</name>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>CONNECT</source> <source>CONNECT</source>
<translation>CONNECT</translation> <translation>CONNECT</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<source>OFFLINE</source> <source>OFFLINE</source>
<translation>线</translation> <translation>线</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>ONLINE</source> <source>ONLINE</source>
<translation>线</translation> <translation>线</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>ERROR</source> <source>ERROR</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>TEMP</source> <source>TEMP</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<source>HIGH</source> <source>HIGH</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<source>GOOD</source> <source>GOOD</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>OK</source> <source>OK</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>VEHICLE</source> <source>VEHICLE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>NO</source> <source>NO</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>PANDA</source> <source>PANDA</source>
<translation>PANDA</translation> <translation>PANDA</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>GPS</source> <source>GPS</source>
<translation>GPS</translation> <translation>GPS</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>SEARCH</source> <source>SEARCH</source>
<translation></translation> <translation></translation>
</message> </message>

@ -876,71 +876,71 @@ location set</source>
<context> <context>
<name>Sidebar</name> <name>Sidebar</name>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>CONNECT</source> <source>CONNECT</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="67"/> <location filename="../qt/sidebar.cc" line="74"/>
<source>OFFLINE</source> <source>OFFLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>ONLINE</source> <source>ONLINE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="69"/> <location filename="../qt/sidebar.cc" line="76"/>
<source>ERROR</source> <source>ERROR</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>TEMP</source> <source>TEMP</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="73"/> <location filename="../qt/sidebar.cc" line="80"/>
<source>HIGH</source> <source>HIGH</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="76"/> <location filename="../qt/sidebar.cc" line="83"/>
<source>GOOD</source> <source>GOOD</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="78"/> <location filename="../qt/sidebar.cc" line="85"/>
<source>OK</source> <source>OK</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="82"/> <location filename="../qt/sidebar.cc" line="89"/>
<source>VEHICLE</source> <source>VEHICLE</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>NO</source> <source>NO</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="84"/> <location filename="../qt/sidebar.cc" line="91"/>
<source>PANDA</source> <source>PANDA</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>GPS</source> <source>GPS</source>
<translation>GPS</translation> <translation>GPS</translation>
</message> </message>
<message> <message>
<location filename="../qt/sidebar.cc" line="86"/> <location filename="../qt/sidebar.cc" line="93"/>
<source>SEARCH</source> <source>SEARCH</source>
<translation></translation> <translation></translation>
</message> </message>

@ -41,7 +41,7 @@ class Joystick:
def __init__(self): def __init__(self):
# TODO: find a way to get this from API, perhaps "inputs" doesn't support it # TODO: find a way to get this from API, perhaps "inputs" doesn't support it
self.min_axis_value = {'ABS_Y': 0., 'ABS_RZ': 0.} self.min_axis_value = {'ABS_Y': 0., 'ABS_RZ': 0.}
self.max_axis_value = {'ABS_Y': 1023., 'ABS_RZ': 255.} self.max_axis_value = {'ABS_Y': 255., 'ABS_RZ': 255.}
self.cancel_button = 'BTN_TRIGGER' self.cancel_button = 'BTN_TRIGGER'
self.axes_values = {'ABS_Y': 0., 'ABS_RZ': 0.} # gb, steer self.axes_values = {'ABS_Y': 0., 'ABS_RZ': 0.} # gb, steer
self.axes_order = ['ABS_Y', 'ABS_RZ'] self.axes_order = ['ABS_Y', 'ABS_RZ']

@ -18,6 +18,7 @@ const std::initializer_list<std::pair<std::string, std::string>> keyboard_shortc
{"space", "Pause/Resume"}, {"space", "Pause/Resume"},
{"e", "Next Engagement"}, {"e", "Next Engagement"},
{"d", "Next Disengagement"}, {"d", "Next Disengagement"},
{"t", "Next User Tag"}
}, },
{ {
{"enter", "Enter seek request"}, {"enter", "Enter seek request"},
@ -32,6 +33,7 @@ enum Color {
Yellow, Yellow,
Green, Green,
Red, Red,
Cyan,
BrightWhite, BrightWhite,
Engaged, Engaged,
Disengaged, Disengaged,
@ -70,6 +72,7 @@ ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"car
init_pair(Color::Debug, 246, COLOR_BLACK); // #949494 init_pair(Color::Debug, 246, COLOR_BLACK); // #949494
init_pair(Color::Yellow, 184, COLOR_BLACK); init_pair(Color::Yellow, 184, COLOR_BLACK);
init_pair(Color::Red, COLOR_RED, COLOR_BLACK); init_pair(Color::Red, COLOR_RED, COLOR_BLACK);
init_pair(Color::Cyan, COLOR_CYAN, COLOR_BLACK);
init_pair(Color::BrightWhite, 15, COLOR_BLACK); init_pair(Color::BrightWhite, 15, COLOR_BLACK);
init_pair(Color::Disengaged, COLOR_BLUE, COLOR_BLUE); init_pair(Color::Disengaged, COLOR_BLUE, COLOR_BLUE);
init_pair(Color::Engaged, 28, 28); init_pair(Color::Engaged, 28, 28);
@ -205,6 +208,7 @@ void ConsoleUI::displayTimelineDesc() {
{Color::Green, " Info ", true}, {Color::Green, " Info ", true},
{Color::Yellow, " Warning ", true}, {Color::Yellow, " Warning ", true},
{Color::Red, " Critical ", true}, {Color::Red, " Critical ", true},
{Color::Cyan, " User Tag ", true},
}; };
for (auto [color, name, bold] : indicators) { for (auto [color, name, bold] : indicators) {
add_str(w[Win::TimelineDesc], "__", color, bold); add_str(w[Win::TimelineDesc], "__", color, bold);
@ -263,6 +267,8 @@ void ConsoleUI::updateTimeline() {
if (type == TimelineType::Engaged) { if (type == TimelineType::Engaged) {
mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
} else if (type == TimelineType::UserFlag) {
mvwchgat(win, 3, start_pos, end_pos - start_pos + 1, ACS_S3, Color::Cyan, NULL);
} else { } else {
auto color_id = Color::Green; auto color_id = Color::Green;
if (type != TimelineType::AlertInfo) { if (type != TimelineType::AlertInfo) {
@ -336,6 +342,8 @@ void ConsoleUI::handleKey(char c) {
replay->seekToFlag(FindFlag::nextEngagement); replay->seekToFlag(FindFlag::nextEngagement);
} else if (c == 'd') { } else if (c == 'd') {
replay->seekToFlag(FindFlag::nextDisEngagement); replay->seekToFlag(FindFlag::nextDisEngagement);
} else if (c == 't') {
replay->seekToFlag(FindFlag::nextUserFlag);
} else if (c == 'm') { } else if (c == 'm') {
replay->seekTo(+60, true); replay->seekTo(+60, true);
} else if (c == 'M') { } else if (c == 'M') {

@ -149,6 +149,9 @@ void Replay::buildTimeline() {
timeline.push_back({toSeconds(alert_begin), toSeconds(e->mono_time), alert_type}); timeline.push_back({toSeconds(alert_begin), toSeconds(e->mono_time), alert_type});
alert_begin = 0; alert_begin = 0;
} }
} else if (e->which == cereal::Event::Which::USER_FLAG) {
std::lock_guard lk(timeline_lock);
timeline.push_back({toSeconds(e->mono_time), toSeconds(e->mono_time), TimelineType::UserFlag});
} }
} }
} }
@ -163,6 +166,10 @@ std::optional<uint64_t> Replay::find(FindFlag flag) {
} else if (flag == FindFlag::nextDisEngagement && end_ts > cur_ts) { } else if (flag == FindFlag::nextDisEngagement && end_ts > cur_ts) {
return end_ts; return end_ts;
} }
} else if (type == TimelineType::UserFlag) {
if (flag == FindFlag::nextUserFlag && start_ts > cur_ts) {
return start_ts;
}
} }
} }
return std::nullopt; return std::nullopt;

@ -24,10 +24,11 @@ enum REPLAY_FLAGS {
enum class FindFlag { enum class FindFlag {
nextEngagement, nextEngagement,
nextDisEngagement nextDisEngagement,
nextUserFlag,
}; };
enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical }; enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag };
class Replay : public QObject { class Replay : public QObject {
Q_OBJECT Q_OBJECT

@ -179,7 +179,7 @@ def gps_callback(gps, vehicle_state):
] ]
dat.gpsLocationExternal = { dat.gpsLocationExternal = {
"timestamp": int(time.time() * 1000), "unixTimestampMillis": int(time.time() * 1000),
"flags": 1, # valid fix "flags": 1, # valid fix
"accuracy": 1.0, "accuracy": 1.0,
"verticalAccuracy": 1.0, "verticalAccuracy": 1.0,

@ -4,11 +4,26 @@ set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR cd $DIR
RC_FILE="${HOME}/.$(basename ${SHELL})rc"
if [ "$(uname)" == "Darwin" ] && [ $SHELL == "/bin/bash" ]; then
RC_FILE="$HOME/.bash_profile"
fi
if ! command -v "pyenv" > /dev/null 2>&1; then if ! command -v "pyenv" > /dev/null 2>&1; then
echo "pyenv install ..." echo "pyenv install ..."
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
export PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
echo -e "\n. ~/.pyenvrc" >> $RC_FILE
cat <<EOF > "${HOME}/.pyenvrc"
if [ -z "\$PYENV_ROOT" ]; then
export PATH=\$HOME/.pyenv/bin:\$HOME/.pyenv/shims:\$PATH
export PYENV_ROOT="\$HOME/.pyenv"
eval "\$(pyenv init -)"
eval "\$(pyenv virtualenv-init -)"
fi
EOF
fi fi
source $RC_FILE
export MAKEFLAGS="-j$(nproc)" export MAKEFLAGS="-j$(nproc)"

Loading…
Cancel
Save