Merge remote-tracking branch 'upstream/master' into take-steering

pull/26010/head
Shane Smiskol 3 years ago
commit cb81650843
  1. 8
      .github/workflows/prebuilt.yaml
  2. 114
      .github/workflows/selfdrive_tests.yaml
  3. 8
      .github/workflows/setup/action.yaml
  4. 50
      .github/workflows/tools_tests.yaml
  5. 1
      .pre-commit-config.yaml
  6. 10
      SConstruct
  7. 2
      cereal
  8. 10
      docs/CARS.md
  9. 2
      opendbc
  10. 2
      panda
  11. 4
      release/build_devel.sh
  12. 68
      selfdrive/athena/tests/test_athenad.py
  13. 6
      selfdrive/boardd/boardd.cc
  14. 4
      selfdrive/boardd/panda.cc
  15. 1
      selfdrive/boardd/panda.h
  16. 15
      selfdrive/car/__init__.py
  17. 8
      selfdrive/car/car_helpers.py
  18. 9
      selfdrive/car/chrysler/values.py
  19. 50
      selfdrive/car/ford/carcontroller.py
  20. 8
      selfdrive/car/ford/carstate.py
  21. 29
      selfdrive/car/ford/fordcan.py
  22. 9
      selfdrive/car/ford/interface.py
  23. 29
      selfdrive/car/ford/values.py
  24. 4
      selfdrive/car/fw_query_definitions.py
  25. 19
      selfdrive/car/fw_versions.py
  26. 17
      selfdrive/car/gm/carcontroller.py
  27. 41
      selfdrive/car/gm/carstate.py
  28. 8
      selfdrive/car/gm/interface.py
  29. 126
      selfdrive/car/hyundai/carcontroller.py
  30. 66
      selfdrive/car/hyundai/carstate.py
  31. 11
      selfdrive/car/hyundai/hyundaican.py
  32. 118
      selfdrive/car/hyundai/hyundaicanfd.py
  33. 85
      selfdrive/car/hyundai/interface.py
  34. 29
      selfdrive/car/hyundai/tests/test_hyundai.py
  35. 104
      selfdrive/car/hyundai/values.py
  36. 14
      selfdrive/car/interfaces.py
  37. 5
      selfdrive/car/subaru/carstate.py
  38. 8
      selfdrive/car/tests/routes.py
  39. 7
      selfdrive/car/tests/test_fw_fingerprint.py
  40. 2
      selfdrive/car/tests/test_models.py
  41. 1
      selfdrive/car/torque_data/override.yaml
  42. 4
      selfdrive/car/torque_data/substitute.yaml
  43. 2
      selfdrive/car/toyota/values.py
  44. 2
      selfdrive/car/volkswagen/carcontroller.py
  45. 9
      selfdrive/car/volkswagen/carstate.py
  46. 11
      selfdrive/car/volkswagen/interface.py
  47. 2
      selfdrive/car/volkswagen/pqcan.py
  48. 20
      selfdrive/car/volkswagen/values.py
  49. 31
      selfdrive/controls/controlsd.py
  50. 4
      selfdrive/controls/lib/events.py
  51. 14
      selfdrive/controls/lib/latcontrol_torque.py
  52. 28
      selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
  53. 10
      selfdrive/controls/lib/lateral_planner.py
  54. 6
      selfdrive/controls/tests/test_lateral_mpc.py
  55. 3
      selfdrive/controls/tests/test_startup.py
  56. 2
      selfdrive/debug/check_timings.py
  57. 28
      selfdrive/locationd/locationd.cc
  58. 2
      selfdrive/locationd/locationd.h
  59. 2
      selfdrive/locationd/models/live_kf.cc
  60. 1
      selfdrive/locationd/test/test_locationd.py
  61. 2
      selfdrive/loggerd/loggerd.cc
  62. 4
      selfdrive/modeld/models/dmonitoring_model.current
  63. 4
      selfdrive/modeld/models/dmonitoring_model.onnx
  64. 4
      selfdrive/modeld/models/dmonitoring_model_q.dlc
  65. 4
      selfdrive/modeld/models/supercombo.onnx
  66. 0
      selfdrive/modeld/tests/__init__.py
  67. 0
      selfdrive/modeld/tests/dmon_lag/repro.cc
  68. 0
      selfdrive/modeld/tests/snpe_benchmark/.gitignore
  69. 1
      selfdrive/modeld/tests/snpe_benchmark/benchmark.cc
  70. 0
      selfdrive/modeld/tests/snpe_benchmark/benchmark.sh
  71. 0
      selfdrive/modeld/tests/test_modeld.py
  72. 0
      selfdrive/modeld/tests/tf_test/build.sh
  73. 0
      selfdrive/modeld/tests/tf_test/main.cc
  74. 0
      selfdrive/modeld/tests/tf_test/pb_loader.py
  75. 0
      selfdrive/modeld/tests/timing/benchmark.py
  76. 21
      selfdrive/monitoring/driver_monitor.py
  77. 2
      selfdrive/navd/SConscript
  78. 27
      selfdrive/navd/map_renderer.cc
  79. 1
      selfdrive/navd/map_renderer.h
  80. 3
      selfdrive/navd/map_renderer.py
  81. 1
      selfdrive/navd/style.json
  82. 99
      selfdrive/sensord/pigeond.py
  83. 4
      selfdrive/sensord/rawgps/rawgpsd.py
  84. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  85. 2
      selfdrive/test/process_replay/ref_commit
  86. 7
      selfdrive/test/process_replay/regen.py
  87. 2
      selfdrive/test/process_replay/test_processes.py
  88. 23
      selfdrive/test/test_onroad.py
  89. 20
      selfdrive/test/update_ci_routes.py
  90. 4
      selfdrive/ui/qt/offroad/driverview.cc
  91. 2
      selfdrive/ui/qt/offroad/driverview.h
  92. 2
      selfdrive/ui/qt/offroad/wifiManager.cc
  93. 55
      selfdrive/ui/qt/onroad.cc
  94. 7
      selfdrive/ui/qt/onroad.h
  95. 26
      selfdrive/ui/qt/widgets/cameraview.cc
  96. 6
      selfdrive/ui/qt/widgets/cameraview.h
  97. 2
      selfdrive/ui/translations/main_ar.ts
  98. 46
      selfdrive/ui/translations/main_ja.ts
  99. 50
      selfdrive/ui/translations/main_ko.ts
  100. 2
      selfdrive/ui/translations/main_nl.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,7 +2,6 @@ name: prebuilt
on:
schedule:
- cron: '0 * * * *'
workflow_dispatch:
env:
@ -11,9 +10,7 @@ env:
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
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 .
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
jobs:
build_prebuilt:
@ -37,8 +34,7 @@ jobs:
- name: Build Docker image
run: |
eval "$BUILD"
docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true
docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f Dockerfile.openpilot .
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f Dockerfile.openpilot .
- name: Push to container registry
run: |
$DOCKER_LOGIN

@ -1,10 +1,15 @@
name: selfdrive
on:
push:
branches-ignore:
- 'testing-closet*'
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }}
cancel-in-progress: true
env:
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
@ -13,15 +18,12 @@ env:
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
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 .
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: |
docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN_CL: docker run --shm-size 1G -v $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 $CL_BASE_IMAGE /bin/sh -c
UNIT_TEST: coverage run --append -m unittest discover
@ -36,13 +38,10 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- name: Build devel
run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh
- uses: ./.github/workflows/setup
with:
save-cache: true
- name: Check submodules
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: release/check-submodules.sh
@ -67,18 +66,20 @@ jobs:
build_all:
name: build all
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
with:
save-cache: true
- name: Build openpilot with all flags
run: ${{ env.RUN }} "scons -j$(nproc) --extras && release/check-dirty.sh"
- name: Cleanup scons cache
run: |
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --extras --cache-populate"
scons -j$(nproc) --cache-populate"
#build_mac:
# name: build macos
@ -140,7 +141,7 @@ jobs:
docker_push:
name: docker push
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 22
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
needs: static_analysis # hack to ensure slow tests run first since this and static_analysis are fast
steps:
@ -149,12 +150,14 @@ jobs:
submodules: true
- name: Build Docker image
run: eval "$BUILD"
timeout-minutes: 13
- name: Push to container registry
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
- name: Build CL Docker image
run: eval "$BUILD_CL"
timeout-minutes: 4
- name: Push to container registry
run: |
$DOCKER_LOGIN
@ -163,7 +166,7 @@ jobs:
static_analysis:
name: static analysis
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
with:
@ -171,21 +174,23 @@ jobs:
- name: Build Docker image
run: eval "$BUILD"
- name: pre-commit
timeout-minutes: 5
run: ${{ env.RUN }} "pre-commit run --all"
valgrind:
name: valgrind
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Run valgrind
run: |
${{ env.RUN }} "scons -j$(nproc) && \
python selfdrive/test/test_valgrind_replay.py"
${{ env.RUN }} "python selfdrive/test/test_valgrind_replay.py"
- name: Print logs
if: always()
run: cat selfdrive/test/valgrind_logs.txt
@ -193,16 +198,18 @@ jobs:
unit_tests:
name: unit tests
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Run unit tests
timeout-minutes: 15
run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
scons -j$(nproc) && \
$UNIT_TEST common && \
$UNIT_TEST opendbc/can && \
$UNIT_TEST selfdrive/boardd && \
@ -215,7 +222,6 @@ jobs:
$UNIT_TEST selfdrive/athena && \
$UNIT_TEST selfdrive/thermald && \
$UNIT_TEST system/hardware/tici && \
$UNIT_TEST selfdrive/modeld && \
$UNIT_TEST tools/lib/tests && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
@ -234,7 +240,7 @@ jobs:
process_replay:
name: process replay
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 25
steps:
- uses: actions/checkout@v3
with:
@ -246,10 +252,12 @@ jobs:
with:
path: /tmp/comma_download_cache
key: proc-replay-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/ref_commit') }}
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run replay
run: |
${{ env.RUN }} "scons -j$(nproc) && \
CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
coverage xml"
- name: Print diff
if: always()
@ -263,15 +271,14 @@ jobs:
- name: Upload reference logs
if: ${{ failure() && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
run: |
${{ env.RUN }} "scons -j$(nproc) && \
CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
model_replay_onnx:
name: model replay onnx
test_modeld:
name: model tests
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
with:
@ -280,29 +287,41 @@ jobs:
- name: Build Docker image
# Sim docker is needed to get the OpenCL drivers
run: eval "$BUILD_CL"
- name: Run replay
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run model replay with ONNX
timeout-minutes: 2
run: |
${{ env.RUN_CL }} "ONNXCPU=1 CI=1 coverage run selfdrive/test/process_replay/model_replay.py && \
coverage xml"
- name: Run unit tests
timeout-minutes: 5
run: |
${{ env.RUN_CL }} "scons -j$(nproc) && \
ONNXCPU=1 CI=1 coverage run \
selfdrive/test/process_replay/model_replay.py -j$(nproc) && \
${{ env.RUN_CL }} "$UNIT_TEST selfdrive/modeld && \
coverage xml"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
test_longitudinal:
name: longitudinal
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: ./.github/workflows/setup
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Test longitudinal
run: |
${{ env.RUN }} "mkdir -p selfdrive/test/out && \
scons -j$(nproc) && \
cd selfdrive/test/longitudinal_maneuvers && \
coverage run ./test_longitudinal.py && \
coverage xml"
timeout-minutes: 2
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
- uses: actions/upload-artifact@v2
@ -315,7 +334,7 @@ jobs:
test_cars:
name: cars
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
@ -331,10 +350,12 @@ jobs:
with:
path: /tmp/comma_download_cache
key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }}
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Test car models
timeout-minutes: 12
run: |
${{ env.RUN }} "scons -j$(nproc) && \
coverage run -m pytest selfdrive/car/tests/test_models.py && \
${{ env.RUN }} "coverage run -m pytest selfdrive/car/tests/test_models.py && \
coverage xml && \
chmod -R 777 /tmp/comma_download_cache"
env:
@ -343,29 +364,10 @@ jobs:
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
docs:
name: build docs
runs-on: ubuntu-20.04
timeout-minutes: 50
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build docker container
run: |
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker pull $DOCKER_REGISTRY/openpilot-docs:latest || true
DOCKER_BUILDKIT=1 docker build --cache-from $DOCKER_REGISTRY/openpilot-docs:latest -t $DOCKER_REGISTRY/openpilot-docs:latest -f docs/docker/Dockerfile .
- name: Push docker container
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/openpilot-docs:latest
car_docs_diff:
name: comment on PR with car docs diff
name: PR comments
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 20
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v3

@ -15,9 +15,9 @@ runs:
# build cache
- id: date
shell: bash
run: echo "::set-output name=date::$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d')"
run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
- shell: bash
run: echo "${{ steps.date.outputs.date }}"
run: echo "$CACHE_COMMIT_DATE"
- shell: bash
run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV
if: github.ref != 'refs/heads/master' || inputs.save-cache == 'false'
@ -27,9 +27,9 @@ runs:
uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b
with:
path: /tmp/scons_cache
key: scons-${{ steps.date.outputs.date }}-${{ github.sha }}
key: scons-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
restore-keys: |
scons-${{ steps.date.outputs.date }}-
scons-${{ env.CACHE_COMMIT_DATE }}-
scons-
# build our docker image

