Merge remote-tracking branch 'upstream/master' into test-models-check-safety-model

pull/29759/head
Shane Smiskol 2 years ago
commit 0795749154
  1. 21
      .github/workflows/selfdrive_tests.yaml
  2. 2
      .pre-commit-config.yaml
  3. 2
      cereal
  4. 2
      docs/CARS.md
  5. 2
      panda
  6. 18
      poetry.lock
  7. 2
      pyproject.toml
  8. 1
      selfdrive/athena/tests/test_athenad.py
  9. 1
      selfdrive/car/ford/values.py
  10. 3
      selfdrive/car/hyundai/values.py
  11. 4
      selfdrive/car/subaru/values.py
  12. 1
      selfdrive/car/tests/routes.py
  13. 2
      selfdrive/car/tests/test_fw_fingerprint.py
  14. 2
      selfdrive/locationd/test/test_locationd.py
  15. 18
      selfdrive/test/pytest-ci.ini
  16. 2
      selfdrive/ui/qt/widgets/cameraview.cc
  17. 3
      selfdrive/ui/soundd/sound.cc
  18. 21
      selfdrive/ui/soundd/sound.h
  19. 2
      selfdrive/ui/tests/test_sound.cc
  20. 3
      system/logmessaged.py
  21. 169
      system/sensord/rawgps/nmeaport.py
  22. 27
      system/sensord/tests/test_sensord.py
  23. 4
      system/swaglog.py
  24. 4
      system/tests/test_logmessaged.py
  25. 6
      system/ubloxd/tests/test_ublox_processing.py

@ -26,6 +26,7 @@ env:
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
UNIT_TEST: coverage run --append -m unittest discover UNIT_TEST: coverage run --append -m unittest discover
PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5
jobs: jobs:
build_release: build_release:
@ -248,22 +249,9 @@ jobs:
timeout-minutes: 40 timeout-minutes: 40
run: | run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \ ${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
$UNIT_TEST common && \ $PYTEST --rootdir . -c selfdrive/test/pytest-ci.ini && \
$UNIT_TEST selfdrive/boardd && \
$UNIT_TEST selfdrive/controls && \
$UNIT_TEST selfdrive/monitoring && \
$UNIT_TEST system/loggerd && \
$UNIT_TEST selfdrive/car && \
$UNIT_TEST selfdrive/locationd && \
$UNIT_TEST selfdrive/test/longitudinal_maneuvers && \
$UNIT_TEST system/tests && \
$UNIT_TEST system/ubloxd && \
selfdrive/locationd/test/_test_locationd_lib.py && \ selfdrive/locationd/test/_test_locationd_lib.py && \
./system/ubloxd/tests/test_glonass_runner && \ ./system/ubloxd/tests/test_glonass_runner && \
$UNIT_TEST selfdrive/athena && \
$UNIT_TEST selfdrive/thermald && \
$UNIT_TEST system/hardware/tici && \
$UNIT_TEST tools/lib/tests && \
./selfdrive/ui/tests/create_test_translations.sh && \ ./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py && \ ./selfdrive/ui/tests/test_translations.py && \
@ -274,8 +262,7 @@ jobs:
./tools/replay/tests/test_replay && \ ./tools/replay/tests/test_replay && \
./tools/cabana/tests/test_cabana && \ ./tools/cabana/tests/test_cabana && \
./system/camerad/test/ae_gray_test && \ ./system/camerad/test/ae_gray_test && \
./selfdrive/test/process_replay/test_fuzzy.py && \ ./selfdrive/test/process_replay/test_fuzzy.py"
coverage xml"
- name: "Upload coverage to Codecov" - name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3
@ -374,7 +361,7 @@ jobs:
- name: Test car models - name: Test car models
timeout-minutes: 25 timeout-minutes: 25
run: | run: |
${{ env.RUN }} "pytest --cov --cov-report=xml -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \ ${{ env.RUN }} "$PYTEST -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache" chmod -R 777 /tmp/comma_download_cache"
env: env:
NUM_JOBS: 5 NUM_JOBS: 5

@ -35,7 +35,7 @@ repos:
args: ['--explicit-package-bases'] args: ['--explicit-package-bases']
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.286 rev: v0.0.287
hooks: hooks:
- id: ruff - id: ruff
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'

@ -1 +1 @@
Subproject commit 82bca3a9714b73c05414fdf848b6016a0ffac17d Subproject commit 4291784b4d372782c95279e9fe7741e38633ca5e

@ -83,7 +83,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Ioniq 6 (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 6 (with HDA II) 2023">Buy Here</a></sub></details>|| |Hyundai|Ioniq 6 (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq 6 (with HDA II) 2023">Buy Here</a></sub></details>||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>|| |Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>||
|Hyundai|Ioniq Electric 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>|| |Hyundai|Ioniq Electric 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>|| |Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>|| |Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>|| |Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|| |Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||

@ -1 +1 @@
Subproject commit ef1a9338a17f63ad1c666364c695e2b36a47350e Subproject commit 3ab4f43de06d7abcc4d594ee2a4efc0466e42c94

18
poetry.lock generated

@ -3152,13 +3152,13 @@ files = [
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "3.3.3" version = "3.4.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"},
{file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"},
] ]
[package.dependencies] [package.dependencies]
@ -3745,13 +3745,13 @@ cp2110 = ["hidapi"]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "7.4.0" version = "7.4.1"
description = "pytest: simple powerful testing with Python" description = "pytest: simple powerful testing with Python"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"},
{file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"},
] ]
[package.dependencies] [package.dependencies]
@ -3876,13 +3876,13 @@ numpy = ["numpy (>=1.6.0)"]
[[package]] [[package]]
name = "pytz" name = "pytz"
version = "2023.3" version = "2023.3.post1"
description = "World timezone definitions, modern and historical" description = "World timezone definitions, modern and historical"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
{file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
] ]
[[package]] [[package]]

