Merge remote-tracking branch 'upstream/master' into ui_accel_gradient

pull/27391/head
Shane Smiskol 3 years ago
commit 011a4b948a
  1. 2
      .github/workflows/selfdrive_tests.yaml
  2. 2
      RELEASES.md
  3. 2
      cereal
  4. 8
      common/params.cc
  5. 15
      docs/CARS.md
  6. 2
      laika_repo
  7. 2
      panda
  8. 433
      poetry.lock
  9. 15
      pyproject.toml
  10. 31
      selfdrive/boardd/boardd.cc
  11. 2
      selfdrive/boardd/panda.cc
  12. 2
      selfdrive/boardd/tests/test_boardd_loopback.py
  13. 13
      selfdrive/car/car_helpers.py
  14. 116
      selfdrive/car/chrysler/values.py
  15. 12
      selfdrive/car/ecu_addrs.py
  16. 14
      selfdrive/car/fw_query_definitions.py
  17. 90
      selfdrive/car/fw_versions.py
  18. 39
      selfdrive/car/gm/interface.py
  19. 11
      selfdrive/car/gm/values.py
  20. 10
      selfdrive/car/honda/carcontroller.py
  21. 5
      selfdrive/car/honda/carstate.py
  22. 54
      selfdrive/car/honda/hondacan.py
  23. 14
      selfdrive/car/honda/interface.py
  24. 0
      selfdrive/car/honda/tests/__init__.py
  25. 2
      selfdrive/car/honda/values.py
  26. 0
      selfdrive/car/hyundai/tests/__init__.py
  27. 78
      selfdrive/car/hyundai/values.py
  28. 16
      selfdrive/car/interfaces.py
  29. 2
      selfdrive/car/isotp_parallel_query.py
  30. 4
      selfdrive/car/nissan/values.py
  31. 4
      selfdrive/car/subaru/values.py
  32. 3
      selfdrive/car/tests/routes.py
  33. 12
      selfdrive/car/tests/test_lateral_limits.py
  34. 4
      selfdrive/car/torque_data/override.yaml
  35. 2
      selfdrive/car/torque_data/params.yaml
  36. 1
      selfdrive/car/torque_data/substitute.yaml
  37. 5
      selfdrive/car/toyota/carcontroller.py
  38. 21
      selfdrive/car/toyota/interface.py
  39. 0
      selfdrive/car/toyota/tests/__init__.py
  40. 13
      selfdrive/car/toyota/tests/test_toyota.py
  41. 2
      selfdrive/car/toyota/toyotacan.py
  42. 103
      selfdrive/car/toyota/values.py
  43. 22
      selfdrive/car/volkswagen/values.py
  44. 10
      selfdrive/controls/lib/drive_helpers.py
  45. 9
      selfdrive/controls/lib/events.py
  46. 6
      selfdrive/controls/lib/latcontrol_torque.py
  47. 5
      selfdrive/controls/tests/test_startup.py
  48. 3
      selfdrive/debug/hyundai_enable_radar_points.py
  49. 43
      selfdrive/debug/internal/check_frame_frequencies.py
  50. 327
      selfdrive/locationd/laikad.py
  51. 17
      selfdrive/locationd/locationd.cc
  52. 1
      selfdrive/locationd/locationd.h
  53. 262
      selfdrive/locationd/test/test_laikad.py
  54. 2
      selfdrive/manager/manager.py
  55. 4
      selfdrive/modeld/models/supercombo.onnx
  56. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  57. 131
      selfdrive/test/process_replay/process_replay.py
  58. 2
      selfdrive/test/process_replay/ref_commit
  59. 11
      selfdrive/test/process_replay/test_processes.py
  60. 9
      selfdrive/ui/qt/offroad/settings.cc
  61. 1
      selfdrive/ui/qt/widgets/cameraview.cc
  62. 24
      selfdrive/ui/translations/main_de.ts
  63. 24
      selfdrive/ui/translations/main_ja.ts
  64. 24
      selfdrive/ui/translations/main_ko.ts
  65. 24
      selfdrive/ui/translations/main_pt-BR.ts
  66. 24
      selfdrive/ui/translations/main_zh-CHS.ts
  67. 24
      selfdrive/ui/translations/main_zh-CHT.ts
  68. 2
      selfdrive/ui/update_translations.py
  69. 3
      system/camerad/cameras/camera_common.cc
  70. 5
      system/camerad/cameras/camera_common.h
  71. 2
      system/camerad/cameras/camera_qcom2.cc
  72. 9
      system/camerad/cameras/sensor2_i2c.h
  73. 9
      system/hardware/tici/amplifier.py
  74. 6
      system/hardware/tici/hardware.py
  75. 2
      system/hardware/tici/pins.py
  76. 7
      system/loggerd/bootlog.cc
  77. 8
      system/loggerd/tests/test_loggerd.py
  78. 26
      system/sensord/tests/test_sensord.py
  79. 136
      system/ubloxd/generated/ubx.cpp
  80. 94
      system/ubloxd/generated/ubx.h
  81. 1
      system/ubloxd/tests/ublox.py
  82. 84
      system/ubloxd/ublox_msg.cc
  83. 7
      system/ubloxd/ublox_msg.h
  84. 40
      system/ubloxd/ubx.ksy
  85. 2
      tools/cabana/.gitignore
  86. 5
      tools/cabana/SConscript
  87. 14
      tools/cabana/binaryview.cc
  88. 5
      tools/cabana/binaryview.h
  89. 415
      tools/cabana/chartswidget.cc
  90. 34
      tools/cabana/chartswidget.h
  91. 2
      tools/cabana/commands.h
  92. 4
      tools/cabana/dbc/dbc.cc
  93. 6
      tools/cabana/dbc/dbc.h
  94. 296
      tools/cabana/dbc/dbcfile.cc
  95. 55
      tools/cabana/dbc/dbcfile.h
  96. 218
      tools/cabana/dbc/dbcmanager.cc
  97. 69
      tools/cabana/dbc/dbcmanager.h
  98. 0
      tools/cabana/dbc/generate_dbc_json.py
  99. 78
      tools/cabana/dbcmanager.h
  100. 1
      tools/cabana/detailwidget.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -261,7 +261,7 @@ jobs:
name: process_replay_diff.txt name: process_replay_diff.txt
path: selfdrive/test/process_replay/diff.txt path: selfdrive/test/process_replay/diff.txt
- name: Upload reference logs - name: Upload reference logs
if: ${{ failure() && steps.print-diff.outcome == 'success' && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }} if: ${{ failure() && steps.print-diff.outcome == 'success' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }}
run: | run: |
${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only" ${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
- name: "Upload coverage to Codecov" - name: "Upload coverage to Codecov"

@ -1,6 +1,8 @@
Version 0.9.2 (2023-03-XX) Version 0.9.2 (2023-03-XX)
======================== ========================
* New driving model, trained on a new dataset
* 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.
* Buick LaCrosse 2017-19 support thanks to koch-cf!
* Škoda Fabia 2022-23 support thanks to jyoung8607! * Škoda Fabia 2022-23 support thanks to jyoung8607!
Version 0.9.1 (2023-02-28) Version 0.9.1 (2023-02-28)

@ -1 +1 @@
Subproject commit 9888e0476c05069d46f273569467e4371b2d8690 Subproject commit d70d215de6c584f671272d2de2f46a4f778e9f14

@ -98,6 +98,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CarVin", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CarVin", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CompletedTrainingVersion", PERSISTENT}, {"CompletedTrainingVersion", PERSISTENT},
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CurrentBootlog", PERSISTENT},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT}, {"DisablePowerDown", PERSISTENT},
@ -110,7 +111,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DoReboot", CLEAR_ON_MANAGER_START}, {"DoReboot", CLEAR_ON_MANAGER_START},
{"DoShutdown", CLEAR_ON_MANAGER_START}, {"DoShutdown", CLEAR_ON_MANAGER_START},
{"DoUninstall", CLEAR_ON_MANAGER_START}, {"DoUninstall", CLEAR_ON_MANAGER_START},
{"FirmwareObdQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"ForcePowerDown", CLEAR_ON_MANAGER_START}, {"ForcePowerDown", CLEAR_ON_MANAGER_START},
{"GitBranch", PERSISTENT}, {"GitBranch", PERSISTENT},
{"GitCommit", PERSISTENT}, {"GitCommit", PERSISTENT},
@ -137,7 +138,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"IsReleaseBranch", CLEAR_ON_MANAGER_START}, {"IsReleaseBranch", CLEAR_ON_MANAGER_START},
{"IsUpdateAvailable", CLEAR_ON_MANAGER_START}, {"IsUpdateAvailable", CLEAR_ON_MANAGER_START},
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"LaikadEphemerisV2", PERSISTENT | DONT_LOG}, {"LaikadEphemerisV3", PERSISTENT | DONT_LOG},
{"LanguageSetting", PERSISTENT}, {"LanguageSetting", PERSISTENT},
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START}, {"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
{"LastGPSPosition", PERSISTENT}, {"LastGPSPosition", PERSISTENT},
@ -154,7 +155,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"NavSettingTime24h", PERSISTENT}, {"NavSettingTime24h", PERSISTENT},
{"NavSettingLeftSide", PERSISTENT}, {"NavSettingLeftSide", PERSISTENT},
{"NavdRender", PERSISTENT}, {"NavdRender", PERSISTENT},
{"ObdMultiplexingDisabled", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"OpenpilotEnabledToggle", PERSISTENT}, {"OpenpilotEnabledToggle", PERSISTENT},
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"PandaSignatures", CLEAR_ON_MANAGER_START}, {"PandaSignatures", CLEAR_ON_MANAGER_START},

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system.
# 238 Supported Cars # 239 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -17,6 +17,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=Audi&model=Q3 2019-23">J533</a>|| |Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=Audi&model=Q3 2019-23">J533</a>||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=RS3 2018">J533</a>|| |Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=Audi&model=RS3 2018">J533</a>||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=S3 2015-17">J533</a>|| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=Audi&model=S3 2015-17">J533</a>||
|Buick|LaCrosse 2017-19[<sup>3</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Buick&model=LaCrosse 2017-19">OBD-II</a>||
|Cadillac|Escalade 2017[<sup>3</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">OBD-II</a>|| |Cadillac|Escalade 2017[<sup>3</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">OBD-II</a>||
|Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">OBD-II</a>|| |Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">OBD-II</a>||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EUV 2022-23">GM</a>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EUV 2022-23">GM</a>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
@ -43,9 +44,9 @@ A supported vehicle is one that just works when you install a comma three. All s
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord Hybrid 2018-22">Honda Bosch A</a>|| |Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord Hybrid 2018-22">Honda Bosch A</a>||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2016-18">Honda Nidec</a>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2016-18">Honda Nidec</a>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>4</sup>](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2019-21">Honda Bosch A</a>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>4</sup>](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2019-21">Honda Bosch A</a>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2022">Honda Bosch B</a>|| |Honda|Civic 2022|All|openpilot available[<sup>1</sup>](#footnotes)|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=Honda&model=Civic 2022">Honda Bosch B</a>||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Honda Bosch A</a>|| |Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Honda Bosch A</a>||
|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2022">Honda Bosch B</a>|| |Honda|Civic Hatchback 2022|All|openpilot available[<sup>1</sup>](#footnotes)|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=Honda&model=Civic Hatchback 2022">Honda Bosch B</a>||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Honda Nidec</a>|| |Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Honda Nidec</a>||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Honda Bosch A</a>|| |Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Honda Bosch A</a>||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Honda Bosch A</a>|| |Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Honda Bosch A</a>||
@ -116,7 +117,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|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=Kia&model=Seltos 2021">Hyundai A</a>|| |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|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=Kia&model=Seltos 2021">Hyundai A</a>||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|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=Kia&model=Sorento 2018">Hyundai C</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|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=Kia&model=Sorento 2018">Hyundai C</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2019">Hyundai E</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|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=Kia&model=Sorento 2019">Hyundai E</a>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Kia|Sorento 2022-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|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=Kia&model=Sorento 2022-23">Hyundai K</a>|| |Kia|Sorento 2021-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|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=Kia&model=Sorento 2021-23">Hyundai K</a>||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Hyundai A</a>|| |Kia|Sorento Plug-in Hybrid 2022-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|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=Kia&model=Sorento Plug-in Hybrid 2022-23">Hyundai A</a>||
|Kia|Sportage 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage 2023">Hyundai N</a>|| |Kia|Sportage 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|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=Kia&model=Sportage 2023">Hyundai N</a>||
|Kia|Sportage Hybrid 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage Hybrid 2023">Hyundai N</a>|| |Kia|Sportage Hybrid 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage Hybrid 2023">Hyundai N</a>||
@ -125,14 +126,14 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|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=Kia&model=Telluride 2020-22">Hyundai H</a>|| |Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|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=Kia&model=Telluride 2020-22">Hyundai H</a>||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=CT Hybrid 2017-18">Toyota</a>|| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=CT Hybrid 2017-18">Toyota</a>||
|Lexus|ES 2019-22|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=Lexus&model=ES 2019-22">Toyota</a>|| |Lexus|ES 2019-22|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=Lexus&model=ES 2019-22">Toyota</a>||
|Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=ES Hybrid 2017-18">Toyota</a>|| |Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=ES Hybrid 2017-18">Toyota</a>||
|Lexus|ES Hybrid 2019-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=Lexus&model=ES Hybrid 2019-23">Toyota</a>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Lexus|ES Hybrid 2019-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=Lexus&model=ES Hybrid 2019-23">Toyota</a>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=IS 2017-19">Toyota</a>|| |Lexus|IS 2017-19|All|Stock|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=Lexus&model=IS 2017-19">Toyota</a>||
|Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=NX 2018-19">Toyota</a>|| |Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=NX 2018-19">Toyota</a>||
|Lexus|NX 2020-21|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=Lexus&model=NX 2020-21">Toyota</a>|| |Lexus|NX 2020-21|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=Lexus&model=NX 2020-21">Toyota</a>||
|Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=NX Hybrid 2018-19">Toyota</a>|| |Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=NX Hybrid 2018-19">Toyota</a>||
|Lexus|NX Hybrid 2020-21|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=Lexus&model=NX Hybrid 2020-21">Toyota</a>|| |Lexus|NX Hybrid 2020-21|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=Lexus&model=NX Hybrid 2020-21">Toyota</a>||
|Lexus|RC 2017-20|All|Stock|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=Lexus&model=RC 2017-20">Toyota</a>|| |Lexus|RC 2018-20|All|Stock|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=Lexus&model=RC 2018-20">Toyota</a>||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=RX 2016">Toyota</a>|| |Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=RX 2016">Toyota</a>||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=RX 2017-19">Toyota</a>|| |Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|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=Lexus&model=RX 2017-19">Toyota</a>||
|Lexus|RX 2020-22|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=Lexus&model=RX 2020-22">Toyota</a>|| |Lexus|RX 2020-22|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=Lexus&model=RX 2020-22">Toyota</a>||
@ -148,7 +149,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Nissan|Leaf 2018-22|ProPILOT Assist|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=Nissan&model=Leaf 2018-22">Nissan A</a>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Nissan|Leaf 2018-22|ProPILOT Assist|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=Nissan&model=Leaf 2018-22">Nissan A</a>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=Rogue 2018-20">Nissan A</a>|| |Nissan|Rogue 2018-20|ProPILOT Assist|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=Nissan&model=Rogue 2018-20">Nissan A</a>||
|Nissan|X-Trail 2017|ProPILOT Assist|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=Nissan&model=X-Trail 2017">Nissan A</a>|| |Nissan|X-Trail 2017|ProPILOT Assist|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=Nissan&model=X-Trail 2017">Nissan A</a>||
|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Ram&model=1500 2019-22">Ram</a>|| |Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Ram&model=1500 2019-23">Ram</a>||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Ateca 2018">J533</a>|| |SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=SEAT&model=Ateca 2018">J533</a>||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Leon 2014-20">J533</a>|| |SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|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=SEAT&model=Leon 2014-20">J533</a>||
|Subaru|Ascent 2019-21|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=Subaru&model=Ascent 2019-21">Subaru A</a>|| |Subaru|Ascent 2019-21|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=Subaru&model=Ascent 2019-21">Subaru A</a>||

@ -1 +1 @@
Subproject commit b740b71c82a748e3520b1599487d9a7aaf728670 Subproject commit 6fadabd86043ee19e06c6ed59aa4e688c14fa8e4

@ -1 +1 @@
Subproject commit b231281c8b2c79c46892576dee4c8a56d5e0b2d2 Subproject commit a12c0a795678373d946b644b43d9402b72e502a9

433
poetry.lock generated

@ -113,9 +113,12 @@ category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
[package.dependencies]
packaging = ">20.6"
[package.source] [package.source]
type = "url" type = "url"
url = "https://github.com/commaai/apex/releases/download/pytorch1.10.0%2Bcu11.1/apex-0.1-cp38-cp38-linux_x86_64.whl" url = "https://github.com/commaai/apex/releases/download/pytorch2.0.0%2Bcu11.8/apex-0.1-cp38-cp38-linux_x86_64.whl"
[[package]] [[package]]
name = "appdirs" name = "appdirs"
@ -457,6 +460,14 @@ python-versions = "*"
docutils = ">=0.12" docutils = ">=0.12"
Sphinx = ">=4.0,<5.0.0 || >5.0.0,<6" Sphinx = ">=4.0,<5.0.0 || >5.0.0,<6"
[[package]]
name = "brotli"
version = "1.0.9"
description = "Python bindings for the Brotli compression library"
category = "dev"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "cachecontrol" name = "cachecontrol"
version = "0.12.11" version = "0.12.11"
@ -600,6 +611,17 @@ category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[package]]
name = "cmake"
version = "3.26.0"
description = "CMake is an open-source, cross-platform family of tools designed to build, test and package software"
category = "dev"
optional = false
python-versions = "*"
[package.extras]
test = ["codecov (>=2.0.5)", "coverage (>=4.2)", "flake8 (>=3.0.4)", "path.py (>=11.5.0)", "pytest (>=3.0.3)", "pytest-cov (>=2.4.0)", "pytest-runner (>=2.9)", "pytest-virtualenv (>=1.7.0)", "scikit-build (>=0.10.0)", "setuptools (>=28.0.0)", "virtualenv (>=15.0.3)", "wheel"]
[[package]] [[package]]
name = "colorama" name = "colorama"
version = "0.4.5" version = "0.4.5"
@ -716,8 +738,8 @@ ssh = ["bcrypt (>=3.1.5)"]
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
[[package]] [[package]]
name = "cupy-cuda113" name = "cupy-cuda11x"
version = "10.6.0" version = "11.6.0"
description = "CuPy: NumPy & SciPy for GPU" description = "CuPy: NumPy & SciPy for GPU"
category = "dev" category = "dev"
optional = false optional = false
@ -725,12 +747,12 @@ python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
fastrlock = ">=0.5" fastrlock = ">=0.5"
numpy = ">=1.18,<1.25" numpy = ">=1.20,<1.27"
[package.extras] [package.extras]
all = ["Cython (>=0.29.22,<3)", "optuna (>=2.0)", "scipy (>=1.4,<1.11)"] all = ["Cython (>=0.29.22,<3)", "optuna (>=2.0)", "scipy (>=1.6,<1.12)"]
stylecheck = ["autopep8 (==1.5.5)", "flake8 (==3.8.4)", "mypy (==0.950)", "pbr (==5.5.1)", "pycodestyle (==2.6.0)", "types-setuptools (==57.4.14)"] stylecheck = ["autopep8 (==1.5.5)", "flake8 (==3.8.4)", "mypy (==0.950)", "pbr (==5.5.1)", "pycodestyle (==2.6.0)", "types-setuptools (==57.4.14)"]
test = ["hypothesis (>=6.37.2)", "pytest (>=6.2)"] test = ["hypothesis (>=6.37.2,<6.55.0)", "pytest (>=7.2)"]
[[package]] [[package]]
name = "cycler" name = "cycler"
@ -1180,6 +1202,20 @@ monitor = ["psutil (>=5.7.0)"]
recommended = ["backports.socketpair", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)", "selectors2"] recommended = ["backports.socketpair", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)", "selectors2"]
test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "dnspython (>=1.16.0,<2.0)", "futures", "idna", "mock", "objgraph", "psutil (>=5.7.0)", "requests", "selectors2"] test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "dnspython (>=1.16.0,<2.0)", "futures", "idna", "mock", "objgraph", "psutil (>=5.7.0)", "requests", "selectors2"]
[[package]]
name = "geventhttpclient"
version = "2.0.2"
description = "http client library for gevent"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
brotli = "*"
certifi = "*"
gevent = ">=0.13"
six = "*"
[[package]] [[package]]
name = "greenlet" name = "greenlet"
version = "1.1.3.post0" version = "1.1.3.post0"
@ -1898,6 +1934,14 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "lit"
version = "15.0.7"
description = "A Software Testing Tool"
category = "dev"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "lockfile" name = "lockfile"
version = "0.12.2" version = "0.12.2"
@ -2586,7 +2630,7 @@ numpy = [
[package.source] [package.source]
type = "url" type = "url"
url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu113/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl" url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl"
[[package]] [[package]]
name = "osmium" name = "osmium"
@ -3339,6 +3383,14 @@ category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "python-rapidjson"
version = "1.10"
description = "Python wrapper around rapidjson"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]] [[package]]
name = "python-socketio" name = "python-socketio"
version = "5.7.2" version = "5.7.2"
@ -4220,18 +4272,26 @@ python-versions = ">=3.6,<4.0"
[[package]] [[package]]
name = "torch" name = "torch"
version = "1.11.0+cu113" version = "2.0.0+cu118"
description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7.0" python-versions = ">=3.8.0"
[package.dependencies] [package.dependencies]
filelock = "*"
jinja2 = "*"
networkx = "*"
sympy = "*"
triton = {version = "2.0.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
typing-extensions = "*" typing-extensions = "*"
[package.extras]
opt-einsum = ["opt-einsum (>=3.3)"]
[package.source] [package.source]
type = "url" type = "url"
url = "https://download.pytorch.org/whl/cu113/torch-1.11.0%2Bcu113-cp38-cp38-linux_x86_64.whl" url = "https://download.pytorch.org/whl/cu118/torch-2.0.0%2Bcu118-cp38-cp38-linux_x86_64.whl"
[[package]] [[package]]
name = "torchsummary" name = "torchsummary"
@ -4243,25 +4303,24 @@ python-versions = "*"
[[package]] [[package]]
name = "torchvision" name = "torchvision"
version = "0.12.0+cu113" version = "0.15.1+cu118"
description = "image and video datasets and models for torch deep learning" description = "image and video datasets and models for torch deep learning"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
[package.dependencies] [package.dependencies]
numpy = "*" numpy = "*"
pillow = ">=5.3.0,<8.3.0 || >=8.4.0" pillow = ">=5.3.0,<8.3.0 || >=8.4.0"
requests = "*" requests = "*"
torch = "1.11.0" torch = "2.0.0"
typing-extensions = "*"
[package.extras] [package.extras]
scipy = ["scipy"] scipy = ["scipy"]
[package.source] [package.source]
type = "url" type = "url"
url = "https://download.pytorch.org/whl/cu113/torchvision-0.12.0%2Bcu113-cp38-cp38-linux_x86_64.whl" url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.1%2Bcu118-cp38-cp38-linux_x86_64.whl"
[[package]] [[package]]
name = "tornado" name = "tornado"
@ -4302,16 +4361,41 @@ test = ["pre-commit", "pytest"]
[[package]] [[package]]
name = "triton" name = "triton"
version = "1.1.1" version = "2.0.0"
description = "A language and compiler for custom Deep Learning operations" description = "A language and compiler for custom Deep Learning operations"
category = "dev" category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
[package.dependencies] [package.dependencies]
cmake = "*"
filelock = "*" filelock = "*"
lit = "*"
torch = "*" torch = "*"
[package.extras]
tests = ["autopep8", "flake8", "isort", "numpy", "pytest", "scipy (>=1.7.1)"]
tutorials = ["matplotlib", "pandas", "tabulate"]
[[package]]
name = "tritonclient"
version = "2.28.0"
description = "Python client library and utilities for communicating with Triton Inference Server"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
aiohttp = {version = ">=3.8.1", optional = true, markers = "extra == \"http\""}
geventhttpclient = {version = ">=1.4.4,<=2.0.2", optional = true, markers = "extra == \"http\""}
numpy = ">=1.19.1"
python-rapidjson = ">=0.9.1"
[package.extras]
all = ["aiohttp (>=3.8.1)", "geventhttpclient (>=1.4.4,<=2.0.2)", "grpcio (==1.41.0)", "numpy (>=1.19.1)", "protobuf (>=3.5.0,<3.20)", "python-rapidjson (>=0.9.1)"]
grpc = ["grpcio (==1.41.0)", "numpy (>=1.19.1)", "protobuf (>=3.5.0,<3.20)", "python-rapidjson (>=0.9.1)"]
http = ["aiohttp (>=3.8.1)", "geventhttpclient (>=1.4.4,<=2.0.2)", "numpy (>=1.19.1)", "python-rapidjson (>=0.9.1)"]
[[package]] [[package]]
name = "types-atomicwrites" name = "types-atomicwrites"
version = "1.4.5.1" version = "1.4.5.1"
@ -4357,9 +4441,9 @@ types-urllib3 = "<1.27"
[[package]] [[package]]
name = "types-tabulate" name = "types-tabulate"
version = "0.9.0.1" version = "0.8.11"
description = "Typing stubs for tabulate" description = "Typing stubs for tabulate"
category = "main" category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -4575,7 +4659,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 = "669485055bf8d77336509cb7a3878e06aa32431c520825948914d76b57347fde" content-hash = "b7cd75dfa0dcddff224696ccc7f41d87aac64652f744ab386321c1eee920fbe9"
[metadata.files] [metadata.files]
adal = [ adal = [
@ -4885,6 +4969,90 @@ breathe = [
{file = "breathe-4.34.0-py3-none-any.whl", hash = "sha256:48804dcf0e607a89fb6ad88c729ef12743a42db03ae9489be4ef8f7c4011774a"}, {file = "breathe-4.34.0-py3-none-any.whl", hash = "sha256:48804dcf0e607a89fb6ad88c729ef12743a42db03ae9489be4ef8f7c4011774a"},
{file = "breathe-4.34.0.tar.gz", hash = "sha256:ac0768a5e84addad3e632028fe67749c567aba2b29088493b64c2c1634bcdba1"}, {file = "breathe-4.34.0.tar.gz", hash = "sha256:ac0768a5e84addad3e632028fe67749c567aba2b29088493b64c2c1634bcdba1"},
] ]
brotli = [
{file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"},
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"},
{file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"},
{file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"},
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"},
{file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"},
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"},
{file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"},
{file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"},
{file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"},
{file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"},
{file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"},
{file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"},
{file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"},
{file = "Brotli-1.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"},
{file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"},
{file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"},
{file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"},
{file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"},
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"},
{file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"},
{file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"},
{file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"},
{file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"},
{file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"},
{file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"},
{file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"},
{file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"},
{file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"},
{file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"},
{file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"},
{file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"},
{file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"},
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"},
{file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"},
{file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"},
{file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"},
{file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"},
{file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"},
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"},
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"},
{file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"},
{file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"},
{file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019"},
{file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be"},
{file = "Brotli-1.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7"},
{file = "Brotli-1.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"},
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
]
cachecontrol = [ cachecontrol = [
{file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"},
{file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"},
@ -5040,6 +5208,25 @@ cloudpickle = [
{file = "cloudpickle-2.2.0-py3-none-any.whl", hash = "sha256:7428798d5926d8fcbfd092d18d01a2a03daf8237d8fcdc8095d256b8490796f0"}, {file = "cloudpickle-2.2.0-py3-none-any.whl", hash = "sha256:7428798d5926d8fcbfd092d18d01a2a03daf8237d8fcdc8095d256b8490796f0"},
{file = "cloudpickle-2.2.0.tar.gz", hash = "sha256:3f4219469c55453cfe4737e564b67c2a149109dabf7f242478948b895f61106f"}, {file = "cloudpickle-2.2.0.tar.gz", hash = "sha256:3f4219469c55453cfe4737e564b67c2a149109dabf7f242478948b895f61106f"},
] ]
cmake = [
{file = "cmake-3.26.0-py2.py3-none-macosx_10_10_universal2.macosx_10_10_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:4881727389325af84e39f1ec646b7249d8910b4ed637205bee6d589cb2b2ebd2"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2010_i686.manylinux_2_12_i686.whl", hash = "sha256:babd1e38c85d38a4bf4164c3126ec8cf4cd8d374072e2a4a181e52e953007f8c"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:1851be29d79bb39505954165e934d31994268d49f566ead6fff840a5092e444d"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:483aaaeb9535deaa2657c928af5d9f0da9329f89bc249f494923495745a03677"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2f78c6194f224d462333e08d3acd571c553b58d04935971b87efbe76f241353c"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:cf129c8b71f1344975f179f30287baa8804c4c61ff1b13003244b4157b676e13"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fabb82c69223eda27e673c5c6bb02985d60cf0baa631a2e4932eed87e8229928"},
{file = "cmake-3.26.0-py2.py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502cbed2335557920b88687c6f3ff4ce433bd416204c928ad489e399e76149f5"},
{file = "cmake-3.26.0-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:b81c7775b125786c1696232078e9ceb0c9c66d26fb0a2259f40e33983170f924"},
{file = "cmake-3.26.0-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:f4cc1dde7613cc813d15fd840a33e18cd07a443aa67205d74b9b05f55f08a459"},
{file = "cmake-3.26.0-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:ac6ca9d9ff58900138bbbebc55857647fb99f1c40b84d8a232f23b0d27d7f48a"},
{file = "cmake-3.26.0-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:85e0bad5aeb3a82919ed7d78b76a0462eafe2f918076a1823a09c6f37910f3e0"},
{file = "cmake-3.26.0-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:5523b9701be367572155e77294bf8aa3ac9aa0d73c50dcd6c57903d12bcb308e"},
{file = "cmake-3.26.0-py2.py3-none-win32.whl", hash = "sha256:96f0e887260255eeb83bcf3465d51bc6c94078251c9312fa142dce6d3e80acac"},
{file = "cmake-3.26.0-py2.py3-none-win_amd64.whl", hash = "sha256:a0719a6f79cdc4d7b16caf757b8ae13eae1c6ce5a08d594cc09774afe129515d"},
{file = "cmake-3.26.0-py2.py3-none-win_arm64.whl", hash = "sha256:11159c9b64c6473d84361ab44ef2c85e1bd2db7a2b1b798ce8bf5988f32adf43"},
{file = "cmake-3.26.0.tar.gz", hash = "sha256:c18185c9cc147d0fa1e9228962aa37901b37866bd5d617e9efa23dfe706f7321"},
]
colorama = [ colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
@ -5209,15 +5396,17 @@ cryptography = [
{file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"},
{file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"},
] ]
cupy-cuda113 = [ cupy-cuda11x = [
{file = "cupy_cuda113-10.6.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:27e5efe2c3afa80ff48654cb27f9e0eddb36f8b26ef0d32d3ba0a233e1359b51"}, {file = "cupy_cuda11x-11.6.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:1b9914f57868a1559e9bfabfbae8c724585914e8e1f277acb9cdb6aa0756eaa4"},
{file = "cupy_cuda113-10.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:8b96076d1ddd33fdb2c908ed0f8109caf69d37d36f839a8a8cdae1312508336f"}, {file = "cupy_cuda11x-11.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac8dd082ddb00996bc4d37cc5765907048f467aadb61bcbff25f3c2a88c50583"},
{file = "cupy_cuda113-10.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22363c2863727cae5154aa4bab9e8a648d7fe66c9e2195d81dd4e8693c2e61ce"}, {file = "cupy_cuda11x-11.6.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:bf7496340dfbc2eaa3e0ebfd03c4ab8b1fc36d7d14f68718c33eeb395aaa6eed"},
{file = "cupy_cuda113-10.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8cc69b9d5735372477a7af3822c8f8e996ffe6de05cfc917500af9dc0117ca3e"}, {file = "cupy_cuda11x-11.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:e3bbd55c26d60069d7e5af2200fb47e7c42ca6437acac297637a2d3c00f6fe46"},
{file = "cupy_cuda113-10.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:10dc6899577e445426d81f0960ba9059d9aaa750426997c61fad882d6345264c"}, {file = "cupy_cuda11x-11.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0d7ebcfd7234946719ad28890593d9fc78fad5753ef4b073e0d7bdb7da2f5640"},
{file = "cupy_cuda113-10.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:c6893ac9040a11610e63973063dfd715dbda8bd07ef99951bab7a09c7f335e1e"}, {file = "cupy_cuda11x-11.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3bc3f81fbc5a86c7155c6036809f2ada4023fc0870dce158d3d9f6d0b575727f"},
{file = "cupy_cuda113-10.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4bf4bc06d991c06b95f6fe558d117cafd93bd4eeaf80606f18dd31d20d2eff25"}, {file = "cupy_cuda11x-11.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1d6810568e683cb153972b3019ddb5efc369036511122117d9eda09cf84d1042"},
{file = "cupy_cuda113-10.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:3745fc42dca86ba8a1109ddc7964aed8e1efc0ce8085cb2f140dcd6429f26354"}, {file = "cupy_cuda11x-11.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:31c1ec72ffe9ad6fda9fb0a2aff1fcca38da66a8e521e333bae0d67ebc80ead0"},
{file = "cupy_cuda11x-11.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6a4822daadfce0464cb619099eb82c7ba30ae7755d6869ba5eba1c675a9eed67"},
{file = "cupy_cuda11x-11.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:3d0f44d747b38ce2b213d64270265f953b68cf29860ec5fa65848b9faaea3fc1"},
] ]
cycler = [ cycler = [
{file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"},
@ -5648,6 +5837,86 @@ gevent = [
{file = "gevent-22.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0569e133bb620de1001ac807ad9a8abaadedd25349c6d695f80c9048a3f59d42"}, {file = "gevent-22.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0569e133bb620de1001ac807ad9a8abaadedd25349c6d695f80c9048a3f59d42"},
{file = "gevent-22.10.1.tar.gz", hash = "sha256:df3042349c9a4460eeaec8d0e56d737cb183eed055e75a6af9dbda94aaddaf4d"}, {file = "gevent-22.10.1.tar.gz", hash = "sha256:df3042349c9a4460eeaec8d0e56d737cb183eed055e75a6af9dbda94aaddaf4d"},
] ]
geventhttpclient = [
{file = "geventhttpclient-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd76acdc7e7ee5c54c7b279f806b28957a6b092f79c40db34adcfd972749343c"},
{file = "geventhttpclient-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:320a2c756d8a4f296de370476a1515485c186d9e22c3fc29e04f8f743a7d47bb"},
{file = "geventhttpclient-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36d3345c6585b09738195a7c45d279a87ccbab0350f1cce3679d3f0dce8577a1"},
{file = "geventhttpclient-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:407d54499556c2741b93691b86da93232590b013f4a0b773327d766fe3e5c0a9"},
{file = "geventhttpclient-2.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcf325131b0e4600b793643108cd85dddd66bbf532fd2eb498be5727ef532a1e"},
{file = "geventhttpclient-2.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5841dd02e6f792a4ef15dbd04fefe620c831ba0b78105808160bb779a31af4"},
{file = "geventhttpclient-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2ba69422d4e8670dd99803b1313ba574a4d41f52e92b512af51068c9c577bdc1"},
{file = "geventhttpclient-2.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e3af579c6b46b9caa515a8baf6a2cadeafcd1d41ad22ca5712851f074a40b47"},
{file = "geventhttpclient-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ff7fc19f9a4fdd54a2b1c106a705ea2c679fa049685ed763051d417725bdab1"},
{file = "geventhttpclient-2.0.2-cp310-cp310-win32.whl", hash = "sha256:eec7c52e8eb817674a193e0124486b507215d9e86d34f2638bf9a9292d16f815"},
{file = "geventhttpclient-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:0e9f7283c01d970e643d89da81127869a8d94bb7a0081020dcad5b590bc007c4"},
{file = "geventhttpclient-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5ceb492d43a659b895794999dc40d0e7c23b1d41dd34040bbacd0dc264b57d5b"},
{file = "geventhttpclient-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95959c201d3151fa8f57e0f1ce184476d1173996bdde41dc7d600006023dc5be"},
{file = "geventhttpclient-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:31c7febba298ecf44838561074a3fb7a01523adca286469b5a82dcc90e8d6a07"},
{file = "geventhttpclient-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:996c5f453d810b3c592160193d6832a065cca0112e92adc74e62df0e4c564df6"},
{file = "geventhttpclient-2.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f817e226c02b5a71d86de3772d6accdf250288d1e6825e426c713759830162d"},
{file = "geventhttpclient-2.0.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c55b7ac0ba0e1e1afbf297b7608f0b3a0bbc34fb4b0c19b7869f32a77ddc6209"},
{file = "geventhttpclient-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6775bc81e25c48fa58b034444aecfa508b0c3d1bc1e4ae546cc17661be1f51aa"},
{file = "geventhttpclient-2.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a0156882c73537bbbbc7c693ae44c9808119963174078692613ffa4feea21fcf"},
{file = "geventhttpclient-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3ebb582a291c4c5daaac2ea115b413f4be86874baa60def44d333301cee17bd7"},
{file = "geventhttpclient-2.0.2-cp311-cp311-win32.whl", hash = "sha256:716f1f72f50b841daf9c9511a01fc31a030866510a11863f27741e26e4f556a7"},
{file = "geventhttpclient-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:777fcdb72077dfbf70516ecb9e9022246dd337b83a4c1e96f17f3ab9e15f4547"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:379d90d8b1fcdda94e74d693806e0b0116c0610504e7f62d5576bac738dc66a5"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00b7b2b836294c091c53789a469c5671202d79420b5191931df4e3a767d607fa"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d075355862d7726eb3436f0136fce7650c884f2d04eaae7a39fed3aad9798bc"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa7b1a27f950d209fe223a97906fe41312dc12c92372424639b8a9b96f1adf91"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fe4e06313aad353b103950780b050d3958000464cc732d621ff8ea3cacbd2bc4"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:84d7be660b6bc53dd53e3f46b3bc5d275972a8116bd183a77139bb4d9d6d9fb1"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:81f839d6becd664d0972b488422f5dc821f8ad2f2196d53aa5e4d799a3a35a66"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:e707f62271a093e6e3af6f1bbd8cc398b414b8c508fe6b15505dd8e76c4409ac"},
{file = "geventhttpclient-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:28d7655d1d50bc75ece683a0ae8faf978821d4aeae358d77b59371548db07f1e"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58877b4440a580063571a23fbc616aed7c735c6bf9ef525c5129783df8b6966"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57c993c4b2bea551c4a71b75ae1e172e9f3e4352f704ff1b619a0f16aa762f76"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f67e789e31c7b1ce440cd1465dcdefeca29ba6108735eac0b1a593d3a55b7f"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f3326e115ec7e7ce95a5d0d47698e8f3584944c4c434a7404937d56b17136b8"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ef328ee3e7dca5055b833fdf3c181647a335abf0249947b27f5df2d95390198c"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:27049ea40e3b559eee380310272aaa9b7c19e73c1d9e51e2ec137362be2caa70"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b88a10538341e33fed1682c0dd4579c655d49db5863e7456583085a1cd6bd9d4"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:d52aba2c38420b3fc518188449f1c2a46b1a99adf1c0266c68e72ee0422cd0fa"},
{file = "geventhttpclient-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3648626ca58ea4b340e695d78e5d533e6b8be78d375edbd42ff188bc3447e095"},
{file = "geventhttpclient-2.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fcf96e212b55b93490f3a5fcdfe7a2ef4995a0d13b7d9df398b11e319b7a86b1"},
{file = "geventhttpclient-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e9f2ff09706e3a64a99886d5f2595f3bf364821bc609f2865dbc3e499e21a36"},
{file = "geventhttpclient-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:721c3075897bfc81e918066f16ae3d1a88c7bb14eeeb831a4f89ea636474643e"},
{file = "geventhttpclient-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91615fed7931acd49cfe5fc30984acd5411dc1f2643b1544c879d1a537233c6d"},
{file = "geventhttpclient-2.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7adaa29e5699dea54e0224d1d2d9d8869668d8ad79f5b89433ff9c46f9424a6c"},
{file = "geventhttpclient-2.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9be5000ba57336a90b438782117c1e43205f51f49aa9b1499a82e210e8431b11"},
{file = "geventhttpclient-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:12d271cc53486efb3716e99855dc5cb84f2cd3fc9f3243721747bb39ec0fff8a"},
{file = "geventhttpclient-2.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b9c0c6b75b3905000d2490dc64b4c98a8bac155efbc0ff8917ac082ae0bad261"},
{file = "geventhttpclient-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e956a457d8831dc81d6f046ab09ebeec680f9a1e9c07e25a1906e77b287918ee"},
{file = "geventhttpclient-2.0.2-cp38-cp38-win32.whl", hash = "sha256:bc46d5479673dfb293ea428c057d2e23e48ebef5c5d44587cdbaada7f87553e4"},
{file = "geventhttpclient-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:f44153e4b3ef9b901edcd14be54145a0058bf5fa371b3e583153865fac866245"},
{file = "geventhttpclient-2.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ebf98db9435824cf0b80b5247be6c88b20bfafd6249f7ebaabb85297da37e380"},
{file = "geventhttpclient-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c8b7298eb1ebd015257bf4503e34f5fbbe64bd83324140f76b511046aba5a0d5"},
{file = "geventhttpclient-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:60b81a6d4e65db7c1a5350c9fb72ebf800b478849a7e8020d1ab93af237a3747"},
{file = "geventhttpclient-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad6c2fcbc3733785bd3b8c2bb43d1f605f9085b0a8b70ce354d198f37143f884"},
{file = "geventhttpclient-2.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94edb022fa50d576cf63f6dd0c437c1acd24a719872a5935991aaf08f8e88cb2"},
{file = "geventhttpclient-2.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca459cedb3827d960362e05ea3a4ae600a6d0d93de77eac2ac0f79828e5e18c"},
{file = "geventhttpclient-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7551b6db860b56411de1f96618e91b54f65e1a7be8d10255bd1adfb738bb6ee5"},
{file = "geventhttpclient-2.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bcb7e061c243308d9a44b02de5298001e917f1636a9f270c10da86601fcc8dfa"},
{file = "geventhttpclient-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:96922d170ef8933f4c20036e8d70d4fbe861f54c543e32e7459ebdbaafa65a2e"},
{file = "geventhttpclient-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ebb3c993903d40fd4bb1f3e55b84c62c8fc1d14433ae6d4d477dd9a325354c94"},
{file = "geventhttpclient-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:dbccf1ba155dea3ea99ba0e67a835c05b4303f05298e85f5bb2a46700ccdf092"},
{file = "geventhttpclient-2.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8770b8ab9e8c31d2aaf8a6fbc63fbb7239c58db10bb49cee191ca5c141c61542"},
{file = "geventhttpclient-2.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daff1e977fccf98f27266d3891afdc101f1d705a48331754909e960bcae83f8a"},
{file = "geventhttpclient-2.0.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2435e0f2a60e00d977822ec4c12e7851deb7aa49a23d32d648e72c641aae3b05"},
{file = "geventhttpclient-2.0.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09acd03d0a8c1bb7d5a1cb6fcb77aaa19a907c1b4915ab58da5d283675edb0a5"},
{file = "geventhttpclient-2.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5d0813d97050446dab2fb243312e6c446e4ef5e9591befd597ef8f2887f8e2a8"},
{file = "geventhttpclient-2.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:852da9bb0fc792cdca5ffc9327490094783e42415494b3569e5d532615027439"},
{file = "geventhttpclient-2.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79304a63a9d0512f2757c5862487b332b18a9c85feebecf6ebc3526c6dd1ba2"},
{file = "geventhttpclient-2.0.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c1c783fce45f16db448d7e34864f1e9c22fe60a7780d2c1c14edbb1fb7262e"},
{file = "geventhttpclient-2.0.2-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c407c2b4bea817c6f752502db4ab0e9f9465b4fb85b459d1332b5f93a3096c"},
{file = "geventhttpclient-2.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f0d70a83ef4ab93102c6601477c13e9cdbc87205e5237fbf5797e30dc9d3ee8"},
{file = "geventhttpclient-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b03f298ec19b8a4717cce8112fe30322c9e5bfada84dde61a1a44d1eeffc1d3c"},
{file = "geventhttpclient-2.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2dc94b9a23eb6744a8c729aec2b1cdc4e39acf1d8f16ea85a62810aa6b2cae5"},
{file = "geventhttpclient-2.0.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:805554594bb29231fd990cc2cbbe493d223d76a6085fec891dd76bb4e0928933"},
{file = "geventhttpclient-2.0.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb23527d98f626ca7a4e8961ed9bdc6aed3388de306614c69a133b34262460f4"},
{file = "geventhttpclient-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a594ab319872a38fb7f16be4cfb107d3c63c43a081f2abe241834e9877f27401"},
{file = "geventhttpclient-2.0.2.tar.gz", hash = "sha256:8135a85200b170def7293d01dd1557931fcd1bec1ac78c52ad7cedd22368b9ba"},
]
greenlet = [ greenlet = [
{file = "greenlet-1.1.3.post0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:949c9061b8c6d3e6e439466a9be1e787208dec6246f4ec5fffe9677b4c19fcc3"}, {file = "greenlet-1.1.3.post0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:949c9061b8c6d3e6e439466a9be1e787208dec6246f4ec5fffe9677b4c19fcc3"},
{file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d7815e1519a8361c5ea2a7a5864945906f8e386fa1bc26797b4d443ab11a4589"}, {file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d7815e1519a8361c5ea2a7a5864945906f8e386fa1bc26797b4d443ab11a4589"},
@ -6045,6 +6314,9 @@ libusb1 = [
{file = "libusb1-3.0.0-py3-none-win_amd64.whl", hash = "sha256:6f6bb010632ada35c661d17a65e135077beef0fbb2434d5ffdb3a4a911fd9490"}, {file = "libusb1-3.0.0-py3-none-win_amd64.whl", hash = "sha256:6f6bb010632ada35c661d17a65e135077beef0fbb2434d5ffdb3a4a911fd9490"},
{file = "libusb1-3.0.0.tar.gz", hash = "sha256:5792a9defee40f15d330a40d9b1800545c32e47ba7fc66b6f28f133c9fcc8538"}, {file = "libusb1-3.0.0.tar.gz", hash = "sha256:5792a9defee40f15d330a40d9b1800545c32e47ba7fc66b6f28f133c9fcc8538"},
] ]
lit = [
{file = "lit-15.0.7.tar.gz", hash = "sha256:ed08ac55afe714a193653df293ae8a6ee6c45d6fb11eeca72ce347d99b88ecc8"},
]
lockfile = [ lockfile = [
{file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"},
{file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"},
@ -6624,6 +6896,7 @@ 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"},
@ -7009,6 +7282,7 @@ 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"},
@ -7016,12 +7290,14 @@ 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"},
@ -7290,6 +7566,64 @@ python-engineio = [
python-logstash = [ python-logstash = [
{file = "python-logstash-0.4.8.tar.gz", hash = "sha256:d04e1ce11ecc107e4a4f3b807fc57d96811e964a554081b3bbb44732f74ef5f9"}, {file = "python-logstash-0.4.8.tar.gz", hash = "sha256:d04e1ce11ecc107e4a4f3b807fc57d96811e964a554081b3bbb44732f74ef5f9"},
] ]
python-rapidjson = [
{file = "python-rapidjson-1.10.tar.gz", hash = "sha256:acfecbf5edb91ec72a20a125de7f56b8c2f6161eff4c65382c8ee6a2484d3540"},
{file = "python_rapidjson-1.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1db7b0af882999f5685eb7046a0f3b3aca5d55a3e84b3089747d29a4ec6fdade"},
{file = "python_rapidjson-1.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87c8c8b615513f9dc414af1554140589036d14840f5e1f1845965e1c0a080e1"},
{file = "python_rapidjson-1.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a0a2f5c4abe529ca2764343416e35710a263832533b7bdc76c3285efb5b5ecc8"},
{file = "python_rapidjson-1.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40467c3a6d8f070cc4d196fe46a79ed59d1a13a4d3fdc6a0325a21816600e5a7"},
{file = "python_rapidjson-1.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df4e7237a3e77666ccb9b437013294e6aa3968528f7c61f60f6f38eea0f8f79"},
{file = "python_rapidjson-1.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:99a5215f24ff1fa6cc67ee275a6852aa56d934d3b8cd7a40197feb632b54fd76"},
{file = "python_rapidjson-1.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3dbea0ee9fa1cd6ecc13a949f6bb94013639d39cdb56f58df4ab61130d35e57c"},
{file = "python_rapidjson-1.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:6d1d0c5da3bd5f701b1aed550e1e7bd59b16ae642877cddf18815006cf998f9a"},
{file = "python_rapidjson-1.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:072f76c1f1483bcc4056d7d3a8b0319bf841a73e955f188302094b62b2163bf9"},
{file = "python_rapidjson-1.10-cp310-cp310-win32.whl", hash = "sha256:c95d466307a2140a7687a575103980c6e81c9f62d19556cafad3d6b2932b7eb1"},
{file = "python_rapidjson-1.10-cp310-cp310-win_amd64.whl", hash = "sha256:454ffda58cc6fed64d983b1b8ae4b39a563b4fd671dae9132e06450025898539"},
{file = "python_rapidjson-1.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fff343076fbeee0cd7e4e3fb9472f2d567a127ec7b8b5b7ecba6bf7960a3ce07"},
{file = "python_rapidjson-1.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:686482c67727edad4b6d0c753bc159f35134a5a623e9651c4b7c008ef2996252"},
{file = "python_rapidjson-1.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ef7d55688b7123d62690b193537cc048fa9f35cfa43d249fedc0d9fd398890a"},
{file = "python_rapidjson-1.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f27c0601792533ab6e98452961d61566480dc155da19d2a358a5fd9a85d9321"},
{file = "python_rapidjson-1.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95176e35e3bacb8a1a27f563e815b5b57c717992c871b1c25fd76a835fbba32c"},
{file = "python_rapidjson-1.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47a0ec20886b8be86af307c10d699a447e22979ed7dd1f2b7ed5cb7496b3d920"},
{file = "python_rapidjson-1.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7968c0cb09d9a76aa2483556ba46ab42634baf216cb2f2c7cd6bf77119a33c1"},
{file = "python_rapidjson-1.10-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5faab270a4dd49216ceaca7169682680b2f5df8311c1ed259e4612d9d0cf61b7"},
{file = "python_rapidjson-1.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81b797934dc037810f5f98af138b55a3b6f18dd569cc5e8f81fe79956a4717ca"},
{file = "python_rapidjson-1.10-cp311-cp311-win32.whl", hash = "sha256:6c1d62cc58a61629fc5e216fb7b3a1b02787c98fded874a7b474b1e6325e377e"},
{file = "python_rapidjson-1.10-cp311-cp311-win_amd64.whl", hash = "sha256:29d31fc4254f1a4dca420e58bd1331e990fc2959d09ff2daa7934d52732a8491"},
{file = "python_rapidjson-1.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:718f4e217b511cfbf9166f55ccf4bf4e4538495bee403e390cf89791c0debc26"},
{file = "python_rapidjson-1.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:541bbb96353cf3fe2bdb29e727087226532be4e4573daad6f042cfdea533a564"},
{file = "python_rapidjson-1.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50f0402a6899c6a177d4a37152deefcd59c61e44bef56b71e8d006a186c86286"},
{file = "python_rapidjson-1.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c035e17744d6d6fba073b550b0040a74e55f2ad33fd798df206ff6879b41ad10"},
{file = "python_rapidjson-1.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da970bde42309a74a5556e696673ea11c4545b8bee5081b84265ded460b2e9ef"},
{file = "python_rapidjson-1.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7437a649821008aa456f2fbec737880d7f9bdda7ec94cc1743a43ccf32b5d26"},
{file = "python_rapidjson-1.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:362d969bbd277f78bf0b1ffaa810857ea40351146b827f896f8d49e9c25fc99c"},
{file = "python_rapidjson-1.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:409256e7748c4ab7f17b3793c7a78ca01914c487644fc42140d116ed4dec8c4f"},
{file = "python_rapidjson-1.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a34a7e2853456fe50ba2ee22e38e7841e55eee10021d4496cce62285f148e8e7"},
{file = "python_rapidjson-1.10-cp37-cp37m-win32.whl", hash = "sha256:bc4a97940e5afa60a598483d0eb863b26e4810aaf030d92a4301f5fc183e1b6d"},
{file = "python_rapidjson-1.10-cp37-cp37m-win_amd64.whl", hash = "sha256:89586b67f9c69b66885774acebf3d018e7b8f93cea2b3cffa306ec9d37877594"},
{file = "python_rapidjson-1.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e38082b1a8ce3e2bd55821852c0cd643cdabe6497fd9c054f6b47a099afbea0"},
{file = "python_rapidjson-1.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0955ef22fabb36b26fcad702ae54c1bce2bc2a74b1883c42d251d72011d0d426"},
{file = "python_rapidjson-1.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a9db03c68ab0158bcdf80299b2c980186d148aa3e05d5650fea5148a425a29a"},
{file = "python_rapidjson-1.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde8ab0f06debaa06d93085f19dc3ec3db53f22883f1625dd32b96a87e7009de"},
{file = "python_rapidjson-1.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23539c9f7d85d64a00d3cb44c7d9ab3be2184d4da42a5f3263dcfd1d0203ee43"},
{file = "python_rapidjson-1.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8bfd484285f3477acef0bb45abd2b80b6252e35a5a53395ce48f0327cbe43c23"},
{file = "python_rapidjson-1.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8b0ed643ebaa8ddf3f40422752efe83abda29aa30a9e6866ccd9dd591b5057d0"},
{file = "python_rapidjson-1.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:5aeca661a0f229f1312fb3ad3e1a5c6736d49942d80d4931810158559eb8f119"},
{file = "python_rapidjson-1.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8a68ed066e8f0878b7112f943cf35ba9e5217395bcdd8cb478cde01871e2701c"},
{file = "python_rapidjson-1.10-cp38-cp38-win32.whl", hash = "sha256:d286be6f63446776c4958bb37824c683194b4878fc9cd5b7255134fb5a6ba536"},
{file = "python_rapidjson-1.10-cp38-cp38-win_amd64.whl", hash = "sha256:aece5270c6e6d5c3d54586c9a5fb9677d70d7019744a59560c5c369c7b9bba25"},
{file = "python_rapidjson-1.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc09c5ad0fe71f262cdcc5655409f132f1560a8af80e76e7757945ce401fdbab"},
{file = "python_rapidjson-1.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f6447bd7a8ff5135ab7e372b48a174d3c560d5b322e32bd465e8458e6e4593"},
{file = "python_rapidjson-1.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22ede69213885391b46cc14596bfd4cd1a5c6f34a2db6600fb08b03982dbc7b7"},
{file = "python_rapidjson-1.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a9425129623718a04b885a12190faa23e7997c4e8632054e18df7ea473f746d"},
{file = "python_rapidjson-1.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e10f32e1a8d155448842934cbe71eb620b4b4a0cb3627ba4c4856e27556534"},
{file = "python_rapidjson-1.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d23caab17b87ed5b82e28cdc19172ba1ca65c982e3fff387961d3f33710031f"},
{file = "python_rapidjson-1.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f07d4fcdcfd64bdad0143b9705c5d5089677ebddf60ac6c1f8074a34b1c70cf9"},
{file = "python_rapidjson-1.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:87b991c7ae435489c56a46cef228d2b65a3df689ee4fe24fab69c791c841f633"},
{file = "python_rapidjson-1.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3706a5c4f21073c04be133c36565efc6e3f5646a03c8d19af78c19d7c70eb708"},
{file = "python_rapidjson-1.10-cp39-cp39-win32.whl", hash = "sha256:47f9078ea6884f700166a8728d863609fec62232e66a33b8fb4a7706ce7c731c"},
{file = "python_rapidjson-1.10-cp39-cp39-win_amd64.whl", hash = "sha256:9e4921ab7002ae9faad7f439a7c50aa195039f177e9e51a76c34c97966c79a79"},
]
python-socketio = [ python-socketio = [
{file = "python-socketio-5.7.2.tar.gz", hash = "sha256:92395062d9db3c13d30e7cdedaa0e1330bba78505645db695415f9a3c628d097"}, {file = "python-socketio-5.7.2.tar.gz", hash = "sha256:92395062d9db3c13d30e7cdedaa0e1330bba78505645db695415f9a3c628d097"},
{file = "python_socketio-5.7.2-py3-none-any.whl", hash = "sha256:d9a9f047e6fdd306c852fbac36516f4b495c2096f8ad9ceb8803b8e5ff5622e3"}, {file = "python_socketio-5.7.2-py3-none-any.whl", hash = "sha256:d9a9f047e6fdd306c852fbac36516f4b495c2096f8ad9ceb8803b8e5ff5622e3"},
@ -7663,6 +7997,18 @@ 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"},
@ -8032,11 +8378,28 @@ traitlets = [
{file = "traitlets-5.5.0.tar.gz", hash = "sha256:b122f9ff2f2f6c1709dab289a05555be011c87828e911c0cf4074b85cb780a79"}, {file = "traitlets-5.5.0.tar.gz", hash = "sha256:b122f9ff2f2f6c1709dab289a05555be011c87828e911c0cf4074b85cb780a79"},
] ]
triton = [ triton = [
{file = "triton-1.1.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8441e6f44517aef8f6345f621c003926cbe970892802411a949ccda516cbd5ba"}, {file = "triton-2.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38806ee9663f4b0f7cd64790e96c579374089e58f49aac4a6608121aa55e2505"},
{file = "triton-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:840776bc1f4757fb2d6af974694c5e5313220ceec238ee6118b9728bc2aa9ade"}, {file = "triton-2.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:226941c7b8595219ddef59a1fdb821e8c744289a132415ddd584facedeb475b1"},
{file = "triton-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d42cdaa7d56de463d762c18cc876bfd0828a2b6a706263393fe7e10d1c83ca"}, {file = "triton-2.0.0-1-cp36-cp36m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4c9fc8c89874bc48eb7e7b2107a9b8d2c0bf139778637be5bfccb09191685cfd"},
{file = "triton-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc19c0e902bbf7d29de4d444455608065a2c56e3524f4bc94e724511ca518f3"}, {file = "triton-2.0.0-1-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d2684b6a60b9f174f447f36f933e9a45f31db96cb723723ecd2dcfd1c57b778b"},
{file = "triton-1.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02f798cd2dd922228082ce1a4e9d81badb9a6217a9aac6d783e95bf7055974d"}, {file = "triton-2.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9d4978298b74fcf59a75fe71e535c092b023088933b2f1df933ec32615e4beef"},
{file = "triton-2.0.0-1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:74f118c12b437fb2ca25e1a04759173b517582fcf4c7be11913316c764213656"},
{file = "triton-2.0.0-1-pp37-pypy37_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9618815a8da1d9157514f08f855d9e9ff92e329cd81c0305003eb9ec25cc5add"},
{file = "triton-2.0.0-1-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aca3303629cd3136375b82cb9921727f804e47ebee27b2677fef23005c3851a"},
{file = "triton-2.0.0-1-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e3e13aa8b527c9b642e3a9defcc0fbd8ffbe1c80d8ac8c15a01692478dc64d8a"},
{file = "triton-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f05a7e64e4ca0565535e3d5d3405d7e49f9d308505bb7773d21fb26a4c008c2"},
{file = "triton-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4b99ca3c6844066e516658541d876c28a5f6e3a852286bbc97ad57134827fd"},
{file = "triton-2.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47b4d70dc92fb40af553b4460492c31dc7d3a114a979ffb7a5cdedb7eb546c08"},
{file = "triton-2.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fedce6a381901b1547e0e7e1f2546e4f65dca6d91e2d8a7305a2d1f5551895be"},
{file = "triton-2.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75834f27926eab6c7f00ce73aaf1ab5bfb9bec6eb57ab7c0bfc0a23fac803b4c"},
{file = "triton-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0117722f8c2b579cd429e0bee80f7731ae05f63fe8e9414acd9a679885fcbf42"},
{file = "triton-2.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcd9be5d0c2e45d2b7e6ddc6da20112b6862d69741576f9c3dbaf941d745ecae"},
{file = "triton-2.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42a0d2c3fc2eab4ba71384f2e785fbfd47aa41ae05fa58bf12cb31dcbd0aeceb"},
{file = "triton-2.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c47b72c72693198163ece9d90a721299e4fb3b8e24fd13141e384ad952724f"},
]
tritonclient = [
{file = "tritonclient-2.28.0-py3-none-any.whl", hash = "sha256:1f58bbe09a88c35f7979de8ab6579a5337372951f723c0aba31e8bee3e8d79da"},
{file = "tritonclient-2.28.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:47d93197a0876a743012db4c03f1100f7b225b9aaf8d5f8025bf4a5d9e61bfd2"},
] ]
types-atomicwrites = [ types-atomicwrites = [
{file = "types-atomicwrites-1.4.5.1.tar.gz", hash = "sha256:9e9f0923ebf93524b28bcece5a23ac8c3820f39b060df29f671936d2e4bc04bc"}, {file = "types-atomicwrites-1.4.5.1.tar.gz", hash = "sha256:9e9f0923ebf93524b28bcece5a23ac8c3820f39b060df29f671936d2e4bc04bc"},
@ -8059,8 +8422,8 @@ types-requests = [
{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 = [ types-tabulate = [
{file = "types-tabulate-0.9.0.1.tar.gz", hash = "sha256:e486292c279f19247865bdabe802419740a0e74b53444e7f7a8009e08129da5d"}, {file = "types-tabulate-0.8.11.tar.gz", hash = "sha256:17a5fa3b5ca453815778fc9865e8ecd0118b07b2b9faff3e2b06fe448174dd5e"},
{file = "types_tabulate-0.9.0.1-py3-none-any.whl", hash = "sha256:be2ea0de05f615ccfcbadf6206aa720e265955eb1de23e343aec9d8bf3fa9aaa"}, {file = "types_tabulate-0.8.11-py3-none-any.whl", hash = "sha256:af811268241e8fb87b63c052c87d1e329898a93191309d5d42111372232b2e0e"},
] ]
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"},

@ -59,7 +59,6 @@ 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]
@ -84,7 +83,7 @@ mypy = "^0.961"
myst-parser = "^0.18.0" myst-parser = "^0.18.0"
natsort = "^8.1.0" natsort = "^8.1.0"
numpy = "^1.23.0" numpy = "^1.23.0"
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu113/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl", platform = "linux" } opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl", platform = "linux" }
pandas = "^1.4.3" pandas = "^1.4.3"
parameterized = "^0.8.1" parameterized = "^0.8.1"
paramiko = "^2.11.0" paramiko = "^2.11.0"
@ -108,6 +107,7 @@ types-certifi = "^2021.10.8"
types-pycurl = "^7.45.1" types-pycurl = "^7.45.1"
types-PyYAML = "^6.0" types-PyYAML = "^6.0"
types-requests = "^2.28.11" types-requests = "^2.28.11"
types-tabulate = "^0.8.10"
[tool.poetry.group.xx] [tool.poetry.group.xx]
@ -117,7 +117,7 @@ optional = true
aenum = "^3.1.11" aenum = "^3.1.11"
aiohttp = "^3.8.1" aiohttp = "^3.8.1"
albumentations = "^1.2.1" albumentations = "^1.2.1"
apex = { url = "https://github.com/commaai/apex/releases/download/pytorch1.10.0%2Bcu11.1/apex-0.1-cp38-cp38-linux_x86_64.whl" } apex = { url = "https://github.com/commaai/apex/releases/download/pytorch2.0.0%2Bcu11.8/apex-0.1-cp38-cp38-linux_x86_64.whl" }
azure-cli-core = "^2.38.0" azure-cli-core = "^2.38.0"
azure-common = "^1.1.28" azure-common = "^1.1.28"
azure-core = "^1.24.2" azure-core = "^1.24.2"
@ -127,7 +127,7 @@ azure-storage-nspkg = "~3.1"
blosc = "==1.9.2" blosc = "==1.9.2"
cloudpickle = "^2.1.0" cloudpickle = "^2.1.0"
configargparse = "^1.5.3" configargparse = "^1.5.3"
cupy-cuda113 = "^10.6.0" cupy-cuda11x = "^11.6.0"
datadog = "^0.44.0" datadog = "^0.44.0"
dotmap = "^1.3.30" dotmap = "^1.3.30"
einops = "^0.5.0" einops = "^0.5.0"
@ -166,14 +166,15 @@ scikit-learn = "^1.1.1"
segmentation-models-pytorch = "==0.2.1" segmentation-models-pytorch = "==0.2.1"
simplejson = "^3.17.6" simplejson = "^3.17.6"
SQLAlchemy = "^1.4.39" SQLAlchemy = "^1.4.39"
torch = { url = "https://download.pytorch.org/whl/cu113/torch-1.11.0%2Bcu113-cp38-cp38-linux_x86_64.whl" } torch = { url = "https://download.pytorch.org/whl/cu118/torch-2.0.0%2Bcu118-cp38-cp38-linux_x86_64.whl" }
torchsummary = "^1.5.1" torchsummary = "^1.5.1"
torchvision = { url = "https://download.pytorch.org/whl/cu113/torchvision-0.12.0%2Bcu113-cp38-cp38-linux_x86_64.whl" } torchvision = { url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.1%2Bcu118-cp38-cp38-linux_x86_64.whl" }
triton = "^1.1.1" triton = "^2.0.0"
Werkzeug = "^2.1.2" Werkzeug = "^2.1.2"
zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" } zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" }
omegaconf = "^2.3.0" omegaconf = "^2.3.0"
osmnx = "==1.2.2" osmnx = "==1.2.2"
tritonclient = {version = "2.28.0", extras = ["http"]}
[build-system] [build-system]

@ -113,32 +113,35 @@ bool safety_setter_thread(std::vector<Panda *> pandas) {
return false; return false;
} }
// set to ELM327 for fingerprinting // initialize to ELM327 without OBD multiplexing for fingerprinting
bool obd_multiplexing_enabled = false;
for (int i = 0; i < pandas.size(); i++) { for (int i = 0; i < pandas.size(); i++) {
const uint16_t safety_param = (i > 0) ? 1U : 0U; pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
} }
// wait for FW query at OBD port to finish // openpilot can switch between multiplexing modes for different FW queries
while (true) { while (true) {
if (do_exit || !check_all_connected(pandas) || !ignition) { if (do_exit || !check_all_connected(pandas) || !ignition) {
return false; return false;
} }
if (p.getBool("FirmwareObdQueryDone")) { bool obd_multiplexing_requested = p.getBool("ObdMultiplexingEnabled");
LOGW("finished FW query at OBD port"); if (obd_multiplexing_requested != obd_multiplexing_enabled) {
for (int i = 0; i < pandas.size(); i++) {
const uint16_t safety_param = (i > 0 || !obd_multiplexing_requested) ? 1U : 0U;
pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
}
obd_multiplexing_enabled = obd_multiplexing_requested;
p.putBool("ObdMultiplexingChanged", true);
}
if (p.getBool("FirmwareQueryDone")) {
LOGW("finished FW query");
break; break;
} }
util::sleep_for(20); util::sleep_for(20);
} }
// set to ELM327 to finish fingerprinting and for potential ECU knockouts
for (Panda *panda : pandas) {
panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
}
p.putBool("ObdMultiplexingDisabled", true);
std::string params; std::string params;
LOGW("waiting for params to set safety model"); LOGW("waiting for params to set safety model");
while (true) { while (true) {
@ -415,7 +418,7 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
size_t j = 0; size_t j = 0;
for (size_t f = size_t(cereal::PandaState::FaultType::RELAY_MALFUNCTION); for (size_t f = size_t(cereal::PandaState::FaultType::RELAY_MALFUNCTION);
f <= size_t(cereal::PandaState::FaultType::INTERRUPT_RATE_EXTI); f++) { f <= size_t(cereal::PandaState::FaultType::SIREN_MALFUNCTION); f++) {
if (fault_bits.test(f)) { if (fault_bits.test(f)) {
faults.set(j, cereal::PandaState::FaultType(f)); faults.set(j, cereal::PandaState::FaultType(f));
j++; j++;

@ -203,7 +203,7 @@ void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data
assert(can_data.size() <= 64); assert(can_data.size() <= 64);
assert(can_data.size() == dlc_to_len[data_len_code]); assert(can_data.size() == dlc_to_len[data_len_code]);
can_header header; can_header header = {};
header.addr = cmsg.getAddress(); header.addr = cmsg.getAddress();
header.extended = (cmsg.getAddress() >= 0x800) ? 1 : 0; header.extended = (cmsg.getAddress() >= 0x800) ? 1 : 0;
header.data_len_code = data_len_code; header.data_len_code = data_len_code;

@ -51,7 +51,7 @@ class TestBoardd(unittest.TestCase):
cp.safetyConfigs = [safety_config]*num_pandas cp.safetyConfigs = [safety_config]*num_pandas
params = Params() params = Params()
params.put_bool("FirmwareObdQueryDone", True) params.put_bool("FirmwareQueryDone", True)
params.put_bool("ControlsReady", True) params.put_bool("ControlsReady", True)
params.put("CarParams", cp.to_bytes()) params.put("CarParams", cp.to_bytes())

@ -8,7 +8,7 @@ from system.version import is_comma_remote, is_tested_branch
from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import disable_obd_multiplexing, get_fw_versions_ordered, match_fw_to_car, get_present_ecus from selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
from system.swaglog import cloudlog from system.swaglog import cloudlog
import cereal.messaging as messaging import cereal.messaging as messaging
from selfdrive.car import gen_empty_fingerprint from selfdrive.car import gen_empty_fingerprint
@ -80,12 +80,13 @@ def fingerprint(logcan, sendcan, num_pandas):
fixed_fingerprint = os.environ.get('FINGERPRINT', "") fixed_fingerprint = os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
ecu_rx_addrs = set() ecu_rx_addrs = set()
params = Params()
if not skip_fw_query: if not skip_fw_query:
# Vin query only reliably works through OBDII # Vin query only reliably works through OBDII
bus = 1 bus = 1
cached_params = Params().get("CarParamsCache") cached_params = params.get("CarParamsCache")
if cached_params is not None: if cached_params is not None:
cached_params = car.CarParams.from_bytes(cached_params) cached_params = car.CarParams.from_bytes(cached_params)
if cached_params.carName == "mock": if cached_params.carName == "mock":
@ -98,6 +99,7 @@ def fingerprint(logcan, sendcan, num_pandas):
cached = True cached = True
else: else:
cloudlog.warning("Getting VIN & FW versions") cloudlog.warning("Getting VIN & FW versions")
set_obd_multiplexing(params, True)
vin_rx_addr, vin = get_vin(logcan, sendcan, bus) vin_rx_addr, vin = get_vin(logcan, sendcan, bus)
ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas) ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas)
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas) car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
@ -113,10 +115,11 @@ def fingerprint(logcan, sendcan, num_pandas):
cloudlog.event("Malformed VIN", vin=vin, error=True) cloudlog.event("Malformed VIN", vin=vin, error=True)
vin = VIN_UNKNOWN vin = VIN_UNKNOWN
cloudlog.warning("VIN %s", vin) cloudlog.warning("VIN %s", vin)
params = Params()
params.put("CarVin", vin) params.put("CarVin", vin)
disable_obd_multiplexing(params)
# disable OBD multiplexing for potential ECU knockouts
set_obd_multiplexing(params, False)
params.put_bool("FirmwareQueryDone", True)
finger = gen_empty_fingerprint() finger = gen_empty_fingerprint()
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1

@ -50,6 +50,7 @@ class CarControllerParams:
self.STEER_DELTA_DOWN = 3 self.STEER_DELTA_DOWN = 3
self.STEER_MAX = 261 # higher than this faults the EPS self.STEER_MAX = 261 # higher than this faults the EPS
STEER_THRESHOLD = 120 STEER_THRESHOLD = 120
RAM_DT = {CAR.RAM_1500, } RAM_DT = {CAR.RAM_1500, }
@ -62,6 +63,7 @@ class ChryslerCarInfo(CarInfo):
package: str = "Adaptive Cruise Control (ACC)" package: str = "Adaptive Cruise Control (ACC)"
harness: Enum = Harness.fca harness: Enum = Harness.fca
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"), CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"),
CAR.PACIFICA_2018_HYBRID: None, # same platforms CAR.PACIFICA_2018_HYBRID: None, # same platforms
@ -73,10 +75,10 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
], ],
CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-22", harness=Harness.ram), CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-23", harness=Harness.ram),
CAR.RAM_HD: [ CAR.RAM_HD: [
ChryslerCarInfo("Ram 2500 2020-22", harness=Harness.ram), ChryslerCarInfo("Ram 2500 2020-22", harness=Harness.ram),
ChryslerCarInfo("Ram 3500 2020-22", harness=Harness.ram), ChryslerCarInfo("Ram 3500 2019-22", harness=Harness.ram),
], ],
} }
@ -180,79 +182,153 @@ FW_QUERY_CONFIG = FwQueryConfig(
) )
FW_VERSIONS = { FW_VERSIONS = {
CAR.JEEP_CHEROKEE_2019: {
(Ecu.combinationMeter, 0x742, None): [
b'68402971AD',
],
(Ecu.srs, 0x744, None): [
b'68355363AB',
],
(Ecu.abs, 0x747, None): [
b'68408639AD',
],
(Ecu.fwdRadar, 0x753, None): [
b'68456722AC',
],
(Ecu.eps, 0x75A, None): [
b'68453431AA',
],
(Ecu.engine, 0x7e0, None): [
b'05035674AB ',
],
(Ecu.transmission, 0x7e1, None): [
b'05035707AA',
],
},
CAR.RAM_1500: { CAR.RAM_1500: {
(Ecu.combinationMeter, 0x742, None): [ (Ecu.combinationMeter, 0x742, None): [
b'68294063AH', b'68294051AG',
b'68294051AI',
b'68294052AG',
b'68294063AG', b'68294063AG',
b'68294063AH',
b'68294063AI',
b'68434846AC',
b'68434858AC',
b'68434860AC', b'68434860AC',
b'68527375AD',
b'68453503AC', b'68453503AC',
b'68453505AC',
b'68453511AC',
b'68453513AD',
b'68453514AD',
b'68510283AG',
b'68527375AD',
], ],
(Ecu.srs, 0x744, None): [ (Ecu.srs, 0x744, None): [
b'68428609AB',
b'68441329AB', b'68441329AB',
b'68473844AB',
b'68490898AA', b'68490898AA',
b'68428609AB',
b'68500728AA', b'68500728AA',
b'68615033AA',
], ],
(Ecu.abs, 0x747, None): [ (Ecu.abs, 0x747, None): [
b'68432418AD', b'68292406AH',
b'68432418AB', b'68432418AB',
b'68432418AD',
b'68436004AD',
b'68436004AE', b'68436004AE',
b'68438454AC',
b'68438454AD', b'68438454AD',
b'68436004AD', b'68438456AE',
b'68438456AF',
b'68535469AB', b'68535469AB',
b'68438454AC', b'68535470AC',
b'68586307AB',
], ],
(Ecu.fwdRadar, 0x753, None): [ (Ecu.fwdRadar, 0x753, None): [
b'68320950AL', b'04672892AB',
b'04672932AB',
b'68320950AH',
b'68320950AI',
b'68320950AJ', b'68320950AJ',
b'68320950AL',
b'68320950AM',
b'68454268AB', b'68454268AB',
b'68475160AG',
b'04672892AB',
b'68475160AE', b'68475160AE',
b'68475160AF',
b'68475160AG',
], ],
(Ecu.eps, 0x75A, None): [ (Ecu.eps, 0x75A, None): [
b'68273275AF',
b'68273275AG', b'68273275AG',
b'68312176AE',
b'68312176AG',
b'68440789AC',
b'68466110AB',
b'68469901AA', b'68469901AA',
b'68522583AB',
b'68522585AB',
b'68552788AA', b'68552788AA',
b'68552790AA',
b'68585112AB',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'05036065AE ',
b'05036066AE ',
b'68378701AI ',
b'68378758AM ',
b'68448163AJ', b'68448163AJ',
b'68448165AK',
b'68500630AD', b'68500630AD',
b'68500630AE',
b'68539650AD', b'68539650AD',
b'68378758AM ',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'68360078AL', b'68360078AL',
b'68384328AD', b'68360080AM',
b'68360085AL',
b'68360081AM', b'68360081AM',
b'68502994AD', b'68360085AL',
b'68384328AD',
b'68384332AD',
b'68445533AB', b'68445533AB',
b'68540431AB',
b'68484467AC', b'68484467AC',
b'68502994AD',
b'68540431AB',
], ],
}, },
CAR.RAM_HD: { CAR.RAM_HD: {
(Ecu.combinationMeter, 0x742, None): [ (Ecu.combinationMeter, 0x742, None): [
b'68361606AH', b'68361606AH',
b'68437735AC',
b'68492693AD', b'68492693AD',
b'68525485AB',
b'68525487AB',
b'68525498AB',
], ],
(Ecu.srs, 0x744, None): [ (Ecu.srs, 0x744, None): [
b'68399794AC', b'68399794AC',
b'68428503AA', b'68428503AA',
b'68428505AA', b'68428505AA',
b'68428507AA',
], ],
(Ecu.abs, 0x747, None): [ (Ecu.abs, 0x747, None): [
b'68334977AH', b'68334977AH',
b'68455481AC',
b'68504022AA',
b'68504022AB', b'68504022AB',
b'68530686AB',
b'68504022AC', b'68504022AC',
b'68530686AB',
b'68530686AC',
], ],
(Ecu.fwdRadar, 0x753, None): [ (Ecu.fwdRadar, 0x753, None): [
b'04672895AB', b'04672895AB',
b'56029827AG', b'56029827AG',
b'56029827AH',
b'68462657AE',
b'68484694AD',
b'68484694AE', b'68484694AE',
], ],
(Ecu.eps, 0x761, None): [ (Ecu.eps, 0x761, None): [
@ -260,7 +336,13 @@ FW_VERSIONS = {
b'68507906AB', b'68507906AB',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'52370131AF',
b'52370231AF',
b'52370231AG',
b'52370931CT',
b'52401032AE',
b'52421132AF', b'52421132AF',
b'68527616AD ',
b'M2370131MB', b'M2370131MB',
b'M2421132MB', b'M2421132MB',
], ],

@ -9,6 +9,8 @@ from selfdrive.car import make_can_msg
from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.boardd.boardd import can_list_to_can_capnp
from system.swaglog import cloudlog from system.swaglog import cloudlog
EcuAddrBusType = Tuple[int, Optional[int], int]
def make_tester_present_msg(addr, bus, subaddr=None): def make_tester_present_msg(addr, bus, subaddr=None):
dat = [0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0] dat = [0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0]
@ -33,16 +35,16 @@ def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subadd
return False return False
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[Tuple[int, Optional[int], int]]: def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[EcuAddrBusType]:
addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)] addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)]
queries: Set[Tuple[int, Optional[int], int]] = {(addr, None, bus) for addr in addr_list} queries: Set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list}
responses = queries responses = queries
return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug) return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug)
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[Tuple[int, Optional[int], int]], def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[EcuAddrBusType],
responses: Set[Tuple[int, Optional[int], int]], timeout: float = 1, debug: bool = False) -> Set[Tuple[int, Optional[int], int]]: responses: Set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> Set[EcuAddrBusType]:
ecu_responses: Set[Tuple[int, Optional[int], int]] = set() # set((addr, subaddr, bus),) ecu_responses: Set[EcuAddrBusType] = set() # set((addr, subaddr, bus),)
try: try:
msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries] msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries]

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import capnp import capnp
import copy
from dataclasses import dataclass, field from dataclasses import dataclass, field
import struct import struct
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
@ -57,10 +58,12 @@ class Request:
whitelist_ecus: List[int] = field(default_factory=list) whitelist_ecus: List[int] = field(default_factory=list)
rx_offset: int = 0x8 rx_offset: int = 0x8
bus: int = 1 bus: int = 1
# Whether this query should be run on the first auxiliary panda (CAN FD cars for example)
auxiliary: bool = False
# FW responses from these queries will not be used for fingerprinting # FW responses from these queries will not be used for fingerprinting
logging: bool = False logging: bool = False
# These requests are done once OBD multiplexing is disabled, after all others # boardd toggles OBD multiplexing on/off as needed
non_obd: bool = False obd_multiplexing: bool = True
@dataclass @dataclass
@ -71,3 +74,10 @@ class FwQueryConfig:
non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict) non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict)
# Ecus added for data collection, not to be fingerprinted on # Ecus added for data collection, not to be fingerprinted on
extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list) extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list)
def __post_init__(self):
for i in range(len(self.requests)):
if self.requests[i].auxiliary:
new_request = copy.deepcopy(self.requests[i])
new_request.bus += 4
self.requests.append(new_request)

@ -1,12 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import defaultdict from collections import defaultdict
from typing import Any, Optional, Set, Tuple from typing import Any, Dict, List, Set
from tqdm import tqdm from tqdm import tqdm
import panda.python.uds as uds import panda.python.uds as uds
from cereal import car from cereal import car
from common.params import Params from common.params import Params
from selfdrive.car.ecu_addrs import get_ecu_addrs from selfdrive.car.ecu_addrs import EcuAddrBusType, get_ecu_addrs
from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import FW_VERSIONS from selfdrive.car.fingerprints import FW_VERSIONS
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
@ -19,7 +19,7 @@ FW_QUERY_CONFIGS = get_interface_attr('FW_QUERY_CONFIG', ignore_none=True)
VERSIONS = get_interface_attr('FW_VERSIONS', ignore_none=True) VERSIONS = get_interface_attr('FW_VERSIONS', ignore_none=True)
MODEL_TO_BRAND = {c: b for b, e in VERSIONS.items() for c in e} MODEL_TO_BRAND = {c: b for b, e in VERSIONS.items() for c in e}
REQUESTS = [(brand, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests] REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests]
def chunks(l, n=128): def chunks(l, n=128):
@ -39,6 +39,8 @@ def build_fw_dict(fw_versions, filter_brand=None):
def get_brand_addrs(): def get_brand_addrs():
brand_addrs = defaultdict(set) brand_addrs = defaultdict(set)
for brand, cars in VERSIONS.items(): for brand, cars in VERSIONS.items():
# Add ecus in database + extra ecus to match against
brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in FW_QUERY_CONFIGS[brand].extra_ecus}
for fw in cars.values(): for fw in cars.values():
brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()} brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()}
return brand_addrs return brand_addrs
@ -146,38 +148,43 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True):
return True, set() return True, set()
def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[Tuple[int, Optional[int], int]]: def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]:
queries = list() params = Params()
parallel_queries = list() # queries are split by OBD multiplexing mode
queries: Dict[bool, List[List[EcuAddrBusType]]] = {True: [], False: []}
parallel_queries: Dict[bool, List[EcuAddrBusType]] = {True: [], False: []}
responses = set() responses = set()
for brand, r in REQUESTS: for brand, config, r in REQUESTS:
# Skip query if no panda available # Skip query if no panda available
if r.bus > num_pandas * 4 - 1: if r.bus > num_pandas * 4 - 1:
continue continue
for brand_versions in VERSIONS[brand].values(): for brand_versions in VERSIONS[brand].values():
for ecu_type, addr, sub_addr in brand_versions: for ecu_type, addr, sub_addr in list(brand_versions) + config.extra_ecus:
# Only query ecus in whitelist if whitelist is not empty # Only query ecus in whitelist if whitelist is not empty
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus: if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
a = (addr, sub_addr, r.bus) a = (addr, sub_addr, r.bus)
# Build set of queries # Build set of queries
if sub_addr is None: if sub_addr is None:
if a not in parallel_queries: if a not in parallel_queries[r.obd_multiplexing]:
parallel_queries.append(a) parallel_queries[r.obd_multiplexing].append(a)
else: # subaddresses must be queried one by one else: # subaddresses must be queried one by one
if [a] not in queries: if [a] not in queries[r.obd_multiplexing]:
queries.append([a]) queries[r.obd_multiplexing].append([a])
# Build set of expected responses to filter # Build set of expected responses to filter
response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset) response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset)
responses.add((response_addr, sub_addr, r.bus)) responses.add((response_addr, sub_addr, r.bus))
queries.insert(0, parallel_queries) for obd_multiplexing in queries:
queries[obd_multiplexing].insert(0, parallel_queries[obd_multiplexing])
ecu_responses = set() ecu_responses = set()
for query in queries: for obd_multiplexing in queries:
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1)) set_obd_multiplexing(params, obd_multiplexing)
for query in queries[obd_multiplexing]:
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1))
return ecu_responses return ecu_responses
@ -185,9 +192,9 @@ def get_brand_ecu_matches(ecu_rx_addrs):
"""Returns dictionary of brands and matches with ECUs in their FW versions""" """Returns dictionary of brands and matches with ECUs in their FW versions"""
brand_addrs = get_brand_addrs() brand_addrs = get_brand_addrs()
brand_matches = {brand: set() for brand, _ in REQUESTS} brand_matches = {brand: set() for brand, _, _ in REQUESTS}
brand_rx_offsets = set((brand, r.rx_offset) for brand, r in REQUESTS) brand_rx_offsets = set((brand, r.rx_offset) for brand, _, r in REQUESTS)
for addr, sub_addr, _ in ecu_rx_addrs: for addr, sub_addr, _ in ecu_rx_addrs:
# Since we can't know what request an ecu responded to, add matches for all possible rx offsets # Since we can't know what request an ecu responded to, add matches for all possible rx offsets
for brand, rx_offset in brand_rx_offsets: for brand, rx_offset in brand_rx_offsets:
@ -198,13 +205,13 @@ def get_brand_ecu_matches(ecu_rx_addrs):
return brand_matches return brand_matches
def disable_obd_multiplexing(params): def set_obd_multiplexing(params: Params, obd_multiplexing: bool):
if not params.get_bool("ObdMultiplexingDisabled"): if params.get_bool("ObdMultiplexingEnabled") != obd_multiplexing:
params.put_bool("FirmwareObdQueryDone", True) cloudlog.warning(f"Setting OBD multiplexing to {obd_multiplexing}")
params.remove("ObdMultiplexingChanged")
cloudlog.warning("Waiting for OBD multiplexing to be disabled") params.put_bool("ObdMultiplexingEnabled", obd_multiplexing)
params.get_bool("ObdMultiplexingDisabled", block=True) params.get_bool("ObdMultiplexingChanged", block=True)
cloudlog.warning("OBD multiplexing disabled") cloudlog.warning("OBD multiplexing set successfully")
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False): def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False):
@ -212,7 +219,6 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
all_car_fw = [] all_car_fw = []
brand_matches = get_brand_ecu_matches(ecu_rx_addrs) brand_matches = get_brand_ecu_matches(ecu_rx_addrs)
matched_brand: Optional[str] = None
for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True): for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True):
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress) car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress)
@ -220,21 +226,14 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
# Try to match using FW returned from this brand only # Try to match using FW returned from this brand only
matches = match_fw_to_car_exact(build_fw_dict(car_fw)) matches = match_fw_to_car_exact(build_fw_dict(car_fw))
if len(matches) == 1: if len(matches) == 1:
matched_brand = brand
break break
disable_obd_multiplexing(Params())
# Do non-OBD queries for matched brand, or all if no match is found
for brand in FW_QUERY_CONFIGS.keys():
if brand == matched_brand or matched_brand is None:
all_car_fw.extend(get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, obd_multiplexed=False, debug=debug, progress=progress))
return all_car_fw return all_car_fw
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, obd_multiplexed=True, debug=False, progress=False): def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False):
versions = VERSIONS.copy() versions = VERSIONS.copy()
params = Params()
# Each brand can define extra ECUs to query for data collection # Each brand can define extra ECUs to query for data collection
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():
@ -250,19 +249,15 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
# ECUs using a subaddress need be queried one by one, the rest can be done in parallel # ECUs using a subaddress need be queried one by one, the rest can be done in parallel
addrs = [] addrs = []
parallel_addrs = [] parallel_addrs = []
logging_addrs = []
ecu_types = {} ecu_types = {}
for brand, brand_versions in versions.items(): for brand, brand_versions in versions.items():
for candidate, ecu in brand_versions.items(): for ecu in brand_versions.values():
for ecu_type, addr, sub_addr in ecu.keys(): for ecu_type, addr, sub_addr in ecu.keys():
a = (brand, addr, sub_addr) a = (brand, addr, sub_addr)
if a not in ecu_types: if a not in ecu_types:
ecu_types[a] = ecu_type ecu_types[a] = ecu_type
if a not in logging_addrs and candidate == "debug":
logging_addrs.append(a)
if sub_addr is None: if sub_addr is None:
if a not in parallel_addrs: if a not in parallel_addrs:
parallel_addrs.append(a) parallel_addrs.append(a)
@ -274,16 +269,17 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
# Get versions and build capnp list to put into CarParams # Get versions and build capnp list to put into CarParams
car_fw = [] car_fw = []
requests = [(brand, r) for brand, r in REQUESTS if query_brand is None or brand == query_brand] requests = [(brand, config, r) for brand, config, r in REQUESTS if query_brand is None or brand == query_brand]
for addr in tqdm(addrs, disable=not progress): for addr in tqdm(addrs, disable=not progress):
for addr_chunk in chunks(addr): for addr_chunk in chunks(addr):
for brand, r in requests: for brand, config, r in requests:
# Skip query if no panda available # Skip query if no panda available
if r.bus > num_pandas * 4 - 1: if r.bus > num_pandas * 4 - 1:
continue continue
# Or if request is not designated for current multiplexing mode
elif r.non_obd == obd_multiplexed: # Toggle OBD multiplexing for each request
continue if r.bus % 4 == 1:
set_obd_multiplexing(params, r.obd_multiplexing)
try: try:
addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and
@ -294,15 +290,15 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
for (tx_addr, sub_addr), version in query.get_data(timeout).items(): for (tx_addr, sub_addr), version in query.get_data(timeout).items():
f = car.CarParams.CarFw.new_message() f = car.CarParams.CarFw.new_message()
ecu_key = (brand, tx_addr, sub_addr) f.ecu = ecu_types.get((brand, tx_addr, sub_addr), Ecu.unknown)
f.ecu = ecu_types.get(ecu_key, Ecu.unknown)
f.fwVersion = version f.fwVersion = version
f.address = tx_addr f.address = tx_addr
f.responseAddress = uds.get_rx_addr_for_tx_addr(tx_addr, r.rx_offset) f.responseAddress = uds.get_rx_addr_for_tx_addr(tx_addr, r.rx_offset)
f.request = r.request f.request = r.request
f.brand = brand f.brand = brand
f.bus = r.bus f.bus = r.bus
f.logging = r.logging or ecu_key in logging_addrs f.logging = r.logging or (f.ecu, tx_addr, sub_addr) in config.extra_ecus
f.obdMultiplexing = r.obd_multiplexing
if sub_addr is not None: if sub_addr is not None:
f.subAddress = sub_addr f.subAddress = sub_addr

@ -1,13 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from math import fabs from math import fabs, exp
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config
from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD
from selfdrive.controls.lib.drive_helpers import get_friction
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -44,6 +45,29 @@ class CarInterface(CarInterfaceBase):
else: else:
return CarInterfaceBase.get_steer_feedforward_default return CarInterfaceBase.get_steer_feedforward_default
@staticmethod
def torque_from_lateral_accel_bolt(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
def sig(val):
return 1 / (1 + exp(-val)) - 0.5
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
# This has big effect on the stability about 0 (noise when going straight)
# ToDo: To generalize to other GMs, explore tanh function as the nonlinear
a, b, c, _ = [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178] # weights computed offline
steer_torque = (sig(lateral_accel_value * a) * b) + (lateral_accel_value * c)
return float(steer_torque) + friction
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
if self.CP.carFingerprint == CAR.BOLT_EUV:
return self.torque_from_lateral_accel_bolt
else:
return self.torque_from_lateral_accel_linear
@staticmethod @staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret.carName = "gm" ret.carName = "gm"
@ -78,7 +102,6 @@ class CarInterface(CarInterfaceBase):
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
ret.vEgoStopping = 0.25 ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25 ret.vEgoStarting = 0.25
ret.longitudinalActuatorDelayUpperBound = 0.5
if experimental_long: if experimental_long:
ret.pcmCruise = False ret.pcmCruise = False
@ -113,6 +136,7 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
if candidate == CAR.VOLT: if candidate == CAR.VOLT:
ret.mass = 1607. + STD_CARGO_KG ret.mass = 1607. + STD_CARGO_KG
@ -148,7 +172,13 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.4 # end to end is 13.46 ret.steerRatio = 14.4 # end to end is 13.46
ret.centerToFront = ret.wheelbase * 0.4 ret.centerToFront = ret.wheelbase * 0.4
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia() ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia()
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
elif candidate == CAR.BUICK_LACROSSE:
ret.mass = 1712. + STD_CARGO_KG
ret.wheelbase = 2.91
ret.steerRatio = 15.8
ret.centerToFront = ret.wheelbase * 0.4 # wild guess
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BUICK_REGAL: elif candidate == CAR.BUICK_REGAL:
ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2 ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2
@ -168,7 +198,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.95 # 116 inches in meters ret.wheelbase = 2.95 # 116 inches in meters
ret.steerRatio = 17.3 ret.steerRatio = 17.3
ret.centerToFront = ret.wheelbase * 0.5 ret.centerToFront = ret.wheelbase * 0.5
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ESCALADE_ESV: elif candidate == CAR.ESCALADE_ESV:

@ -14,8 +14,8 @@ class CarControllerParams:
STEER_STEP = 3 # Active control frames per command (~33hz) STEER_STEP = 3 # Active control frames per command (~33hz)
INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz) INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz)
STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness
STEER_DELTA_DOWN = 25 STEER_DELTA_DOWN = 15
STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_ALLOWANCE = 65
STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_MULTIPLIER = 4
STEER_DRIVER_FACTOR = 100 STEER_DRIVER_FACTOR = 100
NEAR_STOP_BRAKE_PHASE = 0.5 # m/s NEAR_STOP_BRAKE_PHASE = 0.5 # m/s
@ -66,6 +66,7 @@ class CAR:
CADILLAC_ATS = "CADILLAC ATS Premium Performance 2018" CADILLAC_ATS = "CADILLAC ATS Premium Performance 2018"
MALIBU = "CHEVROLET MALIBU PREMIER 2017" MALIBU = "CHEVROLET MALIBU PREMIER 2017"
ACADIA = "GMC ACADIA DENALI 2018" ACADIA = "GMC ACADIA DENALI 2018"
BUICK_LACROSSE = "BUICK LACROSSE 2017"
BUICK_REGAL = "BUICK REGAL ESSENCE 2018" BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE = "CADILLAC ESCALADE 2017" ESCALADE = "CADILLAC ESCALADE 2017"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
@ -99,6 +100,7 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"), CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"),
CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"), CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"),
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
CAR.BUICK_LACROSSE: GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"),
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"),
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
@ -156,6 +158,11 @@ FINGERPRINTS = {
{ {
170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 8, 381: 2, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 5, 844: 8, 853: 8, 866: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 8, 381: 2, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 5, 844: 8, 853: 8, 866: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7
}], }],
CAR.BUICK_LACROSSE: [
# LaCrosse Premium AWD 2017
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
}],
CAR.BUICK_REGAL : [ CAR.BUICK_REGAL : [
# Regal TourX Essence w/ ACC 2018 # Regal TourX Essence w/ ACC 2018
{ {

@ -1,7 +1,6 @@
from collections import namedtuple from collections import namedtuple
from cereal import car from cereal import car
from common.conversions import Conversions as CV
from common.numpy_fast import clip, interp from common.numpy_fast import clip, 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
@ -117,6 +116,7 @@ class CarController:
self.brake_last = 0. self.brake_last = 0.
self.apply_brake_last = 0 self.apply_brake_last = 0
self.last_pump_ts = 0. self.last_pump_ts = 0.
self.stopping_counter = 0
self.accel = 0.0 self.accel = 0.0
self.speed = 0.0 self.speed = 0.0
@ -127,7 +127,8 @@ class CarController:
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
actuators = CC.actuators actuators = CC.actuators
hud_control = CC.hudControl hud_control = CC.hudControl
hud_v_cruise = hud_control.setSpeed * CV.MS_TO_KPH if hud_control.speedVisible else 255 conversion = hondacan.get_cruise_speed_conversion(self.CP.carFingerprint, CS.is_metric)
hud_v_cruise = hud_control.setSpeed / conversion if hud_control.speedVisible else 255
pcm_cancel_cmd = CC.cruiseControl.cancel pcm_cancel_cmd = CC.cruiseControl.cancel
if CC.longActive: if CC.longActive:
@ -161,7 +162,7 @@ class CarController:
can_sends = [] can_sends = []
# tester present - w/ no response (keeps radar disabled) # tester present - w/ no response (keeps radar disabled)
if self.CP.carFingerprint in HONDA_BOSCH and self.CP.openpilotLongitudinalControl: if self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and self.CP.openpilotLongitudinalControl:
if self.frame % 10 == 0: if self.frame % 10 == 0:
can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1)) can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1))
@ -217,8 +218,9 @@ class CarController:
self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V) self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
stopping = actuators.longControlState == LongCtrlState.stopping stopping = actuators.longControlState == LongCtrlState.stopping
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas, can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas,
stopping, self.CP.carFingerprint)) self.stopping_counter, self.CP.carFingerprint))
else: else:
apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0) apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0)
apply_brake = int(clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1)) apply_brake = int(clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))

@ -5,7 +5,7 @@ from common.conversions import Conversions as CV
from common.numpy_fast import interp from common.numpy_fast import interp
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.honda.hondacan import get_pt_bus from selfdrive.car.honda.hondacan import get_cruise_speed_conversion, get_pt_bus
from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
@ -246,8 +246,7 @@ class CarState(CarStateBase):
ret.cruiseState.nonAdaptive = acc_hud["CRUISE_CONTROL_LABEL"] != 0 ret.cruiseState.nonAdaptive = acc_hud["CRUISE_CONTROL_LABEL"] != 0
ret.cruiseState.standstill = acc_hud["CRUISE_SPEED"] == 252. ret.cruiseState.standstill = acc_hud["CRUISE_SPEED"] == 252.
# on certain cars, CRUISE_SPEED changes to imperial with car's unit setting conversion = get_cruise_speed_conversion(self.CP.carFingerprint, self.is_metric)
conversion = CV.MPH_TO_MS if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS and not self.is_metric else CV.KPH_TO_MS
# On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this. # On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this.
ret.cruiseState.speed = self.v_cruise_pcm_prev if acc_hud["CRUISE_SPEED"] > 160.0 else acc_hud["CRUISE_SPEED"] * conversion ret.cruiseState.speed = self.v_cruise_pcm_prev if acc_hud["CRUISE_SPEED"] > 160.0 else acc_hud["CRUISE_SPEED"] * conversion
self.v_cruise_pcm_prev = ret.cruiseState.speed self.v_cruise_pcm_prev = ret.cruiseState.speed

@ -21,6 +21,11 @@ def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False):
return 0 return 0
def get_cruise_speed_conversion(car_fingerprint: str, is_metric: bool) -> float:
# on certain cars, CRUISE_SPEED changes to imperial with car's unit setting
return CV.MPH_TO_MS if car_fingerprint in HONDA_BOSCH_RADARLESS and not is_metric else CV.KPH_TO_MS
def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake): def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake):
# TODO: do we loose pressure if we keep pump off for long? # TODO: do we loose pressure if we keep pump off for long?
brakelights = apply_brake > 0 brakelights = apply_brake > 0
@ -46,7 +51,7 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_
return packer.make_can_msg("BRAKE_COMMAND", bus, values) return packer.make_can_msg("BRAKE_COMMAND", bus, values)
def create_acc_commands(packer, enabled, active, accel, gas, stopping, car_fingerprint): def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, car_fingerprint):
commands = [] commands = []
bus = get_pt_bus(car_fingerprint) bus = get_pt_bus(car_fingerprint)
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0] min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
@ -55,30 +60,39 @@ def create_acc_commands(packer, enabled, active, accel, gas, stopping, car_finge
gas_command = gas if active and accel > min_gas_accel else -30000 gas_command = gas if active and accel > min_gas_accel else -30000
accel_command = accel if active else 0 accel_command = accel if active else 0
braking = 1 if active and accel < min_gas_accel else 0 braking = 1 if active and accel < min_gas_accel else 0
standstill = 1 if active and stopping else 0 standstill = 1 if active and stopping_counter > 0 else 0
standstill_release = 1 if active and not stopping else 0 standstill_release = 1 if active and stopping_counter == 0 else 0
# common ACC_CONTROL values
acc_control_values = { acc_control_values = {
# setting CONTROL_ON causes car to set POWERTRAIN_DATA->ACC_STATUS = 1 'ACCEL_COMMAND': accel_command,
"CONTROL_ON": control_on, 'STANDSTILL': standstill,
"GAS_COMMAND": gas_command, # used for gas
"ACCEL_COMMAND": accel_command, # used for brakes
"BRAKE_LIGHTS": braking,
"BRAKE_REQUEST": braking,
"STANDSTILL": standstill,
"STANDSTILL_RELEASE": standstill_release,
} }
commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values))
acc_control_on_values = { if car_fingerprint in HONDA_BOSCH_RADARLESS:
"SET_TO_3": 0x03, acc_control_values.update({
"CONTROL_ON": enabled, "CONTROL_ON": enabled,
"SET_TO_FF": 0xff, "IDLESTOP_ALLOW": stopping_counter > 200, # allow idle stop after 4 seconds (50 Hz)
"SET_TO_75": 0x75, })
"SET_TO_30": 0x30, else:
} acc_control_values.update({
commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values)) # setting CONTROL_ON causes car to set POWERTRAIN_DATA->ACC_STATUS = 1
"CONTROL_ON": control_on,
"GAS_COMMAND": gas_command, # used for gas
"BRAKE_LIGHTS": braking,
"BRAKE_REQUEST": braking,
"STANDSTILL_RELEASE": standstill_release,
})
acc_control_on_values = {
"SET_TO_3": 0x03,
"CONTROL_ON": enabled,
"SET_TO_FF": 0xff,
"SET_TO_75": 0x75,
"SET_TO_30": 0x30,
}
commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values))
commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values))
return commands return commands

@ -37,13 +37,11 @@ class CarInterface(CarInterfaceBase):
if candidate in HONDA_BOSCH: if candidate in HONDA_BOSCH:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)]
ret.radarUnavailable = True ret.radarUnavailable = True
# Disable the radar and let openpilot control longitudinal
if candidate not in HONDA_BOSCH_RADARLESS: # WARNING: THIS DISABLES AEB!
# Disable the radar and let openpilot control longitudinal # If Bosch radarless, this blocks ACC messages from the camera
# WARNING: THIS DISABLES AEB! ret.experimentalLongitudinalAvailable = True
ret.experimentalLongitudinalAvailable = True ret.openpilotLongitudinalControl = experimental_long
ret.openpilotLongitudinalControl = experimental_long
ret.pcmCruise = not ret.openpilotLongitudinalControl ret.pcmCruise = not ret.openpilotLongitudinalControl
else: else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaNidec)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaNidec)]
@ -75,6 +73,8 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalTuning.kpV = [0.25] ret.longitudinalTuning.kpV = [0.25]
ret.longitudinalTuning.kiV = [0.05] ret.longitudinalTuning.kiV = [0.05]
ret.longitudinalActuatorDelayUpperBound = 0.5 # s ret.longitudinalActuatorDelayUpperBound = 0.5 # s
if candidate in HONDA_BOSCH_RADARLESS:
ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
else: else:
# default longitudinal tuning for all hondas # default longitudinal tuning for all hondas
ret.longitudinalTuning.kpBP = [0., 5., 35.] ret.longitudinalTuning.kpBP = [0., 5., 35.]

@ -186,7 +186,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
[StdQueries.UDS_VERSION_RESPONSE], [StdQueries.UDS_VERSION_RESPONSE],
bus=1, bus=1,
logging=True, logging=True,
non_obd=True, obd_multiplexing=False,
), ),
], ],
extra_ecus=[ extra_ecus=[

@ -224,7 +224,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e),
], ],
CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2022-23", harness=Harness.hyundai_k), CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", harness=Harness.hyundai_k),
CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", harness=Harness.hyundai_a), CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", harness=Harness.hyundai_a),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n),
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c),
@ -335,10 +335,15 @@ FINGERPRINTS = {
HYUNDAI_VERSION_REQUEST_LONG = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ HYUNDAI_VERSION_REQUEST_LONG = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf100) # Long description p16(0xf100) # Long description
HYUNDAI_VERSION_REQUEST_ALT = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf110) # Alt long description
HYUNDAI_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ HYUNDAI_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \ p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + \ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + \
p16(0xf100) p16(0xf100)
HYUNDAI_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) HYUNDAI_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
FW_QUERY_CONFIG = FwQueryConfig( FW_QUERY_CONFIG = FwQueryConfig(
@ -355,22 +360,46 @@ FW_QUERY_CONFIG = FwQueryConfig(
[HYUNDAI_VERSION_RESPONSE], [HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar], whitelist_ecus=[Ecu.engine, Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar],
), ),
# CAN-FD queries (camera)
# CAN-FD queries (from camera)
# TODO: combine shared whitelists with CAN requests
Request( Request(
[HYUNDAI_VERSION_REQUEST_LONG], [HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE], [HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar], whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac],
bus=4, bus=0,
auxiliary=True,
), ),
Request( Request(
[HYUNDAI_VERSION_REQUEST_LONG], [HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE], [HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.adas, Ecu.cornerRadar], whitelist_ecus=[Ecu.fwdCamera, Ecu.adas, Ecu.cornerRadar, Ecu.hvac],
bus=5, bus=1,
auxiliary=True,
obd_multiplexing=False,
),
# CAN-FD debugging queries
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.parkingAdas],
bus=0,
auxiliary=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.parkingAdas],
bus=1,
auxiliary=True,
obd_multiplexing=False,
), ),
], ],
extra_ecus=[ extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms (Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms
(Ecu.parkingAdas, 0x7b1, None), # ADAS Parking ECU (may exist on all platforms)
(Ecu.hvac, 0x7b3, None), # HVAC Control Assembly
(Ecu.cornerRadar, 0x7b7, None), (Ecu.cornerRadar, 0x7b7, None),
], ],
) )
@ -1025,11 +1054,23 @@ FW_VERSIONS = {
], ],
}, },
CAR.GENESIS_G70: { CAR.GENESIS_G70: {
(Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x00IK__ SCC F-CUP 1.00 1.02 96400-G9100 ', ], (Ecu.fwdRadar, 0x7d0, None): [
(Ecu.engine, 0x7e0, None): [b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00', ], b'\xf1\x00IK__ SCC F-CUP 1.00 1.02 96400-G9100 ',
(Ecu.eps, 0x7d4, None): [b'\xf1\x00IK MDPS R 1.00 1.06 57700-G9420 4I4VL106', ], b'\xf1\x00IK__ SCC F-CUP 1.00 1.01 96400-G9100 ',
(Ecu.fwdCamera, 0x7c4, None): [b'\xf1\x00IK MFC AT USA LHD 1.00 1.01 95740-G9000 170920', ], ],
(Ecu.transmission, 0x7e1, None): [b'\xf1\x87VDJLT17895112DN4\x88fVf\x99\x88\x88\x88\x87fVe\x88vhwwUFU\x97eFex\x99\xff\xb7\x82\xf1\x81E25\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33NB2\x11\x1am\xda', ], (Ecu.engine, 0x7e0, None): [
b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00IK MDPS R 1.00 1.06 57700-G9420 4I4VL106',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00IK MFC AT USA LHD 1.00 1.01 95740-G9000 170920',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33NB2\x11\x1am\xda',
b'\xf1\x87VDJLT17895112DN4\x88fVf\x99\x88\x88\x88\x87fVe\x88vhwwUFU\x97eFex\x99\xff\xb7\x82\xf1\x81E25\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33NB2\x11\x1am\xda',
],
}, },
CAR.GENESIS_G70_2020: { CAR.GENESIS_G70_2020: {
(Ecu.eps, 0x7d4, None): [ (Ecu.eps, 0x7d4, None): [
@ -1444,25 +1485,29 @@ FW_VERSIONS = {
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA000 210930', b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA000 210930',
b'\xf1\000CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819', b'\xf1\000CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819',
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.07 99210-AA000 220426', b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.07 99210-AA000 220426',
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.08 99210-AA000 220728',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\000CNhe SCC FHCUP 1.00 1.01 99110-BY000 ', b'\xf1\x00CNhe SCC FHCUP 1.00 1.01 99110-BY000 ',
b'\xf1\x8799110BY000\xf1\x00CNhe SCC FHCUP 1.00 1.01 99110-BY000 ', b'\xf1\x8799110BY000\xf1\x00CNhe SCC FHCUP 1.00 1.01 99110-BY000 ',
], ],
(Ecu.eps, 0x7d4, None): [ (Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.03 56310BY0500 4CNHC103', b'\xf1\x00CN7 MDPS C 1.00 1.03 56310BY0500 4CNHC103',
b'\xf1\x8756310/BY050\xf1\x00CN7 MDPS C 1.00 1.03 56310/BY050 4CNHC103', b'\xf1\x8756310/BY050\xf1\x00CN7 MDPS C 1.00 1.03 56310/BY050 4CNHC103',
b'\xf1\x8756310/BY050\xf1\000CN7 MDPS C 1.00 1.02 56310/BY050 4CNHC102', b'\xf1\x8756310/BY050\xf1\000CN7 MDPS C 1.00 1.02 56310/BY050 4CNHC102',
b'\xf1\x00CN7 MDPS C 1.00 1.04 56310BY050\x00 4CNHC104',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa', b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa',
b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\000\000\000\000', b'\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\000\000\000\000',
b'\xf1\x816U3K3051\000\000\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa', b'\xf1\x816U3K3051\000\000\xf1\0006U3L0_C2\000\0006U3K3051\000\000HCN0G16NS0\xb9?A\xaa',
b'\xf1\x816U3K3051\x00\x00\xf1\x006U3L0_C2\x00\x006U3K3051\x00\x00HCN0G16NS0\x00\x00\x00\x00', b'\xf1\x816U3K3051\x00\x00\xf1\x006U3L0_C2\x00\x006U3K3051\x00\x00HCN0G16NS0\x00\x00\x00\x00',
b'\xf1\x006U3L0_C2\x00\x006U3K9051\x00\x00HCN0G16NS1\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G8051\x00\x00\x00\x00\x00\x00\x00\x00',
] ]
}, },
CAR.KONA_HEV: { CAR.KONA_HEV: {
@ -1552,6 +1597,8 @@ FW_VERSIONS = {
b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.06 99210-CV000 220328', b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.06 99210-CV000 220328',
b'\xf1\x00CV1 MFC AT EUR RHD 1.00 1.00 99210-CV100 220630', b'\xf1\x00CV1 MFC AT EUR RHD 1.00 1.00 99210-CV100 220630',
b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.00 99210-CV100 220630', b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.00 99210-CV100 220630',
b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.04 99210-CV000 210823',
b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.06 99210-CV000 220328',
], ],
}, },
CAR.IONIQ_5: { CAR.IONIQ_5: {
@ -1562,6 +1609,7 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813', b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614',
b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.05 99211-GI010 220614',
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',
@ -1570,9 +1618,11 @@ FW_VERSIONS = {
}, },
CAR.TUCSON_4TH_GEN: { CAR.TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9210 14G',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T', b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ', b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ',
], ],
}, },
@ -1628,6 +1678,7 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU100 211215', b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU100 211215',
b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU000 211215', b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU000 211215',
b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.03 99211-CU000 221118',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JW1_ RDR ----- 1.00 1.00 99110-CU000 ', b'\xf1\x00JW1_ RDR ----- 1.00 1.00 99110-CU000 ',
@ -1636,14 +1687,17 @@ FW_VERSIONS = {
CAR.KIA_SORENTO_4TH_GEN: { CAR.KIA_SORENTO_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.05 99210-R5000 210623', b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.05 99210-R5000 210623',
b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.03 99210-R5000 200903',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.06 99110-P2000 ', b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.06 99110-P2000 ',
b'\xf1\x00MQ4_ SCC F-CUP 1.00 1.06 99110-P2000 ',
], ],
}, },
CAR.KIA_NIRO_HEV_2ND_GEN: { CAR.KIA_NIRO_HEV_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SG2HMFC AT USA LHD 1.01 1.08 99211-AT000 220531', b'\xf1\x00SG2HMFC AT USA LHD 1.01 1.08 99211-AT000 220531',
b'\xf1\x00SG2HMFC AT USA LHD 1.01 1.09 99211-AT000 220801',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SG2_ RDR ----- 1.00 1.01 99110-AT000 ', b'\xf1\x00SG2_ RDR ----- 1.00 1.01 99110-AT000 ',

@ -8,10 +8,10 @@ from cereal import car
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from common.numpy_fast import clip, interp from common.numpy_fast import clip
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, apply_center_deadzone from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, get_friction
from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
@ -131,15 +131,11 @@ class CarInterfaceBase(ABC):
return self.get_steer_feedforward_default return self.get_steer_feedforward_default
@staticmethod @staticmethod
def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation): def torque_from_lateral_accel_linear(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
friction_interp = interp( friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), return (lateral_accel_value / float(torque_params.latAccelFactor)) + friction
[-FRICTION_THRESHOLD, FRICTION_THRESHOLD],
[-torque_params.friction, torque_params.friction]
)
friction = friction_interp if friction_compensation else 0.0
return (lateral_accel_value / torque_params.latAccelFactor) + friction
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType: def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
return self.torque_from_lateral_accel_linear return self.torque_from_lateral_accel_linear

@ -60,7 +60,7 @@ class IsoTpParallelQuery:
return msgs return msgs
def _drain_rx(self): def _drain_rx(self):
messaging.drain_sock(self.logcan) messaging.drain_sock_raw(self.logcan)
self.msg_buffer = defaultdict(list) self.msg_buffer = defaultdict(list)
def _create_isotp_msg(self, tx_addr, sub_addr, rx_addr): def _create_isotp_msg(self, tx_addr, sub_addr, rx_addr):

@ -126,18 +126,22 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x707, None): [ (Ecu.fwdCamera, 0x707, None): [
b'5SH1BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80', b'5SH1BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
b'5SK0ADB\x04\x18\x00\x00\x00\x00\x00_(5\x07\x9aQ\x00\x00\x00\x80', b'5SK0ADB\x04\x18\x00\x00\x00\x00\x00_(5\x07\x9aQ\x00\x00\x00\x80',
b'5SH4BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
], ],
(Ecu.abs, 0x740, None): [ (Ecu.abs, 0x740, None): [
b'476605SH1D', b'476605SH1D',
b'476605SK2A', b'476605SK2A',
b'476605SD2E',
], ],
(Ecu.eps, 0x742, None): [ (Ecu.eps, 0x742, None): [
b'5SH2A\x99A\x05\x02N123F\x15\x81\x00\x00\x00\x00\x00\x00\x00\x80', b'5SH2A\x99A\x05\x02N123F\x15\x81\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SK3A\x99A\x05\x02N123F\x15u\x00\x00\x00\x00\x00\x00\x00\x80', b'5SK3A\x99A\x05\x02N123F\x15u\x00\x00\x00\x00\x00\x00\x00\x80',
b'5SH2C\xb7A\x05\x02N123F\x15\xa3\x00\x00\x00\x00\x00\x00\x00\x80',
], ],
(Ecu.gateway, 0x18dad0f1, None): [ (Ecu.gateway, 0x18dad0f1, None): [
b'284U25SH3A', b'284U25SH3A',
b'284U25SK2D', b'284U25SK2D',
b'284U25SF0C',
], ],
}, },
CAR.XTRAIL: { CAR.XTRAIL: {

@ -273,9 +273,11 @@ FW_VERSIONS = {
b'\xa3 \031\024\000', b'\xa3 \031\024\000',
b'\xa3 \x14\x01', b'\xa3 \x14\x01',
b'\xf1\x00\xbb\r\x05', b'\xf1\x00\xbb\r\x05',
b'\xa3 \x18&\x00',
], ],
(Ecu.eps, 0x746, None): [ (Ecu.eps, 0x746, None): [
b'\x8d\xc0\x04\x00', b'\x8d\xc0\x04\x00',
b'\x8d\xc0\x00\x00',
], ],
(Ecu.fwdCamera, 0x787, None): [ (Ecu.fwdCamera, 0x787, None): [
b'\x00\x00e!\x1f@ \x11', b'\x00\x00e!\x1f@ \x11',
@ -292,6 +294,7 @@ FW_VERSIONS = {
b'\xcb\"`p\a', b'\xcb\"`p\a',
b'\xf1\x00\xa2\x10\n', b'\xf1\x00\xa2\x10\n',
b'\xcf"`p\x07', b'\xcf"`p\x07',
b'\xb6\xa2`A\x07',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\032\xf6B0\000', b'\032\xf6B0\000',
@ -299,6 +302,7 @@ FW_VERSIONS = {
b'\032\xf6b`\000', b'\032\xf6b`\000',
b'\x1a\xf6B`\x00', b'\x1a\xf6B`\x00',
b'\x1a\xf6b0\x00', b'\x1a\xf6b0\x00',
b'\x1a\xe6B1\x00',
], ],
}, },
CAR.FORESTER_PREGLOBAL: { CAR.FORESTER_PREGLOBAL: {

@ -27,6 +27,8 @@ non_tested_cars = [
HYUNDAI.KIA_OPTIMA_H, HYUNDAI.KIA_OPTIMA_H,
HONDA.ODYSSEY_CHN, HONDA.ODYSSEY_CHN,
VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter
TOYOTA.RAV4_TSS2_2023,
TOYOTA.RAV4H_TSS2_2023,
] ]
CarTestRoute = namedtuple('CarTestRoute', ['route', 'car_model', 'segment'], defaults=(None,)) CarTestRoute = namedtuple('CarTestRoute', ['route', 'car_model', 'segment'], defaults=(None,))
@ -50,6 +52,7 @@ routes = [
CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA), CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA),
CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL), CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
CarTestRoute("75a6bcb9b8b40373|2023-03-11--22-47-33", GM.BUICK_LACROSSE),
CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE), CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE),
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),

@ -10,7 +10,6 @@ from common.realtime import DT_CTRL
from selfdrive.car.car_helpers import interfaces from selfdrive.car.car_helpers import interfaces
from selfdrive.car.fingerprints import all_known_cars from selfdrive.car.fingerprints import all_known_cars
from selfdrive.car.interfaces import get_torque_params from selfdrive.car.interfaces import get_torque_params
from selfdrive.car.hyundai.values import CAR as HYUNDAI
CAR_MODELS = all_known_cars() CAR_MODELS = all_known_cars()
@ -22,14 +21,6 @@ MAX_LAT_ACCEL = 3.0 # m/s^2
# jerk is measured over half a second # jerk is measured over half a second
JERK_MEAS_FRAMES = 0.5 / DT_CTRL JERK_MEAS_FRAMES = 0.5 / DT_CTRL
# TODO: update the max measured lateral accel for these cars
ABOVE_LIMITS_CARS = [
HYUNDAI.KONA_EV,
HYUNDAI.KONA_HEV,
HYUNDAI.KONA,
HYUNDAI.KONA_EV_2022,
]
car_model_jerks: DefaultDict[str, Dict[str, float]] = defaultdict(dict) car_model_jerks: DefaultDict[str, Dict[str, float]] = defaultdict(dict)
@ -52,9 +43,6 @@ class TestLateralLimits(unittest.TestCase):
if CP.notCar: if CP.notCar:
raise unittest.SkipTest raise unittest.SkipTest
if CP.carFingerprint in ABOVE_LIMITS_CARS:
raise unittest.SkipTest
CarControllerParams = importlib.import_module(f'selfdrive.car.{CP.carName}.values').CarControllerParams CarControllerParams = importlib.import_module(f'selfdrive.car.{CP.carName}.values').CarControllerParams
cls.control_params = CarControllerParams(CP) cls.control_params = CarControllerParams(CP)
cls.torque_params = get_torque_params(cls.car_model) cls.torque_params = get_torque_params(cls.car_model)

@ -7,6 +7,10 @@ NISSAN LEAF 2018 Instrument Cluster: [.nan, 1.5, .nan]
NISSAN LEAF 2018: [.nan, 1.5, .nan] NISSAN LEAF 2018: [.nan, 1.5, .nan]
NISSAN ROGUE 2019: [.nan, 1.5, .nan] NISSAN ROGUE 2019: [.nan, 1.5, .nan]
# Toyota LTA also has torque
TOYOTA RAV4 2023: [.nan, 3.0, .nan]
TOYOTA RAV4 HYBRID 2023: [.nan, 3.0, .nan]
# Tesla has high torque # Tesla has high torque
TESLA AP1 MODEL S: [.nan, 2.5, .nan] TESLA AP1 MODEL S: [.nan, 2.5, .nan]
TESLA AP2 MODEL S: [.nan, 2.5, .nan] TESLA AP2 MODEL S: [.nan, 2.5, .nan]

@ -30,7 +30,7 @@ HYUNDAI IONIQ 5 2022: [3.172929, 2.713050, 0.096019]
HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276] HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276]
HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778] HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778]
HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482] HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482]
HYUNDAI KONA ELECTRIC 2019: [3.078814714619148, 3.2961956260770484, 0.12359762054065548] HYUNDAI KONA ELECTRIC 2019: [3.078814714619148, 2.307336938253934, 0.12359762054065548]
HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651] HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651]
HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945] HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945]
HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963] HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963]

@ -50,6 +50,7 @@ HONDA CIVIC SEDAN 1.6 DIESEL 2019: HONDA CIVIC (BOSCH) 2019
HONDA E 2020: HONDA CIVIC (BOSCH) 2019 HONDA E 2020: HONDA CIVIC (BOSCH) 2019
HONDA ODYSSEY CHN 2019: HONDA ODYSSEY 2018 HONDA ODYSSEY CHN 2019: HONDA ODYSSEY 2018
BUICK LACROSSE 2017: CHEVROLET VOLT PREMIER 2017
BUICK REGAL ESSENCE 2018: CHEVROLET VOLT PREMIER 2017 BUICK REGAL ESSENCE 2018: CHEVROLET VOLT PREMIER 2017
CADILLAC ESCALADE ESV 2016: CHEVROLET VOLT PREMIER 2017 CADILLAC ESCALADE ESV 2016: CHEVROLET VOLT PREMIER 2017
CADILLAC ATS Premium Performance 2018: CHEVROLET VOLT PREMIER 2017 CADILLAC ATS Premium Performance 2018: CHEVROLET VOLT PREMIER 2017

@ -76,6 +76,11 @@ class CarController:
apply_steer_req = 0 apply_steer_req = 0
self.steer_rate_counter = 0 self.steer_rate_counter = 0
# Never actuate with LKA on cars that only support LTA
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
apply_steer = 0
apply_steer_req = 0
# TODO: probably can delete this. CS.pcm_acc_status uses a different signal # TODO: probably can delete this. CS.pcm_acc_status uses a different signal
# than CS.cruiseState.enabled. confirm they're not meaningfully different # than CS.cruiseState.enabled. confirm they're not meaningfully different
if not CC.enabled and CS.pcm_acc_status: if not CC.enabled and CS.pcm_acc_status:

@ -2,7 +2,8 @@
from cereal import car from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from panda import Panda from panda import Panda
from selfdrive.car.toyota.values import Ecu, CAR, ToyotaFlags, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, MIN_ACC_SPEED, EPS_SCALE, EV_HYBRID_CAR, UNSUPPORTED_DSU_CAR, CarControllerParams, NO_STOP_TIMER_CAR from selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, EV_HYBRID_CAR, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR
from selfdrive.car import STD_CARGO_KG, scale_tire_stiffness, get_safety_config from selfdrive.car import STD_CARGO_KG, scale_tire_stiffness, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
@ -20,16 +21,22 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate] ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
if candidate in (CAR.RAV4, CAR.PRIUS_V, CAR.COROLLA, CAR.LEXUS_ESH, CAR.LEXUS_CTH): # BRAKE_MODULE is on a different address for these cars
if DBC[candidate]["pt"] == "toyota_new_mc_pt_generated":
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_ALT_BRAKE ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_ALT_BRAKE
if candidate in ANGLE_CONTROL_CAR:
ret.dashcamOnly = True
ret.steerControlType = car.CarParams.SteerControlType.angle
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_LTA
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.steerActuatorDelay = 0.12 # Default delay, Prius has larger delay ret.steerActuatorDelay = 0.12 # Default delay, Prius has larger delay
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop
stop_and_go = False stop_and_go = False
steering_angle_deadzone_deg = 0.0
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
if candidate == CAR.PRIUS: if candidate == CAR.PRIUS:
stop_and_go = True stop_and_go = True
@ -40,9 +47,8 @@ class CarInterface(CarInterfaceBase):
# Only give steer angle deadzone to for bad angle sensor prius # Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw: for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00': if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
steering_angle_deadzone_deg = 0.2
ret.steerActuatorDelay = 0.25 ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2)
elif candidate == CAR.PRIUS_V: elif candidate == CAR.PRIUS_V:
stop_and_go = True stop_and_go = True
@ -102,7 +108,8 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.7983 tire_stiffness_factor = 0.7983
ret.mass = 3505. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid ret.mass = 3505. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022): elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022,
CAR.RAV4_TSS2_2023, CAR.RAV4H_TSS2_2023):
stop_and_go = True stop_and_go = True
ret.wheelbase = 2.68986 ret.wheelbase = 2.68986
ret.steerRatio = 14.3 ret.steerRatio = 14.3

@ -0,0 +1,13 @@
#!/usr/bin/env python3
import unittest
from selfdrive.car.toyota.values import TSS2_CAR, ANGLE_CONTROL_CAR
class TestToyotaInterfaces(unittest.TestCase):
def test_angle_car_set(self):
self.assertTrue(len(ANGLE_CONTROL_CAR - TSS2_CAR) == 0)
if __name__ == "__main__":
unittest.main()

@ -17,7 +17,7 @@ def create_lta_steer_command(packer, steer, steer_req, raw_cnt):
"SETME_X1": 1, "SETME_X1": 1,
"SETME_X3": 3, "SETME_X3": 3,
"PERCENTAGE": 100, "PERCENTAGE": 100,
"SETME_X64": 0x64, "SETME_X64": 0,
"ANGLE": 0, "ANGLE": 0,
"STEER_ANGLE_CMD": steer, "STEER_ANGLE_CMD": steer,
"STEER_REQUEST": steer_req, "STEER_REQUEST": steer_req,

@ -67,8 +67,10 @@ class CAR:
RAV4H = "TOYOTA RAV4 HYBRID 2017" RAV4H = "TOYOTA RAV4 HYBRID 2017"
RAV4_TSS2 = "TOYOTA RAV4 2019" RAV4_TSS2 = "TOYOTA RAV4 2019"
RAV4_TSS2_2022 = "TOYOTA RAV4 2022" RAV4_TSS2_2022 = "TOYOTA RAV4 2022"
RAV4_TSS2_2023 = "TOYOTA RAV4 2023"
RAV4H_TSS2 = "TOYOTA RAV4 HYBRID 2019" RAV4H_TSS2 = "TOYOTA RAV4 HYBRID 2019"
RAV4H_TSS2_2022 = "TOYOTA RAV4 HYBRID 2022" RAV4H_TSS2_2022 = "TOYOTA RAV4 HYBRID 2022"
RAV4H_TSS2_2023 = "TOYOTA RAV4 HYBRID 2023"
MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5 MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5
SIENNA = "TOYOTA SIENNA 2018" SIENNA = "TOYOTA SIENNA 2018"
@ -157,14 +159,16 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
], ],
CAR.RAV4_TSS2: ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"), CAR.RAV4_TSS2: ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
CAR.RAV4_TSS2_2022: ToyotaCarInfo("Toyota RAV4 2022"), CAR.RAV4_TSS2_2022: ToyotaCarInfo("Toyota RAV4 2022"),
CAR.RAV4_TSS2_2023: ToyotaCarInfo("Toyota RAV4 2023"),
CAR.RAV4H_TSS2: ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"), CAR.RAV4H_TSS2: ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"),
CAR.RAV4H_TSS2_2022: ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), CAR.RAV4H_TSS2_2022: ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
CAR.RAV4H_TSS2_2023: ToyotaCarInfo("Toyota RAV4 Hybrid 2023"),
CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"), CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"),
CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED), CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED),
# Lexus # Lexus
CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"), CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"),
CAR.LEXUS_ESH: ToyotaCarInfo("Lexus ES Hybrid 2017-18", "Lexus Safety System+"), CAR.LEXUS_ESH: ToyotaCarInfo("Lexus ES Hybrid 2017-18"),
CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"), CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"),
CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-23", video_link="https://youtu.be/BZ29osRVJeg?t=12"), CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-23", video_link="https://youtu.be/BZ29osRVJeg?t=12"),
CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"), CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"),
@ -172,7 +176,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
CAR.LEXUS_NXH: ToyotaCarInfo("Lexus NX Hybrid 2018-19"), CAR.LEXUS_NXH: ToyotaCarInfo("Lexus NX Hybrid 2018-19"),
CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"), CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"),
CAR.LEXUS_NXH_TSS2: ToyotaCarInfo("Lexus NX Hybrid 2020-21"), CAR.LEXUS_NXH_TSS2: ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2017-20"), CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2018-20"),
CAR.LEXUS_RX: [ CAR.LEXUS_RX: [
ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"), ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX 2017-19"), ToyotaCarInfo("Lexus RX 2017-19"),
@ -323,6 +327,7 @@ FW_VERSIONS = {
CAR.AVALON_TSS2: { CAR.AVALON_TSS2: {
(Ecu.abs, 0x7b0, None): [ (Ecu.abs, 0x7b0, None): [
b'\x01F152607240\x00\x00\x00\x00\x00\x00', b'\x01F152607240\x00\x00\x00\x00\x00\x00',
b'\x01F152607250\x00\x00\x00\x00\x00\x00',
b'\x01F152607280\x00\x00\x00\x00\x00\x00', b'\x01F152607280\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
@ -330,6 +335,7 @@ FW_VERSIONS = {
], ],
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x01896630742000\x00\x00\x00\x00', b'\x01896630742000\x00\x00\x00\x00',
b'\x01896630743000\x00\x00\x00\x00',
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00', b'\x018821F6201200\x00\x00\x00\x00',
@ -527,14 +533,18 @@ FW_VERSIONS = {
CAR.CAMRY_TSS2: { CAR.CAMRY_TSS2: {
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
b'8965B33630\x00\x00\x00\x00\x00\x00', b'8965B33630\x00\x00\x00\x00\x00\x00',
b'8965B33640\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.abs, 0x7b0, None): [ (Ecu.abs, 0x7b0, None): [
b'\x01F152606370\x00\x00\x00\x00\x00\x00', b'\x01F152606370\x00\x00\x00\x00\x00\x00',
b'\x01F152606390\x00\x00\x00\x00\x00\x00', b'\x01F152606390\x00\x00\x00\x00\x00\x00',
b'\x01F152606400\x00\x00\x00\x00\x00\x00', b'\x01F152606400\x00\x00\x00\x00\x00\x00',
b'\x01F152606431\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x018966306Q5000\x00\x00\x00\x00', b'\x018966306Q5000\x00\x00\x00\x00',
b'\x018966306Q9000\x00\x00\x00\x00',
b'\x018966306R3000\x00\x00\x00\x00',
b'\x018966306T3100\x00\x00\x00\x00', b'\x018966306T3100\x00\x00\x00\x00',
b'\x018966306T3200\x00\x00\x00\x00', b'\x018966306T3200\x00\x00\x00\x00',
b'\x018966306T4000\x00\x00\x00\x00', b'\x018966306T4000\x00\x00\x00\x00',
@ -542,12 +552,15 @@ FW_VERSIONS = {
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00', b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0602100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F0602100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F0602200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F0602200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305500\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
], ],
}, },
CAR.CAMRYH_TSS2: { CAR.CAMRYH_TSS2: {
@ -716,22 +729,26 @@ FW_VERSIONS = {
], ],
}, },
CAR.CHRH_TSS2: { CAR.CHRH_TSS2: {
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
b'8965B10092\x00\x00\x00\x00\x00\x00', b'8965B10092\x00\x00\x00\x00\x00\x00',
], b'8965B10091\x00\x00\x00\x00\x00\x00',
(Ecu.abs, 0x7b0, None): [ ],
b'F152610041\x00\x00\x00\x00\x00\x00', (Ecu.abs, 0x7b0, None): [
], b'F152610041\x00\x00\x00\x00\x00\x00',
(Ecu.engine, 0x700, None): [ ],
b'\x0189663F438000\x00\x00\x00\x00', (Ecu.engine, 0x700, None): [
], b'\x0189663F438000\x00\x00\x00\x00',
(Ecu.fwdRadar, 0x750, 15): [ b'\x0289663F453000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x018821FF410500\x00\x00\x00\x00', ],
], (Ecu.fwdRadar, 0x750, 15): [
(Ecu.fwdCamera, 0x750, 109): [ b'\x018821FF410500\x00\x00\x00\x00',
b'\x028646FF413100\x00\x00\x00\x008646GF411100\x00\x00\x00\x00', b'\x018821FF410300\x00\x00\x00\x00',
], ],
}, (Ecu.fwdCamera, 0x750, 109): [
b'\x028646FF413100\x00\x00\x00\x008646GF411100\x00\x00\x00\x00',
b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00',
],
},
CAR.COROLLA: { CAR.COROLLA: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\x0230ZC2000\x00\x00\x00\x00\x00\x00\x00\x0050212000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0230ZC2000\x00\x00\x00\x00\x00\x00\x00\x0050212000\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1454,6 +1471,23 @@ FW_VERSIONS = {
b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00', b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00',
], ],
}, },
CAR.RAV4_TSS2_2023: {
(Ecu.abs, 0x7b0, None): [
b'\x01F15260R450\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x028965B0R11000\x00\x00\x00\x008965B0R12000\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896634AJ2000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R03100\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0R05100\x00\x00\x00\x008646G0R02100\x00\x00\x00\x00',
],
},
CAR.RAV4H_TSS2: { CAR.RAV4H_TSS2: {
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x01896634A15000\x00\x00\x00\x00', b'\x01896634A15000\x00\x00\x00\x00',
@ -1541,6 +1575,23 @@ FW_VERSIONS = {
b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00', b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00',
], ],
}, },
CAR.RAV4H_TSS2_2023: {
(Ecu.abs, 0x7b0, None): [
b'\x01F15264283200\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x028965B0R11000\x00\x00\x00\x008965B0R12000\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896634AE1001\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R03100\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0R05100\x00\x00\x00\x008646G0R02100\x00\x00\x00\x00',
],
},
CAR.SIENNA: { CAR.SIENNA: {
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x01896630832100\x00\x00\x00\x00', b'\x01896630832100\x00\x00\x00\x00',
@ -1729,6 +1780,7 @@ FW_VERSIONS = {
b'\x018966378B2100\x00\x00\x00\x00', b'\x018966378B2100\x00\x00\x00\x00',
b'\x018966378B3000\x00\x00\x00\x00', b'\x018966378B3000\x00\x00\x00\x00',
b'\x018966378B4100\x00\x00\x00\x00', b'\x018966378B4100\x00\x00\x00\x00',
b'\x018966378G2000\x00\x00\x00\x00',
b'\x018966378G3000\x00\x00\x00\x00', b'\x018966378G3000\x00\x00\x00\x00',
b'\x018966378B2000\x00\x00\x00\x00', b'\x018966378B2000\x00\x00\x00\x00',
], ],
@ -1751,6 +1803,7 @@ FW_VERSIONS = {
CAR.LEXUS_NXH_TSS2: { CAR.LEXUS_NXH_TSS2: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\x0237887000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0237887000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02378A0000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.abs, 0x7b0, None): [ (Ecu.abs, 0x7b0, None): [
b'F152678210\x00\x00\x00\x00\x00\x00', b'F152678210\x00\x00\x00\x00\x00\x00',
@ -1760,6 +1813,7 @@ FW_VERSIONS = {
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00', b'\x018821F3301400\x00\x00\x00\x00',
b'\x018821F3301300\x00\x00\x00\x00',
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F78030A0\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F78030A0\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
@ -2096,6 +2150,7 @@ DBC = {
CAR.AVALONH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.AVALONH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.COROLLAH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.COROLLAH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
@ -2106,6 +2161,7 @@ DBC = {
CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.RAV4H_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.RAV4H_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4H_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), CAR.RAV4H_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4H_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.LEXUS_NXH: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_NXH: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
@ -2120,8 +2176,8 @@ DBC = {
EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.PRIUS_V: 100}) EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.PRIUS_V: 100})
# Toyota/Lexus Safety Sense 2.0 and 2.5 # Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022,
CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, CAR.RAV4H_TSS2_2023, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2,
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2} CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2}
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH}
@ -2130,10 +2186,13 @@ NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH}
UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC} UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC}
# these cars have a radar which sends ACC messages instead of the camera # these cars have a radar which sends ACC messages instead of the camera
RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.CHR_TSS2, CAR.CHRH_TSS2} RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2, CAR.CHRH_TSS2}
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL_CAR = {CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023}
EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.CHRH_TSS2, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.CHRH_TSS2, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS,
CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH,
CAR.LEXUS_RXH_TSS2, CAR.LEXUS_NXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2} CAR.LEXUS_RXH_TSS2, CAR.LEXUS_NXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2}
# no resume button press required # no resume button press required

@ -293,6 +293,7 @@ FW_VERSIONS = {
b'\xf1\x873G0906259N \xf1\x890004', b'\xf1\x873G0906259N \xf1\x890004',
b'\xf1\x873G0906259P \xf1\x890001', b'\xf1\x873G0906259P \xf1\x890001',
b'\xf1\x875NA907115H \xf1\x890002', b'\xf1\x875NA907115H \xf1\x890002',
b'\xf1\x873G0906259G \xf1\x890004',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158L \xf1\x893611', b'\xf1\x8709G927158L \xf1\x893611',
@ -304,17 +305,19 @@ FW_VERSIONS = {
b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1616001613121157161111572900', b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1616001613121157161111572900',
b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1616001613121177161113772900', b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1616001613121177161113772900',
b'\xf1\x873Q0959655DL\xf1\x890732\xf1\x82\0161812141812171105141123052J00', b'\xf1\x873Q0959655DL\xf1\x890732\xf1\x82\0161812141812171105141123052J00',
b'\xf1\x873Q0959655CK\xf1\x890711\xf1\x82\x0e1712141712141105121122052900',
], ],
(Ecu.eps, 0x712, None): [ (Ecu.eps, 0x712, None): [
b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571B41815A1', b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571B41815A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571B00817A1', b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571B00817A1',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\00567B0020800', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567B0020800',
b'\xf1\x875WA907145M \xf1\x891051\xf1\x82\x002MB4092M7N', b'\xf1\x875WA907145M \xf1\x891051\xf1\x82\x002MB4092M7N',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572AA\xf1\x890396', b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572T \xf1\x890383', b'\xf1\x872Q0907572T \xf1\x890383',
b'\xf1\x875Q0907572J \xf1\x890654', b'\xf1\x875Q0907572J \xf1\x890654',
b'\xf1\x875Q0907572R \xf1\x890771',
], ],
}, },
CAR.ATLAS_MK1: { CAR.ATLAS_MK1: {
@ -394,6 +397,7 @@ FW_VERSIONS = {
b'\xf1\x8704L906021DT\xf1\x895520', b'\xf1\x8704L906021DT\xf1\x895520',
b'\xf1\x8704L906021DT\xf1\x898127', b'\xf1\x8704L906021DT\xf1\x898127',
b'\xf1\x8704L906021N \xf1\x895518', b'\xf1\x8704L906021N \xf1\x895518',
b'\xf1\x8704L906026BN\xf1\x891197',
b'\xf1\x8704L906026BP\xf1\x897608', b'\xf1\x8704L906026BP\xf1\x897608',
b'\xf1\x8704L906026NF\xf1\x899528', b'\xf1\x8704L906026NF\xf1\x899528',
b'\xf1\x8704L906056CL\xf1\x893823', b'\xf1\x8704L906056CL\xf1\x893823',
@ -514,6 +518,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\x0511A00403A0', b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\x0511A00403A0',
b'\xf1\x875Q0909144R \xf1\x891061\xf1\x82\x0516A00604A1', b'\xf1\x875Q0909144R \xf1\x891061\xf1\x82\x0516A00604A1',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A00404A1', b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A00404A1',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A00504A1',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A00604A1', b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A00604A1',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A07A02A1', b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516A07A02A1',
b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\x0521A00507A1', b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\x0521A00507A1',
@ -861,6 +866,7 @@ FW_VERSIONS = {
b'\xf1\x878V0906264B \xf1\x890003', b'\xf1\x878V0906264B \xf1\x890003',
b'\xf1\x878V0907115B \xf1\x890007', b'\xf1\x878V0907115B \xf1\x890007',
b'\xf1\x878V0907404A \xf1\x890005', b'\xf1\x878V0907404A \xf1\x890005',
b'\xf1\x875G0906259D \xf1\x890002',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x870CW300044T \xf1\x895245', b'\xf1\x870CW300044T \xf1\x895245',
@ -868,6 +874,7 @@ FW_VERSIONS = {
b'\xf1\x870D9300012 \xf1\x894912', b'\xf1\x870D9300012 \xf1\x894912',
b'\xf1\x870D9300012 \xf1\x894931', b'\xf1\x870D9300012 \xf1\x894931',
b'\xf1\x870D9300012K \xf1\x894513', b'\xf1\x870D9300012K \xf1\x894513',
b'\xf1\x870D9300013B \xf1\x894902',
b'\xf1\x870D9300013B \xf1\x894931', b'\xf1\x870D9300013B \xf1\x894931',
b'\xf1\x870D9300041N \xf1\x894512', b'\xf1\x870D9300041N \xf1\x894512',
b'\xf1\x870D9300043T \xf1\x899699', b'\xf1\x870D9300043T \xf1\x899699',
@ -905,11 +912,13 @@ FW_VERSIONS = {
b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\00503G00803A0', b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\00503G00803A0',
b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\x0503G0G803A0', b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\x0503G0G803A0',
b'\xf1\x875Q0909144R \xf1\x891061\xf1\x82\00516G00804A1', b'\xf1\x875Q0909144R \xf1\x891061\xf1\x82\00516G00804A1',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\x0516G00804A1',
b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521G00807A1', b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521G00807A1',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x875Q0907567N \xf1\x890400\xf1\x82\00101', b'\xf1\x875Q0907567N \xf1\x890400\xf1\x82\x0101',
b'\xf1\x875Q0907572D \xf1\x890304\xf1\x82\00101', b'\xf1\x875Q0907572F \xf1\x890400\xf1\x82\x0101',
b'\xf1\x875Q0907572D \xf1\x890304\xf1\x82\x0101',
b'\xf1\x875Q0907572G \xf1\x890571', b'\xf1\x875Q0907572G \xf1\x890571',
b'\xf1\x875Q0907572H \xf1\x890620', b'\xf1\x875Q0907572H \xf1\x890620',
b'\xf1\x875Q0907572P \xf1\x890682', b'\xf1\x875Q0907572P \xf1\x890682',
@ -1073,6 +1082,8 @@ FW_VERSIONS = {
b'\xf1\x8704L906026DE\xf1\x895418', b'\xf1\x8704L906026DE\xf1\x895418',
b'\xf1\x8704L906026EJ\xf1\x893661', b'\xf1\x8704L906026EJ\xf1\x893661',
b'\xf1\x8704L906026HT\xf1\x893617', b'\xf1\x8704L906026HT\xf1\x893617',
b'\xf1\x8783A907115E \xf1\x890001',
b'\xf1\x8705E906018DJ\xf1\x890915',
b'\xf1\x875NA907115E \xf1\x890003', b'\xf1\x875NA907115E \xf1\x890003',
b'\xf1\x875NA907115E \xf1\x890005', b'\xf1\x875NA907115E \xf1\x890005',
], ],
@ -1082,6 +1093,8 @@ FW_VERSIONS = {
b'\xf1\x870DL300012M \xf1\x892107', b'\xf1\x870DL300012M \xf1\x892107',
b'\xf1\x870DL300012N \xf1\x892110', b'\xf1\x870DL300012N \xf1\x892110',
b'\xf1\x870DL300013G \xf1\x892119', b'\xf1\x870DL300013G \xf1\x892119',
b'\xf1\x870GC300014N \xf1\x892801',
b'\xf1\x870GC300046Q \xf1\x892802',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
b'\xf1\x873Q0959655AP\xf1\x890306\xf1\x82\r11110011110011421111314211', b'\xf1\x873Q0959655AP\xf1\x890306\xf1\x82\r11110011110011421111314211',
@ -1089,11 +1102,13 @@ FW_VERSIONS = {
b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1213001211001244212111442100', 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',
b'\xf1\x873Q0959655DJ\xf1\x890731\xf1\x82\x0e1513001511001205232113052J00',
], ],
(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\x875Q0909143P \xf1\x892051\xf1\x820527T6070405',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T600G500',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T600G600', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T600G600',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
@ -1101,6 +1116,7 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372', b'\xf1\x872Q0907572R \xf1\x890372',
b'\xf1\x872Q0907572T \xf1\x890383', b'\xf1\x872Q0907572T \xf1\x890383',
b'\xf1\x872Q0907572AA\xf1\x890396', b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572AB\xf1\x890397',
], ],
}, },
CAR.SKODA_OCTAVIA_MK3: { CAR.SKODA_OCTAVIA_MK3: {

@ -190,3 +190,13 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates):
current_curvature_desired + max_curvature_rate * DT_MDL) current_curvature_desired + max_curvature_rate * DT_MDL)
return safe_desired_curvature, safe_desired_curvature_rate return safe_desired_curvature, safe_desired_curvature_rate
def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float, torque_params: car.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float:
friction_interp = interp(
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
[-friction_threshold, friction_threshold],
[-torque_params.friction, torque_params.friction]
)
friction = float(friction_interp) if friction_compensation else 0.0
return friction

@ -658,6 +658,11 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"), ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"),
}, },
EventName.steerTimeLimit: {
ET.SOFT_DISABLE: soft_disable_alert("Vehicle Steering Time Limit"),
ET.NO_ENTRY: NoEntryAlert("Vehicle Steering Time Limit"),
},
EventName.outOfSpace: { EventName.outOfSpace: {
ET.PERMANENT: out_of_space_alert, ET.PERMANENT: out_of_space_alert,
ET.NO_ENTRY: NoEntryAlert("Out of Storage"), ET.NO_ENTRY: NoEntryAlert("Out of Storage"),
@ -811,10 +816,6 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"), ET.NO_ENTRY: NoEntryAlert("Cruise Fault: Restart the Car"),
}, },
EventName.accFaultedTemp: {
ET.NO_ENTRY: NoEntryAlert("Cruise Temporarily Faulted"),
},
EventName.controlsMismatch: { EventName.controlsMismatch: {
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"), ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch"),
ET.NO_ENTRY: NoEntryAlert("Controls Mismatch"), ET.NO_ENTRY: NoEntryAlert("Controls Mismatch"),

@ -61,10 +61,12 @@ class LatControlTorque(LatControl):
low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2 low_speed_factor = interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature measurement = actual_lateral_accel + low_speed_factor * actual_curvature
error = setpoint - measurement
gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY
pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error, torque_from_setpoint = self.torque_from_lateral_accel(setpoint, self.torque_params, setpoint,
lateral_accel_deadzone, friction_compensation=False) lateral_accel_deadzone, friction_compensation=False)
torque_from_measurement = self.torque_from_lateral_accel(measurement, self.torque_params, measurement,
lateral_accel_deadzone, friction_compensation=False)
pid_log.error = torque_from_setpoint - torque_from_measurement
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params, ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params,
desired_lateral_accel - actual_lateral_accel, desired_lateral_accel - actual_lateral_accel,
lateral_accel_deadzone, friction_compensation=True) lateral_accel_deadzone, friction_compensation=True)

@ -72,7 +72,6 @@ class TestStartup(unittest.TestCase):
params.clear_all() params.clear_all()
params.put_bool("Passive", False) params.put_bool("Passive", False)
params.put_bool("OpenpilotEnabledToggle", True) params.put_bool("OpenpilotEnabledToggle", True)
params.put_bool("ObdMultiplexingDisabled", True)
# Build capnn version of FW array # Build capnn version of FW array
if fw_versions is not None: if fw_versions is not None:
@ -109,6 +108,10 @@ class TestStartup(unittest.TestCase):
finger = _FINGERPRINTS[car_model][0] finger = _FINGERPRINTS[car_model][0]
for _ in range(1000): for _ in range(1000):
# controlsd waits for boardd to echo back that it has changed the multiplexing mode
if not params.get_bool("ObdMultiplexingChanged"):
params.put_bool("ObdMultiplexingChanged", True)
msgs = [[addr, 0, b'\x00'*length, 0] for addr, length in finger.items()] msgs = [[addr, 0, b'\x00'*length, 0] for addr, length in finger.items()]
pm.send('can', can_list_to_can_capnp(msgs)) pm.send('can', can_list_to_can_capnp(msgs))

@ -32,6 +32,9 @@ SUPPORTED_FW_VERSIONS = {
b"DN8_ SCC FHCUP 1.00 1.00 99110-L0000\x19\x08)\x15T ": ConfigValues( b"DN8_ SCC FHCUP 1.00 1.00 99110-L0000\x19\x08)\x15T ": ConfigValues(
default_config=b"\x00\x00\x00\x01\x00\x00", default_config=b"\x00\x00\x00\x01\x00\x00",
tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), tracks_enabled=b"\x00\x00\x00\x01\x00\x01"),
b"DN8_ SCC F-CUP 1.00 1.00 99110-L0000\x19\x08)\x15T ": ConfigValues(
default_config=b"\x00\x00\x00\x01\x00\x00",
tracks_enabled=b"\x00\x00\x00\x01\x00\x01"),
# 2021 SONATA HYBRID # 2021 SONATA HYBRID
b"DNhe SCC FHCUP 1.00 1.02 99110-L5000 \x01#\x15# ": ConfigValues( b"DNhe SCC FHCUP 1.00 1.02 99110-L5000 \x01#\x15# ": ConfigValues(
default_config=b"\x00\x00\x00\x01\x00\x00", default_config=b"\x00\x00\x00\x01\x00\x00",

@ -0,0 +1,43 @@
#!/usr/bin/env python3
import time
import statistics
import cereal.messaging as messaging
from typing import Dict
camera_states = [
'roadCameraState',
'wideRoadCameraState',
'driverCameraState'
]
def fmt(val):
ref = 0.05
return f"{val:.6f} ({100 * val / ref:.2f}%)"
if __name__ == "__main__":
sm = messaging.SubMaster(camera_states)
prev_sof = {state: None for state in camera_states}
diffs: Dict[str, list] = {state: [] for state in camera_states}
st = time.monotonic()
while True:
sm.update()
for state in camera_states:
if sm.updated[state]:
if prev_sof[state] is not None:
diffs[state].append((sm[state].timestampSof - prev_sof[state]) / 1e9)
prev_sof[state] = sm[state].timestampSof
if time.monotonic() - st > 10:
for state in camera_states:
values = diffs[state]
ref = 0.05
print(f"{state} \tMean: {fmt(statistics.mean(values))} \t Min: {fmt(min(values))} \t Max: {fmt(max(values))} \t Std: {statistics.stdev(values):.6f} \t Num frames: {len(values)}")
diffs[state] = []
print()
st = time.monotonic()

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json
import math import math
import os import os
import time import time
@ -8,7 +7,7 @@ from collections import defaultdict
from concurrent.futures import Future, ProcessPoolExecutor from concurrent.futures import Future, ProcessPoolExecutor
from datetime import datetime from datetime import datetime
from enum import IntEnum from enum import IntEnum
from typing import List, Optional from typing import List, Optional, Dict, Any
import numpy as np import numpy as np
@ -17,9 +16,9 @@ from common.params import Params, put_nonblocking
from laika import AstroDog from laika import AstroDog
from laika.constants import SECS_IN_HR, SECS_IN_MIN from laika.constants import SECS_IN_HR, SECS_IN_MIN
from laika.downloader import DownloadFailed from laika.downloader import DownloadFailed
from laika.ephemeris import Ephemeris, EphemerisType, convert_ublox_gps_ephem, convert_ublox_glonass_ephem, parse_qcom_ephem from laika.ephemeris import EphemerisType, GPSEphemeris, GLONASSEphemeris, ephemeris_structs, parse_qcom_ephem
from laika.gps_time import GPSTime from laika.gps_time import GPSTime
from laika.helpers import ConstellationId from laika.helpers import ConstellationId, get_sv_id
from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox, read_raw_qcom from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox, read_raw_qcom
from laika.opt import calc_pos_fix, get_posfix_sympy_fun, calc_vel_fix, get_velfix_sympy_func from laika.opt import calc_pos_fix, get_posfix_sympy_fun, calc_vel_fix, get_velfix_sympy_func
from selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind from selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind
@ -28,15 +27,53 @@ from selfdrive.locationd.models.gnss_kf import States as GStates
from system.swaglog import cloudlog from system.swaglog import cloudlog
MAX_TIME_GAP = 10 MAX_TIME_GAP = 10
EPHEMERIS_CACHE = 'LaikadEphemerisV2' EPHEMERIS_CACHE = 'LaikadEphemerisV3'
DOWNLOADS_CACHE_FOLDER = "/tmp/comma_download_cache/" DOWNLOADS_CACHE_FOLDER = "/tmp/comma_download_cache/"
CACHE_VERSION = 0.2 CACHE_VERSION = 0.2
POS_FIX_RESIDUAL_THRESHOLD = 100.0 POS_FIX_RESIDUAL_THRESHOLD = 100.0
class LogEphemerisType(IntEnum):
nav = 0
nasaUltraRapid = 1
glonassIacUltraRapid = 2
qcom = 3
class EphemerisSource(IntEnum):
gnssChip = 0
internet = 1
cache = 2
unknown = 3
def get_log_eph_type(ephem):
if ephem.eph_type == EphemerisType.NAV:
source_type = LogEphemerisType.nav
elif ephem.eph_type == EphemerisType.QCOM_POLY:
source_type = LogEphemerisType.qcom
else:
assert ephem.file_epoch is not None
file_src = ephem.file_source
if file_src == 'igu': # example nasa: '2214/igu22144_00.sp3.Z'
source_type = LogEphemerisType.nasaUltraRapid
elif file_src == 'Sta': # example nasa: '22166/ultra/Stark_1D_22061518.sp3'
source_type = LogEphemerisType.glonassIacUltraRapid
else:
raise Exception(f"Didn't expect file source {file_src}")
return source_type
def get_log_eph_source(ephem):
if ephem.file_name == 'qcom' or ephem.file_name == 'ublox':
source = EphemerisSource.gnssChip
elif ephem.file_name == EPHEMERIS_CACHE:
source = EphemerisSource.cache
else:
source = EphemerisSource.internet
return source
class Laikad: class Laikad:
def __init__(self, valid_const=("GPS", "GLONASS"), auto_fetch_navs=True, auto_update=False, def __init__(self, valid_const=(ConstellationId.GPS, ConstellationId.GLONASS), auto_fetch_navs=True, auto_update=False,
valid_ephem_types=(EphemerisType.NAV,), valid_ephem_types=(EphemerisType.NAV, EphemerisType.QCOM_POLY),
save_ephemeris=False, use_qcom=False): save_ephemeris=False, use_qcom=False):
""" """
valid_const: GNSS constellation which can be used valid_const: GNSS constellation which can be used
@ -51,10 +88,11 @@ class Laikad:
self.auto_fetch_navs = auto_fetch_navs self.auto_fetch_navs = auto_fetch_navs
self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None
self.orbit_fetch_future: Optional[Future] = None self.orbit_fetch_future: Optional[Future] = None
self.last_fetch_navs_t = None
self.got_first_gnss_msg = False self.got_first_gnss_msg = False
self.last_cached_t = None
self.last_report_time = GPSTime(0, 0)
self.last_fetch_navs_t = GPSTime(0, 0)
self.last_cached_t = GPSTime(0, 0)
self.save_ephemeris = save_ephemeris self.save_ephemeris = save_ephemeris
self.load_cache() self.load_cache()
@ -64,36 +102,58 @@ class Laikad:
self.last_fix_t = None self.last_fix_t = None
self.gps_week = None self.gps_week = None
self.use_qcom = use_qcom self.use_qcom = use_qcom
self.first_log_time = None
self.ttff = -1
def load_cache(self): def load_cache(self):
if not self.save_ephemeris: if not self.save_ephemeris:
return return
cache = Params().get(EPHEMERIS_CACHE) cache_bytes = Params().get(EPHEMERIS_CACHE)
if not cache: if not cache_bytes:
return return
nav_dict = {}
try: try:
cache = json.loads(cache, object_hook=deserialize_hook) ephem_cache = ephemeris_structs.EphemerisCache.from_bytes(cache_bytes)
if cache['version'] == CACHE_VERSION: glonass_navs = [GLONASSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.glonassEphemerides]
self.astro_dog.add_navs(cache['navs']) gps_navs = [GPSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.gpsEphemerides]
self.last_fetch_navs_t = cache['last_fetch_navs_t'] for e in sum([glonass_navs, gps_navs], []):
else: if e.prn not in nav_dict:
cache['navs'] = {} nav_dict[e.prn] = []
except json.decoder.JSONDecodeError: nav_dict[e.prn].append(e)
self.astro_dog.add_navs(nav_dict)
except Exception:
cloudlog.exception("Error parsing cache") cloudlog.exception("Error parsing cache")
timestamp = self.last_fetch_navs_t.as_datetime() if self.last_fetch_navs_t is not None else 'Nan'
cloudlog.debug( cloudlog.debug(
f"Loaded navs ({sum([len(v) for v in cache['navs']])}) cache with timestamp: {timestamp}. Unique orbit and nav sats: {list(cache['navs'].keys())} " + f"Loaded navs ({sum([len(nav_dict[prn]) for prn in nav_dict.keys()])}). Unique orbit and nav sats: {list(nav_dict.keys())} ")
f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in self.astro_dog.navs_fetched_times._ranges]}")
def cache_ephemeris(self):
def cache_ephemeris(self, t: GPSTime):
if self.save_ephemeris and (self.last_cached_t is None or t - self.last_cached_t > SECS_IN_MIN): if self.save_ephemeris and (self.last_report_time - self.last_cached_t > SECS_IN_MIN):
put_nonblocking(EPHEMERIS_CACHE, json.dumps( nav_list: List = sum([v for k,v in self.astro_dog.navs.items()], [])
{'version': CACHE_VERSION, 'last_fetch_navs_t': self.last_fetch_navs_t, 'navs': self.astro_dog.navs}, ephem_cache = ephemeris_structs.EphemerisCache(**{'glonassEphemerides': [e.data for e in nav_list if e.prn[0]=='R'],
cls=CacheSerializer)) 'gpsEphemerides': [e.data for e in nav_list if e.prn[0]=='G']})
put_nonblocking(EPHEMERIS_CACHE, ephem_cache.to_bytes())
cloudlog.debug("Cache saved") cloudlog.debug("Cache saved")
self.last_cached_t = t self.last_cached_t = self.last_report_time
def create_ephem_statuses(self):
ephemeris_statuses = []
prns_to_check = list(self.astro_dog.get_all_ephem_prns())
prns_to_check.sort()
for prn in prns_to_check:
eph = self.astro_dog.get_eph(prn, self.last_report_time)
if eph is not None:
status = log.GnssMeasurements.EphemerisStatus.new_message()
status.constellationId = ConstellationId.from_rinex_char(prn[0]).value
status.svId = get_sv_id(prn)
status.type = get_log_eph_type(eph).value
status.source = get_log_eph_source(eph).value
ephemeris_statuses.append(status)
return ephemeris_statuses
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:
@ -121,8 +181,15 @@ class Laikad:
def is_good_report(self, gnss_msg): def is_good_report(self, gnss_msg):
if gnss_msg.which() == 'drMeasurementReport' and self.use_qcom: if gnss_msg.which() == 'drMeasurementReport' and self.use_qcom:
constellation_id = ConstellationId.from_qcom_source(gnss_msg.drMeasurementReport.source) constellation_id = ConstellationId.from_qcom_source(gnss_msg.drMeasurementReport.source)
# TODO support GLONASS # TODO: Understand and use remaining unknown constellations
return constellation_id in [ConstellationId.GPS, ConstellationId.SBAS] try:
good_constellation = constellation_id in [ConstellationId.GPS, ConstellationId.SBAS]
except NotImplementedError:
good_constellation = False
# gpsWeek 65535 is received rarely from quectel, this cannot be
# passed to GnssMeasurements's gpsWeek (Int16)
good_week = not getattr(gnss_msg, gnss_msg.which()).gpsWeek > np.iinfo(np.int16).max
return good_constellation and good_week
elif gnss_msg.which() == 'measurementReport' and not self.use_qcom: elif gnss_msg.which() == 'measurementReport' and not self.use_qcom:
return True return True
else: else:
@ -139,6 +206,7 @@ class Laikad:
week = report.gpsWeek week = report.gpsWeek
tow = report.rcvTow tow = report.rcvTow
new_meas = read_raw_ublox(report) new_meas = read_raw_ublox(report)
self.last_report_time = GPSTime(week, tow)
return week, tow, new_meas return week, tow, new_meas
def is_ephemeris(self, gnss_msg): def is_ephemeris(self, gnss_msg):
@ -152,29 +220,47 @@ class Laikad:
# TODO this is not robust to gps week rollover # TODO this is not robust to gps week rollover
if self.gps_week is None: if self.gps_week is None:
return return
ephem = parse_qcom_ephem(gnss_msg.drSvPoly, self.gps_week) try:
ephem = parse_qcom_ephem(gnss_msg.drSvPoly, self.gps_week)
self.astro_dog.add_qcom_polys({ephem.prn: [ephem]})
except Exception:
cloudlog.exception("Error parsing qcom svPoly ephemeris from qcom module")
return
else: else:
if gnss_msg.which() == 'ephemeris': if gnss_msg.which() == 'ephemeris':
ephem = convert_ublox_gps_ephem(gnss_msg.ephemeris) data_struct = ephemeris_structs.Ephemeris.new_message(**gnss_msg.ephemeris.to_dict())
try:
ephem = GPSEphemeris(data_struct, file_name='ublox')
except Exception:
cloudlog.exception("Error parsing GPS ephemeris from ublox")
return
elif gnss_msg.which() == 'glonassEphemeris': elif gnss_msg.which() == 'glonassEphemeris':
ephem = convert_ublox_glonass_ephem(gnss_msg.glonassEphemeris) data_struct = ephemeris_structs.GlonassEphemeris.new_message(**gnss_msg.glonassEphemeris.to_dict())
try:
ephem = GLONASSEphemeris(data_struct, file_name='ublox')
except Exception:
cloudlog.exception("Error parsing GLONASS ephemeris from ublox")
return
else: else:
cloudlog.error(f"Unsupported ephemeris type: {gnss_msg.which()}") cloudlog.error(f"Unsupported ephemeris type: {gnss_msg.which()}")
return return
self.astro_dog.add_navs({ephem.prn: [ephem]}) self.astro_dog.add_navs({ephem.prn: [ephem]})
self.cache_ephemeris(t=ephem.epoch) self.cache_ephemeris()
def process_report(self, new_meas, t): def process_report(self, new_meas, t):
# Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites
new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7]
processed_measurements = process_measurements(new_meas, self.astro_dog) processed_measurements = process_measurements(new_meas, self.astro_dog)
if self.last_fix_pos is not None: if self.last_fix_pos is not None:
corrected_measurements = correct_measurements(processed_measurements, self.last_fix_pos, self.astro_dog) est_pos = self.last_fix_pos
instant_fix = self.get_lsq_fix(t, corrected_measurements)
#instant_fix = self.get_lsq_fix(t, processed_measurements)
else: else:
corrected_measurements = [] est_pos = self.gnss_kf.x[GStates.ECEF_POS].tolist()
instant_fix = self.get_lsq_fix(t, processed_measurements) corrected_measurements = correct_measurements(processed_measurements, est_pos, self.astro_dog)
return corrected_measurements
def calc_fix(self, t, measurements):
instant_fix = self.get_lsq_fix(t, measurements)
if instant_fix is None: if instant_fix is None:
return None return None
else: else:
@ -182,60 +268,53 @@ class Laikad:
self.last_fix_t = t self.last_fix_t = t
self.last_fix_pos = position_estimate self.last_fix_pos = position_estimate
self.lat_fix_pos_std = position_std self.lat_fix_pos_std = position_std
if (t*1e9) % 10 == 0: return position_estimate, position_std, velocity_estimate, velocity_std
cloudlog.debug(f"Measurements Incoming/Processed/Corrected: {len(new_meas), len(processed_measurements), len(corrected_measurements)}")
return position_estimate, position_std, velocity_estimate, velocity_std, corrected_measurements, processed_measurements
def process_gnss_msg(self, gnss_msg, gnss_mono_time: int, block=False): def process_gnss_msg(self, gnss_msg, gnss_mono_time: int, block=False):
out_msg = messaging.new_message("gnssMeasurements")
t = gnss_mono_time * 1e-9
msg_dict: Dict[str, Any] = {"measTime": gnss_mono_time}
if self.first_log_time is None:
self.first_log_time = 1e-9 * gnss_mono_time
if self.is_ephemeris(gnss_msg): if self.is_ephemeris(gnss_msg):
self.read_ephemeris(gnss_msg) self.read_ephemeris(gnss_msg)
return None
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 self.gps_week = week
if len(new_meas) == 0:
return None
t = gnss_mono_time * 1e-9
if week > 0: if week > 0:
self.got_first_gnss_msg = True self.got_first_gnss_msg = True
latest_msg_t = GPSTime(week, tow) latest_msg_t = GPSTime(week, tow)
if self.auto_fetch_navs: if self.auto_fetch_navs:
self.fetch_navs(latest_msg_t, block) self.fetch_navs(latest_msg_t, block)
output = self.process_report(new_meas, t) corrected_measurements = self.process_report(new_meas, t)
if output is None: msg_dict['correctedMeasurements'] = [create_measurement_msg(m) for m in corrected_measurements]
return None
position_estimate, position_std, velocity_estimate, velocity_std, corrected_measurements, _ = output
self.update_localizer(position_estimate, t, corrected_measurements) fix = self.calc_fix(t, corrected_measurements)
meas_msgs = [create_measurement_msg(m) for m in corrected_measurements]
msg = messaging.new_message("gnssMeasurements")
measurement_msg = log.LiveLocationKalman.Measurement.new_message measurement_msg = log.LiveLocationKalman.Measurement.new_message
if fix is not None:
position_estimate, position_std, velocity_estimate, velocity_std = fix
if self.ttff <= 0:
self.ttff = max(1e-3, t - self.first_log_time)
msg_dict["positionECEF"] = measurement_msg(value=position_estimate, std=position_std.tolist(), valid=bool(self.last_fix_t == t))
msg_dict["velocityECEF"] = measurement_msg(value=velocity_estimate, std=velocity_std.tolist(), valid=bool(self.last_fix_t == t))
self.update_localizer(self.last_fix_pos, t, corrected_measurements)
P_diag = self.gnss_kf.P.diagonal() P_diag = self.gnss_kf.P.diagonal()
kf_valid = all(self.kf_valid(t)) kf_valid = all(self.kf_valid(t))
msg.gnssMeasurements = { msg_dict["kalmanPositionECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_POS].tolist(),
"gpsWeek": week,
"gpsTimeOfWeek": tow,
"kalmanPositionECEF": measurement_msg(value=self.gnss_kf.x[GStates.ECEF_POS].tolist(),
std=np.sqrt(P_diag[GStates.ECEF_POS]).tolist(), std=np.sqrt(P_diag[GStates.ECEF_POS]).tolist(),
valid=kf_valid), valid=kf_valid)
"kalmanVelocityECEF": measurement_msg(value=self.gnss_kf.x[GStates.ECEF_VELOCITY].tolist(), msg_dict["kalmanVelocityECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_VELOCITY].tolist(),
std=np.sqrt(P_diag[GStates.ECEF_VELOCITY]).tolist(), std=np.sqrt(P_diag[GStates.ECEF_VELOCITY]).tolist(),
valid=kf_valid), valid=kf_valid)
"positionECEF": measurement_msg(value=position_estimate, std=position_std.tolist(), valid=bool(self.last_fix_t == t)),
"velocityECEF": measurement_msg(value=velocity_estimate, std=velocity_std.tolist(), valid=bool(self.last_fix_t == t)),
"measTime": gnss_mono_time,
"correctedMeasurements": meas_msgs
}
return msg
#elif gnss_msg.which() == 'ionoData':
# TODO: add this, Needed to better correct messages offline. First fix ublox_msg.cc to sent them.
msg_dict['gpsWeek'] = self.last_report_time.week
msg_dict['gpsTimeOfWeek'] = self.last_report_time.tow
msg_dict['timeToFirstFix'] = self.ttff
msg_dict['ephemerisStatuses'] = self.create_ephem_statuses()
out_msg.gnssMeasurements = msg_dict
return out_msg
def update_localizer(self, est_pos, t: float, measurements: List[GNSSMeasurement]): def update_localizer(self, est_pos, t: float, measurements: List[GNSSMeasurement]):
# Check time and outputs are valid # Check time and outputs are valid
@ -247,7 +326,7 @@ class Laikad:
cloudlog.error("Time gap of over 10s detected, gnss kalman reset") cloudlog.error("Time gap of over 10s detected, gnss kalman reset")
elif not valid[2]: elif not valid[2]:
cloudlog.error("Gnss kalman filter state is nan") cloudlog.error("Gnss kalman filter state is nan")
if len(est_pos) > 0: if est_pos is not None and len(est_pos) > 0:
cloudlog.info(f"Reset kalman filter with {est_pos}") cloudlog.info(f"Reset kalman filter with {est_pos}")
self.init_gnss_localizer(est_pos) self.init_gnss_localizer(est_pos)
else: else:
@ -272,7 +351,7 @@ class Laikad:
def fetch_navs(self, t: GPSTime, block): def fetch_navs(self, t: GPSTime, block):
# Download new navs if 1 hour of navs data left # Download new navs if 1 hour of navs data left
if t + SECS_IN_HR not in self.astro_dog.navs_fetched_times and (self.last_fetch_navs_t is None or abs(t - self.last_fetch_navs_t) > SECS_IN_MIN): if t + SECS_IN_HR not in self.astro_dog.navs_fetched_times and (abs(t - self.last_fetch_navs_t) > SECS_IN_MIN):
astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types, self.astro_dog.cache_dir astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types, self.astro_dog.cache_dir
ret = None ret = None
@ -290,7 +369,7 @@ class Laikad:
self.last_fetch_navs_t = ret[2] self.last_fetch_navs_t = ret[2]
else: else:
self.astro_dog.navs, self.astro_dog.navs_fetched_times, self.last_fetch_navs_t = ret self.astro_dog.navs, self.astro_dog.navs_fetched_times, self.last_fetch_navs_t = ret
self.cache_ephemeris(t=t) self.cache_ephemeris()
def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir): def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir):
@ -320,31 +399,8 @@ def create_measurement_msg(meas: GNSSMeasurement):
c.satPos = meas.sat_pos_final.tolist() c.satPos = meas.sat_pos_final.tolist()
c.satVel = meas.sat_vel.tolist() c.satVel = meas.sat_vel.tolist()
c.satVel = meas.sat_vel.tolist() c.satVel = meas.sat_vel.tolist()
ephem = meas.sat_ephemeris
assert ephem is not None
week, time_of_week = -1, -1
if ephem.eph_type == EphemerisType.NAV:
source_type = EphemerisSourceType.nav
elif ephem.eph_type == EphemerisType.QCOM_POLY:
source_type = EphemerisSourceType.qcom
else:
assert ephem.file_epoch is not None
week = ephem.file_epoch.week
time_of_week = ephem.file_epoch.tow
file_src = ephem.file_source
if file_src == 'igu': # example nasa: '2214/igu22144_00.sp3.Z'
source_type = EphemerisSourceType.nasaUltraRapid
elif file_src == 'Sta': # example nasa: '22166/ultra/Stark_1D_22061518.sp3'
source_type = EphemerisSourceType.glonassIacUltraRapid
else:
raise Exception(f"Didn't expect file source {file_src}")
c.ephemerisSource.type = source_type.value
c.ephemerisSource.gpsWeek = week
c.ephemerisSource.gpsTimeOfWeek = int(time_of_week)
return c return c
def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMeasurement]): def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMeasurement]):
ekf_data = defaultdict(list) ekf_data = defaultdict(list)
for m in measurements: for m in measurements:
@ -360,69 +416,29 @@ def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMe
gnss_kf.predict_and_observe(t, kind, data) gnss_kf.predict_and_observe(t, kind, data)
class CacheSerializer(json.JSONEncoder):
def default(self, o):
if isinstance(o, Ephemeris):
return o.to_json()
if isinstance(o, GPSTime):
return o.__dict__
if isinstance(o, np.ndarray):
return o.tolist()
return json.JSONEncoder.default(self, o)
def deserialize_hook(dct):
if 'ephemeris' in dct:
return Ephemeris.from_json(dct)
if 'week' in dct:
return GPSTime(dct['week'], dct['tow'])
return dct
class EphemerisSourceType(IntEnum):
nav = 0
nasaUltraRapid = 1
glonassIacUltraRapid = 2
qcom = 3
def process_msg(laikad, gnss_msg, mono_time, block=False):
# TODO: Understand and use remaining unknown constellations
if gnss_msg.which() == "drMeasurementReport":
if getattr(gnss_msg, gnss_msg.which()).source not in ['glonass', 'gps', 'beidou', 'sbas']:
return None
if getattr(gnss_msg, gnss_msg.which()).gpsWeek > np.iinfo(np.int16).max:
# gpsWeek 65535 is received rarely from quectel, this cannot be
# passed to GnssMeasurements's gpsWeek (Int16)
return None
return laikad.process_gnss_msg(gnss_msg, mono_time, block=block)
def clear_tmp_cache(): def clear_tmp_cache():
if os.path.exists(DOWNLOADS_CACHE_FOLDER): if os.path.exists(DOWNLOADS_CACHE_FOLDER):
shutil.rmtree(DOWNLOADS_CACHE_FOLDER) shutil.rmtree(DOWNLOADS_CACHE_FOLDER)
os.mkdir(DOWNLOADS_CACHE_FOLDER) os.mkdir(DOWNLOADS_CACHE_FOLDER)
def main(sm=None, pm=None, qc=None): def main(sm=None, pm=None):
#clear_tmp_cache() #clear_tmp_cache()
use_qcom = not Params().get_bool("UbloxAvailable", block=True) use_qcom = not Params().get_bool("UbloxAvailable", block=True)
if use_qcom or (qc is not None and qc): if use_qcom:
raw_gnss_socket = "qcomGnss" raw_name = "qcomGnss"
else: else:
raw_gnss_socket = "ubloxGnss" raw_name = "ubloxGnss"
raw_gnss_sock = messaging.sub_sock(raw_name, conflate=False, timeout=1000)
if sm is None: if sm is None:
sm = messaging.SubMaster([raw_gnss_socket, 'clocks']) sm = messaging.SubMaster(['clocks',])
if pm is None: if pm is None:
pm = messaging.PubMaster(['gnssMeasurements']) pm = messaging.PubMaster(['gnssMeasurements'])
# disable until set as main gps source, to better analyze startup time # disable until set as main gps source, to better analyze startup time
use_internet = False #"LAIKAD_NO_INTERNET" not in os.environ use_internet = False # "LAIKAD_NO_INTERNET" not in os.environ
replay = "REPLAY" in os.environ replay = "REPLAY" in os.environ
if replay or "CI" in os.environ: if replay or "CI" in os.environ:
@ -431,22 +447,17 @@ def main(sm=None, pm=None, qc=None):
laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom) laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom)
while True: while True:
sm.update() for in_msg in messaging.drain_sock(raw_gnss_sock):
out_msg = laikad.process_gnss_msg(getattr(in_msg, raw_name), in_msg.logMonoTime, replay)
if sm.updated[raw_gnss_socket]: pm.send('gnssMeasurements', out_msg)
gnss_msg = sm[raw_gnss_socket]
msg = process_msg(laikad, gnss_msg, sm.logMonoTime[raw_gnss_socket], replay)
if msg is None:
# TODO: beautify this, locationd needs a valid message
msg = messaging.new_message("gnssMeasurements")
pm.send('gnssMeasurements', msg)
sm.update(0)
if not laikad.got_first_gnss_msg and sm.updated['clocks']: if not laikad.got_first_gnss_msg and sm.updated['clocks']:
clocks_msg = sm['clocks'] clocks_msg = sm['clocks']
t = GPSTime.from_datetime(datetime.utcfromtimestamp(clocks_msg.wallTimeNanos * 1E-9)) t = GPSTime.from_datetime(datetime.utcfromtimestamp(clocks_msg.wallTimeNanos * 1E-9))
if laikad.auto_fetch_navs: if laikad.auto_fetch_navs:
laikad.fetch_navs(t, block=replay) laikad.fetch_navs(t, block=replay)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

@ -27,7 +27,6 @@ const double MAX_FILTER_REWIND_TIME = 0.8; // s
// They should be replaced with synced time from a real clock // They should be replaced with synced time from a real clock
const double GPS_QUECTEL_SENSOR_TIME_OFFSET = 0.630; // s const double GPS_QUECTEL_SENSOR_TIME_OFFSET = 0.630; // s
const double GPS_UBLOX_SENSOR_TIME_OFFSET = 0.095; // s const double GPS_UBLOX_SENSOR_TIME_OFFSET = 0.095; // s
const float GPS_MUL_FACTOR = 10.0;
const float GPS_POS_STD_THRESHOLD = 50.0; const float GPS_POS_STD_THRESHOLD = 50.0;
const float GPS_VEL_STD_THRESHOLD = 5.0; const float GPS_VEL_STD_THRESHOLD = 5.0;
const float GPS_POS_ERROR_RESET_THRESHOLD = 300.0; const float GPS_POS_ERROR_RESET_THRESHOLD = 300.0;
@ -326,8 +325,14 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector();
VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos;
MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(GPS_MUL_FACTOR * log.getAccuracy(),2) + std::pow(GPS_MUL_FACTOR * log.getVerticalAccuracy(),2)).asDiagonal(); float ecef_pos_std;
MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(GPS_MUL_FACTOR * log.getSpeedAccuracy(), 2)).asDiagonal(); if (ublox_available) {
ecef_pos_std = std::sqrt(std::pow(log.getAccuracy(), 2) + std::pow(log.getVerticalAccuracy(), 2));
} else {
ecef_pos_std = std::sqrt(3 * std::pow(log.getVerticalAccuracy(), 2));
}
MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(this->gps_std_factor * ecef_pos_std, 2)).asDiagonal();
MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(this->gps_std_factor * log.getSpeedAccuracy(), 2)).asDiagonal();
this->unix_timestamp_millis = log.getUnixTimestampMillis(); this->unix_timestamp_millis = log.getUnixTimestampMillis();
double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm(); double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm();
@ -377,14 +382,14 @@ void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements:
// indexed at 0 cause all std values are the same MAE // indexed at 0 cause all std values are the same MAE
auto ecef_pos_std = log.getPositionECEF().getStd()[0]; auto ecef_pos_std = log.getPositionECEF().getStd()[0];
MatrixXdr ecef_pos_R = Vector3d::Constant(pow(GPS_MUL_FACTOR*ecef_pos_std, 2)).asDiagonal(); MatrixXdr ecef_pos_R = Vector3d::Constant(pow(this->gps_std_factor*ecef_pos_std, 2)).asDiagonal();
auto ecef_vel_v = log.getVelocityECEF().getValue(); auto ecef_vel_v = log.getVelocityECEF().getValue();
VectorXd ecef_vel = Vector3d(ecef_vel_v[0], ecef_vel_v[1], ecef_vel_v[2]); VectorXd ecef_vel = Vector3d(ecef_vel_v[0], ecef_vel_v[1], ecef_vel_v[2]);
// indexed at 0 cause all std values are the same MAE // indexed at 0 cause all std values are the same MAE
auto ecef_vel_std = log.getVelocityECEF().getStd()[0]; auto ecef_vel_std = log.getVelocityECEF().getStd()[0];
MatrixXdr ecef_vel_R = Vector3d::Constant(pow(GPS_MUL_FACTOR*ecef_vel_std, 2)).asDiagonal(); MatrixXdr ecef_vel_R = Vector3d::Constant(pow(this->gps_std_factor*ecef_vel_std, 2)).asDiagonal();
double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm(); double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm();
@ -661,8 +666,10 @@ int Localizer::locationd_thread() {
const char* gps_location_socket; const char* gps_location_socket;
if (ublox_available) { if (ublox_available) {
gps_location_socket = "gpsLocationExternal"; gps_location_socket = "gpsLocationExternal";
this->gps_std_factor = 10.0;
} else { } else {
gps_location_socket = "gpsLocation"; gps_location_socket = "gpsLocation";
this->gps_std_factor = 2.0;
} }
const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration",
"carState", "carParams", "accelerometer", "gyroscope"}; "carState", "carParams", "accelerometer", "gyroscope"};

@ -84,4 +84,5 @@ private:
std::map<std::string, double> observation_values_invalid; std::map<std::string, double> observation_values_invalid;
bool standstill = true; bool standstill = true;
int32_t orientation_reset_count = 0; int32_t orientation_reset_count = 0;
float gps_std_factor;
}; };

@ -1,21 +1,51 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import time import time
import unittest import unittest
from collections import defaultdict from cereal import log
import cereal.messaging as messaging
from common.params import Params
from datetime import datetime from datetime import datetime
from unittest import mock from unittest import mock
from unittest.mock import patch #from unittest.mock import patch
from tqdm import tqdm
from common.params import Params
from laika.constants import SECS_IN_DAY from laika.constants import SECS_IN_DAY
from laika.downloader import DownloadFailed from laika.downloader import DownloadFailed
from laika.ephemeris import EphemerisType, GPSEphemeris from laika.ephemeris import EphemerisType
from laika.gps_time import GPSTime from laika.gps_time import GPSTime
from laika.helpers import ConstellationId, TimeRangeHolder from laika.helpers import ConstellationId
from laika.raw_gnss import GNSSMeasurement, read_raw_ublox from laika.raw_gnss import GNSSMeasurement, read_raw_ublox, read_raw_qcom
from selfdrive.locationd.laikad import EPHEMERIS_CACHE, EphemerisSourceType, Laikad, create_measurement_msg from selfdrive.locationd.laikad import EPHEMERIS_CACHE, Laikad
from selfdrive.test.openpilotci import get_url from selfdrive.test.openpilotci import get_url
from tools.lib.logreader import LogReader from tools.lib.logreader import LogReader
from selfdrive.manager.process_config import managed_processes
from selfdrive.test.process_replay.helpers import OpenpilotPrefix
def get_ublox_gnss(ubloxraw):
with OpenpilotPrefix():
managed_processes['ubloxd'].start()
timeout_ms = 30
pm = messaging.PubMaster(['ubloxRaw'])
sock = messaging.sub_sock('ubloxGnss', timeout=timeout_ms)
log_msgs = []
log_t = []
for x in tqdm(ubloxraw):
pm.send(x.which(), x.as_builder())
ret = messaging.recv_one(sock)
if ret is not None:
msg = log.Event.new_message(ubloxGnss=ret.ubloxGnss.to_dict())
msg.logMonoTime = x.logMonoTime
log_msgs.append(msg)
log_t.append(1e-9 * x.logMonoTime)
assert managed_processes['ubloxd'].get_process_state_msg().running
assert len(log_msgs) > 1 or len(ubloxraw) == 0
managed_processes['ubloxd'].stop()
return log_t, log_msgs
def get_log(segs=range(0)): def get_log(segs=range(0)):
@ -23,7 +53,8 @@ def get_log(segs=range(0)):
for i in segs: for i in segs:
logs.extend(LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", i))) logs.extend(LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", i)))
all_logs = [m for m in logs if m.which() == 'ubloxGnss'] raw_logs = [m for m in logs if m.which() == 'ubloxRaw']
all_logs = get_ublox_gnss(raw_logs)[1]
low_gnss = [] low_gnss = []
for m in all_logs: for m in all_logs:
if m.ubloxGnss.which() != 'measurementReport': if m.ubloxGnss.which() != 'measurementReport':
@ -31,21 +62,31 @@ def get_log(segs=range(0)):
MAX_MEAS = 7 MAX_MEAS = 7
if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS: if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS:
mb = m.as_builder() mb = log.Event.new_message(ubloxGnss=m.ubloxGnss.to_dict())
mb.logMonoTime = m.logMonoTime
mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS
mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS] mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS]
mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000 mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000
low_gnss.append(mb.as_reader()) low_gnss.append(mb.as_reader())
else: else:
low_gnss.append(m) low_gnss.append(m)
return all_logs, low_gnss return all_logs, low_gnss
def get_log_qcom(segs=range(0)):
logs = []
for i in segs:
logs.extend(LogReader(get_url("b0b3cba7abf862d1|2023-03-11--09-40-33", i)))
all_logs = [m for m in logs if m.which() == 'qcomGnss']
return all_logs
def verify_messages(lr, laikad, return_one_success=False): def verify_messages(lr, laikad, return_one_success=False):
good_msgs = [] good_msgs = []
for m in lr: for m in lr:
msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) if m.which() == 'ubloxGnss':
gnss_msg = m.ubloxGnss
else:
gnss_msg = m.qcomGnss
msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=True)
if msg is not None and len(msg.gnssMeasurements.correctedMeasurements) > 0: if msg is not None and len(msg.gnssMeasurements.correctedMeasurements) > 0:
good_msgs.append(msg) good_msgs.append(msg)
if return_one_success: if return_one_success:
@ -55,10 +96,16 @@ def verify_messages(lr, laikad, return_one_success=False):
def get_first_gps_time(logs): def get_first_gps_time(logs):
for m in logs: for m in logs:
if m.ubloxGnss.which == 'measurementReport': if m.which() == 'ubloxGnss':
new_meas = read_raw_ublox(m.ubloxGnss.measurementReport) if m.ubloxGnss.which == 'measurementReport':
if len(new_meas) > 0: new_meas = read_raw_ublox(m.ubloxGnss.measurementReport)
return new_meas[0].recv_time if len(new_meas) > 0:
return new_meas[0].recv_time
else:
if m.qcomGnss.which == 'measurementReport':
new_meas = read_raw_qcom(m.qcomGnss.measurementReport)
if len(new_meas) > 0:
return new_meas[0].recv_time
def get_measurement_mock(gpstime, sat_ephemeris): def get_measurement_mock(gpstime, sat_ephemeris):
@ -79,6 +126,7 @@ class TestLaikad(unittest.TestCase):
logs, low_gnss = get_log(range(1)) logs, low_gnss = get_log(range(1))
cls.logs = logs cls.logs = logs
cls.low_gnss = low_gnss cls.low_gnss = low_gnss
cls.logs_qcom = get_log_qcom(range(1))
first_gps_time = get_first_gps_time(logs) first_gps_time = get_first_gps_time(logs)
cls.first_gps_time = first_gps_time cls.first_gps_time = first_gps_time
@ -90,9 +138,9 @@ class TestLaikad(unittest.TestCase):
laikad = Laikad() laikad = Laikad()
laikad.fetch_navs(gpstime, block=False) laikad.fetch_navs(gpstime, block=False)
laikad.orbit_fetch_future.result(30) laikad.orbit_fetch_future.result(30)
# Get results and save orbits to laikad: # Get results and save orbits to laikad:
laikad.fetch_navs(gpstime, block=False) laikad.fetch_navs(gpstime, block=False)
ephem = laikad.astro_dog.navs['G01'][0] ephem = laikad.astro_dog.navs['G01'][0]
self.assertIsNotNone(ephem) self.assertIsNotNone(ephem)
@ -105,6 +153,7 @@ class TestLaikad(unittest.TestCase):
self.assertIsNotNone(ephem) self.assertIsNotNone(ephem)
self.assertNotEqual(ephem, ephem2) self.assertNotEqual(ephem, ephem2)
def test_fetch_navs_with_wrong_clocks(self): def test_fetch_navs_with_wrong_clocks(self):
laikad = Laikad() laikad = Laikad()
@ -127,40 +176,11 @@ class TestLaikad(unittest.TestCase):
check_has_navs() check_has_navs()
self.assertEqual(laikad.last_fetch_navs_t, real_current_time) self.assertEqual(laikad.last_fetch_navs_t, real_current_time)
def test_ephemeris_source_in_msg(self):
data_mock = defaultdict(str)
data_mock['sv_id'] = 1
gpstime = GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC
laikad = Laikad()
laikad.fetch_navs(gpstime, block=True)
meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['R01'][0])
msg = create_measurement_msg(meas)
self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav)
# Verify gps satellite returns same source
meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['R01'][0])
msg = create_measurement_msg(meas)
self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav)
# Test nasa source by using older date
gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1))
laikad = Laikad()
laikad.fetch_navs(gpstime, block=True)
meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['G01'][0])
msg = create_measurement_msg(meas)
self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav)
# Test nav source type
ephem = GPSEphemeris(data_mock, gpstime)
meas = get_measurement_mock(gpstime, ephem)
msg = create_measurement_msg(meas)
self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav)
def test_laika_online(self): def test_laika_online(self):
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 = 559 correct_msgs_expected = 560
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]))
@ -177,12 +197,15 @@ class TestLaikad(unittest.TestCase):
self.assertTrue(kf_valid) self.assertTrue(kf_valid)
def test_laika_online_nav_only(self): def test_laika_online_nav_only(self):
laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV) for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs]):
# Disable fetch_orbits to test NAV only laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV, use_qcom=use_qcom)
correct_msgs = verify_messages(self.logs, laikad) # Disable fetch_orbits to test NAV only
correct_msgs_expected = 559 correct_msgs = verify_messages(logs, laikad)
self.assertEqual(correct_msgs_expected, len(correct_msgs)) correct_msgs_expected = 44 if use_qcom else 560
self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) valid_fix_expected = 43 if use_qcom else 560
self.assertEqual(correct_msgs_expected, len(correct_msgs))
self.assertEqual(valid_fix_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid]))
@mock.patch('laika.downloader.download_and_cache_file') @mock.patch('laika.downloader.download_and_cache_file')
def test_laika_offline(self, downloader_mock): def test_laika_offline(self, downloader_mock):
@ -195,8 +218,9 @@ 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(255, len(correct_msgs)) expected_msgs = 376
self.assertEqual(255, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) self.assertEqual(expected_msgs, len(correct_msgs))
self.assertEqual(expected_msgs, 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)
@ -212,65 +236,87 @@ class TestLaikad(unittest.TestCase):
self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) self.assertGreater(len(laikad.astro_dog.navs[prn]), 0)
prn = "R01" prn = "R01"
self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) self.assertGreater(len(laikad.astro_dog.navs[prn]), 0)
print(min(laikad.astro_dog.navs[prn], key=lambda e: e.epoch).epoch.as_datetime())
def test_get_navs_in_process(self): def test_get_navs_in_process(self):
laikad = Laikad(auto_update=False) for auto_fetch_navs in [True, False]:
has_navs = False for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs]):
for m in self.logs: laikad = Laikad(auto_update=False, use_qcom=use_qcom, auto_fetch_navs=auto_fetch_navs)
laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=False) has_navs = False
if laikad.orbit_fetch_future is not None: has_fix = False
laikad.orbit_fetch_future.result() seen_chip_eph = False
vals = laikad.astro_dog.navs.values() seen_internet_eph = False
has_navs = len(vals) > 0 and max([len(v) for v in vals]) > 0
if has_navs: for m in logs:
break gnss_msg = m.qcomGnss if use_qcom else m.ubloxGnss
self.assertTrue(has_navs) out_msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=False)
self.assertGreater(len(laikad.astro_dog.navs_fetched_times._ranges), 0) if laikad.orbit_fetch_future is not None:
self.assertEqual(None, laikad.orbit_fetch_future) laikad.orbit_fetch_future.result()
vals = laikad.astro_dog.navs.values()
has_navs = len(vals) > 0 and max([len(v) for v in vals]) > 0
vals = laikad.astro_dog.qcom_polys.values()
has_polys = len(vals) > 0 and max([len(v) for v in vals]) > 0
has_fix = has_fix or out_msg.gnssMeasurements.positionECEF.valid
if len(out_msg.gnssMeasurements.ephemerisStatuses):
seen_chip_eph = seen_chip_eph or any([x.source == 'gnssChip' for x in out_msg.gnssMeasurements.ephemerisStatuses])
seen_internet_eph = seen_internet_eph or any([x.source == 'internet' for x in out_msg.gnssMeasurements.ephemerisStatuses])
self.assertTrue(has_navs or has_polys)
self.assertTrue(has_fix)
self.assertTrue(seen_chip_eph or auto_fetch_navs)
self.assertTrue(seen_internet_eph or not auto_fetch_navs)
self.assertEqual(len(laikad.astro_dog.navs_fetched_times._ranges), 0)
self.assertEqual(None, laikad.orbit_fetch_future)
def test_cache(self): def test_cache(self):
laikad = Laikad(auto_update=True, save_ephemeris=True) use_qcom = True
for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs]):
def wait_for_cache(): laikad = Laikad(auto_update=True, save_ephemeris=True, use_qcom=use_qcom)
max_time = 2 def wait_for_cache():
while Params().get(EPHEMERIS_CACHE) is None: max_time = 2
time.sleep(0.1) while Params().get(EPHEMERIS_CACHE) is None:
max_time -= 0.1 time.sleep(0.1)
if max_time < 0: max_time -= 0.1
self.fail("Cache has not been written after 2 seconds") if max_time < 0:
self.fail("Cache has not been written after 2 seconds")
# Test cache with no ephemeris
laikad.cache_ephemeris(t=GPSTime(0, 0)) # Test cache with no ephemeris
wait_for_cache() laikad.last_report_time = GPSTime(1,0)
Params().remove(EPHEMERIS_CACHE) laikad.cache_ephemeris()
wait_for_cache()
Params().remove(EPHEMERIS_CACHE)
#laikad.astro_dog.get_navs(self.first_gps_time)
msg = verify_messages(logs, laikad, return_one_success=True)
laikad.cache_ephemeris()
wait_for_cache()
# Check both nav and orbits separate
laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.NAV,
save_ephemeris=True, use_qcom=use_qcom, auto_fetch_navs=False)
# Verify navs are loaded from cache
self.dict_has_values(laikad.astro_dog.navs)
# Verify cache is working for only nav by running a segment
msg = verify_messages(logs, laikad, return_one_success=True)
self.assertTrue(len(msg.gnssMeasurements.ephemerisStatuses))
self.assertTrue(any([x.source=='cache' for x in msg.gnssMeasurements.ephemerisStatuses]))
self.assertIsNotNone(msg)
#laikad.astro_dog.get_navs(self.first_gps_time)
laikad.fetch_navs(self.first_gps_time, block=True)
# Wait for cache to save
wait_for_cache()
# Check both nav and orbits separate #TODO test cache with only orbits
laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.NAV, save_ephemeris=True) #with patch('selfdrive.locationd.laikad.get_orbit_data', return_value=None) as mock_method:
# Verify navs are loaded from cache # # Verify no orbit downloads even if orbit fetch times is reset since the cache has recently been saved and we don't want to download high frequently
self.dict_has_values(laikad.astro_dog.navs) # laikad.astro_dog.orbit_fetched_times = TimeRangeHolder()
# Verify cache is working for only nav by running a segment # laikad.fetch_navs(self.first_gps_time, block=False)
msg = verify_messages(self.logs, laikad, return_one_success=True) # mock_method.assert_not_called()
self.assertIsNotNone(msg)
# # Verify cache is working for only orbits by running a segment
with patch('selfdrive.locationd.laikad.get_orbit_data', return_value=None) as mock_method: # laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT, save_ephemeris=True)
# Verify no orbit downloads even if orbit fetch times is reset since the cache has recently been saved and we don't want to download high frequently # msg = verify_messages(self.logs, laikad, return_one_success=True)
laikad.astro_dog.orbit_fetched_times = TimeRangeHolder() # self.assertIsNotNone(msg)
laikad.fetch_navs(self.first_gps_time, block=False) # # Verify orbit data is not downloaded
mock_method.assert_not_called() # mock_method.assert_not_called()
#break
# Verify cache is working for only orbits by running a segment
laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT, save_ephemeris=True)
msg = verify_messages(self.logs, laikad, return_one_success=True)
self.assertIsNotNone(msg)
# Verify orbit data is not downloaded
mock_method.assert_not_called()
def test_low_gnss_meas(self): def test_low_gnss_meas(self):
cnt = 0 cnt = 0
@ -282,12 +328,10 @@ 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, 559) self.assertEqual(cnt, 560)
def dict_has_values(self, dct): def dict_has_values(self, dct):
self.assertGreater(len(dct), 0) self.assertGreater(len(dct), 0)
self.assertGreater(min([len(v) for v in dct.values()]), 0) self.assertGreater(min([len(v) for v in dct.values()]), 0)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -153,7 +153,7 @@ def manager_thread() -> None:
for param in ("DoUninstall", "DoShutdown", "DoReboot"): for param in ("DoUninstall", "DoShutdown", "DoReboot"):
if params.get_bool(param): if params.get_bool(param):
shutdown = True shutdown = True
params.put("LastManagerExitReason", param) params.put("LastManagerExitReason", f"{param} {datetime.datetime.now()}")
cloudlog.warning(f"Shutting down manager - {param} set") cloudlog.warning(f"Shutting down manager - {param} set")
if shutdown: if shutdown:

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:736ddc08497d7596bae4d9515a8efb996676be80e67a6d34d632bb8af2ed3fa9 oid sha256:5121deb0d5c683b0fbee4c1cad7bc625953bf127b1383fb7599a6b644efd0aea
size 45962515 size 46011200

@ -1 +1 @@
ab64afd1abd1059c14f50c67b51e5ef89029f216 82db08d52b155336e9a1dadd11485d5acdf2eba0

@ -5,7 +5,8 @@ import sys
import threading import threading
import time import time
import signal import signal
from collections import namedtuple from dataclasses import dataclass, field
from typing import Dict, List, Optional, Callable
import capnp import capnp
@ -28,7 +29,20 @@ TIMEOUT = 15
PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__)) PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__))
FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/")
ProcessConfig = namedtuple('ProcessConfig', ['proc_name', 'pub_sub', 'ignore', 'init_callback', 'should_recv_callback', 'tolerance', 'fake_pubsubmaster', 'submaster_config', 'environ', 'subtest_name', "field_tolerances"], defaults=({}, {}, "", {})) @dataclass
class ProcessConfig:
proc_name: str
pub_sub: Dict[str, List[str]]
ignore: List[str]
init_callback: Optional[Callable]
should_recv_callback: Optional[Callable]
tolerance: Optional[float]
fake_pubsubmaster: bool
submaster_config: Dict[str, List[str]] = field(default_factory=dict)
environ: Dict[str, str] = field(default_factory=dict)
subtest_name: str = ""
field_tolerances: Dict[str, float] = field(default_factory=dict)
timeout: int = 30
def wait_for_event(evt): def wait_for_event(evt):
@ -238,21 +252,12 @@ def torqued_rcv_callback(msg, CP, cfg, fsm):
return recv_socks, fsm.frame == 0 or msg.which() == 'liveLocationKalman' return recv_socks, fsm.frame == 0 or msg.which() == 'liveLocationKalman'
def ublox_rcv_callback(msg): def ublox_rcv_callback(msg, CP, cfg, fsm):
msg_class, msg_id = msg.ubloxRaw[2:4] msg_class, msg_id = msg.ubloxRaw[2:4]
if (msg_class, msg_id) in {(1, 7 * 16)}: if (msg_class, msg_id) in {(1, 7 * 16)}:
return ["gpsLocationExternal"] return ["gpsLocationExternal"], True
elif (msg_class, msg_id) in {(2, 1 * 16 + 5), (10, 9)}: elif (msg_class, msg_id) in {(2, 1 * 16 + 5), (10, 9)}:
return ["ubloxGnss"] return ["ubloxGnss"], True
else:
return []
def laika_rcv_callback(msg, CP, cfg, fsm):
if msg.which() == 'ubloxGnss' and msg.ubloxGnss.which() == "measurementReport":
return ["gnssMeasurements"], True
elif msg.which() == 'qcomGnss' and msg.qcomGnss.which() == "drMeasurementReport":
return ["gnssMeasurements"], True
else: else:
return [], False return [], False
@ -331,7 +336,7 @@ CONFIGS = [
pub_sub={ pub_sub={
"cameraOdometry": ["liveLocationKalman"], "cameraOdometry": ["liveLocationKalman"],
"accelerometer": [], "gyroscope": [], "accelerometer": [], "gyroscope": [],
"gpsLocationExternal": [], "liveCalibration": [], "carState": [], "gpsLocationExternal": [], "liveCalibration": [], "carState": [], "gpsLocation": [],
}, },
ignore=["logMonoTime", "valid"], ignore=["logMonoTime", "valid"],
init_callback=get_car_params, init_callback=get_car_params,
@ -371,9 +376,10 @@ CONFIGS = [
}, },
ignore=["logMonoTime"], ignore=["logMonoTime"],
init_callback=get_car_params, init_callback=get_car_params,
should_recv_callback=laika_rcv_callback, should_recv_callback=None,
tolerance=NUMPY_TOLERANCE, tolerance=NUMPY_TOLERANCE,
fake_pubsubmaster=True, fake_pubsubmaster=False,
timeout=60*10, # first messages are blocked on internet assistance
), ),
ProcessConfig( ProcessConfig(
proc_name="torqued", proc_name="torqued",
@ -395,10 +401,10 @@ def replay_process(cfg, lr, fingerprint=None):
if cfg.fake_pubsubmaster: if cfg.fake_pubsubmaster:
return python_replay_process(cfg, lr, fingerprint) return python_replay_process(cfg, lr, fingerprint)
else: else:
return cpp_replay_process(cfg, lr, fingerprint) return replay_process_with_sockets(cfg, lr, fingerprint)
def setup_env(simulation=False, CP=None, cfg=None, controlsState=None): def setup_env(simulation=False, CP=None, cfg=None, controlsState=None, lr=None):
params = Params() params = Params()
params.clear_all() params.clear_all()
params.put_bool("OpenpilotEnabledToggle", True) params.put_bool("OpenpilotEnabledToggle", True)
@ -406,13 +412,19 @@ def setup_env(simulation=False, CP=None, cfg=None, controlsState=None):
params.put_bool("DisengageOnAccelerator", True) params.put_bool("DisengageOnAccelerator", True)
params.put_bool("WideCameraOnly", False) params.put_bool("WideCameraOnly", False)
params.put_bool("DisableLogging", False) params.put_bool("DisableLogging", False)
params.put_bool("UbloxAvailable", True)
params.put_bool("ObdMultiplexingDisabled", True)
os.environ["NO_RADAR_SLEEP"] = "1" os.environ["NO_RADAR_SLEEP"] = "1"
os.environ["REPLAY"] = "1" os.environ["REPLAY"] = "1"
os.environ['SKIP_FW_QUERY'] = "" os.environ["SKIP_FW_QUERY"] = ""
os.environ['FINGERPRINT'] = "" os.environ["FINGERPRINT"] = ""
if lr is not None:
services = {m.which() for m in lr}
params.put_bool("UbloxAvailable", "ubloxGnss" in services)
if lr is not None:
services = {m.which() for m in lr}
params.put_bool("UbloxAvailable", "ubloxGnss" in services)
if cfg is not None: if cfg is not None:
# Clear all custom processConfig environment variables # Clear all custom processConfig environment variables
@ -464,12 +476,6 @@ def python_replay_process(cfg, lr, fingerprint=None):
all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime)
pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())] pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())]
# laikad needs decision between submaster ubloxGnss and qcomGnss, prio given to ubloxGnss
if cfg.proc_name == "laikad":
args = (*args, not any(m.which() == "ubloxGnss" for m in pub_msgs))
service = "qcomGnss" if args[2] else "ubloxGnss"
pub_msgs = [m for m in pub_msgs if m.which() == service or m.which() == 'clocks']
controlsState = None controlsState = None
initialized = False initialized = False
for msg in lr: for msg in lr:
@ -485,10 +491,10 @@ def python_replay_process(cfg, lr, fingerprint=None):
if fingerprint is not None: if fingerprint is not None:
os.environ['SKIP_FW_QUERY'] = "1" os.environ['SKIP_FW_QUERY'] = "1"
os.environ['FINGERPRINT'] = fingerprint os.environ['FINGERPRINT'] = fingerprint
setup_env(cfg=cfg, controlsState=controlsState) setup_env(cfg=cfg, controlsState=controlsState, lr=lr)
else: else:
CP = [m for m in lr if m.which() == 'carParams'][0].carParams CP = [m for m in lr if m.which() == 'carParams'][0].carParams
setup_env(CP=CP, cfg=cfg, controlsState=controlsState) setup_env(CP=CP, cfg=cfg, controlsState=controlsState, lr=lr)
assert(type(managed_processes[cfg.proc_name]) is PythonProcess) assert(type(managed_processes[cfg.proc_name]) is PythonProcess)
managed_processes[cfg.proc_name].prepare() managed_processes[cfg.proc_name].prepare()
@ -517,7 +523,7 @@ def python_replay_process(cfg, lr, fingerprint=None):
recv_socks, should_recv = cfg.should_recv_callback(msg, CP, cfg, fsm) recv_socks, should_recv = cfg.should_recv_callback(msg, CP, cfg, fsm)
else: else:
recv_socks = [s for s in cfg.pub_sub[msg.which()] if recv_socks = [s for s in cfg.pub_sub[msg.which()] if
(fsm.frame + 1) % int(service_list[msg.which()].frequency / service_list[s].frequency) == 0] (fsm.frame + 1) % max(1, int(service_list[msg.which()].frequency / service_list[s].frequency)) == 0]
should_recv = bool(len(recv_socks)) should_recv = bool(len(recv_socks))
if msg.which() == 'can': if msg.which() == 'can':
@ -540,49 +546,58 @@ def python_replay_process(cfg, lr, fingerprint=None):
return log_msgs return log_msgs
def cpp_replay_process(cfg, lr, fingerprint=None): def replay_process_with_sockets(cfg, lr, fingerprint=None):
sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub] # We get responses here
pm = messaging.PubMaster(cfg.pub_sub.keys()) pm = messaging.PubMaster(cfg.pub_sub.keys())
sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub]
sockets = {s: messaging.sub_sock(s, timeout=100) for s in sub_sockets}
all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime)
pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())] pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())]
log_msgs = []
# We need to fake SubMaster alive since we can't inject a fake clock # We need to fake SubMaster alive since we can't inject a fake clock
setup_env(simulation=True, cfg=cfg) setup_env(simulation=True, cfg=cfg, lr=lr)
if cfg.proc_name == "laikad":
ublox = Params().get_bool("UbloxAvailable")
keys = set(cfg.pub_sub.keys()) - ({"qcomGnss", } if ublox else {"ubloxGnss", })
pub_msgs = [msg for msg in pub_msgs if msg.which() in keys]
managed_processes[cfg.proc_name].prepare() managed_processes[cfg.proc_name].prepare()
managed_processes[cfg.proc_name].start() managed_processes[cfg.proc_name].start()
log_msgs = []
try: try:
with Timeout(TIMEOUT, error_msg=f"timed out testing process {repr(cfg.proc_name)}"): # Wait for process to startup
while not all(pm.all_readers_updated(s) for s in cfg.pub_sub.keys()): with Timeout(10, error_msg=f"timed out waiting for process to start: {repr(cfg.proc_name)}"):
while not any(pm.all_readers_updated(s) for s in cfg.pub_sub.keys()):
time.sleep(0) time.sleep(0)
# Make sure all subscribers are connected for s in sockets.values():
sockets = {s: messaging.sub_sock(s, timeout=2000) for s in sub_sockets} messaging.recv_one_or_none(s)
for s in sub_sockets:
messaging.recv_one_or_none(sockets[s])
for i, msg in enumerate(pub_msgs): # Do the replay
pm.send(msg.which(), msg.as_builder()) cnt = 0
for msg in pub_msgs:
resp_sockets = cfg.pub_sub[msg.which()] if cfg.should_recv_callback is None else cfg.should_recv_callback(msg) with Timeout(cfg.timeout, error_msg=f"timed out testing process {repr(cfg.proc_name)}, {cnt}/{len(pub_msgs)} msgs done"):
for s in resp_sockets: resp_sockets = cfg.pub_sub[msg.which()]
response = messaging.recv_one_retry(sockets[s]) if cfg.should_recv_callback is not None:
resp_sockets, _ = cfg.should_recv_callback(msg, None, None, None)
if response is None: # Make sure all subscribers are connected
print(f"Warning, no response received {i}") if len(log_msgs) == 0 and len(resp_sockets) > 0:
else: for s in sockets.values():
messaging.recv_one_or_none(s)
response = response.as_builder() pm.send(msg.which(), msg.as_builder())
response.logMonoTime = msg.logMonoTime while not pm.all_readers_updated(msg.which()):
response = response.as_reader() time.sleep(0)
log_msgs.append(response)
if not len(resp_sockets): # We only need to wait if we didn't already wait for a response for s in resp_sockets:
while not pm.all_readers_updated(msg.which()): m = messaging.recv_one_retry(sockets[s])
time.sleep(0) m = m.as_builder()
m.logMonoTime = msg.logMonoTime
log_msgs.append(m.as_reader())
cnt += 1
finally: finally:
managed_processes[cfg.proc_name].signal(signal.SIGKILL) managed_processes[cfg.proc_name].signal(signal.SIGKILL)
managed_processes[cfg.proc_name].stop() managed_processes[cfg.proc_name].stop()

@ -1 +1 @@
3c5ebb007f76ba0de710ff7a8cf5910ad2edf22f 1bb3f665191e1b75c1b786f60e76d51b274f98ae

@ -18,7 +18,7 @@ from tools.lib.logreader import LogReader
source_segments = [ source_segments = [
("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY ("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY
("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA
("HYUNDAI2", "d545129f3ca90f28|2022-11-07--20-43-08--3"), # HYUNDAI.KIA_EV6 ("HYUNDAI2", "d545129f3ca90f28|2022-11-07--20-43-08--3"), # HYUNDAI.KIA_EV6 (+ QCOM GPS)
("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI) ("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI)
("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR) ("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR)
("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2 ("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2
@ -70,7 +70,7 @@ def run_test_process(data):
res = None res = None
if not args.upload_only: if not args.upload_only:
lr = LogReader.from_bytes(lr_dat) lr = LogReader.from_bytes(lr_dat)
res, log_msgs = test_process(cfg, lr, ref_log_path, cur_log_fn, args.ignore_fields, args.ignore_msgs) res, log_msgs = test_process(cfg, lr, segment, ref_log_path, cur_log_fn, args.ignore_fields, args.ignore_msgs)
# save logs so we can upload when updating refs # save logs so we can upload when updating refs
save_log(cur_log_fn, log_msgs) save_log(cur_log_fn, log_msgs)
@ -88,7 +88,7 @@ def get_log_data(segment):
return (segment, f.read()) return (segment, f.read())
def test_process(cfg, lr, ref_log_path, new_log_path, ignore_fields=None, ignore_msgs=None): def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=None, ignore_msgs=None):
if ignore_fields is None: if ignore_fields is None:
ignore_fields = [] ignore_fields = []
if ignore_msgs is None: if ignore_msgs is None:
@ -96,7 +96,10 @@ def test_process(cfg, lr, ref_log_path, new_log_path, ignore_fields=None, ignore
ref_log_msgs = list(LogReader(ref_log_path)) ref_log_msgs = list(LogReader(ref_log_path))
log_msgs = replay_process(cfg, lr) try:
log_msgs = replay_process(cfg, lr)
except Exception as e:
raise Exception("failed on segment: " + segment) from e
# check to make sure openpilot is engaged in the route # check to make sure openpilot is engaged in the route
if cfg.proc_name == "controlsd": if cfg.proc_name == "controlsd":

@ -43,10 +43,10 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
}, },
{ {
"ExperimentalLongitudinalEnabled", "ExperimentalLongitudinalEnabled",
tr("Experimental openpilot Longitudinal Control"), tr("openpilot Longitudinal Control (Alpha)"),
QString("<b>%1</b><br>%2") QString("<b>%1</b><br><br>%2")
.arg(tr("WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).")) .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB)."))
.arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.")), .arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")),
"../assets/offroad/icon_speed_limit.png", "../assets/offroad/icon_speed_limit.png",
}, },
{ {
@ -193,6 +193,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
connect(resetCalibBtn, &ButtonControl::clicked, [&]() { connect(resetCalibBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) {
params.remove("CalibrationParams"); params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
} }
}); });
addItem(resetCalibBtn); addItem(resetCalibBtn);

@ -374,7 +374,6 @@ void CameraWidget::vipcThread() {
clearFrames(); clearFrames();
auto streams = VisionIpcClient::getAvailableStreams(stream_name, false); auto streams = VisionIpcClient::getAvailableStreams(stream_name, false);
if (streams.empty()) { if (streams.empty()) {
qWarning() << "VisionIPC connected, but no streams available";
QThread::msleep(100); QThread::msleep(100);
continue; continue;
} }

@ -992,22 +992,10 @@ This may take up to a minute.</source>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation>Experimenteller Modus</translation> <translation>Experimenteller Modus</translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>Experimenteller Openpilot Tempomat</translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>WARNUNG: Der Openpilot Tempomat ist für dieses Auto experimentell und deaktiviert den Notbremsassistenten.</translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation>Bei Gasbetätigung ausschalten</translation> <translation>Bei Gasbetätigung ausschalten</translation>
</message> </message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation>Bei diesem auto wird standardmäßig der im Auto eingebaute adaptive Tempomat anstelle des Openpilot Tempomats benutzt. Aktiviere diesen Schalter, um zum Openpilot Tempomaten zu wechseln. Es ist empfohlen den Experimentellen Modus bei Nutzung des Openpilot Tempomats zu aktivieren.</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>Openpilot fährt standardmäßig im &lt;b&gt;entspannten Modus&lt;/b&gt;. Der Experimentelle Modus aktiviert&lt;b&gt;Alpha-level Funktionen&lt;/b&gt;, die noch nicht für den entspannten Modus bereit sind. Die experimentellen Funktionen sind die Folgenden:</translation> <translation>Openpilot fährt standardmäßig im &lt;b&gt;entspannten Modus&lt;/b&gt;. Der Experimentelle Modus aktiviert&lt;b&gt;Alpha-level Funktionen&lt;/b&gt;, die noch nicht für den entspannten Modus bereit sind. Die experimentellen Funktionen sind die Folgenden:</translation>
@ -1044,6 +1032,18 @@ This may take up to a minute.</source>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation>Aktiviere den experimentellen Openpilot Tempomaten für experimentelle Funktionen.</translation> <translation>Aktiviere den experimentellen Openpilot Tempomaten für experimentelle Funktionen.</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -962,10 +962,6 @@ This may take up to a minute.</source>
<source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source> <source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>openpilotによるアクセル制御</translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation> openpilot </translation> <translation> openpilot </translation>
@ -994,14 +990,6 @@ This may take up to a minute.</source>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>警告: この車種でのopenpilotによるアクセル制御は実験段階であり(AEB)</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation>openpilotはこの車の場合ACCを標準で利用しますopenpilotによるアクセル制御を利用できます</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>openpilotは標準ではゆっくりとくつろげる運転を提供します</translation> <translation>openpilotは標準ではゆっくりとくつろげる運転を提供します</translation>
@ -1038,6 +1026,18 @@ This may take up to a minute.</source>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation>openpilotによるアクセル制御を有効にしてください</translation> <translation>openpilotによるアクセル制御を有効にしてください</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -963,10 +963,6 @@ This may take up to a minute.</source>
<source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source> <source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source>
<translation> .</translation> <translation> .</translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>openpilot ()</translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation> </translation> <translation> </translation>
@ -995,14 +991,6 @@ This may take up to a minute.</source>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation> </translation> <translation> </translation>
</message> </message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>경고: openpilot (AEB) .</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation> openpilot ACC로 . openpilot . openpilot .</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>openpilot은 &lt;b&gt; &lt;/b&gt; . &lt;b&gt; &lt;/b&gt; . </translation> <translation>openpilot은 &lt;b&gt; &lt;/b&gt; . &lt;b&gt; &lt;/b&gt; . </translation>
@ -1039,6 +1027,18 @@ This may take up to a minute.</source>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation> .</translation> <translation> .</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation>openpilot ()</translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>경고: openpilot (AEB) .</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation> openpilot ACC로 . openpilot . openpilot .</translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -967,10 +967,6 @@ Isso pode levar até um minuto.</translation>
<source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source> <source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source>
<translation>Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor.</translation> <translation>Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor.</translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>Controle longitudinal experimental openpilot</translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation>Desacionar com Pedal do Acelerador</translation> <translation>Desacionar com Pedal do Acelerador</translation>
@ -999,14 +995,6 @@ Isso pode levar até um minuto.</translation>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation>Modo Experimental</translation> <translation>Modo Experimental</translation>
</message> </message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>ATENÇÃO: o controle longitudinal do openpilot é experimental para este carro e desativará a Frenagem Automática de Emergência (AEB).</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation>Neste carro o penpilot por padrão utiliza o ACC nativo do veículo ao invés de controlar longitudinalmente. Ative isto para mudar para o controle longitudinal do openpilot. Ativar o Modo Experimental é recomendado quando em uso do controle longitudinal experimental do openpilot.</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>openpilot por padrão funciona em &lt;b&gt;modo chill&lt;/b&gt;. modo Experimental ativa &lt;b&gt;recursos de nível-alfa&lt;/b&gt; que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo:</translation> <translation>openpilot por padrão funciona em &lt;b&gt;modo chill&lt;/b&gt;. modo Experimental ativa &lt;b&gt;recursos de nível-alfa&lt;/b&gt; que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo:</translation>
@ -1043,6 +1031,18 @@ Isso pode levar até um minuto.</translation>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation>Ative o controle longitudinal experimental para permitir o modo Experimental.</translation> <translation>Ative o controle longitudinal experimental para permitir o modo Experimental.</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation>Controle Longitudinal openpilot (Alpha)</translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>AVISO: o controle longitudinal openpilot está em alfa para este carro e desativará a Frenagem Automática de Emergência (AEB).</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation>Neste carro, o openpilot tem como padrão o ACC embutido do carro em vez do controle longitudinal do openpilot. Habilite isso para alternar para o controle longitudinal openpilot. Recomenda-se ativar o modo Experimental ao ativar o alfa de controle longitudinal openpilot.</translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -960,10 +960,6 @@ This may take up to a minute.</source>
<source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source> <source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>openpilot纵向控制</translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation></translation> <translation></translation>
@ -992,14 +988,6 @@ This may take up to a minute.</source>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation> openpilot纵向控制是试验性功能AEB自动刹车功能</translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation>openpilot默认使用车辆自带的ACCopenpilot的纵向控制openpilot纵向控制使openpilot纵向控制时</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>openpilot &lt;b&gt;&lt;/b&gt; &lt;b&gt;&lt;/b&gt;</translation> <translation>openpilot &lt;b&gt;&lt;/b&gt; &lt;b&gt;&lt;/b&gt;</translation>
@ -1036,6 +1024,18 @@ This may take up to a minute.</source>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation>便使</translation> <translation>便使</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -962,10 +962,6 @@ This may take up to a minute.</source>
<source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source> <source>Upload data from the driver facing camera and help improve the driver monitoring algorithm.</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Experimental openpilot Longitudinal Control</source>
<translation>使 openpilot </translation>
</message>
<message> <message>
<source>Disengage on Accelerator Pedal</source> <source>Disengage on Accelerator Pedal</source>
<translation></translation> <translation></translation>
@ -994,14 +990,6 @@ This may take up to a minute.</source>
<source>Experimental Mode</source> <source>Experimental Mode</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>openpilot (AEB) </translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control.</source>
<translation>openpilot預設將使用原車內建的ACC系統openpilot縱向控制openpilot縱向控制使</translation>
</message>
<message> <message>
<source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source> <source>openpilot defaults to driving in &lt;b&gt;chill mode&lt;/b&gt;. Experimental mode enables &lt;b&gt;alpha-level features&lt;/b&gt; that aren&apos;t ready for chill mode. Experimental features are listed below:</source>
<translation>openpilot &lt;b&gt;&lt;/b&gt; &lt;b&gt;alpha &lt;/b&gt;</translation> <translation>openpilot &lt;b&gt;&lt;/b&gt; &lt;b&gt;alpha &lt;/b&gt;</translation>
@ -1038,6 +1026,18 @@ This may take up to a minute.</source>
<source>Enable experimental longitudinal control to allow Experimental mode.</source> <source>Enable experimental longitudinal control to allow Experimental mode.</source>
<translation>使</translation> <translation>使</translation>
</message> </message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, openpilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Updater</name> <name>Updater</name>

@ -19,7 +19,7 @@ def update_translations(vanish=False, plural_only=None, translations_dir=TRANSLA
for file in translation_files.values(): for file in translation_files.values():
tr_file = os.path.join(translations_dir, f"{file}.ts") tr_file = os.path.join(translations_dir, f"{file}.ts")
args = f"lupdate -locations none -recursive {UI_DIR} -ts {tr_file}" args = f"lupdate -locations none -recursive {UI_DIR} -ts {tr_file} -I {BASEDIR}"
if vanish: if vanish:
args += " -no-obsolete" args += " -no-obsolete"
if file in plural_only: if file in plural_only:

@ -157,9 +157,6 @@ void fill_frame_data(cereal::FrameData::Builder &framed, const FrameMetadata &fr
framed.setHighConversionGain(frame_data.high_conversion_gain); framed.setHighConversionGain(frame_data.high_conversion_gain);
framed.setMeasuredGreyFraction(frame_data.measured_grey_fraction); framed.setMeasuredGreyFraction(frame_data.measured_grey_fraction);
framed.setTargetGreyFraction(frame_data.target_grey_fraction); framed.setTargetGreyFraction(frame_data.target_grey_fraction);
framed.setLensPos(frame_data.lens_pos);
framed.setLensErr(frame_data.lens_err);
framed.setLensTruePos(frame_data.lens_true_pos);
framed.setProcessingTime(frame_data.processing_time); framed.setProcessingTime(frame_data.processing_time);
const float ev = c->cur_ev[frame_data.frame_id % 3]; const float ev = c->cur_ev[frame_data.frame_id % 3];

@ -66,11 +66,6 @@ typedef struct FrameMetadata {
float measured_grey_fraction; float measured_grey_fraction;
float target_grey_fraction; float target_grey_fraction;
// Focus
unsigned int lens_pos;
float lens_err;
float lens_true_pos;
float processing_time; float processing_time;
} FrameMetadata; } FrameMetadata;

@ -1175,7 +1175,7 @@ void CameraState::set_camera_exposure(float grey_frac) {
// t_HCG&t_LCG + t_VS on LPD, t_SPD on SPD // t_HCG&t_LCG + t_VS on LPD, t_SPD on SPD
uint32_t hcg_time = exposure_time; uint32_t hcg_time = exposure_time;
uint32_t lcg_time = hcg_time; uint32_t lcg_time = hcg_time;
uint32_t spd_time = exposure_time_max + VS_TIME_MAX_OX03C10; uint32_t spd_time = std::min(std::max((uint32_t)exposure_time, (exposure_time_max + VS_TIME_MAX_OX03C10) / 3), exposure_time_max + VS_TIME_MAX_OX03C10);
uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 40, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10); uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 40, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10);
uint32_t real_gain = ox03c10_analog_gains_reg[new_exp_g]; uint32_t real_gain = ox03c10_analog_gains_reg[new_exp_g];

@ -58,7 +58,7 @@ struct i2c_random_wr_payload init_array_ox03c10[] = {
// SC ctrl // SC ctrl
{0x3001, 0x03}, // io_pad_oen {0x3001, 0x03}, // io_pad_oen
{0x3002, 0xf8}, // io_pad_oen {0x3002, 0xfc}, // io_pad_oen
{0x3005, 0x80}, // io_pad_out {0x3005, 0x80}, // io_pad_out
{0x3007, 0x01}, // io_pad_sel {0x3007, 0x01}, // io_pad_sel
{0x3008, 0x80}, // io_pad_sel {0x3008, 0x80}, // io_pad_sel
@ -85,6 +85,9 @@ struct i2c_random_wr_payload init_array_ox03c10[] = {
{0x3882, 0x8}, {0x3883, 0x0D}, {0x3882, 0x8}, {0x3883, 0x0D},
{0x3836, 0x1F}, {0x3837, 0x40}, {0x3836, 0x1F}, {0x3837, 0x40},
{0x3892, 0x44},
{0x3823, 0x48},
{0x3012, 0x41}, // SC_PHY_CTRL = 4 lane MIPI {0x3012, 0x41}, // SC_PHY_CTRL = 4 lane MIPI
{0x3020, 0x05}, // SC_CTRL_20 {0x3020, 0x05}, // SC_CTRL_20
@ -179,8 +182,8 @@ struct i2c_random_wr_payload init_array_ox03c10[] = {
{0x3820, 0x04}, {0x3820, 0x04},
{0x3821, 0x19}, {0x3821, 0x19},
{0x3832, 0x00}, {0x3832, 0xF0},
{0x3834, 0x00}, {0x3834, 0xF0},
{0x384c, 0x02}, {0x384c, 0x02},
{0x384d, 0x0d}, {0x384d, 0x0d},
{0x3850, 0x00}, {0x3850, 0x00},

@ -129,3 +129,12 @@ class Amplifier:
self.set_config(config) self.set_config(config)
self.set_global_shutdown(amp_disabled=False) self.set_global_shutdown(amp_disabled=False)
if __name__ == "__main__":
with open("/sys/firmware/devicetree/base/model") as f:
model = f.read().strip('\x00')
model = model.split('comma ')[-1]
amp = Amplifier()
amp.initialize_configuration(model)

@ -452,6 +452,12 @@ class Tici(HardwareBase):
# Allow thermald to write engagement status to kmsg # Allow thermald to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg") os.system("sudo chmod a+w /dev/kmsg")
# TODO: remove the if once agnos 7 ships
# Turn off fan, turned on by the ABL
if os.path.exists('/sys/class/gpio/gpio49/'):
gpio_init(GPIO.SOM_ST_IO, True)
gpio_set(GPIO.SOM_ST_IO, 0)
# *** IRQ config *** # *** IRQ config ***
# move these off the default core # move these off the default core

@ -10,6 +10,8 @@ class GPIO:
STM_RST_N = 124 STM_RST_N = 124
STM_BOOT0 = 134 STM_BOOT0 = 134
SOM_ST_IO = 49
LTE_RST_N = 50 LTE_RST_N = 50
LTE_PWRKEY = 116 LTE_PWRKEY = 116
LTE_BOOT = 52 LTE_BOOT = 52

@ -2,6 +2,7 @@
#include <string> #include <string>
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "common/params.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "system/loggerd/logger.h" #include "system/loggerd/logger.h"
@ -48,7 +49,8 @@ static kj::Array<capnp::word> build_boot_log() {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
const std::string path = LOG_ROOT + "/boot/" + logger_get_route_name(); const std::string timestr = logger_get_route_name();
const std::string path = LOG_ROOT + "/boot/" + timestr;
LOGW("bootlog to %s", path.c_str()); LOGW("bootlog to %s", path.c_str());
// Open bootlog // Open bootlog
@ -61,5 +63,8 @@ int main(int argc, char** argv) {
// Write bootlog // Write bootlog
file.write(build_boot_log().asBytes()); file.write(build_boot_log().asBytes());
// Write out bootlog param to match routes with bootlog
Params().put("CurrentBootlog", timestr.c_str());
return 0; return 0;
} }

@ -86,7 +86,7 @@ class TestLoggerd(unittest.TestCase):
params.clear_all() params.clear_all()
for k, _, v in fake_params: for k, _, v in fake_params:
params.put(k, v) params.put(k, v)
params.put("LaikadEphemerisV2", "abc") params.put("LaikadEphemerisV3", "abc")
lr = list(LogReader(str(self._gen_bootlog()))) lr = list(LogReader(str(self._gen_bootlog())))
initData = lr[0].initData initData = lr[0].initData
@ -103,14 +103,14 @@ class TestLoggerd(unittest.TestCase):
# check params # check params
logged_params = {entry.key: entry.value for entry in initData.params.entries} logged_params = {entry.key: entry.value for entry in initData.params.entries}
expected_params = set(k for k, _, __ in fake_params) | {'LaikadEphemerisV2'} expected_params = set(k for k, _, __ in fake_params) | {'LaikadEphemerisV3'}
assert set(logged_params.keys()) == expected_params, set(logged_params.keys()) ^ expected_params assert set(logged_params.keys()) == expected_params, set(logged_params.keys()) ^ expected_params
assert logged_params['LaikadEphemerisV2'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['LaikadEphemerisV2'])}" assert logged_params['LaikadEphemerisV3'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['LaikadEphemerisV3'])}"
for param_key, initData_key, v in fake_params: for param_key, initData_key, v in fake_params:
self.assertEqual(getattr(initData, initData_key), v) self.assertEqual(getattr(initData, initData_key), v)
self.assertEqual(logged_params[param_key].decode(), v) self.assertEqual(logged_params[param_key].decode(), v)
params.put("LaikadEphemerisV2", "") params.put("LaikadEphemerisV3", "")
def test_rotation(self): def test_rotation(self):
os.environ["LOGGERD_TEST"] = "1" os.environ["LOGGERD_TEST"] = "1"

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import glob
import time import time
import unittest import unittest
import numpy as np import numpy as np
@ -7,7 +8,7 @@ from collections import namedtuple, defaultdict
import cereal.messaging as messaging import cereal.messaging as messaging
from cereal import log from cereal import log
from system.hardware import TICI, HARDWARE from system.hardware import TICI
from selfdrive.manager.process_config import managed_processes from selfdrive.manager.process_config import managed_processes
BMX = { BMX = {
@ -70,7 +71,6 @@ ALL_SENSORS = {
} }
} }
LSM_IRQ = 336
def get_irq_count(irq: int): def get_irq_count(irq: int):
with open(f"/sys/kernel/irq/{irq}/per_cpu_count") as f: with open(f"/sys/kernel/irq/{irq}/per_cpu_count") as f:
@ -101,9 +101,6 @@ class TestSensord(unittest.TestCase):
if not TICI: if not TICI:
raise unittest.SkipTest raise unittest.SkipTest
# make sure gpiochip0 is readable
HARDWARE.initialize_hardware()
# enable LSM self test # enable LSM self test
os.environ["LSM_SELF_TEST"] = "1" os.environ["LSM_SELF_TEST"] = "1"
@ -114,6 +111,15 @@ class TestSensord(unittest.TestCase):
time.sleep(3) time.sleep(3)
cls.sample_secs = 10 cls.sample_secs = 10
cls.events = read_sensor_events(cls.sample_secs) cls.events = read_sensor_events(cls.sample_secs)
# determine sensord's irq
cls.sensord_irq = None
for fn in glob.glob('/sys/kernel/irq/*/actions'):
with open(fn) as f:
if "sensord" in f.read():
cls.sensord_irq = int(fn.split('/')[-2])
break
assert cls.sensord_irq is not None
finally: finally:
# teardown won't run if this doesn't succeed # teardown won't run if this doesn't succeed
managed_processes["sensord"].stop() managed_processes["sensord"].stop()
@ -121,8 +127,6 @@ class TestSensord(unittest.TestCase):
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
managed_processes["sensord"].stop() managed_processes["sensord"].stop()
if "LSM_SELF_TEST" in os.environ:
del os.environ['LSM_SELF_TEST']
def tearDown(self): def tearDown(self):
managed_processes["sensord"].stop() managed_processes["sensord"].stop()
@ -250,9 +254,9 @@ class TestSensord(unittest.TestCase):
time.sleep(3) time.sleep(3)
# read /proc/interrupts to verify interrupts are received # read /proc/interrupts to verify interrupts are received
state_one = get_irq_count(LSM_IRQ) state_one = get_irq_count(self.sensord_irq)
time.sleep(1) time.sleep(1)
state_two = get_irq_count(LSM_IRQ) state_two = get_irq_count(self.sensord_irq)
error_msg = f"no interrupts received after sensord start!\n{state_one} {state_two}" error_msg = f"no interrupts received after sensord start!\n{state_one} {state_two}"
assert state_one != state_two, error_msg assert state_one != state_two, error_msg
@ -261,9 +265,9 @@ class TestSensord(unittest.TestCase):
time.sleep(1) time.sleep(1)
# read /proc/interrupts to verify no more interrupts are received # read /proc/interrupts to verify no more interrupts are received
state_one = get_irq_count(LSM_IRQ) state_one = get_irq_count(self.sensord_irq)
time.sleep(1) time.sleep(1)
state_two = get_irq_count(LSM_IRQ) state_two = get_irq_count(self.sensord_irq)
assert state_one == state_two, "Interrupts received after sensord stop!" assert state_one == state_two, "Interrupts received after sensord stop!"

@ -40,6 +40,11 @@ void ubx_t::_read() {
m_body = new rxm_sfrbx_t(m__io, this, m__root); m_body = new rxm_sfrbx_t(m__io, this, m__root);
break; break;
} }
case 309: {
n_body = false;
m_body = new nav_sat_t(m__io, this, m__root);
break;
}
case 2571: { case 2571: {
n_body = false; n_body = false;
m_body = new mon_hw2_t(m__io, this, m__root); m_body = new mon_hw2_t(m__io, this, m__root);
@ -70,9 +75,9 @@ void ubx_t::_clean_up() {
ubx_t::rxm_rawx_t::rxm_rawx_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) { ubx_t::rxm_rawx_t::rxm_rawx_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent; m__parent = p__parent;
m__root = p__root; m__root = p__root;
m_measurements = 0; m_meas = 0;
m__raw_measurements = 0; m__raw_meas = 0;
m__io__raw_measurements = 0; m__io__raw_meas = 0;
try { try {
_read(); _read();
@ -89,15 +94,15 @@ void ubx_t::rxm_rawx_t::_read() {
m_num_meas = m__io->read_u1(); m_num_meas = m__io->read_u1();
m_rec_stat = m__io->read_u1(); m_rec_stat = m__io->read_u1();
m_reserved1 = m__io->read_bytes(3); m_reserved1 = m__io->read_bytes(3);
m__raw_measurements = new std::vector<std::string>(); m__raw_meas = new std::vector<std::string>();
m__io__raw_measurements = new std::vector<kaitai::kstream*>(); m__io__raw_meas = new std::vector<kaitai::kstream*>();
m_measurements = new std::vector<meas_t*>(); m_meas = new std::vector<measurement_t*>();
const int l_measurements = num_meas(); const int l_meas = num_meas();
for (int i = 0; i < l_measurements; i++) { for (int i = 0; i < l_meas; i++) {
m__raw_measurements->push_back(m__io->read_bytes(32)); m__raw_meas->push_back(m__io->read_bytes(32));
kaitai::kstream* io__raw_measurements = new kaitai::kstream(m__raw_measurements->at(m__raw_measurements->size() - 1)); kaitai::kstream* io__raw_meas = new kaitai::kstream(m__raw_meas->at(m__raw_meas->size() - 1));
m__io__raw_measurements->push_back(io__raw_measurements); m__io__raw_meas->push_back(io__raw_meas);
m_measurements->push_back(new meas_t(io__raw_measurements, this, m__root)); m_meas->push_back(new measurement_t(io__raw_meas, this, m__root));
} }
} }
@ -106,24 +111,24 @@ ubx_t::rxm_rawx_t::~rxm_rawx_t() {
} }
void ubx_t::rxm_rawx_t::_clean_up() { void ubx_t::rxm_rawx_t::_clean_up() {
if (m__raw_measurements) { if (m__raw_meas) {
delete m__raw_measurements; m__raw_measurements = 0; delete m__raw_meas; m__raw_meas = 0;
} }
if (m__io__raw_measurements) { if (m__io__raw_meas) {
for (std::vector<kaitai::kstream*>::iterator it = m__io__raw_measurements->begin(); it != m__io__raw_measurements->end(); ++it) { for (std::vector<kaitai::kstream*>::iterator it = m__io__raw_meas->begin(); it != m__io__raw_meas->end(); ++it) {
delete *it; delete *it;
} }
delete m__io__raw_measurements; m__io__raw_measurements = 0; delete m__io__raw_meas; m__io__raw_meas = 0;
} }
if (m_measurements) { if (m_meas) {
for (std::vector<meas_t*>::iterator it = m_measurements->begin(); it != m_measurements->end(); ++it) { for (std::vector<measurement_t*>::iterator it = m_meas->begin(); it != m_meas->end(); ++it) {
delete *it; delete *it;
} }
delete m_measurements; m_measurements = 0; delete m_meas; m_meas = 0;
} }
} }
ubx_t::rxm_rawx_t::meas_t::meas_t(kaitai::kstream* p__io, ubx_t::rxm_rawx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) { ubx_t::rxm_rawx_t::measurement_t::measurement_t(kaitai::kstream* p__io, ubx_t::rxm_rawx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent; m__parent = p__parent;
m__root = p__root; m__root = p__root;
@ -135,7 +140,7 @@ ubx_t::rxm_rawx_t::meas_t::meas_t(kaitai::kstream* p__io, ubx_t::rxm_rawx_t* p__
} }
} }
void ubx_t::rxm_rawx_t::meas_t::_read() { void ubx_t::rxm_rawx_t::measurement_t::_read() {
m_pr_mes = m__io->read_f8le(); m_pr_mes = m__io->read_f8le();
m_cp_mes = m__io->read_f8le(); m_cp_mes = m__io->read_f8le();
m_do_mes = m__io->read_f4le(); m_do_mes = m__io->read_f4le();
@ -152,11 +157,11 @@ void ubx_t::rxm_rawx_t::meas_t::_read() {
m_reserved3 = m__io->read_bytes(1); m_reserved3 = m__io->read_bytes(1);
} }
ubx_t::rxm_rawx_t::meas_t::~meas_t() { ubx_t::rxm_rawx_t::measurement_t::~measurement_t() {
_clean_up(); _clean_up();
} }
void ubx_t::rxm_rawx_t::meas_t::_clean_up() { void ubx_t::rxm_rawx_t::measurement_t::_clean_up() {
} }
ubx_t::rxm_sfrbx_t::rxm_sfrbx_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) { ubx_t::rxm_sfrbx_t::rxm_sfrbx_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
@ -198,6 +203,89 @@ void ubx_t::rxm_sfrbx_t::_clean_up() {
} }
} }
ubx_t::nav_sat_t::nav_sat_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_svs = 0;
m__raw_svs = 0;
m__io__raw_svs = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ubx_t::nav_sat_t::_read() {
m_itow = m__io->read_u4le();
m_version = m__io->read_u1();
m_num_svs = m__io->read_u1();
m_reserved = m__io->read_bytes(2);
m__raw_svs = new std::vector<std::string>();
m__io__raw_svs = new std::vector<kaitai::kstream*>();
m_svs = new std::vector<nav_t*>();
const int l_svs = num_svs();
for (int i = 0; i < l_svs; i++) {
m__raw_svs->push_back(m__io->read_bytes(12));
kaitai::kstream* io__raw_svs = new kaitai::kstream(m__raw_svs->at(m__raw_svs->size() - 1));
m__io__raw_svs->push_back(io__raw_svs);
m_svs->push_back(new nav_t(io__raw_svs, this, m__root));
}
}
ubx_t::nav_sat_t::~nav_sat_t() {
_clean_up();
}
void ubx_t::nav_sat_t::_clean_up() {
if (m__raw_svs) {
delete m__raw_svs; m__raw_svs = 0;
}
if (m__io__raw_svs) {
for (std::vector<kaitai::kstream*>::iterator it = m__io__raw_svs->begin(); it != m__io__raw_svs->end(); ++it) {
delete *it;
}
delete m__io__raw_svs; m__io__raw_svs = 0;
}
if (m_svs) {
for (std::vector<nav_t*>::iterator it = m_svs->begin(); it != m_svs->end(); ++it) {
delete *it;
}
delete m_svs; m_svs = 0;
}
}
ubx_t::nav_sat_t::nav_t::nav_t(kaitai::kstream* p__io, ubx_t::nav_sat_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void ubx_t::nav_sat_t::nav_t::_read() {
m_gnss_id = static_cast<ubx_t::gnss_type_t>(m__io->read_u1());
m_sv_id = m__io->read_u1();
m_cno = m__io->read_u1();
m_elev = m__io->read_s1();
m_azim = m__io->read_s2le();
m_pr_res = m__io->read_s2le();
m_flags = m__io->read_u4le();
}
ubx_t::nav_sat_t::nav_t::~nav_t() {
_clean_up();
}
void ubx_t::nav_sat_t::nav_t::_clean_up() {
}
ubx_t::nav_pvt_t::nav_pvt_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) { ubx_t::nav_pvt_t::nav_pvt_t(kaitai::kstream* p__io, ubx_t* p__parent, ubx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent; m__parent = p__parent;
m__root = p__root; m__root = p__root;

@ -16,6 +16,7 @@ class ubx_t : public kaitai::kstruct {
public: public:
class rxm_rawx_t; class rxm_rawx_t;
class rxm_sfrbx_t; class rxm_sfrbx_t;
class nav_sat_t;
class nav_pvt_t; class nav_pvt_t;
class mon_hw2_t; class mon_hw2_t;
class mon_hw_t; class mon_hw_t;
@ -42,7 +43,7 @@ public:
class rxm_rawx_t : public kaitai::kstruct { class rxm_rawx_t : public kaitai::kstruct {
public: public:
class meas_t; class measurement_t;
rxm_rawx_t(kaitai::kstream* p__io, ubx_t* p__parent = 0, ubx_t* p__root = 0); rxm_rawx_t(kaitai::kstream* p__io, ubx_t* p__parent = 0, ubx_t* p__root = 0);
@ -53,18 +54,18 @@ public:
public: public:
~rxm_rawx_t(); ~rxm_rawx_t();
class meas_t : public kaitai::kstruct { class measurement_t : public kaitai::kstruct {
public: public:
meas_t(kaitai::kstream* p__io, ubx_t::rxm_rawx_t* p__parent = 0, ubx_t* p__root = 0); measurement_t(kaitai::kstream* p__io, ubx_t::rxm_rawx_t* p__parent = 0, ubx_t* p__root = 0);
private: private:
void _read(); void _read();
void _clean_up(); void _clean_up();
public: public:
~meas_t(); ~measurement_t();
private: private:
double m_pr_mes; double m_pr_mes;
@ -110,11 +111,11 @@ public:
uint8_t m_num_meas; uint8_t m_num_meas;
uint8_t m_rec_stat; uint8_t m_rec_stat;
std::string m_reserved1; std::string m_reserved1;
std::vector<meas_t*>* m_measurements; std::vector<measurement_t*>* m_meas;
ubx_t* m__root; ubx_t* m__root;
ubx_t* m__parent; ubx_t* m__parent;
std::vector<std::string>* m__raw_measurements; std::vector<std::string>* m__raw_meas;
std::vector<kaitai::kstream*>* m__io__raw_measurements; std::vector<kaitai::kstream*>* m__io__raw_meas;
public: public:
double rcv_tow() const { return m_rcv_tow; } double rcv_tow() const { return m_rcv_tow; }
@ -123,11 +124,11 @@ public:
uint8_t num_meas() const { return m_num_meas; } uint8_t num_meas() const { return m_num_meas; }
uint8_t rec_stat() const { return m_rec_stat; } uint8_t rec_stat() const { return m_rec_stat; }
std::string reserved1() const { return m_reserved1; } std::string reserved1() const { return m_reserved1; }
std::vector<meas_t*>* measurements() const { return m_measurements; } std::vector<measurement_t*>* meas() const { return m_meas; }
ubx_t* _root() const { return m__root; } ubx_t* _root() const { return m__root; }
ubx_t* _parent() const { return m__parent; } ubx_t* _parent() const { return m__parent; }
std::vector<std::string>* _raw_measurements() const { return m__raw_measurements; } std::vector<std::string>* _raw_meas() const { return m__raw_meas; }
std::vector<kaitai::kstream*>* _io__raw_measurements() const { return m__io__raw_measurements; } std::vector<kaitai::kstream*>* _io__raw_meas() const { return m__io__raw_meas; }
}; };
class rxm_sfrbx_t : public kaitai::kstruct { class rxm_sfrbx_t : public kaitai::kstruct {
@ -170,6 +171,79 @@ public:
ubx_t* _parent() const { return m__parent; } ubx_t* _parent() const { return m__parent; }
}; };
class nav_sat_t : public kaitai::kstruct {
public:
class nav_t;
nav_sat_t(kaitai::kstream* p__io, ubx_t* p__parent = 0, ubx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nav_sat_t();
class nav_t : public kaitai::kstruct {
public:
nav_t(kaitai::kstream* p__io, ubx_t::nav_sat_t* p__parent = 0, ubx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~nav_t();
private:
gnss_type_t m_gnss_id;
uint8_t m_sv_id;
uint8_t m_cno;
int8_t m_elev;
int16_t m_azim;
int16_t m_pr_res;
uint32_t m_flags;
ubx_t* m__root;
ubx_t::nav_sat_t* m__parent;
public:
gnss_type_t gnss_id() const { return m_gnss_id; }
uint8_t sv_id() const { return m_sv_id; }
uint8_t cno() const { return m_cno; }
int8_t elev() const { return m_elev; }
int16_t azim() const { return m_azim; }
int16_t pr_res() const { return m_pr_res; }
uint32_t flags() const { return m_flags; }
ubx_t* _root() const { return m__root; }
ubx_t::nav_sat_t* _parent() const { return m__parent; }
};
private:
uint32_t m_itow;
uint8_t m_version;
uint8_t m_num_svs;
std::string m_reserved;
std::vector<nav_t*>* m_svs;
ubx_t* m__root;
ubx_t* m__parent;
std::vector<std::string>* m__raw_svs;
std::vector<kaitai::kstream*>* m__io__raw_svs;
public:
uint32_t itow() const { return m_itow; }
uint8_t version() const { return m_version; }
uint8_t num_svs() const { return m_num_svs; }
std::string reserved() const { return m_reserved; }
std::vector<nav_t*>* svs() const { return m_svs; }
ubx_t* _root() const { return m__root; }
ubx_t* _parent() const { return m__parent; }
std::vector<std::string>* _raw_svs() const { return m__raw_svs; }
std::vector<kaitai::kstream*>* _io__raw_svs() const { return m__io__raw_svs; }
};
class nav_pvt_t : public kaitai::kstruct { class nav_pvt_t : public kaitai::kstruct {
public: public:

@ -48,6 +48,7 @@ MSG_NAV_TIMEGPS = 0x20
MSG_NAV_TIMEUTC = 0x21 MSG_NAV_TIMEUTC = 0x21
MSG_NAV_CLOCK = 0x22 MSG_NAV_CLOCK = 0x22
MSG_NAV_SVINFO = 0x30 MSG_NAV_SVINFO = 0x30
MSG_NAV_SAT = 0x35
MSG_NAV_AOPSTATUS = 0x60 MSG_NAV_AOPSTATUS = 0x60
MSG_NAV_DGPS = 0x31 MSG_NAV_DGPS = 0x31
MSG_NAV_DOP = 0x04 MSG_NAV_DOP = 0x04

@ -65,22 +65,6 @@ inline bool UbloxMsgParser::valid_so_far() {
return true; return true;
} }
inline uint16_t UbloxMsgParser::get_glonass_year(uint8_t N4, uint16_t Nt) {
// convert time to year (conversion from A3.1.3)
int J = 0;
if (1 <= Nt && Nt <= 366) {
J = 1;
} else if (367 <= Nt && Nt <= 731) {
J = 2;
} else if (732 <= Nt && Nt <= 1096) {
J = 3;
} else if (1097 <= Nt && Nt <= 1461) {
J = 4;
}
uint16_t year = 1996 + 4*(N4 -1) + (J - 1);
return year;
}
bool UbloxMsgParser::add_data(float log_time, const uint8_t *incoming_data, uint32_t incoming_data_len, size_t &bytes_consumed) { bool UbloxMsgParser::add_data(float log_time, const uint8_t *incoming_data, uint32_t incoming_data_len, size_t &bytes_consumed) {
last_log_time = log_time; last_log_time = log_time;
int needed = needed_bytes(); int needed = needed_bytes();
@ -203,6 +187,7 @@ kj::Array<capnp::word> UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m
int iode_s2 = 0; int iode_s2 = 0;
int iode_s3 = 0; int iode_s3 = 0;
int iodc_lsb = 0; int iodc_lsb = 0;
int week;
// Subframe 1 // Subframe 1
{ {
@ -210,7 +195,14 @@ kj::Array<capnp::word> UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m
gps_t subframe(&stream); gps_t subframe(&stream);
gps_t::subframe_1_t* subframe_1 = static_cast<gps_t::subframe_1_t*>(subframe.body()); gps_t::subframe_1_t* subframe_1 = static_cast<gps_t::subframe_1_t*>(subframe.body());
eph.setGpsWeek(subframe_1->week_no()); // Each message is incremented to be greater or equal than week 1877 (2015-12-27).
// To skip this use the current_time argument
week = subframe_1->week_no();
week += 1024;
if (week < 1877) {
week += 1024;
}
//eph.setGpsWeek(subframe_1->week_no());
eph.setTgd(subframe_1->t_gd() * pow(2, -31)); eph.setTgd(subframe_1->t_gd() * pow(2, -31));
eph.setToc(subframe_1->t_oc() * pow(2, 4)); eph.setToc(subframe_1->t_oc() * pow(2, 4));
eph.setAf2(subframe_1->af_2() * pow(2, -55)); eph.setAf2(subframe_1->af_2() * pow(2, -55));
@ -227,6 +219,12 @@ kj::Array<capnp::word> UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m
gps_t subframe(&stream); gps_t subframe(&stream);
gps_t::subframe_2_t* subframe_2 = static_cast<gps_t::subframe_2_t*>(subframe.body()); gps_t::subframe_2_t* subframe_2 = static_cast<gps_t::subframe_2_t*>(subframe.body());
// GPS week refers to current week, the ephemeris can be valid for the next
// if toe equals 0, this can be verified by the TOW count if it is within the
// last 2 hours of the week (gps ephemeris valid for 4hours)
if (subframe_2->t_oe() == 0 and subframe.how()->tow_count()*6 >= (SECS_IN_WEEK - 2*SECS_IN_HR)){
week += 1;
}
eph.setCrs(subframe_2->c_rs() * pow(2, -5)); eph.setCrs(subframe_2->c_rs() * pow(2, -5));
eph.setDeltaN(subframe_2->delta_n() * pow(2, -43) * gpsPi); eph.setDeltaN(subframe_2->delta_n() * pow(2, -43) * gpsPi);
eph.setM0(subframe_2->m_0() * pow(2, -31) * gpsPi); eph.setM0(subframe_2->m_0() * pow(2, -31) * gpsPi);
@ -256,6 +254,9 @@ kj::Array<capnp::word> UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m
iode_s3 = subframe_3->iode(); iode_s3 = subframe_3->iode();
} }
eph.setToeWeek(week);
eph.setTocWeek(week);
gps_subframes[msg->sv_id()].clear(); gps_subframes[msg->sv_id()].clear();
if (iodc_lsb != iode_s2 || iodc_lsb != iode_s3) { if (iodc_lsb != iode_s2 || iodc_lsb != iode_s3) {
// data set cutover, reject ephemeris // data set cutover, reject ephemeris
@ -329,7 +330,10 @@ kj::Array<capnp::word> UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_
MessageBuilder msg_builder; MessageBuilder msg_builder;
auto eph = msg_builder.initEvent().initUbloxGnss().initGlonassEphemeris(); auto eph = msg_builder.initEvent().initUbloxGnss().initGlonassEphemeris();
eph.setSvId(msg->sv_id()); eph.setSvId(msg->sv_id());
eph.setFreqNum(msg->freq_id() - 7);
uint16_t current_day = 0; uint16_t current_day = 0;
uint16_t tk = 0;
// string number 1 // string number 1
{ {
@ -338,7 +342,8 @@ kj::Array<capnp::word> UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_
glonass_t::string_1_t* data = static_cast<glonass_t::string_1_t*>(gl_stream.data()); glonass_t::string_1_t* data = static_cast<glonass_t::string_1_t*>(gl_stream.data());
eph.setP1(data->p1()); eph.setP1(data->p1());
eph.setTk(data->t_k()); tk = data->t_k();
eph.setTkDEPRECATED(tk);
eph.setXVel(data->x_vel() * pow(2, -20)); eph.setXVel(data->x_vel() * pow(2, -20));
eph.setXAccel(data->x_accel() * pow(2, -30)); eph.setXAccel(data->x_accel() * pow(2, -30));
eph.setX(data->x() * pow(2, -11)); eph.setX(data->x() * pow(2, -11));
@ -379,6 +384,7 @@ kj::Array<capnp::word> UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_
glonass_t::string_4_t* data = static_cast<glonass_t::string_4_t*>(gl_stream.data()); glonass_t::string_4_t* data = static_cast<glonass_t::string_4_t*>(gl_stream.data());
current_day = data->n_t(); current_day = data->n_t();
eph.setNt(current_day);
eph.setTauN(data->tau_n() * pow(2, -30)); eph.setTauN(data->tau_n() * pow(2, -30));
eph.setDeltaTauN(data->delta_tau_n() * pow(2, -30)); eph.setDeltaTauN(data->delta_tau_n() * pow(2, -30));
eph.setAge(data->e_n()); eph.setAge(data->e_n());
@ -398,27 +404,9 @@ kj::Array<capnp::word> UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_
// string5 parsing is only needed to get the year, this can be removed and // string5 parsing is only needed to get the year, this can be removed and
// the year can be fetched later in laika (note rollovers and leap year) // the year can be fetched later in laika (note rollovers and leap year)
uint8_t n_4 = data->n_4(); eph.setN4(data->n_4());
uint16_t year = get_glonass_year(n_4, current_day); int tk_seconds = SECS_IN_HR * ((tk>>7) & 0x1F) + SECS_IN_MIN * ((tk>>1) & 0x3F) + (tk & 0x1) * 30;
if (current_day > 1461) { eph.setTkSeconds(tk_seconds);
// impossible day within last 4 year, reject ephemeris
// TODO: check if this can be detected via hamming code
LOGE("INVALID DATA: current day out of range: %d, %d", current_day, n_4);
glonass_strings[msg->sv_id()].clear();
return kj::Array<capnp::word>();
}
uint16_t last_leap_year = 1996 + 4*(n_4-1);
uint16_t days_till_this_year = (year - last_leap_year)*365;
if (days_till_this_year != 0) {
days_till_this_year++;
}
eph.setYear(year);
eph.setDayInYear(current_day - days_till_this_year);
eph.setHour((eph.getTk()>>7) & 0x1F);
eph.setMinute((eph.getTk()>>1) & 0x3F);
eph.setSecond((eph.getTk() & 0x1) * 30);
} }
glonass_strings[msg->freq_id()].clear(); glonass_strings[msg->freq_id()].clear();
@ -446,7 +434,7 @@ kj::Array<capnp::word> UbloxMsgParser::gen_rxm_rawx(ubx_t::rxm_rawx_t *msg) {
mr.setGpsWeek(msg->week()); mr.setGpsWeek(msg->week());
auto mb = mr.initMeasurements(msg->num_meas()); auto mb = mr.initMeasurements(msg->num_meas());
auto measurements = *msg->measurements(); auto measurements = *msg->meas();
for(int8_t i = 0; i < msg->num_meas(); i++) { for(int8_t i = 0; i < msg->num_meas(); i++) {
mb[i].setSvId(measurements[i]->sv_id()); mb[i].setSvId(measurements[i]->sv_id());
mb[i].setPseudorange(measurements[i]->pr_mes()); mb[i].setPseudorange(measurements[i]->pr_mes());
@ -475,6 +463,22 @@ kj::Array<capnp::word> UbloxMsgParser::gen_rxm_rawx(ubx_t::rxm_rawx_t *msg) {
return capnp::messageToFlatArray(msg_builder); return capnp::messageToFlatArray(msg_builder);
} }
kj::Array<capnp::word> UbloxMsgParser::gen_nav_sat(ubx_t::nav_sat_t *msg) {
MessageBuilder msg_builder;
auto sr = msg_builder.initEvent().initUbloxGnss().initSatReport();
sr.setITow(msg->itow());
auto svs = sr.initSvs(msg->num_svs());
auto svs_data = *msg->svs();
for(int8_t i = 0; i < msg->num_svs(); i++) {
svs[i].setSvId(svs_data[i]->sv_id());
svs[i].setGnssId(svs_data[i]->gnss_id());
svs[i].setFlagsBitfield(svs_data[i]->flags());
}
return capnp::messageToFlatArray(msg_builder);
}
kj::Array<capnp::word> UbloxMsgParser::gen_mon_hw(ubx_t::mon_hw_t *msg) { kj::Array<capnp::word> UbloxMsgParser::gen_mon_hw(ubx_t::mon_hw_t *msg) {
MessageBuilder msg_builder; MessageBuilder msg_builder;
auto hwStatus = msg_builder.initEvent().initUbloxGnss().initHwStatus(); auto hwStatus = msg_builder.initEvent().initUbloxGnss().initHwStatus();

@ -15,6 +15,11 @@
using namespace std::string_literals; using namespace std::string_literals;
const int SECS_IN_MIN = 60;
const int SECS_IN_HR = 60 * SECS_IN_MIN;
const int SECS_IN_DAY = 24 * SECS_IN_HR;
const int SECS_IN_WEEK = 7 * SECS_IN_DAY;
// protocol constants // protocol constants
namespace ublox { namespace ublox {
const uint8_t PREAMBLE1 = 0xb5; const uint8_t PREAMBLE1 = 0xb5;
@ -97,12 +102,12 @@ class UbloxMsgParser {
kj::Array<capnp::word> gen_rxm_rawx(ubx_t::rxm_rawx_t *msg); kj::Array<capnp::word> gen_rxm_rawx(ubx_t::rxm_rawx_t *msg);
kj::Array<capnp::word> gen_mon_hw(ubx_t::mon_hw_t *msg); kj::Array<capnp::word> gen_mon_hw(ubx_t::mon_hw_t *msg);
kj::Array<capnp::word> gen_mon_hw2(ubx_t::mon_hw2_t *msg); kj::Array<capnp::word> gen_mon_hw2(ubx_t::mon_hw2_t *msg);
kj::Array<capnp::word> gen_nav_sat(ubx_t::nav_sat_t *msg);
private: private:
inline bool valid_cheksum(); inline bool valid_cheksum();
inline bool valid(); inline bool valid();
inline bool valid_so_far(); inline bool valid_so_far();
inline uint16_t get_glonass_year(uint8_t N4, uint16_t Nt);
kj::Array<capnp::word> parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg); kj::Array<capnp::word> parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg);
kj::Array<capnp::word> parse_glonass_ephemeris(ubx_t::rxm_sfrbx_t *msg); kj::Array<capnp::word> parse_glonass_ephemeris(ubx_t::rxm_sfrbx_t *msg);

@ -17,6 +17,7 @@ seq:
0x0215: rxm_rawx 0x0215: rxm_rawx
0x0a09: mon_hw 0x0a09: mon_hw
0x0a0b: mon_hw2 0x0a0b: mon_hw2
0x0135: nav_sat
instances: instances:
checksum: checksum:
pos: length + 6 pos: length + 6
@ -142,13 +143,13 @@ types:
type: u1 type: u1
- id: reserved1 - id: reserved1
size: 3 size: 3
- id: measurements - id: meas
type: meas type: measurement
size: 32 size: 32
repeat: expr repeat: expr
repeat-expr: num_meas repeat-expr: num_meas
types: types:
meas: measurement:
seq: seq:
- id: pr_mes - id: pr_mes
type: f8 type: f8
@ -179,6 +180,39 @@ types:
type: u1 type: u1
- id: reserved3 - id: reserved3
size: 1 size: 1
nav_sat:
seq:
- id: itow
type: u4
- id: version
type: u1
- id: num_svs
type: u1
- id: reserved
size: 2
- id: svs
type: nav
size: 12
repeat: expr
repeat-expr: num_svs
types:
nav:
seq:
- id: gnss_id
type: u1
enum: gnss_type
- id: sv_id
type: u1
- id: cno
type: u1
- id: elev
type: s1
- id: azim
type: s2
- id: pr_res
type: s2
- id: flags
type: u4
nav_pvt: nav_pvt:
seq: seq:

@ -3,5 +3,5 @@ moc_*
_cabana _cabana
settings settings
car_fingerprint_to_dbc.json dbc/car_fingerprint_to_dbc.json
tests/_test_cabana tests/_test_cabana

@ -28,7 +28,8 @@ cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "asset
prev_moc_path = cabana_env['QT_MOCHPREFIX'] prev_moc_path = cabana_env['QT_MOCHPREFIX']
cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_'
cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbc.cc', 'dbcmanager.cc', cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc',
'commands.cc', 'messageswidget.cc', 'route.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) 'commands.cc', 'messageswidget.cc', 'route.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
@ -39,5 +40,5 @@ if GetOption('test'):
cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs]) cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs])
def generate_dbc_json(target, source, env): def generate_dbc_json(target, source, env):
env.Execute('tools/cabana/generate_dbc_json.py --out tools/cabana/car_fingerprint_to_dbc.json') env.Execute('tools/cabana/dbc/generate_dbc_json.py --out tools/cabana/dbc/car_fingerprint_to_dbc.json')
cabana_env.Command('generate_dbc_json', [], generate_dbc_json) cabana_env.Command('generate_dbc_json', [], generate_dbc_json)

@ -11,7 +11,7 @@
#include <QToolTip> #include <QToolTip>
#include "tools/cabana/commands.h" #include "tools/cabana/commands.h"
#include "tools/cabana/signaledit.h" #include "tools/cabana/signalview.h"
// BinaryView // BinaryView
@ -273,6 +273,10 @@ void BinaryViewModel::refresh() {
row_count = can->lastMessage(msg_id).dat.size(); row_count = can->lastMessage(msg_id).dat.size();
items.resize(row_count * column_count); items.resize(row_count * column_count);
} }
int valid_rows = std::min(can->lastMessage(msg_id).dat.size(), row_count);
for (int i = 0; i < valid_rows * column_count; ++i) {
items[i].valid = true;
}
endResetModel(); endResetModel();
updateState(); updateState();
} }
@ -307,9 +311,6 @@ void BinaryViewModel::updateState() {
items[i * column_count + 8].val = toHex(binary[i]); items[i * column_count + 8].val = toHex(binary[i]);
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) {
items[i].val = "-";
}
for (int i = 0; i < items.size(); ++i) { for (int i = 0; i < items.size(); ++i) {
if (i >= prev_items.size() || prev_items[i].val != items[i].val || prev_items[i].bg_color != items[i].bg_color) { if (i >= prev_items.size() || prev_items[i].val != items[i].val || prev_items[i].bg_color != items[i].bg_color) {
@ -376,6 +377,9 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
} }
} }
if (!item->valid) {
painter->fillRect(option.rect, QBrush(Qt::darkGray, Qt::BDiagPattern));
}
painter->drawText(option.rect, Qt::AlignCenter, item->val); painter->drawText(option.rect, Qt::AlignCenter, item->val);
if (item->is_msb || item->is_lsb) { if (item->is_msb || item->is_lsb) {
painter->setFont(small_font); painter->setFont(small_font);
@ -422,7 +426,7 @@ void BinaryItemDelegate::drawBorder(QPainter* painter, const QStyleOptionViewIte
painter->setClipRegion(QRegion(rc).subtracted(subtract)); painter->setClipRegion(QRegion(rc).subtracted(subtract));
if (!subtract.isEmpty()) { if (!subtract.isEmpty()) {
// fill gaps inside corners. // fill gaps inside corners.
painter->setPen(QPen(border_color, 2)); painter->setPen(QPen(border_color, 2, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin));
for (auto &r : subtract) { for (auto &r : subtract) {
painter->drawRect(r); painter->drawRect(r);
} }

@ -6,7 +6,7 @@
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QTableView> #include <QTableView>
#include "tools/cabana/dbcmanager.h" #include "tools/cabana/dbc/dbcmanager.h"
#include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/streams/abstractstream.h"
class BinaryItemDelegate : public QStyledItemDelegate { class BinaryItemDelegate : public QStyledItemDelegate {
@ -42,8 +42,9 @@ public:
QColor bg_color = QColor(102, 86, 169, 0); QColor bg_color = QColor(102, 86, 169, 0);
bool is_msb = false; bool is_msb = false;
bool is_lsb = false; bool is_lsb = false;
QString val = "-"; QString val;
QList<const cabana::Signal *> sigs; QList<const cabana::Signal *> sigs;
bool valid = false;
}; };
std::vector<Item> items; std::vector<Item> items;

@ -9,16 +9,20 @@
#include <QGraphicsLayout> #include <QGraphicsLayout>
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include <QRubberBand> #include <QOpenGLWidget>
#include <QPushButton> #include <QPushButton>
#include <QRubberBand>
#include <QStylePainter>
#include <QToolBar> #include <QToolBar>
#include <QToolTip> #include <QToolTip>
#include <QtConcurrent> #include <QtConcurrent>
const int MAX_COLUMN_COUNT = 4; const int MAX_COLUMN_COUNT = 4;
static inline bool xLessThan(const QPointF &p, float x) { return p.x() < x; }
// ChartsWidget // ChartsWidget
ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), QFrame(parent) {
setFrameStyle(QFrame::StyledPanel | QFrame::Plain); setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setContentsMargins(0, 0, 0, 0);
@ -53,6 +57,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
range_slider->setPageStep(60); // 1 min range_slider->setPageStep(60); // 1 min
range_slider_action = toolbar->addWidget(range_slider); range_slider_action = toolbar->addWidget(range_slider);
undo_zoom_action = toolbar->addAction(utils::icon("arrow-counterclockwise"), tr("Previous zoom"));
qobject_cast<QToolButton*>(toolbar->widgetForAction(undo_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), tr("Reset Zoom")); reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), tr("Reset Zoom"));
qobject_cast<QToolButton*>(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); qobject_cast<QToolButton*>(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
@ -64,13 +71,13 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
charts_layout = new QGridLayout(); charts_layout = new QGridLayout();
charts_layout->setSpacing(10); charts_layout->setSpacing(10);
QWidget *charts_container = new QWidget(this); charts_container = new QWidget(this);
QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container); QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container);
charts_main_layout->setContentsMargins(0, 0, 0, 0); charts_main_layout->setContentsMargins(0, 0, 0, 0);
charts_main_layout->addLayout(charts_layout); charts_main_layout->addLayout(charts_layout);
charts_main_layout->addStretch(0); charts_main_layout->addStretch(0);
QScrollArea *charts_scroll = new QScrollArea(this); charts_scroll = new QScrollArea(this);
charts_scroll->setFrameStyle(QFrame::NoFrame); charts_scroll->setFrameStyle(QFrame::NoFrame);
charts_scroll->setWidgetResizable(true); charts_scroll->setWidgetResizable(true);
charts_scroll->setWidget(charts_container); charts_scroll->setWidget(charts_container);
@ -86,12 +93,15 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
range_slider->setValue(max_chart_range); range_slider->setValue(max_chart_range);
updateToolBar(); updateToolBar();
align_timer.setSingleShot(true);
QObject::connect(&align_timer, &QTimer::timeout, this, &ChartsWidget::alignCharts);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged);
QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState);
QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange);
QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart);
QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll);
QObject::connect(undo_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomUndo);
QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset); QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset);
QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged);
QObject::connect(dock_btn, &QAction::triggered, [this]() { QObject::connect(dock_btn, &QAction::triggered, [this]() {
@ -108,17 +118,17 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
void ChartsWidget::eventsMerged() { void ChartsWidget::eventsMerged() {
{ {
assert(!can->liveStreaming());
QFutureSynchronizer<void> future_synchronizer; QFutureSynchronizer<void> future_synchronizer;
const auto events = can->events();
for (auto c : charts) { for (auto c : charts) {
future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, events, true)); future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr));
} }
} }
updateState(); if (can->isPaused()) {
updateState();
}
} }
void ChartsWidget::zoomIn(double min, double max) { void ChartsWidget::setZoom(double min, double max) {
zoomed_range = {min, max}; zoomed_range = {min, max};
is_zoomed = zoomed_range != display_range; is_zoomed = zoomed_range != display_range;
updateToolBar(); updateToolBar();
@ -126,33 +136,53 @@ void ChartsWidget::zoomIn(double min, double max) {
emit rangeChanged(min, max, is_zoomed); emit rangeChanged(min, max, is_zoomed);
} }
void ChartsWidget::zoomIn(double min, double max) {
// Save previous zoom on undo stack
if (is_zoomed) {
zoom_stack.push({zoomed_range.first, zoomed_range.second});
}
setZoom(min, max);
}
void ChartsWidget::zoomReset() { void ChartsWidget::zoomReset() {
zoomIn(display_range.first, display_range.second); setZoom(display_range.first, display_range.second);
zoom_stack.clear();
} }
void ChartsWidget::updateState() { void ChartsWidget::zoomUndo() {
if (charts.isEmpty()) return; if (!zoom_stack.isEmpty()) {
auto r = zoom_stack.pop();
setZoom(r.first, r.second);
} else {
zoomReset();
}
}
const auto events = can->events(); void ChartsWidget::showValueTip(double sec) {
if (can->liveStreaming()) { const QRect visible_rect(-charts_container->pos(), charts_scroll->viewport()->size());
// appends incoming events to the end of series for (auto c : charts) {
for (auto c : charts) { if (sec >= 0 && visible_rect.contains(QRect(c->mapTo(charts_container, QPoint(0, 0)), c->size()))) {
c->updateSeries(nullptr, events, false); c->showTip(sec);
} else {
c->hideTip();
} }
} }
}
void ChartsWidget::updateState() {
if (charts.isEmpty()) return;
const double cur_sec = can->currentSec(); const double cur_sec = can->currentSec();
if (!is_zoomed) { if (!is_zoomed) {
double pos = (cur_sec - display_range.first) / std::max(1.0, (display_range.second - display_range.first)); double pos = (cur_sec - display_range.first) / std::max<float>(1.0, max_chart_range);
if (pos < 0 || pos > 0.8) { if (pos < 0 || pos > 0.8) {
display_range.first = std::max(0.0, cur_sec - max_chart_range * 0.1); display_range.first = std::max(0.0, cur_sec - max_chart_range * 0.1);
} }
double max_event_sec = events->empty() ? 0 : (events->back()->mono_time / 1e9 - can->routeStartTime()); double max_sec = std::min(std::floor(display_range.first + max_chart_range), can->lastEventSecond());
double max_sec = std::min(std::floor(display_range.first + max_chart_range), max_event_sec);
display_range.first = std::max(0.0, max_sec - max_chart_range); display_range.first = std::max(0.0, max_sec - max_chart_range);
display_range.second = display_range.first + max_chart_range; display_range.second = display_range.first + max_chart_range;
} else if (cur_sec < zoomed_range.first || cur_sec >= zoomed_range.second) { } else if (cur_sec < zoomed_range.first || cur_sec >= zoomed_range.second) {
// loop in zoommed range // loop in zoomed range
can->seekTo(zoomed_range.first); can->seekTo(zoomed_range.first);
} }
@ -171,9 +201,10 @@ void ChartsWidget::setMaxChartRange(int value) {
void ChartsWidget::updateToolBar() { void ChartsWidget::updateToolBar() {
title_label->setText(tr("Charts: %1").arg(charts.size())); title_label->setText(tr("Charts: %1").arg(charts.size()));
columns_action->setText(tr("Column: %1").arg(column_count)); columns_action->setText(tr("Column: %1").arg(column_count));
range_lb->setText(QString("Range: %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); range_lb->setText(QString("Range: %1 ").arg(utils::formatSeconds(max_chart_range)));
range_lb_action->setVisible(!is_zoomed); range_lb_action->setVisible(!is_zoomed);
range_slider_action->setVisible(!is_zoomed); range_slider_action->setVisible(!is_zoomed);
undo_zoom_action->setVisible(is_zoomed);
reset_zoom_action->setVisible(is_zoomed); reset_zoom_action->setVisible(is_zoomed);
reset_zoom_action->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); reset_zoom_action->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : "");
remove_all_btn->setEnabled(!charts.isEmpty()); remove_all_btn->setEnabled(!charts.isEmpty());
@ -203,10 +234,11 @@ ChartView *ChartsWidget::createChart() {
chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight);
QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); });
QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn);
QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); QObject::connect(chart, &ChartView::zoomUndo, this, &ChartsWidget::zoomUndo);
QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged);
QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged);
QObject::connect(chart, &ChartView::axisYLabelWidthChanged, this, &ChartsWidget::alignCharts); QObject::connect(chart, &ChartView::axisYLabelWidthChanged, &align_timer, qOverload<>(&QTimer::start));
QObject::connect(chart, &ChartView::hovered, this, &ChartsWidget::showValueTip);
charts.push_back(chart); charts.push_back(chart);
updateLayout(); updateLayout();
return chart; return chart;
@ -308,9 +340,26 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
return false; return false;
} }
bool ChartsWidget::event(QEvent *event) {
bool back_button = false;
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *ev = static_cast<QMouseEvent *>(event);
back_button = ev->button() == Qt::BackButton;
} else if (event->type() == QEvent::NativeGesture) { // MacOS emulates a back swipe on pressing the mouse back button
QNativeGestureEvent *ev = static_cast<QNativeGestureEvent *>(event);
back_button = (ev->value() == 180);
}
if (back_button) {
zoomUndo();
return true;
}
return QFrame::event(event);
}
// ChartView // ChartView
ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { ChartView::ChartView(QWidget *parent) : tip_label(this), QChartView(nullptr, parent) {
series_type = (SeriesType)settings.chart_series_type; series_type = (SeriesType)settings.chart_series_type;
QChart *chart = new QChart(); QChart *chart = new QChart();
chart->setBackgroundVisible(false); chart->setBackgroundVisible(false);
@ -318,7 +367,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
axis_y = new QValueAxis(this); axis_y = new QValueAxis(this);
chart->addAxis(axis_x, Qt::AlignBottom); chart->addAxis(axis_x, Qt::AlignBottom);
chart->addAxis(axis_y, Qt::AlignLeft); chart->addAxis(axis_y, Qt::AlignLeft);
chart->legend()->layout()->setContentsMargins(16, 0, 40, 0); chart->legend()->layout()->setContentsMargins(0, 0, 0, 0);
chart->legend()->setShowToolTips(true); chart->legend()->setShowToolTips(true);
chart->setMargins({0, 0, 0, 0}); chart->setMargins({0, 0, 0, 0});
@ -333,6 +382,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
setRenderHint(QPainter::Antialiasing); setRenderHint(QPainter::Antialiasing);
// TODO: enable zoomIn/seekTo in live streaming mode. // TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand); setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand);
setMouseTracking(true);
QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated);
@ -443,24 +493,34 @@ void ChartView::manageSeries() {
} }
void ChartView::resizeEvent(QResizeEvent *event) { void ChartView::resizeEvent(QResizeEvent *event) {
updatePlotArea(align_to); qreal left, top, right, bottom;
int top_margin = style()->pixelMetric(QStyle::PM_LayoutTopMargin); chart()->layout()->getContentsMargins(&left, &top, &right, &bottom);
int spacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); move_icon->setPos(left, top);
int x = event->size().width() - close_btn_proxy->size().width() - style()->pixelMetric(QStyle::PM_LayoutRightMargin); close_btn_proxy->setPos(rect().right() - right - close_btn_proxy->size().width(), top);
close_btn_proxy->setPos(x, top_margin); int x = close_btn_proxy->pos().x() - manage_btn_proxy->size().width() - style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - spacing, top_margin); manage_btn_proxy->setPos(x, top);
move_icon->setPos(style()->pixelMetric(QStyle::PM_LayoutLeftMargin), top_margin); chart()->legend()->setGeometry({move_icon->sceneBoundingRect().topRight(), manage_btn_proxy->sceneBoundingRect().bottomLeft()});
if (align_to > 0) {
updatePlotArea(align_to);
}
QChartView::resizeEvent(event); QChartView::resizeEvent(event);
} }
void ChartView::updatePlotArea(int left) { void ChartView::updatePlotArea(int left_pos) {
QRect r = rect(); if (align_to != left_pos || rect() != background->rect()) {
if (align_to != left || r != background->rect()) { align_to = left_pos;
align_to = left; background->setRect(rect());
background->setRect(r);
chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45)); qreal left, top, right, bottom;
chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 36, r.height() - 80)); chart()->layout()->getContentsMargins(&left, &top, &right, &bottom);
QSizeF x_label_size = QFontMetrics(axis_x->labelsFont()).size(Qt::TextSingleLine, QString::number(axis_x->max(), 'f', 2));
x_label_size += QSizeF{5 * devicePixelRatioF(), 5 * devicePixelRatioF()};
int adjust_top = chart()->legend()->geometry().height() + style()->pixelMetric(QStyle::PM_LayoutTopMargin);
chart()->setPlotArea(rect().adjusted(align_to + left, adjust_top + top, -x_label_size.width() / 2 - right, -x_label_size.height() - bottom));
chart()->layout()->invalidate(); chart()->layout()->invalidate();
if (can->isPaused()) {
update();
}
} }
} }
@ -481,86 +541,60 @@ void ChartView::updatePlot(double cur, double min, double max) {
updateAxisY(); updateAxisY();
updateSeriesPoints(); updateSeriesPoints();
} }
scene()->invalidate({}, QGraphicsScene::ForegroundLayer); scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
} }
void ChartView::updateSeriesPoints() { void ChartView::updateSeriesPoints() {
// Show points when zoomed in enough // Show points when zoomed in enough
for (auto &s : sigs) { for (auto &s : sigs) {
auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), xLessThan);
auto end = std::lower_bound(begin, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); auto end = std::lower_bound(begin, s.vals.end(), axis_x->max(), xLessThan);
if (begin != end) {
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; QPointF right_pt = end == s.vals.end() ? s.vals.back() : *end;
double pixels_per_point = (chart()->mapToPosition(right_pt).x() - chart()->mapToPosition(*begin).x()) / num_points;
if (series_type == SeriesType::Scatter) {
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8)); if (series_type == SeriesType::Scatter) {
} else { qreal size = std::clamp(pixels_per_point / 2.0, 2.0, 8.0);
s.series->setPointsVisible(pixels_per_point > 20); if (s.series->useOpenGL()) {
size *= devicePixelRatioF();
}
((QScatterSeries *)s.series)->setMarkerSize(size);
} else {
s.series->setPointsVisible(pixels_per_point > 20);
}
} }
} }
} }
void ChartView::updateSeries(const cabana::Signal *sig, const std::vector<Event *> *events, bool clear) { void ChartView::updateSeries(const cabana::Signal *sig) {
events = events ? events : can->events();
for (auto &s : sigs) { for (auto &s : sigs) {
if (!sig || s.sig == sig) { if (!sig || s.sig == sig) {
if (clear) { if (!can->liveStreaming()) {
s.vals.clear(); s.vals.clear();
s.step_vals.clear(); s.step_vals.clear();
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));
struct Chunk { const auto &msgs = can->events().at(s.msg_id);
std::vector<Event *>::const_iterator first, second; auto first = std::upper_bound(msgs.cbegin(), msgs.cend(), CanEvent{.mono_time = s.last_value_mono_time});
QVector<QPointF> vals; int new_size = std::max<int>(s.vals.size() + std::distance(first, msgs.cend()), settings.max_cached_minutes * 60 * 100);
QVector<QPointF> step_vals; if (s.vals.capacity() <= new_size) {
}; s.vals.reserve(new_size * 2);
// split into one minitue chunks s.step_vals.reserve(new_size * 4);
QVector<Chunk> chunks;
Event begin_event(cereal::Event::Which::INIT_DATA, s.last_value_mono_time);
auto begin = std::upper_bound(events->begin(), events->end(), &begin_event, Event::lessThan());
for (auto it = begin, second = begin; it != events->end(); it = second) {
second = std::lower_bound(it, events->end(), (*it)->mono_time + 1e9 * 60, [](auto &e, uint64_t ts) { return e->mono_time < ts; });
chunks.push_back({it, second});
} }
QtConcurrent::blockingMap(chunks, [&](Chunk &chunk) { const double route_start_time = can->routeStartTime();
chunk.vals.reserve(60 * 100); // 100 hz for (auto end = msgs.cend(); first != end; ++first) {
chunk.step_vals.reserve(60 * 100 * 2); // 100 hz double value = get_raw_value(first->dat, first->size, *s.sig);
double route_start_time = can->routeStartTime(); double ts = first->mono_time / 1e9 - route_start_time; // seconds
for (auto it = chunk.first; it != chunk.second; ++it) { s.vals.append({ts, value});
if ((*it)->which == cereal::Event::Which::CAN) { if (!s.step_vals.empty()) {
for (const auto &c : (*it)->event.getCan()) { s.step_vals.append({ts, s.step_vals.back().y()});
if (s.msg_id.address == c.getAddress() && s.msg_id.source == c.getSrc()) {
auto dat = c.getDat();
double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig);
double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds
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});
}
}
}
}
});
for (auto &c : chunks) {
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);
} }
} s.step_vals.append({ts, value});
if (events->size()) { s.last_value_mono_time = first->mono_time;
s.last_value_mono_time = events->back()->mono_time;
} }
if (!can->liveStreaming()) { if (!can->liveStreaming()) {
s.segment_tree.build(s.vals); s.segment_tree.build(s.vals);
@ -587,25 +621,29 @@ void ChartView::updateAxisY() {
unit.clear(); 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(), xLessThan);
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(), xLessThan);
s.min = std::numeric_limits<double>::max();
s.max = std::numeric_limits<double>::lowest();
if (can->liveStreaming()) { if (can->liveStreaming()) {
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
if (it->y() < min) min = it->y(); if (it->y() < s.min) s.min = it->y();
if (it->y() > max) max = it->y(); if (it->y() > s.max) s.max = it->y();
} }
} else { } else {
auto [min_y, max_y] = s.segment_tree.minmax(std::distance(s.vals.begin(), first), std::distance(s.vals.begin(), last)); 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); s.min = min_y;
max = std::max(max, max_y); s.max = max_y;
} }
min = std::min(min, s.min);
max = std::max(max, s.max);
} }
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) { if (axis_y->titleText() != unit) {
axis_y->setTitleText(unit); axis_y->setTitleText(unit);
y_label_width = 0;// recalc width 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;
@ -614,10 +652,10 @@ void ChartView::updateAxisY() {
axis_y->setRange(min_y, max_y); axis_y->setRange(min_y, max_y);
axis_y->setTickCount(tick_count); axis_y->setTickCount(tick_count);
int title_spacing = unit.isEmpty() ? 0 : QFontMetrics(axis_y->titleFont()).size(Qt::TextSingleLine, unit).height();
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;
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;
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);
} }
@ -651,8 +689,9 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) {
} }
void ChartView::leaveEvent(QEvent *event) { void ChartView::leaveEvent(QEvent *event) {
clearTrackPoints(); if (tip_label.isVisible()) {
scene()->update(); emit hovered(-1);
}
QChartView::leaveEvent(event); QChartView::leaveEvent(event);
} }
@ -664,10 +703,7 @@ void ChartView::mousePressEvent(QMouseEvent *event) {
drag->setMimeData(mimeData); drag->setMimeData(mimeData);
drag->setPixmap(grab()); drag->setPixmap(grab());
drag->setHotSpot(event->pos()); drag->setHotSpot(event->pos());
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction); drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction);
if (dropAction == Qt::MoveAction) {
return;
}
} else if (event->button() == Qt::LeftButton && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { } else if (event->button() == Qt::LeftButton && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) {
if (!can->liveStreaming()) { if (!can->liveStreaming()) {
// Save current playback state when scrubbing // Save current playback state when scrubbing
@ -675,7 +711,6 @@ void ChartView::mousePressEvent(QMouseEvent *event) {
if (resume_after_scrub) { if (resume_after_scrub) {
can->pause(true); can->pause(true);
} }
is_scrubbing = true; is_scrubbing = true;
} }
} else { } else {
@ -700,13 +735,14 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
if (rubber->width() <= 0) { if (rubber->width() <= 0) {
// no rubber dragged, seek to mouse position // no rubber dragged, seek to mouse position
can->seekTo(min); can->seekTo(min);
} else if ((max_rounded - min_rounded) >= 0.5) { } else if (rubber->width() > 10) {
// zoom in if selected range is greater than 0.5s
emit zoomIn(min_rounded, max_rounded); emit zoomIn(min_rounded, max_rounded);
} else {
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
} }
event->accept(); event->accept();
} else if (!can->liveStreaming() && event->button() == Qt::RightButton) { } else if (!can->liveStreaming() && event->button() == Qt::RightButton) {
emit zoomReset(); emit zoomUndo();
event->accept(); event->accept();
} else { } else {
QGraphicsView::mouseReleaseEvent(event); QGraphicsView::mouseReleaseEvent(event);
@ -721,50 +757,23 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) {
} }
void ChartView::mouseMoveEvent(QMouseEvent *ev) { void ChartView::mouseMoveEvent(QMouseEvent *ev) {
const auto plot_area = chart()->plotArea();
// Scrubbing // Scrubbing
if (is_scrubbing && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { if (is_scrubbing && QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) {
if (chart()->plotArea().contains(ev->pos())) { if (plot_area.contains(ev->pos())) {
double t = chart()->mapToValue(ev->pos()).x(); can->seekTo(std::clamp(chart()->mapToValue(ev->pos()).x(), 0., can->totalSeconds()));
// Prevent seeking past the end of the route
t = std::clamp(t, 0., can->totalSeconds());
can->seekTo(t);
} }
return;
} }
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();
clearTrackPoints(); clearTrackPoints();
if (!is_zooming && plot_area.contains(ev->pos())) { if (!is_zooming && plot_area.contains(ev->pos())) {
QStringList text_list;
const double sec = chart()->mapToValue(ev->pos()).x(); const double sec = chart()->mapToValue(ev->pos()).x();
qreal x = -1; emit hovered(sec);
for (auto &s : sigs) { } else if (tip_label.isVisible()) {
if (!s.series->isVisible()) continue; emit hovered(-1);
// use reverse iterator to find last item <= sec.
double value = 0;
auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; });
if (it != s.vals.rend() && it->x() >= axis_x->min()) {
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(s.series->color().name(), s.sig->name,
s.track_pt.isNull() ? "--" : QString::number(value)));
}
if (x < 0) {
x = ev->pos().x();
}
text_list.push_front(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3));
QPointF tooltip_pt(x + 12, plot_area.top() - 20);
QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), text_list.join("<br />"), this, plot_area.toRect());
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
} else {
QToolTip::hideText();
} }
QChartView::mouseMoveEvent(ev); QChartView::mouseMoveEvent(ev);
@ -775,7 +784,37 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
if (rubber_rect != rubber->geometry()) { if (rubber_rect != rubber->geometry()) {
rubber->setGeometry(rubber_rect); rubber->setGeometry(rubber_rect);
} }
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
}
}
void ChartView::showTip(double sec) {
qreal x = chart()->mapToPosition({sec, 0}).x();
QStringList text_list(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3));
for (auto &s : sigs) {
if (s.series->isVisible()) {
QString value = "--";
// use reverse iterator to find last item <= sec.
auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; });
if (it != s.vals.rend() && it->x() >= axis_x->min()) {
value = QString::number(it->y());
s.track_pt = chart()->mapToPosition(*it);
x = std::max(x, s.track_pt.x());
}
text_list << QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b> (%4 - %5)")
.arg(s.series->color().name(), s.sig->name, value, QString::number(s.min), QString::number(s.max));
}
} }
QPointF tooltip_pt(x, chart()->plotArea().top());
int plot_right = mapToGlobal(chart()->plotArea().topRight().toPoint()).x();
tip_label.showText(mapToGlobal(tooltip_pt.toPoint()), "<p style='white-space:pre'>" + text_list.join("<br />") + "</p>", plot_right);
scene()->update();
}
void ChartView::hideTip() {
clearTrackPoints();
tip_label.hide();
scene()->update();
} }
void ChartView::dragMoveEvent(QDragMoveEvent *event) { void ChartView::dragMoveEvent(QDragMoveEvent *event) {
@ -833,14 +872,29 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
for (auto &s : sigs) { for (auto &s : sigs) {
if (s.series->useOpenGL() && s.series->isVisible() && s.series->pointsVisible()) { if (s.series->useOpenGL() && s.series->isVisible() && s.series->pointsVisible()) {
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(), xLessThan);
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(), xLessThan);
painter->setBrush(s.series->color());
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
painter->setBrush(s.series->color());
painter->drawEllipse(chart()->mapToPosition(*it), 4, 4); painter->drawEllipse(chart()->mapToPosition(*it), 4, 4);
} }
} }
} }
// paint zoom range
auto rubber = findChild<QRubberBand *>();
if (rubber && rubber->isVisible() && rubber->width() > 1) {
painter->setPen(Qt::white);
auto rubber_rect = rubber->geometry().normalized();
for (const auto &pt : {rubber_rect.bottomLeft(), rubber_rect.bottomRight()}) {
QString sec = QString::number(chart()->mapToValue(pt).x(), 'f', 1);
// ChartAxisElement's padding is 4 (https://codebrowser.dev/qt5/qtcharts/src/charts/axis/chartaxiselement_p.h.html)
auto r = painter->fontMetrics().boundingRect(sec).adjusted(-6, -4, 6, 4);
pt == rubber_rect.bottomLeft() ? r.moveTopRight(pt + QPoint{0, 2}) : r.moveTopLeft(pt + QPoint{0, 2});
painter->fillRect(r, Qt::gray);
painter->drawText(r, Qt::AlignCenter, sec);
}
}
} }
QXYSeries *ChartView::createSeries(SeriesType type, QColor color) { QXYSeries *ChartView::createSeries(SeriesType type, QColor color) {
@ -856,18 +910,25 @@ QXYSeries *ChartView::createSeries(SeriesType type, QColor color) {
chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle); chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle);
} }
series->setColor(color); series->setColor(color);
// TODO: Due to a bug in CameraWidget the camera frames // TODO: Due to a bug in CameraWidget the camera frames
// are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed
#ifndef __APPLE__ #ifndef __APPLE__
series->setUseOpenGL(true); series->setUseOpenGL(true);
// Qt doesn't properly apply device pixel ratio in OpenGL mode // Qt doesn't properly apply device pixel ratio in OpenGL mode
QPen pen = series->pen(); QPen pen = series->pen();
pen.setWidth(2.0 * qApp->devicePixelRatio()); pen.setWidthF(2.0 * devicePixelRatioF());
series->setPen(pen); series->setPen(pen);
#endif #endif
chart()->addSeries(series); chart()->addSeries(series);
series->attachAxis(axis_x); series->attachAxis(axis_x);
series->attachAxis(axis_y); series->attachAxis(axis_y);
// disables the delivery of mouse events to the opengl widget.
// this enables the user to select the zoom area when the mouse press on the data point.
auto glwidget = findChild<QOpenGLWidget *>();
if (glwidget && !glwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
glwidget->setAttribute(Qt::WA_TransparentForMouseEvents);
}
return series; return series;
} }
@ -936,7 +997,7 @@ SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent)
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
main_layout->addWidget(buttonBox, 3, 2); main_layout->addWidget(buttonBox, 3, 2);
for (auto it = can->can_msgs.cbegin(); it != can->can_msgs.cend(); ++it) { for (auto it = can->last_msgs.cbegin(); it != can->last_msgs.cend(); ++it) {
if (auto m = dbc()->msg(it.key())) { if (auto m = dbc()->msg(it.key())) {
msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key().toString()), QVariant::fromValue(it.key())); msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key().toString()), QVariant::fromValue(it.key()));
} }
@ -950,7 +1011,7 @@ SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent)
QObject::connect(available_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::add); QObject::connect(available_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::add);
QObject::connect(selected_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::remove); QObject::connect(selected_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::remove);
QObject::connect(add_btn, &QPushButton::clicked, [this]() { if (auto item = available_list->currentItem()) add(item); }); QObject::connect(add_btn, &QPushButton::clicked, [this]() { if (auto item = available_list->currentItem()) add(item); });
QObject::connect(remove_btn, &QPushButton::clicked, [this]() { if (auto item = selected_list->currentItem()) remove(item);}); QObject::connect(remove_btn, &QPushButton::clicked, [this]() { if (auto item = selected_list->currentItem()) remove(item); });
QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
} }
@ -998,3 +1059,39 @@ QList<SeriesSelector::ListItem *> SeriesSelector::seletedItems() {
for (int i = 0; i < selected_list->count(); ++i) ret.push_back((ListItem *)selected_list->item(i)); for (int i = 0; i < selected_list->count(); ++i) ret.push_back((ListItem *)selected_list->item(i));
return ret; return ret;
} }
// ValueTipLabel
ValueTipLabel::ValueTipLabel(QWidget *parent) : QLabel(parent, Qt::Tool | Qt::FramelessWindowHint) {
setForegroundRole(QPalette::ToolTipText);
setBackgroundRole(QPalette::ToolTipBase);
setPalette(QToolTip::palette());
ensurePolished();
setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, this));
setAttribute(Qt::WA_ShowWithoutActivating);
setTextFormat(Qt::RichText);
setVisible(false);
}
void ValueTipLabel::showText(const QPoint &pt, const QString &text, int right_edge) {
setText(text);
if (!text.isEmpty()) {
QSize extra(1, 1);
resize(sizeHint() + extra);
QPoint tip_pos(pt.x() + 12, pt.y());
if (tip_pos.x() + size().width() >= right_edge) {
tip_pos.rx() = pt.x() - size().width() - 12;
}
move(tip_pos);
}
setVisible(!text.isEmpty());
}
void ValueTipLabel::paintEvent(QPaintEvent *ev) {
QStylePainter p(this);
QStyleOptionFrame opt;
opt.init(this);
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
p.end();
QLabel::paintEvent(ev);
}

@ -1,19 +1,19 @@
#pragma once #pragma once
#include <QDragEnterEvent>
#include <QGridLayout> #include <QGridLayout>
#include <QLabel> #include <QLabel>
#include <QListWidget> #include <QListWidget>
#include <QGraphicsPixmapItem> #include <QGraphicsPixmapItem>
#include <QGraphicsProxyWidget> #include <QGraphicsProxyWidget>
#include <QSlider> #include <QStack>
#include <QTimer>
#include <QtCharts/QChartView> #include <QtCharts/QChartView>
#include <QtCharts/QLegendMarker> #include <QtCharts/QLegendMarker>
#include <QtCharts/QLineSeries> #include <QtCharts/QLineSeries>
#include <QtCharts/QScatterSeries> #include <QtCharts/QScatterSeries>
#include <QtCharts/QValueAxis> #include <QtCharts/QValueAxis>
#include "tools/cabana/dbcmanager.h" #include "tools/cabana/dbc/dbcmanager.h"
#include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/streams/abstractstream.h"
using namespace QtCharts; using namespace QtCharts;
@ -25,6 +25,13 @@ enum class SeriesType {
Scatter Scatter
}; };
class ValueTipLabel : public QLabel {
public:
ValueTipLabel(QWidget *parent = nullptr);
void showText(const QPoint &pt, const QString &sec, int right_edge);
void paintEvent(QPaintEvent *ev) override;
};
class ChartView : public QChartView { class ChartView : public QChartView {
Q_OBJECT Q_OBJECT
@ -32,10 +39,12 @@ public:
ChartView(QWidget *parent = nullptr); ChartView(QWidget *parent = nullptr);
void addSeries(const MessageId &msg_id, const cabana::Signal *sig); void addSeries(const MessageId &msg_id, const cabana::Signal *sig);
bool hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const; bool hasSeries(const MessageId &msg_id, const cabana::Signal *sig) const;
void updateSeries(const cabana::Signal *sig = nullptr, const std::vector<Event*> *events = nullptr, bool clear = true); void updateSeries(const cabana::Signal *sig = nullptr);
void updatePlot(double cur, double min, double max); void updatePlot(double cur, double min, double max);
void setSeriesType(SeriesType type); void setSeriesType(SeriesType type);
void updatePlotArea(int left); void updatePlotArea(int left);
void showTip(double sec);
void hideTip();
struct SigItem { struct SigItem {
MessageId msg_id; MessageId msg_id;
@ -46,15 +55,18 @@ public:
uint64_t last_value_mono_time = 0; uint64_t last_value_mono_time = 0;
QPointF track_pt{}; QPointF track_pt{};
SegmentTree segment_tree; SegmentTree segment_tree;
double min = 0;
double max = 0;
}; };
signals: signals:
void seriesRemoved(const MessageId &id, const cabana::Signal *sig); void seriesRemoved(const MessageId &id, const cabana::Signal *sig);
void seriesAdded(const MessageId &id, const cabana::Signal *sig); void seriesAdded(const MessageId &id, const cabana::Signal *sig);
void zoomIn(double min, double max); void zoomIn(double min, double max);
void zoomReset(); void zoomUndo();
void remove(); void remove();
void axisYLabelWidthChanged(int w); void axisYLabelWidthChanged(int w);
void hovered(double sec);
private slots: private slots:
void signalUpdated(const cabana::Signal *sig); void signalUpdated(const cabana::Signal *sig);
@ -92,6 +104,7 @@ private:
QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *close_btn_proxy;
QGraphicsProxyWidget *manage_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy;
QGraphicsRectItem *background; QGraphicsRectItem *background;
ValueTipLabel tip_label;
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";
@ -120,18 +133,22 @@ signals:
private: private:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
bool event(QEvent *event) override;
void alignCharts(); void alignCharts();
void newChart(); void newChart();
ChartView * createChart(); ChartView *createChart();
void removeChart(ChartView *chart); void removeChart(ChartView *chart);
void eventsMerged(); void eventsMerged();
void updateState(); void updateState();
void zoomIn(double min, double max); void zoomIn(double min, double max);
void zoomReset(); void zoomReset();
void zoomUndo();
void setZoom(double min, double max);
void updateToolBar(); void updateToolBar();
void setMaxChartRange(int value); void setMaxChartRange(int value);
void updateLayout(); void updateLayout();
void settingChanged(); void settingChanged();
void showValueTip(double sec);
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
ChartView *findChart(const MessageId &id, const cabana::Signal *sig); ChartView *findChart(const MessageId &id, const cabana::Signal *sig);
@ -142,18 +159,23 @@ private:
QAction *range_slider_action; QAction *range_slider_action;
bool docking = true; bool docking = true;
QAction *dock_btn; QAction *dock_btn;
QAction *undo_zoom_action;
QAction *reset_zoom_action; QAction *reset_zoom_action;
QAction *remove_all_btn; QAction *remove_all_btn;
QGridLayout *charts_layout; QGridLayout *charts_layout;
QList<ChartView *> charts; QList<ChartView *> charts;
QWidget *charts_container;
QScrollArea *charts_scroll;
uint32_t max_chart_range = 0; uint32_t max_chart_range = 0;
bool is_zoomed = false; bool is_zoomed = false;
std::pair<double, double> display_range; std::pair<double, double> display_range;
std::pair<double, double> zoomed_range; std::pair<double, double> zoomed_range;
QStack<QPair<double, double>> zoom_stack;
bool use_dark_theme = false; bool use_dark_theme = false;
QAction *columns_action; QAction *columns_action;
int column_count = 1; int column_count = 1;
int current_column_count = 0; int current_column_count = 0;
QTimer align_timer;
}; };
class SeriesSelector : public QDialog { class SeriesSelector : public QDialog {

@ -3,7 +3,7 @@
#include <QUndoCommand> #include <QUndoCommand>
#include <QUndoStack> #include <QUndoStack>
#include "tools/cabana/dbcmanager.h" #include "tools/cabana/dbc/dbcmanager.h"
#include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/streams/abstractstream.h"
class EditMsgCommand : public QUndoCommand { class EditMsgCommand : public QUndoCommand {

@ -1,4 +1,4 @@
#include "tools/cabana/dbc.h" #include "tools/cabana/dbc/dbc.h"
#include "tools/cabana/util.h" #include "tools/cabana/util.h"
uint qHash(const MessageId &item) { uint qHash(const MessageId &item) {
@ -27,7 +27,7 @@ static QVector<int> BIG_ENDIAN_START_BITS = []() {
return ret; return ret;
}(); }();
double get_raw_value(uint8_t *data, size_t data_size, const cabana::Signal &sig) { double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig) {
int64_t val = 0; int64_t val = 0;
int i = sig.msb / 8; int i = sig.msb / 8;

@ -38,6 +38,10 @@ struct MessageId {
uint qHash(const MessageId &item); uint qHash(const MessageId &item);
Q_DECLARE_METATYPE(MessageId); Q_DECLARE_METATYPE(MessageId);
template <>
struct std::hash<MessageId> {
std::size_t operator()(const MessageId &k) const noexcept { return qHash(k); }
};
typedef QList<std::pair<QString, QString>> ValueDescription; typedef QList<std::pair<QString, QString>> ValueDescription;
@ -72,7 +76,7 @@ namespace cabana {
} }
// Helper functions // Helper functions
double get_raw_value(uint8_t *data, size_t data_size, const cabana::Signal &sig); double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig);
int bigEndianStartBitsIndex(int start_bit); int bigEndianStartBitsIndex(int start_bit);
int bigEndianBitIndex(int index); int bigEndianBitIndex(int index);
void updateSigSizeParamsFromRange(cabana::Signal &s, int start_bit, int size); void updateSigSizeParamsFromRange(cabana::Signal &s, int start_bit, int size);

@ -1,58 +1,200 @@
#include "tools/cabana/dbcmanager.h" #include "tools/cabana/dbc/dbcfile.h"
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QRegularExpression> #include <QRegularExpression>
#include <QTextStream> #include <QTextStream>
#include <QVector> #include <QVector>
#include <limits> #include <limits>
#include <sstream> #include <sstream>
bool DBCManager::open(const QString &dbc_file_name, QString *error) {
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name); DBCFile::DBCFile(const QString &dbc_file_name, QObject *parent) : QObject(parent) {
QFile file(opendbc_file_path); QFile file(dbc_file_name);
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
return open(dbc_file_name, file.readAll(), error); name_ = QFileInfo(dbc_file_name).baseName();
// Remove auto save file extension
if (dbc_file_name.endsWith(AUTO_SAVE_EXTENSION)) {
filename = dbc_file_name.left(dbc_file_name.length() - AUTO_SAVE_EXTENSION.length());
} else {
filename = dbc_file_name;
}
open(file.readAll());
} else {
throw std::runtime_error("Failed to open file.");
} }
return false; }
}
DBCFile::DBCFile(const QString &name, const QString &content, QObject *parent) : QObject(parent), name_(name), filename("") {
bool DBCManager::open(const QString &name, const QString &content, QString *error) { // Open from clipboard
try { open(content);
std::istringstream stream(content.toStdString()); }
auto dbc = const_cast<DBC *>(dbc_parse_from_stream(name.toStdString(), stream));
msgs.clear(); void DBCFile::open(const QString &content) {
for (auto &msg : dbc->msgs) { std::istringstream stream(content.toStdString());
auto &m = msgs[msg.address]; auto dbc = const_cast<DBC *>(dbc_parse_from_stream(name_.toStdString(), stream));
m.name = msg.name.c_str(); msgs.clear();
m.size = msg.size; for (auto &msg : dbc->msgs) {
for (auto &s : msg.sigs) { auto &m = msgs[msg.address];
m.sigs.push_back({}); m.name = msg.name.c_str();
auto &sig = m.sigs.last(); m.size = msg.size;
sig.name = s.name.c_str(); for (auto &s : msg.sigs) {
sig.start_bit = s.start_bit; m.sigs.push_back({});
sig.msb = s.msb; auto &sig = m.sigs.last();
sig.lsb = s.lsb; sig.name = s.name.c_str();
sig.size = s.size; sig.start_bit = s.start_bit;
sig.is_signed = s.is_signed; sig.msb = s.msb;
sig.factor = s.factor; sig.lsb = s.lsb;
sig.offset = s.offset; sig.size = s.size;
sig.is_little_endian = s.is_little_endian; sig.is_signed = s.is_signed;
sig.updatePrecision(); sig.factor = s.factor;
} sig.offset = s.offset;
sig.is_little_endian = s.is_little_endian;
sig.updatePrecision();
} }
parseExtraInfo(content); }
name_ = name; parseExtraInfo(content);
emit DBCFileChanged();
delete dbc; delete dbc;
} catch (std::exception &e) { }
if (error) *error = e.what();
bool DBCFile::save() {
assert (!filename.isEmpty());
if (writeContents(filename)) {
cleanupAutoSaveFile();
return true;
} else {
return false;
}
}
bool DBCFile::saveAs(const QString &new_filename) {
filename = new_filename;
return save();
}
bool DBCFile::autoSave() {
if (!filename.isEmpty()) {
return writeContents(filename + AUTO_SAVE_EXTENSION);
} else {
return false; return false;
} }
return true;
} }
void DBCManager::parseExtraInfo(const QString &content) { void DBCFile::cleanupAutoSaveFile() {
if (!filename.isEmpty()) {
QFile::remove(filename + AUTO_SAVE_EXTENSION);
}
}
bool DBCFile::writeContents(const QString &fn) {
QFile file(fn);
if (file.open(QIODevice::WriteOnly)) {
file.write(generateDBC().toUtf8());
return true;
} else {
return false;
}
}
cabana::Signal *DBCFile::addSignal(const MessageId &id, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id.address))) {
m->sigs.push_back(sig);
return &m->sigs.last();
}
return nullptr;
}
cabana::Signal *DBCFile::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
if (auto s = (cabana::Signal *)m->sig(sig_name)) {
*s = sig;
return s;
}
}
return nullptr;
}
cabana::Signal *DBCFile::getSignal(const MessageId &id, const QString &sig_name) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; });
if (it != m->sigs.end()) {
return &(*it);
}
}
return nullptr;
}
void DBCFile::removeSignal(const MessageId &id, const QString &sig_name) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; });
if (it != m->sigs.end()) {
m->sigs.erase(it);
}
}
}
void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto &m = msgs[id.address];
m.name = name;
m.size = size;
}
void DBCFile::removeMsg(const MessageId &id) {
msgs.erase(id.address);
}
std::map<uint32_t, cabana::Msg> DBCFile::getMessages() {
return msgs;
}
const cabana::Msg *DBCFile::msg(const MessageId &id) const {
return msg(id.address);
}
const cabana::Msg *DBCFile::msg(uint32_t address) const {
auto it = msgs.find(address);
return it != msgs.end() ? &it->second : nullptr;
}
const cabana::Msg* DBCFile::msg(const QString &name) {
for (auto &[_, msg] : msgs) {
if (msg.name == name) {
return &msg;
}
}
return nullptr;
}
QStringList DBCFile::signalNames() const {
// Used for autocompletion
QStringList ret;
for (auto const& [_, msg] : msgs) {
for (auto sig: msg.getSignals()) {
ret << sig->name;
}
}
ret.sort();
ret.removeDuplicates();
return ret;
}
int DBCFile::msgCount() const {
return msgs.size();
}
QString DBCFile::name() const {
return name_;
}
void DBCFile::parseExtraInfo(const QString &content) {
static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))");
static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
@ -108,7 +250,7 @@ void DBCManager::parseExtraInfo(const QString &content) {
} }
} }
QString DBCManager::generateDBC() { QString DBCFile::generateDBC() {
QString dbc_string, signal_comment, val_desc; QString dbc_string, signal_comment, val_desc;
for (auto &[address, m] : msgs) { for (auto &[address, m] : msgs) {
dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size); dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size);
@ -139,77 +281,3 @@ QString DBCManager::generateDBC() {
} }
return dbc_string + signal_comment + val_desc; return dbc_string + signal_comment + val_desc;
} }
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto &m = msgs[id.address];
m.name = name;
m.size = size;
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit msgUpdated({.source = source, .address = id.address});
}
}
void DBCManager::removeMsg(const MessageId &id) {
msgs.erase(id.address);
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit msgRemoved({.source = source, .address = id.address});
}
}
void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id.address))) {
m->sigs.push_back(sig);
auto s = &m->sigs.last();
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit signalAdded({.source = source, .address = id.address}, s);
}
}
}
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
if (auto s = (cabana::Signal *)m->sig(sig_name)) {
*s = sig;
emit signalUpdated(s);
}
}
}
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; });
if (it != m->sigs.end()) {
emit signalRemoved(&(*it));
m->sigs.erase(it);
}
}
}
QStringList DBCManager::signalNames() {
// Used for autocompletion
QStringList ret;
for (auto const& [_, msg] : msgs) {
for (auto sig: msg.getSignals()) {
ret << sig->name;
}
}
ret.sort();
ret.removeDuplicates();
return ret;
}
void DBCManager::updateSources(const QSet<uint8_t> &s) {
sources = s;
}
DBCManager *dbc() {
static DBCManager dbc_manager(nullptr);
return &dbc_manager;
}

@ -0,0 +1,55 @@
#pragma once
#include <map>
#include <QList>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QSet>
#include <QDebug>
#include "tools/cabana/dbc/dbc.h"
const QString AUTO_SAVE_EXTENSION = ".tmp";
class DBCFile : public QObject {
Q_OBJECT
public:
DBCFile(const QString &dbc_file_name, QObject *parent=nullptr);
DBCFile(const QString &name, const QString &content, QObject *parent=nullptr);
~DBCFile() {}
void open(const QString &content);
bool save();
bool saveAs(const QString &new_filename);
bool autoSave();
bool writeContents(const QString &fn);
void cleanupAutoSaveFile();
QString generateDBC();
cabana::Signal *addSignal(const MessageId &id, const cabana::Signal &sig);
cabana::Signal *updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
cabana::Signal *getSignal(const MessageId &id, const QString &sig_name);
void removeSignal(const MessageId &id, const QString &sig_name);
void updateMsg(const MessageId &id, const QString &name, uint32_t size);
void removeMsg(const MessageId &id);
std::map<uint32_t, cabana::Msg> getMessages();
const cabana::Msg *msg(const MessageId &id) const;
const cabana::Msg *msg(uint32_t address) const;
const cabana::Msg* msg(const QString &name);
QStringList signalNames() const;
int msgCount() const;
QString name() const;
QString filename;
private:
void parseExtraInfo(const QString &content);
std::map<uint32_t, cabana::Msg> msgs;
QString name_;
};

@ -0,0 +1,218 @@
#include "tools/cabana/dbc/dbcmanager.h"
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>
#include <QVector>
#include <limits>
#include <sstream>
bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error) {
for (int i = 0; i < dbc_files.size(); i++) {
auto [ss, dbc_file] = dbc_files[i];
// Check if file is already open, and merge sources
if (dbc_file->filename == dbc_file_name) {
dbc_files[i] = {ss | s, dbc_file};
emit DBCFileChanged();
return true;
}
// Check if there is already a file for this sourceset, then replace it
if (ss == s) {
try {
dbc_files[i] = {s, new DBCFile(dbc_file_name, this)};
delete dbc_file;
emit DBCFileChanged();
return true;
} catch (std::exception &e) {
if (error) *error = e.what();
return false;
}
}
}
try {
dbc_files.push_back({s, new DBCFile(dbc_file_name, this)});
} catch (std::exception &e) {
if (error) *error = e.what();
return false;
}
emit DBCFileChanged();
return true;
}
bool DBCManager::open(SourceSet s, const QString &name, const QString &content, QString *error) {
try {
dbc_files.push_back({s, new DBCFile(name, content, this)});
} catch (std::exception &e) {
if (error) *error = e.what();
return false;
}
emit DBCFileChanged();
return true;
}
void DBCManager::closeAll() {
while (dbc_files.size()) {
DBCFile *dbc_file = dbc_files.back().second;
dbc_files.pop_back();
delete dbc_file;
}
emit DBCFileChanged();
}
void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
auto sources_dbc_file = findDBCFile(id);
assert(sources_dbc_file); // Create new DBC?
auto [dbc_sources, dbc_file] = *sources_dbc_file;
cabana::Signal *s = dbc_file->addSignal(id, sig);
if (s != nullptr) {
for (uint8_t source : dbc_sources) {
emit signalAdded({.source = source, .address = id.address}, s);
}
}
}
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
auto sources_dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible
auto [_, dbc_file] = *sources_dbc_file;
cabana::Signal *s = dbc_file->updateSignal(id, sig_name, sig);
if (s != nullptr) {
emit signalUpdated(s);
}
}
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
auto sources_dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible
auto [_, dbc_file] = *sources_dbc_file;
cabana::Signal *s = dbc_file->getSignal(id, sig_name);
if (s != nullptr) {
emit signalRemoved(s);
dbc_file->removeSignal(id, sig_name);
}
}
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto sources_dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible
auto [dbc_sources, dbc_file] = *sources_dbc_file;
dbc_file->updateMsg(id, name, size);
for (uint8_t source : dbc_sources) {
emit msgUpdated({.source = source, .address = id.address});
}
}
void DBCManager::removeMsg(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible
auto [dbc_sources, dbc_file] = *sources_dbc_file;
dbc_file->removeMsg(id);
for (uint8_t source : dbc_sources) {
emit msgRemoved({.source = source, .address = id.address});
}
}
std::map<MessageId, cabana::Msg> DBCManager::getMessages(uint8_t source) {
std::map<MessageId, cabana::Msg> ret;
auto sources_dbc_file = findDBCFile({.source = source, .address = 0});
if (!sources_dbc_file) {
return ret;
}
auto [_, dbc_file] = *sources_dbc_file;
for (auto &[address, msg] : dbc_file->getMessages()) {
MessageId id = {.source = source, .address = address};
ret[id] = msg;
}
return ret;
}
const cabana::Msg *DBCManager::msg(const MessageId &id) const {
auto sources_dbc_file = findDBCFile(id);
if (!sources_dbc_file) {
return nullptr;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->msg(id);
}
const cabana::Msg* DBCManager::msg(uint8_t source, const QString &name) {
auto sources_dbc_file = findDBCFile({.source = source, .address = 0});
if (!sources_dbc_file) {
return nullptr;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->msg(name);
}
QStringList DBCManager::signalNames() const {
QStringList ret;
for (auto &[_, dbc_file] : dbc_files) {
ret << dbc_file->signalNames();
}
ret.sort();
ret.removeDuplicates();
return ret;
}
int DBCManager::msgCount() const {
int ret = 0;
for (auto &[_, dbc_file] : dbc_files) {
ret += dbc_file->msgCount();
}
return ret;
}
int DBCManager::dbcCount() const {
return dbc_files.size();
}
void DBCManager::updateSources(const SourceSet &s) {
sources = s;
}
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const uint8_t source) const {
// Find DBC file that matches id.source, fall back to SOURCE_ALL if no specific DBC is found
for (auto &[source_set, dbc_file] : dbc_files) {
if (source_set.contains(source)) return {{source_set, dbc_file}};
}
for (auto &[source_set, dbc_file] : dbc_files) {
if (source_set == SOURCE_ALL) return {{sources, dbc_file}};
}
return {};
}
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const MessageId &id) const {
return findDBCFile(id.source);
}
DBCManager *dbc() {
static DBCManager dbc_manager(nullptr);
return &dbc_manager;
}

@ -0,0 +1,69 @@
#pragma once
#include <map>
#include <optional>
#include <QList>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QSet>
#include <QDebug>
#include "tools/cabana/dbc/dbc.h"
#include "tools/cabana/dbc/dbcfile.h"
typedef QSet<uint8_t> SourceSet;
const SourceSet SOURCE_ALL = {};
class DBCManager : public QObject {
Q_OBJECT
public:
DBCManager(QObject *parent) {}
~DBCManager() {}
bool open(SourceSet s, const QString &dbc_file_name, QString *error = nullptr);
bool open(SourceSet s, const QString &name, const QString &content, QString *error = nullptr);
void closeAll();
void addSignal(const MessageId &id, const cabana::Signal &sig);
void updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
void removeSignal(const MessageId &id, const QString &sig_name);
void updateMsg(const MessageId &id, const QString &name, uint32_t size);
void removeMsg(const MessageId &id);
std::map<MessageId, cabana::Msg> getMessages(uint8_t source);
const cabana::Msg *msg(const MessageId &id) const;
const cabana::Msg* msg(uint8_t source, const QString &name);
QStringList signalNames() const;
int msgCount() const;
int dbcCount() const;
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const uint8_t source) const;
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const MessageId &id) const;
QList<std::pair<SourceSet, DBCFile*>> dbc_files;
private:
SourceSet sources;
public slots:
void updateSources(const SourceSet &s);
signals:
void signalAdded(MessageId id, const cabana::Signal *sig);
void signalRemoved(const cabana::Signal *sig);
void signalUpdated(const cabana::Signal *sig);
void msgUpdated(MessageId id);
void msgRemoved(MessageId id);
void DBCFileChanged();
};
DBCManager *dbc();
inline QString msgName(const MessageId &id) {
auto msg = dbc()->msg(id);
return msg ? msg->name : UNTITLED;
}

@ -1,78 +0,0 @@
#pragma once
#include <map>
#include <QList>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QSet>
#include <QDebug>
#include "tools/cabana/dbc.h"
class DBCManager : public QObject {
Q_OBJECT
public:
DBCManager(QObject *parent) {}
~DBCManager() {}
bool open(const QString &dbc_file_name, QString *error = nullptr);
bool open(const QString &name, const QString &content, QString *error = nullptr);
QString generateDBC();
void addSignal(const MessageId &id, const cabana::Signal &sig);
void updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
void removeSignal(const MessageId &id, const QString &sig_name);
inline int msgCount() const { return msgs.size(); }
inline QString name() const { return name_; }
void updateMsg(const MessageId &id, const QString &name, uint32_t size);
void removeMsg(const MessageId &id);
inline std::map<MessageId, cabana::Msg> getMessages(uint8_t source) {
std::map<MessageId, cabana::Msg> ret;
for (auto &[address, msg] : msgs) {
MessageId id = {.source = source, .address = address};
ret[id] = msg;
}
return ret;
}
inline const cabana::Msg *msg(const MessageId &id) const { return msg(id.address); }
inline const cabana::Msg* msg(uint8_t source, const QString &name) {
for (auto &[_, msg] : msgs) {
if (msg.name == name) {
return &msg;
}
}
return nullptr;
}
QStringList signalNames();
public slots:
void updateSources(const QSet<uint8_t> &s);
signals:
void signalAdded(MessageId id, const cabana::Signal *sig);
void signalRemoved(const cabana::Signal *sig);
void signalUpdated(const cabana::Signal *sig);
void msgUpdated(MessageId id);
void msgRemoved(MessageId id);
void DBCFileChanged();
private:
void parseExtraInfo(const QString &content);
std::map<uint32_t, cabana::Msg> msgs;
QString name_;
QSet<uint8_t> sources;
inline const cabana::Msg *msg(uint32_t address) const {
auto it = msgs.find(address);
return it != msgs.end() ? &it->second : nullptr;
}
};
DBCManager *dbc();
inline QString msgName(const MessageId &id) {
auto msg = dbc()->msg(id);
return msg ? msg->name : UNTITLED;
}

@ -48,7 +48,6 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
// msg widget // msg widget
splitter = new QSplitter(Qt::Vertical, this); splitter = new QSplitter(Qt::Vertical, this);
splitter->setAutoFillBackground(true);
splitter->addWidget(binary_view = new BinaryView(this)); splitter->addWidget(binary_view = new BinaryView(this));
splitter->addWidget(signal_view = new SignalView(charts, this)); splitter->addWidget(signal_view = new SignalView(charts, this));
binary_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); binary_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);

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

Loading…
Cancel
Save