@ -1,8 +1,15 @@
name: tools
on:
push:
branches-ignore:
- 'testing-closet*'
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }}
cancel-in-progress: true
env:
BASE_IMAGE: openpilot-base
CL_BASE_IMAGE: openpilot-base-cl
@ -10,21 +17,20 @@ env:
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
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 .
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base .
RUN: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
BUILD_CL: |
docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base_cl) || true
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e \
GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN_CL: docker run --shm-size 1G -v $GITHUB_WORKSPACE:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
jobs:
plotjuggler:
name: plotjuggler
runs-on: ubuntu-20.04
timeout-minutes: 30
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
with:
@ -32,6 +38,7 @@ jobs:
- name: Build Docker image
run: eval "$BUILD"
- name: Unit test
timeout-minutes: 2
run: |
${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \
apt-get update && \
@ -42,7 +49,7 @@ jobs:
simulator:
name: simulator
runs-on: ubuntu-20.04
timeout-minutes: 50
timeout-minutes: 30
env:
IMAGE_NAME: openpilot-sim
if: github.repository == 'commaai/openpilot'
@ -56,12 +63,29 @@ jobs:
run: eval "$BUILD"
- name: Build base cl image
run: eval "$BUILD_CL"
- name: Pull latest simulator image
run: docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true
- name: Build simulator image
run: docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/sim/Dockerfile.sim .
run: DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/sim/Dockerfile.sim .
- name: Push to container registry
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
docs:
name: build docs
runs-on: ubuntu-20.04
timeout-minutes: 25
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build docker container
run: |
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/openpilot-docs:latest -t $DOCKER_REGISTRY/openpilot-docs:latest -f docs/docker/Dockerfile .
- name: Push docker container
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/openpilot-docs:latest

@ -53,6 +53,7 @@ repos:
types: [python]
exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
args:
- -j0
- -rn
- -sn
- --rcfile=.pylintrc

@ -327,6 +327,7 @@ qt_flags = [
qt_env['CXXFLAGS'] += qt_flags
qt_env['LIBPATH'] += ['#selfdrive/ui']
qt_env['LIBS'] = qt_libs
qt_env['QT_MOCHPREFIX'] = cache_dir + '/moc_files/moc_'
if GetOption("clazy"):
checks = [
@ -431,11 +432,12 @@ SConscript(['selfdrive/sensord/SConscript'])
SConscript(['selfdrive/ui/SConscript'])
SConscript(['selfdrive/navd/SConscript'])
SConscript(['tools/replay/SConscript'])
if arch in ['x86_64', 'Darwin'] or GetOption('extras'):
SConscript(['tools/replay/SConscript'])
opendbc = abspath([File('opendbc/can/libdbc.so')])
Export('opendbc')
SConscript(['tools/cabana/SConscript'])
opendbc = abspath([File('opendbc/can/libdbc.so')])
Export('opendbc')
SConscript(['tools/cabana/SConscript'])
if GetOption('test'):
SConscript('panda/tests/safety/SConscript')

@ -1 +1 @@
Subproject commit b29717c4c328d5cf34d46f682f25267150f82637
Subproject commit 107048c83ec2f488286a1be314e7aece0a20a6b1

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system.
# 206 Supported Cars
# 208 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|
@ -55,9 +55,10 @@ A supported vehicle is one that just works when you install a comma three. All s
|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Elantra Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|
|Hyundai|Ioniq 5 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|
|Hyundai|Ioniq 5 (with HDA II) 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|
|Hyundai|Ioniq 5 (without HDA II) 2022|Highway Driving Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
@ -83,7 +84,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Kia|EV6 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|
|Kia|EV6 (with HDA II) 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|
|Kia|EV6 (without HDA II) 2022|Highway Driving Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Kia|Niro EV 2019|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|

@ -1 +1 @@
Subproject commit c2de79ef6e34ef2ebfd707d3428680ccd4ff9c34
Subproject commit 8f245e6ef5e25814d8e6e1f096221f6dfeefe86b

@ -1 +1 @@
Subproject commit 94a6fc6b41351587ca54a9bfda4ccfc371210d4a
Subproject commit 56adec981da48631538cee975048e3573f95e379

@ -22,8 +22,8 @@ pre-commit uninstall || true
echo "[-] bringing master-ci and devel in sync T=$SECONDS"
cd $TARGET_DIR
git fetch origin master-ci
git fetch origin devel
git fetch --depth 1 origin master-ci
git fetch --depth 1 origin devel
git checkout -f --track origin/master-ci
git reset --hard master-ci

@ -9,6 +9,7 @@ import threading
import queue
import unittest
from datetime import datetime, timedelta
from typing import Optional
from multiprocessing import Process
from pathlib import Path
@ -46,12 +47,26 @@ class TestAthenadMethods(unittest.TestCase):
else:
os.unlink(p)
def wait_for_upload(self):
# *** test helpers ***
@staticmethod
def _wait_for_upload():
now = time.time()
while time.time() - now < 5:
if athenad.upload_queue.qsize() == 0:
break
@staticmethod
def _create_file(file: str, parent: Optional[str] = None) -> str:
fn = os.path.join(athenad.ROOT if parent is None else parent, file)
os.makedirs(os.path.dirname(fn), exist_ok=True)
Path(fn).touch()
return fn
# *** test cases ***
def test_echo(self):
assert dispatcher["echo"]("bob") == "bob"
@ -85,9 +100,7 @@ class TestAthenadMethods(unittest.TestCase):
filenames = ['qlog', 'qcamera.ts', 'rlog', 'fcamera.hevc', 'ecamera.hevc', 'dcamera.hevc']
files = [f'{route}--{s}/{f}' for s in segments for f in filenames]
for file in files:
fn = os.path.join(athenad.ROOT, file)
os.makedirs(os.path.dirname(fn), exist_ok=True)
Path(fn).touch()
self._create_file(file)
resp = dispatcher["listDataDirectory"]()
self.assertTrue(resp, 'list empty!')
@ -121,16 +134,14 @@ class TestAthenadMethods(unittest.TestCase):
self.assertCountEqual(resp, expected)
def test_strip_bz2_extension(self):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
if fn.endswith('.bz2'):
self.assertEqual(athenad.strip_bz2_extension(fn), fn[:-4])
@with_http_server
def test_do_upload(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url="http://localhost:1238", headers={}, created_at=int(time.time()*1000), id='')
with self.assertRaises(requests.exceptions.ConnectionError):
@ -142,8 +153,7 @@ class TestAthenadMethods(unittest.TestCase):
@with_http_server
def test_uploadFileToUrl(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
resp = dispatcher["uploadFileToUrl"]("qlog.bz2", f"{host}/qlog.bz2", {})
self.assertEqual(resp['enqueued'], 1)
@ -154,8 +164,7 @@ class TestAthenadMethods(unittest.TestCase):
@with_http_server
def test_uploadFileToUrl_duplicate(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
self._create_file('qlog.bz2')
url1 = f"{host}/qlog.bz2?sig=sig1"
dispatcher["uploadFileToUrl"]("qlog.bz2", url1, {})
@ -172,8 +181,7 @@ class TestAthenadMethods(unittest.TestCase):
@with_http_server
def test_upload_handler(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
end_event = threading.Event()
@ -182,7 +190,7 @@ class TestAthenadMethods(unittest.TestCase):
athenad.upload_queue.put_nowait(item)
try:
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
# TODO: verify that upload actually succeeded
@ -195,8 +203,7 @@ class TestAthenadMethods(unittest.TestCase):
def test_upload_handler_retry(self, host, mock_put):
for status, retry in ((500, True), (412, False)):
mock_put.return_value.status_code = status
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
end_event = threading.Event()
@ -205,7 +212,7 @@ class TestAthenadMethods(unittest.TestCase):
athenad.upload_queue.put_nowait(item)
try:
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
self.assertEqual(athenad.upload_queue.qsize(), 1 if retry else 0)
@ -217,8 +224,7 @@ class TestAthenadMethods(unittest.TestCase):
def test_upload_handler_timeout(self):
"""When an upload times out or fails to connect it should be placed back in the queue"""
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
item_no_retry = item._replace(retry_count=MAX_RETRY_COUNT)
@ -228,14 +234,14 @@ class TestAthenadMethods(unittest.TestCase):
try:
athenad.upload_queue.put_nowait(item_no_retry)
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
# Check that upload with retry count exceeded is not put back
self.assertEqual(athenad.upload_queue.qsize(), 0)
athenad.upload_queue.put_nowait(item)
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
# Check that upload item was put back in the queue with incremented retry count
@ -256,7 +262,7 @@ class TestAthenadMethods(unittest.TestCase):
thread = threading.Thread(target=athenad.upload_handler, args=(end_event,))
thread.start()
try:
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
self.assertEqual(athenad.upload_queue.qsize(), 0)
@ -269,8 +275,7 @@ class TestAthenadMethods(unittest.TestCase):
ts = int(t_future.strftime("%s")) * 1000
# Item that would time out if actually uploaded
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url="http://localhost:44444/qlog.bz2", headers={}, created_at=ts, id='', allow_cellular=True)
@ -279,7 +284,7 @@ class TestAthenadMethods(unittest.TestCase):
thread.start()
try:
athenad.upload_queue.put_nowait(item)
self.wait_for_upload()
self._wait_for_upload()
time.sleep(0.1)
self.assertEqual(athenad.upload_queue.qsize(), 0)
@ -292,8 +297,7 @@ class TestAthenadMethods(unittest.TestCase):
@with_http_server
def test_listUploadQueueCurrent(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
fn = self._create_file('qlog.bz2')
item = athenad.UploadItem(path=fn, url=f"{host}/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='', allow_cellular=True)
end_event = threading.Event()
@ -302,7 +306,7 @@ class TestAthenadMethods(unittest.TestCase):
try:
athenad.upload_queue.put_nowait(item)
self.wait_for_upload()
self._wait_for_upload()
items = dispatcher["listUploadQueue"]()
self.assertEqual(len(items), 1)
@ -405,9 +409,9 @@ class TestAthenadMethods(unittest.TestCase):
def test_get_logs_to_send_sorted(self):
fl = list()
for i in range(10):
fn = os.path.join(swaglog.SWAGLOG_DIR, f'swaglog.{i:010}')
Path(fn).touch()
fl.append(os.path.basename(fn))
file = f'swaglog.{i:010}'
self._create_file(file, athenad.SWAGLOG_DIR)
fl.append(file)
# ensure the list is all logs except most recent
sl = athenad.get_logs_to_send_sorted()

@ -113,8 +113,9 @@ bool safety_setter_thread(std::vector<Panda *> pandas) {
}
// set to ELM327 for fingerprinting
for (Panda *panda : pandas) {
panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327);
for (int i = 0; i < pandas.size(); i++) {
const uint16_t safety_param = (i > 0) ? 1U : 0U;
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
}
Params p = Params();
@ -407,6 +408,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
cs[j].setCanDataSpeed(can_health.can_data_speed);
cs[j].setCanfdEnabled(can_health.canfd_enabled);
cs[j].setBrsEnabled(can_health.brs_enabled);
cs[j].setCanfdNonIso(can_health.canfd_non_iso);
}
// Convert faults bitset to capnp list

@ -360,6 +360,10 @@ void Panda::set_data_speed_kbps(uint16_t bus, uint16_t speed) {
usb_write(0xf9, bus, (speed * 10));
}
void Panda::set_canfd_non_iso(uint16_t bus, bool non_iso) {
usb_write(0xfc, bus, non_iso);
}
static uint8_t len_to_dlc(uint8_t len) {
if (len <= 8) {
return len;

@ -91,6 +91,7 @@ class Panda {
void send_heartbeat(bool engaged);
void set_can_speed_kbps(uint16_t bus, uint16_t speed);
void set_data_speed_kbps(uint16_t bus, uint16_t speed);
void set_canfd_non_iso(uint16_t bus, bool non_iso);
void can_send(capnp::List<cereal::CanData>::Reader can_data_list);
bool can_receive(std::vector<can_frame>& out_vec);

@ -3,7 +3,7 @@ import capnp
from cereal import car
from common.numpy_fast import clip
from typing import Dict, List
from typing import Dict
# kg of standard extra cargo to count for drive, gas, etc...
STD_CARGO_KG = 136.
@ -32,19 +32,6 @@ def create_button_event(cur_but: int, prev_but: int, buttons_dict: Dict[int, cap
return be
def create_button_enable_events(buttonEvents: capnp.lib.capnp._DynamicListBuilder, pcm_cruise: bool = False) -> List[int]:
events = []
for b in buttonEvents:
# do enable on both accel and decel buttons
if not pcm_cruise:
if b.type in (ButtonType.accelCruise, ButtonType.decelCruise) and not b.pressed:
events.append(EventName.buttonEnable)
# do disable on button down
if b.type == ButtonType.cancel and b.pressed:
events.append(EventName.buttonCancel)
return events
def gen_empty_fingerprint():
return {i: {} for i in range(0, 8)}

@ -76,7 +76,7 @@ interfaces = load_interfaces(interface_names)
# **** for use live only ****
def fingerprint(logcan, sendcan):
def fingerprint(logcan, sendcan, num_pandas):
fixed_fingerprint = os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
ecu_rx_addrs = set()
@ -100,7 +100,7 @@ def fingerprint(logcan, sendcan):
cloudlog.warning("Getting VIN & FW versions")
vin_rx_addr, vin = get_vin(logcan, sendcan, bus)
ecu_rx_addrs = get_present_ecus(logcan, sendcan)
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs)
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
cached = False
exact_fw_match, fw_candidates = match_fw_to_car(car_fw)
@ -173,8 +173,8 @@ def fingerprint(logcan, sendcan):
return car_fingerprint, finger, vin, car_fw, source, exact_match
def get_car(logcan, sendcan):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan)
def get_car(logcan, sendcan, num_pandas=1):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
if candidate is None:
cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints)

@ -166,14 +166,13 @@ FW_QUERY_CONFIG = FwQueryConfig(
bus=0,
),
],
extra_ecus=[
(Ecu.hcp, 0x7e2, None), # manages transmission on hybrids
(Ecu.abs, 0x7e4, None), # alt address for abs on hybrids
],
)
FW_VERSIONS = {
CAR.PACIFICA_2019_HYBRID: {
(Ecu.hcp, 0x7e2, None): [],
(Ecu.abs, 0x7e4, None): [],
},
CAR.RAM_1500: {
(Ecu.combinationMeter, 0x742, None): [
b'68294063AH',

@ -3,7 +3,7 @@ from cereal import car
from common.numpy_fast import clip, interp
from opendbc.can.packer import CANPacker
from selfdrive.car.ford import fordcan
from selfdrive.car.ford.values import CarControllerParams
from selfdrive.car.ford.values import CANBUS, CarControllerParams
VisualAlert = car.CarControl.HUDControl.VisualAlert
@ -16,9 +16,9 @@ def apply_ford_steer_angle_limits(apply_angle, apply_angle_last, vEgo):
apply_angle = clip(apply_angle, (apply_angle_last - max_angle_diff), (apply_angle_last + max_angle_diff))
# absolute limit (LatCtlPath_An_Actl)
apply_path_angle = math.radians(apply_angle) / CarControllerParams.STEER_RATIO
apply_path_angle = clip(apply_path_angle, -0.4995, 0.5240)
apply_angle = math.degrees(apply_path_angle) * CarControllerParams.STEER_RATIO
apply_path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO
apply_path_angle = clip(apply_path_angle, -0.5, 0.5235)
apply_angle = math.degrees(apply_path_angle) * CarControllerParams.LKAS_STEER_RATIO
return apply_angle
@ -47,40 +47,46 @@ class CarController:
### acc buttons ###
if CC.cruiseControl.cancel:
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True))
elif CC.cruiseControl.resume:
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True, bus=CANBUS.main))
elif CC.cruiseControl.resume and (self.frame % CarControllerParams.BUTTONS_STEP) == 0:
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True))
# if stock lane centering is active or in standby, toggle it off
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True, bus=CANBUS.main))
# if stock lane centering isn't off, send a button press to toggle it off
# the stock system checks for steering pressed, and eventually disengages cruise control
if (self.frame % 200) == 0 and CS.acc_tja_status_stock_values["Tja_D_Stat"] != 0:
elif CS.acc_tja_status_stock_values["Tja_D_Stat"] != 0 and (self.frame % CarControllerParams.ACC_UI_STEP) == 0:
can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, tja_toggle=True))
### lateral control ###
if CC.latActive:
lca_rq = 1
apply_angle = apply_ford_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgo)
else:
apply_angle = CS.out.steeringAngleDeg
lca_rq = 0
apply_angle = 0.
# send steering commands at 20Hz
if (self.frame % CarControllerParams.LKAS_STEER_STEP) == 0:
lca_rq = 1 if CC.latActive else 0
# use LatCtlPath_An_Actl to actuate steering
# path angle is the car wheel angle, not the steering wheel angle
path_angle = math.radians(apply_angle) / CarControllerParams.STEER_RATIO
# ramp rate: 0=Slow, 1=Medium, 2=Fast, 3=Immediately
# TODO: try slower ramp speed when driver torque detected
ramp_type = 3
path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO
# set slower ramp type when small steering angle change
# 0=Slow, 1=Medium, 2=Fast, 3=Immediately
steer_change = abs(CS.out.steeringAngleDeg - actuators.steeringAngleDeg)
if steer_change < 2.0:
ramp_type = 0
elif steer_change < 4.0:
ramp_type = 1
elif steer_change < 6.0:
ramp_type = 2
else:
ramp_type = 3
precision = 1 # 0=Comfortable, 1=Precise (the stock system always uses comfortable)
offset_roll_compensation_curvature = clip(self.VM.calc_curvature(0, CS.out.vEgo, -CS.yaw_data["VehYaw_W_Actl"]), -0.02, 0.02094)
self.apply_angle_last = apply_angle
can_sends.append(fordcan.create_lka_command(self.packer, apply_angle, 0))
can_sends.append(fordcan.create_lka_command(self.packer, 0, 0))
can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision,
0, path_angle, 0, offset_roll_compensation_curvature))
0, path_angle, 0, 0))
### ui ###
@ -99,7 +105,7 @@ class CarController:
self.steer_alert_last = steer_alert
new_actuators = actuators.copy()
new_actuators.steeringAngleDeg = apply_angle
new_actuators.steeringAngleDeg = self.apply_angle_last
self.frame += 1
return new_actuators, can_sends

