Merge branch 'commaai:master' into port_honda_hrv_2023

pull/27482/head
AlexandreSato 2 years ago committed by GitHub
commit 0167106bf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      RELEASES.md
  2. 2
      cereal
  3. 1
      common/params.cc
  4. 2
      docs/CARS.md
  5. 2
      laika_repo
  6. 2
      panda
  7. 30
      poetry.lock
  8. 1
      pyproject.toml
  9. 2
      release/check-submodules.sh
  10. 9
      release/files_common
  11. BIN
      selfdrive/assets/img_driver_face.png
  12. BIN
      selfdrive/assets/img_driver_face_static.png
  13. 40
      selfdrive/car/__init__.py
  14. 4
      selfdrive/car/chrysler/carcontroller.py
  15. 2
      selfdrive/car/chrysler/values.py
  16. 6
      selfdrive/car/ford/values.py
  17. 4
      selfdrive/car/gm/carcontroller.py
  18. 6
      selfdrive/car/honda/values.py
  19. 4
      selfdrive/car/hyundai/carcontroller.py
  20. 4
      selfdrive/car/hyundai/values.py
  21. 6
      selfdrive/car/mazda/carcontroller.py
  22. 4
      selfdrive/car/subaru/carcontroller.py
  23. 4
      selfdrive/car/toyota/carcontroller.py
  24. 2
      selfdrive/car/toyota/values.py
  25. 4
      selfdrive/car/volkswagen/carcontroller.py
  26. 10
      selfdrive/car/volkswagen/values.py
  27. 3
      selfdrive/controls/controlsd.py
  28. 4
      selfdrive/controls/lib/events.py
  29. 3
      selfdrive/controls/lib/longitudinal_planner.py
  30. 1
      selfdrive/controls/plannerd.py
  31. 2
      selfdrive/debug/test_fw_query_on_routes.py
  32. 22
      selfdrive/locationd/laikad.py
  33. 10
      selfdrive/locationd/test/test_laikad.py
  34. 2
      selfdrive/manager/manager.py
  35. 2
      selfdrive/modeld/SConscript
  36. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  37. 3
      selfdrive/test/process_replay/process_replay.py
  38. 2
      selfdrive/test/process_replay/ref_commit
  39. 3
      selfdrive/test/process_replay/test_processes.py
  40. 2
      selfdrive/ui/qt/offroad/driverview.cc
  41. 5
      selfdrive/ui/qt/onroad.cc
  42. 47
      selfdrive/ui/qt/setup/setup.cc
  43. 5
      selfdrive/ui/qt/setup/setup.h
  44. 8
      selfdrive/ui/translations/main_de.ts
  45. 8
      selfdrive/ui/translations/main_ja.ts
  46. 17
      selfdrive/ui/translations/main_ko.ts
  47. 8
      selfdrive/ui/translations/main_pt-BR.ts
  48. 54
      selfdrive/ui/translations/main_th.ts
  49. 8
      selfdrive/ui/translations/main_zh-CHS.ts
  50. 8
      selfdrive/ui/translations/main_zh-CHT.ts
  51. 12
      system/hardware/tici/test_power_draw.py
  52. 2
      tinygrad_repo
  53. 7
      tools/cabana/binaryview.cc
  54. 203
      tools/cabana/chartswidget.cc
  55. 20
      tools/cabana/chartswidget.h
  56. 14
      tools/cabana/historylog.cc
  57. 37
      tools/cabana/mainwin.cc
  58. 3
      tools/cabana/mainwin.h
  59. 35
      tools/cabana/messageswidget.cc
  60. 2
      tools/cabana/messageswidget.h
  61. 2
      tools/cabana/settings.cc
  62. 30
      tools/cabana/signaledit.cc
  63. 7
      tools/cabana/streams/livestream.cc
  64. 70
      tools/cabana/util.cc
  65. 22
      tools/cabana/util.h
  66. 5
      tools/replay/consoleui.cc

@ -3,7 +3,7 @@ Version 0.9.2 (2023-03-XX)
* Draw MPC path instead of model predicted path, this is a more accurate representation of what the car will do. * Draw MPC path instead of model predicted path, this is a more accurate representation of what the car will do.
* Honda HR-V 2023 support thanks to Takeda! * Honda HR-V 2023 support thanks to Takeda!
Version 0.9.1 (2023-02-23) Version 0.9.1 (2023-02-28)
======================== ========================
* New driving model * New driving model
* 30% improved height estimation resulting in better driving performance for tall cars * 30% improved height estimation resulting in better driving performance for tall cars

@ -1 +1 @@
Subproject commit b88523f05ac958f87a8f6d57c3f4bb20da55f216 Subproject commit 42f84fd85d06c0fc85371daa2b4820fca49d763e

@ -99,7 +99,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CompletedTrainingVersion", PERSISTENT}, {"CompletedTrainingVersion", PERSISTENT},
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DashcamOverride", PERSISTENT},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT}, {"DisablePowerDown", PERSISTENT},
{"ExperimentalMode", PERSISTENT}, {"ExperimentalMode", PERSISTENT},

