Merge remote-tracking branch 'upstream/master' into hkg-fuzzy

pull/26939/head
Shane Smiskol 2 years ago
commit 7c44fbbbf2
  1. 2
      cereal
  2. 20
      docs/CARS.md
  3. 2
      panda
  4. 6
      selfdrive/car/fw_query_definitions.py
  5. 4
      selfdrive/car/honda/interface.py
  6. 40
      selfdrive/car/hyundai/tests/test_hyundai.py
  7. 4
      selfdrive/car/hyundai/values.py
  8. 10
      selfdrive/car/tests/test_docs.py
  9. 10
      selfdrive/car/tests/test_fw_fingerprint.py
  10. 4
      selfdrive/controls/controlsd.py
  11. 65
      selfdrive/test/process_replay/migration.py
  12. 3
      selfdrive/test/process_replay/process_replay.py
  13. 2
      selfdrive/test/process_replay/ref_commit
  14. 59
      selfdrive/test/process_replay/regen.py
  15. 2
      selfdrive/thermald/thermald.py
  16. 78
      selfdrive/ui/qt/maps/map_settings.cc
  17. 9
      selfdrive/ui/qt/maps/map_settings.h
  18. 10
      selfdrive/ui/translations/main_ko.ts
  19. 1
      system/hardware/tici/tests/test_amplifier.py
  20. 24
      tools/cabana/mainwin.cc
  21. 2
      tools/cabana/signalview.cc
  22. 2
      tools/cabana/util.cc
  23. 4
      tools/ssh/README.md

@ -1 +1 @@
Subproject commit c331e76b966d324a4f8d56b48db44f59601423dc Subproject commit 44c93ef17cd0973347935b93c67b65ddd5ace22c

@ -8,8 +8,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video| |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>|| |Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>|| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2019-22">Buy Here</a></sub></details>|| |Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Acura&model=RDX 2019-22">Buy Here</a></sub></details>||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,10</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>|| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,10</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,10</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|| |Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,10</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
@ -55,20 +55,20 @@ A supported vehicle is one that just works when you install a comma three. All s
|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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>|| |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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>||
|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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></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)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Buy Here</a></sub></details>|| |Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Buy Here</a></sub></details>||
|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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>|| |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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>||
|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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Buy Here</a></sub></details>|| |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)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Buy Here</a></sub></details>||
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=e 2020">Buy Here</a></sub></details>|| |Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=e 2020">Buy Here</a></sub></details>||
|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Fit 2018-20">Buy Here</a></sub></details>|| |Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Fit 2018-20">Buy Here</a></sub></details>||
|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Freed 2020">Buy Here</a></sub></details>|| |Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Freed 2020">Buy Here</a></sub></details>||
|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=HR-V 2019-22">Buy Here</a></sub></details>|| |Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=HR-V 2019-22">Buy Here</a></sub></details>||
|Honda|HR-V 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=HR-V 2023">Buy Here</a></sub></details>|| |Honda|HR-V 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=HR-V 2023">Buy Here</a></sub></details>||
|Honda|Insight 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Insight 2019-22">Buy Here</a></sub></details>|| |Honda|Insight 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Insight 2019-22">Buy Here</a></sub></details>||
|Honda|Inspire 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Inspire 2018">Buy Here</a></sub></details>|| |Honda|Inspire 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Inspire 2018">Buy Here</a></sub></details>||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Odyssey 2018-20">Buy Here</a></sub></details>|| |Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Odyssey 2018-20">Buy Here</a></sub></details>||
|Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Passport 2019-23">Buy Here</a></sub></details>|| |Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Passport 2019-23">Buy Here</a></sub></details>||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>|| |Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>|| |Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2017-19">Buy Here</a></sub></details>|| |Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2017-19">Buy Here</a></sub></details>||
|Hyundai|Elantra 2021-23|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)](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>| |Hyundai|Elantra 2021-23|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)](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra GT 2017-19">Buy Here</a></sub></details>|| |Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra GT 2017-19">Buy Here</a></sub></details>||

@ -1 +1 @@
Subproject commit 1a9a94c519cb74e9e0f57bea4d316a54c27ea8aa Subproject commit b56340590485bba2428538259e020f176127458c

@ -76,12 +76,12 @@ class FwQueryConfig:
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)
# Brand-specific fuzzy fingerprinting config options: # Brand-specific fuzzy fingerprinting config options:
# A function to get uniquely identifiable codes for a set of versions # A function to get unique, platform-specific identification codes for a set of versions
fuzzy_get_platform_codes: Optional[Callable[[List[bytes]], Set[bytes]]] = None fuzzy_get_platform_codes: Optional[Callable[[List[bytes]], Set[bytes]]] = None
# The minimum number of version matches to fuzzy fingerprint # The minimum number of version matches to fuzzy fingerprint
fuzzy_min_match_count: int = 2 fuzzy_min_match_count: int = 2
# List of ECUs to consider for fuzzy fingerprinting, only used with platforms codes # List of ECUs expected to have platform codes
fuzzy_ecus: List[capnp.lib.capnp._EnumModule] = field(default_factory=list) platform_code_ecus: List[capnp.lib.capnp._EnumModule] = field(default_factory=list)
def __post_init__(self): def __post_init__(self):
for i in range(len(self.requests)): for i in range(len(self.requests)):