@ -20,7 +20,7 @@ class CarState(CarStateBase):
ret = car.CarState.new_message()
# car speed
ret.vEgoRaw = cp.vl["EngVehicleSpThrottle2"]["Veh_V_ActlEng"] * CV.KPH_TO_MS
ret.vEgoRaw = cp.vl["BrakeSysFeatures"]["Veh_V_ActlBrk"] * CV.KPH_TO_MS
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.yawRate = cp.vl["Yaw_Data_FD1"]["VehYaw_W_Actl"]
ret.standstill = cp.vl["DesiredTorqBrk"]["VehStop_D_Stat"] == 1
@ -85,8 +85,6 @@ class CarState(CarStateBase):
# Stock values from IPMA so that we can retain some stock functionality
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
# Use stock sensor values
self.yaw_data = cp.vl["Yaw_Data_FD1"]
return ret
@ -94,7 +92,7 @@ class CarState(CarStateBase):
def get_can_parser(CP):
signals = [
# sig_name, sig_address
("Veh_V_ActlEng", "EngVehicleSpThrottle2"), # ABS vehicle speed (kph)
("Veh_V_ActlBrk", "BrakeSysFeatures"), # ABS vehicle speed (kph)
("VehYaw_W_Actl", "Yaw_Data_FD1"), # ABS vehicle yaw rate (rad/s)
("VehStop_D_Stat", "DesiredTorqBrk"), # ABS vehicle stopped
("PrkBrkStatus", "DesiredTorqBrk"), # ABS park brake status
@ -156,7 +154,7 @@ class CarState(CarStateBase):
checks = [
# sig_address, frequency
("EngVehicleSpThrottle2", 50),
("BrakeSysFeatures", 50),
("Yaw_Data_FD1", 100),
("DesiredTorqBrk", 50),
("EngVehicleSpThrottle", 100),

@ -8,8 +8,7 @@ def create_lka_command(packer, angle_deg: float, curvature: float):
"""
Creates a CAN message for the Ford LKAS Command.
This command can apply "Lane Keeping Aid" manoeuvres, which are subject to the
PSCM lockout.
This command can apply "Lane Keeping Aid" manoeuvres, which are subject to the PSCM lockout.
Frequency is 20Hz.
"""
@ -30,12 +29,20 @@ def create_tja_command(packer, lca_rq: int, ramp_type: int, precision: int, path
"""
Creates a CAN message for the Ford TJA/LCA Command.
This command can apply "Lane Centering" manoeuvres: continuous lane centering
for traffic jam assist and highway driving. It is not subject to the PSCM
lockout.
This command can apply "Lane Centering" manoeuvres: continuous lane centering for traffic jam
assist and highway driving. It is not subject to the PSCM lockout.
The PSCM should be configured to accept TJA/LCA commands before these
commands will be processed. This can be done using tools such as Forscan.
Ford lane centering command uses a third order polynomial to describe the road centerline. The
polynomial is defined by the following coefficients:
c0: lateral offset between the vehicle and the centerline
c1: heading angle between the vehicle and the centerline
c2: curvature of the centerline
c3: rate of change of curvature of the centerline
As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and
speed, the steering angle cannot be easily controlled.
The PSCM should be configured to accept TJA/LCA commands before these commands will be processed.
This can be done using tools such as Forscan.
Frequency is 20Hz.
"""
@ -47,7 +54,7 @@ def create_tja_command(packer, lca_rq: int, ramp_type: int, precision: int, path
"LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
"LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.4995|0.5240] radians
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
}
@ -108,8 +115,8 @@ def create_lkas_ui_command(packer, main_on: bool, enabled: bool, steer_alert: bo
def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, stock_values: dict):
"""
Creates a CAN message for the Ford IPC adaptive cruise, forward collision
warning and traffic jam assist status.
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam
assist status.
Stock functionality is maintained by passing through unmodified signals.
@ -141,7 +148,7 @@ def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, sto
return packer.make_can_msg("ACCDATA_3", CANBUS.main, values)
def create_button_command(packer, stock_values: dict, cancel = False, resume = False, tja_toggle = False, bus = CANBUS.camera):
def create_button_command(packer, stock_values: dict, cancel = False, resume = False, tja_toggle = False, bus: int = CANBUS.camera):
"""
Creates a CAN message for the Ford SCCM buttons/switches.

@ -5,8 +5,7 @@ from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness,
from selfdrive.car.ford.values import CAR, Ecu, TransmissionType, GearShifter
from selfdrive.car.interfaces import CarInterfaceBase
EventName = car.CarEvent.EventName
CarParams = car.CarParams
class CarInterface(CarInterfaceBase):
@ -19,10 +18,10 @@ class CarInterface(CarInterfaceBase):
ret.carName = "ford"
ret.dashcamOnly = True
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.ford)]
ret.safetyConfigs = [get_safety_config(CarParams.SafetyModel.ford)]
# Angle-based steering
ret.steerControlType = car.CarParams.SteerControlType.angle
ret.steerControlType = CarParams.SteerControlType.angle
ret.steerActuatorDelay = 0.4
ret.steerLimitTimer = 1.0
tire_stiffness_factor = 1.0
@ -43,7 +42,7 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1350 + STD_CARGO_KG
else:
raise ValueError(f"Unsupported car: ${candidate}")
raise ValueError(f"Unsupported car: {candidate}")
# Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1
found_ecus = [fw.ecu for fw in car_fw]

@ -1,4 +1,4 @@
from collections import namedtuple
from collections import defaultdict, namedtuple
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List, Union
@ -22,19 +22,17 @@ class CarControllerParams:
LKAS_UI_STEP = 100
# Message: ACCDATA_3
ACC_UI_STEP = 5
# Message: Steering_Data_FD1, but send twice as fast
BUTTONS_STEP = 10 / 2
STEER_RATIO = 2.75
STEER_DRIVER_ALLOWANCE = 0.8
LKAS_STEER_RATIO = 2.75 # Approximate ratio between LatCtlPath_An_Actl and steering angle in radians
# TODO: remove this once we understand how the EPS calculates the steering angle better
STEER_DRIVER_ALLOWANCE = 0.8 # Driver intervention threshold in Nm
RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15])
RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
class RADAR:
DELPHI_ESR = 'ford_fusion_2018_adas'
DELPHI_MRR = 'FORD_CADS'
class CANBUS:
main = 0
radar = 1
@ -47,6 +45,14 @@ class CAR:
FOCUS_MK4 = "FORD FOCUS 4TH GEN"
class RADAR:
DELPHI_ESR = 'ford_fusion_2018_adas'
DELPHI_MRR = 'FORD_CADS'
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR))
@dataclass
class FordCarInfo(CarInfo):
package: str = "Co-Pilot360 Assist+"
@ -143,10 +149,3 @@ FW_VERSIONS = {
],
},
}
DBC = {
CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
CAR.EXPLORER_MK6: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR),
}

@ -2,7 +2,7 @@
import capnp
from dataclasses import dataclass, field
import struct
from typing import Dict, List
from typing import Dict, List, Optional, Tuple
import panda.python.uds as uds
@ -64,3 +64,5 @@ class FwQueryConfig:
requests: List[Request]
# Overrides and removes from essential ecus for specific models and ecus (exact matching)
non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict)
# Ecus added for data collection, not to be fingerprinted on
extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list)

@ -194,14 +194,14 @@ def get_brand_ecu_matches(ecu_rx_addrs):
return brand_matches
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=False, progress=False):
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False):
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
all_car_fw = []
brand_matches = get_brand_ecu_matches(ecu_rx_addrs)
for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True):
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress)
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress)
all_car_fw.extend(car_fw)
# Try to match using FW returned from this brand only
matches = match_fw_to_car_exact(build_fw_dict(car_fw))
@ -211,8 +211,13 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa
return all_car_fw
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, debug=False, progress=False):
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False):
versions = VERSIONS.copy()
# Each brand can define extra ECUs to query for data collection
for brand, config in FW_QUERY_CONFIGS.items():
versions[brand]["debug"] = {ecu: [] for ecu in config.extra_ecus}
if query_brand is not None:
versions = {query_brand: versions[query_brand]}
@ -247,6 +252,10 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
for addr in tqdm(addrs, disable=not progress):
for addr_chunk in chunks(addr):
for brand, r in requests:
# Skip query if no panda available
if r.bus > num_pandas * 4 - 1:
continue
try:
addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and
(len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)]
@ -287,6 +296,7 @@ if __name__ == "__main__":
args = parser.parse_args()
logcan = messaging.sub_sock('can')
pandaStates_sock = messaging.sub_sock('pandaStates')
sendcan = messaging.pub_sock('sendcan')
extra: Any = None
@ -300,6 +310,7 @@ if __name__ == "__main__":
extra = {"any": {"debug": extra}}
time.sleep(1.)
num_pandas = len(messaging.recv_one_retry(pandaStates_sock).pandaStates)
t = time.time()
print("Getting vin...")
@ -309,7 +320,7 @@ if __name__ == "__main__":
print()
t = time.time()
fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, debug=args.debug, progress=True)
fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, num_pandas=num_pandas, debug=args.debug, progress=True)
_, candidates = match_fw_to_car(fw_vers)
print()

@ -21,7 +21,8 @@ class CarController:
self.frame = 0
self.last_button_frame = 0
self.lka_steering_cmd_counter_last = -1
self.lka_steering_cmd_counter = 0
self.sent_lka_steering_cmd = False
self.lka_icon_status_last = (False, False)
self.params = CarControllerParams()
@ -44,9 +45,14 @@ class CarController:
# Steering (50Hz)
# Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just received the
# next Panda loopback confirmation in the current CS frame.
if CS.lka_steering_cmd_counter != self.lka_steering_cmd_counter_last:
self.lka_steering_cmd_counter_last = CS.lka_steering_cmd_counter
if CS.loopback_lka_steering_cmd_updated:
self.lka_steering_cmd_counter += 1
self.sent_lka_steering_cmd = True
elif (self.frame % self.params.STEER_STEP) == 0:
# Initialize ASCMLKASteeringCmd counter using the camera
if not self.sent_lka_steering_cmd and self.CP.networkLocation == NetworkLocation.fwdCamera:
self.lka_steering_cmd_counter = CS.camera_lka_steering_cmd_counter + 1
if CC.latActive:
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
@ -54,10 +60,7 @@ class CarController:
apply_steer = 0
self.apply_steer_last = apply_steer
# GM EPS faults on any gap in received message counters. To handle transient OP/Panda safety sync issues at the
# moment of disengaging, increment the counter based on the last message known to pass Panda safety checks.
idx = (CS.lka_steering_cmd_counter + 1) % 4
idx = self.lka_steering_cmd_counter % 4
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive))
if self.CP.openpilotLongitudinalControl:

@ -9,6 +9,7 @@ from selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD
TransmissionType = car.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation
STANDSTILL_THRESHOLD = 10 * 0.0311 * CV.KPH_TO_MS
class CarState(CarStateBase):
@ -16,7 +17,8 @@ class CarState(CarStateBase):
super().__init__(CP)
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
self.lka_steering_cmd_counter = 0
self.loopback_lka_steering_cmd_updated = False
self.camera_lka_steering_cmd_counter = 0
self.buttons_counter = 0
def update(self, pt_cp, cam_cp, loopback_cp):
@ -26,6 +28,11 @@ class CarState(CarStateBase):
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
# Variables used for avoiding LKAS faults
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]) > 0
if self.CP.networkLocation == NetworkLocation.fwdCamera:
self.camera_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
ret.wheelSpeeds = self.get_wheel_speeds(
pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
pt_cp.vl["EBCMWheelSpdFront"]["FRWheelSpd"],
@ -34,7 +41,8 @@ class CarState(CarStateBase):
)
ret.vEgoRaw = mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr])
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw < 0.01
# sample rear wheel speeds, standstill=True if ECM allows engagement with brake
ret.standstill = ret.wheelSpeeds.rl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1:
ret.gearShifter = self.parse_gear_shifter("T")
@ -45,8 +53,13 @@ class CarState(CarStateBase):
# that the brake is being intermittently pressed without user interaction.
# To avoid a cruise fault we need to match the ECM's brake pressed signal and threshold
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"] / 0xd0
ret.brakePressed = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"] >= 8
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
if self.CP.networkLocation != NetworkLocation.fwdCamera:
ret.brakePressed = ret.brake >= 8
else:
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
# Match ECM threshold at a standstill to allow the camera to cancel earlier
ret.brakePressed = ret.brake >= 20
# Regen braking is braking
if self.CP.transmissionType == TransmissionType.direct:
@ -60,7 +73,6 @@ class CarState(CarStateBase):
ret.steeringTorque = pt_cp.vl["PSCMStatus"]["LKADriverAppldTrq"]
ret.steeringTorqueEps = pt_cp.vl["PSCMStatus"]["LKATorqueDelivered"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
self.lka_steering_cmd_counter = loopback_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
# 0 inactive, 1 active, 2 temporarily limited, 3 failed
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
@ -88,6 +100,7 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL
if self.CP.networkLocation == NetworkLocation.fwdCamera:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
return ret
@ -96,8 +109,16 @@ class CarState(CarStateBase):
signals = []
checks = []
if CP.networkLocation == NetworkLocation.fwdCamera:
signals.append(("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"))
checks.append(("ASCMActiveCruiseControlStatus", 25))
signals += [
("AEBCmdActive", "AEBCmd"),
("RollingCounter", "ASCMLKASteeringCmd"),
("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"),
]
checks += [
("AEBCmd", 10),
("ASCMLKASteeringCmd", 10),
("ASCMActiveCruiseControlStatus", 25),
]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.CAMERA)
@ -167,9 +188,7 @@ class CarState(CarStateBase):
]
checks = [
("ASCMLKASteeringCmd", 10), # 10 Hz is the stock inactive rate (every 100ms).
# While active 50 Hz (every 20 ms) is normal
# EPS will tolerate around 200ms when active before faulting
("ASCMLKASteeringCmd", 0),
]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.LOOPBACK)
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.LOOPBACK, enforce_checks=False)

@ -212,7 +212,13 @@ class CarInterface(CarInterfaceBase):
if ret.cruiseState.standstill:
events.add(EventName.resumeRequired)
if ret.vEgo < self.CP.minSteerSpeed:
events.add(car.CarEvent.EventName.belowSteerSpeed)
events.add(EventName.belowSteerSpeed)
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.pcmCruise:
# The ECM has a higher brake pressed threshold than the camera, causing an
# ACC fault when you engage at a stop with your foot partially on the brake
if ret.vEgoRaw < 0.1 and ret.brake < 20:
events.add(EventName.gmAccFaultedTemp)
ret.events = events.to_msg()

@ -10,6 +10,12 @@ from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerPar
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
@ -40,22 +46,21 @@ class CarController:
self.CP = CP
self.params = CarControllerParams(CP)
self.packer = CANPacker(dbc_name)
self.angle_limit_counter = 0
self.frame = 0
self.apply_steer_last = 0
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
self.accel = 0
def update(self, CC, CS):
actuators = CC.actuators
hud_control = CC.hudControl
# Steering Torque
# These cars have significantly more torque than most HKG. Limit to 70% of max.
# steering torque
steer = actuators.steer
if self.CP.carFingerprint in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
# these cars have significantly more torque than most HKG; limit to 70% of max
steer = clip(steer, -0.7, 0.7)
new_steer = int(round(steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
@ -65,52 +70,85 @@ class CarController:
self.apply_steer_last = apply_steer
# accel + longitudinal
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends = []
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and self.CP.openpilotLongitudinalControl:
addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, 5
can_sends.append([addr, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", bus])
# >90 degree steering fault prevention
# Count up to MAX_ANGLE_FRAMES, at which point we need to cut torque to avoid a steering fault
if CC.latActive and abs(CS.out.steeringAngleDeg) >= MAX_ANGLE:
self.angle_limit_counter += 1
else:
self.angle_limit_counter = 0
# Cut steer actuation bit for two frames and hold torque with induced temporary fault
torque_fault = CC.latActive and self.angle_limit_counter > MAX_ANGLE_FRAMES
lat_active = CC.latActive and not torque_fault
if self.angle_limit_counter >= MAX_ANGLE_FRAMES + MAX_ANGLE_CONSECUTIVE_FRAMES:
self.angle_limit_counter = 0
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
can_sends.append(hyundaicanfd.create_lkas(self.packer, self.CP, CC.enabled, CC.latActive, apply_steer))
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, CC.enabled, lat_active, apply_steer))
# block LFA on HDA2
if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.CANFD_HDA2):
# disable LFA on HDA2
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_cam_0x2a4(self.packer, CS.cam_0x2a4))
# 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))
if self.frame % 5 == 0 and (not hda2 or hda2_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CP, CC.enabled))
# button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_cruise_info(self.packer, CS.cruise_info_copy, True))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
else:
# tester present - w/ no response (keeps radar disabled)
if self.CP.openpilotLongitudinalControl:
if self.frame % 100 == 0:
can_sends.append([0x7D0, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 0])
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, CC.latActive,
CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.frame))
if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CP, CC.enabled, accel, stopping, CC.cruiseControl.override,
set_speed_in_units))
else:
# button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, lat_active,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if not self.CP.openpilotLongitudinalControl:
if CC.cruiseControl.cancel:
@ -123,18 +161,10 @@ class CarController:
self.last_button_frame = self.frame
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
accel = actuators.accel
#TODO unclear if this is needed
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
accel = clip(accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_MPH if CS.clu11["CF_Clu_SPEED_UNIT"] == 1 else CV.MS_TO_KPH)
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control.leadVisible, set_speed_in_units, stopping, CS.out.gasPressed))
self.accel = accel
hud_control.leadVisible, set_speed_in_units, stopping, CC.cruiseControl.override))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021,
@ -153,7 +183,7 @@ class CarController:
new_actuators = actuators.copy()
new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.accel = self.accel
new_actuators.accel = accel
self.frame += 1
return new_actuators, can_sends

