Merge remote-tracking branch 'origin/master' into nlp-driving

pull/30209/head
Yassine 2 years ago
commit ace3a59dad
  1. 1
      .github/workflows/docs.yaml
  2. 1
      README.md
  3. 1
      RELEASES.md
  4. 1
      SConstruct
  5. 6
      docs/CARS.md
  6. 5
      release/files_common
  7. 13
      selfdrive/car/gm/interface.py
  8. 6
      selfdrive/car/gm/values.py
  9. 9
      selfdrive/car/hyundai/interface.py
  10. 3
      selfdrive/car/hyundai/tests/test_hyundai.py
  11. 50
      selfdrive/car/hyundai/values.py
  12. 22
      selfdrive/car/tests/routes.py
  13. 2
      selfdrive/car/tests/test_car_interfaces.py
  14. 4
      selfdrive/car/tests/test_models_segs.txt
  15. 2
      selfdrive/car/torque_data/override.yaml
  16. 13
      selfdrive/car/torque_data/params.yaml
  17. 10
      selfdrive/car/torque_data/substitute.yaml
  18. 13
      selfdrive/car/toyota/interface.py
  19. 442
      selfdrive/car/toyota/values.py
  20. 6
      selfdrive/car/volkswagen/values.py
  21. 468
      selfdrive/locationd/laikad.py
  22. 319
      selfdrive/locationd/test/test_laikad.py
  23. 2
      selfdrive/manager/process_config.py
  24. 2
      selfdrive/modeld/constants.py
  25. 11
      selfdrive/modeld/fill_model_msg.py
  26. 8
      selfdrive/modeld/modeld.py
  27. 4
      selfdrive/test/process_replay/README.md
  28. 21
      selfdrive/test/process_replay/process_replay.py
  29. 2
      selfdrive/test/process_replay/test_fuzzy.py
  30. 2
      selfdrive/test/profiling/profiler.py
  31. 2
      selfdrive/test/test_onroad.py
  32. 2
      selfdrive/test/update_ci_routes.py
  33. 1
      system/clocksd/.gitignore
  34. 2
      system/clocksd/SConscript
  35. 73
      system/clocksd/clocksd.cc
  36. 19
      system/sensord/rawgps/test_rawgps.py
  37. 29
      tools/cabana/mainwin.cc
  38. 10
      tools/gpstest/fuzzy_testing.py
  39. 185
      tools/gpstest/rpc_server.py
  40. 105
      tools/gpstest/test_laikad.py
  41. 16
      tools/replay/util.cc

@ -54,6 +54,7 @@ jobs:
cp -r ../build/docs/html/ docs/
touch docs/.nojekyll
echo -n docs.comma.ai > docs/CNAME
git add -f .
git commit -m "build docs"

@ -109,7 +109,6 @@ Directory Structure
├── third_party # External libraries
└── system # Generic services
├── camerad # Driver to capture images from the camera sensors
├── clocksd # Broadcasts current time
├── hardware # Hardware abstraction classes
├── logcatd # systemd journal as a service
├── loggerd # Logger and uploader of car data

@ -2,6 +2,7 @@ Version 0.9.5 (2023-XX-XX)
========================
* New driving model
* Improved navigate on openpilot performance using navigation instructions as an additional model input
* Cadillac Escalade ESV 2019 support thanks to twilsonco!
* Hyundai Azera 2022 support thanks to sunnyhaibin!
* Hyundai Azera Hybrid 2020 support thanks to chanhojung and haram-KONA!
* Hyundai Custin 2023 support thanks to sunnyhaibin and Saber422!