@ -292,8 +292,8 @@ class CarInterface(CarInterfaceBase):
# min speed to enable ACC. if car can do stop and go, then set enabling speed # min speed to enable ACC. if car can do stop and go, then set enabling speed
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not # to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc # conflict with PCM acc
stop_and_go = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor
ret.minEnableSpeed = -1. if stop_and_go else 25.5 * CV.MPH_TO_MS ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.5 * CV.MPH_TO_MS
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors

@ -6,7 +6,7 @@ from cereal import car
from selfdrive.car.tests.test_fw_fingerprint import TestFwFingerprintBase from selfdrive.car.tests.test_fw_fingerprint import TestFwFingerprintBase
from selfdrive.car.fw_versions import match_fw_to_car from selfdrive.car.fw_versions import match_fw_to_car
from selfdrive.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, FW_QUERY_CONFIG, \ from selfdrive.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, FW_QUERY_CONFIG, \
FW_VERSIONS, LEGACY_SAFETY_MODE_CAR, PLATFORM_CODE_PATTERN FW_VERSIONS, LEGACY_SAFETY_MODE_CAR
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()} ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
@ -28,36 +28,38 @@ class TestHyundaiFingerprint(TestFwFingerprintBase):
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_in_whitelist]) ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_in_whitelist])
self.assertEqual(len(ecus_not_in_whitelist), 0, f'{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}') self.assertEqual(len(ecus_not_in_whitelist), 0, f'{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}')
def test_fuzzy_ecus_available(self): def test_platform_code_ecus_available(self):
no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL,
CAR.SONATA_LF, CAR.TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80}
# Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms # Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model): with self.subTest(car_model=car_model):
for fuzzy_ecu in FW_QUERY_CONFIG.fuzzy_ecus: for fuzzy_ecu in FW_QUERY_CONFIG.platform_code_ecus:
if car_model == CAR.HYUNDAI_GENESIS: if fuzzy_ecu in (Ecu.fwdRadar, Ecu.eps) and car_model == CAR.HYUNDAI_GENESIS:
raise unittest.SkipTest continue
if fuzzy_ecu == Ecu.eps and car_model in no_eps_platforms:
continue
self.assertIn(fuzzy_ecu, [e[0] for e in ecus]) self.assertIn(fuzzy_ecu, [e[0] for e in ecus])
def test_fuzzy_fw_dates(self): def test_fuzzy_fw_dates(self):
# Some newer platforms have date codes in a different format we don't yet parse, # Some newer platforms have date codes in a different format we don't yet parse,
# for now assert date format is consistent across each platform # for now assert date format is consistent for all FW across each platform
for car_model, ecus in FW_VERSIONS.items(): for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model): with self.subTest(car_model=car_model):
for ecu, fws in ecus.items(): for ecu, fws in ecus.items():
if ecu[0] in FW_QUERY_CONFIG.fuzzy_ecus: if ecu[0] not in FW_QUERY_CONFIG.platform_code_ecus:
dates = set() continue
for fw in fws:
# TODO: use FW_QUERY_CONFIG.fuzzy_get_platform_codes codes = set()
_, date = PLATFORM_CODE_PATTERN.search(fw).groups() for fw in fws:
dates.add(date) codes |= FW_QUERY_CONFIG.fuzzy_get_platform_codes([fw])
if date is not None:
# Assert date is parsable and reasonable # Either no dates should be parsed or all dates should be parsed
parsed = datetime.strptime(date.decode()[:4], '%y%m') self.assertEqual(len({b'-' in code for code in codes}), 1)
self.assertTrue(2013 < parsed.year < 2023, parsed)
# Either no dates should exist or all dates should be parsed
self.assertEqual(len({d is None for d in dates}), 1)
def test_fuzzy_platform_codes(self): def test_fuzzy_platform_codes(self):
# Asserts basic platform code parsing behavior
codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00DH LKAS 1.1 -150210']) codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00DH LKAS 1.1 -150210'])
self.assertEqual(codes, {b"DH-1502"}) self.assertEqual(codes, {b"DH-1502"})