@ -30,9 +30,12 @@ class CarState(CarStateBase):
else: # preferred and elect gear methods use same definition
self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"]
self.is_metric = False
self.brake_error = False
self.buttons_counter = 0
self.cruise_info = {}
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
@ -45,8 +48,8 @@ class CarState(CarStateBase):
ret = car.CarState.new_message()
cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp
is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"],
cp.vl["CGW2"]["CF_Gway_RLDrSw"], cp.vl["CGW2"]["CF_Gway_RRDrSw"]])
@ -69,7 +72,7 @@ class CarState(CarStateBase):
self.cluster_speed_counter = 0
# mimic how dash converts to imperial
if not is_metric:
if not self.is_metric:
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
ret.vEgoCluster = self.cluster_speed * speed_conv
@ -126,12 +129,12 @@ class CarState(CarStateBase):
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl:
if self.CP.carFingerprint in FEATURES["use_fca"]:
ret.stockAeb = cp_cruise.vl["FCA11"]["FCA_CmdAct"] != 0
ret.stockFcw = cp_cruise.vl["FCA11"]["CF_VSM_Warn"] == 2
else:
ret.stockAeb = cp_cruise.vl["SCC12"]["AEB_CmdAct"] != 0
ret.stockFcw = cp_cruise.vl["SCC12"]["CF_VSM_Warn"] == 2
aeb_src = "FCA11" if self.CP.carFingerprint in FEATURES["use_fca"] else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.carFingerprint in FEATURES["use_fca"] else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = aeb_warning and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
@ -151,9 +154,9 @@ class CarState(CarStateBase):
def update_canfd(self, cp, cp_cam):
ret = car.CarState.new_message()
if self.CP.flags & HyundaiFlags.CANFD_HDA2:
if self.CP.carFingerprint in EV_CAR:
ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255.
else:
elif self.CP.carFingerprint in HYBRID_CAR:
ret.gas = cp.vl["ACCELERATOR_ALT"]["ACCELERATOR_PEDAL"] / 1023.
ret.gasPressed = ret.gas > 1e-5
ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1
@ -180,22 +183,26 @@ class CarState(CarStateBase):
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"]["LEFT_LAMP"],
cp.vl["BLINKERS"]["RIGHT_LAMP"])
ret.cruiseState.available = True
ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1
cp_cruise_info = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS
ret.cruiseState.speed = cp_cruise_info.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor
ret.cruiseState.standstill = cp_cruise_info.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1
self.is_metric = cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] != 1
if not self.CP.openpilotLongitudinalControl:
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
cp_cruise_info = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
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_info = copy.copy(cp_cruise_info.vl["CRUISE_INFO"])
cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"])
self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"])
self.buttons_counter = cp.vl[cruise_btn_msg]["COUNTER"]
self.cruise_info_copy = copy.copy(cp_cruise_info.vl["CRUISE_INFO"])
if self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"])
@ -294,12 +301,14 @@ class CarState(CarStateBase):
signals += [
("FCA_CmdAct", "FCA11"),
("CF_VSM_Warn", "FCA11"),
("CF_VSM_DecCmdAct", "FCA11"),
]
checks.append(("FCA11", 50))
else:
signals += [
("AEB_CmdAct", "SCC12"),
("CF_VSM_Warn", "SCC12"),
("CF_VSM_DecCmdAct", "SCC12"),
]
if CP.enableBsm:
@ -383,12 +392,14 @@ class CarState(CarStateBase):
signals += [
("FCA_CmdAct", "FCA11"),
("CF_VSM_Warn", "FCA11"),
("CF_VSM_DecCmdAct", "FCA11"),
]
checks.append(("FCA11", 50))
else:
signals += [
("AEB_CmdAct", "SCC12"),
("CF_VSM_Warn", "SCC12"),
("CF_VSM_DecCmdAct", "SCC12"),
]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2)
@ -410,6 +421,7 @@ class CarState(CarStateBase):
("STEERING_ANGLE", "STEERING_SENSORS"),
("STEERING_COL_TORQUE", "MDPS"),
("STEERING_OUT_TORQUE", "MDPS"),
("LKA_FAULT", "MDPS"),
("CRUISE_ACTIVE", "SCC1"),
("COUNTER", cruise_btn_msg),
@ -438,18 +450,23 @@ class CarState(CarStateBase):
("DOORS_SEATBELTS", 4),
]
if CP.flags & HyundaiFlags.CANFD_HDA2:
if CP.flags & HyundaiFlags.CANFD_HDA2 and not CP.openpilotLongitudinalControl:
signals += [
("ACCELERATOR_PEDAL", "ACCELERATOR"),
("GEAR", "ACCELERATOR"),
("SET_SPEED", "CRUISE_INFO"),
("CRUISE_STANDSTILL", "CRUISE_INFO"),
]
checks += [
("CRUISE_INFO", 50),
]
if CP.carFingerprint in EV_CAR:
signals += [
("ACCELERATOR_PEDAL", "ACCELERATOR"),
]
checks += [
("ACCELERATOR", 100),
]
else:
elif CP.carFingerprint in HYBRID_CAR:
signals += [
("ACCELERATOR_PEDAL", "ACCELERATOR_ALT"),
]
@ -472,17 +489,14 @@ class CarState(CarStateBase):
("CRUISE_MAIN", "CRUISE_INFO"),
("CRUISE_STATUS", "CRUISE_INFO"),
("CRUISE_INACTIVE", "CRUISE_INFO"),
("NEW_SIGNAL_2", "CRUISE_INFO"),
("ZEROS_9", "CRUISE_INFO"),
("CRUISE_STANDSTILL", "CRUISE_INFO"),
("NEW_SIGNAL_3", "CRUISE_INFO"),
("BYTE11", "CRUISE_INFO"),
("ZEROS_5", "CRUISE_INFO"),
("DISTANCE_SETTING", "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),
]

@ -4,7 +4,7 @@ from selfdrive.car.hyundai.values import CAR, CHECKSUM, CAMERA_SCC_CAR
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
lkas11, sys_warning, sys_state, enabled,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart):
values = lkas11
@ -14,6 +14,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
values["CF_Lkas_LdwsRHWarning"] = right_lane_depart
values["CR_Lkas_StrToqReq"] = apply_steer
values["CF_Lkas_ActToi"] = steer_req
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
values["CF_Lkas_MsgCount"] = frame % 0x10
if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
@ -38,7 +39,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
# Likely cars lacking the ability to show individual lane lines in the dash
elif car_fingerprint in (CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_2019):
elif car_fingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
# SysWarning 4 = keep hands on wheel + beep
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
@ -94,7 +95,7 @@ def create_lfahda_mfc(packer, enabled, hda_set_speed=0):
}
return packer.make_can_msg("LFAHDA_MFC", 0, values)
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, set_speed, stopping, gas_pressed):
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, set_speed, stopping, long_override):
commands = []
scc11_values = {
@ -111,7 +112,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, s
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
scc12_values = {
"ACCMode": 2 if enabled and gas_pressed else 1 if enabled else 0,
"ACCMode": 2 if enabled and long_override else 1 if enabled else 0,
"StopReq": 1 if stopping else 0,
"aReqRaw": accel,
"aReqValue": accel, # stock ramps up and down respecting jerk limit until it reaches aReqRaw
@ -127,7 +128,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, s
"ComfortBandLower": 0.0, # stock usually is 0 but sometimes uses higher values
"JerkUpperLimit": upper_jerk, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": 5.0, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": 2 if enabled and gas_pressed else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": 2 if lead_visible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
}
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))

@ -1,7 +1,17 @@
from selfdrive.car.hyundai.values import HyundaiFlags
def create_lkas(packer, CP, enabled, lat_active, apply_steer):
def get_e_can_bus(CP):
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. HDA2 cars
# have a a different harness than the HDA1 and non-HDA variants in order to split
# a different bus, since the steering is done by different ECUs.
return 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4
def create_steering_messages(packer, CP, enabled, lat_active, apply_steer):
ret = []
values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1,
@ -14,8 +24,14 @@ def create_lkas(packer, CP, enabled, lat_active, apply_steer):
"NEW_SIGNAL_2": 0,
}
msg = "LKAS" if CP.flags & HyundaiFlags.CANFD_HDA2 else "LFA"
return packer.make_can_msg(msg, 4, values)
if CP.flags & HyundaiFlags.CANFD_HDA2:
if CP.openpilotLongitudinalControl:
ret.append(packer.make_can_msg("LFA", 5, values))
ret.append(packer.make_can_msg("LKAS", 4, values))
else:
ret.append(packer.make_can_msg("LFA", 4, values))
return ret
def create_cam_0x2a4(packer, camera_values):
camera_values.update({
@ -31,16 +47,98 @@ def create_buttons(packer, cnt, btn):
}
return packer.make_can_msg("CRUISE_BUTTONS", 5, values)
def create_cruise_info(packer, cruise_info_copy, cancel):
def create_acc_cancel(packer, CP, cruise_info_copy):
values = cruise_info_copy
if cancel:
values["CRUISE_STATUS"] = 0
values["CRUISE_INACTIVE"] = 1
return packer.make_can_msg("CRUISE_INFO", 4, values)
values.update({
"CRUISE_STATUS": 0,
"CRUISE_INACTIVE": 1,
})
return packer.make_can_msg("CRUISE_INFO", get_e_can_bus(CP), values)
def create_lfahda_cluster(packer, enabled):
def create_lfahda_cluster(packer, CP, 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)
return packer.make_can_msg("LFAHDA_CLUSTER", get_e_can_bus(CP), values)
def create_acc_control(packer, CP, enabled, accel, stopping, gas_override, set_speed):
cruise_status = 0 if not enabled else (4 if gas_override else 2)
if not enabled or gas_override:
accel = 0
values = {
"CRUISE_STATUS": cruise_status,
"CRUISE_INACTIVE": 0 if enabled else 1,
"CRUISE_MAIN": 1,
"CRUISE_STANDSTILL": 0,
"STOP_REQ": 1 if stopping else 0,
"ACCEL_REQ": accel,
"ACCEL_REQ2": accel,
"SET_SPEED": set_speed,
"DISTANCE_SETTING": 4,
"ACC_ObjDist": 1,
"ObjValid": 1,
"OBJ_STATUS": 2,
"SET_ME_2": 0x2,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"NEW_SIGNAL_9": 2,
"NEW_SIGNAL_10": 4,
}
return packer.make_can_msg("CRUISE_INFO", get_e_can_bus(CP), values)
def create_adrv_messages(packer, frame):
# messages needed to car happy after disabling
# the ADAS Driving ECU to do longitudinal control
ret = []
values = {
}
ret.append(packer.make_can_msg("ADRV_0x51", 4, values))
if frame % 2 == 0:
values = {
'AEB_SETTING': 0x1, # show AEB disabled icon
'SET_ME_2': 0x2,
'SET_ME_FF': 0xff,
'SET_ME_FC': 0xfc,
'SET_ME_9': 0x9,
}
ret.append(packer.make_can_msg("ADRV_0x160", 5, values))
if frame % 5 == 0:
values = {
'SET_ME_1C': 0x1c,
'SET_ME_FF': 0xff,
'SET_ME_TMP_F': 0xf,
'SET_ME_TMP_F_2': 0xf,
}
ret.append(packer.make_can_msg("ADRV_0x1ea", 5, values))
values = {
'SET_ME_E1': 0xe1,
'SET_ME_3A': 0x3a,
}
ret.append(packer.make_can_msg("ADRV_0x200", 5, values))
if frame % 20 == 0:
values = {
'SET_ME_15': 0x15,
}
ret.append(packer.make_can_msg("ADRV_0x345", 5, values))
if frame % 100 == 0:
values = {
'SET_ME_22': 0x22,
'SET_ME_41': 0x41,
}
ret.append(packer.make_can_msg("ADRV_0x1da", 5, values))
return ret

@ -27,31 +27,24 @@ class CarInterface(CarInterfaceBase):
ret.carName = "hyundai"
ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
# WARNING: disabling radar also disables AEB (and we show the same warning on the instrument cluster as if you manually disabled AEB)
ret.experimentalLongitudinalAvailable = candidate not in (LEGACY_SAFETY_MODE_CAR | CAMERA_SCC_CAR | CANFD_CAR)
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
ret.pcmCruise = not ret.openpilotLongitudinalControl
# These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30}
if candidate in CANFD_CAR:
# detect HDA2 with LKAS message
if 0x50 in fingerprint[6]:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
else:
# non-HDA2
if 0x1cf not in fingerprint[4]:
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
ret.steerActuatorDelay = 0.1 # Default delay
ret.steerLimitTimer = 0.4
tire_stiffness_factor = 1.
ret.stoppingControl = True
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 2.0
ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0]
ret.longitudinalActuatorDelayLowerBound = 0.5 # s
ret.longitudinalActuatorDelayUpperBound = 0.5 # s
if candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022):
ret.lateralTuning.pid.kf = 0.00005
ret.mass = 3982. * CV.LB_TO_KG + STD_CARGO_KG
@ -202,12 +195,12 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.indi.timeConstantV = [1.4]
ret.lateralTuning.indi.actuatorEffectivenessBP = [0.]
ret.lateralTuning.indi.actuatorEffectivenessV = [1.8]
elif candidate in (CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_2019, CAR.KIA_OPTIMA_H):
elif candidate in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H):
ret.mass = 3558. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75
tire_stiffness_factor = 0.5
if candidate == CAR.KIA_OPTIMA:
if candidate == CAR.KIA_OPTIMA_G4:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.KIA_STINGER:
@ -291,20 +284,36 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.01]]
# panda safety config
# *** longitudinal control ***
if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
ret.longitudinalActuatorDelayLowerBound = 0.15
ret.longitudinalActuatorDelayUpperBound = 0.5
ret.experimentalLongitudinalAvailable = bool(ret.flags & HyundaiFlags.CANFD_HDA2)
else:
ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0]
ret.longitudinalActuatorDelayLowerBound = 0.5
ret.longitudinalActuatorDelayUpperBound = 0.5
ret.experimentalLongitudinalAvailable = candidate not in (LEGACY_SAFETY_MODE_CAR | CAMERA_SCC_CAR)
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.stoppingControl = True
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 2.0
# *** panda safety config ***
if candidate in CANFD_CAR:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput),
get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd)]
# detect HDA2 with LKAS message
if 0x50 in fingerprint[6]:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
if ret.flags & HyundaiFlags.CANFD_HDA2:
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
ret.safetyConfigs[1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
ret.safetyConfigs[1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS
else:
ret.enableBsm = 0x58b in fingerprint[0]
@ -314,18 +323,16 @@ class CarInterface(CarInterfaceBase):
else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)]
# set appropriate safety param for gas signal
if candidate in HYBRID_CAR:
ret.safetyConfigs[0].safetyParam = 2
elif candidate in EV_CAR:
ret.safetyConfigs[0].safetyParam = 1
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LONG
if candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG
if candidate in HYBRID_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_HYBRID_GAS
elif candidate in EV_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS
ret.centerToFront = ret.wheelbase * 0.4
# TODO: get actual value, for now starting with reasonable value for
@ -336,13 +343,15 @@ class CarInterface(CarInterfaceBase):
# mass and CG position, so all cars will have approximately similar dyn behaviors
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor)
return ret
@staticmethod
def init(CP, logcan, sendcan):
if CP.openpilotLongitudinalControl:
disable_ecu(logcan, sendcan, addr=0x7d0, com_cont_req=b'\x28\x83\x01')
addr, bus = 0x7d0, 0
if CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, 5
disable_ecu(logcan, sendcan, bus=bus, addr=addr, com_cont_req=b'\x28\x83\x01')
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam)

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import unittest
from cereal import car
from selfdrive.car.car_helpers import get_interface_attr
from selfdrive.car.fw_versions import FW_QUERY_CONFIGS
from selfdrive.car.hyundai.values import CANFD_CAR
Ecu = car.CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
VERSIONS = get_interface_attr("FW_VERSIONS", ignore_none=True)
class TestHyundaiFingerprint(unittest.TestCase):
def test_auxiliary_request_ecu_whitelist(self):
# Asserts only auxiliary Ecus can exist in database for CAN-FD cars
config = FW_QUERY_CONFIGS['hyundai']
whitelisted_ecus = {ecu for r in config.requests for ecu in r.whitelist_ecus if r.bus > 3}
for car_model in CANFD_CAR:
ecus = {fw[0] for fw in VERSIONS['hyundai'][car_model].keys()}
ecus_not_in_whitelist = ecus - whitelisted_ecus
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_in_whitelist])
self.assertEqual(len(ecus_not_in_whitelist), 0, f'{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}')
if __name__ == "__main__":
unittest.main()