@ -382,7 +382,6 @@ SConscript(['rednose/SConscript'])
# Build system services
SConscript([
'system/clocksd/SConscript',
'system/proclogd/SConscript',
'system/ubloxd/SConscript',
'system/loggerd/SConscript',

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 265 Supported Cars
# 267 Supported Cars
|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|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@ -20,6 +20,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Buick|LaCrosse 2017-19[<sup>4</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 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-3x.html?make=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|Cadillac|Escalade 2017[<sup>4</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 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-3x.html?make=Cadillac&model=Escalade 2017">Buy Here</a></sub></details>||
|Cadillac|Escalade ESV 2016[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 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-3x.html?make=Cadillac&model=Escalade ESV 2016">Buy Here</a></sub></details>||
|Cadillac|Escalade ESV 2019[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 OBD-II connector<br>- 1 comma 3X<br>- 2 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-3x.html?make=Cadillac&model=Escalade ESV 2019">Buy Here</a></sub></details>||
|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)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Bolt EV 2022-23">Buy Here</a></sub></details>||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Chevrolet&model=Silverado 1500 2020-21">Buy Here</a></sub></details>||
@ -74,7 +75,8 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Azera 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>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera 2022">Buy Here</a></sub></details>||
|Hyundai|Azera Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Azera Hybrid 2020">Buy Here</a></sub></details>||
|Hyundai|Custin 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>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Custin 2023">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>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra 2017-19">Buy Here</a></sub></details>||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra 2017-18">Buy Here</a></sub></details>||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra 2019">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>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=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>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra GT 2017-19">Buy Here</a></sub></details>||
|Hyundai|Elantra Hybrid 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>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Elantra Hybrid 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>|

@ -132,10 +132,6 @@ selfdrive/car/tesla/*.py
selfdrive/car/toyota/*.py
selfdrive/car/volkswagen/*.py
system/clocksd/.gitignore
system/clocksd/SConscript
system/clocksd/clocksd.cc
selfdrive/debug/can_printer.py
selfdrive/debug/check_freq.py
selfdrive/debug/dump.py
@ -230,7 +226,6 @@ system/ubloxd/*.cc
selfdrive/locationd/__init__.py
selfdrive/locationd/SConscript
selfdrive/locationd/.gitignore
selfdrive/locationd/laikad.py
selfdrive/locationd/locationd.h
selfdrive/locationd/locationd.cc
selfdrive/locationd/paramsd.py

@ -198,17 +198,22 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.5
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ESCALADE_ESV:
elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019):
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.mass = 2739.
ret.wheelbase = 3.302
ret.steerRatio = 17.3
ret.centerToFront = ret.wheelbase * 0.5
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.13, 0.24], [0.01, 0.02]]
ret.lateralTuning.pid.kf = 0.000045
ret.tireStiffnessFactor = 1.0
if candidate == CAR.ESCALADE_ESV:
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.13, 0.24], [0.01, 0.02]]
ret.lateralTuning.pid.kf = 0.000045
else:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BOLT_EUV:
ret.mass = 1669.
ret.wheelbase = 2.63779

@ -71,6 +71,7 @@ class CAR(StrEnum):
BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE = "CADILLAC ESCALADE 2017"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
ESCALADE_ESV_2019 = "CADILLAC ESCALADE ESV 2019"
BOLT_EUV = "CHEVROLET BOLT EUV 2022"
SILVERADO = "CHEVROLET SILVERADO 1500 2020"
EQUINOX = "CHEVROLET EQUINOX 2019"
@ -106,6 +107,7 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
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_2019: GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"),
CAR.BOLT_EUV: [
GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
@ -198,6 +200,10 @@ FINGERPRINTS = {
{
309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8
}],
CAR.ESCALADE_ESV_2019: [
{
715: 8, 840: 5, 717: 5, 869: 4, 880: 6, 289: 8, 454: 8, 842: 5, 460: 5, 463: 3, 801: 8, 170: 8, 190: 6, 241: 6, 201: 8, 417: 7, 211: 2, 419: 1, 398: 8, 426: 7, 487: 8, 442: 8, 451: 8, 452: 8, 453: 6, 479: 3, 311: 8, 500: 6, 647: 6, 193: 8, 707: 8, 197: 8, 209: 7, 199: 4, 455: 7, 313: 8, 481: 7, 485: 8, 489: 8, 249: 8, 393: 7, 407: 7, 413: 8, 422: 4, 431: 8, 501: 8, 499: 3, 810: 8, 508: 8, 381: 8, 462: 4, 532: 6, 562: 8, 386: 8, 761: 7, 573: 1, 554: 3, 719: 5, 560: 8, 1279: 4, 388: 8, 288: 5, 1005: 6, 497: 8, 844: 8, 961: 8, 967: 4, 977: 8, 979: 8, 985: 5, 1001: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 510: 8, 866: 4, 304: 1, 969: 8, 384: 4, 1033: 7, 1009: 8, 1034: 7, 1296: 4, 1930: 7, 1105: 5, 1013: 5, 1225: 7, 1919: 7, 320: 3, 534: 2, 352: 5, 298: 8, 1223: 2, 1233: 8, 608: 8, 1265: 8, 609: 6, 1267: 1, 1417: 8, 610: 6, 1906: 7, 611: 6, 612: 8, 613: 8, 208: 8, 564: 5, 309: 8, 1221: 5, 1280: 4, 1249: 8, 1907: 7, 1257: 6, 1300: 8, 1920: 7, 563: 5, 1322: 6, 1323: 4, 1328: 4, 1917: 7, 328: 1, 1912: 7, 1914: 7, 804: 3, 1918: 7
}],
CAR.BOLT_EUV: [
{
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7

@ -3,8 +3,8 @@ from panda import Panda
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, UNSUPPORTED_LONGITUDINAL_CAR, \
Buttons
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, Buttons
from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
@ -88,7 +88,7 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.90
ret.steerRatio = 15.6 * 1.15
ret.tireStiffnessFactor = 0.63
elif candidate == CAR.ELANTRA:
elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30):
ret.mass = 1275.
ret.wheelbase = 2.7
ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535
@ -260,7 +260,8 @@ class CarInterface(CarInterfaceBase):
if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = candidate in (HYBRID_CAR | EV_CAR) and candidate not in CANFD_RADAR_SCC_CAR
ret.experimentalLongitudinalAvailable = (candidate in (HYBRID_CAR | EV_CAR) and candidate not in
(CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR))
else:
ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0]

@ -23,6 +23,7 @@ NO_DATES_PLATFORMS = {
CAR.TUCSON_HYBRID_4TH_GEN,
# CAN
CAR.ELANTRA,
CAR.ELANTRA_GT_I30,
CAR.KIA_CEED,
CAR.KIA_FORTE,
CAR.KIA_OPTIMA_G4,
@ -99,7 +100,7 @@ class TestHyundaiFingerprint(unittest.TestCase):
def test_platform_code_ecus_available(self):
# TODO: add queries for these non-CAN FD cars to get EPS
no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H,
CAR.KIA_OPTIMA_H_G4_FL, CAR.SONATA_LF, CAR.TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80}
CAR.KIA_OPTIMA_H_G4_FL, CAR.SONATA_LF, CAR.TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80, CAR.ELANTRA}
# Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms
for car_model, ecus in FW_VERSIONS.items():

@ -37,7 +37,7 @@ class CarControllerParams:
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.IONIQ,
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.IONIQ,
CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO):
self.STEER_MAX = 255
@ -72,6 +72,7 @@ class CAR(StrEnum):
AZERA_6TH_GEN = "HYUNDAI AZERA 6TH GEN"
AZERA_HEV_6TH_GEN = "HYUNDAI AZERA HYBRID 6TH GEN"
ELANTRA = "HYUNDAI ELANTRA 2017"
ELANTRA_GT_I30 = "HYUNDAI I30 N LINE 2019 & GT 2018 DCT"
ELANTRA_2021 = "HYUNDAI ELANTRA 2021"
ELANTRA_HEV_2021 = "HYUNDAI ELANTRA HYBRID 2021"
HYUNDAI_GENESIS = "HYUNDAI GENESIS 2015-2016"
@ -160,7 +161,11 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.AZERA_HEV_6TH_GEN: HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.ELANTRA: [
HyundaiCarInfo("Hyundai Elantra 2017-19", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
# TODO: 2017-18 could be Hyundai G
HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])),
],
CAR.ELANTRA_GT_I30: [
HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
@ -298,9 +303,6 @@ class Buttons:
CANCEL = 4 # on newer models, this is a pause/resume button
FINGERPRINTS = {
CAR.ELANTRA: [{
66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 897: 8, 832: 8, 899: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1170: 8, 1265: 4, 1280: 1, 1282: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1345: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1366: 8, 1367: 8, 1369: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2001: 8, 2003: 8, 2004: 8, 2009: 8, 2012: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
}],
CAR.HYUNDAI_GENESIS: [{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4
},
@ -1702,6 +1704,33 @@ FW_VERSIONS = {
],
},
CAR.ELANTRA: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00ADP LKAS AT USA LHD 1.00 1.03 99211-F2000 X31',
b'\xf1\x00AD LKAS AT USA LHD 1.01 1.01 95895-F2000 251',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T6K0_C2\x00\x006T6S2051\x00\x00TAD0N20NSD(\xfcA\x9d',
b'\xf1\x006T6K0_C2\x00\x006T6S2051\x00\x00TAD0N20NSD\x00\x00\x00\x00',
b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20NS2\x00\x00\x00\x00',
b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20NS2\xc5\x92\x9e\x8a',
b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20SS2.~\x90\x87',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8161698051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x8161657051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816165D051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816165E051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00AD ESC \x11 11 \x18\x05\x06 58910-F2840',
b'\xf1\x00AD ESC \x11 12 \x15\t\t 58920-F2810',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00AD__ SCC H-CUP 1.00 1.00 99110-F2100 ',
b'\xf1\x00AD__ SCC H-CUP 1.00 1.01 96400-F2100 ',
],
},
CAR.ELANTRA_GT_I30: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00PD LKAS AT USA LHD 1.01 1.01 95740-G3100 A54',
b'\xf1\x00PD LKAS AT KOR LHD 1.00 1.02 95740-G3000 A51',
@ -2065,7 +2094,7 @@ CHECKSUM = {
CAN_GEARS = {
# which message has the gear. hybrid and EV use ELECT_GEAR
"use_cluster_gears": {CAR.ELANTRA, CAR.KONA},
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
}
@ -2076,7 +2105,11 @@ CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCS
# The radar does SCC on these cars when HDA I, rather than the camera
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80,
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KONA_EV_2ND_GEN, CAR.IONIQ_6}
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN}
# These CAN FD cars do not accept communication control to disable the ADAS ECU,
# responds with 0x7F2822 - 'conditions not correct'
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = {CAR.IONIQ_6, CAR.KONA_EV_2ND_GEN}
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
@ -2094,7 +2127,7 @@ EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR
# these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.KIA_OPTIMA_G4,
CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022,
CAR.KIA_OPTIMA_H}
CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30}
# these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc.
UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4_FL,
@ -2106,6 +2139,7 @@ DBC = {
CAR.AZERA_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.AZERA_HEV_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_GT_I30: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_2021: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G70: dbc_dict('hyundai_kia_generic', None),

@ -25,7 +25,6 @@ non_tested_cars = [
HONDA.ODYSSEY_CHN,
VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter
TOYOTA.RAV4_TSS2_2023,
TOYOTA.RAV4H_TSS2_2023,
SUBARU.FORESTER_HYBRID,
]
@ -61,6 +60,7 @@ routes = [
CarTestRoute("75a6bcb9b8b40373|2023-03-11--22-47-33", GM.BUICK_LACROSSE),
CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE),
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
CarTestRoute("168f8b3be57f66ae|2023-09-12--21-44-42", GM.ESCALADE_ESV_2019),
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1),
CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.BOLT_EUV, segment=14), # Bolt EV
@ -159,7 +159,7 @@ routes = [
CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
CarTestRoute("192283cdbb7a58c2|2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN),
CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA),
CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.ELANTRA), # 2019 Elantra GT
CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.ELANTRA_GT_I30),
CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021),
CarTestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021),
CarTestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID),
@ -167,17 +167,17 @@ routes = [
CarTestRoute("6b0d44d22df18134|2023-05-06--10-36-55", HYUNDAI.GENESIS_GV80),
CarTestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2),
CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2),
CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARD_TSS2), # hybrid
CarTestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON),
CarTestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019),
CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019),
CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALON_2019), # hybrid
CarTestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2),
CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2),
CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALON_TSS2), # hybrid
CarTestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY),
CarTestRoute("2f37c007683e85ba|2023-09-02--14-39-44", TOYOTA.CAMRY), # openpilot longitudinal, with radar CAN filter
CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRY), # hybrid
CarTestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2),
CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2),
CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH),
CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRY_TSS2), # hybrid
CarTestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA),
CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2),
CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLA_TSS2), # hybrid
@ -186,7 +186,7 @@ routes = [
CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2),
CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022),
CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4_TSS2), # hybrid
CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022),
CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4_TSS2_2022), # hybrid
CarTestRoute("7a31f030957b9c85|2023-04-01--14-12-51", TOYOTA.LEXUS_ES),
CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2),
CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ES_TSS2), # hybrid
@ -202,7 +202,7 @@ routes = [
CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2),
CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NX_TSS2), # hybrid
CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2),
CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2),
CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDER_TSS2), # hybrid
CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER),
CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH),
CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA),
@ -214,8 +214,8 @@ routes = [
CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHR), # hybrid
CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.CHR_TSS2),
CarTestRoute("ea8fbe72b96a185c|2023-02-22--09-20-34", TOYOTA.CHR_TSS2), # openpilot longitudinal, with smartDSU
CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHRH_TSS2),
# CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHRH_TSS2), # openpilot longitudinal, radar disabled
CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHR_TSS2), # hybrid
# CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled
CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V),
CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1),

@ -133,7 +133,7 @@ class TestCarInterfaces(unittest.TestCase):
# Make sure we can combine dicts
ret = get_interface_attr('DBC', combine_brands=True)
self.assertGreaterEqual(len(ret), 170)
self.assertGreaterEqual(len(ret), 160)
# We don't support combining non-dicts
ret = get_interface_attr('CAR', combine_brands=True)

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e57357c95937475fe92c13709ff2ee067f036643d5d455383bd9c3335525614
size 126281
oid sha256:8674d9a8304d501aee2bfaea2d668d746fd424b5b0872b83ab265d15d3a3d887
size 125301

@ -14,7 +14,6 @@ SUBARU ASCENT 2023: [.nan, 3.0, .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 AP1 MODEL S: [.nan, 2.5, .nan]
@ -37,6 +36,7 @@ RAM 1500 5TH GEN: [2.0, 2.0, 0.05]
RAM HD 5TH GEN: [1.4, 1.4, 0.05]
SUBARU OUTBACK 6TH GEN: [2.0, 2.0, 0.2]
CADILLAC ESCALADE 2017: [1.899999976158142, 1.842270016670227, 0.1120000034570694]
CADILLAC ESCALADE ESV 2019: [1.15, 1.3, 0.2]
CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
CHEVROLET TRAILBLAZER 2021: [1.33, 1.9, 0.16]

@ -61,16 +61,13 @@ TOYOTA AVALON 2019: [1.7036141952825095, 1.239619084240008, 0.08459830394899492]
TOYOTA AVALON 2022: [2.3154403649717357, 2.7777922854327124, 0.11453999639164605]
TOYOTA C-HR 2018: [1.5591084333664578, 1.271271459066948, 0.20259087058453193]
TOYOTA C-HR 2021: [1.7678810166088303, 1.3742176337919942, 0.2319674583741509]
TOYOTA CAMRY 2018: [2.1172995371905015, 1.7156177222420887, 0.105192506]
TOYOTA CAMRY 2021: [2.446083, 2.3476510120007434, 0.121615]
TOYOTA CAMRY HYBRID 2018: [1.996333, 1.7996193116697359, 0.112565]
TOYOTA CAMRY HYBRID 2021: [2.263582, 2.3901492458927986, 0.115257]
TOYOTA CAMRY 2018: [2.0568162685952505, 1.7576185169559122, 0.108878753]
TOYOTA CAMRY 2021: [2.3548324999999997, 2.368900128946771, 0.118436]
TOYOTA COROLLA 2017: [3.117154369115421, 1.8438132575043773, 0.12289685869250652]
TOYOTA COROLLA TSS2 2019: [1.991132339206426, 1.868866242720403, 0.19570063298031432]
TOYOTA HIGHLANDER 2017: [1.8696367437248915, 1.626293990451463, 0.17485372210240796]
TOYOTA HIGHLANDER 2020: [2.022340166827233, 1.6183134804881791, 0.14592306380054457]
TOYOTA HIGHLANDER 2020: [1.9617570834136164, 1.8611643317268927, 0.14519673256119725]
TOYOTA HIGHLANDER HYBRID 2018: [1.752033, 1.6433903296845025, 0.144600]
TOYOTA HIGHLANDER HYBRID 2020: [1.901174, 2.104015182965606, 0.14447040132184993]
TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565]
TOYOTA PRIUS 2017: [1.60, 1.5023147650693636, 0.151515]
TOYOTA PRIUS TSS2 2021: [1.972600, 1.9104337425537743, 0.170968]
@ -79,8 +76,8 @@ TOYOTA RAV4 2019: [2.279239424615458, 2.087101966779332, 0.13682208413446817]
TOYOTA RAV4 2019 8965: [2.3080951748210854, 2.1189367835820603, 0.12942102328134028]
TOYOTA RAV4 2019 x02: [2.762293266024922, 2.243615865975329, 0.11113568178327986]
TOYOTA RAV4 HYBRID 2017: [1.9796257271652042, 1.7503987331707576, 0.14628860048885406]
TOYOTA RAV4 HYBRID 2022: [2.241883248393209, 1.9304407208090029, 0.112174]
TOYOTA RAV4 HYBRID 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736]
TOYOTA RAV4 2022: [2.241883248393209, 1.9304407208090029, 0.112174]
TOYOTA RAV4 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736]
TOYOTA SIENNA 2018: [1.689726, 1.3208264576110418, 0.140456]
VOLKSWAGEN ARTEON 1ST GEN: [1.45136518053819, 1.3639364049316804, 0.23806361745695032]
VOLKSWAGEN ATLAS 1ST GEN: [1.4677006726964945, 1.6733266634075656, 0.12959584092073367]

@ -4,18 +4,13 @@ MAZDA CX-5: MAZDA CX-9 2021
MAZDA CX-5 2022: MAZDA CX-9 2021
MAZDA CX-9: MAZDA CX-9 2021
TOYOTA ALPHARD HYBRID 2021 : TOYOTA SIENNA 2018
TOYOTA ALPHARD 2020: TOYOTA SIENNA 2018
TOYOTA PRIUS v 2017 : TOYOTA PRIUS 2017
TOYOTA RAV4 2022: TOYOTA RAV4 HYBRID 2022
TOYOTA C-HR HYBRID 2022: TOYOTA C-HR 2021
LEXUS IS 2018: LEXUS NX 2018
LEXUS CT HYBRID 2018 : LEXUS NX 2018
LEXUS ES 2018: TOYOTA CAMRY HYBRID 2018
LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018
LEXUS ES 2018: TOYOTA CAMRY 2018
LEXUS ES HYBRID 2018: TOYOTA CAMRY 2018
LEXUS RC 2020: LEXUS NX 2020
TOYOTA AVALON HYBRID 2019: TOYOTA AVALON 2019
TOYOTA AVALON HYBRID 2022: TOYOTA AVALON 2022
KIA OPTIMA 4TH GEN: HYUNDAI SONATA 2020
KIA OPTIMA 4TH GEN FACELIFT: HYUNDAI SONATA 2020
@ -34,6 +29,7 @@ HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019
HYUNDAI I30 N LINE 2019 & GT 2018 DCT: HYUNDAI SONATA 2019
HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020
HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019
HYUNDAI TUCSON 4TH GEN: HYUNDAI TUCSON HYBRID 4TH GEN

@ -85,28 +85,28 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFactor = 0.5533
ret.mass = 4481. * CV.LB_TO_KG # mean between min and max
elif candidate in (CAR.CHR, CAR.CHR_TSS2, CAR.CHRH_TSS2):
elif candidate in (CAR.CHR, CAR.CHR_TSS2):
stop_and_go = True
ret.wheelbase = 2.63906
ret.steerRatio = 13.6
ret.tireStiffnessFactor = 0.7933
ret.mass = 3300. * CV.LB_TO_KG
elif candidate in (CAR.CAMRY, CAR.CAMRYH, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2):
elif candidate in (CAR.CAMRY, CAR.CAMRY_TSS2):
stop_and_go = True
ret.wheelbase = 2.82448
ret.steerRatio = 13.7
ret.tireStiffnessFactor = 0.7933
ret.mass = 3400. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2):
elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.HIGHLANDER_TSS2):
stop_and_go = True
ret.wheelbase = 2.8194 # average of 109.8 and 112.2 in
ret.steerRatio = 16.0
ret.tireStiffnessFactor = 0.8
ret.mass = 4516. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALONH_2019, CAR.AVALON_TSS2, CAR.AVALONH_TSS2):
elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALON_TSS2):
# starting from 2019, all Avalon variants have stop and go
# https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf
stop_and_go = candidate != CAR.AVALON
@ -115,8 +115,7 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFactor = 0.7983
ret.mass = 3505. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2_2022,
CAR.RAV4_TSS2_2023, CAR.RAV4H_TSS2_2023):
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023):
ret.wheelbase = 2.68986
ret.steerRatio = 14.3
ret.tireStiffnessFactor = 0.7933
@ -191,7 +190,7 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFactor = 0.8
ret.mass = 4300. * CV.LB_TO_KG
elif candidate in (CAR.ALPHARD_TSS2, CAR.ALPHARDH_TSS2):
elif candidate == CAR.ALPHARD_TSS2:
ret.wheelbase = 3.00
ret.steerRatio = 14.2
ret.tireStiffnessFactor = 0.444

@ -49,26 +49,19 @@ class ToyotaFlags(IntFlag):
class CAR(StrEnum):
# Toyota
ALPHARD_TSS2 = "TOYOTA ALPHARD 2020"
ALPHARDH_TSS2 = "TOYOTA ALPHARD HYBRID 2021"
AVALON = "TOYOTA AVALON 2016"
AVALON_2019 = "TOYOTA AVALON 2019"
AVALONH_2019 = "TOYOTA AVALON HYBRID 2019"
AVALON_TSS2 = "TOYOTA AVALON 2022" # TSS 2.5
AVALONH_TSS2 = "TOYOTA AVALON HYBRID 2022"
CAMRY = "TOYOTA CAMRY 2018"
CAMRYH = "TOYOTA CAMRY HYBRID 2018"
CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5
CAMRYH_TSS2 = "TOYOTA CAMRY HYBRID 2021"
CHR = "TOYOTA C-HR 2018"
CHR_TSS2 = "TOYOTA C-HR 2021"
CHRH_TSS2 = "TOYOTA C-HR HYBRID 2022"
COROLLA = "TOYOTA COROLLA 2017"
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019"
HIGHLANDER = "TOYOTA HIGHLANDER 2017"
HIGHLANDER_TSS2 = "TOYOTA HIGHLANDER 2020"
HIGHLANDERH = "TOYOTA HIGHLANDER HYBRID 2018"
HIGHLANDERH_TSS2 = "TOYOTA HIGHLANDER HYBRID 2020"
PRIUS = "TOYOTA PRIUS 2017"
PRIUS_V = "TOYOTA PRIUS v 2017"
PRIUS_TSS2 = "TOYOTA PRIUS TSS2 2021"
@ -77,8 +70,6 @@ class CAR(StrEnum):
RAV4_TSS2 = "TOYOTA RAV4 2019"
RAV4_TSS2_2022 = "TOYOTA RAV4 2022"
RAV4_TSS2_2023 = "TOYOTA RAV4 2023"
RAV4H_TSS2_2022 = "TOYOTA RAV4 HYBRID 2022"
RAV4H_TSS2_2023 = "TOYOTA RAV4 HYBRID 2023"
MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5
SIENNA = "TOYOTA SIENNA 2018"
@ -111,26 +102,38 @@ class ToyotaCarInfo(CarInfo):
CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
# Toyota
CAR.ALPHARD_TSS2: ToyotaCarInfo("Toyota Alphard 2019-20"),
CAR.ALPHARDH_TSS2: ToyotaCarInfo("Toyota Alphard Hybrid 2021"),
CAR.ALPHARD_TSS2: [
ToyotaCarInfo("Toyota Alphard 2019-20"),
ToyotaCarInfo("Toyota Alphard Hybrid 2021"),
],
CAR.AVALON: [
ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota Avalon 2017-18"),
],
CAR.AVALON_2019: ToyotaCarInfo("Toyota Avalon 2019-21"),
CAR.AVALONH_2019: ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"),
CAR.AVALON_TSS2: ToyotaCarInfo("Toyota Avalon 2022"),
CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-23", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-24"),
CAR.AVALON_2019: [
ToyotaCarInfo("Toyota Avalon 2019-21"),
ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"),
],
CAR.AVALON_TSS2: [
ToyotaCarInfo("Toyota Avalon 2022"),
ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
],
CAR.CAMRY: [
ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CAR.CAMRY_TSS2: [
ToyotaCarInfo("Toyota Camry 2021-23", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2021-24"),
],
CAR.CHR: [
ToyotaCarInfo("Toyota C-HR 2017-20"),
ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"),
],
CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"),
CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"),
CAR.CHR_TSS2: [
ToyotaCarInfo("Toyota C-HR 2021"),
ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"),
],
CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"),
CAR.COROLLA_TSS2: [
ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
@ -143,9 +146,11 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
ToyotaCarInfo("Lexus UX Hybrid 2019-23"),
],
CAR.HIGHLANDER: ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"),
CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-23"),
CAR.HIGHLANDER_TSS2: [
ToyotaCarInfo("Toyota Highlander 2020-23"),
ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"),
],
CAR.HIGHLANDERH: ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"),
CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"),
CAR.PRIUS: [
ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
@ -168,10 +173,14 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"),
],
CAR.RAV4_TSS2_2022: ToyotaCarInfo("Toyota RAV4 2022"),
CAR.RAV4_TSS2_2023: ToyotaCarInfo("Toyota RAV4 2023"),
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.RAV4_TSS2_2022: [
ToyotaCarInfo("Toyota RAV4 2022"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
],
CAR.RAV4_TSS2_2023: [
ToyotaCarInfo("Toyota RAV4 2023"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2023"),
],
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),
@ -381,7 +390,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
non_essential_ecus={
# FIXME: On some models, abs can sometimes be missing
Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS],
Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS, CAR.ALPHARD_TSS2],
# On some models, the engine can show on two different addresses
Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS, CAR.LEXUS_RC,
CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX_TSS2],
@ -453,44 +462,26 @@ FW_VERSIONS = {
b'F152607171\x00\x00\x00\x00\x00\x00',
b'F152607110\x00\x00\x00\x00\x00\x00',
b'F152607180\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'881510703200\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B41080\x00\x00\x00\x00\x00\x00',
b'8965B07010\x00\x00\x00\x00\x00\x00',
b'8965B41090\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896630725200\x00\x00\x00\x00',
b'\x01896630725300\x00\x00\x00\x00',
b'\x01896630735100\x00\x00\x00\x00',
b'\x01896630738000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'8821F4702300\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F0702100\x00\x00\x00\x00',
],
},
CAR.AVALONH_2019: {
(Ecu.abs, 0x7b0, None): [
b'F152641040\x00\x00\x00\x00\x00\x00',
b'F152641061\x00\x00\x00\x00\x00\x00',
b'F152641050\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'881510703200\x00\x00\x00\x00',
b'881510704200\x00\x00\x00\x00',
b'881514107100\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B41080\x00\x00\x00\x00\x00\x00',
b'8965B07010\x00\x00\x00\x00\x00\x00',
b'8965B41090\x00\x00\x00\x00\x00\x00',
b'8965B41070\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896630725200\x00\x00\x00\x00',
b'\x01896630725300\x00\x00\x00\x00',
b'\x01896630735100\x00\x00\x00\x00',
b'\x01896630738000\x00\x00\x00\x00',
b'\x02896630724000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
b'\x02896630737000\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00',
b'\x02896630728000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
@ -507,6 +498,7 @@ FW_VERSIONS = {
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'F152641080\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B41110\x00\x00\x00\x00\x00\x00',
@ -514,24 +506,6 @@ FW_VERSIONS = {
(Ecu.engine, 0x700, None): [
b'\x01896630742000\x00\x00\x00\x00',
b'\x01896630743000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F4104100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F4104100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
],
},
CAR.AVALONH_TSS2: {
(Ecu.abs, 0x7b0, None): [
b'F152641080\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B41110\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x018966306Q6000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@ -567,65 +541,6 @@ FW_VERSIONS = {
b'\x018966333Q6300\x00\x00\x00\x00',
b'\x018966333Q6500\x00\x00\x00\x00',
b'\x018966333W6000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x02333P1100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'8821F0601200 ',
b'8821F0601300 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0604100 ',
b'8821F0605200 ',
b'8821F0607200 ',
b'8821F0608000 ',
b'8821F0608200 ',
b'8821F0609100 ',
],
(Ecu.abs, 0x7b0, None): [
b'F152606210\x00\x00\x00\x00\x00\x00',
b'F152606230\x00\x00\x00\x00\x00\x00',
b'F152606270\x00\x00\x00\x00\x00\x00',
b'F152606290\x00\x00\x00\x00\x00\x00',
b'F152606410\x00\x00\x00\x00\x00\x00',
b'F152633540\x00\x00\x00\x00\x00\x00',
b'F152633A10\x00\x00\x00\x00\x00\x00',
b'F152633A20\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B33540\x00\x00\x00\x00\x00\x00',
b'8965B33542\x00\x00\x00\x00\x00\x00',
b'8965B33580\x00\x00\x00\x00\x00\x00',
b'8965B33581\x00\x00\x00\x00\x00\x00',
b'8965B33621\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [ # Same as 0x791
b'8821F0601200 ',
b'8821F0601300 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0604100 ',
b'8821F0605200 ',
b'8821F0607200 ',
b'8821F0608000 ',
b'8821F0608200 ',
b'8821F0609100 ',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F0601200 ',
b'8646F0601300 ',
b'8646F0601400 ',
b'8646F0603400 ',
b'8646F0604100 ',
b'8646F0605000 ',
b'8646F0606000 ',
b'8646F0606100 ',
b'8646F0607100 ',
],
},
CAR.CAMRYH: {
(Ecu.engine, 0x700, None): [
b'\x018966306Q6000\x00\x00\x00\x00',
b'\x018966333N1100\x00\x00\x00\x00',
b'\x018966333N4300\x00\x00\x00\x00',
@ -648,78 +563,97 @@ FW_VERSIONS = {
b'\x028966306S0100\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00',
b'\x028966306S1100\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152633214\x00\x00\x00\x00\x00\x00',
b'F152633660\x00\x00\x00\x00\x00\x00',
b'F152633712\x00\x00\x00\x00\x00\x00',
b'F152633713\x00\x00\x00\x00\x00\x00',
b'F152633B51\x00\x00\x00\x00\x00\x00',
b'F152633B60\x00\x00\x00\x00\x00\x00',
(Ecu.engine, 0x7e0, None): [
b'\x02333P1100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'8821F0601200 ',
b'8821F0601300 ',
b'8821F0603400 ',
b'8821F0604000 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0604100 ',
b'8821F0604200 ',
b'8821F0605200 ',
b'8821F0606200 ',
b'8821F0607200 ',
b'8821F0608000 ',
b'8821F0608200 ',
b'8821F0609000 ',
b'8821F0609100 ',
b'8821F0603400 ',
b'8821F0604000 ',
b'8821F0604200 ',
b'8821F0606200 ',
b'8821F0609000 ',
],
(Ecu.abs, 0x7b0, None): [
b'F152606210\x00\x00\x00\x00\x00\x00',
b'F152606230\x00\x00\x00\x00\x00\x00',
b'F152606270\x00\x00\x00\x00\x00\x00',
b'F152606290\x00\x00\x00\x00\x00\x00',
b'F152606410\x00\x00\x00\x00\x00\x00',
b'F152633540\x00\x00\x00\x00\x00\x00',
b'F152633A10\x00\x00\x00\x00\x00\x00',
b'F152633A20\x00\x00\x00\x00\x00\x00',
b'F152633214\x00\x00\x00\x00\x00\x00',
b'F152633660\x00\x00\x00\x00\x00\x00',
b'F152633712\x00\x00\x00\x00\x00\x00',
b'F152633713\x00\x00\x00\x00\x00\x00',
b'F152633B51\x00\x00\x00\x00\x00\x00',
b'F152633B60\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B33540\x00\x00\x00\x00\x00\x00',
b'8965B33542\x00\x00\x00\x00\x00\x00',
b'8965B33550\x00\x00\x00\x00\x00\x00',
b'8965B33551\x00\x00\x00\x00\x00\x00',
b'8965B33580\x00\x00\x00\x00\x00\x00',
b'8965B33581\x00\x00\x00\x00\x00\x00',
b'8965B33611\x00\x00\x00\x00\x00\x00',
b'8965B33621\x00\x00\x00\x00\x00\x00',
b'8965B33550\x00\x00\x00\x00\x00\x00',
b'8965B33551\x00\x00\x00\x00\x00\x00',
b'8965B33611\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [ # Same as 0x791
b'8821F0601200 ',
b'8821F0601300 ',
b'8821F0603400 ',
b'8821F0604000 ',
b'8821F0602000 ',
b'8821F0603300 ',
b'8821F0604100 ',
b'8821F0604200 ',
b'8821F0605200 ',
b'8821F0606200 ',
b'8821F0607200 ',
b'8821F0608000 ',
b'8821F0608200 ',
b'8821F0609000 ',
b'8821F0609100 ',
b'8821F0603400 ',
b'8821F0604000 ',
b'8821F0604200 ',
b'8821F0606200 ',
b'8821F0609000 ',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F0601200 ',
b'8646F0601300 ',
b'8646F0601400 ',
b'8646F0603400 ',
b'8646F0603500 ',
b'8646F0604100 ',
b'8646F0605000 ',
b'8646F0606000 ',
b'8646F0606100 ',
b'8646F0607000 ',
b'8646F0607100 ',
b'8646F0603500 ',
b'8646F0607000 ',
],
},
CAR.CAMRY_TSS2: {
(Ecu.eps, 0x7a1, None): [
b'8965B33630\x00\x00\x00\x00\x00\x00',
b'8965B33640\x00\x00\x00\x00\x00\x00',
b'8965B33650\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F152606370\x00\x00\x00\x00\x00\x00',
b'\x01F152606390\x00\x00\x00\x00\x00\x00',
b'\x01F152606400\x00\x00\x00\x00\x00\x00',
b'\x01F152606431\x00\x00\x00\x00\x00\x00',
b'F152633D00\x00\x00\x00\x00\x00\x00',
b'F152633D60\x00\x00\x00\x00\x00\x00',
b'F152633310\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x018966306Q5000\x00\x00\x00\x00',
@ -730,10 +664,16 @@ FW_VERSIONS = {
b'\x018966306T3200\x00\x00\x00\x00',
b'\x018966306T4000\x00\x00\x00\x00',
b'\x018966306T4100\x00\x00\x00\x00',
b'\x018966306Q6000\x00\x00\x00\x00',
b'\x018966306Q7000\x00\x00\x00\x00',
b'\x018966306T0000\x00\x00\x00\x00',
b'\x018966306V1000\x00\x00\x00\x00',
b'\x01896633T20000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0602100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
@ -743,35 +683,7 @@ FW_VERSIONS = {
b'\x028646F3305200\x00\x00\x00\x008646G3304000\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: {
(Ecu.eps, 0x7a1, None): [
b'8965B33630\x00\x00\x00\x00\x00\x00',
b'8965B33650\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152633D00\x00\x00\x00\x00\x00\x00',
b'F152633D60\x00\x00\x00\x00\x00\x00',
b'F152633310\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x018966306Q6000\x00\x00\x00\x00',
b'\x018966306Q7000\x00\x00\x00\x00',
b'\x018966306T0000\x00\x00\x00\x00',
b'\x018966306V1000\x00\x00\x00\x00',
b'\x01896633T20000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 15): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 109): [
b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305300\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F3305500\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
],
},
CAR.CHR: {
@ -863,13 +775,19 @@ FW_VERSIONS = {
(Ecu.abs, 0x7b0, None): [
b'F152610260\x00\x00\x00\x00\x00\x00',
b'F1526F4270\x00\x00\x00\x00\x00\x00',
b'F152610041\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B10091\x00\x00\x00\x00\x00\x00',
b'8965B10110\x00\x00\x00\x00\x00\x00',
b'8965B10092\x00\x00\x00\x00\x00\x00',
b'8965B10111\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x0189663F459000\x00\x00\x00\x00',
b'\x0189663F438000\x00\x00\x00\x00',
b'\x02896631025000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x0289663F453000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x0331014000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00',
@ -877,33 +795,12 @@ FW_VERSIONS = {
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821FF410200\x00\x00\x00\x00',
b'\x018821FF410300\x00\x00\x00\x00',
b'\x018821FF410500\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646FF410200\x00\x00\x00\x008646GF408200\x00\x00\x00\x00',
b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00',
],
},
CAR.CHRH_TSS2: {
(Ecu.eps, 0x7a1, None): [
b'8965B10092\x00\x00\x00\x00\x00\x00',
b'8965B10091\x00\x00\x00\x00\x00\x00',
b'8965B10111\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',
b'\x02896631025000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x0289663F453000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 15): [
b'\x018821FF410500\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: {
@ -1189,6 +1086,18 @@ FW_VERSIONS = {
b'\x01F15260E110\x00\x00\x00\x00\x00\x00',
b'\x01F15260E170\x00\x00\x00\x00\x00\x00',
b'\x01F15260E05300\x00\x00\x00\x00',
b'\x01F15264872300\x00\x00\x00\x00',
b'\x01F15264872400\x00\x00\x00\x00',
b'\x01F15264872500\x00\x00\x00\x00',
b'\x01F15264872600\x00\x00\x00\x00',
b'\x01F15264872700\x00\x00\x00\x00',
b'\x01F15264873500\x00\x00\x00\x00',
b'\x01F152648C6300\x00\x00\x00\x00',
b'\x01F152648J4000\x00\x00\x00\x00',
b'\x01F152648J5000\x00\x00\x00\x00',
b'\x01F152648J6000\x00\x00\x00\x00',
b'\x01F152648J7000\x00\x00\x00\x00',
b'\x01F152648L5000\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896630E62100\x00\x00\x00\x00',
@ -1209,44 +1118,13 @@ FW_VERSIONS = {
b'\x01896630EE1100\x00\x00\x00\x00',
b'\x01896630EG3000\x00\x00\x00\x00',
b'\x01896630EG5000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00',
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F4803200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
],
},
CAR.HIGHLANDERH_TSS2: {
(Ecu.eps, 0x7a1, None): [
b'8965B48241\x00\x00\x00\x00\x00\x00',
b'8965B48310\x00\x00\x00\x00\x00\x00',
b'8965B48400\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F15264872300\x00\x00\x00\x00',
b'\x01F15264872400\x00\x00\x00\x00',
b'\x01F15264872500\x00\x00\x00\x00',
b'\x01F15264872600\x00\x00\x00\x00',
b'\x01F15264873500\x00\x00\x00\x00',
b'\x01F152648C6300\x00\x00\x00\x00',
b'\x01F152648J4000\x00\x00\x00\x00',
b'\x01F152648J5000\x00\x00\x00\x00',
b'\x01F152648J6000\x00\x00\x00\x00',
b'\x01F15264872700\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896630E67000\x00\x00\x00\x00',
b'\x01896630EA1000\x00\x00\x00\x00',
b'\x01896630EE4000\x00\x00\x00\x00',
b'\x01896630EE4100\x00\x00\x00\x00',
b'\x01896630EE5000\x00\x00\x00\x00',
b'\x01896630EE6000\x00\x00\x00\x00',
b'\x01896630EE7000\x00\x00\x00\x00',
b'\x01896630EF8000\x00\x00\x00\x00',
b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
@ -1634,46 +1512,6 @@ FW_VERSIONS = {
(Ecu.abs, 0x7b0, None): [
b'\x01F15260R350\x00\x00\x00\x00\x00\x00',
b'\x01F15260R361\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x028965B0R01500\x00\x00\x00\x008965B0R02500\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896634AA0000\x00\x00\x00\x00',
b'\x01896634AA0100\x00\x00\x00\x00',
b'\x01896634AA1000\x00\x00\x00\x00',
b'\x01896634A88000\x00\x00\x00\x00',
b'\x01896634A89000\x00\x00\x00\x00',
b'\x01896634A89100\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R01100\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
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'\x01896634A88100\x00\x00\x00\x00',
b'\x01896634AJ2000\x00\x00\x00\x00',
b'\x01896634A89100\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_2022: {
(Ecu.abs, 0x7b0, None): [
b'\x01F15264283100\x00\x00\x00\x00',
b'\x01F15264286200\x00\x00\x00\x00',
b'\x01F15264286100\x00\x00\x00\x00',
@ -1685,6 +1523,12 @@ FW_VERSIONS = {
b'8965B42172\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896634AA0000\x00\x00\x00\x00',
b'\x01896634AA0100\x00\x00\x00\x00',
b'\x01896634AA1000\x00\x00\x00\x00',
b'\x01896634A88000\x00\x00\x00\x00',
b'\x01896634A89000\x00\x00\x00\x00',
b'\x01896634A89100\x00\x00\x00\x00',
b'\x01896634A02001\x00\x00\x00\x00',
b'\x01896634A03000\x00\x00\x00\x00',
b'\x01896634A08000\x00\x00\x00\x00',
@ -1700,8 +1544,9 @@ FW_VERSIONS = {
b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00',
],
},
CAR.RAV4H_TSS2_2023: {
CAR.RAV4_TSS2_2023: {
(Ecu.abs, 0x7b0, None): [
b'\x01F15260R450\x00\x00\x00\x00\x00\x00',
b'\x01F15264283200\x00\x00\x00\x00',
b'\x01F15264283300\x00\x00\x00\x00',
b'\x01F152642F1000\x00\x00\x00\x00',
@ -1711,15 +1556,12 @@ FW_VERSIONS = {
b'8965B42371\x00\x00\x00\x00\x00\x00',
],
(Ecu.engine, 0x700, None): [
b'\x01896634A88100\x00\x00\x00\x00',
b'\x01896634AJ2000\x00\x00\x00\x00',
b'\x01896634A89100\x00\x00\x00\x00',
b'\x01896634AE1001\x00\x00\x00\x00',
b'\x01896634AF0000\x00\x00\x00\x00',
],
(Ecu.hybrid, 0x7d2, None): [
b'\x02899830R39000\x00\x00\x00\x00899850R20000\x00\x00\x00\x00',
b'\x02899830R41000\x00\x00\x00\x00899850R20000\x00\x00\x00\x00',
b'\x028998342C0000\x00\x00\x00\x00899854224000\x00\x00\x00\x00',
b'\x028998342C6000\x00\x00\x00\x00899854224000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R03100\x00\x00\x00\x00',
],
@ -2178,34 +2020,22 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'\x0235870000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x0235883000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B58040\x00\x00\x00\x00\x00\x00',
b'8965B58052\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301200\x00\x00\x00\x00',
b'\x018821F3301400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F58010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F5803200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
],
},
CAR.ALPHARDH_TSS2: {
(Ecu.engine, 0x7e0, None): [
b'\x0235879000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B58040\x00\x00\x00\x00\x00\x00',
b'8965B58052\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152658341\x00\x00\x00\x00\x00\x00'
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301200\x00\x00\x00\x00',
b'\x018821F3301400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F58010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F5803200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646FV201000\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
],
},
@ -2225,20 +2055,14 @@ DBC = {
CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.CHRH_TSS2: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CAMRYH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.CAMRYH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.HIGHLANDER: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.HIGHLANDER_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.HIGHLANDERH: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.HIGHLANDERH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.AVALON: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.AVALON_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.AVALONH_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.AVALON_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_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
@ -2250,14 +2074,11 @@ DBC = {
CAR.LEXUS_IS: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_IS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.RAV4H_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4H_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
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.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.ALPHARDH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
}
# These cars have non-standard EPS torque scale factors. All others are 73
@ -2265,20 +2086,19 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.LEXUS_ES_TSS2,
CAR.RAV4H_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2,
CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, CAR.LEXUS_IS_TSS2, CAR.MIRAI, CAR.LEXUS_NX_TSS2,
CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2}
CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2,
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.CHR_TSS2}
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY, CAR.CAMRYH}
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY}
# the DSU uses the AEB message for longitudinal on these cars
UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC}
# 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.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2, CAR.CHRH_TSS2}
RADAR_ACC_CAR = {CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2}
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL_CAR = {CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023}
ANGLE_CONTROL_CAR = {CAR.RAV4_TSS2_2023}
# no resume button press required
NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_ESH}

@ -832,6 +832,7 @@ FW_VERSIONS = {
b'\xf1\x8783A907115F \xf1\x890002',
b'\xf1\x8783A907115G \xf1\x890001',
b'\xf1\x8783A907115K \xf1\x890001',
b'\xf1\x8783A907115K \xf1\x890002',
b'\xf1\x8704E906024AP\xf1\x891461',
b'\xf1\x8783A907115 \xf1\x890007',
],
@ -841,6 +842,7 @@ FW_VERSIONS = {
b'\xf1\x8709G927158GC\xf1\x893821',
b'\xf1\x8709G927158GD\xf1\x893820',
b'\xf1\x8709G927158GM\xf1\x893936',
b'\xf1\x8709G927158GN\xf1\x893938',
b'\xf1\x870D9300043 \xf1\x895202',
b'\xf1\x870DL300011N \xf1\x892001',
b'\xf1\x870DL300011N \xf1\x892012',
@ -879,6 +881,7 @@ FW_VERSIONS = {
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1',
b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\00521A60804A1',
b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x002RA60A2ROM',
b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x002SA6092SOM',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6017A00',
b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60804A1',
@ -1071,11 +1074,13 @@ FW_VERSIONS = {
b'\xf1\x8783A906259 \xf1\x890001',
b'\xf1\x8783A906259 \xf1\x890005',
b'\xf1\x8783A906259C \xf1\x890002',
b'\xf1\x8783A906259D \xf1\x890001',
b'\xf1\x8783A906259F \xf1\x890001',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158CN\xf1\x893608',
b'\xf1\x8709G927158FL\xf1\x893758',
b'\xf1\x8709G927158GG\xf1\x893825',
b'\xf1\x8709G927158GP\xf1\x893937',
b'\xf1\x870GC300045D \xf1\x892802',
b'\xf1\x870GC300046F \xf1\x892701',
@ -1085,6 +1090,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0959655BQ\xf1\x890421\xf1\x82\x132121111121120031112124218C219321532111',
b'\xf1\x875Q0959655CC\xf1\x890421\xf1\x82\x131111111111120031111224118A119321532111',
b'\xf1\x875Q0959655CC\xf1\x890421\xf1\x82\x131111111111120031111237116A119321532111',
b'\xf1\x875Q0959655BQ\xf1\x890421\xf1\x82\x132121111121120031112124218A219321532111',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567G6000300',

@ -1,468 +0,0 @@
#!/usr/bin/env python3
import math
import os
import time
import shutil
from collections import defaultdict
from concurrent.futures import Future, ProcessPoolExecutor
from enum import IntEnum
from typing import List, Optional, Dict, Any
import numpy as np
from cereal import log, messaging
from openpilot.common.params import Params, put_nonblocking
from laika import AstroDog
from laika.constants import SECS_IN_HR, SECS_IN_MIN
from laika.downloader import DownloadFailed
from laika.ephemeris import EphemerisType, GPSEphemeris, GLONASSEphemeris, ephemeris_structs, parse_qcom_ephem
from laika.gps_time import GPSTime
from laika.helpers import ConstellationId, get_sv_id
from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox
from laika.raw_gnss import gps_time_from_qcom_report, get_measurements_from_qcom_reports
from laika.opt import calc_pos_fix, get_posfix_sympy_fun, calc_vel_fix, get_velfix_sympy_func
from openpilot.selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind
from openpilot.selfdrive.locationd.models.gnss_kf import GNSSKalman
from openpilot.selfdrive.locationd.models.gnss_kf import States as GStates
from openpilot.system.hardware.hw import Paths
from openpilot.system.swaglog import cloudlog
MAX_TIME_GAP = 10
EPHEMERIS_CACHE = 'LaikadEphemerisV3'
CACHE_VERSION = 0.2
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:
def __init__(self, valid_const=(ConstellationId.GPS, ConstellationId.GLONASS), auto_fetch_navs=True, auto_update=False,
valid_ephem_types=(EphemerisType.NAV, EphemerisType.QCOM_POLY),
save_ephemeris=False, use_qcom=False):
"""
valid_const: GNSS constellation which can be used
auto_fetch_navs: If true fetch navs from internet when needed
auto_update: If true download AstroDog will download all files needed. This can be ephemeris or correction data like ionosphere.
valid_ephem_types: Valid ephemeris types to be used by AstroDog
save_ephemeris: If true saves and loads nav and orbit ephemeris to cache.
"""
self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types,
clear_old_ephemeris=True, cache_dir=Paths.download_cache_root())
self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True, erratic_clock=use_qcom)
self.auto_fetch_navs = auto_fetch_navs
self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None
self.orbit_fetch_future: Optional[Future] = 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.load_cache()
self.posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)}
self.velfix_function = get_velfix_sympy_func()
self.last_fix_pos = None
self.last_fix_t = None
self.use_qcom = use_qcom
self.first_log_time = None
self.ttff = -1
self.measurement_lag = 0.630 if self.use_qcom else 0.095
# qcom specific stuff
self.qcom_reports_received = 4
self.qcom_reports = []
def load_cache(self):
if not self.save_ephemeris:
return
cache_bytes = Params().get(EPHEMERIS_CACHE)
if not cache_bytes:
return
nav_dict = {}
try:
with ephemeris_structs.EphemerisCache.from_bytes(cache_bytes) as ephem_cache:
glonass_navs = [GLONASSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.glonassEphemerides]
gps_navs = [GPSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.gpsEphemerides]
for e in sum([glonass_navs, gps_navs], []):
if e.prn not in nav_dict:
nav_dict[e.prn] = []
nav_dict[e.prn].append(e)
self.astro_dog.add_navs(nav_dict)
except Exception:
cloudlog.exception("Error parsing cache")
cloudlog.debug(
f"Loaded navs ({sum([len(nav_dict[prn]) for prn in nav_dict.keys()])}). Unique orbit and nav sats: {list(nav_dict.keys())} ")
def cache_ephemeris(self):
if self.save_ephemeris and (self.last_report_time - self.last_cached_t > SECS_IN_MIN):
nav_list: List = sum([v for k,v in self.astro_dog.navs.items()], [])
#TODO this only saves currently valid ephems, when we download future ephems we should save them too
valid_navs = [e for e in nav_list if e.valid(self.last_report_time)]
if len(valid_navs) > 0:
ephem_cache = ephemeris_structs.EphemerisCache(glonassEphemerides=[e.data for e in valid_navs if e.prn[0]=='R'],
gpsEphemerides=[e.data for e in valid_navs if e.prn[0]=='G'])
put_nonblocking(EPHEMERIS_CACHE, ephem_cache.to_bytes())
cloudlog.debug("Cache saved")
self.last_cached_t = self.last_report_time
def create_ephem_statuses(self):
ephemeris_statuses = []
eph_list: List = sum([v for k,v in self.astro_dog.navs.items()], []) + sum([v for k,v in self.astro_dog.qcom_polys.items()], [])
for eph in eph_list:
status = log.GnssMeasurements.EphemerisStatus.new_message()
status.constellationId = ConstellationId.from_rinex_char(eph.prn[0]).value
status.svId = get_sv_id(eph.prn)
status.type = get_log_eph_type(eph).value
status.source = get_log_eph_source(eph).value
status.tow = eph.epoch.tow
status.gpsWeek = eph.epoch.week
ephemeris_statuses.append(status)
return ephemeris_statuses
def get_lsq_fix(self, t, measurements):
if self.last_fix_t is None or abs(self.last_fix_t - t) > 0:
min_measurements = 5 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 4
position_solution, pr_residuals, pos_std = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements)
if len(position_solution) < 3:
return None
position_estimate = position_solution[:3]
position_std = pos_std[:3]
velocity_solution, prr_residuals, vel_std = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements)
if len(velocity_solution) < 3:
return None
velocity_estimate = velocity_solution[:3]
velocity_std = vel_std[:3]
return position_estimate, position_std, velocity_estimate, velocity_std
def is_good_report(self, gnss_msg):
if gnss_msg.which() in ['drMeasurementReport', 'measurementReport'] and self.use_qcom:
# TODO: Understand and use remaining unknown constellations
try:
if gnss_msg.which() == 'drMeasurementReport':
constellation_id = ConstellationId.from_qcom_source(gnss_msg.drMeasurementReport.source)
else:
constellation_id = ConstellationId.from_qcom_source(gnss_msg.measurementReport.source)
good_constellation = constellation_id in [ConstellationId.GPS, ConstellationId.SBAS, ConstellationId.GLONASS]
report_time = gps_time_from_qcom_report(gnss_msg)
except NotImplementedError:
return False
# Garbage timestamps with week > 32767 are sometimes sent by module.
# This is an issue with gpsTime and GLONASS time.
good_week = report_time.week < np.iinfo(np.int16).max
return good_constellation and good_week
elif gnss_msg.which() == 'measurementReport' and not self.use_qcom:
return True
else:
return False
def read_report(self, gnss_msg):
if self.use_qcom:
# QCOM reports are per constellation, so we need to aggregate them
# Additionally, the pseudoranges are broken in the measurementReports
# and the doppler filteredSpeed is broken in the drMeasurementReports
report_time = gps_time_from_qcom_report(gnss_msg)
if report_time - self.last_report_time == 0:
self.qcom_reports.append(gnss_msg)
self.last_report_time = report_time
elif report_time - self.last_report_time > 0:
self.qcom_reports_received = max(1, len(self.qcom_reports))
self.qcom_reports = [gnss_msg]
self.last_report_time = report_time
else:
# Sometimes DR reports get sent one iteration late (1second), they need to be ignored
cloudlog.warning(f"Received report with time {report_time} before last report time {self.last_report_time}")
if len(self.qcom_reports) == self.qcom_reports_received:
new_meas = get_measurements_from_qcom_reports(self.qcom_reports)
else:
new_meas = []
else:
report = gnss_msg.measurementReport
self.last_report_time = GPSTime(report.gpsWeek, report.rcvTow)
new_meas = read_raw_ublox(report)
return self.last_report_time, new_meas
def is_ephemeris(self, gnss_msg):
if self.use_qcom:
return gnss_msg.which() == 'drSvPoly'
else:
return gnss_msg.which() in ('ephemeris', 'glonassEphemeris')
def read_ephemeris(self, gnss_msg):
if self.use_qcom:
try:
ephem = parse_qcom_ephem(gnss_msg.drSvPoly)
self.astro_dog.add_qcom_polys({ephem.prn: [ephem]})
except Exception:
cloudlog.exception("Error parsing qcom svPoly ephemeris from qcom module")
return
else:
if gnss_msg.which() == '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':
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:
cloudlog.error(f"Unsupported ephemeris type: {gnss_msg.which()}")
return
self.astro_dog.add_navs({ephem.prn: [ephem]})
self.cache_ephemeris()
def process_report(self, new_meas, t):
# Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites
new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7]
processed_measurements = process_measurements(new_meas, self.astro_dog)
if self.last_fix_pos is not None:
est_pos = self.last_fix_pos
correct_delay = True
else:
est_pos = self.gnss_kf.x[GStates.ECEF_POS].tolist()
correct_delay = False
corrected_measurements = correct_measurements(processed_measurements, est_pos, self.astro_dog, correct_delay=correct_delay)
# If many measurements weren't corrected, position may be garbage, so reset
if len(processed_measurements) >= 8 and len(corrected_measurements) < 5:
cloudlog.error("Didn't correct enough measurements, resetting estimate position")
self.last_fix_pos = None
self.last_fix_t = None
return corrected_measurements
def calc_fix(self, t, measurements):
instant_fix = self.get_lsq_fix(t, measurements)
if instant_fix is None:
return None
else:
position_estimate, position_std, velocity_estimate, velocity_std = instant_fix
self.last_fix_t = t
self.last_fix_pos = position_estimate
self.lat_fix_pos_std = position_std
return position_estimate, position_std, velocity_estimate, velocity_std
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 - int(1e9 * self.measurement_lag)}
if self.first_log_time is None:
self.first_log_time = 1e-9 * gnss_mono_time
if self.is_ephemeris(gnss_msg):
self.read_ephemeris(gnss_msg)
elif self.is_good_report(gnss_msg):
report_t, new_meas = self.read_report(gnss_msg)
if report_t.week > 0:
if self.auto_fetch_navs:
self.fetch_navs(report_t, block)
corrected_measurements = self.process_report(new_meas, t)
msg_dict['correctedMeasurements'] = [create_measurement_msg(m) for m in corrected_measurements]
fix = self.calc_fix(t, corrected_measurements)
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()
kf_valid = all(self.kf_valid(t))
msg_dict["kalmanPositionECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_POS].tolist(),
std=np.sqrt(P_diag[GStates.ECEF_POS]).tolist(),
valid=kf_valid)
msg_dict["kalmanVelocityECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_VELOCITY].tolist(),
std=np.sqrt(P_diag[GStates.ECEF_VELOCITY]).tolist(),
valid=kf_valid)
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]):
# Check time and outputs are valid
valid = self.kf_valid(t)
if not all(valid):
if not valid[0]: # Filter not initialized
pass
elif not valid[1]:
cloudlog.error("Time gap of over 10s detected, gnss kalman reset")
elif not valid[2]:
cloudlog.error("Gnss kalman filter state is nan")
if est_pos is not None and len(est_pos) > 0:
cloudlog.info(f"Reset kalman filter with {est_pos}")
self.init_gnss_localizer(est_pos)
else:
return
if len(measurements) > 0:
kf_add_observations(self.gnss_kf, t, measurements)
else:
# Ensure gnss filter is updated even with no new measurements
self.gnss_kf.predict(t)
def kf_valid(self, t: float) -> List[bool]:
filter_time = self.gnss_kf.filter.get_filter_time()
return [not math.isnan(filter_time),
abs(t - filter_time) < MAX_TIME_GAP,
all(np.isfinite(self.gnss_kf.x[GStates.ECEF_POS]))]
def init_gnss_localizer(self, est_pos):
x_initial, p_initial_diag = np.copy(GNSSKalman.x_initial), np.copy(np.diagonal(GNSSKalman.P_initial))
x_initial[GStates.ECEF_POS] = est_pos
p_initial_diag[GStates.ECEF_POS] = 1000 ** 2
self.gnss_kf.init_state(x_initial, covs_diag=p_initial_diag)
def fetch_navs(self, t: GPSTime, block):
# Download new navs if 1 hour of navs data left
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
ret = None
if block: # Used for testing purposes
ret = get_orbit_data(t, *astro_dog_vars)
elif self.orbit_fetch_future is None:
self.orbit_fetch_executor = ProcessPoolExecutor(max_workers=1)
self.orbit_fetch_future = self.orbit_fetch_executor.submit(get_orbit_data, t, *astro_dog_vars)
elif self.orbit_fetch_future.done():
ret = self.orbit_fetch_future.result()
self.orbit_fetch_executor = self.orbit_fetch_future = None
if ret is not None:
if ret[0] is None:
self.last_fetch_navs_t = ret[2]
else:
self.astro_dog.navs, self.astro_dog.navs_fetched_times, self.last_fetch_navs_t = ret
self.cache_ephemeris()
def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir):
astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, cache_dir=cache_dir)
cloudlog.info(f"Start to download/parse navs for time {t.as_datetime()}")
start_time = time.monotonic()
try:
astro_dog.get_navs(t)
cloudlog.info(f"Done parsing navs. Took {time.monotonic() - start_time:.1f}s")
cloudlog.debug(f"Downloaded navs ({sum([len(v) for v in astro_dog.navs])}): {list(astro_dog.navs.keys())}" +
f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in astro_dog.orbit_fetched_times._ranges]}")
return astro_dog.navs, astro_dog.navs_fetched_times, t
except (DownloadFailed, RuntimeError, ValueError, IOError) as e:
cloudlog.warning(f"No orbit data found or parsing failure: {e}")
return None, None, t
def create_measurement_msg(meas: GNSSMeasurement):
c = log.GnssMeasurements.CorrectedMeasurement.new_message()
c.constellationId = meas.constellation_id.value
c.svId = meas.sv_id
c.glonassFrequency = meas.glonass_freq if meas.constellation_id == ConstellationId.GLONASS else 0
c.pseudorange = float(meas.observables_final['C1C'])
c.pseudorangeStd = float(meas.observables_std['C1C'])
c.pseudorangeRate = float(meas.observables_final['D1C'])
c.pseudorangeRateStd = float(meas.observables_std['D1C'])
c.satPos = meas.sat_pos_final.tolist()
c.satVel = meas.sat_vel.tolist()
c.satVel = meas.sat_vel.tolist()
return c
def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMeasurement]):
ekf_data = defaultdict(list)
for m in measurements:
m_arr = m.as_array()
if m.constellation_id == ConstellationId.GPS:
ekf_data[ObservationKind.PSEUDORANGE_GPS].append(m_arr)
elif m.constellation_id == ConstellationId.GLONASS:
ekf_data[ObservationKind.PSEUDORANGE_GLONASS].append(m_arr)
ekf_data[ObservationKind.PSEUDORANGE_RATE_GPS] = ekf_data[ObservationKind.PSEUDORANGE_GPS]
ekf_data[ObservationKind.PSEUDORANGE_RATE_GLONASS] = ekf_data[ObservationKind.PSEUDORANGE_GLONASS]
for kind, data in ekf_data.items():
if len(data) > 0:
gnss_kf.predict_and_observe(t, kind, data)
def clear_tmp_cache():
if os.path.exists(Paths.download_cache_root()):
shutil.rmtree(Paths.download_cache_root())
os.mkdir(Paths.download_cache_root())
def main():
#clear_tmp_cache()
use_qcom = not Params().get_bool("UbloxAvailable")
if use_qcom:
raw_name = "qcomGnss"
else:
raw_name = "ubloxGnss"
raw_gnss_sock = messaging.sub_sock(raw_name, conflate=False)
pm = messaging.PubMaster(['gnssMeasurements'])
# disable until set as main gps source, to better analyze startup time
# TODO ensure low CPU usage before enabling
use_internet = False # "LAIKAD_NO_INTERNET" not in os.environ
replay = "REPLAY" in os.environ
laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom)
while True:
for in_msg in messaging.drain_sock(raw_gnss_sock, wait_for_one=True):
out_msg = laikad.process_gnss_msg(getattr(in_msg, raw_name), in_msg.logMonoTime, replay)
pm.send('gnssMeasurements', out_msg)
if __name__ == "__main__":
main()

@ -1,319 +0,0 @@
#!/usr/bin/env python3
import pytest
import time
import unittest
from cereal import log
from openpilot.common.params import Params
from datetime import datetime
from unittest import mock
from laika.constants import SECS_IN_DAY
from laika.downloader import DownloadFailed
from laika.ephemeris import EphemerisType
from laika.gps_time import GPSTime
from laika.helpers import ConstellationId
from laika.raw_gnss import GNSSMeasurement, read_raw_ublox, read_raw_qcom
from openpilot.selfdrive.locationd.laikad import EPHEMERIS_CACHE, Laikad
from openpilot.selfdrive.test.openpilotci import get_url
from openpilot.tools.lib.logreader import LogReader
from openpilot.selfdrive.test.process_replay.process_replay import get_process_config, replay_process
GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC = GPSTime.from_datetime(datetime(2022, month=1, day=29, hour=12))
UBLOX_TEST_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"
QCOM_TEST_ROUTE = "616dc83ca1f7f11e|2023-07-11--10-52-31"
def get_log_ublox():
logs = LogReader(get_url(UBLOX_TEST_ROUTE, 0))
ublox_cfg = get_process_config("ubloxd")
all_logs = replay_process(ublox_cfg, logs)
low_gnss = []
for m in all_logs:
if m.which() != "ubloxGnss" or m.ubloxGnss.which() != 'measurementReport':
continue
MAX_MEAS = 7
if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS:
mb = log.Event.new_message(ubloxGnss=m.ubloxGnss.to_dict())
mb.logMonoTime = m.logMonoTime
mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS
mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS]
mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000
low_gnss.append(mb.as_reader())
else:
low_gnss.append(m)
return all_logs, low_gnss
def get_log_qcom():
logs = LogReader(get_url(QCOM_TEST_ROUTE, 0))
all_logs = [m for m in logs if m.which() == 'qcomGnss']
return all_logs
def verify_messages(lr, laikad, return_one_success=False):
good_msgs = []
for m in lr:
if m.which() == 'ubloxGnss':
gnss_msg = m.ubloxGnss
elif m.which() == 'qcomGnss':
gnss_msg = m.qcomGnss
else:
continue
msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=True)
if msg is not None and len(msg.gnssMeasurements.correctedMeasurements) > 0:
good_msgs.append(msg)
if return_one_success:
return msg
return good_msgs
def get_first_gps_time(logs):
for m in logs:
if m.which() == 'ubloxGnss':
if m.ubloxGnss.which == 'measurementReport':
new_meas = read_raw_ublox(m.ubloxGnss.measurementReport)
if len(new_meas) > 0:
return new_meas[0].recv_time
elif m.which() == "qcomGnss":
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):
meas = GNSSMeasurement(ConstellationId.GPS, 1, gpstime.week, gpstime.tow, {'C1C': 0., 'D1C': 0.}, {'C1C': 0., 'D1C': 0.})
# Fake measurement being processed
meas.observables_final = meas.observables
meas.sat_ephemeris = sat_ephemeris
return meas
@pytest.mark.slow
class TestLaikad(unittest.TestCase):
@classmethod
def setUpClass(cls):
logs, low_gnss = get_log_ublox()
cls.logs = logs
cls.low_gnss = low_gnss
cls.logs_qcom = get_log_qcom()
first_gps_time = get_first_gps_time(logs)
cls.first_gps_time = first_gps_time
def setUp(self):
Params().remove(EPHEMERIS_CACHE)
def test_fetch_navs_non_blocking(self):
gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1))
laikad = Laikad()
laikad.fetch_navs(gpstime, block=False)
laikad.orbit_fetch_future.result(30)
# Get results and save orbits to laikad:
laikad.fetch_navs(gpstime, block=False)
ephem = laikad.astro_dog.navs['G01'][0]
self.assertIsNotNone(ephem)
laikad.fetch_navs(gpstime+2*SECS_IN_DAY, block=False)
laikad.orbit_fetch_future.result(30)
# Get results and save orbits to laikad:
laikad.fetch_navs(gpstime + 2 * SECS_IN_DAY, block=False)
ephem2 = laikad.astro_dog.navs['G01'][0]
self.assertIsNotNone(ephem)
self.assertNotEqual(ephem, ephem2)
def test_fetch_navs_with_wrong_clocks(self):
laikad = Laikad()
def check_has_navs():
self.assertGreater(len(laikad.astro_dog.navs), 0)
ephem = laikad.astro_dog.navs['G01'][0]
self.assertIsNotNone(ephem)
real_current_time = GPSTime.from_datetime(datetime(2021, month=3, day=1))
wrong_future_clock_time = real_current_time + SECS_IN_DAY
laikad.fetch_navs(wrong_future_clock_time, block=True)
check_has_navs()
self.assertEqual(laikad.last_fetch_navs_t, wrong_future_clock_time)
# Test fetching orbits with earlier time
assert real_current_time < laikad.last_fetch_navs_t
laikad.astro_dog.orbits = {}
laikad.fetch_navs(real_current_time, block=True)
check_has_navs()
self.assertEqual(laikad.last_fetch_navs_t, real_current_time)
def test_laika_online(self):
laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT)
correct_msgs = verify_messages(self.logs, laikad)
correct_msgs_expected = 560
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]))
def test_kf_becomes_valid(self):
laikad = Laikad(auto_update=False)
m = self.logs[0]
self.assertFalse(all(laikad.kf_valid(m.logMonoTime * 1e-9)))
kf_valid = False
for m in self.logs:
if m.which() != 'ubloxGnss':
continue
laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True)
kf_valid = all(laikad.kf_valid(m.logMonoTime * 1e-9))
if kf_valid:
break
self.assertTrue(kf_valid)
def test_laika_online_nav_only(self):
for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True):
laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV, use_qcom=use_qcom)
# Disable fetch_orbits to test NAV only
correct_msgs = verify_messages(logs, laikad)
correct_msgs_expected = 55 if use_qcom else 560
valid_fix_expected = 55 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')
def test_laika_offline(self, downloader_mock):
downloader_mock.side_effect = DownloadFailed("Mock download failed")
laikad = Laikad(auto_update=False)
laikad.fetch_navs(GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC, block=True)
@mock.patch('laika.downloader.download_and_cache_file')
def test_download_failed_russian_source(self, downloader_mock):
downloader_mock.side_effect = DownloadFailed
laikad = Laikad(auto_update=False)
correct_msgs = verify_messages(self.logs, laikad)
expected_msgs = 376
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):
laikad = Laikad(auto_update=False)
# Pretend process has loaded the orbits on startup by using the time of the first gps message.
laikad.fetch_navs(self.first_gps_time, block=True)
self.dict_has_values(laikad.astro_dog.navs)
@unittest.skip("Use to debug live data")
def test_laika_get_navs_now(self):
laikad = Laikad(auto_update=False)
laikad.fetch_navs(GPSTime.from_datetime(datetime.utcnow()), block=True)
prn = "G01"
self.assertGreater(len(laikad.astro_dog.navs[prn]), 0)
prn = "R01"
self.assertGreater(len(laikad.astro_dog.navs[prn]), 0)
def test_get_navs_in_process(self):
for auto_fetch_navs in [True, False]:
for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True):
laikad = Laikad(auto_update=False, use_qcom=use_qcom, auto_fetch_navs=auto_fetch_navs)
has_navs = False
has_fix = False
seen_chip_eph = False
seen_internet_eph = False
for m in logs:
if m.which() != 'ubloxGnss' and m.which() != 'qcomGnss':
continue
gnss_msg = m.qcomGnss if use_qcom else m.ubloxGnss
out_msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=False)
if laikad.orbit_fetch_future is not None:
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):
use_qcom = True
for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True):
Params().remove(EPHEMERIS_CACHE)
laikad = Laikad(auto_update=True, save_ephemeris=True, use_qcom=use_qcom)
def wait_for_cache():
max_time = 2
while Params().get(EPHEMERIS_CACHE) is None:
time.sleep(0.1)
max_time -= 0.1
if max_time < 0:
self.fail("Cache has not been written after 2 seconds")
# Test cache with no ephemeris
laikad.last_report_time = GPSTime(1,0)
laikad.cache_ephemeris()
if Params().get(EPHEMERIS_CACHE) is not None:
self.fail("Cache should not have been written without valid ephem")
#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)
#TODO test cache with only orbits
#with patch('selfdrive.locationd.laikad.get_orbit_data', return_value=None) as mock_method:
# # 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
# laikad.astro_dog.orbit_fetched_times = TimeRangeHolder()
# laikad.fetch_navs(self.first_gps_time, block=False)
# mock_method.assert_not_called()
# # 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()
#break
def test_low_gnss_meas(self):
cnt = 0
laikad = Laikad()
for m in self.low_gnss:
msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True)
if msg is None:
continue
gm = msg.gnssMeasurements
if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid:
cnt += 1
self.assertEqual(cnt, 560)
def dict_has_values(self, dct):
self.assertGreater(len(dct), 0)
self.assertGreater(min([len(v) for v in dct.values()]), 0)
if __name__ == "__main__":
unittest.main()

@ -45,7 +45,6 @@ procs = [
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
NativeProcess("camerad", "system/camerad", ["./camerad"], driverview),
NativeProcess("clocksd", "system/clocksd", ["./clocksd"], only_onroad),
NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad),
NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad),
PythonProcess("logmessaged", "system.logmessaged", always_run),
@ -69,7 +68,6 @@ procs = [
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
PythonProcess("deleter", "system.loggerd.deleter", always_run),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
PythonProcess("laikad", "selfdrive.locationd.laikad", only_onroad),
PythonProcess("rawgpsd", "system.sensord.rawgps.rawgpsd", qcomgps, enabled=TICI),
PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),

@ -26,6 +26,8 @@ class ModelConstants:
# model outputs constants
FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32)
FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32)
FCW_5MS2_PROBS_WIDTH = 5
FCW_3MS2_PROBS_WIDTH = 2
DISENGAGE_WIDTH = 5
POSE_WIDTH = 6

@ -1,16 +1,19 @@
import os
import capnp
import numpy as np
from typing import Dict
from cereal import log
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
ConfidenceClass = log.ModelDataV2.ConfidenceClass
class PublishState:
def __init__(self):
self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_5ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_3ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_5ms2_probs = np.zeros(ModelConstants.FCW_5MS2_PROBS_WIDTH, dtype=np.float32)
self.prev_brake_3ms2_probs = np.zeros(ModelConstants.FCW_3MS2_PROBS_WIDTH, dtype=np.float32)
def fill_xyzt(builder, t, x, y, z, x_std=None, y_std=None, z_std=None):
builder.t = t
@ -168,6 +171,10 @@ def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str,
else:
modelV2.confidence = ConfidenceClass.red
# raw prediction if enabled
if SEND_RAW_PRED:
modelV2.rawPredictions = net_output_data['raw_pred'].tobytes()
def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray],
vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None:
msg.valid = live_calib_seen & (vipc_dropped_frames < 1)

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import sys
import os
import time
import pickle
import numpy as np
@ -22,6 +23,8 @@ from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
MODEL_PATHS = {
ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed',
ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'}
@ -73,7 +76,10 @@ class ModelState:
self.model.addInput(k, v)
def slice_outputs(self, model_outputs: np.ndarray) -> Dict[str, np.ndarray]:
return {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_model_outputs['raw_pred'] = model_outputs.copy()
return parsed_model_outputs
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[Dict[str, np.ndarray]]:

@ -17,7 +17,6 @@ Currently the following processes are tested:
* locationd
* paramsd
* ubloxd
* laikad
* torqued
### Usage
@ -71,7 +70,7 @@ lr = LogReader(...)
output_logs = replay_process_with_name('locationd', lr)
# or list of names
output_logs = replay_process_with_name(['ubloxd', 'locationd', 'laikad'], lr)
output_logs = replay_process_with_name(['ubloxd', 'locationd'], lr)
```
Supported processes:
@ -83,7 +82,6 @@ Supported processes:
* locationd
* paramsd
* ubloxd
* laikad
* torqued
* modeld
* dmonitoringmodeld

@ -448,16 +448,6 @@ def controlsd_config_callback(params, cfg, lr):
params.put("ReplayControlsState", controlsState.as_builder().to_bytes())
def laikad_config_pubsub_callback(params, cfg, lr):
ublox = params.get_bool("UbloxAvailable")
main_key = "ubloxGnss" if ublox else "qcomGnss"
sub_keys = ({"qcomGnss", } if ublox else {"ubloxGnss", })
cfg.pubs = set(cfg.pubs) - sub_keys
cfg.main_pub = main_key
cfg.main_pub_drained = True
def locationd_config_pubsub_callback(params, cfg, lr):
ublox = params.get_bool("UbloxAvailable")
sub_keys = ({"gpsLocation", } if ublox else {"gpsLocationExternal", })
@ -543,17 +533,6 @@ CONFIGS = [
subs=["ubloxGnss", "gpsLocationExternal"],
ignore=["logMonoTime"],
),
ProcessConfig(
proc_name="laikad",
pubs=["ubloxGnss", "qcomGnss"],
subs=["gnssMeasurements"],
ignore=["logMonoTime"],
config_callback=laikad_config_pubsub_callback,
tolerance=NUMPY_TOLERANCE,
processing_time=0.002,
timeout=60*10, # first messages are blocked on internet assistance
main_pub="ubloxGnss", # config_callback will switch this to qcom if needed
),
ProcessConfig(
proc_name="torqued",
pubs=["liveLocationKalman", "carState", "carControl"],

@ -13,7 +13,7 @@ import openpilot.selfdrive.test.process_replay.process_replay as pr
# These processes currently fail because of unrealistic data breaking assumptions
# that openpilot makes causing error with NaN, inf, int size, array indexing ...
# TODO: Make each one testable
NOT_TESTED = ['controlsd', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'laikad', 'dmonitoringmodeld', 'modeld']
NOT_TESTED = ['controlsd', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld']
TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED]
class TestFuzzProcesses(unittest.TestCase):

@ -83,14 +83,12 @@ if __name__ == '__main__':
from openpilot.selfdrive.controls.radard import radard_thread
from openpilot.selfdrive.locationd.paramsd import main as paramsd_thread
from openpilot.selfdrive.controls.plannerd import main as plannerd_thread
from openpilot.selfdrive.locationd.laikad import main as laikad_thread
procs = {
'radard': radard_thread,
'controlsd': controlsd_thread,
'paramsd': paramsd_thread,
'plannerd': plannerd_thread,
'laikad': laikad_thread,
}
proc = sys.argv[1]

@ -47,7 +47,6 @@ PROCS = {
"selfdrive.monitoring.dmonitoringd": 4.0,
"./proclogd": 1.54,
"system.logmessaged": 0.2,
"./clocksd": 0.02,
"selfdrive.tombstoned": 0,
"./logcatd": 0,
"system.micd": 10.0,
@ -57,7 +56,6 @@ PROCS = {
"selfdrive.navd.navd": 0.4,
"system.loggerd.uploader": 3.0,
"system.loggerd.deleter": 0.1,
"selfdrive.locationd.laikad": (1.0, 80.0), # TODO: better GPS setup in testing closet
}
PROCS.update({

@ -8,7 +8,6 @@ from azure.storage.blob import ContainerClient
from tqdm import tqdm
from openpilot.selfdrive.car.tests.routes import routes as test_car_models_routes
from openpilot.selfdrive.locationd.test.test_laikad import UBLOX_TEST_ROUTE, QCOM_TEST_ROUTE
from openpilot.selfdrive.test.process_replay.test_processes import source_segments as replay_segments
from openpilot.selfdrive.test.openpilotci import (DATA_CI_ACCOUNT, DATA_CI_ACCOUNT_URL, DATA_CI_CONTAINER,
get_azure_credential, get_container_sas)
@ -90,7 +89,6 @@ if __name__ == "__main__":
if not len(to_sync):
# sync routes from the car tests routes and process replay
to_sync.extend([UBLOX_TEST_ROUTE, QCOM_TEST_ROUTE])
to_sync.extend([rt.route for rt in test_car_models_routes])
to_sync.extend([s[1].rsplit('--', 1)[0] for s in replay_segments])

@ -1 +0,0 @@
clocksd

@ -1,2 +0,0 @@
Import('env', 'common', 'cereal', 'messaging')
env.Program('clocksd.cc', LIBS=[common, cereal, messaging, 'capnp', 'zmq', 'kj'])

@ -1,73 +0,0 @@
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <cstdint>
#include <cstdio>
// Apple doesn't have timerfd
#ifdef __APPLE__
#include <thread>
#else
#include <sys/timerfd.h>
#endif
#include <cassert>
#include <chrono>
#include "cereal/messaging/messaging.h"
#include "common/timing.h"
#include "common/util.h"
ExitHandler do_exit;
int main() {
setpriority(PRIO_PROCESS, 0, -13);
PubMaster pm({"clocks"});
#ifndef __APPLE__
int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
assert(timerfd >= 0);
struct itimerspec spec = {0};
spec.it_interval.tv_sec = 1;
spec.it_interval.tv_nsec = 0;
spec.it_value.tv_sec = 1;
spec.it_value.tv_nsec = 0;
int err = timerfd_settime(timerfd, 0, &spec, 0);
assert(err == 0);
uint64_t expirations = 0;
while (!do_exit && (err = read(timerfd, &expirations, sizeof(expirations)))) {
if (err < 0) {
if (errno == EINTR) continue;
break;
}
#else
// Just run at 1Hz on apple
while (!do_exit) {
util::sleep_for(1000);
#endif
uint64_t boottime = nanos_since_boot();
uint64_t monotonic = nanos_monotonic();
uint64_t monotonic_raw = nanos_monotonic_raw();
uint64_t wall_time = nanos_since_epoch();
MessageBuilder msg;
auto clocks = msg.initEvent().initClocks();
clocks.setBootTimeNanos(boottime);
clocks.setMonotonicNanos(monotonic);
clocks.setMonotonicRawNanos(monotonic_raw);
clocks.setWallTimeNanos(wall_time);
pm.send("clocks", msg);
}
#ifndef __APPLE__
close(timerfd);
#endif
return 0;
}

@ -5,13 +5,11 @@ import time
import datetime
import unittest
import subprocess
import numpy as np
import cereal.messaging as messaging
from openpilot.system.hardware import TICI
from openpilot.system.sensord.rawgps.rawgpsd import at_cmd, wait_for_modem
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.common.transformations.coordinates import ecef_from_geodetic
GOOD_SIGNAL = bool(int(os.getenv("GOOD_SIGNAL", '0')))
@ -123,22 +121,5 @@ class TestRawgpsd(unittest.TestCase):
managed_processes['rawgpsd'].stop()
self.check_assistance(True)
@unittest.skipIf(not GOOD_SIGNAL, "No good GPS signal")
def test_fix(self):
managed_processes['rawgpsd'].start()
managed_processes['laikad'].start()
assert self._wait_for_output(60)
assert self.sm.updated['qcomGnss']
assert self.sm.updated['gpsLocation']
assert self.sm['gpsLocation'].flags == 1
module_fix = ecef_from_geodetic([self.sm['gpsLocation'].latitude,
self.sm['gpsLocation'].longitude,
self.sm['gpsLocation'].altitude])
assert self.sm['gnssMeasurements'].positionECEF.valid
total_diff = np.array(self.sm['gnssMeasurements'].positionECEF.value) - module_fix
self.assertLess(np.linalg.norm(total_diff), 100)
managed_processes['laikad'].stop()
managed_processes['rawgpsd'].stop()
if __name__ == "__main__":
unittest.main(failfast=True)

@ -22,12 +22,6 @@
#include "tools/cabana/streamselector.h"
#include "tools/cabana/tools/findsignal.h"
static MainWindow *main_win = nullptr;
void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl;
if (main_win) emit main_win->showMessage(msg, 2000);
}
MainWindow::MainWindow() : QMainWindow() {
createDockWindows();
setCentralWidget(center_widget = new CenterWidget(this));
@ -45,20 +39,23 @@ MainWindow::MainWindow() : QMainWindow() {
}
restoreState(settings.window_state);
// install handlers
static auto static_main_win = this;
qRegisterMetaType<uint64_t>("uint64_t");
qRegisterMetaType<SourceSet>("SourceSet");
qRegisterMetaType<ReplyMsgType>("ReplyMsgType");
installMessageHandler([this](ReplyMsgType type, const std::string msg) {
// use queued connection to recv the log messages from replay.
emit showMessage(QString::fromStdString(msg), 2000);
installDownloadProgressHandler([](uint64_t cur, uint64_t total, bool success) {
emit static_main_win->updateProgressBar(cur, total, success);
});
installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) {
emit updateProgressBar(cur, total, success);
qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl;
emit static_main_win->showMessage(msg, 2000);
});
installMessageHandler([](ReplyMsgType type, const std::string msg) {
qInfo() << QString::fromStdString(msg);
});
main_win = this;
qInstallMessageHandler(qLogMessageHandler);
// load fingerprints
QFile json_file(QApplication::applicationDirPath() + "/dbc/car_fingerprint_to_dbc.json");
if (json_file.open(QIODevice::ReadOnly)) {
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
@ -597,7 +594,9 @@ void MainWindow::closeEvent(QCloseEvent *event) {
cleanupAutoSaveFile();
remindSaveChanges();
main_win = nullptr;
installDownloadProgressHandler(nullptr);
qInstallMessageHandler(nullptr);
if (floating_window)
floating_window->deleteLater();

@ -33,8 +33,7 @@ def run_remote_checker(lat, lon, alt, duration, ip_addr):
return False, None, None
matched, log, info = con.root.exposed_run_checker(lat, lon, alt,
timeout=duration,
use_laikad=True)
timeout=duration)
con.close() # TODO: might wanna fetch more logs here
con = None
@ -43,7 +42,7 @@ def run_remote_checker(lat, lon, alt, duration, ip_addr):
stats = defaultdict(int) # type: ignore
keys = ['success', 'failed', 'ublox_fail', 'laikad_fail', 'proc_crash', 'checker_crash']
keys = ['success', 'failed', 'ublox_fail', 'proc_crash', 'checker_crash']
def print_report():
print("\nFuzzy testing report summary:")
@ -62,10 +61,7 @@ def update_stats(matched, log, info):
if log == "CHECKER CRASHED":
stats['checker_crash'] += 1
if log == "TIMEOUT":
if "LAIKAD" in info:
stats['laikad_fail'] += 1
else: # "UBLOX" in info
stats['ublox_fail'] += 1
stats['ublox_fail'] += 1
def main(ip_addr, continuous_mode, timeout, pos):

@ -1,185 +0,0 @@
import os
import time
import shutil
from datetime import datetime
from collections import defaultdict
from openpilot.system.hardware.hw import Paths
import rpyc
from rpyc.utils.server import ThreadedServer
#from openpilot.common.params import Params
import cereal.messaging as messaging
from openpilot.selfdrive.manager.process_config import managed_processes
from laika.lib.coordinates import ecef2geodetic
DELTA = 0.001
ALT_DELTA = 30
MATCH_NUM = 10
REPORT_STATS = 10
EPHEM_CACHE = "/data/params/d/LaikadEphemerisV3"
SERVER_LOG_FILE = "/tmp/fuzzy_server.log"
server_log = open(SERVER_LOG_FILE, "w+")
def slog(msg):
server_log.write(f"{datetime.now().strftime('%H:%M:%S.%f')} | {msg}\n")
server_log.flush()
def handle_laikad(msg):
if not hasattr(msg, 'correctedMeasurements'):
return None
num_corr = len(msg.correctedMeasurements)
pos_ecef = msg.positionECEF.value
pos_geo = []
if len(pos_ecef) > 0:
pos_geo = ecef2geodetic(pos_ecef)
pos_std = msg.positionECEF.std
pos_valid = msg.positionECEF.valid
slog(f"{num_corr} {pos_geo} {pos_ecef} {pos_std} {pos_valid}")
return pos_geo, (num_corr, pos_geo, list(pos_ecef), list(msg.positionECEF.std))
hw_msgs = 0
ephem_msgs: dict = defaultdict(int)
def handle_ublox(msg):
global hw_msgs
d = msg.to_dict()
if 'hwStatus2' in d:
hw_msgs += 1
if 'ephemeris' in d:
ephem_msgs[msg.ephemeris.svId] += 1
num_meas = None
if 'measurementReport' in d:
num_meas = msg.measurementReport.numMeas
return [hw_msgs, ephem_msgs, num_meas]
def start_procs(procs):
for p in procs:
managed_processes[p].start()
time.sleep(1)
def kill_procs(procs, no_retry=False):
for p in procs:
managed_processes[p].stop()
time.sleep(1)
if not no_retry:
for p in procs:
mp = managed_processes[p].proc
if mp is not None and mp.is_alive():
managed_processes[p].stop()
time.sleep(3)
def check_alive_procs(procs):
for p in procs:
mp = managed_processes[p].proc
if mp is None or not mp.is_alive():
return False, p
return True, None
class RemoteCheckerService(rpyc.Service):
def on_connect(self, conn):
pass
def on_disconnect(self, conn):
#kill_procs(self.procs, no_retry=False)
# this execution is delayed, it will kill the next run of laikad
# TODO: add polling to wait for everything is killed
pass
def run_checker(self, slat, slon, salt, sockets, procs, timeout):
global hw_msgs, ephem_msgs
hw_msgs = 0
ephem_msgs = defaultdict(int)
slog(f"Run test: {slat} {slon} {salt}")
# quectel_mod = Params().get_bool("UbloxAvailable")
match_cnt = 0
msg_cnt = 0
stats_laikad = []
stats_ublox = []
self.procs = procs
start_procs(procs)
sm = messaging.SubMaster(sockets)
start_time = time.monotonic()
while True:
sm.update()
if sm.updated['ubloxGnss']:
stats_ublox.append(handle_ublox(sm['ubloxGnss']))
if sm.updated['gnssMeasurements']:
pos_geo, stats = handle_laikad(sm['gnssMeasurements'])
if pos_geo is None or len(pos_geo) == 0:
continue
match = all(abs(g-s) < DELTA for g,s in zip(pos_geo[:2], [slat, slon], strict=True))
match &= abs(pos_geo[2] - salt) < ALT_DELTA
if match:
match_cnt += 1
if match_cnt >= MATCH_NUM:
return True, "MATCH", f"After: {round(time.monotonic() - start_time, 4)}"
# keep some stats for error reporting
stats_laikad.append(stats)
if (msg_cnt % 10) == 0:
a, p = check_alive_procs(procs)
if not a:
return False, "PROC CRASH", f"{p}"
msg_cnt += 1
if (time.monotonic() - start_time) > timeout:
h = f"LAIKAD: {stats_laikad[-REPORT_STATS:]}"
if len(h) == 0:
h = f"UBLOX: {stats_ublox[-REPORT_STATS:]}"
return False, "TIMEOUT", h
def exposed_run_checker(self, slat, slon, salt, timeout=180, use_laikad=True):
try:
procs = []
sockets = []
if use_laikad:
procs.append("laikad") # pigeond, ubloxd # might wanna keep them running
sockets += ['ubloxGnss', 'gnssMeasurements']
if os.path.exists(EPHEM_CACHE):
os.remove(EPHEM_CACHE)
shutil.rmtree(Paths.download_cache_root(), ignore_errors=True)
ret = self.run_checker(slat, slon, salt, sockets, procs, timeout)
kill_procs(procs)
return ret
except Exception as e:
# always make sure processes get killed
kill_procs(procs)
return False, "CHECKER CRASHED", f"{str(e)}"
def exposed_kill_procs(self):
kill_procs(self.procs, no_retry=True)
if __name__ == "__main__":
print(f"Sever Log written to: {SERVER_LOG_FILE}")
t = ThreadedServer(RemoteCheckerService, port=18861)
t.start()

@ -1,105 +0,0 @@
#!/usr/bin/env python3
import os
import time
import unittest
import cereal.messaging as messaging
import openpilot.system.sensord.pigeond as pd
from openpilot.common.params import Params
from openpilot.system.hardware import TICI
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.selfdrive.test.helpers import with_processes
def wait_for_location(sm, timeout, con=10):
cons_meas = 0
start_time = time.monotonic()
while (time.monotonic() - start_time) < timeout:
sm.update()
if not sm.updated["gnssMeasurements"]:
continue
msg = sm["gnssMeasurements"]
cons_meas = (cons_meas + 1) if 'positionECEF' in msg.to_dict() else 0
if cons_meas >= con:
return True
return False
class TestLaikad(unittest.TestCase):
@classmethod
def setUpClass(self):
if not TICI:
raise unittest.SkipTest
ublox_available = Params().get_bool("UbloxAvailable")
if not ublox_available:
raise unittest.SkipTest
def setUp(self):
# ensure laikad cold start
Params().remove("LaikadEphemerisV3")
os.environ["LAIKAD_NO_INTERNET"] = "1"
managed_processes['laikad'].start()
def tearDown(self):
managed_processes['laikad'].stop()
@with_processes(['pigeond', 'ubloxd'])
def test_laikad_cold_start(self):
time.sleep(5)
start_time = time.monotonic()
sm = messaging.SubMaster(["gnssMeasurements"])
success = wait_for_location(sm, 60*2, con=10)
duration = time.monotonic() - start_time
assert success, "Waiting for location timed out (2min)!"
assert duration < 60, f"Received Location {duration}!"
@with_processes(['ubloxd'])
def test_laikad_ublox_reset_start(self):
time.sleep(2)
pigeon, pm = pd.create_pigeon()
pd.init_baudrate(pigeon)
assert pigeon.reset_device(), "Could not reset device!"
laikad_sock = messaging.sub_sock("gnssMeasurements", timeout=0.1)
ublox_gnss_sock = messaging.sub_sock("ubloxGnss", timeout=0.1)
pd.init_baudrate(pigeon)
pd.initialize_pigeon(pigeon)
pd.run_receiving(pigeon, pm, 180)
ublox_msgs = messaging.drain_sock(ublox_gnss_sock)
laikad_msgs = messaging.drain_sock(laikad_sock)
gps_ephem_cnt = 0
glonass_ephem_cnt = 0
for um in ublox_msgs:
if um.ubloxGnss.which() == 'ephemeris':
gps_ephem_cnt += 1
elif um.ubloxGnss.which() == 'glonassEphemeris':
glonass_ephem_cnt += 1
assert gps_ephem_cnt > 0, "NO gps ephemeris collected!"
assert glonass_ephem_cnt > 0, "NO glonass ephemeris collected!"
pos_meas = 0
duration = -1
for lm in laikad_msgs:
pos_meas = (pos_meas + 1) if 'positionECEF' in lm.gnssMeasurements.to_dict() else 0
if pos_meas > 5:
duration = (lm.logMonoTime - laikad_msgs[0].logMonoTime)*1e-9
break
assert pos_meas > 5, "NOT enough positions at end of read!"
assert duration < 120, "Laikad took too long to get a Position!"
if __name__ == "__main__":
unittest.main()

@ -19,10 +19,7 @@
#include "common/util.h"
ReplayMessageHandler message_handler = nullptr;
DownloadProgressHandler download_progress_handler = nullptr;
void installMessageHandler(ReplayMessageHandler handler) { message_handler = handler; }
void installDownloadProgressHandler(DownloadProgressHandler handler) { download_progress_handler = handler; }
void logMessage(ReplyMsgType type, const char *fmt, ...) {
static std::mutex lock;
@ -94,6 +91,11 @@ size_t write_cb(char *data, size_t size, size_t count, void *userp) {
size_t dumy_write_cb(char *data, size_t size, size_t count, void *userp) { return size * count; }
struct DownloadStats {
void installDownloadProgressHandler(DownloadProgressHandler handler) {
std::lock_guard lk(lock);
download_progress_handler = handler;
}
void add(const std::string &url, uint64_t total_bytes) {
std::lock_guard lk(lock);
items[url] = {0, total_bytes};
@ -121,10 +123,17 @@ struct DownloadStats {
std::mutex lock;
std::map<std::string, std::pair<uint64_t, uint64_t>> items;
double prev_tm = 0;
DownloadProgressHandler download_progress_handler = nullptr;
};
static DownloadStats download_stats;
} // namespace
void installDownloadProgressHandler(DownloadProgressHandler handler) {
download_stats.installDownloadProgressHandler(handler);
}
std::string formattedDataSize(size_t size) {
if (size < 1024) {
return std::to_string(size) + " B";
@ -167,7 +176,6 @@ std::string getUrlWithoutQuery(const std::string &url) {
template <class T>
bool httpDownload(const std::string &url, T &buf, size_t chunk_size, size_t content_length, std::atomic<bool> *abort) {
static DownloadStats download_stats;
download_stats.add(url, content_length);
int parts = 1;

Loading…
Cancel
Save