@ -451,8 +451,8 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
# Custom fuzzy fingerprinting config using platform codes + FW dates: # Custom fuzzy fingerprinting config using platform codes + FW dates:
fuzzy_get_platform_codes=get_platform_codes, fuzzy_get_platform_codes=get_platform_codes,
# Hyundai works best with camera and radar (which have standardized platform codes) # Camera and radar should exist on all cars
fuzzy_ecus=[Ecu.fwdRadar, Ecu.fwdCamera], platform_code_ecus=[Ecu.fwdRadar, Ecu.fwdCamera, Ecu.eps],
) )
FW_VERSIONS = { FW_VERSIONS = {

@ -1,12 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import defaultdict from collections import defaultdict
import os
import re import re
import unittest import unittest
from common.basedir import BASEDIR
from selfdrive.car.car_helpers import interfaces, get_interface_attr from selfdrive.car.car_helpers import interfaces, get_interface_attr
from selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info from selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info
from selfdrive.car.docs_definitions import Cable, Column, PartType, Star from selfdrive.car.docs_definitions import Cable, Column, PartType, Star
from selfdrive.car.honda.values import CAR as HONDA from selfdrive.car.honda.values import CAR as HONDA
from selfdrive.debug.dump_car_info import dump_car_info
from selfdrive.debug.print_docs_diff import print_car_info_diff
class TestCarDocs(unittest.TestCase): class TestCarDocs(unittest.TestCase):
@ -22,6 +26,12 @@ class TestCarDocs(unittest.TestCase):
self.assertEqual(generated_cars_md, current_cars_md, self.assertEqual(generated_cars_md, current_cars_md,
"Run selfdrive/car/docs.py to update the compatibility documentation") "Run selfdrive/car/docs.py to update the compatibility documentation")
def test_docs_diff(self):
dump_path = os.path.join(BASEDIR, "selfdrive", "car", "tests", "cars_dump")
dump_car_info(dump_path)
print_car_info_diff(dump_path)
os.remove(dump_path)
def test_duplicate_years(self): def test_duplicate_years(self):
make_model_years = defaultdict(list) make_model_years = defaultdict(list)
for car in self.all_cars: for car in self.all_cars:

@ -131,19 +131,19 @@ class TestFwFingerprint(TestFwFingerprintBase):
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():
with self.subTest(brand=brand): with self.subTest(brand=brand):
if config.fuzzy_get_platform_codes is None: if config.fuzzy_get_platform_codes is None:
self.assertEqual(len(config.fuzzy_ecus), 0, "Cannot specify fuzzy ECUs without full config") self.assertEqual(len(config.platform_code_ecus), 0, "Cannot specify platform code ECUs without full config")
self.assertEqual(config.fuzzy_min_match_count, 2, "Cannot override minimum match count without full config") self.assertEqual(config.fuzzy_min_match_count, 2, "Cannot override minimum match count without full config")
else: else:
self.assertGreater(len(config.fuzzy_ecus), 0, "Need to specify fuzzy ECUs") self.assertGreater(len(config.platform_code_ecus), 0, "Need to specify platform code ECUs")
self.assertEqual(config.fuzzy_min_match_count, len(config.fuzzy_ecus), self.assertEqual(config.fuzzy_min_match_count, len(config.platform_code_ecus),
"Fuzzy ECU match count must equal number of fuzzy ECUs") "Fuzzy ECU match count must equal number of fuzzy ECUs")
# Assert every supported ECU FW version returns one platform code # Assert every supported ECU FW version returns one platform code
for fw_by_addr in VERSIONS[brand].values(): for fw_by_addr in VERSIONS[brand].values():
for addr, fws in fw_by_addr.items(): for addr, fws in fw_by_addr.items():
if addr[0] in config.fuzzy_ecus: if addr[0] in config.platform_code_ecus:
for f in fws: for f in fws:
self.assertEqual(1, len(config.fuzzy_get_platform_codes([f]))) self.assertEqual(1, len(config.fuzzy_get_platform_codes([f])), f"Unable to parse FW: {f}")
def test_fw_request_ecu_whitelist(self): def test_fw_request_ecu_whitelist(self):
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():

@ -170,6 +170,7 @@ class Controls:
self.events_prev = [] self.events_prev = []
self.current_alert_types = [ET.PERMANENT] self.current_alert_types = [ET.PERMANENT]
self.logged_comm_issue = None self.logged_comm_issue = None
self.not_running_prev = None
self.last_actuators = car.CarControl.Actuators.new_message() self.last_actuators = car.CarControl.Actuators.new_message()
self.steer_limited = False self.steer_limited = False
self.desired_curvature = 0.0 self.desired_curvature = 0.0
@ -330,6 +331,9 @@ class Controls:
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning} not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
if self.sm.rcv_frame['managerState'] and (not_running - IGNORE_PROCESSES): if self.sm.rcv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
self.events.add(EventName.processNotRunning) self.events.add(EventName.processNotRunning)
if not_running != self.not_running_prev:
cloudlog.event("process_not_running", not_running=not_running, error=True)
self.not_running_prev = not_running
else: else:
if not SIMULATION and not self.rk.lagging: if not SIMULATION and not self.rk.lagging:
if not self.sm.all_alive(self.camera_packets): if not self.sm.all_alive(self.camera_packets):

@ -0,0 +1,65 @@
from cereal import messaging
def migrate_all(lr, old_logtime=False):
msgs = migrate_sensorEvents(lr, old_logtime)
msgs = migrate_carParams(msgs, old_logtime)
return msgs
def migrate_carParams(lr, old_logtime=False):
all_msgs = []
for msg in lr:
if msg.which() == 'carParams':
CP = messaging.new_message('carParams')
CP.carParams = msg.carParams.as_builder()
for car_fw in CP.carParams.carFw:
car_fw.brand = CP.carParams.carName
if old_logtime:
CP.logMonoTime = msg.logMonoTime
msg = CP.as_reader()
all_msgs.append(msg)
return all_msgs
def migrate_sensorEvents(lr, old_logtime=False):
all_msgs = []
for msg in lr:
if msg.which() != 'sensorEventsDEPRECATED':
all_msgs.append(msg)
continue
# migrate to split sensor events
for evt in msg.sensorEventsDEPRECATED:
# build new message for each sensor type
sensor_service = ''
if evt.which() == 'acceleration':
sensor_service = 'accelerometer'
elif evt.which() == 'gyro' or evt.which() == 'gyroUncalibrated':
sensor_service = 'gyroscope'
elif evt.which() == 'light' or evt.which() == 'proximity':
sensor_service = 'lightSensor'
elif evt.which() == 'magnetic' or evt.which() == 'magneticUncalibrated':
sensor_service = 'magnetometer'
elif evt.which() == 'temperature':
sensor_service = 'temperatureSensor'
m = messaging.new_message(sensor_service)
m.valid = True
if old_logtime:
m.logMonoTime = msg.logMonoTime
m_dat = getattr(m, sensor_service)
m_dat.version = evt.version
m_dat.sensor = evt.sensor
m_dat.type = evt.type
m_dat.source = evt.source
if old_logtime:
m_dat.timestamp = evt.timestamp
setattr(m_dat, evt.which(), getattr(evt, evt.which()))
all_msgs.append(m.as_reader())
return all_msgs

@ -18,6 +18,7 @@ from panda.python import ALTERNATIVE_EXPERIENCE
from selfdrive.car.car_helpers import get_car, interfaces from selfdrive.car.car_helpers import get_car, interfaces
from selfdrive.manager.process_config import managed_processes from selfdrive.manager.process_config import managed_processes
from selfdrive.test.process_replay.helpers import OpenpilotPrefix from selfdrive.test.process_replay.helpers import OpenpilotPrefix
from selfdrive.test.process_replay.migration import migrate_all
# Numpy gives different results based on CPU features after version 19 # Numpy gives different results based on CPU features after version 19
NUMPY_TOLERANCE = 1e-7 NUMPY_TOLERANCE = 1e-7
@ -342,7 +343,7 @@ def replay_process_with_name(name, lr, *args, **kwargs):
def replay_process(cfg, lr, fingerprint=None, return_all_logs=False, disable_progress=False): def replay_process(cfg, lr, fingerprint=None, return_all_logs=False, disable_progress=False):
all_msgs = list(lr) all_msgs = migrate_all(lr, old_logtime=True)
process_logs = _replay_single_process(cfg, all_msgs, fingerprint, disable_progress) process_logs = _replay_single_process(cfg, all_msgs, fingerprint, disable_progress)
if return_all_logs: if return_all_logs:

@ -1 +1 @@
3e684aef4483b8d311d71bab3bb543d7bad26563 67bcd498f0432252a3f11a9fd2b59b344ee214ba

@ -20,6 +20,7 @@ from selfdrive.car.toyota.values import EPS_SCALE
from selfdrive.manager.process import ensure_running from selfdrive.manager.process import ensure_running
from selfdrive.manager.process_config import managed_processes from selfdrive.manager.process_config import managed_processes
from selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, setup_env, check_openpilot_enabled from selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, setup_env, check_openpilot_enabled
from selfdrive.test.process_replay.migration import migrate_all
from selfdrive.test.update_ci_routes import upload_route from selfdrive.test.update_ci_routes import upload_route
from tools.lib.route import Route from tools.lib.route import Route
from tools.lib.framereader import FrameReader from tools.lib.framereader import FrameReader
@ -179,67 +180,11 @@ def replay_cameras(lr, frs, disable_tqdm=False):
return vs, p return vs, p
def migrate_carparams(lr):
all_msgs = []
for msg in lr:
if msg.which() == 'carParams':
CP = messaging.new_message('carParams')
CP.carParams = msg.carParams.as_builder()
for car_fw in CP.carParams.carFw:
car_fw.brand = CP.carParams.carName
msg = CP.as_reader()
all_msgs.append(msg)
return all_msgs
def migrate_sensorEvents(lr, old_logtime=False):
all_msgs = []
for msg in lr:
if msg.which() != 'sensorEventsDEPRECATED':
all_msgs.append(msg)
continue
# migrate to split sensor events
for evt in msg.sensorEventsDEPRECATED:
# build new message for each sensor type
sensor_service = ''
if evt.which() == 'acceleration':
sensor_service = 'accelerometer'
elif evt.which() == 'gyro' or evt.which() == 'gyroUncalibrated':
sensor_service = 'gyroscope'
elif evt.which() == 'light' or evt.which() == 'proximity':
sensor_service = 'lightSensor'
elif evt.which() == 'magnetic' or evt.which() == 'magneticUncalibrated':
sensor_service = 'magnetometer'
elif evt.which() == 'temperature':
sensor_service = 'temperatureSensor'
m = messaging.new_message(sensor_service)
m.valid = True
if old_logtime:
m.logMonoTime = msg.logMonoTime
m_dat = getattr(m, sensor_service)
m_dat.version = evt.version
m_dat.sensor = evt.sensor
m_dat.type = evt.type
m_dat.source = evt.source
if old_logtime:
m_dat.timestamp = evt.timestamp
setattr(m_dat, evt.which(), getattr(evt, evt.which()))
all_msgs.append(m.as_reader())
return all_msgs
def regen_segment(lr, frs=None, daemons="all", outdir=FAKEDATA, disable_tqdm=False): def regen_segment(lr, frs=None, daemons="all", outdir=FAKEDATA, disable_tqdm=False):
if not isinstance(daemons, str) and not hasattr(daemons, "__iter__"): if not isinstance(daemons, str) and not hasattr(daemons, "__iter__"):
raise ValueError("whitelist_proc must be a string or iterable") raise ValueError("whitelist_proc must be a string or iterable")
lr = migrate_carparams(list(lr)) lr = migrate_all(lr)
lr = migrate_sensorEvents(list(lr))
if frs is None: if frs is None:
frs = dict() frs = dict()

@ -41,7 +41,7 @@ HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'ne
THERMAL_BANDS = OrderedDict({ THERMAL_BANDS = OrderedDict({
ThermalStatus.green: ThermalBand(None, 80.0), ThermalStatus.green: ThermalBand(None, 80.0),
ThermalStatus.yellow: ThermalBand(75.0, 96.0), ThermalStatus.yellow: ThermalBand(75.0, 96.0),
ThermalStatus.red: ThermalBand(80.0, 107.), ThermalStatus.red: ThermalBand(88.0, 107.),
ThermalStatus.danger: ThermalBand(94.0, None), ThermalStatus.danger: ThermalBand(94.0, None),
}) })

@ -13,46 +13,46 @@ static QString shorten(const QString &str, int max_len) {
} }
MapPanel::MapPanel(QWidget* parent) : QWidget(parent) { MapPanel::MapPanel(QWidget* parent) : QWidget(parent) {
stack = new QStackedWidget; QStackedLayout *stack = new QStackedLayout(this);
QWidget * main_widget = new QWidget; QWidget *main_widget = new QWidget;
QVBoxLayout *main_layout = new QVBoxLayout(main_widget); QVBoxLayout *main_layout = new QVBoxLayout(main_widget);
const int icon_size = 200; main_layout->setSpacing(20);
// Home
QHBoxLayout *home_layout = new QHBoxLayout;
home_button = new QPushButton;
home_button->setIconSize(QSize(icon_size, icon_size));
home_layout->addWidget(home_button);
home_address = new QLabel;
home_address->setWordWrap(true);
home_layout->addSpacing(30);
home_layout->addWidget(home_address);
home_layout->addStretch();
// Work
QHBoxLayout *work_layout = new QHBoxLayout;
work_button = new QPushButton;
work_button->setIconSize(QSize(icon_size, icon_size));
work_layout->addWidget(work_button);
work_address = new QLabel;
work_address->setWordWrap(true);
work_layout->addSpacing(30);
work_layout->addWidget(work_address);
work_layout->addStretch();
// Home & Work layout // Home & Work layout
QHBoxLayout *home_work_layout = new QHBoxLayout; QHBoxLayout *home_work_layout = new QHBoxLayout;
home_work_layout->addLayout(home_layout, 1); {
home_work_layout->addSpacing(50); // Home
home_work_layout->addLayout(work_layout, 1); home_button = new QPushButton;
home_button->setIconSize(QSize(MAP_PANEL_ICON_SIZE, MAP_PANEL_ICON_SIZE));
home_address = new QLabel;
home_address->setWordWrap(true);
QHBoxLayout *home_layout = new QHBoxLayout;
home_layout->addWidget(home_button);
home_layout->addSpacing(30);
home_layout->addWidget(home_address);
home_layout->addStretch();
// Work
work_button = new QPushButton;
work_button->setIconSize(QSize(MAP_PANEL_ICON_SIZE, MAP_PANEL_ICON_SIZE));
work_address = new QLabel;
work_address->setWordWrap(true);
QHBoxLayout *work_layout = new QHBoxLayout;
work_layout->addWidget(work_button);
work_layout->addSpacing(30);
work_layout->addWidget(work_address);
work_layout->addStretch();
home_work_layout->addLayout(home_layout, 1);
home_work_layout->addSpacing(50);
home_work_layout->addLayout(work_layout, 1);
}
main_layout->addLayout(home_work_layout); main_layout->addLayout(home_work_layout);
main_layout->addSpacing(20);
main_layout->addWidget(horizontal_line()); main_layout->addWidget(horizontal_line());
main_layout->addSpacing(20);
// Current route // Current route
{ {
@ -81,7 +81,6 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) {
QLabel *recents_title = new QLabel(tr("Recent Destinations")); QLabel *recents_title = new QLabel(tr("Recent Destinations"));
recents_title->setStyleSheet("font-size: 55px"); recents_title->setStyleSheet("font-size: 55px");
main_layout->addWidget(recents_title); main_layout->addWidget(recents_title);
main_layout->addSpacing(20);
recent_layout = new QVBoxLayout; recent_layout = new QVBoxLayout;
QWidget *recent_widget = new LayoutWidget(recent_layout, this); QWidget *recent_widget = new LayoutWidget(recent_layout, this);
@ -119,9 +118,6 @@ MapPanel::MapPanel(QWidget* parent) : QWidget(parent) {
stack->setCurrentIndex(prime_type ? 0 : 1); stack->setCurrentIndex(prime_type ? 0 : 1);
}); });
QVBoxLayout *wrapper = new QVBoxLayout(this);
wrapper->addWidget(stack);
clear(); clear();
@ -207,8 +203,9 @@ void MapPanel::refresh() {
prev_destinations = cur_destinations; prev_destinations = cur_destinations;
clear(); clear();
// add favorites before recents
bool has_recents = false; bool has_recents = false;
for (auto &save_type: {"favorite", "recent"}) { for (auto &save_type: {NAV_TYPE_FAVORITE, NAV_TYPE_RECENT}) {
for (auto location : doc.array()) { for (auto location : doc.array()) {
auto obj = location.toObject(); auto obj = location.toObject();
@ -219,7 +216,7 @@ void MapPanel::refresh() {
if (type != save_type) continue; if (type != save_type) continue;
if (type == "favorite" && label == "home") { if (type == NAV_TYPE_FAVORITE && label == NAV_FAVORITE_LABEL_HOME) {
home_address->setText(name); home_address->setText(name);
home_address->setStyleSheet(R"(font-size: 50px; color: white;)"); home_address->setStyleSheet(R"(font-size: 50px; color: white;)");
home_button->setIcon(QPixmap("../assets/navigation/home.png")); home_button->setIcon(QPixmap("../assets/navigation/home.png"));
@ -227,7 +224,7 @@ void MapPanel::refresh() {
navigateTo(obj); navigateTo(obj);
emit closeSettings(); emit closeSettings();
}); });
} else if (type == "favorite" && label == "work") { } else if (type == NAV_TYPE_FAVORITE && label == NAV_FAVORITE_LABEL_WORK) {
work_address->setText(name); work_address->setText(name);
work_address->setStyleSheet(R"(font-size: 50px; color: white;)"); work_address->setStyleSheet(R"(font-size: 50px; color: white;)");
work_button->setIcon(QPixmap("../assets/navigation/work.png")); work_button->setIcon(QPixmap("../assets/navigation/work.png"));
@ -245,7 +242,7 @@ void MapPanel::refresh() {
sp.setRetainSizeWhenHidden(true); sp.setRetainSizeWhenHidden(true);
star->setSizePolicy(sp); star->setSizePolicy(sp);
star->setVisible(type == "favorite"); star->setVisible(type == NAV_TYPE_FAVORITE);
star->setStyleSheet(R"(font-size: 60px;)"); star->setStyleSheet(R"(font-size: 60px;)");
layout->addWidget(star); layout->addWidget(star);
layout->addSpacing(10); layout->addSpacing(10);
@ -284,7 +281,6 @@ void MapPanel::refresh() {
has_recents = true; has_recents = true;
} }
} }
} }
if (!has_recents) { if (!has_recents) {

@ -11,6 +11,14 @@
#include "common/params.h" #include "common/params.h"
#include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/controls.h"
const int MAP_PANEL_ICON_SIZE = 200;
const QString NAV_TYPE_FAVORITE = "favorite";
const QString NAV_TYPE_RECENT = "recent";
const QString NAV_FAVORITE_LABEL_HOME = "home";
const QString NAV_FAVORITE_LABEL_WORK = "work";
class MapPanel : public QWidget { class MapPanel : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
@ -27,7 +35,6 @@ private:
Params params; Params params;
QString prev_destinations, cur_destinations; QString prev_destinations, cur_destinations;
QStackedWidget *stack;
QPushButton *home_button, *work_button; QPushButton *home_button, *work_button;
QLabel *home_address, *work_address; QLabel *home_address, *work_address;
QVBoxLayout *recent_layout; QVBoxLayout *recent_layout;

@ -1053,23 +1053,23 @@ This may take up to a minute.</source>
</message> </message>
<message> <message>
<source>Aggressive</source> <source>Aggressive</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Standard</source> <source>Standard</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Relaxed</source> <source>Relaxed</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Driving Personality</source> <source>Driving Personality</source>
<translation type="unfinished"></translation> <translation> </translation>
</message> </message>
<message> <message>
<source>Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars.</source> <source>Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars.</source>
<translation type="unfinished"></translation> <translation> . openpilot은 . openpilot은 .</translation>
</message> </message>
</context> </context>
<context> <context>

@ -21,6 +21,7 @@ class TestAmplifier(unittest.TestCase):
# clear dmesg # clear dmesg
subprocess.check_call("sudo dmesg -C", shell=True) subprocess.check_call("sudo dmesg -C", shell=True)
Panda.wait_for_panda(None, 30)
self.panda = Panda() self.panda = Panda()
self.panda.reset() self.panda.reset()

@ -77,6 +77,7 @@ MainWindow::MainWindow() : QMainWindow() {
} }
void MainWindow::createActions() { void MainWindow::createActions() {
// File menu
QMenu *file_menu = menuBar()->addMenu(tr("&File")); QMenu *file_menu = menuBar()->addMenu(tr("&File"));
file_menu->addAction(tr("Open Stream..."), this, &MainWindow::openStream); file_menu->addAction(tr("Open Stream..."), this, &MainWindow::openStream);
close_stream_act = file_menu->addAction(tr("Close stream"), this, &MainWindow::closeStream); close_stream_act = file_menu->addAction(tr("Close stream"), this, &MainWindow::closeStream);
@ -124,6 +125,7 @@ void MainWindow::createActions() {
file_menu->addSeparator(); file_menu->addSeparator();
file_menu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows)->setShortcuts(QKeySequence::Quit); file_menu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows)->setShortcuts(QKeySequence::Quit);
// Edit Menu
QMenu *edit_menu = menuBar()->addMenu(tr("&Edit")); QMenu *edit_menu = menuBar()->addMenu(tr("&Edit"));
auto undo_act = UndoStack::instance()->createUndoAction(this, tr("&Undo")); auto undo_act = UndoStack::instance()->createUndoAction(this, tr("&Undo"));
undo_act->setShortcuts(QKeySequence::Undo); undo_act->setShortcuts(QKeySequence::Undo);
@ -138,14 +140,22 @@ void MainWindow::createActions() {
commands_act->setDefaultWidget(new QUndoView(UndoStack::instance())); commands_act->setDefaultWidget(new QUndoView(UndoStack::instance()));
commands_menu->addAction(commands_act); commands_menu->addAction(commands_act);
edit_menu->addSeparator(); // View Menu
edit_menu->addAction(tr("Reset Window Layout"), QMenu *view_menu = menuBar()->addMenu(tr("&View"));
[this]() { restoreState(default_state); }); auto act = view_menu->addAction(tr("Full Screen"), this, &MainWindow::toggleFullScreen, QKeySequence::FullScreen);
addAction(act);
view_menu->addSeparator();
view_menu->addAction(messages_dock->toggleViewAction());
view_menu->addAction(video_dock->toggleViewAction());
view_menu->addSeparator();
view_menu->addAction(tr("Reset Window Layout"), [this]() { restoreState(default_state); });
// Tools Menu
tools_menu = menuBar()->addMenu(tr("&Tools")); tools_menu = menuBar()->addMenu(tr("&Tools"));
tools_menu->addAction(tr("Find &Similar Bits"), this, &MainWindow::findSimilarBits); tools_menu->addAction(tr("Find &Similar Bits"), this, &MainWindow::findSimilarBits);
tools_menu->addAction(tr("&Find Signal"), this, &MainWindow::findSignal); tools_menu->addAction(tr("&Find Signal"), this, &MainWindow::findSignal);
// Help Menu
QMenu *help_menu = menuBar()->addMenu(tr("&Help")); QMenu *help_menu = menuBar()->addMenu(tr("&Help"));
help_menu->addAction(tr("Help"), this, &MainWindow::onlineHelp)->setShortcuts(QKeySequence::HelpContents); help_menu->addAction(tr("Help"), this, &MainWindow::onlineHelp)->setShortcuts(QKeySequence::HelpContents);
help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
@ -155,13 +165,13 @@ void MainWindow::createDockWindows() {
messages_dock = new QDockWidget(tr("MESSAGES"), this); messages_dock = new QDockWidget(tr("MESSAGES"), this);
messages_dock->setObjectName("MessagesPanel"); messages_dock->setObjectName("MessagesPanel");
messages_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); messages_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
messages_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); messages_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
addDockWidget(Qt::LeftDockWidgetArea, messages_dock); addDockWidget(Qt::LeftDockWidgetArea, messages_dock);
video_dock = new QDockWidget("", this); video_dock = new QDockWidget("", this);
video_dock->setObjectName(tr("VideoPanel")); video_dock->setObjectName(tr("VideoPanel"));
video_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); video_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
video_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); video_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
addDockWidget(Qt::RightDockWidgetArea, video_dock); addDockWidget(Qt::RightDockWidgetArea, video_dock);
} }
@ -206,8 +216,6 @@ void MainWindow::createStatusBar() {
void MainWindow::createShortcuts() { void MainWindow::createShortcuts() {
auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut); auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut);
QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); }); QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); });
shortcut = new QShortcut(QKeySequence(QKeySequence::FullScreen), this, nullptr, nullptr, Qt::ApplicationShortcut);
QObject::connect(shortcut, &QShortcut::activated, this, &MainWindow::toggleFullScreen);
// TODO: add more shortcuts here. // TODO: add more shortcuts here.
} }