@ -1,6 +1,6 @@
[tool.pytest.ini_options] [tool.pytest.ini_options]
minversion = "6.0" minversion = "6.0"
addopts = "--ignore=openpilot/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers" addopts = "--ignore=openpilot/ --ignore=cereal/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers"
python_files = "test_*.py" python_files = "test_*.py"
#timeout = "30" # you get this long by default #timeout = "30" # you get this long by default
markers = [ markers = [

@ -48,6 +48,7 @@ class TestAthenadMethods(unittest.TestCase):
else: else:
os.unlink(p) os.unlink(p)
dispatcher["listUploadQueue"]() # ensure queue is empty at start
# *** test helpers *** # *** test helpers ***

@ -265,6 +265,7 @@ FW_VERSIONS = {
b'NZ6A-14C204-AAA\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NZ6A-14C204-AAA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PZ6A-14C204-BE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PZ6A-14C204-JC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'PZ6A-14C204-JC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
}, },

@ -1930,6 +1930,7 @@ FW_VERSIONS = {
CAR.KIA_SORENTO_HEV_4TH_GEN: { CAR.KIA_SORENTO_HEV_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331', b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ', b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ',
@ -1975,7 +1976,7 @@ EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR
CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KONA_EV_2ND_GEN} CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KONA_EV_2ND_GEN}
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_OPTIMA_G4, LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_OPTIMA_G4,
CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022, CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022,
CAR.KIA_OPTIMA_H} CAR.KIA_OPTIMA_H}