@ -182,7 +182,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2017-20">Toyota</a>|| |Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2017-20">Toyota</a>||
|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2021-22">Toyota</a>|| |Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=C-HR Hybrid 2021-22">Toyota</a>||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>6</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Toyota|Camry 2018-20|All|Stock|0 mph[<sup>6</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|Camry 2021-22|All|openpilot|0 mph[<sup>6</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2021-22">Toyota</a>|| |Toyota|Camry 2021-23|All|openpilot|0 mph[<sup>6</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry 2021-23">Toyota</a>||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2018-20">Toyota</a>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2021-23">Toyota</a>|| |Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Camry Hybrid 2021-23">Toyota</a>||
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla 2017-19">Toyota</a>|| |Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Corolla 2017-19">Toyota</a>||

@ -1 +1 @@
Subproject commit 278b44ba8c2dec28adc5b30cce13dabc195f15fc Subproject commit b740b71c82a748e3520b1599487d9a7aaf728670

@ -1 +1 @@
Subproject commit 9fe24bd3683377146b04c68ab94d0935f0bd9d78 Subproject commit e0e754de2c7fdbf943dd02db203b7bb40bf3e9b5

30
poetry.lock generated

@ -4355,6 +4355,14 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
types-urllib3 = "<1.27" types-urllib3 = "<1.27"
[[package]]
name = "types-tabulate"
version = "0.9.0.1"
description = "Typing stubs for tabulate"
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "types-urllib3" name = "types-urllib3"
version = "1.26.25.1" version = "1.26.25.1"
@ -4567,7 +4575,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "~3.8" python-versions = "~3.8"
content-hash = "9e9495c896e6fd0855803aeaf46513c6c22424b86be820759a8baf27d44e73ee" content-hash = "669485055bf8d77336509cb7a3878e06aa32431c520825948914d76b57347fde"
[metadata.files] [metadata.files]
adal = [ adal = [
@ -6616,7 +6624,6 @@ onnx = [
] ]
onnx2torch = [ onnx2torch = [
{file = "onnx2torch-1.5.4-py3-none-any.whl", hash = "sha256:fd1a0fe05072bfb9f3d86d9330299b130b41f11bd4ae634db17078974e711725"}, {file = "onnx2torch-1.5.4-py3-none-any.whl", hash = "sha256:fd1a0fe05072bfb9f3d86d9330299b130b41f11bd4ae634db17078974e711725"},
{file = "onnx2torch-1.5.4.tar.gz", hash = "sha256:df837b557a63540223d85fde4a1d679fde0ca8d8bb89d5379c030b01eddc9c24"},
] ]
onnxoptimizer = [ onnxoptimizer = [
{file = "onnxoptimizer-0.3.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e73a5e2e3ca4db9bff54f7131768749c861677b97ee811a136fcf1a52783cf6e"}, {file = "onnxoptimizer-0.3.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e73a5e2e3ca4db9bff54f7131768749c861677b97ee811a136fcf1a52783cf6e"},
@ -7002,7 +7009,6 @@ pycryptodome = [
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"},
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"},
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"},
{file = "pycryptodome-3.15.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:2ae53125de5b0d2c95194d957db9bb2681da8c24d0fb0fe3b056de2bcaf5d837"},
{file = "pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"},
{file = "pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"},
@ -7010,14 +7016,12 @@ pycryptodome = [
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:eb6fce570869e70cc8ebe68eaa1c26bed56d40ad0f93431ee61d400525433c54"},
{file = "pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"}, {file = "pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"},
{file = "pycryptodome-3.15.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:50ca7e587b8e541eb6c192acf92449d95377d1f88908c0a32ac5ac2703ebe28b"},
{file = "pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"}, {file = "pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"},
{file = "pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"}, {file = "pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"},
{file = "pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"}, {file = "pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"},
@ -7659,18 +7663,6 @@ setproctitle = [
{file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f2719a398e1a2c01c2a63bf30377a34d0b6ef61946ab9cf4d550733af8f1ef1"}, {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f2719a398e1a2c01c2a63bf30377a34d0b6ef61946ab9cf4d550733af8f1ef1"},
{file = "setproctitle-1.3.2-cp310-cp310-win32.whl", hash = "sha256:e425be62524dc0c593985da794ee73eb8a17abb10fe692ee43bb39e201d7a099"}, {file = "setproctitle-1.3.2-cp310-cp310-win32.whl", hash = "sha256:e425be62524dc0c593985da794ee73eb8a17abb10fe692ee43bb39e201d7a099"},
{file = "setproctitle-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e85e50b9c67854f89635a86247412f3ad66b132a4d8534ac017547197c88f27d"}, {file = "setproctitle-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e85e50b9c67854f89635a86247412f3ad66b132a4d8534ac017547197c88f27d"},
{file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a97d51c17d438cf5be284775a322d57b7ca9505bb7e118c28b1824ecaf8aeaa"},
{file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587c7d6780109fbd8a627758063d08ab0421377c0853780e5c356873cdf0f077"},
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d17c8bd073cbf8d141993db45145a70b307385b69171d6b54bcf23e5d644de"},
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e932089c35a396dc31a5a1fc49889dd559548d14cb2237adae260382a090382e"},
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4f8f12258a8739c565292a551c3db62cca4ed4f6b6126664e2381acb4931bf"},
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:570d255fd99c7f14d8f91363c3ea96bd54f8742275796bca67e1414aeca7d8c3"},
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a8e0881568c5e6beff91ef73c0ec8ac2a9d3ecc9edd6bd83c31ca34f770910c4"},
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4bba3be4c1fabf170595b71f3af46c6d482fbe7d9e0563999b49999a31876f77"},
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:37ece938110cab2bb3957e3910af8152ca15f2b6efdf4f2612e3f6b7e5459b80"},
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db684d6bbb735a80bcbc3737856385b55d53f8a44ce9b46e9a5682c5133a9bf7"},
{file = "setproctitle-1.3.2-cp311-cp311-win32.whl", hash = "sha256:ca58cd260ea02759238d994cfae844fc8b1e206c684beb8f38877dcab8451dfc"},
{file = "setproctitle-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:88486e6cce2a18a033013d17b30a594f1c5cb42520c49c19e6ade40b864bb7ff"},
{file = "setproctitle-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:92c626edc66169a1b09e9541b9c0c9f10488447d8a2b1d87c8f0672e771bc927"}, {file = "setproctitle-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:92c626edc66169a1b09e9541b9c0c9f10488447d8a2b1d87c8f0672e771bc927"},
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710e16fa3bade3b026907e4a5e841124983620046166f355bbb84be364bf2a02"}, {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710e16fa3bade3b026907e4a5e841124983620046166f355bbb84be364bf2a02"},
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f29b75e86260b0ab59adb12661ef9f113d2f93a59951373eb6d68a852b13e83"}, {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f29b75e86260b0ab59adb12661ef9f113d2f93a59951373eb6d68a852b13e83"},
@ -8066,6 +8058,10 @@ types-requests = [
{file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"}, {file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"},
{file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"}, {file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"},
] ]
types-tabulate = [
{file = "types-tabulate-0.9.0.1.tar.gz", hash = "sha256:e486292c279f19247865bdabe802419740a0e74b53444e7f7a8009e08129da5d"},
{file = "types_tabulate-0.9.0.1-py3-none-any.whl", hash = "sha256:be2ea0de05f615ccfcbadf6206aa720e265955eb1de23e343aec9d8bf3fa9aaa"},
]
types-urllib3 = [ types-urllib3 = [
{file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"}, {file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"},
{file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"}, {file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"},

@ -59,6 +59,7 @@ urllib3 = "^1.26.10"
utm = "^0.7.0" utm = "^0.7.0"
websocket_client = "^1.3.3" websocket_client = "^1.3.3"
polyline = "^1.4.0" polyline = "^1.4.0"
types-tabulate = "^0.9.0.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
while read hash submodule ref; do while read hash submodule ref; do
git -C $submodule fetch --depth 300 origin master git -C $submodule fetch --depth 1000 origin master
git -C $submodule branch -r --contains $hash | grep "origin/master" git -C $submodule branch -r --contains $hash | grep "origin/master"
if [ "$?" -eq 0 ]; then if [ "$?" -eq 0 ]; then
echo "$submodule ok" echo "$submodule ok"

@ -577,13 +577,14 @@ opendbc/tesla_can.dbc
opendbc/tesla_radar.dbc opendbc/tesla_radar.dbc
opendbc/tesla_powertrain.dbc opendbc/tesla_powertrain.dbc
tinygrad_repo/accel/opencl/*
tinygrad_repo/tinygrad/llops/ops_opencl.py
tinygrad_repo/openpilot/compile.py tinygrad_repo/openpilot/compile.py
tinygrad_repo/extra/onnx.py tinygrad_repo/extra/onnx.py
tinygrad_repo/extra/onnx_ops.py
tinygrad_repo/extra/thneed.py tinygrad_repo/extra/thneed.py
tinygrad_repo/extra/utils.py tinygrad_repo/extra/utils.py
tinygrad_repo/tinygrad/llops/ops_gpu.py tinygrad_repo/tinygrad/codegen/ast.py
tinygrad_repo/tinygrad/shape/* tinygrad_repo/tinygrad/codegen/gpu.py
tinygrad_repo/tinygrad/nn/* tinygrad_repo/tinygrad/nn/*
tinygrad_repo/tinygrad/runtime/ops_gpu.py
tinygrad_repo/tinygrad/shape/*
tinygrad_repo/tinygrad/*.py tinygrad_repo/tinygrad/*.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

@ -73,7 +73,7 @@ def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> Dict[str, st
return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc} return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc}
def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS): def apply_driver_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS):
# limits due to driver torque # limits due to driver torque
driver_max_torque = LIMITS.STEER_MAX + (LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER driver_max_torque = LIMITS.STEER_MAX + (LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER
@ -93,29 +93,37 @@ def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque
return int(round(float(apply_torque))) return int(round(float(apply_torque)))
def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS): def apply_dist_to_meas_limits(val, val_last, val_meas,
# limits due to comparison of commanded torque VS motor reported torque STEER_DELTA_UP, STEER_DELTA_DOWN,
max_lim = min(max(motor_torque + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_ERROR_MAX), LIMITS.STEER_MAX) STEER_ERROR_MAX, STEER_MAX):
min_lim = max(min(motor_torque - LIMITS.STEER_ERROR_MAX, -LIMITS.STEER_ERROR_MAX), -LIMITS.STEER_MAX) # limits due to comparison of commanded val VS measured val (torque/angle/curvature)
max_lim = min(max(val_meas + STEER_ERROR_MAX, STEER_ERROR_MAX), STEER_MAX)
min_lim = max(min(val_meas - STEER_ERROR_MAX, -STEER_ERROR_MAX), -STEER_MAX)
apply_torque = clip(apply_torque, min_lim, max_lim) val = clip(val, min_lim, max_lim)
# slow rate if steer torque increases in magnitude # slow rate if val increases in magnitude
if apply_torque_last > 0: if val_last > 0:
apply_torque = clip(apply_torque, val = clip(val,
max(apply_torque_last - LIMITS.STEER_DELTA_DOWN, -LIMITS.STEER_DELTA_UP), max(val_last - STEER_DELTA_DOWN, -STEER_DELTA_UP),
apply_torque_last + LIMITS.STEER_DELTA_UP) val_last + STEER_DELTA_UP)
else: else:
apply_torque = clip(apply_torque, val = clip(val,
apply_torque_last - LIMITS.STEER_DELTA_UP, val_last - STEER_DELTA_UP,
min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP)) min(val_last + STEER_DELTA_DOWN, STEER_DELTA_UP))
return int(round(float(apply_torque))) return float(val)
def apply_meas_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS):
return int(round(apply_dist_to_meas_limits(apply_torque, apply_torque_last, motor_torque,
LIMITS.STEER_DELTA_UP, LIMITS.STEER_DELTA_DOWN,
LIMITS.STEER_ERROR_MAX, LIMITS.STEER_MAX)))
def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, LIMITS): def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, LIMITS):
# pick angle rate limits based on wind up/down # pick angle rate limits based on wind up/down
steer_up = apply_angle_last * apply_angle > 0. and abs(apply_angle) > abs(apply_angle_last) steer_up = apply_angle_last * apply_angle >= 0. and abs(apply_angle) > abs(apply_angle_last)
rate_limits = LIMITS.ANGLE_RATE_LIMIT_UP if steer_up else LIMITS.ANGLE_RATE_LIMIT_DOWN rate_limits = LIMITS.ANGLE_RATE_LIMIT_UP if steer_up else LIMITS.ANGLE_RATE_LIMIT_DOWN
angle_rate_lim = interp(v_ego, rate_limits.speed_bp, rate_limits.angle_v) angle_rate_lim = interp(v_ego, rate_limits.speed_bp, rate_limits.angle_v)

@ -1,6 +1,6 @@
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import apply_toyota_steer_torque_limits from selfdrive.car import apply_meas_steer_torque_limits
from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons
from selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags from selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
@ -67,7 +67,7 @@ class CarController:
# steer torque # steer torque
new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX)) new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX))
apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params) apply_steer = apply_meas_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params)
if not lkas_active or not lkas_control_bit: if not lkas_active or not lkas_control_bit:
apply_steer = 0 apply_steer = 0
self.apply_steer_last = apply_steer self.apply_steer_last = apply_steer

@ -135,7 +135,7 @@ FINGERPRINTS = {
}], }],
CAR.JEEP_CHEROKEE_2019: [{ CAR.JEEP_CHEROKEE_2019: [{
# Jeep Grand Cherokee 2019, including most 2020 models # Jeep Grand Cherokee 2019, including most 2020 models
55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1538: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
}], }],
} }

@ -28,8 +28,8 @@ class CarControllerParams:
# Curvature rate limits # Curvature rate limits
# TODO: unify field names used by curvature and angle control cars # TODO: unify field names used by curvature and angle control cars
# ~2 m/s^3 up, -3 m/s^3 down # ~2 m/s^3 up, ~-3 m/s^3 down
ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.0002]) ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.00016])
ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.006, 0.00066, 0.00024]) ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.006, 0.00066, 0.00024])
ACCEL_MAX = 2.0 # m/s^s max acceleration ACCEL_MAX = 2.0 # m/s^s max acceleration
@ -165,6 +165,7 @@ FW_VERSIONS = {
b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.abs, 0x760, None): [ (Ecu.abs, 0x760, None): [
b'L1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MC-2D053-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'L1MC-2D053-KB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MC-2D053-KB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -178,6 +179,7 @@ FW_VERSIONS = {
b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7E0, None): [ (Ecu.engine, 0x7E0, None): [
b'LB5A-14C204-BUJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'MB5A-14C204-MD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'MB5A-14C204-MD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',

@ -3,7 +3,7 @@ from common.conversions import Conversions as CV
from common.numpy_fast import interp from common.numpy_fast import interp
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_driver_steer_torque_limits
from selfdrive.car.gm import gmcan from selfdrive.car.gm import gmcan
from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons
@ -73,7 +73,7 @@ class CarController:
if CC.latActive: if CC.latActive:
new_steer = int(round(actuators.steer * self.params.STEER_MAX)) new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
else: else:
apply_steer = 0 apply_steer = 0

@ -370,10 +370,12 @@ FW_VERSIONS = {
b'57114-TWA-A050\x00\x00', b'57114-TWA-A050\x00\x00',
b'57114-TWA-A530\x00\x00', b'57114-TWA-A530\x00\x00',
b'57114-TWA-B520\x00\x00', b'57114-TWA-B520\x00\x00',
b'57114-TWB-H030\x00\x00',
], ],
(Ecu.srs, 0x18da53f1, None): [ (Ecu.srs, 0x18da53f1, None): [
b'77959-TWA-A440\x00\x00', b'77959-TWA-A440\x00\x00',
b'77959-TWA-L420\x00\x00', b'77959-TWA-L420\x00\x00',
b'77959-TWB-H220\x00\x00',
], ],
(Ecu.combinationMeter, 0x18da60f1, None): [ (Ecu.combinationMeter, 0x18da60f1, None): [
b'78109-TWA-A010\x00\x00', b'78109-TWA-A010\x00\x00',
@ -387,6 +389,7 @@ FW_VERSIONS = {
b'78109-TWA-A230\x00\x00', b'78109-TWA-A230\x00\x00',
b'78109-TWA-L010\x00\x00', b'78109-TWA-L010\x00\x00',
b'78109-TWA-L210\x00\x00', b'78109-TWA-L210\x00\x00',
b'78109-TWA-H210\x00\x00',
], ],
(Ecu.shiftByWire, 0x18da0bf1, None): [ (Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TWA-A910\x00\x00', b'54008-TWA-A910\x00\x00',
@ -398,16 +401,19 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x18dab5f1, None): [ (Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TWA-A070\x00\x00', b'36161-TWA-A070\x00\x00',
b'36161-TWA-A330\x00\x00', b'36161-TWA-A330\x00\x00',
b'36161-TWB-H040\x00\x00',
], ],
(Ecu.fwdRadar, 0x18dab0f1, None): [ (Ecu.fwdRadar, 0x18dab0f1, None): [
b'36802-TWA-A070\x00\x00', b'36802-TWA-A070\x00\x00',
b'36802-TWA-A080\x00\x00', b'36802-TWA-A080\x00\x00',
b'36802-TWA-A330\x00\x00', b'36802-TWA-A330\x00\x00',
b'36802-TWB-H060\x00\x00',
], ],
(Ecu.eps, 0x18da30f1, None): [ (Ecu.eps, 0x18da30f1, None): [
b'39990-TVA-A160\x00\x00', b'39990-TVA-A160\x00\x00',
b'39990-TVA-A150\x00\x00', b'39990-TVA-A150\x00\x00',
b'39990-TVA-A340\x00\x00', b'39990-TVA-A340\x00\x00',
b'39990-TWB-H120\x00\x00',
], ],
}, },
CAR.CIVIC: { CAR.CIVIC: {

@ -3,7 +3,7 @@ from common.conversions import Conversions as CV
from common.numpy_fast import clip from common.numpy_fast import clip
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_driver_steer_torque_limits
from selfdrive.car.hyundai import hyundaicanfd, hyundaican from selfdrive.car.hyundai import hyundaicanfd, hyundaican
from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
@ -60,7 +60,7 @@ class CarController:
# steering torque # steering torque
new_steer = int(round(actuators.steer * self.params.STEER_MAX)) new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
if not CC.latActive: if not CC.latActive:
apply_steer = 0 apply_steer = 0

@ -512,7 +512,6 @@ FW_VERSIONS = {
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ', b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ',
b'\xf1\x00DN89110-L0000 \xaa\xaa\xaa\xaa\xaa\xaa\xaa ', b'\xf1\x00DN89110-L0000 \xaa\xaa\xaa\xaa\xaa\xaa\xaa ',
b'\xf1\x8799110L0000\xf1\x00DN8_ SCC F-CUP 1.00 1.00 99110-L0000 ', b'\xf1\x8799110L0000\xf1\x00DN8_ SCC F-CUP 1.00 1.00 99110-L0000 ',
b'\xf1\x8799110L0000\xf1\x00DN8_ SCC FHCUP 1.00 1.00 99110-L0000 ',
], ],
(Ecu.abs, 0x7d1, None): [ (Ecu.abs, 0x7d1, None): [
b'\xf1\x00DN ESC \x07 106 \x07\x01 58910-L0100', b'\xf1\x00DN ESC \x07 106 \x07\x01 58910-L0100',
@ -525,7 +524,6 @@ FW_VERSIONS = {
b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 104\x19\x08\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 104\x19\x08\x01 58910-L0100',
b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100',
b'\xf1\x8758910-L0100\xf1\x00DN ESC \x07 104\x19\x08\x01 58910-L0100', b'\xf1\x8758910-L0100\xf1\x00DN ESC \x07 104\x19\x08\x01 58910-L0100',
b'\xf1\x8758910-L0300\xf1\x00DN ESC \x03 100 \x08\x01 58910-L0300',
b'\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100', b'\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
@ -587,6 +585,7 @@ FW_VERSIONS = {
b'\xf1\x00HT6WA280BLHT6WAD10A1SDN8G25NB4\x00\x00\x00\x00\x00\x00g!l[', b'\xf1\x00HT6WA280BLHT6WAD10A1SDN8G25NB4\x00\x00\x00\x00\x00\x00g!l[',
b'\xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5', b'\xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5',
b'\xf1\x00T02601BL T02832A1 VDN8T25XXX832NS8G\x0e\xfeE', b'\xf1\x00T02601BL T02832A1 VDN8T25XXX832NS8G\x0e\xfeE',
b'\xf1\x00T02601BL T02900A1 VDN8T25XXX900NSCF\xe4!Y',
b'\xf1\x87954A02N060\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5', b'\xf1\x87954A02N060\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5',
b'\xf1\x87SAKFBA2926554GJ2VefVww\x87xwwwww\x88\x87xww\x87wTo\xfb\xffvUo\xff\x8d\x16\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v', b'\xf1\x87SAKFBA2926554GJ2VefVww\x87xwwwww\x88\x87xww\x87wTo\xfb\xffvUo\xff\x8d\x16\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v',
b'\xf1\x87SAKFBA3030524GJ2UVugww\x97yx\x88\x87\x88vw\x87gww\x87wto\xf9\xfffUo\xff\xa2\x0c\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v', b'\xf1\x87SAKFBA3030524GJ2UVugww\x97yx\x88\x87\x88vw\x87gww\x87wto\xf9\xfffUo\xff\xa2\x0c\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v',
@ -1566,6 +1565,7 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401',
], ],
}, },
CAR.TUCSON_4TH_GEN: { CAR.TUCSON_4TH_GEN: {

@ -1,6 +1,6 @@
from cereal import car from cereal import car
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_driver_steer_torque_limits
from selfdrive.car.mazda import mazdacan from selfdrive.car.mazda import mazdacan
from selfdrive.car.mazda.values import CarControllerParams, Buttons from selfdrive.car.mazda.values import CarControllerParams, Buttons
@ -23,8 +23,8 @@ class CarController:
if CC.latActive: if CC.latActive:
# calculate steer and also set limits due to driver torque # calculate steer and also set limits due to driver torque
new_steer = int(round(CC.actuators.steer * CarControllerParams.STEER_MAX)) new_steer = int(round(CC.actuators.steer * CarControllerParams.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last,
CS.out.steeringTorque, CarControllerParams) CS.out.steeringTorque, CarControllerParams)
if CC.cruiseControl.cancel: if CC.cruiseControl.cancel:
# If brake is pressed, let us wait >70ms before trying to disable crz to avoid # If brake is pressed, let us wait >70ms before trying to disable crz to avoid

@ -1,5 +1,5 @@
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_driver_steer_torque_limits
from selfdrive.car.subaru import subarucan from selfdrive.car.subaru import subarucan
from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CarControllerParams from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CarControllerParams
@ -34,7 +34,7 @@ class CarController:
# limits due to driver torque # limits due to driver torque
new_steer = int(round(apply_steer)) new_steer = int(round(apply_steer))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p) apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p)
if not CC.latActive: if not CC.latActive:
apply_steer = 0 apply_steer = 0

@ -1,6 +1,6 @@
from cereal import car from cereal import car
from common.numpy_fast import clip, interp from common.numpy_fast import clip, interp
from selfdrive.car import apply_toyota_steer_torque_limits, create_gas_interceptor_command, make_can_msg from selfdrive.car import apply_meas_steer_torque_limits, create_gas_interceptor_command, make_can_msg
from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_command, \ from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_command, \
create_accel_command, create_acc_cancel_command, \ create_accel_command, create_acc_cancel_command, \
create_fcw_command, create_lta_steer_command create_fcw_command, create_lta_steer_command
@ -60,7 +60,7 @@ class CarController:
# steer torque # steer torque
new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX)) new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
apply_steer = apply_toyota_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.torque_rate_limits) apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.torque_rate_limits)
# Count up to MAX_STEER_RATE_FRAMES, at which point we need to cut torque to avoid a steering fault # Count up to MAX_STEER_RATE_FRAMES, at which point we need to cut torque to avoid a steering fault
if lat_active and abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE: if lat_active and abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE:

@ -115,7 +115,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"), CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]), CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-23", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-23"), CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-23"),
CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"), CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"),
CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"),

@ -3,7 +3,7 @@ from opendbc.can.packer import CANPacker
from common.numpy_fast import clip from common.numpy_fast import clip
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_driver_steer_torque_limits
from selfdrive.car.volkswagen import mqbcan, pqcan from selfdrive.car.volkswagen import mqbcan, pqcan
from selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams from selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams
@ -44,7 +44,7 @@ class CarController:
if CC.latActive: if CC.latActive:
new_steer = int(round(actuators.steer * self.CCP.STEER_MAX)) new_steer = int(round(actuators.steer * self.CCP.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP) apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP)
if apply_steer == 0: if apply_steer == 0:
hcaEnabled = False hcaEnabled = False
self.hcaEnabledFrameCount = 0 self.hcaEnabledFrameCount = 0

@ -739,12 +739,14 @@ FW_VERSIONS = {
b'\xf1\x8783A907115B \xf1\x890005', b'\xf1\x8783A907115B \xf1\x890005',
b'\xf1\x8783A907115F \xf1\x890002', b'\xf1\x8783A907115F \xf1\x890002',
b'\xf1\x8783A907115G \xf1\x890001', b'\xf1\x8783A907115G \xf1\x890001',
b'\xf1\x8783A907115K \xf1\x890001',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158DT\xf1\x893698', b'\xf1\x8709G927158DT\xf1\x893698',
b'\xf1\x8709G927158FM\xf1\x893757', b'\xf1\x8709G927158FM\xf1\x893757',
b'\xf1\x8709G927158GC\xf1\x893821', b'\xf1\x8709G927158GC\xf1\x893821',
b'\xf1\x8709G927158GD\xf1\x893820', b'\xf1\x8709G927158GD\xf1\x893820',
b'\xf1\x8709G927158GM\xf1\x893936',
b'\xf1\x870D9300043 \xf1\x895202', b'\xf1\x870D9300043 \xf1\x895202',
b'\xf1\x870DL300011N \xf1\x892001', b'\xf1\x870DL300011N \xf1\x892001',
b'\xf1\x870DL300011N \xf1\x892012', b'\xf1\x870DL300011N \xf1\x892012',
@ -775,6 +777,7 @@ FW_VERSIONS = {
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\00521A60804A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\00521A60804A1',
b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x002SA6092SOM',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572AA\xf1\x890396', b'\xf1\x872Q0907572AA\xf1\x890396',
@ -1049,6 +1052,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027DD\xf1\x893123', b'\xf1\x8704E906027DD\xf1\x893123',
b'\xf1\x8704L906026DE\xf1\x895418', b'\xf1\x8704L906026DE\xf1\x895418',
b'\xf1\x8704L906026HT\xf1\x893617',
b'\xf1\x875NA907115E \xf1\x890003', b'\xf1\x875NA907115E \xf1\x890003',
b'\xf1\x875NA907115E \xf1\x890005', b'\xf1\x875NA907115E \xf1\x890005',
], ],
@ -1060,17 +1064,20 @@ FW_VERSIONS = {
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
b'\xf1\x873Q0959655BJ\xf1\x890703\xf1\x82\x0e1213001211001205212111052100', b'\xf1\x873Q0959655BJ\xf1\x890703\xf1\x82\x0e1213001211001205212111052100',
b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1213001211001244212111442100',
b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\x0e1213001211001205212112052100', b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\x0e1213001211001205212112052100',
b'\xf1\x873Q0959655CQ\xf1\x890720\xf1\x82\x0e1213111211001205212112052111', b'\xf1\x873Q0959655CQ\xf1\x890720\xf1\x82\x0e1213111211001205212112052111',
], ],
(Ecu.eps, 0x712, None): [ (Ecu.eps, 0x712, None): [
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6050405', b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6050405',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6060405', b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6060405',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820527T6070405',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T600G600', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T600G600',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572Q \xf1\x890342', b'\xf1\x872Q0907572Q \xf1\x890342',
b'\xf1\x872Q0907572R \xf1\x890372', b'\xf1\x872Q0907572R \xf1\x890372',
b'\xf1\x872Q0907572AA\xf1\x890396',
], ],
}, },
CAR.SKODA_OCTAVIA_MK3: { CAR.SKODA_OCTAVIA_MK3: {
@ -1140,6 +1147,7 @@ FW_VERSIONS = {
b'\xf1\x8704L906026FP\xf1\x891196', b'\xf1\x8704L906026FP\xf1\x891196',
b'\xf1\x8704L906026KB\xf1\x894071', b'\xf1\x8704L906026KB\xf1\x894071',
b'\xf1\x8704L906026KD\xf1\x894798', b'\xf1\x8704L906026KD\xf1\x894798',
b'\xf1\x873G0906259 \xf1\x890004',
b'\xf1\x873G0906259B \xf1\x890002', b'\xf1\x873G0906259B \xf1\x890002',
b'\xf1\x873G0906264A \xf1\x890002', b'\xf1\x873G0906264A \xf1\x890002',
], ],
@ -1149,12 +1157,14 @@ FW_VERSIONS = {
b'\xf1\x870D9300012 \xf1\x894940', b'\xf1\x870D9300012 \xf1\x894940',
b'\xf1\x870D9300041H \xf1\x894905', b'\xf1\x870D9300041H \xf1\x894905',
b'\xf1\x870GC300043 \xf1\x892301', b'\xf1\x870GC300043 \xf1\x892301',
b'\xf1\x870D9300043F \xf1\x895202',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\x12111200111121001121110012211292221111', b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\x12111200111121001121110012211292221111',
b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\022111200111121001121118112231292221111', b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\022111200111121001121118112231292221111',
b'\xf1\x875Q0959655AK\xf1\x890130\xf1\x82\022111200111121001121110012211292221111', b'\xf1\x875Q0959655AK\xf1\x890130\xf1\x82\022111200111121001121110012211292221111',
b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\02331310031313100313131013141319331413100', b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\02331310031313100313131013141319331413100',
b'\xf1\x875Q0959655CA\xf1\x890403\xf1\x82\x1331310031313100313151013141319331423100',
], ],
(Ecu.eps, 0x712, None): [ (Ecu.eps, 0x712, None): [
b'\xf1\x875Q0909143K \xf1\x892033\xf1\x820514UZ070203', b'\xf1\x875Q0909143K \xf1\x892033\xf1\x820514UZ070203',

@ -112,9 +112,6 @@ class Controls:
if not self.disengage_on_accelerator: if not self.disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
if self.CP.dashcamOnly and self.params.get_bool("DashcamOverride"):
self.CP.dashcamOnly = False
# read params # read params
self.is_metric = self.params.get_bool("IsMetric") self.is_metric = self.params.get_bool("IsMetric")
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")

@ -317,9 +317,9 @@ def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM
def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
text = "Cruise Mode Disabled" text = "Enable Adaptive Cruise to Engage"
if CP.carName == "honda": if CP.carName == "honda":
text = "Main Switch Off" text = "Enable Main Switch to Engage"
return NoEntryAlert(text) return NoEntryAlert(text)

@ -122,8 +122,9 @@ class LongitudinalPlanner:
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j) self.mpc.update(sm['radarState'], v_cruise, x, v, a, j)
self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution) self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.a_solution)
self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N] self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N]
self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution) self.a_desired_trajectory = self.a_desired_trajectory_full[:CONTROL_N]
self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution)
# TODO counter is only needed because radar is glitchy, remove once radar is gone # TODO counter is only needed because radar is glitchy, remove once radar is gone

@ -22,6 +22,7 @@ def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner):
uiPlan.position.x = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,0]).tolist() uiPlan.position.x = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,0]).tolist()
uiPlan.position.y = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,1]).tolist() uiPlan.position.y = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,1]).tolist()
uiPlan.position.z = np.interp(plan_odo, model_odo, lateral_planner.path_xyz[:,2]).tolist() uiPlan.position.z = np.interp(plan_odo, model_odo, lateral_planner.path_xyz[:,2]).tolist()
uiPlan.accel = longitudinal_planner.a_desired_trajectory_full.tolist()
pm.send('uiPlan', ui_send) pm.send('uiPlan', ui_send)
def plannerd_thread(sm=None, pm=None): def plannerd_thread(sm=None, pm=None):

@ -74,7 +74,7 @@ if __name__ == "__main__":
elif msg.which() == "carParams": elif msg.which() == "carParams":
CP = msg.carParams CP = msg.carParams
car_fw = CP.carFw car_fw = [fw for fw in CP.carFw if not fw.logging]
if len(car_fw) == 0: if len(car_fw) == 0:
print("no fw") print("no fw")
break break

@ -97,19 +97,25 @@ class Laikad:
def get_lsq_fix(self, t, measurements): def get_lsq_fix(self, t, measurements):
if self.last_fix_t is None or abs(self.last_fix_t - t) > 0: if self.last_fix_t is None or abs(self.last_fix_t - t) > 0:
min_measurements = 7 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 6 min_measurements = 5 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 4
position_solution, pr_residuals = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) position_solution, pr_residuals, pos_std = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements)
if len(position_solution) < 3: if len(position_solution) < 3:
return None return None
position_estimate = position_solution[:3] position_estimate = position_solution[:3]
#TODO median abs residual is decent estimate of std, can be improved with measurements stds and/or DOP
position_std = np.median(np.abs(pr_residuals)) * np.ones(3) position_std_residual = np.median(np.abs(pr_residuals))
velocity_solution, prr_residuals = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements) position_std = np.median(np.abs(pos_std))/10
position_std = max(position_std_residual, position_std) * np.ones(3)
velocity_solution, prr_residuals, vel_std = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements)
if len(velocity_solution) < 3: if len(velocity_solution) < 3:
return None return None
velocity_estimate = velocity_solution[:3] velocity_estimate = velocity_solution[:3]
velocity_std = np.median(np.abs(prr_residuals)) * np.ones(3)
velocity_std_residual = np.median(np.abs(prr_residuals))
velocity_std = np.median(np.abs(vel_std))/10
velocity_std = max(velocity_std, velocity_std_residual) * np.ones(3)
return position_estimate, position_std, velocity_estimate, velocity_std return position_estimate, position_std, velocity_estimate, velocity_std
def is_good_report(self, gnss_msg): def is_good_report(self, gnss_msg):
@ -187,10 +193,10 @@ class Laikad:
elif self.is_good_report(gnss_msg): elif self.is_good_report(gnss_msg):
week, tow, new_meas = self.read_report(gnss_msg) week, tow, new_meas = self.read_report(gnss_msg)
self.gps_week = week
if len(new_meas) == 0: if len(new_meas) == 0:
return None return None
self.gps_week = week
t = gnss_mono_time * 1e-9 t = gnss_mono_time * 1e-9
if week > 0: if week > 0:
self.got_first_gnss_msg = True self.got_first_gnss_msg = True

@ -160,7 +160,7 @@ class TestLaikad(unittest.TestCase):
laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT)
correct_msgs = verify_messages(self.logs, laikad) correct_msgs = verify_messages(self.logs, laikad)
correct_msgs_expected = 554 correct_msgs_expected = 559
self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len(correct_msgs))
self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid]))
@ -180,7 +180,7 @@ class TestLaikad(unittest.TestCase):
laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV) laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV)
# Disable fetch_orbits to test NAV only # Disable fetch_orbits to test NAV only
correct_msgs = verify_messages(self.logs, laikad) correct_msgs = verify_messages(self.logs, laikad)
correct_msgs_expected = 554 correct_msgs_expected = 559
self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len(correct_msgs))
self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid]))
@ -195,8 +195,8 @@ class TestLaikad(unittest.TestCase):
downloader_mock.side_effect = DownloadFailed downloader_mock.side_effect = DownloadFailed
laikad = Laikad(auto_update=False) laikad = Laikad(auto_update=False)
correct_msgs = verify_messages(self.logs, laikad) correct_msgs = verify_messages(self.logs, laikad)
self.assertEqual(0, len(correct_msgs)) self.assertEqual(255, len(correct_msgs))
self.assertEqual(0, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) self.assertEqual(255, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid]))
def test_laika_get_orbits(self): def test_laika_get_orbits(self):
laikad = Laikad(auto_update=False) laikad = Laikad(auto_update=False)
@ -282,7 +282,7 @@ class TestLaikad(unittest.TestCase):
gm = msg.gnssMeasurements gm = msg.gnssMeasurements
if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid:
cnt += 1 cnt += 1
self.assertEqual(cnt, 554) self.assertEqual(cnt, 559)
def dict_has_values(self, dct): def dict_has_values(self, dct):
self.assertGreater(len(dct), 0) self.assertGreater(len(dct), 0)

@ -36,7 +36,7 @@ def manager_init() -> None:
default_params: List[Tuple[str, Union[str, bytes]]] = [ default_params: List[Tuple[str, Union[str, bytes]]] = [
("CompletedTrainingVersion", "0"), ("CompletedTrainingVersion", "0"),
("DisengageOnAccelerator", "1"), ("DisengageOnAccelerator", "0"),
("GsmMetered", "1"), ("GsmMetered", "1"),
("HasAcceptedTerms", "0"), ("HasAcceptedTerms", "0"),
("LanguageSetting", "main_en"), ("LanguageSetting", "main_en"),

@ -70,7 +70,7 @@ lenv.Program('_dmonitoringmodeld', [
if use_thneed and arch == "larch64" or GetOption('pc_thneed'): if use_thneed and arch == "larch64" or GetOption('pc_thneed'):
fn = File("models/supercombo").abspath fn = File("models/supercombo").abspath
tinygrad_opts = ["NATIVE_EXPLOG=1", "VALIDHACKS=1", "OPTWG=1", "IMAGE=2", "GPU=1", "CLCACHE=0"] tinygrad_opts = ["NATIVE_EXPLOG=1", "VALIDHACKS=1", "OPTLOCAL=1", "IMAGE=2", "GPU=1", "ENABLE_METHOD_CACHE=1"]
if not GetOption('pc_thneed'): if not GetOption('pc_thneed'):
# use FLOAT16 on device for speed + don't cache the CL kernels for space # use FLOAT16 on device for speed + don't cache the CL kernels for space
tinygrad_opts += ["FLOAT16=1", "PYOPENCL_NO_CACHE=1"] tinygrad_opts += ["FLOAT16=1", "PYOPENCL_NO_CACHE=1"]

@ -1 +1 @@
ba947edbb131a2a36ced7c490dfcf3280ad5b167 ab64afd1abd1059c14f50c67b51e5ef89029f216

@ -449,9 +449,6 @@ def setup_env(simulation=False, CP=None, cfg=None, controlsState=None):
if CP.openpilotLongitudinalControl: if CP.openpilotLongitudinalControl:
params.put_bool("ExperimentalLongitudinalEnabled", True) params.put_bool("ExperimentalLongitudinalEnabled", True)
# controlsd process configuration assume all routes are out of dashcam
params.put_bool("DashcamOverride", True)
def python_replay_process(cfg, lr, fingerprint=None): def python_replay_process(cfg, lr, fingerprint=None):
sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub] sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub]

@ -1 +1 @@
5f45771d28c8e7112d474edcdd991689c6440acf f9c7e05b836c4bff364978752e82d64b90f9d6e6

@ -29,12 +29,12 @@ source_segments = [
("SUBARU", "341dccd5359e3c97|2022-09-12--10-35-33--3"), # SUBARU.OUTBACK ("SUBARU", "341dccd5359e3c97|2022-09-12--10-35-33--3"), # SUBARU.OUTBACK
("GM", "0c58b6a25109da2b|2021-02-23--16-35-50--11"), # GM.VOLT ("GM", "0c58b6a25109da2b|2021-02-23--16-35-50--11"), # GM.VOLT
("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.BOLT_EUV ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.BOLT_EUV
("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.BRONCO_SPORT_MK1
("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.XTRAIL ("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.XTRAIL
("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.GOLF ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.GOLF
("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.CX9_2021 ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.CX9_2021
# Enable when port is tested and dashcamOnly is no longer set # Enable when port is tested and dashcamOnly is no longer set
#("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.BRONCO_SPORT_MK1
#("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS #("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS
#("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.PASSAT_NMS #("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.PASSAT_NMS
] ]
@ -53,7 +53,6 @@ segments = [
("SUBARU", "regen1E72BBDCED5|2022-09-27--15-55-31--0"), ("SUBARU", "regen1E72BBDCED5|2022-09-27--15-55-31--0"),
("GM", "regen45B05A80EF6|2022-09-27--15-57-22--0"), ("GM", "regen45B05A80EF6|2022-09-27--15-57-22--0"),
("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"),
("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"),
("NISSAN", "regenC19D899B46D|2022-09-27--15-59-13--0"), ("NISSAN", "regenC19D899B46D|2022-09-27--15-59-13--0"),
("VOLKSWAGEN", "regenD8F7AC4BD0D|2022-09-27--16-41-45--0"), ("VOLKSWAGEN", "regenD8F7AC4BD0D|2022-09-27--16-41-45--0"),
("MAZDA", "regenFC3F9ECBB64|2022-09-27--16-03-09--0"), ("MAZDA", "regenFC3F9ECBB64|2022-09-27--16-03-09--0"),

@ -27,7 +27,7 @@ void DriverViewWindow::mouseReleaseEvent(QMouseEvent* e) {
} }
DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverStateV2"}), QWidget(parent) { DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverStateV2"}), QWidget(parent) {
face_img = loadPixmap("../assets/img_driver_face.png", {FACE_IMG_SIZE, FACE_IMG_SIZE}); face_img = loadPixmap("../assets/img_driver_face_static.png", {FACE_IMG_SIZE, FACE_IMG_SIZE});
} }
void DriverViewScene::showEvent(QShowEvent* event) { void DriverViewScene::showEvent(QShowEvent* event) {

@ -573,7 +573,10 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s)
const int arc_l = 133; const int arc_l = 133;
const float arc_t_default = 6.7; const float arc_t_default = 6.7;
const float arc_t_extend = 12.0; const float arc_t_extend = 12.0;
QColor arc_color = QColor::fromRgbF(0.09, 0.945, 0.26, 0.4*(1.0-dm_fade_state)*(s->engaged())); QColor arc_color = QColor::fromRgbF(0.545 - 0.445 * s->engaged(),
0.545 + 0.4 * s->engaged(),
0.545 - 0.285 * s->engaged(),
0.4 * (1.0 - dm_fade_state));
float delta_x = -scene.driver_pose_sins[1] * arc_l / 2; float delta_x = -scene.driver_pose_sins[1] * arc_l / 2;
float delta_y = -scene.driver_pose_sins[0] * arc_l / 2; float delta_y = -scene.driver_pose_sins[0] * arc_l / 2;
painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[1] * 5.0), Qt::SolidLine, Qt::RoundCap)); painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[1] * 5.0), Qt::SolidLine, Qt::RoundCap));

@ -34,7 +34,7 @@ bool is_elf(char *fname) {
void Setup::download(QString url) { void Setup::download(QString url) {
CURL *curl = curl_easy_init(); CURL *curl = curl_easy_init();
if (!curl) { if (!curl) {
emit finished(false); emit finished(url, tr("Something went wrong. Reboot the device."));
return; return;
} }
@ -53,15 +53,24 @@ void Setup::download(QString url) {
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str()); curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
int ret = curl_easy_perform(curl); int ret = curl_easy_perform(curl);
long res_status = 0; long res_status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status);
if (ret == CURLE_OK && res_status == 200 && is_elf(tmpfile)) {
rename(tmpfile, "/tmp/installer"); if (ret != CURLE_OK || res_status != 200) {
emit finished(true); emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good."));
} else if (!is_elf(tmpfile)) {
emit finished(url, tr("No custom software found at this URL."));
} else { } else {
emit finished(false); rename(tmpfile, "/tmp/installer");
FILE *fp_url = fopen("/tmp/installer_url", "w");
fprintf(fp_url, "%s", url.toStdString().c_str());
fclose(fp_url);
emit finished(url);
} }
curl_slist_free_all(list); curl_slist_free_all(list);
@ -234,10 +243,10 @@ QWidget * Setup::downloading() {
return widget; return widget;
} }
QWidget * Setup::download_failed() { QWidget * Setup::download_failed(QLabel *url, QLabel *body) {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setContentsMargins(55, 225, 55, 55); main_layout->setContentsMargins(55, 185, 55, 55);
main_layout->setSpacing(0); main_layout->setSpacing(0);
QLabel *title = new QLabel(tr("Download Failed")); QLabel *title = new QLabel(tr("Download Failed"));
@ -246,7 +255,13 @@ QWidget * Setup::download_failed() {
main_layout->addSpacing(67); main_layout->addSpacing(67);
QLabel *body = new QLabel(tr("Ensure the entered URL is valid, and the device’s internet connection is good.")); url->setWordWrap(true);
url->setAlignment(Qt::AlignTop | Qt::AlignLeft);
url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;");
main_layout->addWidget(url);
main_layout->addSpacing(48);
body->setWordWrap(true); body->setWordWrap(true);
body->setAlignment(Qt::AlignTop | Qt::AlignLeft); body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;"); body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;");
@ -271,7 +286,7 @@ QWidget * Setup::download_failed() {
restart->setProperty("primary", true); restart->setProperty("primary", true);
blayout->addWidget(restart); blayout->addWidget(restart);
QObject::connect(restart, &QPushButton::clicked, this, [=]() { QObject::connect(restart, &QPushButton::clicked, this, [=]() {
setCurrentIndex(2); setCurrentIndex(1);
}); });
widget->setStyleSheet(R"( widget->setStyleSheet(R"(
@ -304,15 +319,19 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
downloading_widget = downloading(); downloading_widget = downloading();
addWidget(downloading_widget); addWidget(downloading_widget);
failed_widget = download_failed(); QLabel *url_label = new QLabel();
QLabel *body_label = new QLabel();
failed_widget = download_failed(url_label, body_label);
addWidget(failed_widget); addWidget(failed_widget);
QObject::connect(this, &Setup::finished, [=](bool success) { QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) {
// hide setup on success qDebug() << "finished" << url << error;
qDebug() << "finished" << success; if (error.isEmpty()) {
if (success) { // hide setup on success
QTimer::singleShot(3000, this, &QWidget::hide); QTimer::singleShot(3000, this, &QWidget::hide);
} else { } else {
url_label->setText(url);
body_label->setText(error);
setCurrentWidget(failed_widget); setCurrentWidget(failed_widget);
} }
}); });

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QLabel>
#include <QStackedWidget> #include <QStackedWidget>
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
@ -15,13 +16,13 @@ private:
QWidget *getting_started(); QWidget *getting_started();
QWidget *network_setup(); QWidget *network_setup();
QWidget *downloading(); QWidget *downloading();
QWidget *download_failed(); QWidget *download_failed(QLabel *url, QLabel *body);
QWidget *failed_widget; QWidget *failed_widget;
QWidget *downloading_widget; QWidget *downloading_widget;
signals: signals:
void finished(bool success); void finished(const QString &url, const QString &error = "");
public slots: public slots:
void nextPage(); void nextPage();

@ -703,6 +703,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation>Von neuem beginnen</translation> <translation>Von neuem beginnen</translation>
</message> </message>
<message>
<source>No custom software found at this URL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -701,6 +701,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>No custom software found at this URL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -403,7 +403,7 @@ location set</source>
</message> </message>
<message> <message>
<source>Waiting for GPS</source> <source>Waiting for GPS</source>
<translation>GPS </translation> <translation>GPS </translation>
</message> </message>
</context> </context>
<context> <context>
@ -592,16 +592,17 @@ location set</source>
</message> </message>
<message> <message>
<source>Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device.</source> <source>Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device.</source>
<translation type="unfinished"></translation> <translation> . . .</translation>
</message> </message>
<message> <message>
<source>Press confirm to erase all content and settings. Press cancel to resume boot.</source> <source>Press confirm to erase all content and settings. Press cancel to resume boot.</source>
<translation type="unfinished"></translation> <translation> . .</translation>
</message> </message>
<message> <message>
<source>Resetting device... <source>Resetting device...
This may take up to a minute.</source> This may take up to a minute.</source>
<translation type="unfinished"></translation> <translation> ...
1 .</translation>
</message> </message>
</context> </context>
<context> <context>
@ -701,6 +702,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation> </translation> <translation> </translation>
</message> </message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation> . .</translation>
</message>
<message>
<source>No custom software found at this URL.</source>
<translation> URL에서 .</translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -705,6 +705,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation>Inicializar</translation> <translation>Inicializar</translation>
</message> </message>
<message>
<source>No custom software found at this URL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -311,18 +311,6 @@
<source>Installing...</source> <source>Installing...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>
<message>
<source>Receiving objects: </source>
<translation>: </translation>
</message>
<message>
<source>Resolving deltas: </source>
<translation>: </translation>
</message>
<message>
<source>Updating files: </source>
<translation>: </translation>
</message>
</context> </context>
<context> <context>
<name>MapETA</name> <name>MapETA</name>
@ -586,18 +574,10 @@ location set</source>
<source>Are you sure you want to reset your device?</source> <source>Are you sure you want to reset your device?</source>
<translation>?</translation> <translation>?</translation>
</message> </message>
<message>
<source>Resetting device...</source>
<translation>...</translation>
</message>
<message> <message>
<source>System Reset</source> <source>System Reset</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot.</source>
<translation> </translation>
</message>
<message> <message>
<source>Cancel</source> <source>Cancel</source>
<translation></translation> <translation></translation>
@ -611,8 +591,18 @@ location set</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Unable to mount data partition. Press confirm to reset your device.</source> <source>Resetting device...
<translation> </translation> This may take up to a minute.</source>
<translation>...
</translation>
</message>
<message>
<source>Press confirm to erase all content and settings. Press cancel to resume boot.</source>
<translation> </translation>
</message>
<message>
<source>Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device.</source>
<translation> </translation>
</message> </message>
</context> </context>
<context> <context>
@ -684,18 +674,6 @@ location set</source>
<source>Waiting for internet</source> <source>Waiting for internet</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Choose Software to Install</source>
<translation></translation>
</message>
<message>
<source>Dashcam</source>
<translation></translation>
</message>
<message>
<source>Custom Software</source>
<translation></translation>
</message>
<message> <message>
<source>Enter URL</source> <source>Enter URL</source>
<translation> URL</translation> <translation> URL</translation>
@ -724,6 +702,14 @@ location set</source>
<source>Start over</source> <source>Start over</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation> </translation>
</message>
<message>
<source>No custom software found at this URL.</source>
<translation> URL </translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -699,6 +699,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>No custom software found at this URL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -701,6 +701,14 @@ This may take up to a minute.</source>
<source>Start over</source> <source>Start over</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>No custom software found at this URL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Something went wrong. Reboot the device.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SetupWidget</name> <name>SetupWidget</name>

@ -3,6 +3,7 @@ import unittest
import time import time
import math import math
from dataclasses import dataclass from dataclasses import dataclass
from tabulate import tabulate
from system.hardware import HARDWARE, TICI from system.hardware import HARDWARE, TICI
from system.hardware.tici.power_monitor import get_power from system.hardware.tici.power_monitor import get_power
@ -20,7 +21,7 @@ class Proc:
PROCS = [ PROCS = [
Proc('camerad', 2.15), Proc('camerad', 2.15),
Proc('modeld', 1.15, atol=0.2), Proc('modeld', 0.93, atol=0.2),
Proc('dmonitoringmodeld', 0.4), Proc('dmonitoringmodeld', 0.4),
Proc('encoderd', 0.23), Proc('encoderd', 0.23),
] ]
@ -58,15 +59,16 @@ class TestPowerDraw(unittest.TestCase):
manager_cleanup() manager_cleanup()
print("-"*35) tab = []
print(f"Baseline {baseline:.2f}W\n") tab.append(['process', 'expected (W)', 'current (W)'])
for proc in PROCS: for proc in PROCS:
cur = used[proc.name] cur = used[proc.name]
expected = proc.power expected = proc.power
print(f"{proc.name.ljust(20)} {expected:.2f}W {cur:.2f}W") tab.append([proc.name, round(expected, 2), round(cur, 2)])
with self.subTest(proc=proc.name): with self.subTest(proc=proc.name):
self.assertTrue(math.isclose(cur, expected, rel_tol=proc.rtol, abs_tol=proc.atol)) self.assertTrue(math.isclose(cur, expected, rel_tol=proc.rtol, abs_tol=proc.atol))
print("-"*35) print(tabulate(tab))
print(f"Baseline {baseline:.2f}W\n")
if __name__ == "__main__": if __name__ == "__main__":

@ -1 +1 @@
Subproject commit 2e1d47b16625ff343516287cdd9e4bcb26f5c4ef Subproject commit d8dda2af3afcef0bb772fff580cfa8b3eabf7f69

@ -293,7 +293,6 @@ void BinaryViewModel::updateState() {
double max_f = 255.0; double max_f = 255.0;
double factor = 0.25; double factor = 0.25;
double scaler = max_f / log2(1.0 + factor); double scaler = max_f / log2(1.0 + factor);
char hex[3] = {'\0'};
for (int i = 0; i < binary.size(); ++i) { for (int i = 0; i < binary.size(); ++i) {
for (int j = 0; j < 8; ++j) { for (int j = 0; j < 8; ++j) {
auto &item = items[i * column_count + j]; auto &item = items[i * column_count + j];
@ -305,9 +304,7 @@ void BinaryViewModel::updateState() {
double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f); double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f);
item.bg_color.setAlpha(alpha); item.bg_color.setAlpha(alpha);
} }
hex[0] = toHex(binary[i] >> 4); items[i * column_count + 8].val = toHex(binary[i]);
hex[1] = toHex(binary[i] & 0xf);
items[i * column_count + 8].val = hex;
items[i * column_count + 8].bg_color = last_msg.colors[i]; items[i * column_count + 8].bg_color = last_msg.colors[i];
} }
for (int i = binary.size() * column_count; i < items.size(); ++i) { for (int i = binary.size() * column_count; i < items.size(); ++i) {
@ -375,7 +372,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
bg.setAlpha(std::max(50, bg.alpha())); bg.setAlpha(std::max(50, bg.alpha()));
} }
painter->fillRect(option.rect, bg); painter->fillRect(option.rect, bg);
painter->setPen(Qt::black); painter->setPen(option.palette.color(QPalette::Text));
} }
} }

@ -1,5 +1,6 @@
#include "tools/cabana/chartswidget.h" #include "tools/cabana/chartswidget.h"
#include <QActionGroup>
#include <QApplication> #include <QApplication>
#include <QCompleter> #include <QCompleter>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@ -24,11 +25,12 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
// toolbar // toolbar
QToolBar *toolbar = new QToolBar(tr("Charts"), this); QToolBar *toolbar = new QToolBar(tr("Charts"), this);
toolbar->setIconSize({16, 16}); int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
toolbar->setIconSize({icon_size, icon_size});
QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Plot")); QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Plot"));
toolbar->addWidget(title_label = new QLabel()); toolbar->addWidget(title_label = new QLabel());
title_label->setContentsMargins(0, 0, 12, 0); title_label->setContentsMargins(0, 0, style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0);
QMenu *menu = new QMenu(this); QMenu *menu = new QMenu(this);
for (int i = 0; i < MAX_COLUMN_COUNT; ++i) { for (int i = 0; i < MAX_COLUMN_COUNT; ++i) {
@ -76,8 +78,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
main_layout->addWidget(charts_scroll); main_layout->addWidget(charts_scroll);
// init settings // init settings
use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > use_dark_theme = QApplication::palette().color(QPalette::WindowText).value() >
QApplication::style()->standardPalette().color(QPalette::Background).value(); QApplication::palette().color(QPalette::Background).value();
column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT); column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT);
max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60);
display_range = {0, max_chart_range}; display_range = {0, max_chart_range};
@ -183,7 +185,7 @@ void ChartsWidget::settingChanged() {
range_slider->setRange(1, settings.max_cached_minutes * 60); range_slider->setRange(1, settings.max_cached_minutes * 60);
for (auto c : charts) { for (auto c : charts) {
c->setFixedHeight(settings.chart_height); c->setFixedHeight(settings.chart_height);
c->setSeriesType(settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter); c->setSeriesType((SeriesType)settings.chart_series_type);
} }
} }
@ -309,7 +311,7 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
// ChartView // ChartView
ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter; series_type = (SeriesType)settings.chart_series_type;
QChart *chart = new QChart(); QChart *chart = new QChart();
chart->setBackgroundVisible(false); chart->setBackgroundVisible(false);
axis_x = new QValueAxis(this); axis_x = new QValueAxis(this);
@ -321,44 +323,58 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
chart->setMargins({0, 0, 0, 0}); chart->setMargins({0, 0, 0, 0});
background = new QGraphicsRectItem(chart); background = new QGraphicsRectItem(chart);
background->setBrush(Qt::white); background->setBrush(QApplication::palette().color(QPalette::Base));
background->setPen(Qt::NoPen); background->setPen(Qt::NoPen);
background->setZValue(chart->zValue() - 1); background->setZValue(chart->zValue() - 1);
move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart); setChart(chart);
createToolButtons();
setRenderHint(QPainter::Antialiasing);
// TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand);
QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated);
QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated);
}
void ChartView::createToolButtons() {
move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart());
move_icon->setToolTip(tr("Drag and drop to combine charts")); move_icon->setToolTip(tr("Drag and drop to combine charts"));
QToolButton *remove_btn = toolButton("x", tr("Remove Chart")); QToolButton *remove_btn = toolButton("x", tr("Remove Chart"));
close_btn_proxy = new QGraphicsProxyWidget(chart); close_btn_proxy = new QGraphicsProxyWidget(chart());
close_btn_proxy->setWidget(remove_btn); close_btn_proxy->setWidget(remove_btn);
close_btn_proxy->setZValue(chart->zValue() + 11); close_btn_proxy->setZValue(chart()->zValue() + 11);
QToolButton *manage_btn = toolButton("list", ""); // series types
QMenu *menu = new QMenu(this); QMenu *menu = new QMenu(this);
line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); }); auto change_series_group = new QActionGroup(menu);
line_series_action->setCheckable(true); change_series_group->setExclusive(true);
line_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeLine); QStringList types{tr("line"), tr("Step Line"), tr("Scatter")};
scatter_series_action = menu->addAction(tr("Scatter"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeScatter); }); for (int i = 0; i < types.size(); ++i) {
scatter_series_action->setCheckable(true); QAction *act = new QAction(types[i], change_series_group);
scatter_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeScatter); act->setData(i);
act->setCheckable(true);
act->setChecked(i == (int)series_type);
menu->addAction(act);
}
menu->addSeparator(); menu->addSeparator();
menu->addAction(tr("Manage series"), this, &ChartView::manageSeries); menu->addAction(tr("Manage series"), this, &ChartView::manageSeries);
QToolButton *manage_btn = toolButton("list", "");
manage_btn->setMenu(menu); manage_btn->setMenu(menu);
manage_btn->setPopupMode(QToolButton::InstantPopup); manage_btn->setPopupMode(QToolButton::InstantPopup);
manage_btn_proxy = new QGraphicsProxyWidget(chart); manage_btn_proxy = new QGraphicsProxyWidget(chart());
manage_btn_proxy->setWidget(manage_btn); manage_btn_proxy->setWidget(manage_btn);
manage_btn_proxy->setZValue(chart->zValue() + 11); manage_btn_proxy->setZValue(chart()->zValue() + 11);
setChart(chart);
setRenderHint(QPainter::Antialiasing);
// TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand);
QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated);
QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated);
QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove);
QObject::connect(change_series_group, &QActionGroup::triggered, [this](QAction *action) {
setSeriesType((SeriesType)action->data().toInt());
});
} }
void ChartView::addSeries(const MessageId &msg_id, const Signal *sig) { void ChartView::addSeries(const MessageId &msg_id, const Signal *sig) {
@ -428,10 +444,12 @@ void ChartView::manageSeries() {
void ChartView::resizeEvent(QResizeEvent *event) { void ChartView::resizeEvent(QResizeEvent *event) {
updatePlotArea(align_to); updatePlotArea(align_to);
int x = event->size().width() - close_btn_proxy->size().width() - 11; int top_margin = style()->pixelMetric(QStyle::PM_LayoutTopMargin);
close_btn_proxy->setPos(x, 8); int spacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); int x = event->size().width() - close_btn_proxy->size().width() - style()->pixelMetric(QStyle::PM_LayoutRightMargin);
move_icon->setPos(11, 8); close_btn_proxy->setPos(x, top_margin);
manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - spacing, top_margin);
move_icon->setPos(style()->pixelMetric(QStyle::PM_LayoutLeftMargin), top_margin);
QChartView::resizeEvent(event); QChartView::resizeEvent(event);
} }
@ -476,7 +494,7 @@ void ChartView::updateSeriesPoints() {
int num_points = std::max<int>(end - begin, 1); int num_points = std::max<int>(end - begin, 1);
int pixels_per_point = width() / num_points; int pixels_per_point = width() / num_points;
if (series_type == QAbstractSeries::SeriesTypeScatter) { if (series_type == SeriesType::Scatter) {
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8)); ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8));
} else { } else {
s.series->setPointsVisible(pixels_per_point > 20); s.series->setPointsVisible(pixels_per_point > 20);
@ -490,7 +508,9 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
if (!sig || s.sig == sig) { if (!sig || s.sig == sig) {
if (clear) { if (clear) {
s.vals.clear(); s.vals.clear();
s.step_vals.clear();
s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz
s.step_vals.reserve(settings.max_cached_minutes * 60 * 100 * 2);
s.last_value_mono_time = 0; s.last_value_mono_time = 0;
} }
s.series->setColor(getColor(s.sig)); s.series->setColor(getColor(s.sig));
@ -498,6 +518,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
struct Chunk { struct Chunk {
std::vector<Event *>::const_iterator first, second; std::vector<Event *>::const_iterator first, second;
QVector<QPointF> vals; QVector<QPointF> vals;
QVector<QPointF> step_vals;
}; };
// split into one minitue chunks // split into one minitue chunks
QVector<Chunk> chunks; QVector<Chunk> chunks;
@ -510,6 +531,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
QtConcurrent::blockingMap(chunks, [&](Chunk &chunk) { QtConcurrent::blockingMap(chunks, [&](Chunk &chunk) {
chunk.vals.reserve(60 * 100); // 100 hz chunk.vals.reserve(60 * 100); // 100 hz
chunk.step_vals.reserve(60 * 100 * 2); // 100 hz
double route_start_time = can->routeStartTime(); double route_start_time = can->routeStartTime();
for (auto it = chunk.first; it != chunk.second; ++it) { for (auto it = chunk.first; it != chunk.second; ++it) {
if ((*it)->which == cereal::Event::Which::CAN) { if ((*it)->which == cereal::Event::Which::CAN) {
@ -519,6 +541,10 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig);
double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds
chunk.vals.push_back({ts, value}); chunk.vals.push_back({ts, value});
if (!chunk.step_vals.empty()) {
chunk.step_vals.push_back({ts, chunk.step_vals.back().y()});
}
chunk.step_vals.push_back({ts,value});
} }
} }
} }
@ -526,11 +552,20 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
}); });
for (auto &c : chunks) { for (auto &c : chunks) {
s.vals.append(c.vals); s.vals.append(c.vals);
if (!c.step_vals.empty()) {
if (!s.step_vals.empty()) {
s.step_vals.append({c.step_vals.first().x(), s.step_vals.back().y()});
}
s.step_vals.append(c.step_vals);
}
} }
if (events->size()) { if (events->size()) {
s.last_value_mono_time = events->back()->mono_time; s.last_value_mono_time = events->back()->mono_time;
} }
s.series->replace(s.vals); if (!can->liveStreaming()) {
s.segment_tree.build(s.vals);
}
s.series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals);
} }
} }
updateAxisY(); updateAxisY();
@ -542,19 +577,37 @@ void ChartView::updateAxisY() {
double min = std::numeric_limits<double>::max(); double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::lowest(); double max = std::numeric_limits<double>::lowest();
QString unit = sigs[0].sig->unit;
for (auto &s : sigs) { for (auto &s : sigs) {
if (!s.series->isVisible()) continue; if (!s.series->isVisible()) continue;
// Only show unit when all signals have the same unit
if (unit != s.sig->unit) {
unit.clear();
}
auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; });
auto last = std::lower_bound(first, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); auto last = std::lower_bound(first, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; });
for (auto it = first; it != last; ++it) { if (can->liveStreaming()) {
if (it->y() < min) min = it->y(); for (auto it = first; it != last; ++it) {
if (it->y() > max) max = it->y(); if (it->y() < min) min = it->y();
if (it->y() > max) max = it->y();
}
} else {
auto [min_y, max_y] = s.segment_tree.minmax(std::distance(s.vals.begin(), first), std::distance(s.vals.begin(), last));
min = std::min(min, min_y);
max = std::max(max, max_y);
} }
} }
if (min == std::numeric_limits<double>::max()) min = 0; if (min == std::numeric_limits<double>::max()) min = 0;
if (max == std::numeric_limits<double>::lowest()) max = 0; if (max == std::numeric_limits<double>::lowest()) max = 0;
if (axis_y->titleText() != unit) {
axis_y->setTitleText(unit);
y_label_width = 0;// recalc width
}
double delta = std::abs(max - min) < 1e-3 ? 1 : (max - min) * 0.05; double delta = std::abs(max - min) < 1e-3 ? 1 : (max - min) * 0.05;
auto [min_y, max_y, tick_count] = getNiceAxisNumbers(min - delta, max + delta, axis_y->tickCount()); auto [min_y, max_y, tick_count] = getNiceAxisNumbers(min - delta, max + delta, axis_y->tickCount());
if (min_y != axis_y->min() || max_y != axis_y->max() || y_label_width == 0) { if (min_y != axis_y->min() || max_y != axis_y->max() || y_label_width == 0) {
@ -563,7 +616,8 @@ void ChartView::updateAxisY() {
QFontMetrics fm(axis_y->labelsFont()); QFontMetrics fm(axis_y->labelsFont());
int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1;
y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15 int title_spacing = axis_y->titleText().isEmpty() ? 0 : 20;
y_label_width = title_spacing + qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15
axis_y->setLabelFormat(QString("%.%1f").arg(n)); axis_y->setLabelFormat(QString("%.%1f").arg(n));
emit axisYLabelWidthChanged(y_label_width); emit axisYLabelWidthChanged(y_label_width);
} }
@ -597,7 +651,7 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) {
} }
void ChartView::leaveEvent(QEvent *event) { void ChartView::leaveEvent(QEvent *event) {
track_pts.clear(); clearTrackPoints();
scene()->update(); scene()->update();
QChartView::leaveEvent(event); QChartView::leaveEvent(event);
} }
@ -653,26 +707,33 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
auto rubber = findChild<QRubberBand *>(); auto rubber = findChild<QRubberBand *>();
bool is_zooming = rubber && rubber->isVisible(); bool is_zooming = rubber && rubber->isVisible();
const auto plot_area = chart()->plotArea(); const auto plot_area = chart()->plotArea();
track_pts.clear(); clearTrackPoints();
if (!is_zooming && plot_area.contains(ev->pos())) { if (!is_zooming && plot_area.contains(ev->pos())) {
track_pts.resize(sigs.size());
QStringList text_list; QStringList text_list;
const double sec = chart()->mapToValue(ev->pos()).x(); const double sec = chart()->mapToValue(ev->pos()).x();
for (int i = 0; i < sigs.size(); ++i) { qreal x = -1;
QString value = "--"; for (auto &s : sigs) {
if (!s.series->isVisible()) continue;
// use reverse iterator to find last item <= sec. // use reverse iterator to find last item <= sec.
auto it = std::lower_bound(sigs[i].vals.rbegin(), sigs[i].vals.rend(), sec, [](auto &p, double x) { return p.x() > x; }); double value = 0;
if (it != sigs[i].vals.rend() && it->x() >= axis_x->min()) { auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; });
value = QString::number(it->y()); if (it != s.vals.rend() && it->x() >= axis_x->min()) {
track_pts[i] = chart()->mapToPosition(*it); value = it->y();
s.track_pt = chart()->mapToPosition(*it);
x = std::max(x, s.track_pt.x());
} }
text_list.push_back(QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b>").arg(sigs[i].series->color().name(), sigs[i].sig->name, value)); text_list.push_back(QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b>")
.arg(s.series->color().name(), s.sig->name,
s.track_pt.isNull() ? "--" : QString::number(value)));
}
if (x < 0) {
x = ev->pos().x();
} }
auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); text_list.push_front(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3));
auto pt = (max == track_pts.end()) ? ev->pos() : *max; QPointF tooltip_pt(x + 12, plot_area.top() - 20);
text_list.push_front(QString::number(chart()->mapToValue(pt).x(), 'f', 3)); QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), text_list.join("<br />"), this, plot_area.toRect());
QPointF tooltip_pt(pt.x() + 12, plot_area.top() - 20);
QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), pt.isNull() ? "" : text_list.join("<br />"), this, plot_area.toRect());
scene()->invalidate({}, QGraphicsScene::ForegroundLayer); scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
} else { } else {
QToolTip::hideText(); QToolTip::hideText();
@ -717,6 +778,7 @@ void ChartView::dropEvent(QDropEvent *event) {
} }
void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
// draw time line
qreal x = chart()->mapToPosition(QPointF{cur_sec, 0}).x(); qreal x = chart()->mapToPosition(QPointF{cur_sec, 0}).x();
x = std::clamp(x, chart()->plotArea().left(), chart()->plotArea().right()); x = std::clamp(x, chart()->plotArea().left(), chart()->plotArea().right());
qreal y1 = chart()->plotArea().top() - 2; qreal y1 = chart()->plotArea().top() - 2;
@ -724,18 +786,20 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
painter->setPen(QPen(chart()->titleBrush().color(), 2)); painter->setPen(QPen(chart()->titleBrush().color(), 2));
painter->drawLine(QPointF{x, y1}, QPointF{x, y2}); painter->drawLine(QPointF{x, y1}, QPointF{x, y2});
auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); // draw track points
if (max != track_pts.end() && !max->isNull()) { painter->setPen(Qt::NoPen);
painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine)); qreal track_line_x = -1;
painter->drawLine(QPointF{max->x(), y1}, QPointF{max->x(), y2}); for (auto &s : sigs) {
painter->setPen(Qt::NoPen); if (!s.track_pt.isNull() && s.series->isVisible()) {
for (int i = 0; i < track_pts.size(); ++i) { painter->setBrush(s.series->color().darker(125));
if (!track_pts[i].isNull() && i < sigs.size()) { painter->drawEllipse(s.track_pt, 5.5, 5.5);
painter->setBrush(sigs[i].series->color().darker(125)); track_line_x = std::max(track_line_x, s.track_pt.x());
painter->drawEllipse(track_pts[i], 5.5, 5.5);
}
} }
} }
if (track_line_x > 0) {
painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine));
painter->drawLine(QPointF{track_line_x, y1}, QPointF{track_line_x, y2});
}
// paint points. OpenGL mode lacks certain features (such as showing points) // paint points. OpenGL mode lacks certain features (such as showing points)
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
@ -751,11 +815,14 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
} }
} }
QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor color) { QXYSeries *ChartView::createSeries(SeriesType type, QColor color) {
QXYSeries *series = nullptr; QXYSeries *series = nullptr;
if (type == QAbstractSeries::SeriesTypeLine) { if (type == SeriesType::Line) {
series = new QLineSeries(this); series = new QLineSeries(this);
chart()->legend()->setMarkerShape(QLegend::MarkerShapeRectangle); chart()->legend()->setMarkerShape(QLegend::MarkerShapeRectangle);
} else if (type == SeriesType::StepLine) {
series = new QLineSeries(this);
chart()->legend()->setMarkerShape(QLegend::MarkerShapeFromSeries);
} else { } else {
series = new QScatterSeries(this); series = new QScatterSeries(this);
chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle); chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle);
@ -776,9 +843,7 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo
return series; return series;
} }
void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { void ChartView::setSeriesType(SeriesType type) {
line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine);
scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter);
if (type != series_type) { if (type != series_type) {
series_type = type; series_type = type;
for (auto &s : sigs) { for (auto &s : sigs) {
@ -787,7 +852,7 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) {
} }
for (auto &s : sigs) { for (auto &s : sigs) {
auto series = createSeries(series_type, getColor(s.sig)); auto series = createSeries(series_type, getColor(s.sig));
series->replace(s.vals); series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals);
s.series = series; s.series = series;
} }
updateSeriesPoints(); updateSeriesPoints();

@ -20,6 +20,12 @@ using namespace QtCharts;
const int CHART_MIN_WIDTH = 300; const int CHART_MIN_WIDTH = 300;
enum class SeriesType {
Line = 0,
StepLine,
Scatter
};
class ChartView : public QChartView { class ChartView : public QChartView {
Q_OBJECT Q_OBJECT
@ -29,7 +35,7 @@ public:
bool hasSeries(const MessageId &msg_id, const Signal *sig) const; bool hasSeries(const MessageId &msg_id, const Signal *sig) const;
void updateSeries(const Signal *sig = nullptr, const std::vector<Event*> *events = nullptr, bool clear = true); void updateSeries(const Signal *sig = nullptr, const std::vector<Event*> *events = nullptr, bool clear = true);
void updatePlot(double cur, double min, double max); void updatePlot(double cur, double min, double max);
void setSeriesType(QAbstractSeries::SeriesType type); void setSeriesType(SeriesType type);
void updatePlotArea(int left); void updatePlotArea(int left);
struct SigItem { struct SigItem {
@ -37,7 +43,10 @@ public:
const Signal *sig = nullptr; const Signal *sig = nullptr;
QXYSeries *series = nullptr; QXYSeries *series = nullptr;
QVector<QPointF> vals; QVector<QPointF> vals;
QVector<QPointF> step_vals;
uint64_t last_value_mono_time = 0; uint64_t last_value_mono_time = 0;
QPointF track_pt{};
SegmentTree segment_tree;
}; };
signals: signals:
@ -57,6 +66,7 @@ private slots:
void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); }
private: private:
void createToolButtons();
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *ev) override; void mouseMoveEvent(QMouseEvent *ev) override;
@ -70,15 +80,15 @@ private:
void drawForeground(QPainter *painter, const QRectF &rect) override; void drawForeground(QPainter *painter, const QRectF &rect) override;
std::tuple<double, double, int> getNiceAxisNumbers(qreal min, qreal max, int tick_count); std::tuple<double, double, int> getNiceAxisNumbers(qreal min, qreal max, int tick_count);
qreal niceNumber(qreal x, bool ceiling); qreal niceNumber(qreal x, bool ceiling);
QXYSeries *createSeries(QAbstractSeries::SeriesType type, QColor color); QXYSeries *createSeries(SeriesType type, QColor color);
void updateSeriesPoints(); void updateSeriesPoints();
void removeIf(std::function<bool(const SigItem &)> predicate); void removeIf(std::function<bool(const SigItem &)> predicate);
inline void clearTrackPoints() { for (auto &s : sigs) s.track_pt = {}; }
int y_label_width = 0; int y_label_width = 0;
int align_to = 0; int align_to = 0;
QValueAxis *axis_x; QValueAxis *axis_x;
QValueAxis *axis_y; QValueAxis *axis_y;
QVector<QPointF> track_pts;
QGraphicsPixmapItem *move_icon; QGraphicsPixmapItem *move_icon;
QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *close_btn_proxy;
QGraphicsProxyWidget *manage_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy;
@ -86,9 +96,7 @@ private:
QList<SigItem> sigs; QList<SigItem> sigs;
double cur_sec = 0; double cur_sec = 0;
const QString mime_type = "application/x-cabanachartview"; const QString mime_type = "application/x-cabanachartview";
QAbstractSeries::SeriesType series_type = QAbstractSeries::SeriesTypeLine; SeriesType series_type = SeriesType::Line;
QAction *line_series_action;
QAction *scatter_series_action;
friend class ChartsWidget; friend class ChartsWidget;
}; };

@ -15,8 +15,10 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const {
return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2); return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2);
} }
return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data);
} else if (role == Qt::UserRole && index.column() == 1 && !show_signals) { } else if (role == ColorsRole) {
return QVariant::fromValue(m.colors); return QVariant::fromValue(m.colors);
} else if (role == BytesRole) {
return m.data;
} }
return {}; return {};
} }
@ -48,7 +50,15 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
if (section == 0) { if (section == 0) {
return "Time"; return "Time";
} }
return show_signals ? sigs[section - 1]->name : "Data"; if (show_signals) {
QString name = sigs[section - 1]->name;
if (!sigs[section - 1]->unit.isEmpty()) {
name += QString(" (%1)").arg(sigs[section - 1]->unit);
}
return name;
} else {
return "Data";
}
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) { } else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
return QBrush(getColor(sigs[section - 1])); return QBrush(getColor(sigs[section - 1]));
} }

@ -58,14 +58,21 @@ MainWindow::MainWindow() : QMainWindow() {
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll()); fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
} }
setStyleSheet(QString(R"(QMainWindow::separator {
width: %1px; /* when vertical */
height: %1px; /* when horizontal */
})").arg(style()->pixelMetric(QStyle::PM_SplitterWidth)));
QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage); QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage);
QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress); QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress);
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage); QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts);
QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint);
QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::updateStatus);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged);
QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged); QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged);
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MainWindow::undoStackIndexChanged); QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MainWindow::undoStackIndexChanged);
QObject::connect(&settings, &Settings::changed, this, &MainWindow::updateStatus);
} }
void MainWindow::createActions() { void MainWindow::createActions() {
@ -179,11 +186,16 @@ void MainWindow::createStatusBar() {
progress_bar->setVisible(false); progress_bar->setVisible(false);
statusBar()->addWidget(new QLabel(tr("For Help, Press F1"))); statusBar()->addWidget(new QLabel(tr("For Help, Press F1")));
statusBar()->addPermanentWidget(progress_bar); statusBar()->addPermanentWidget(progress_bar);
statusBar()->addPermanentWidget(status_label = new QLabel(this));
updateStatus();
} }
void MainWindow::createShortcuts() { void MainWindow::createShortcuts() {
auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut); auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut);
QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); }); QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); });
shortcut = new QShortcut(QKeySequence(QKeySequence::FullScreen), this, nullptr, nullptr, Qt::ApplicationShortcut);
QObject::connect(shortcut, &QShortcut::activated, this, &MainWindow::toggleFullScreen);
// TODO: add more shortcuts here. // TODO: add more shortcuts here.
} }
@ -403,6 +415,18 @@ void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool succe
} }
} }
void MainWindow::updateStatus() {
float cached_minutes = 0;
if (!can->liveStreaming()) {
if (auto events = can->events(); !events->empty()) {
cached_minutes = (events->back()->mono_time - events->front()->mono_time) / (1e9 * 60);
}
} else {
settings.max_cached_minutes = settings.max_cached_minutes;
}
status_label->setText(tr("Cached Minutes:%1 FPS:%2").arg(cached_minutes, 0, 'f', 1).arg(settings.fps));
}
void MainWindow::dockCharts(bool dock) { void MainWindow::dockCharts(bool dock) {
if (dock && floating_window) { if (dock && floating_window) {
floating_window->removeEventFilter(charts_widget); floating_window->removeEventFilter(charts_widget);
@ -460,6 +484,19 @@ void MainWindow::onlineHelp() {
} }
} }
void MainWindow::toggleFullScreen() {
if (isFullScreen()) {
menuBar()->show();
statusBar()->show();
showNormal();
showMaximized();
} else {
menuBar()->hide();
statusBar()->hide();
showFullScreen();
}
}
// HelpOverlay // HelpOverlay
HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) { HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) {
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);