@ -85,8 +85,8 @@ class CAR:
KIA_NIRO_EV = "KIA NIRO EV 2020"
KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019"
KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021"
KIA_OPTIMA = "KIA OPTIMA 2016"
KIA_OPTIMA_2019 = "KIA OPTIMA 2019"
KIA_OPTIMA_G4 = "KIA OPTIMA 4TH GEN"
KIA_OPTIMA_G4_FL = "KIA OPTIMA 4TH GEN FACELIFT"
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
KIA_SELTOS = "KIA SELTOS 2021"
KIA_SORENTO = "KIA SORENTO GT LINE 2018"
@ -109,8 +109,11 @@ class HyundaiCarInfo(CarInfo):
CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.ELANTRA: HyundaiCarInfo("Hyundai Elantra 2017-19", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_b),
CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-22", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k),
CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-22", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k),
CAR.ELANTRA_GT_I30: None, # dashcamOnly and same platform as CAR.ELANTRA
CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k),
CAR.ELANTRA_GT_I30: [
HyundaiCarInfo("Hyundai Elantra GT 2017-19", harness=Harness.hyundai_e),
HyundaiCarInfo("Hyundai i30 2019", harness=Harness.hyundai_e),
],
CAR.HYUNDAI_GENESIS: HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), # TODO: check 2015 packages
CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", harness=Harness.hyundai_c),
CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", harness=Harness.hyundai_h), # TODO: confirm 2020-21 harness
@ -138,7 +141,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
],
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a),
CAR.IONIQ_5: HyundaiCarInfo("Hyundai Ioniq 5 2022", "Highway Driving Assist II", harness=Harness.hyundai_q),
CAR.IONIQ_5: [
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022" , "Highway Driving Assist", harness=Harness.hyundai_k),
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 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
@ -155,8 +161,8 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify
HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h),
],
CAR.KIA_OPTIMA: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", harness=Harness.hyundai_b), # TODO: may support 2016, 2018
CAR.KIA_OPTIMA_2019: HyundaiCarInfo("Kia Optima 2019-20", harness=Harness.hyundai_g),
CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", harness=Harness.hyundai_b), # TODO: may support 2016, 2018
CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", harness=Harness.hyundai_g),
CAR.KIA_OPTIMA_H: [
HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control"), # TODO: may support adjacent years
HyundaiCarInfo("Kia Optima Hybrid 2019"),
@ -168,7 +174,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
],
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c),
CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e),
CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "Highway Driving Assist II", harness=Harness.hyundai_p),
CAR.KIA_EV6: [
HyundaiCarInfo("Kia EV6 (without HDA II) 2022", "Highway Driving Assist", harness=Harness.hyundai_l),
HyundaiCarInfo("Kia EV6 (with HDA II) 2022", "Highway Driving Assist II", harness=Harness.hyundai_p)
],
# Genesis
CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f),
@ -284,12 +293,30 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera],
),
Request(
[HYUNDAI_VERSION_REQUEST_MULTI],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar],
),
# CAN-FD queries
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdRadar],
bus=4,
),
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.adas],
bus=5,
),
],
extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms
],
)
FW_VERSIONS = {
@ -729,6 +756,7 @@ FW_VERSIONS = {
b'\xf1\x81640E0051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x82CKJN3TMSDE0B\x00\x00\x00\x00',
b'\xf1\x82CKKN3TMD_H0A\x00\x00\x00\x00',
b'\xe0\x19\xff\xe7\xe7g\x01\xa2\x00\x0f\x00\x9e\x00\x06\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x0f\x0e\x0f\x0f\x0e\r\x00\x00\x7f\x02.\xff\x00\x00~p\x00\x00\x00\x00u\xff\xf9\xff\x00\x00\x00\x00V\t\xd5\x01\xc0\x00\x00\x00\x007\xfb\xfc\x0b\x8d\x00',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CK MDPS R 1.00 1.04 57700-J5200 4C2CL104',
@ -751,6 +779,8 @@ FW_VERSIONS = {
b'\xf1\x87VDKLJ18675252DK6\x89vhgwwwwveVU\x88w\x87w\x99vgf\x97vXfgw_\xff\xc2\xfb\xf1\x89E25\x00\x00\x00\x00\x00\x00\x00\xf1\x82TCK0T33NB2',
b'\xf1\x87WAJTE17552812CH4vfFffvfVeT5DwvvVVdFeegeg\x88\x88o\xff\x1a]\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00TCK2T20NB1\x19\xd2\x00\x94',
b'\xf1\x87VDHLG17274082DK2wfFf\x89x\x98wUT5T\x88v\x97xgeGefTGTVvO\xff\x1c\x14\xf1\x81E19\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E19\x00\x00\x00\x00\x00\x00\x00SCK0T33UB2\xee[\x97S',
b'\xf1\x87VDHLG17000192DK2xdFffT\xa5VUD$DwT\x86wveVeeD&T\x99\xba\x8f\xff\xcc\x99\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\t\xb7\x17\xf5',
b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\t\xb7\x17\xf5',
],
},
CAR.PALISADE: {
@ -1136,7 +1166,7 @@ FW_VERSIONS = {
b'\xf1\x87954A22D200\xf1\x81T01950A1 \xf1\000T0190XBL T01950A1 DSP2T16X4X950NS8\r\xfe\x9c\x8b',
],
},
CAR.KIA_OPTIMA: {
CAR.KIA_OPTIMA_G4: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JF__ SCC F-CUP 1.00 1.00 96400-D4100 ',
],
@ -1150,7 +1180,7 @@ FW_VERSIONS = {
b'\xf1\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf1\x816T6J0051\x00\x00\xf1\x006T6J0_C2\x00\x006T6J0051\x00\x00TJF0T20NSB\x00\x00\x00\x00',
],
},
CAR.KIA_OPTIMA_2019: {
CAR.KIA_OPTIMA_G4_FL: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JF__ SCC F-CUP 1.00 1.00 96400-D4110 ',
],
@ -1215,26 +1245,29 @@ FW_VERSIONS = {
],
},
CAR.ELANTRA_HEV_2021: {
(Ecu.fwdCamera, 0x7c4, None) : [
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA000 210930',
b'\xf1\000CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819',
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.07 99210-AA000 220426',
],
(Ecu.fwdRadar, 0x7d0, None) : [
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\000CNhe SCC FHCUP 1.00 1.01 99110-BY000 ',
b'\xf1\x8799110BY000\xf1\x00CNhe SCC FHCUP 1.00 1.01 99110-BY000 ',
],
(Ecu.eps, 0x7d4, None) :[
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.03 56310BY0500 4CNHC103',
b'\xf1\x8756310/BY050\xf1\x00CN7 MDPS C 1.00 1.03 56310/BY050 4CNHC103',
b'\xf1\x8756310/BY050\xf1\000CN7 MDPS C 1.00 1.02 56310/BY050 4CNHC102',
],
(Ecu.transmission, 0x7e1, None) :[
(Ecu.transmission, 0x7e1, None): [
b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa',
b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\000\000\000\000',
b'\xf1\x816U3K3051\000\000\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa',
b'\xf1\x816U3K3051\x00\x00\xf1\x006U3L0_C2\x00\x006U3K3051\x00\x00HCN0G16NS0\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None) : [
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00',
]
},
CAR.KONA_HEV: {
@ -1306,33 +1339,17 @@ FW_VERSIONS = {
],
},
CAR.KIA_EV6: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00CV IEB \x03 101!\x10\x18 58520-CV100',
b'\xf1\x8758520CV100\xf1\x00CV IEB \x02 101!\x10\x18 58520-CV100',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CV1 MDPS R 1.00 1.04 57700-CV000 1B30',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ',
b'\xf1\x8799110CV000\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.05 99210-CV000 211027',
b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.06 99210-CV000 220328',
b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.05 99210-CV000 211027',
],
},
CAR.IONIQ_5: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010',
b'\xf1\x8758520GI010\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010',
b'\xf1\x00NE1 IEB \x08 104!\x04\x05 58520-GI000',
b'\xf1\x8758520GI000\xf1\x00NE1 IEB \x08 104!\x04\x05 58520-GI000',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106',
b'\xf1\x8757700GI000 \xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
b'\xf1\x8799110GI000\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
@ -1346,15 +1363,8 @@ FW_VERSIONS = {
(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',
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
],
},
}
@ -1367,7 +1377,7 @@ CHECKSUM = {
FEATURES = {
# which message has the gear
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_2019, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
"use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
"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
@ -1379,11 +1389,11 @@ CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN}
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_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}
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, CAR.TUCSON_HYBRID_4TH_GEN} # 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, CAR.KIA_EV6, CAR.IONIQ_5}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_2019, CAR.VELOSTER, CAR.KIA_STINGER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022}
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER, CAR.KIA_STINGER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022}
# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points.
# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py
@ -1408,8 +1418,8 @@ DBC = {
CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_2019: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4_FL: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format

@ -10,11 +10,12 @@ from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D
from common.numpy_fast import interp
from common.realtime import DT_CTRL
from selfdrive.car import apply_hysteresis, create_button_enable_events, gen_empty_fingerprint
from selfdrive.car import apply_hysteresis, gen_empty_fingerprint
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, apply_deadzone
from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel
ButtonType = car.CarState.ButtonEvent.Type
GearShifter = car.CarState.GearShifter
EventName = car.CarEvent.EventName
TorqueFromLateralAccelCallbackType = Callable[[float, car.CarParams.LateralTorqueTuning, float, float, bool], float]
@ -210,7 +211,8 @@ class CarInterfaceBase(ABC):
def apply(self, c: car.CarControl) -> Tuple[car.CarControl.Actuators, List[bytes]]:
pass
def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_enable=True):
def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_enable=True,
enable_buttons=(ButtonType.accelCruise, ButtonType.decelCruise)):
events = Events()
if cs_out.doorOpen:
@ -244,7 +246,13 @@ class CarInterfaceBase(ABC):
events.add(EventName.steerOverride)
# Handle button presses
events.events.extend(create_button_enable_events(cs_out.buttonEvents, pcm_cruise=self.CP.pcmCruise))
for b in cs_out.buttonEvents:
# Enable OP long on falling edge of enable buttons (defaults to accelCruise and decelCruise, overridable per-port)
if not self.CP.pcmCruise and (b.type in enable_buttons and not b.pressed):
events.add(EventName.buttonEnable)
# Disable on rising edge of cancel for both stock and OP long
if b.type == ButtonType.cancel and b.pressed:
events.add(EventName.buttonCancel)
# Handle permanent and temporary steering faults
self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1

@ -58,9 +58,6 @@ class CarState(CarStateBase):
ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0
ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS
if self.car_fingerprint not in PREGLOBAL_CARS:
ret.cruiseState.standstill = cp_cam.vl["ES_DashStatus"]["Cruise_State"] == 3
if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \
(self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1):
ret.cruiseState.speed *= CV.MPH_TO_KPH
@ -78,6 +75,8 @@ class CarState(CarStateBase):
else:
ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
ret.cruiseState.standstill = cp_cam.vl["ES_DashStatus"]["Cruise_State"] == 3
ret.stockFcw = cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2
self.es_lkas_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
cp_es_distance = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam

@ -88,8 +88,8 @@ routes = [
CarTestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
CarTestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022, segment=1),
CarTestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA),
CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_2019, segment=0),
CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
@ -110,7 +110,8 @@ routes = [
CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV),
CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER),
CarTestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6),
CarTestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6), # HDA2
CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1
CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV),
@ -175,6 +176,7 @@ routes = [
CarTestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.PASSAT_MK8),
CarTestRoute("3cfdec54aa035f3f|2022-07-19--23-45-10", VOLKSWAGEN.PASSAT_NMS),
CarTestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.POLO_MK6),
CarTestRoute("064d1816e448f8eb|2022-09-29--15-32-34", VOLKSWAGEN.SHARAN_MK2),
CarTestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.TAOS_MK1),
CarTestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.TCROSS_MK1),
CarTestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2),

@ -44,6 +44,13 @@ class TestFwFingerprint(unittest.TestCase):
duplicates = {fw for fw in ecu_fw if ecu_fw.count(fw) > 1}
self.assertFalse(len(duplicates), f"{car_model}: Duplicate FW versions: Ecu.{ECU_NAME[ecu[0]]}, {duplicates}")
def test_data_collection_ecus(self):
for brand, config in FW_QUERY_CONFIGS.items():
for car_model, ecus in VERSIONS[brand].items():
bad_ecus = set(ecus).intersection(config.extra_ecus)
with self.subTest(car_model=car_model):
self.assertFalse(len(bad_ecus), f'{car_model}: Fingerprints contain ECUs added for data collection: {bad_ecus}')
def test_blacklisted_ecus(self):
blacklisted_addrs = (0x7c4, 0x7d0) # includes A/C ecu and an unknown ecu
for car_model, ecus in FW_VERSIONS.items():

@ -234,7 +234,7 @@ class TestCarModelBase(unittest.TestCase):
checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev()
checks['cruiseState'] += CS.cruiseState.enabled and not CS.cruiseState.available
if self.CP.carName not in ("hyundai", "volkswagen", "gm", "body"):
if self.CP.carName not in ("hyundai", "volkswagen", "body"):
# TODO: fix standstill mismatches for other makes
checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving()

@ -30,6 +30,7 @@ CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05]
VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1]
VOLKSWAGEN SHARAN 2ND GEN: [2.5, 2.5, 0.1]
HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0]
# Dashcam or fallback configured as ideal car

@ -17,8 +17,8 @@ LEXUS RC 2020: LEXUS NX 2020
TOYOTA AVALON HYBRID 2019: TOYOTA AVALON 2019
TOYOTA AVALON HYBRID 2022: TOYOTA AVALON 2022
KIA OPTIMA 2016: HYUNDAI SONATA 2020
KIA OPTIMA 2019: HYUNDAI SONATA 2020
KIA OPTIMA 4TH GEN: HYUNDAI SONATA 2020
KIA OPTIMA 4TH GEN FACELIFT: HYUNDAI SONATA 2020
KIA OPTIMA HYBRID 2017 & SPORTS 2019: HYUNDAI SONATA 2020
KIA FORTE E 2018 & GT 2021: HYUNDAI SONATA 2020
KIA CEED INTRO ED 2019: HYUNDAI SONATA 2020

@ -757,6 +757,7 @@ FW_VERSIONS = {
b'\x03312N6000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00',
b'\x03312N6100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00',
b'\x03312N6100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00',
b'\x03312N6200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00',
b'\x03312N6200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00',
b'\x02312K4000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02312U5000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1362,6 +1363,7 @@ FW_VERSIONS = {
],
(Ecu.engine, 0x700, None): [
b'\x01896634AA0000\x00\x00\x00\x00',
b'\x01896634AA0100\x00\x00\x00\x00',
b'\x01896634AA1000\x00\x00\x00\x00',
b'\x01896634A88000\x00\x00\x00\x00',
b'\x01896634A89000\x00\x00\x00\x00',

@ -76,7 +76,7 @@ class CarController:
stopping = actuators.longControlState == LongCtrlState.stopping
starting = actuators.longControlState == LongCtrlState.starting
can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, CS.acc_type, CC.longActive, accel,
acc_control, stopping, starting, CS.out.cruiseState.standstill))
acc_control, stopping, starting, CS.esp_hold_confirmation))
# **** HUD Controls ***************************************************** #

@ -110,20 +110,21 @@ class CarState(CarStateBase):
ret.cruiseState.available = True
ret.cruiseState.enabled = False
elif pt_cp.vl["TSK_06"]["TSK_Status"] in (3, 4, 5):
# ACC okay and enabled, currently regulating speed (3) or driver accel override (4) or overrun coast-down (5)
# ACC okay and enabled, currently regulating speed (3) or driver accel override (4) or brake only (5)
ret.cruiseState.available = True
ret.cruiseState.enabled = True
else:
# ACC okay but disabled (1), or a radar visibility or other fault/disruption (6 or 7)
ret.cruiseState.available = False
ret.cruiseState.enabled = False
ret.cruiseState.standstill = bool(pt_cp.vl["ESP_21"]["ESP_Haltebestaetigung"])
self.esp_hold_confirmation = bool(pt_cp.vl["ESP_21"]["ESP_Haltebestaetigung"])
ret.cruiseState.standstill = self.CP.pcmCruise and self.esp_hold_confirmation
ret.accFaulted = pt_cp.vl["TSK_06"]["TSK_Status"] in (6, 7)
# Update ACC setpoint. When the setpoint is zero or there's an error, the
# radar sends a set-speed of ~90.69 m/s / 203mph.
if self.CP.pcmCruise:
ret.cruiseState.speed = ext_cp.vl["ACC_02"]["ACC_Wunschgeschw"] * CV.KPH_TO_MS
ret.cruiseState.speed = ext_cp.vl["ACC_02"]["ACC_Wunschgeschw_02"] * CV.KPH_TO_MS
if ret.cruiseState.speed > 90:
ret.cruiseState.speed = 0
@ -478,7 +479,7 @@ class CarState(CarStateBase):
class MqbExtraSignals:
# Additional signal and message lists for optional or bus-portable controllers
fwd_radar_signals = [
("ACC_Wunschgeschw", "ACC_02"), # ACC set speed
("ACC_Wunschgeschw_02", "ACC_02"), # ACC set speed
("AWV2_Freigabe", "ACC_10"), # FCW brake jerk release
("ANB_Teilbremsung_Freigabe", "ACC_10"), # AEB partial braking release
("ANB_Zielbremsung_Freigabe", "ACC_10"), # AEB target braking release

@ -6,6 +6,7 @@ from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness,
from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
@ -127,6 +128,13 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1230 + STD_CARGO_KG
ret.wheelbase = 2.55
elif candidate == CAR.SHARAN_MK2:
ret.mass = 1639 + STD_CARGO_KG
ret.wheelbase = 2.92
ret.minEnableSpeed = 30 * CV.KPH_TO_MS
ret.minSteerSpeed = 50 * CV.KPH_TO_MS
ret.steerActuatorDelay = 0.2
elif candidate == CAR.TAOS_MK1:
ret.mass = 1498 + STD_CARGO_KG
ret.wheelbase = 2.69
@ -211,7 +219,8 @@ class CarInterface(CarInterfaceBase):
ret = self.CS.update(self.cp, self.cp_cam, self.cp_ext, self.CP.transmissionType)
events = self.create_common_events(ret, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic],
pcm_enable=not self.CS.CP.openpilotLongitudinalControl)
pcm_enable=not self.CS.CP.openpilotLongitudinalControl,
enable_buttons=(ButtonType.setCruise, ButtonType.resumeCruise))
# Low speed steer alert hysteresis logic
if self.CP.minSteerSpeed > 0. and ret.vEgo < (self.CP.minSteerSpeed + 1.):