@ -310,6 +310,7 @@ FW_VERSIONS = {
b'\xa2 !`\000', b'\xa2 !`\000',
b'\xf1\x00\xb2\x06\x04', b'\xf1\x00\xb2\x06\x04',
b'\xa2 `\x00', b'\xa2 `\x00',
b'\xa2 !3\x00',
], ],
(Ecu.eps, 0x746, None): [ (Ecu.eps, 0x746, None): [
b'\x9a\xc0\000\000', b'\x9a\xc0\000\000',
@ -323,6 +324,7 @@ FW_VERSIONS = {
b'\x00\x00eq\x1f@ "', b'\x00\x00eq\x1f@ "',
b'\x00\x00eq\x00\x00\x00\x00', b'\x00\x00eq\x00\x00\x00\x00',
b'\x00\x00e\x8f\x00\x00\x00\x00', b'\x00\x00e\x8f\x00\x00\x00\x00',
b'\x00\x00e\xa4\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xca!ap\a', b'\xca!ap\a',
@ -337,6 +339,7 @@ FW_VERSIONS = {
b'\xf3"fp\x07', b'\xf3"fp\x07',
b'\xe6"f0\x07', b'\xe6"f0\x07',
b'\xe6"fp\x07', b'\xe6"fp\x07',
b'\xe6!`@\x07',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xe6\xf5\004\000\000', b'\xe6\xf5\004\000\000',
@ -348,6 +351,7 @@ FW_VERSIONS = {
b'\xe9\xf6F0\x00', b'\xe9\xf6F0\x00',
b'\xe9\xf5B0\x00', b'\xe9\xf5B0\x00',
b'\xe9\xf6B0\x00', b'\xe9\xf6B0\x00',
b'\xe9\xf5"\x00\x00',
], ],
}, },
CAR.CROSSTREK_HYBRID: { CAR.CROSSTREK_HYBRID: {

@ -129,6 +129,7 @@ routes = [
CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020), CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020),
CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD), CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD),
CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ), CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ),
CarTestRoute("012c95f06918eca4|2023-01-15--11-19-36", HYUNDAI.IONIQ), # openpilot longitudinal enabled
CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022), CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022),
CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA), CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA),
CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV), CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV),

@ -175,7 +175,7 @@ class TestFwFingerprint(unittest.TestCase):
class TestFwFingerprintTiming(unittest.TestCase): class TestFwFingerprintTiming(unittest.TestCase):
N: int = 5 N: int = 5
TOL: float = 0.1 TOL: float = 0.12
@staticmethod @staticmethod
def _run_thread(thread: threading.Thread) -> float: def _run_thread(thread: threading.Thread) -> float:

@ -80,7 +80,7 @@ class TestLocationdProc(unittest.TestCase):
for msg in sorted(msgs, key=lambda x: x.logMonoTime): for msg in sorted(msgs, key=lambda x: x.logMonoTime):
self.pm.send(msg.which(), msg) self.pm.send(msg.which(), msg)
if msg.which() == "cameraOdometry": if msg.which() == "cameraOdometry":
self.pm.wait_for_readers_to_update(msg.which(), 0.1) self.pm.wait_for_readers_to_update(msg.which(), 0.1, dt=0.005)
time.sleep(1) # wait for async params write time.sleep(1) # wait for async params write
lastGPS = json.loads(self.params.get('LastGPSPosition')) lastGPS = json.loads(self.params.get('LastGPSPosition'))

@ -0,0 +1,18 @@
[pytest]
testpaths =
common
selfdrive/athena
selfdrive/boardd
selfdrive/car
selfdrive/controls
selfdrive/locationd
selfdrive/monitoring
selfdrive/thermald
selfdrive/test/longitudinal_maneuvers
system/hardware/tici
system/loggerd
system/tests
system/ubloxd
tools/lib/tests
markers =
parallel: mark tests as parallelizable (tests with no global state, so can be run in parallel)

@ -41,6 +41,8 @@ const char frame_fragment_shader[] =
"out vec4 colorOut;\n" "out vec4 colorOut;\n"
"void main() {\n" "void main() {\n"
" colorOut = texture(uTexture, vTexCoord);\n" " colorOut = texture(uTexture, vTexCoord);\n"
// gamma to improve worst case visibility when dark
" colorOut.rgb = pow(colorOut.rgb, vec3(1.0/1.28));\n"
"}\n"; "}\n";
#else #else
const char frame_fragment_shader[] = const char frame_fragment_shader[] =