@ -56,6 +56,8 @@ protected:
void undoStackCleanChanged(bool clean); void undoStackCleanChanged(bool clean);
void undoStackIndexChanged(int index); void undoStackIndexChanged(int index);
void onlineHelp(); void onlineHelp();
void toggleFullScreen();
void updateStatus();
VideoWidget *video_widget = nullptr; VideoWidget *video_widget = nullptr;
QDockWidget *video_dock; QDockWidget *video_dock;
@ -65,6 +67,7 @@ protected:
QWidget *floating_window = nullptr; QWidget *floating_window = nullptr;
QVBoxLayout *charts_layout; QVBoxLayout *charts_layout;
QProgressBar *progress_bar; QProgressBar *progress_bar;
QLabel *status_label;
QJsonDocument fingerprint_to_dbc; QJsonDocument fingerprint_to_dbc;
QSplitter *video_splitter;; QSplitter *video_splitter;;
QString current_file = ""; QString current_file = "";

@ -20,11 +20,15 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
table_widget = new QTableView(this); table_widget = new QTableView(this);
model = new MessageListModel(this); model = new MessageListModel(this);
table_widget->setModel(model); table_widget->setModel(model);
table_widget->setItemDelegateForColumn(4, new MessageBytesDelegate(table_widget)); table_widget->setItemDelegateForColumn(5, new MessageBytesDelegate(table_widget));
table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); table_widget->setSelectionBehavior(QAbstractItemView::SelectRows);
table_widget->setSelectionMode(QAbstractItemView::SingleSelection); table_widget->setSelectionMode(QAbstractItemView::SingleSelection);
table_widget->setSortingEnabled(true); table_widget->setSortingEnabled(true);
table_widget->sortByColumn(0, Qt::AscendingOrder); table_widget->sortByColumn(0, Qt::AscendingOrder);
table_widget->setColumnWidth(0, 150);
table_widget->setColumnWidth(1, 50);
table_widget->setColumnWidth(2, 50);
table_widget->setColumnWidth(3, 50);
table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->horizontalHeader()->setStretchLastSection(true);
table_widget->verticalHeader()->hide(); table_widget->verticalHeader()->hide();
main_layout->addWidget(table_widget); main_layout->addWidget(table_widget);
@ -108,7 +112,7 @@ void MessagesWidget::reset() {
QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return (QString[]){"Name", "ID", "Freq", "Count", "Bytes"}[section]; return (QString[]){"Name", "Bus", "ID", "Freq", "Count", "Bytes"}[section];
return {}; return {};
} }
@ -119,12 +123,13 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
switch (index.column()) { switch (index.column()) {
case 0: return msgName(id); case 0: return msgName(id);
case 1: return id.toString(); // TODO: put source and address in separate columns case 1: return id.source;
case 2: return can_data.freq; case 2: return QString::number(id.address, 16);;
case 3: return can_data.count; case 3: return can_data.freq;
case 4: return toHex(can_data.dat); case 4: return can_data.count;
case 5: return toHex(can_data.dat);
} }
} else if (role == Qt::UserRole && index.column() == 4) { } else if (role == ColorsRole) {
QVector<QColor> colors = can_data.colors; QVector<QColor> colors = can_data.colors;
if (!suppressed_bytes.empty()) { if (!suppressed_bytes.empty()) {
for (int i = 0; i < colors.size(); i++) { for (int i = 0; i < colors.size(); i++) {
@ -134,6 +139,8 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
} }
} }
return QVariant::fromValue(colors); return QVariant::fromValue(colors);
} else if (role == BytesRole) {
return can_data.dat;
} }
return {}; return {};
} }
@ -171,15 +178,23 @@ void MessageListModel::sortMessages() {
}); });
} else if (sort_column == 1) { } else if (sort_column == 1) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
return sort_order == Qt::AscendingOrder ? l < r : l > r; auto ll = std::pair{l.source, l};
auto rr = std::pair{r.source, r};
return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr;
}); });
} else if (sort_column == 2) { } else if (sort_column == 2) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
auto ll = std::pair{l.address, l};
auto rr = std::pair{r.address, r};
return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr;
});
} else if (sort_column == 3) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
auto ll = std::pair{can->lastMessage(l).freq, l}; auto ll = std::pair{can->lastMessage(l).freq, l};
auto rr = std::pair{can->lastMessage(r).freq, r}; auto rr = std::pair{can->lastMessage(r).freq, r};
return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr;
}); });
} else if (sort_column == 3) { } else if (sort_column == 4) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
auto ll = std::pair{can->lastMessage(l).count, l}; auto ll = std::pair{can->lastMessage(l).count, l};
auto rr = std::pair{can->lastMessage(r).count, r}; auto rr = std::pair{can->lastMessage(r).count, r};
@ -200,7 +215,7 @@ void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs) {
} }
for (int i = 0; i < msgs.size(); ++i) { for (int i = 0; i < msgs.size(); ++i) {
if (new_msgs->contains(msgs[i])) { if (new_msgs->contains(msgs[i])) {
for (int col = 2; col < columnCount(); ++col) for (int col = 3; col < columnCount(); ++col)
emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole});
} }
} }