@ -331,7 +331,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
if (item && item->type == SignalModel::Item::Sig) { if (item && item->type == SignalModel::Item::Sig) {
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
if (option.state & QStyle::State_Selected) { if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight()); painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
} }
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;

@ -96,7 +96,7 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin); int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
if (option.state & QStyle::State_Selected) { if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight()); painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
} }
const QPoint pt{option.rect.left() + h_margin, option.rect.top() + v_margin}; const QPoint pt{option.rect.left() + h_margin, option.rect.top() + v_margin};

@ -24,14 +24,12 @@ The `id_rsa` key in this directory only works while your device is in the setup
See the [community wiki](https://github.com/commaai/openpilot/wiki/SSH) for more detailed instructions and information. See the [community wiki](https://github.com/commaai/openpilot/wiki/SSH) for more detailed instructions and information.
# Connecting to ssh.comma.ai # Connecting to ssh.comma.ai
SSH into your comma device from anywhere with `ssh.comma.ai`. SSH into your comma device from anywhere with `ssh.comma.ai`. Requires a [comma prime subscription](https://comma.ai/connect).
## Setup ## Setup
With software version 0.6.1 or newer, enter your GitHub username on your device under Developer Settings. Your GitHub authorized public keys will become your authorized SSH keys for `ssh.comma.ai`. You can add any additional keys in `/system/comma/home/.ssh/authorized_keys.persist`. With software version 0.6.1 or newer, enter your GitHub username on your device under Developer Settings. Your GitHub authorized public keys will become your authorized SSH keys for `ssh.comma.ai`. You can add any additional keys in `/system/comma/home/.ssh/authorized_keys.persist`.
Requires [comma SIM with comma prime](https://comma.ai/shop) activated with comma connect, available on iOS and Android. comma two and EON ship with a pre-inserted comma SIM.
## Recommended .ssh/config ## Recommended .ssh/config
With the below SSH configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`.<br /> With the below SSH configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`.<br />

Loading…
Cancel
Save