@ -15,12 +15,13 @@
Sound::Sound(QObject *parent) : sm({"controlsState", "microphone"}) { Sound::Sound(QObject *parent) : sm({"controlsState", "microphone"}) {
qInfo() << "default audio device: " << QAudioDeviceInfo::defaultOutputDevice().deviceName(); qInfo() << "default audio device: " << QAudioDeviceInfo::defaultOutputDevice().deviceName();
for (auto &[alert, fn, loops] : sound_list) { for (auto &[alert, fn, loops, volume] : sound_list) {
QSoundEffect *s = new QSoundEffect(this); QSoundEffect *s = new QSoundEffect(this);
QObject::connect(s, &QSoundEffect::statusChanged, [=]() { QObject::connect(s, &QSoundEffect::statusChanged, [=]() {
assert(s->status() != QSoundEffect::Error); assert(s->status() != QSoundEffect::Error);
}); });
s->setSource(QUrl::fromLocalFile("../../assets/sounds/" + fn)); s->setSource(QUrl::fromLocalFile("../../assets/sounds/" + fn));
s->setVolume(volume);
sounds[alert] = {s, loops}; sounds[alert] = {s, loops};
} }

@ -9,18 +9,21 @@
#include "system/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
const std::tuple<AudibleAlert, QString, int> sound_list[] = {
const float MAX_VOLUME = 1.0;
const std::tuple<AudibleAlert, QString, int, float> sound_list[] = {
// AudibleAlert, file name, loop count // AudibleAlert, file name, loop count
{AudibleAlert::ENGAGE, "engage.wav", 0}, {AudibleAlert::ENGAGE, "engage.wav", 0, MAX_VOLUME},
{AudibleAlert::DISENGAGE, "disengage.wav", 0}, {AudibleAlert::DISENGAGE, "disengage.wav", 0, MAX_VOLUME},
{AudibleAlert::REFUSE, "refuse.wav", 0}, {AudibleAlert::REFUSE, "refuse.wav", 0, MAX_VOLUME},
{AudibleAlert::PROMPT, "prompt.wav", 0}, {AudibleAlert::PROMPT, "prompt.wav", 0, MAX_VOLUME},
{AudibleAlert::PROMPT_REPEAT, "prompt.wav", QSoundEffect::Infinite}, {AudibleAlert::PROMPT_REPEAT, "prompt.wav", QSoundEffect::Infinite, MAX_VOLUME},
{AudibleAlert::PROMPT_DISTRACTED, "prompt_distracted.wav", QSoundEffect::Infinite}, {AudibleAlert::PROMPT_DISTRACTED, "prompt_distracted.wav", QSoundEffect::Infinite, MAX_VOLUME},
{AudibleAlert::WARNING_SOFT, "warning_soft.wav", QSoundEffect::Infinite}, {AudibleAlert::WARNING_SOFT, "warning_soft.wav", QSoundEffect::Infinite, MAX_VOLUME},
{AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", QSoundEffect::Infinite}, {AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", QSoundEffect::Infinite, MAX_VOLUME},
}; };
class Sound : public QObject { class Sound : public QObject {

@ -31,7 +31,7 @@ void controls_thread(int loop_cnt) {
const int DT_CTRL = 10; // ms const int DT_CTRL = 10; // ms
for (int i = 0; i < loop_cnt; ++i) { for (int i = 0; i < loop_cnt; ++i) {
for (auto &[alert, fn, loops] : sound_list) { for (auto &[alert, fn, loops, volume] : sound_list) {
printf("testing %s\n", qPrintable(fn)); printf("testing %s\n", qPrintable(fn));
for (int j = 0; j < 1000 / DT_CTRL; ++j) { for (int j = 0; j < 1000 / DT_CTRL; ++j) {
MessageBuilder msg; MessageBuilder msg;

@ -5,6 +5,7 @@ from typing import NoReturn
import cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.common.logging_extra import SwagLogFileFormatter from openpilot.common.logging_extra import SwagLogFileFormatter
from openpilot.system.swaglog import get_file_handler from openpilot.system.swaglog import get_file_handler
from system.swaglog import SWAGLOG_IPC
def main() -> NoReturn: def main() -> NoReturn:
@ -14,7 +15,7 @@ def main() -> NoReturn:
ctx = zmq.Context.instance() ctx = zmq.Context.instance()
sock = ctx.socket(zmq.PULL) sock = ctx.socket(zmq.PULL)
sock.bind("ipc:///tmp/logmessage") sock.bind(f"ipc://{SWAGLOG_IPC}")
# and we publish them # and we publish them
log_message_sock = messaging.pub_sock('logMessage') log_message_sock = messaging.pub_sock('logMessage')

@ -0,0 +1,169 @@
import os
import sys
from dataclasses import dataclass, fields
from subprocess import check_output, CalledProcessError
from time import sleep
from typing import NoReturn
DEBUG = int(os.environ.get("DEBUG", "0"))
@dataclass
class GnssClockNmeaPort:
# flags bit mask:
# 0x01 = leap_seconds valid
# 0x02 = time_uncertainty_ns valid
# 0x04 = full_bias_ns valid
# 0x08 = bias_ns valid
# 0x10 = bias_uncertainty_ns valid
# 0x20 = drift_nsps valid
# 0x40 = drift_uncertainty_nsps valid
flags: int
leap_seconds: int
time_ns: int
time_uncertainty_ns: int # 1-sigma
full_bias_ns: int
bias_ns: float
bias_uncertainty_ns: float # 1-sigma
drift_nsps: float
drift_uncertainty_nsps: float # 1-sigma
def __post_init__(self):
for field in fields(self):
val = getattr(self, field.name)
setattr(self, field.name, field.type(val) if val else None)
@dataclass
class GnssMeasNmeaPort:
messageCount: int
messageNum: int
svCount: int
# constellation enum:
# 1 = GPS
# 2 = SBAS
# 3 = GLONASS
# 4 = QZSS
# 5 = BEIDOU
# 6 = GALILEO
constellation: int
svId: int
flags: int # always zero
time_offset_ns: int
# state bit mask:
# 0x0001 = CODE LOCK
# 0x0002 = BIT SYNC
# 0x0004 = SUBFRAME SYNC
# 0x0008 = TIME OF WEEK DECODED
# 0x0010 = MSEC AMBIGUOUS
# 0x0020 = SYMBOL SYNC
# 0x0040 = GLONASS STRING SYNC
# 0x0080 = GLONASS TIME OF DAY DECODED
# 0x0100 = BEIDOU D2 BIT SYNC
# 0x0200 = BEIDOU D2 SUBFRAME SYNC
# 0x0400 = GALILEO E1BC CODE LOCK
# 0x0800 = GALILEO E1C 2ND CODE LOCK
# 0x1000 = GALILEO E1B PAGE SYNC
# 0x2000 = GALILEO E1B PAGE SYNC
state: int
time_of_week_ns: int
time_of_week_uncertainty_ns: int # 1-sigma
carrier_to_noise_ratio: float
pseudorange_rate: float
pseudorange_rate_uncertainty: float # 1-sigma
def __post_init__(self):
for field in fields(self):
val = getattr(self, field.name)
setattr(self, field.name, field.type(val) if val else None)
def nmea_checksum_ok(s):
checksum = 0
for i, c in enumerate(s[1:]):
if c == "*":
if i != len(s) - 4: # should be 3rd to last character
print("ERROR: NMEA string does not have checksum delimiter in correct location:", s)
return False
break
checksum ^= ord(c)
else:
print("ERROR: NMEA string does not have checksum delimiter:", s)
return False
return True
def process_nmea_port_messages(device:str="/dev/ttyUSB1") -> NoReturn:
while True:
try:
with open(device, "r") as nmeaport:
for line in nmeaport:
line = line.strip()
if DEBUG:
print(line)
if not line.startswith("$"): # all NMEA messages start with $
continue
if not nmea_checksum_ok(line):
continue
fields = line.split(",")
match fields[0]:
case "$GNCLK":
# fields at end are reserved (not used)
gnss_clock = GnssClockNmeaPort(*fields[1:10]) # type: ignore[arg-type]
print(gnss_clock)
case "$GNMEAS":
# fields at end are reserved (not used)
gnss_meas = GnssMeasNmeaPort(*fields[1:14]) # type: ignore[arg-type]
print(gnss_meas)
except Exception as e:
print(e)
sleep(1)
def main() -> NoReturn:
from openpilot.common.gpio import gpio_init, gpio_set
from openpilot.system.hardware.tici.pins import GPIO
from openpilot.system.sensord.rawgps.rawgpsd import at_cmd
try:
check_output(["pidof", "rawgpsd"])
print("rawgpsd is running, please kill openpilot before running this script! (aborted)")
sys.exit(1)
except CalledProcessError as e:
if e.returncode != 1: # 1 == no process found (boardd not running)
raise e
print("power up antenna ...")
gpio_init(GPIO.GNSS_PWR_EN, True)
gpio_set(GPIO.GNSS_PWR_EN, True)
if b"+QGPS: 0" not in (at_cmd("AT+QGPS?") or b""):
print("stop location tracking ...")
at_cmd("AT+QGPSEND")
if b'+QGPSCFG: "outport",usbnmea' not in (at_cmd('AT+QGPSCFG="outport"') or b""):
print("configure outport ...")
at_cmd('AT+QGPSCFG="outport","usbnmea"') # usbnmea = /dev/ttyUSB1
if b'+QGPSCFG: "gnssrawdata",3,0' not in (at_cmd('AT+QGPSCFG="gnssrawdata"') or b""):
print("configure gnssrawdata ...")
# AT+QGPSCFG="gnssrawdata",<constellation-mask>,<port>'
# <constellation-mask> values:
# 0x01 = GPS
# 0x02 = GLONASS
# 0x04 = BEIDOU
# 0x08 = GALILEO
# 0x10 = QZSS
# <port> values:
# 0 = NMEA port
# 1 = AT port
at_cmd('AT+QGPSCFG="gnssrawdata",3,0') # enable all constellations, output data to NMEA port
print("rebooting ...")
at_cmd('AT+CFUN=1,1')
print("re-run this script when it is back up")
sys.exit(2)
print("starting location tracking ...")
at_cmd("AT+QGPS=1")
process_nmea_port_messages()
if __name__ == "__main__":
main()

@ -9,6 +9,7 @@ import cereal.messaging as messaging
from cereal import log from cereal import log
from cereal.services import service_list from cereal.services import service_list
from openpilot.common.gpio import get_irqs_for_action from openpilot.common.gpio import get_irqs_for_action
from openpilot.common.timeout import Timeout
from openpilot.system.hardware import TICI from openpilot.system.hardware import TICI
from openpilot.selfdrive.manager.process_config import managed_processes from openpilot.selfdrive.manager.process_config import managed_processes
@ -73,15 +74,24 @@ def get_irq_count(irq: int):
def read_sensor_events(duration_sec): def read_sensor_events(duration_sec):
sensor_types = ['accelerometer', 'gyroscope', 'magnetometer', 'accelerometer2', sensor_types = ['accelerometer', 'gyroscope', 'magnetometer', 'accelerometer2',
'gyroscope2', 'temperatureSensor', 'temperatureSensor2'] 'gyroscope2', 'temperatureSensor', 'temperatureSensor2']
esocks = {} socks = {}
poller = messaging.Poller()
events = defaultdict(list) events = defaultdict(list)
for stype in sensor_types: for stype in sensor_types:
esocks[stype] = messaging.sub_sock(stype, timeout=0.1) socks[stype] = messaging.sub_sock(stype, poller=poller, timeout=100)
start_time_sec = time.monotonic() # wait for sensors to come up
while time.monotonic() - start_time_sec < duration_sec: with Timeout(60, "sensors didn't come up"):
for esock in esocks: while len(poller.poll(250)) == 0:
events[esock] += messaging.drain_sock(esocks[esock]) pass
time.sleep(1)
for s in socks.values():
messaging.drain_sock_raw(s)
st = time.monotonic()
while time.monotonic() - st < duration_sec:
for s in socks:
events[s] += messaging.drain_sock(socks[s])
time.sleep(0.1) time.sleep(0.1)
assert sum(map(len, events.values())) != 0, "No sensor events collected!" assert sum(map(len, events.values())) != 0, "No sensor events collected!"
@ -101,8 +111,7 @@ class TestSensord(unittest.TestCase):
os.system("pkill -f ./_sensord") os.system("pkill -f ./_sensord")
try: try:
managed_processes["sensord"].start() managed_processes["sensord"].start()
time.sleep(3) cls.sample_secs = int(os.getenv("SAMPLE_SECS", "10"))
cls.sample_secs = 10
cls.events = read_sensor_events(cls.sample_secs) cls.events = read_sensor_events(cls.sample_secs)
# determine sensord's irq # determine sensord's irq

@ -15,6 +15,8 @@ if PC:
else: else:
SWAGLOG_DIR = "/data/log/" SWAGLOG_DIR = "/data/log/"
SWAGLOG_IPC = "/tmp/logmessage"
def get_file_handler(): def get_file_handler():
Path(SWAGLOG_DIR).mkdir(parents=True, exist_ok=True) Path(SWAGLOG_DIR).mkdir(parents=True, exist_ok=True)
base_filename = os.path.join(SWAGLOG_DIR, "swaglog") base_filename = os.path.join(SWAGLOG_DIR, "swaglog")
@ -89,7 +91,7 @@ class UnixDomainSocketHandler(logging.Handler):
self.zctx = zmq.Context() self.zctx = zmq.Context()
self.sock = self.zctx.socket(zmq.PUSH) self.sock = self.zctx.socket(zmq.PUSH)
self.sock.setsockopt(zmq.LINGER, 10) self.sock.setsockopt(zmq.LINGER, 10)
self.sock.connect("ipc:///tmp/logmessage") self.sock.connect(f"ipc://{SWAGLOG_IPC}")
self.pid = os.getpid() self.pid = os.getpid()
def emit(self, record): def emit(self, record):

@ -7,7 +7,7 @@ import unittest
import cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.selfdrive.manager.process_config import managed_processes from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.system.swaglog import cloudlog, ipchandler from openpilot.system.swaglog import cloudlog, ipchandler
from selfdrive.test.helpers import temporary_swaglog_dir from openpilot.selfdrive.test.helpers import temporary_swaglog_dir, temporary_swaglog_ipc
class TestLogmessaged(unittest.TestCase): class TestLogmessaged(unittest.TestCase):
@ -35,6 +35,7 @@ class TestLogmessaged(unittest.TestCase):
return list(glob.glob(os.path.join(self.temp_dir, "swaglog.*"))) return list(glob.glob(os.path.join(self.temp_dir, "swaglog.*")))
@temporary_swaglog_dir @temporary_swaglog_dir
@temporary_swaglog_ipc
def test_simple_log(self, temp_dir): def test_simple_log(self, temp_dir):
self._setup(temp_dir) self._setup(temp_dir)
msgs = [f"abc {i}" for i in range(10)] msgs = [f"abc {i}" for i in range(10)]
@ -46,6 +47,7 @@ class TestLogmessaged(unittest.TestCase):
assert len(self._get_log_files()) >= 1 assert len(self._get_log_files()) >= 1
@temporary_swaglog_dir @temporary_swaglog_dir
@temporary_swaglog_ipc
def test_big_log(self, temp_dir): def test_big_log(self, temp_dir):
self._setup(temp_dir) self._setup(temp_dir)
n = 10 n = 10

@ -9,6 +9,7 @@ from laika.opt import calc_pos_fix
from openpilot.selfdrive.test.openpilotci import get_url from openpilot.selfdrive.test.openpilotci import get_url
from openpilot.tools.lib.logreader import LogReader from openpilot.tools.lib.logreader import LogReader
from openpilot.selfdrive.test.helpers import with_processes from openpilot.selfdrive.test.helpers import with_processes
from openpilot.selfdrive.test.helpers import temporary_dir
import cereal.messaging as messaging import cereal.messaging as messaging
def get_gnss_measurements(log_reader): def get_gnss_measurements(log_reader):
@ -54,8 +55,9 @@ class TestUbloxProcessing(unittest.TestCase):
self.assertEqual(count_gps, 5036) self.assertEqual(count_gps, 5036)
self.assertEqual(count_glonass, 3651) self.assertEqual(count_glonass, 3651)
def test_get_fix(self): @temporary_dir
dog = AstroDog() def test_get_fix(self, temp_dir):
dog = AstroDog(cache_dir=temp_dir)
position_fix_found = 0 position_fix_found = 0
count_processed_measurements = 0 count_processed_measurements = 0
count_corrected_measurements = 0 count_corrected_measurements = 0

Loading…
Cancel
Save