@ -16,7 +16,7 @@ Q_OBJECT
public: public:
MessageListModel(QObject *parent) : QAbstractTableModel(parent) {} MessageListModel(QObject *parent) : QAbstractTableModel(parent) {}
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 5; } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 6; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); }
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;

@ -65,7 +65,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
form_layout->addRow(tr("Max Cached Minutes"), cached_minutes); form_layout->addRow(tr("Max Cached Minutes"), cached_minutes);
chart_series_type = new QComboBox(this); chart_series_type = new QComboBox(this);
chart_series_type->addItems({tr("Line"), tr("Scatter")}); chart_series_type->addItems({tr("Line"), tr("Step Line"), tr("Scatter")});
chart_series_type->setCurrentIndex(settings.chart_series_type); chart_series_type->setCurrentIndex(settings.chart_series_type);
form_layout->addRow(tr("Chart Default Series Type"), chart_series_type); form_layout->addRow(tr("Chart Default Series Type"), chart_series_type);

@ -62,6 +62,9 @@ void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
int row = 0; int row = 0;
for (auto item : root->children) { for (auto item : root->children) {
QString value = QString::number(get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig)); QString value = QString::number(get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig));
if (!item->sig->unit.isEmpty()){
value += " " + item->sig->unit;
}
if (value != item->sig_val) { if (value != item->sig_val) {
item->sig_val = value; item->sig_val = value;
emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole});
@ -319,22 +322,34 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
// color label // color label
auto bg_color = getColor(item->sig); auto bg_color = getColor(item->sig);
QRect rc{option.rect.left(), option.rect.top(), color_label_width, option.rect.height()}; int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
QRect rc{option.rect.left() + h_margin, option.rect.top(), color_label_width, option.rect.height()};
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color);
painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3); painter->drawRoundedRect(rc.adjusted(0, v_margin, 0, -v_margin), 3, 3);
painter->setPen(item->highlight ? Qt::white : Qt::black); painter->setPen(item->highlight ? Qt::white : Qt::black);
painter->setFont(small_font); painter->setFont(small_font);
painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1)); painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1));
// signal name // signal name
painter->setFont(option.font); painter->setFont(option.font);
painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
QString text = index.data(Qt::DisplayRole).toString(); QString text = index.data(Qt::DisplayRole).toString();
QRect text_rect = option.rect.adjusted(rc.width() + 6, 0, 0, 0); QRect text_rect = option.rect;
text_rect.setLeft(rc.right() + h_margin * 2);
text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width()); text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width());
painter->drawText(text_rect, option.displayAlignment, text); painter->drawText(text_rect, option.displayAlignment, text);
painter->restore(); painter->restore();
} else if (index.column() == 1 && item && item->type == SignalModel::Item::Sig) {
// draw signal value
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
QRect rc = option.rect.adjusted(0, 0, -70, 0);
auto text = painter->fontMetrics().elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rc.width());
painter->drawText(rc, Qt::AlignRight | Qt::AlignVCenter, text);
} else { } else {
QStyledItemDelegate::paint(painter, option, index); QStyledItemDelegate::paint(painter, option, index);
} }
@ -430,8 +445,11 @@ void SignalView::rowsChanged() {
if (!tree->indexWidget(index)) { if (!tree->indexWidget(index)) {
QWidget *w = new QWidget(this); QWidget *w = new QWidget(this);
QHBoxLayout *h = new QHBoxLayout(w); QHBoxLayout *h = new QHBoxLayout(w);
h->setContentsMargins(0, 2, 0, 2); int v_margin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
h->addStretch(1); int h_margin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
h->setContentsMargins(0, v_margin, -h_margin, v_margin);
h->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing));
h->addStretch(0);
auto remove_btn = toolButton("x", tr("Remove signal")); auto remove_btn = toolButton("x", tr("Remove signal"));
auto plot_btn = toolButton("graph-up", ""); auto plot_btn = toolButton("graph-up", "");

@ -89,10 +89,11 @@ void LiveStream::removeExpiredEvents() {
} }
const std::vector<Event *> *LiveStream::events() const { const std::vector<Event *> *LiveStream::events() const {
events_vector.clear();
std::lock_guard lk(lock); std::lock_guard lk(lock);
events_vector.reserve(can_events.size()); if (events_vector.capacity() <= can_events.size()) {
std::copy(can_events.begin(), can_events.end(), std::back_inserter(events_vector)); events_vector.reserve(can_events.size() * 2);
}
events_vector.assign(can_events.begin(), can_events.end());
return &events_vector; return &events_vector;
} }