@ -59,7 +59,7 @@ def acc_hud_status_value(main_switch_on, acc_faulted, long_active):
return hud_status
def create_acc_accel_control(packer, bus, acc_type, enabled, accel, acc_control, stopping, starting, standstill):
def create_acc_accel_control(packer, bus, acc_type, enabled, accel, acc_control, stopping, starting, esp_hold):
commands = []
values = {

@ -116,6 +116,7 @@ class CAR:
PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants
PASSAT_NMS = "VOLKSWAGEN PASSAT NMS" # Chassis A3, North America/China/Mideast NMS Passat, incl. facelift
POLO_MK6 = "VOLKSWAGEN POLO 6TH GEN" # Chassis AW, Mk6 VW Polo
SHARAN_MK2 = "VOLKSWAGEN SHARAN 2ND GEN" # Chassis 7N, Mk2 Volkswagen Sharan and SEAT Alhambra
TAOS_MK1 = "VOLKSWAGEN TAOS 1ST GEN" # Chassis B2, Mk1 VW Taos and Tharu
TCROSS_MK1 = "VOLKSWAGEN T-CROSS 1ST GEN" # Chassis C1, Mk1 VW T-Cross SWB and LWB variants
TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants
@ -135,7 +136,7 @@ class CAR:
SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants
PQ_CARS = {CAR.PASSAT_NMS}
PQ_CARS = {CAR.PASSAT_NMS, CAR.SHARAN_MK2}
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("vw_mqb_2010", None))
@ -206,6 +207,10 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
VWCarInfo("Volkswagen Polo 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
VWCarInfo("Volkswagen Polo GTI 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
],
CAR.SHARAN_MK2: [
VWCarInfo("Volkswagen Sharan 2018-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
VWCarInfo("SEAT Alhambra 2018-20", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
],
CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
CAR.TIGUAN_MK2: VWCarInfo("Volkswagen Tiguan 2019-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
@ -236,6 +241,7 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
],
}
# All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars
# with a manual trans won't return transmission firmware, but all other cars will.
#
@ -625,6 +631,18 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
CAR.SHARAN_MK2: {
# TODO: Sharan Mk2 EPS and DQ250 auto trans both require KWP2000 support for fingerprinting
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906016HE\xf1\x894635',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x877N0959655D \xf1\x890016\xf1\x82\x0801100705----10--',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0153',
],
},
CAR.TAOS_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027NJ\xf1\x891445',

@ -82,30 +82,30 @@ class Controls:
self.log_sock = messaging.sub_sock('androidLog')
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
get_one_can(self.can_sock)
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'])
else:
self.CI, self.CP = CI, CI.CP
params = Params()
self.joystick_mode = params.get_bool("JoystickDebugMode") or (self.CP.notCar and sm is None)
joystick_packet = ['testJoystick'] if self.joystick_mode else []
self.sm = sm
if self.sm is None:
ignore = []
ignore = ['testJoystick']
if SIMULATION:
ignore += ['driverCameraState', 'managerState']
if params.get_bool('WideCameraOnly'):
ignore += ['roadCameraState']
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters'] + self.camera_packets + joystick_packet,
ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan'])
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'testJoystick'] + self.camera_packets,
ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan', 'testJoystick'])
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
get_one_can(self.can_sock)
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], num_pandas)
else:
self.CI, self.CP = CI, CI.CP
self.joystick_mode = params.get_bool("JoystickDebugMode") or (self.CP.notCar and sm is None)
# set alternative experiences from parameters
self.disengage_on_accelerator = params.get_bool("DisengageOnAccelerator")
@ -705,6 +705,7 @@ class Controls:
if len(angular_rate_value) > 2:
CC.angularVelocity = angular_rate_value
CC.cruiseControl.override = self.enabled and not CC.longActive and self.CP.openpilotLongitudinalControl
CC.cruiseControl.cancel = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
if self.joystick_mode and self.sm.rcv_frame['testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
CC.cruiseControl.cancel = True

@ -811,6 +811,10 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
ET.NO_ENTRY: NoEntryAlert("Cruise Faulted"),
},
EventName.gmAccFaultedTemp: {
ET.NO_ENTRY: NoEntryAlert("Cruise Temporarily Faulted"),
},
EventName.controlsMismatch: {
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"),
ET.NO_ENTRY: NoEntryAlert("Controls Mismatch"),

@ -17,6 +17,8 @@ from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
# friction in the steering wheel that needs to be overcome to
# move it at all, this is compensated for too.
LOW_SPEED_FACTOR = 100
class LatControlTorque(LatControl):
def __init__(self, CP, CI):
@ -55,13 +57,15 @@ class LatControlTorque(LatControl):
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
low_speed_factor = interp(CS.vEgo, [0, 10, 20], [500, 500, 200])
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
setpoint = desired_lateral_accel + LOW_SPEED_FACTOR * desired_curvature
measurement = actual_lateral_accel + LOW_SPEED_FACTOR * actual_curvature
error = setpoint - measurement
gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY
pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error, lateral_accel_deadzone, friction_compensation=False)
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params, error, lateral_accel_deadzone, friction_compensation=True)
pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error,
lateral_accel_deadzone, friction_compensation=False)
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params,
desired_lateral_accel - actual_lateral_accel,
lateral_accel_deadzone, friction_compensation=True)
freeze_integrator = steer_limited or CS.steeringPressed or CS.vEgo < 5
output_torque = self.pid.update(pid_log.error,

@ -19,7 +19,7 @@ X_DIM = 4
P_DIM = 2
N = 16
COST_E_DIM = 3
COST_DIM = COST_E_DIM + 1
COST_DIM = COST_E_DIM + 2
SPEED_OFFSET = 10.0
MODEL_NAME = 'lat'
ACADOS_SOLVER_TYPE = 'SQP_RTI'
@ -89,13 +89,21 @@ def gen_lat_ocp():
ocp.cost.yref = np.zeros((COST_DIM, ))
ocp.cost.yref_e = np.zeros((COST_E_DIM, ))
# Add offset to smooth out low speed control
# TODO unclear if this right solution long term
v_ego_offset = v_ego + SPEED_OFFSET
# TODO there are two costs on psi_rate_ego_dot, one
# is correlated to jerk the other to steering wheel movement
# the steering wheel movement cost is added to prevent excessive
# wheel movements
ocp.model.cost_y_expr = vertcat(y_ego,
((v_ego + SPEED_OFFSET) * psi_ego),
((v_ego + SPEED_OFFSET) * psi_rate_ego),
((v_ego + SPEED_OFFSET) * psi_rate_ego_dot))
v_ego_offset * psi_ego,
v_ego_offset * psi_rate_ego,
v_ego_offset * psi_rate_ego_dot,
psi_rate_ego_dot / (v_ego + 0.1))
ocp.model.cost_y_expr_e = vertcat(y_ego,
((v_ego + SPEED_OFFSET) * psi_ego),
((v_ego + SPEED_OFFSET) * psi_rate_ego))
v_ego_offset * psi_ego,
v_ego_offset * psi_rate_ego)
# set constraints
ocp.constraints.constr_type = 'BGH'
@ -144,8 +152,12 @@ class LateralMpc():
self.solve_time = 0.0
self.cost = 0
def set_weights(self, path_weight, heading_weight, yaw_rate_weight, yaw_accel_cost):
W = np.asfortranarray(np.diag([path_weight, heading_weight, yaw_rate_weight, yaw_accel_cost]))
def set_weights(self, path_weight, heading_weight,
lat_accel_weight, lat_jerk_weight,
steering_rate_weight):
W = np.asfortranarray(np.diag([path_weight, heading_weight,
lat_accel_weight, lat_jerk_weight,
steering_rate_weight]))
for i in range(N):
self.solver.cost_set(i, 'W', W)
self.solver.cost_set(N, 'W', W[:COST_E_DIM,:COST_E_DIM])

@ -17,8 +17,13 @@ PATH_COST = 1.0
LATERAL_MOTION_COST = 0.11
LATERAL_ACCEL_COST = 0.0
LATERAL_JERK_COST = 0.05
# Extreme steering rate is unpleasant, even
# when it does not cause bad jerk.
# TODO this cost should be lowered when low
# speed lateral control is stable on all cars
STEERING_RATE_COST = 800.0
MIN_SPEED = 1.5
MIN_SPEED = .3
class LateralPlanner:
@ -67,7 +72,8 @@ class LateralPlanner:
d_path_xyz = self.path_xyz
self.lat_mpc.set_weights(PATH_COST, LATERAL_MOTION_COST,
LATERAL_ACCEL_COST, LATERAL_JERK_COST)
LATERAL_ACCEL_COST, LATERAL_JERK_COST,
STEERING_RATE_COST)
y_pts = np.interp(self.v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1])
heading_pts = np.interp(self.v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw)

@ -10,7 +10,7 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur
if lat_mpc is None:
lat_mpc = LateralMpc()
lat_mpc.set_weights(1., 1., 0.0, 1.)
lat_mpc.set_weights(1., .1, 0.0, .05, 800)
y_pts = poly_shift * np.ones(LAT_MPC_N + 1)
heading_pts = np.zeros(LAT_MPC_N + 1)
@ -77,9 +77,9 @@ class TestLateralMpc(unittest.TestCase):
def test_switch_convergence(self):
lat_mpc = LateralMpc()
sol = run_mpc(lat_mpc=lat_mpc, poly_shift=30.0, v_ref=7.0)
sol = run_mpc(lat_mpc=lat_mpc, poly_shift=3.0, v_ref=7.0)
right_psi_deg = np.degrees(sol[:,2])
sol = run_mpc(lat_mpc=lat_mpc, poly_shift=-30.0, v_ref=7.0)
sol = run_mpc(lat_mpc=lat_mpc, poly_shift=-3.0, v_ref=7.0)
left_psi_deg = np.degrees(sol[:,2])
np.testing.assert_almost_equal(right_psi_deg, -left_psi_deg, decimal=3)

@ -94,6 +94,9 @@ class TestStartup(unittest.TestCase):
time.sleep(2) # wait for controlsd to be ready
pm.send('can', can_list_to_can_capnp([[0, 0, b"", 0]]))
time.sleep(0.1)
msg = messaging.new_message('pandaStates', 1)
msg.pandaStates[0].pandaType = log.PandaState.PandaType.uno
pm.send('pandaStates', msg)

@ -19,7 +19,7 @@ if __name__ == "__main__":
for m in msgs:
ts[s].append(m.logMonoTime / 1e6)
if len(ts[s]):
if len(ts[s]) > 2:
d = np.diff(ts[s])
print(f"{s:25} {np.mean(d):.2f} {np.std(d):.2f} {np.max(d):.2f} {np.min(d):.2f}")
time.sleep(1)

@ -20,6 +20,11 @@ const double VALID_POS_STD = 50.0; // m
const double MAX_RESET_TRACKER = 5.0;
const double SANE_GPS_UNCERTAINTY = 1500.0; // m
// TODO: GPS sensor time offsets are empirically calculated
// They should be replaced with synced time from a real clock
const double GPS_LOCATION_SENSOR_TIME_OFFSET = 0.630; // s
const double GPS_LOCATION_EXTERNAL_SENSOR_TIME_OFFSET = 0.095; // s
static VectorXd floatlist2vector(const capnp::List<float, capnp::Kind::PRIMITIVE>::Reader& floatlist) {
VectorXd res(floatlist.size());
for (int i = 0; i < floatlist.size(); i++) {
@ -257,7 +262,7 @@ void Localizer::input_fake_gps_observations(double current_time) {
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R });
}
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log) {
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) {
// ignore the message if the fix is invalid
bool gps_invalid_flag = (log.getFlags() % 2 == 0);
bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY);
@ -265,13 +270,15 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK));
bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK);
if (gps_invalid_flag || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane){
if (gps_invalid_flag || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane) {
this->determine_gps_mode(current_time);
return;
}
double sensor_time = current_time - sensor_time_offset;
// Process message
this->last_gps_fix = current_time;
this->last_gps_fix = sensor_time;
this->gps_mode = true;
Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() };
this->converter = std::make_unique<LocalCoord>(geodetic);
@ -300,14 +307,14 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
if (ecef_vel.norm() > 5.0 && orientation_error.norm() > 1.0) {
LOGE("Locationd vs ubloxLocation orientation difference too large, kalman reset");
this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R);
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat });
this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat });
} else if (gps_est_error > 100.0) {
LOGE("Locationd vs ubloxLocation position difference too large, kalman reset");
this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R);
}
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R });
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R });
this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R });
this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R });
}
void Localizer::handle_car_state(double current_time, const cereal::CarState::Reader& log) {
@ -443,9 +450,9 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) {
} else if (log.isGyroscope()) {
this->handle_sensor(t, log.getGyroscope());
} else if (log.isGpsLocation()) {
this->handle_gps(t, log.getGpsLocation());
this->handle_gps(t, log.getGpsLocation(), GPS_LOCATION_SENSOR_TIME_OFFSET);
} else if (log.isGpsLocationExternal()) {
this->handle_gps(t, log.getGpsLocationExternal());
this->handle_gps(t, log.getGpsLocationExternal(), GPS_LOCATION_EXTERNAL_SENSOR_TIME_OFFSET);
} else if (log.isCarState()) {
this->handle_car_state(t, log.getCarState());
} else if (log.isCameraOdometry()) {
@ -497,9 +504,8 @@ int Localizer::locationd_thread() {
} else {
gps_location_socket = "gpsLocation";
}
const std::initializer_list<const char *> service_list = {gps_location_socket,
"cameraOdometry", "liveCalibration", "carState", "carParams",
"accelerometer", "gyroscope"};
const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration",
"carState", "carParams", "accelerometer", "gyroscope"};
PubMaster pm({"liveLocationKalman"});
// TODO: remove carParams once we're always sending at 100Hz

@ -46,7 +46,7 @@ public:
void handle_msg_bytes(const char *data, const size_t size);
void handle_msg(const cereal::Event::Reader& log);
void handle_sensor(double current_time, const cereal::SensorEventData::Reader& log);
void handle_gps(double current_time, const cereal::GpsLocationData::Reader& log);
void handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset);
void handle_car_state(double current_time, const cereal::CarState::Reader& log);
void handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log);
void handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log);

@ -44,7 +44,7 @@ LiveKalman::LiveKalman() {
// init filter
this->filter = std::make_shared<EKFSym>(this->name, get_mapmat(this->Q), get_mapvec(this->initial_x),
get_mapmat(initial_P), this->dim_state, this->dim_state_err, 0, 0, 0, std::vector<int>(),
std::vector<int>{3}, std::vector<std::string>(), 0.2);
std::vector<int>{3}, std::vector<std::string>(), 0.8);
}
void LiveKalman::init_state(VectorXd& state, VectorXd& covs_diag, double filter_time) {

@ -53,6 +53,7 @@ class TestLocationdProc(unittest.TestCase):
msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0]
msg.gpsLocationExternal.latitude = self.lat
msg.gpsLocationExternal.longitude = self.lon
msg.gpsLocationExternal.unixTimestampMillis = t * 1e6
msg.gpsLocationExternal.altitude = self.alt
elif name == 'cameraOdometry':
msg.cameraOdometry.rot = [0.0, 0.0, 0.0]