@ -67,30 +67,64 @@ void ChangeTracker::clear() {
colors.clear(); colors.clear();
} }
// SegmentTree
void SegmentTree::build(const QVector<QPointF> &arr) {
size = arr.size();
tree.resize(4 * size); // size of the tree is 4 times the size of the array
if (size > 0) {
build_tree(arr, 1, 0, size - 1);
}
}
void SegmentTree::build_tree(const QVector<QPointF> &arr, int n, int left, int right) {
if (left == right) {
const double y = arr[left].y();
tree[n] = {y, y};
} else {
const int mid = (left + right) >> 1;
build_tree(arr, 2 * n, left, mid);
build_tree(arr, 2 * n + 1, mid + 1, right);
tree[n] = {std::min(tree[2 * n].first, tree[2 * n + 1].first), std::max(tree[2 * n].second, tree[2 * n + 1].second)};
}
}
std::pair<double, double> SegmentTree::get_minmax(int n, int left, int right, int range_left, int range_right) const {
if (range_left > right || range_right < left)
return {std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest()};
if (range_left <= left && range_right >= right)
return tree[n];
int mid = (left + right) >> 1;
auto l = get_minmax(2 * n, left, mid, range_left, range_right);
auto r = get_minmax(2 * n + 1, mid + 1, right, range_left, range_right);
return {std::min(l.first, r.first), std::max(l.second, r.second)};
}
// MessageBytesDelegate // MessageBytesDelegate
MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) {
fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
byte_width = QFontMetrics(fixed_font).width("00 ");
} }
void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto colors = index.data(ColorsRole).value<QVector<QColor>>();
auto byte_list = index.data(BytesRole).toByteArray();
int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
QRect rc{option.rect.left() + h_margin, option.rect.top() + v_margin, byte_width, option.rect.height() - 2 * v_margin};
auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text; auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text;
painter->setPen(option.palette.color(color_role)); painter->setPen(option.palette.color(color_role));
painter->setFont(fixed_font); painter->setFont(fixed_font);
int space = painter->boundingRect(option.rect, option.displayAlignment, " ").width();
QRect pos = painter->boundingRect(option.rect, option.displayAlignment, "00").adjusted(0, 0, 2, 0);
pos.moveLeft(pos.x() + space);
int m = space / 2;
const QMargins margins(m, m, m, m);
auto colors = index.data(Qt::UserRole).value<QVector<QColor>>();
auto byte_list = index.data(Qt::DisplayRole).toString().split(" ");
for (int i = 0; i < byte_list.size(); ++i) { for (int i = 0; i < byte_list.size(); ++i) {
if (i < colors.size() && colors[i].alpha() > 0) { if (i < colors.size() && colors[i].alpha() > 0) {
painter->fillRect(pos.marginsAdded(margins), colors[i]); painter->fillRect(rc, colors[i]);
} }
painter->drawText(pos, Qt::AlignCenter, byte_list[i]); painter->drawText(rc, Qt::AlignCenter, toHex(byte_list[i]));
pos.moveLeft(pos.right() + space); rc.moveLeft(rc.right() + 1);
} }
} }
@ -114,8 +148,8 @@ QValidator::State NameValidator::validate(QString &input, int &pos) const {
namespace utils { namespace utils {
QPixmap icon(const QString &id) { QPixmap icon(const QString &id) {
static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > static bool dark_theme = QApplication::palette().color(QPalette::WindowText).value() >
QApplication::style()->standardPalette().color(QPalette::Background).value(); QApplication::palette().color(QPalette::Background).value();
QPixmap pm; QPixmap pm;
QString key = "bootstrap_" % id % (dark_theme ? "1" : "0"); QString key = "bootstrap_" % id % (dark_theme ? "1" : "0");
if (!QPixmapCache::find(key, &pm)) { if (!QPixmapCache::find(key, &pm)) {
@ -138,3 +172,13 @@ QToolButton *toolButton(const QString &icon, const QString &tooltip) {
btn->setAutoRaise(true); btn->setAutoRaise(true);
return btn; return btn;
}; };
QString toHex(uint8_t byte) {
static std::array<QString, 256> hex = []() {
std::array<QString, 256> ret;
for (int i = 0; i < 256; ++i) ret[i] = QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper();
return ret;
}();
return hex[byte];
}

@ -14,7 +14,6 @@
#include "tools/cabana/dbcmanager.h" #include "tools/cabana/dbcmanager.h"
using namespace dbcmanager; using namespace dbcmanager;
class ChangeTracker { class ChangeTracker {
public: public:
void compute(const QByteArray &dat, double ts, uint32_t freq); void compute(const QByteArray &dat, double ts, uint32_t freq);
@ -31,16 +30,35 @@ private:
QByteArray prev_dat; QByteArray prev_dat;
}; };
enum {
ColorsRole = Qt::UserRole + 1,
BytesRole = Qt::UserRole + 2
};
class SegmentTree {
public:
SegmentTree() = default;
void build(const QVector<QPointF> &arr);
inline std::pair<double, double> minmax(int left, int right) const { return get_minmax(1, 0, size - 1, left, right); }
private:
std::pair<double, double> get_minmax(int n, int left, int right, int range_left, int range_right) const;
void build_tree(const QVector<QPointF> &arr, int n, int left, int right);
std::vector<std::pair<double ,double>> tree;
int size = 0;
};
class MessageBytesDelegate : public QStyledItemDelegate { class MessageBytesDelegate : public QStyledItemDelegate {
Q_OBJECT Q_OBJECT
public: public:
MessageBytesDelegate(QObject *parent); MessageBytesDelegate(QObject *parent);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QFont fixed_font; QFont fixed_font;
int byte_width;
}; };
inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); }
inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } QString toHex(uint8_t byte);
QColor getColor(const dbcmanager::Signal *sig); QColor getColor(const dbcmanager::Signal *sig);
class NameValidator : public QRegExpValidator { class NameValidator : public QRegExpValidator {

@ -165,7 +165,10 @@ void ConsoleUI::updateStatus() {
sm.update(0); sm.update(0);
if (status != Status::Paused) { if (status != Status::Paused) {
status = (sm.updated("carState") || sm.updated("liveParameters")) ? Status::Playing : Status::Waiting; auto events = replay->events();
uint64_t current_mono_time = replay->routeStartTime() + replay->currentSeconds() * 1e9;
bool playing = !events->empty() && events->back()->mono_time > current_mono_time;
status = playing ? Status::Playing : Status::Waiting;
} }
auto [status_str, status_color] = status_text[status]; auto [status_str, status_color] = status_text[status];
write_item(0, 0, "STATUS: ", status_str, " ", false, status_color); write_item(0, 0, "STATUS: ", status_str, " ", false, status_color);

Loading…
Cancel
Save