@ -55,7 +55,7 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct
int bytes_count = 0;
// extract the message
capnp::FlatArrayMessageReader cmsg(kj::ArrayPtr<capnp::word>((capnp::word *)msg->getData(), msg->getSize()));
capnp::FlatArrayMessageReader cmsg(kj::ArrayPtr<capnp::word>((capnp::word *)msg->getData(), msg->getSize() / sizeof(capnp::word)));
auto event = cmsg.getRoot<cereal::Event>();
auto edata = (name == "driverEncodeData") ? event.getDriverEncodeData() :
((name == "wideRoadEncodeData") ? event.getWideRoadEncodeData() :

@ -1,2 +1,2 @@
ee8f830b-d6a1-42ef-9b1b-50fd0b2faae4
cac8f7b69d420506707ff7a19d573d5011ef2533
d1124586-761e-4e18-a771-6b5ef35124fe
6fec774f513a19e44d4316e46ad38277197d45ea

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:932e589e5cce66e5d9f48492426a33c74cd7f352a870d3ddafcede3e9156f30d
size 9157561
oid sha256:517262fa9f1ad3cc8049ad3722903f40356d87ea423ee5cf011226fb6cfc3d5b
size 16072278

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3587976a8b7e3be274fa86c2e2233e3e464cad713f5077c4394cd1ddd3c7c6c5
size 2636965
oid sha256:64b94659226a1e3c6594a13c2e5d029465d5803a5c3005121ec7217acdbbef20
size 4443461

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4d37af666344af6bb218e0b939b1152ad3784c15ac79e37bcf0124643c8286a
size 58539563
oid sha256:022a830c39267f378f45204682060c93e3aa304bbd8cfa6b2dfe4fa8f419102d
size 56972617

@ -102,6 +102,7 @@ void get_testframe(int index, std::unique_ptr<zdl::DlSystem::ITensor> &input) {
fread(frame_buffer, length, 1, pFile);
// std::cout << *(frame_buffer+length/4-1) << std::endl;
std::copy(frame_buffer, frame_buffer+(length/4), input->begin());
fclose(pFile);
}
void SaveITensor(const std::string& path, const zdl::DlSystem::ITensor* tensor)

@ -29,10 +29,10 @@ class DRIVER_MONITOR_SETTINGS():
self._FACE_THRESHOLD = 0.7
self._EYE_THRESHOLD = 0.65
self._SG_THRESHOLD = 0.9
self._BLINK_THRESHOLD = 0.87
self._BLINK_THRESHOLD = 0.895
self._EE_THRESH11 = 0.75
self._EE_THRESH12 = 3.25
self._EE_THRESH11 = 0.275
self._EE_THRESH12 = 3.0
self._EE_THRESH21 = 0.01
self._EE_THRESH22 = 0.35
@ -207,11 +207,11 @@ class DriverStatus():
ee1_dist = self.eev1 > self.ee1_offseter.filtered_stat.M * self.settings._EE_THRESH12
else:
ee1_dist = self.eev1 > self.settings._EE_THRESH11
if self.ee2_calibrated:
ee2_dist = self.eev2 < self.ee2_offseter.filtered_stat.M * self.settings._EE_THRESH22
else:
ee2_dist = self.eev2 < self.settings._EE_THRESH21
if ee1_dist or ee2_dist:
# if self.ee2_calibrated:
# ee2_dist = self.eev2 < self.ee2_offseter.filtered_stat.M * self.settings._EE_THRESH22
# else:
# ee2_dist = self.eev2 < self.settings._EE_THRESH21
if ee1_dist:
distracted_types.append(DistractedType.DISTRACTED_E2E)
return distracted_types
@ -257,12 +257,11 @@ class DriverStatus():
self.pose.low_std = model_std_max < self.settings._POSESTD_THRESHOLD
self.blink.left_blink = driver_data.leftBlinkProb * (driver_data.leftEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
self.blink.right_blink = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
self.eev1 = driver_data.notReadyProb[1]
self.eev1 = driver_data.notReadyProb[0]
self.eev2 = driver_data.readyProb[0]
self.distracted_types = self._get_distracted_types()
self.driver_distracted = (DistractedType.DISTRACTED_POSE in self.distracted_types or
DistractedType.DISTRACTED_BLINK in self.distracted_types) and \
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types or DistractedType.DISTRACTED_BLINK in self.distracted_types) and \
driver_data.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std
self.driver_distraction_filter.update(self.driver_distracted)

@ -11,6 +11,8 @@ if arch in ['larch64', 'x86_64']:
rpath = [Dir(f"#third_party/mapbox-gl-native-qt/{arch}").srcnode().abspath]
qt_env["RPATH"] += rpath
style_path = File("style.json").abspath
qt_env['CXXFLAGS'].append(f'-DSTYLE_PATH=\\"{style_path}\\"')
qt_libs = ["qt_widgets", "qt_util", "qmapboxgl"] + base_libs
nav_src = ["main.cc", "map_renderer.cc"]

@ -1,14 +1,16 @@
#include "selfdrive/navd/map_renderer.h"
#include <string>
#include <QApplication>
#include <QBuffer>
#include <QDebug>
#include "common/util.h"
#include "common/timing.h"
#include "selfdrive/ui/qt/maps/map_helpers.h"
const float ZOOM = 13.5; // Don't go below 13 or features will start to disappear
const int WIDTH = 256;
const float DEFAULT_ZOOM = 13.5; // Don't go below 13 or features will start to disappear
const int WIDTH = 512;
const int HEIGHT = WIDTH;
const int NUM_VIPC_BUFFERS = 4;
@ -35,9 +37,10 @@ MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool online) : m_set
QOpenGLFramebufferObjectFormat fbo_format;
fbo.reset(new QOpenGLFramebufferObject(WIDTH, HEIGHT, fbo_format));
std::string style = util::read_file(STYLE_PATH);
m_map.reset(new QMapboxGL(nullptr, m_settings, fbo->size(), 1));
m_map->setCoordinateZoom(QMapbox::Coordinate(0, 0), ZOOM);
m_map->setStyleUrl("mapbox://styles/commaai/ckvmksrpd4n0a14pfdo5heqzr");
m_map->setCoordinateZoom(QMapbox::Coordinate(0, 0), DEFAULT_ZOOM);
m_map->setStyleJson(style.c_str());
m_map->createRenderer();
m_map->resize(fbo->size());
@ -82,6 +85,15 @@ void MapRenderer::msgUpdate() {
}
}
void MapRenderer::updateZoom(float zoom) {
if (m_map.isNull()) {
return;
}
m_map->setZoom(zoom);
update();
}
void MapRenderer::updatePosition(QMapbox::Coordinate position, float bearing) {
if (m_map.isNull()) {
return;
@ -185,7 +197,7 @@ void MapRenderer::initLayers() {
nav["source"] = "navSource";
m_map->addLayer(nav, "road-intersection");
m_map->setPaintProperty("navLayer", "line-color", QColor("grey"));
m_map->setPaintProperty("navLayer", "line-width", 3);
m_map->setPaintProperty("navLayer", "line-width", 5);
m_map->setLayoutProperty("navLayer", "line-cap", "round");
}
}
@ -210,6 +222,11 @@ extern "C" {
return new MapRenderer(settings, false);
}
void map_renderer_update_zoom(MapRenderer *inst, float zoom) {
inst->updateZoom(zoom);
QApplication::processEvents();
}
void map_renderer_update_position(MapRenderer *inst, float lat, float lon, float bearing) {
inst->updatePosition({lat, lon}, bearing);
QApplication::processEvents();

@ -47,6 +47,7 @@ private:
QTimer* timer;
public slots:
void updateZoom(float zoom);
void updatePosition(QMapbox::Coordinate position, float bearing);
void updateRoute(QList<QGeoCoordinate> coordinates);
void msgUpdate();

@ -9,7 +9,7 @@ from cffi import FFI
from common.ffi_wrapper import suffix
from common.basedir import BASEDIR
HEIGHT = WIDTH = 256
HEIGHT = WIDTH = 512
def get_ffi():
@ -18,6 +18,7 @@ def get_ffi():
ffi = FFI()
ffi.cdef("""
void* map_renderer_init(char *maps_host, char *token);
void map_renderer_update_zoom(void *inst, float zoom);
void map_renderer_update_position(void *inst, float lat, float lon, float bearing);
void map_renderer_update_route(void *inst, char *polyline);
void map_renderer_update(void *inst);

File diff suppressed because one or more lines are too long

@ -7,7 +7,7 @@ import struct
import requests
import urllib.parse
from datetime import datetime
from typing import List, Optional
from typing import List, Optional, Tuple
from cereal import messaging
from common.params import Params
@ -34,7 +34,6 @@ def set_power(enabled: bool) -> None:
gpio_set(GPIO.UBLOX_PWR_EN, enabled)
gpio_set(GPIO.UBLOX_RST_N, enabled)
def add_ubx_checksum(msg: bytes) -> bytes:
A = B = 0
for b in msg[2:]:
@ -115,35 +114,70 @@ class TTYPigeon():
raise TimeoutError('No response from ublox')
time.sleep(0.001)
def reset_device(self) -> bool:
# deleting the backup does not always work on first try (mostly on second try)
for _ in range(5):
# device cold start
self.send(b"\xb5\x62\x06\x04\x04\x00\xff\xff\x00\x00\x0c\x5d")
time.sleep(1) # wait for cold start
init_baudrate(self)
# clear configuration
self.send_with_ack(b"\xb5\x62\x06\x09\x0d\x00\x00\x00\x1f\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x17\x71\x5b")
# clear flash memory (almanac backup)
self.send_with_ack(b"\xB5\x62\x09\x14\x04\x00\x01\x00\x00\x00\x22\xf0")
# try restoring backup to verify it got deleted
self.send(b"\xB5\x62\x09\x14\x00\x00\x1D\x60")
# 1: failed to restore, 2: could restore, 3: no backup
status = self.wait_for_backup_restore_status()
if status == 1 or status == 3:
return True
return False
def init_baudrate(pigeon: TTYPigeon):
# ublox default setting on startup is 9600 baudrate
pigeon.set_baud(9600)
# $PUBX,41,1,0007,0003,460800,0*15\r\n
pigeon.send(b"\x24\x50\x55\x42\x58\x2C\x34\x31\x2C\x31\x2C\x30\x30\x30\x37\x2C\x30\x30\x30\x33\x2C\x34\x36\x30\x38\x30\x30\x2C\x30\x2A\x31\x35\x0D\x0A")
time.sleep(0.1)
pigeon.set_baud(460800)
def initialize_pigeon(pigeon: TTYPigeon) -> bool:
# try initializing a few times
for _ in range(10):
try:
pigeon.set_baud(9600)
# up baud rate
pigeon.send(b"\x24\x50\x55\x42\x58\x2C\x34\x31\x2C\x31\x2C\x30\x30\x30\x37\x2C\x30\x30\x30\x33\x2C\x34\x36\x30\x38\x30\x30\x2C\x30\x2A\x31\x35\x0D\x0A")
time.sleep(0.1)
pigeon.set_baud(460800)
# other configuration messages
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x00\x00\x06\x18")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x01\x00\x01\x08\x22")
pigeon.send_with_ack(b"\xB5\x62\x06\x00\x01\x00\x03\x0A\x24")
# setup port config
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x00\x00\x06\x18")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x01\x00\x01\x08\x22")
pigeon.send_with_ack(b"\xb5\x62\x06\x00\x01\x00\x03\x0A\x24")
# UBX-CFG-RATE (0x06 0x08)
pigeon.send_with_ack(b"\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10")
# UBX-CFG-NAV5 (0x06 0x24)
pigeon.send_with_ack(b"\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63")
# UBX-CFG-ODO (0x06 0x1E)
pigeon.send_with_ack(b"\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37")
pigeon.send_with_ack(b"\xB5\x62\x06\x39\x08\x00\xFF\xAD\x62\xAD\x1E\x63\x00\x00\x83\x0C")
pigeon.send_with_ack(b"\xB5\x62\x06\x23\x28\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x24")
# UBX-CFG-NAV5 (0x06 0x24)
pigeon.send_with_ack(b"\xB5\x62\x06\x24\x00\x00\x2A\x84")
pigeon.send_with_ack(b"\xB5\x62\x06\x23\x00\x00\x29\x81")
pigeon.send_with_ack(b"\xB5\x62\x06\x1E\x00\x00\x24\x72")
pigeon.send_with_ack(b"\xB5\x62\x06\x39\x00\x00\x3F\xC3")
# UBX-CFG-MSG (set message rate)
pigeon.send_with_ack(b"\xB5\x62\x06\x01\x03\x00\x01\x07\x01\x13\x51")
pigeon.send_with_ack(b"\xB5\x62\x06\x01\x03\x00\x02\x15\x01\x22\x70")
pigeon.send_with_ack(b"\xB5\x62\x06\x01\x03\x00\x02\x13\x01\x20\x6C")
@ -224,13 +258,11 @@ def deinitialize_and_exit(pigeon: Optional[TTYPigeon]):
set_power(False)
sys.exit(0)
def main():
assert TICI, "unsupported hardware for pigeond"
def create_pigeon() -> Tuple[TTYPigeon, messaging.PubMaster]:
pigeon = None
# register exit handler
pigeon = None
signal.signal(signal.SIGINT, lambda sig, frame: deinitialize_and_exit(pigeon))
pm = messaging.PubMaster(['ubloxRaw'])
# power cycle ublox
@ -240,15 +272,20 @@ def main():
time.sleep(0.5)
pigeon = TTYPigeon()
r = initialize_pigeon(pigeon)
Params().put_bool("UbloxAvailable", r)
return pigeon, pm
# start receiving data
while True:
def run_receiving(pigeon: TTYPigeon, pm: messaging.PubMaster, duration: int = 0):
start_time = time.monotonic()
def end_condition():
return True if duration == 0 else time.monotonic() - start_time < duration
while end_condition():
dat = pigeon.receive()
if len(dat) > 0:
if dat[0] == 0x00:
cloudlog.warning("received invalid data from ublox, re-initing!")
init_baudrate(pigeon)
initialize_pigeon(pigeon)
continue
@ -260,5 +297,17 @@ def main():
# prevent locking up a CPU core if ublox disconnects
time.sleep(0.001)
def main():
assert TICI, "unsupported hardware for pigeond"
pigeon, pm = create_pigeon()
init_baudrate(pigeon)
r = initialize_pigeon(pigeon)
Params().put_bool("UbloxAvailable", r)
# start receiving data
run_receiving(pigeon, pm)
if __name__ == "__main__":
main()

@ -179,7 +179,9 @@ def main() -> NoReturn:
while 1:
opcode, payload = diag.recv()
assert opcode == DIAG_LOG_F
if opcode != DIAG_LOG_F:
cloudlog.error(f"Unhandled opcode: {opcode}")
continue
(pending_msgs, log_outer_length), inner_log_packet = unpack_from('<BH', payload), payload[calcsize('<BH'):]
if pending_msgs > 0:

@ -1 +1 @@
c171250d2cc013b3eca1cda4fb62f3d0dda28d4d
865885fc49b2766326568e5cc7ec06be8a3f6fad

@ -1 +1 @@
f636c68e2b4ed88d3731930cf15b6dee984eb6dd
6bb7d8baae51d88dd61f0baf561e386664ddd266

@ -30,10 +30,11 @@ def replay_panda_states(s, msgs):
rk = Ratekeeper(service_list[s].frequency, print_delay_threshold=None)
smsgs = [m for m in msgs if m.which() in ['pandaStates', 'pandaStateDEPRECATED']]
# TODO: new safety params from flags, remove after getting new routes for Toyota
# TODO: safety param migration should be handled automatically
safety_param_migration = {
"TOYOTA PRIUS 2017": EPS_SCALE["TOYOTA PRIUS 2017"] | Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL,
"TOYOTA RAV4 2017": EPS_SCALE["TOYOTA RAV4 2017"] | Panda.FLAG_TOYOTA_ALT_BRAKE,
"KIA EV6 2022": Panda.FLAG_HYUNDAI_EV_GAS | Panda.FLAG_HYUNDAI_CANFD_HDA2,
}
# Migrate safety param base on carState
@ -56,6 +57,7 @@ def replay_panda_states(s, msgs):
pm.send(s, new_m)
else:
new_m = m.as_builder()
new_m.pandaStates[-1].safetyParam = safety_param
new_m.logMonoTime = int(sec_since_boot() * 1e9)
pm.send(s, new_m)
@ -222,13 +224,14 @@ def migrate_sensorEvents(lr, old_logtime=False):
m_dat.sensor = evt.sensor
m_dat.type = evt.type
m_dat.source = evt.source
if old_logtime:
m_dat.timestamp = evt.timestamp
setattr(m_dat, evt.which(), getattr(evt, evt.which()))
all_msgs.append(m.as_reader())
return all_msgs
def regen_segment(lr, frs=None, outdir=FAKEDATA, disable_tqdm=False):
lr = migrate_carparams(list(lr))
lr = migrate_sensorEvents(list(lr))

@ -40,7 +40,7 @@ source_segments = [
segments = [
("BODY", "regenFA002A80700|2022-09-27--15-37-02--0"),
("HYUNDAI", "regenBE53A59065B|2022-09-27--16-52-03--0"),
("HYUNDAI2", "regen11AA43BCA5F|2022-09-27--15-39-54--0"),
("HYUNDAI2", "regenFA8B5CA9840|2022-10-12--21-47-06--0"),
("TOYOTA", "regen929C5790007|2022-09-27--16-27-47--0"),
("TOYOTA2", "regenEA3950D7F22|2022-09-27--15-43-24--0"),
("TOYOTA3", "regen89026F6BD8D|2022-09-27--15-45-37--0"),

@ -120,8 +120,8 @@ class TestOnroad(unittest.TestCase):
if "DEBUG" in os.environ:
segs = filter(lambda x: os.path.exists(os.path.join(x, "rlog")), Path(ROOT).iterdir())
segs = sorted(segs, key=lambda x: x.stat().st_mtime)
print(segs[-1])
cls.lr = list(LogReader(os.path.join(segs[-1], "rlog")))
print(segs[-2])
cls.lr = list(LogReader(os.path.join(segs[-2], "rlog")))
return
# setup env
@ -187,6 +187,25 @@ class TestOnroad(unittest.TestCase):
big_logs = [f for f, n in cnt.most_common(3) if n / sum(cnt.values()) > 30.]
self.assertEqual(len(big_logs), 0, f"Log spam: {big_logs}")
def test_ui_timings(self):
result = "\n"
result += "------------------------------------------------\n"
result += "-------------- UI Draw Timing ------------------\n"
result += "------------------------------------------------\n"
ts = [m.uiDebug.drawTimeMillis for m in self.lr if m.which() == 'uiDebug']
result += f"min {min(ts):.2f}ms\n"
result += f"max {max(ts):.2f}ms\n"
result += f"std {np.std(ts):.2f}ms\n"
result += f"mean {np.mean(ts):.2f}ms\n"
result += "------------------------------------------------\n"
print(result)
self.assertGreater(len(ts), 20*50, "insufficient samples")
self.assertLess(max(ts), 30.)
self.assertLess(np.mean(ts), 10.)
self.assertLess(np.std(ts), 5.)
def test_cpu_usage(self):
proclogs = [m for m in self.lr if m.which() == 'procLog']
self.assertGreater(len(proclogs), service_list['procLog'].frequency * 45, "insufficient samples")

@ -14,11 +14,16 @@ SOURCES = [
(_DATA_ACCOUNT_CI, "commadataci"),
]
DEST_KEY = azureutil.get_user_token(_DATA_ACCOUNT_CI, "openpilotci")
SOURCE_KEYS = [azureutil.get_user_token(account, bucket) for account, bucket in SOURCES]
SERVICE = BlockBlobService(_DATA_ACCOUNT_CI, sas_token=DEST_KEY)
def get_azure_keys():
dest_key = azureutil.get_user_token(_DATA_ACCOUNT_CI, "openpilotci")
source_keys = [azureutil.get_user_token(account, bucket) for account, bucket in SOURCES]
service = BlockBlobService(_DATA_ACCOUNT_CI, sas_token=dest_key)
return dest_key, source_keys, service
def upload_route(path, exclude_patterns=None):
dest_key, _, _ = get_azure_keys()
if exclude_patterns is None:
exclude_patterns = ['*/dcamera.hevc']
@ -29,27 +34,28 @@ def upload_route(path, exclude_patterns=None):
"azcopy",
"copy",
f"{path}/*",
f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{destpath}?{DEST_KEY}",
f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{destpath}?{dest_key}",
"--recursive=false",
"--overwrite=false",
] + [f"--exclude-pattern={p}" for p in exclude_patterns]
subprocess.check_call(cmd)
def sync_to_ci_public(route):
dest_key, source_keys, service = get_azure_keys()
key_prefix = route.replace('|', '/')
dongle_id = key_prefix.split('/')[0]
if next(azureutil.list_all_blobs(SERVICE, "openpilotci", prefix=key_prefix), None) is not None:
if next(azureutil.list_all_blobs(service, "openpilotci", prefix=key_prefix), None) is not None:
return True
print(f"Uploading {route}")
for (source_account, source_bucket), source_key in zip(SOURCES, SOURCE_KEYS):
for (source_account, source_bucket), source_key in zip(SOURCES, source_keys):
print(f"Trying {source_account}/{source_bucket}")
cmd = [
"azcopy",
"copy",
f"https://{source_account}.blob.core.windows.net/{source_bucket}/{key_prefix}?{source_key}",
f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{dongle_id}?{DEST_KEY}",
f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{dongle_id}?{dest_key}",
"--recursive=true",
"--overwrite=false",
"--exclude-pattern=*/dcamera.hevc",

@ -12,11 +12,11 @@ DriverViewWindow::DriverViewWindow(QWidget* parent) : QWidget(parent) {
layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
cameraView = new CameraViewWidget("camerad", VISION_STREAM_DRIVER, true, this);
cameraView = new CameraWidget("camerad", VISION_STREAM_DRIVER, true, this);
layout->addWidget(cameraView);
scene = new DriverViewScene(this);
connect(cameraView, &CameraViewWidget::vipcThreadFrameReceived, scene, &DriverViewScene::frameUpdated);
connect(cameraView, &CameraWidget::vipcThreadFrameReceived, scene, &DriverViewScene::frameUpdated);
layout->addWidget(scene);
layout->setCurrentWidget(scene);
}

@ -42,7 +42,7 @@ protected:
void mouseReleaseEvent(QMouseEvent* e) override;
private:
CameraViewWidget *cameraView;
CameraWidget *cameraView;
DriverViewScene *scene;
QStackedLayout *layout;
};

@ -368,7 +368,7 @@ void WifiManager::updateGsmSettings(bool roaming, QString apn, bool metered) {
changes = true;
}
int meteredInt = metered ? NM_METERED_NO : NM_METERED_UNKNOWN;
int meteredInt = metered ? NM_METERED_UNKNOWN : NM_METERED_NO;
if (settings.value("connection").value("metered").toInt() != meteredInt) {
qWarning() << "Changing connection.metered to" << meteredInt;
settings["connection"]["metered"] = meteredInt;

@ -18,7 +18,7 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
stacked_layout->setStackingMode(QStackedLayout::StackAll);
main_layout->addLayout(stacked_layout);
nvg = new NvgWindow(VISION_STREAM_ROAD, this);
nvg = new AnnotatedCameraWidget(VISION_STREAM_ROAD, this);
QWidget * split_wrapper = new QWidget;
split = new QHBoxLayout(split_wrapper);
@ -27,7 +27,7 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
split->addWidget(nvg);
if (getenv("DUAL_CAMERA_VIEW")) {
CameraViewWidget *arCam = new CameraViewWidget("camerad", VISION_STREAM_ROAD, true, this);
CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, true, this);
split->insertWidget(0, arCam);
}
@ -173,14 +173,15 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
}
}
// NvgWindow
NvgWindow::NvgWindow(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraViewWidget("camerad", type, true, parent) {
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) {
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"uiDebug"});
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size});
}
void NvgWindow::updateState(const UIState &s) {
void AnnotatedCameraWidget::updateState(const UIState &s) {
const int SET_SPEED_NA = 255;
const SubMaster &sm = *(s.sm);
@ -232,13 +233,13 @@ void NvgWindow::updateState(const UIState &s) {
}
if (s.scene.calibration_valid) {
CameraViewWidget::updateCalibration(s.scene.view_from_calib);
CameraWidget::updateCalibration(s.scene.view_from_calib);
} else {
CameraViewWidget::updateCalibration(DEFAULT_CALIBRATION);
CameraWidget::updateCalibration(DEFAULT_CALIBRATION);
}
}
void NvgWindow::drawHud(QPainter &p) {
void AnnotatedCameraWidget::drawHud(QPainter &p) {
p.save();
// Header gradient
@ -400,7 +401,11 @@ void NvgWindow::drawHud(QPainter &p) {
p.restore();
}
void NvgWindow::drawText(QPainter &p, int x, int y, const QString &text, int alpha) {
// Window that shows camera view and variety of
// info drawn on top
void AnnotatedCameraWidget::drawText(QPainter &p, int x, int y, const QString &text, int alpha) {
QRect real_rect = getTextRect(p, 0, text);
real_rect.moveCenter({x, y - real_rect.height() / 2});
@ -408,7 +413,7 @@ void NvgWindow::drawText(QPainter &p, int x, int y, const QString &text, int alp
p.drawText(real_rect.x(), real_rect.bottom(), text);
}
void NvgWindow::drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity) {
void AnnotatedCameraWidget::drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity) {
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(x - radius / 2, y - radius / 2, radius, radius);
@ -417,8 +422,8 @@ void NvgWindow::drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, flo
}
void NvgWindow::initializeGL() {
CameraViewWidget::initializeGL();
void AnnotatedCameraWidget::initializeGL() {
CameraWidget::initializeGL();
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
qInfo() << "OpenGL vendor:" << QString((const char*)glGetString(GL_VENDOR));
qInfo() << "OpenGL renderer:" << QString((const char*)glGetString(GL_RENDERER));
@ -428,8 +433,8 @@ void NvgWindow::initializeGL() {
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
}
void NvgWindow::updateFrameMat() {
CameraViewWidget::updateFrameMat();
void AnnotatedCameraWidget::updateFrameMat() {
CameraWidget::updateFrameMat();
UIState *s = uiState();
int w = width(), h = height();
@ -446,7 +451,7 @@ void NvgWindow::updateFrameMat() {
.translate(-intrinsic_matrix.v[2], -intrinsic_matrix.v[5]);
}
void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
painter.save();
const UIScene &scene = s->scene;
@ -505,7 +510,7 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
painter.restore();
}
void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) {
void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) {
painter.save();
const float speedBuff = 10.;
@ -541,11 +546,13 @@ void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV
painter.restore();
}
void NvgWindow::paintGL() {
void AnnotatedCameraWidget::paintGL() {
const double start_draw_t = millis_since_boot();
UIState *s = uiState();
const cereal::ModelDataV2::Reader &model = (*s->sm)["modelV2"].getModelV2();
CameraViewWidget::setFrameId(model.getFrameId());
CameraViewWidget::paintGL();
CameraWidget::setFrameId(model.getFrameId());
CameraWidget::paintGL();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@ -575,10 +582,16 @@ void NvgWindow::paintGL() {
LOGW("slow frame rate: %.2f fps", fps);
}
prev_draw_t = cur_draw_t;
// publish debug msg
MessageBuilder msg;
auto m = msg.initEvent().initUiDebug();
m.setDrawTimeMillis(cur_draw_t - start_draw_t);
pm->send("uiDebug", msg);
}
void NvgWindow::showEvent(QShowEvent *event) {
CameraViewWidget::showEvent(event);
void AnnotatedCameraWidget::showEvent(QShowEvent *event) {
CameraWidget::showEvent(event);
ui_update_params(uiState());
prev_draw_t = millis_since_boot();

@ -25,7 +25,7 @@ private:
};
// container window for the NVG UI
class NvgWindow : public CameraViewWidget {
class AnnotatedCameraWidget : public CameraWidget {
Q_OBJECT
Q_PROPERTY(float speed MEMBER speed);
Q_PROPERTY(QString speedUnit MEMBER speedUnit);
@ -43,7 +43,7 @@ class NvgWindow : public CameraViewWidget {
Q_PROPERTY(int status MEMBER status);
public:
explicit NvgWindow(VisionStreamType type, QWidget* parent = 0);
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
void updateState(const UIState &s);
private:
@ -68,6 +68,7 @@ private:
bool has_eu_speed_limit = false;
bool v_ego_cluster_seen = false;
int status = STATUS_DISENGAGED;
std::unique_ptr<PubMaster> pm;
protected:
void paintGL() override;
@ -97,7 +98,7 @@ private:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent* e) override;
OnroadAlerts *alerts;
NvgWindow *nvg;
AnnotatedCameraWidget *nvg;
QColor bg = bg_colors[STATUS_DISENGAGED];
QWidget *map = nullptr;
QHBoxLayout* split;

@ -93,14 +93,14 @@ mat4 get_fit_view_transform(float widget_aspect_ratio, float frame_aspect_ratio)
} // namespace
CameraViewWidget::CameraViewWidget(std::string stream_name, VisionStreamType type, bool zoom, QWidget* parent) :
CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, bool zoom, QWidget* parent) :
stream_name(stream_name), stream_type(type), zoomed_view(zoom), QOpenGLWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
connect(this, &CameraViewWidget::vipcThreadConnected, this, &CameraViewWidget::vipcConnected, Qt::BlockingQueuedConnection);
connect(this, &CameraViewWidget::vipcThreadFrameReceived, this, &CameraViewWidget::vipcFrameReceived);
connect(this, &CameraWidget::vipcThreadConnected, this, &CameraWidget::vipcConnected, Qt::BlockingQueuedConnection);
connect(this, &CameraWidget::vipcThreadFrameReceived, this, &CameraWidget::vipcFrameReceived);
}
CameraViewWidget::~CameraViewWidget() {
CameraWidget::~CameraWidget() {
makeCurrent();
if (isValid()) {
glDeleteVertexArrays(1, &frame_vao);
@ -111,7 +111,7 @@ CameraViewWidget::~CameraViewWidget() {
doneCurrent();
}
void CameraViewWidget::initializeGL() {
void CameraWidget::initializeGL() {
initializeOpenGLFunctions();
program = std::make_unique<QOpenGLShaderProgram>(context());
@ -161,7 +161,7 @@ void CameraViewWidget::initializeGL() {
#endif
}
void CameraViewWidget::showEvent(QShowEvent *event) {
void CameraWidget::showEvent(QShowEvent *event) {
frames.clear();
if (!vipc_thread) {
vipc_thread = new QThread();
@ -171,7 +171,7 @@ void CameraViewWidget::showEvent(QShowEvent *event) {
}
}
void CameraViewWidget::hideEvent(QHideEvent *event) {
void CameraWidget::hideEvent(QHideEvent *event) {
if (vipc_thread) {
vipc_thread->requestInterruption();
vipc_thread->quit();
@ -180,7 +180,7 @@ void CameraViewWidget::hideEvent(QHideEvent *event) {
}
}
void CameraViewWidget::updateFrameMat() {
void CameraWidget::updateFrameMat() {
int w = width(), h = height();
if (zoomed_view) {
@ -224,12 +224,12 @@ void CameraViewWidget::updateFrameMat() {
}
}
void CameraViewWidget::updateCalibration(const mat3 &calib) {
void CameraWidget::updateCalibration(const mat3 &calib) {
calibration = calib;
updateFrameMat();
}
void CameraViewWidget::paintGL() {
void CameraWidget::paintGL() {
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@ -286,7 +286,7 @@ void CameraViewWidget::paintGL() {
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) {
void CameraWidget::vipcConnected(VisionIpcClient *vipc_client) {
makeCurrent();
frames.clear();
stream_width = vipc_client->buffers[0].width;
@ -339,7 +339,7 @@ void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) {
updateFrameMat();
}
void CameraViewWidget::vipcFrameReceived(VisionBuf *buf, uint32_t frame_id) {
void CameraWidget::vipcFrameReceived(VisionBuf *buf, uint32_t frame_id) {
frames.push_back(std::make_pair(frame_id, buf));
while (frames.size() > FRAME_BUFFER_SIZE) {
frames.pop_front();
@ -347,7 +347,7 @@ void CameraViewWidget::vipcFrameReceived(VisionBuf *buf, uint32_t frame_id) {
update();
}
void CameraViewWidget::vipcThread() {
void CameraWidget::vipcThread() {
VisionStreamType cur_stream_type = stream_type;
std::unique_ptr<VisionIpcClient> vipc_client;
VisionIpcBufExtra meta_main = {0};

@ -23,13 +23,13 @@
const int FRAME_BUFFER_SIZE = 5;
static_assert(FRAME_BUFFER_SIZE <= YUV_BUFFER_COUNT);
class CameraViewWidget : public QOpenGLWidget, protected QOpenGLFunctions {
class CameraWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
using QOpenGLWidget::QOpenGLWidget;
explicit CameraViewWidget(std::string stream_name, VisionStreamType stream_type, bool zoom, QWidget* parent = nullptr);
~CameraViewWidget();
explicit CameraWidget(std::string stream_name, VisionStreamType stream_type, bool zoom, QWidget* parent = nullptr);
~CameraWidget();
void setStreamType(VisionStreamType type) { stream_type = type; }
void setBackgroundColor(const QColor &color) { bg = color; }
void setFrameId(int frame_id) { draw_frame_id = frame_id; }

@ -493,7 +493,7 @@ location set</source>
</message>
</context>
<context>
<name>NvgWindow</name>
<name>AnnotatedCameraWidget</name>
<message>
<location filename="../qt/onroad.cc" line="218"/>
<source>km/h</source>

@ -67,6 +67,29 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AnnotatedCameraWidget</name>
<message>
<source>km/h</source>
<translation>km/h</translation>
</message>
<message>
<source>mph</source>
<translation>mph</translation>
</message>
<message>
<source>MAX</source>
<translation></translation>
</message>
<message>
<source>SPEED</source>
<translation></translation>
</message>
<message>
<source>LIMIT</source>
<translation></translation>
</message>
</context>
<context>
<name>ConfirmationDialog</name>
<message>
@ -406,29 +429,6 @@ location set</source>
<translation></translation>
</message>
</context>
<context>
<name>NvgWindow</name>
<message>
<source>km/h</source>
<translation>km/h</translation>
</message>
<message>
<source>mph</source>
<translation>mph</translation>
</message>
<message>
<source>MAX</source>
<translation></translation>
</message>
<message>
<source>SPEED</source>
<translation></translation>
</message>
<message>
<source>LIMIT</source>
<translation></translation>
</message>
</context>
<context>
<name>OffroadHome</name>
<message>

@ -60,11 +60,34 @@
</message>
<message>
<source>Cellular Metered</source>
<translation type="unfinished"></translation>
<translation> </translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation type="unfinished"></translation>
<translation> </translation>
</message>
</context>
<context>
<name>AnnotatedCameraWidget</name>
<message>
<source>km/h</source>
<translation>km/h</translation>
</message>
<message>
<source>mph</source>
<translation>mph</translation>
</message>
<message>
<source>MAX</source>
<translation>MAX</translation>
</message>
<message>
<source>SPEED</source>
<translation>SPEED</translation>
</message>
<message>
<source>LIMIT</source>
<translation>LIMIT</translation>
</message>
</context>
<context>
@ -406,29 +429,6 @@ location set</source>
<translation> </translation>
</message>
</context>
<context>
<name>NvgWindow</name>
<message>
<source>km/h</source>
<translation>km/h</translation>
</message>
<message>
<source>mph</source>
<translation>mph</translation>
</message>
<message>
<source>MAX</source>
<translation>MAX</translation>
</message>
<message>
<source>SPEED</source>
<translation>SPEED</translation>
</message>
<message>
<source>LIMIT</source>
<translation>LIMIT</translation>
</message>
</context>
<context>
<name>OffroadHome</name>
<message>

@ -489,7 +489,7 @@ ingesteld</translation>
</message>
</context>
<context>
<name>NvgWindow</name>
<name>AnnotatedCameraWidget</name>
<message>
<location filename="../qt/onroad.cc" line="218"/>
<source>km/h</source>

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

Loading…
Cancel
Save