Merge remote-tracking branch 'upstream/master' into take-steering

pull/26010/head
Shane Smiskol 3 years ago
commit f8c41268e1
  1. 1
      RELEASES.md
  2. 2
      cereal
  3. 9
      docs/CARS.md
  4. 2
      panda
  5. 20
      selfdrive/car/gm/interface.py
  6. 33
      selfdrive/car/honda/values.py
  7. 9
      selfdrive/car/hyundai/carcontroller.py
  8. 8
      selfdrive/car/hyundai/hyundaicanfd.py
  9. 10
      selfdrive/car/hyundai/interface.py
  10. 19
      selfdrive/car/hyundai/values.py
  11. 5
      selfdrive/car/tests/routes.py
  12. 8
      selfdrive/car/tests/test_fw_fingerprint.py
  13. 1
      selfdrive/car/torque_data/override.yaml
  14. 2
      selfdrive/controls/lib/events.py
  15. 2
      selfdrive/controls/lib/latcontrol_torque.py
  16. 3
      selfdrive/controls/lib/lateral_planner.py
  17. 2
      selfdrive/test/process_replay/ref_commit
  18. 4
      selfdrive/test/process_replay/test_processes.py
  19. 6
      selfdrive/test/update_ci_routes.py
  20. 2
      tools/cabana/.gitignore
  21. 15
      tools/cabana/README.md
  22. 1
      tools/cabana/SConscript
  23. 47
      tools/cabana/binaryview.cc
  24. 12
      tools/cabana/binaryview.h
  25. 29
      tools/cabana/canmessages.cc
  26. 11
      tools/cabana/canmessages.h
  27. 18
      tools/cabana/detailwidget.cc
  28. 3
      tools/cabana/detailwidget.h
  29. 24
      tools/cabana/generate_dbc_json.py
  30. 2
      tools/cabana/historylog.cc
  31. 33
      tools/cabana/messageswidget.cc
  32. 7
      tools/cabana/messageswidget.h
  33. 96
      tools/cabana/signaledit.cc
  34. 14
      tools/cabana/signaledit.h

@ -12,6 +12,7 @@ Version 0.8.17 (2022-XX-XX)
* Border turns grey while overriding steering * Border turns grey while overriding steering
* Added button to bookmark events while driving; view them later in comma connect * Added button to bookmark events while driving; view them later in comma connect
* AGNOS 6 * AGNOS 6
* Kia Sportage Hybrid 2023 support thanks to sunnyhaibin!
Version 0.8.16 (2022-08-26) Version 0.8.16 (2022-08-26)
======================== ========================

@ -1 +1 @@
Subproject commit 107048c83ec2f488286a1be314e7aece0a20a6b1 Subproject commit 1e3dd70a391bc1bbe437d3eea8be30947f929a75

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system.
# 208 Supported Cars # 209 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness| |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|
@ -18,8 +18,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
@ -32,7 +32,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Genesis|G90 2017-18|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Genesis|G90 2017-18|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Honda|Accord 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| |Honda|Accord 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|
|Honda|Accord Hybrid 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| |Honda|Accord Hybrid 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|
@ -100,6 +100,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Kia|Telluride 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Kia|Telluride 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock[<sup>3</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|

@ -1 +1 @@
Subproject commit b1ca52f86d59500c6232df6afac97a51daf7bd51 Subproject commit 02b74fcfe19cbb000e5fb696e028f6f67690c20c

@ -62,11 +62,14 @@ class CarInterface(CarInterfaceBase):
ret.radarOffCan = True # no radar ret.radarOffCan = True # no radar
ret.pcmCruise = True ret.pcmCruise = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
else: # ASCM, OBD-II harness else: # ASCM, OBD-II harness
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
ret.networkLocation = NetworkLocation.gateway ret.networkLocation = NetworkLocation.gateway
ret.radarOffCan = False ret.radarOffCan = False
ret.pcmCruise = False # stock non-adaptive cruise control is kept off ret.pcmCruise = False # stock non-adaptive cruise control is kept off
# supports stop and go, but initial engage must (conservatively) be above 18mph
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
# These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is # These cars likely still work fine. Once a user confirms each car works and a test route is
@ -90,9 +93,6 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
# supports stop and go, but initial engage must (conservatively) be above 18mph
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
if candidate == CAR.VOLT: if candidate == CAR.VOLT:
ret.mass = 1607. + STD_CARGO_KG ret.mass = 1607. + STD_CARGO_KG
ret.wheelbase = 2.69 ret.wheelbase = 2.69
@ -153,7 +153,6 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 1.0 tire_stiffness_factor = 1.0
elif candidate in (CAR.BOLT_EV, CAR.BOLT_EUV): elif candidate in (CAR.BOLT_EV, CAR.BOLT_EUV):
ret.minEnableSpeed = -1
ret.mass = 1669. + STD_CARGO_KG ret.mass = 1669. + STD_CARGO_KG
ret.wheelbase = 2.63779 ret.wheelbase = 2.63779
ret.steerRatio = 16.8 ret.steerRatio = 16.8
@ -163,7 +162,6 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.SILVERADO: elif candidate == CAR.SILVERADO:
ret.minEnableSpeed = -1
ret.mass = 2200. + STD_CARGO_KG ret.mass = 2200. + STD_CARGO_KG
ret.wheelbase = 3.75 ret.wheelbase = 3.75
ret.steerRatio = 16.3 ret.steerRatio = 16.3
@ -172,7 +170,6 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.EQUINOX: elif candidate == CAR.EQUINOX:
ret.minEnableSpeed = -1
ret.mass = 3500. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3500. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.72 ret.wheelbase = 2.72
ret.steerRatio = 14.4 ret.steerRatio = 14.4
@ -207,19 +204,16 @@ class CarInterface(CarInterfaceBase):
GearShifter.eco, GearShifter.manumatic], GearShifter.eco, GearShifter.manumatic],
pcm_enable=self.CP.pcmCruise) pcm_enable=self.CP.pcmCruise)
if ret.vEgo < self.CP.minEnableSpeed: # Enabling at a standstill with brake is allowed
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
if ret.vEgo < self.CP.minEnableSpeed and not (ret.standstill and ret.brake >= 20 and
self.CP.networkLocation == NetworkLocation.fwdCamera):
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
if ret.cruiseState.standstill: if ret.cruiseState.standstill:
events.add(EventName.resumeRequired) events.add(EventName.resumeRequired)
if ret.vEgo < self.CP.minSteerSpeed: if ret.vEgo < self.CP.minSteerSpeed:
events.add(EventName.belowSteerSpeed) events.add(EventName.belowSteerSpeed)
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.pcmCruise:
# The ECM has a higher brake pressed threshold than the camera, causing an
# ACC fault when you engage at a stop with your foot partially on the brake
if ret.vEgoRaw < 0.1 and ret.brake < 20:
events.add(EventName.gmAccFaultedTemp)
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret

@ -105,7 +105,6 @@ class Footnote(Enum):
@dataclass @dataclass
class HondaCarInfo(CarInfo): class HondaCarInfo(CarInfo):
package: str = "Honda Sensing" package: str = "Honda Sensing"
min_steer_speed: float = 12. * CV.MPH_TO_MS
CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
@ -114,31 +113,31 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
], ],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [ CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", "https://www.youtube.com/watch?v=4Iz1Mz5LGF8", [Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Civic 2019-21", "All", "https://www.youtube.com/watch?v=4Iz1Mz5LGF8", [Footnote.CIVIC_DIESEL], 2. * CV.MPH_TO_MS, harness=Harness.bosch_a),
HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a), HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a),
], ],
CAR.CIVIC_BOSCH_DIESEL: None, # same platform CAR.CIVIC_BOSCH_DIESEL: None, # same platform
CAR.CIVIC_2022: [ CAR.CIVIC_2022: [
HondaCarInfo("Honda Civic 2022", "All", min_steer_speed=0., harness=Harness.bosch_b), HondaCarInfo("Honda Civic 2022", "All", harness=Harness.bosch_b),
HondaCarInfo("Honda Civic Hatchback 2022", "All", min_steer_speed=0., harness=Harness.bosch_b), HondaCarInfo("Honda Civic Hatchback 2022", "All", harness=Harness.bosch_b),
], ],
CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", harness=Harness.nidec), CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", harness=Harness.bosch_a), CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring
CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", harness=Harness.bosch_a), CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.FIT: HondaCarInfo("Honda Fit 2018-20", harness=Harness.nidec), CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec), CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", harness=Harness.nidec), CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", min_steer_speed=0., harness=Harness.nidec), CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", harness=Harness.nidec),
CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey
CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec), CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", harness=Harness.nidec), CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", harness=Harness.nidec), CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", harness=Harness.nidec), CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
} }

@ -135,14 +135,17 @@ class CarController:
self.last_button_frame = self.frame self.last_button_frame = self.frame
else: else:
for _ in range(20): for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL)) can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame self.last_button_frame = self.frame
# cruise standstill resume # cruise standstill resume
elif CC.cruiseControl.resume: elif CC.cruiseControl.resume:
if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS): if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# TODO: resume for alt button cars
pass
else:
for _ in range(20): for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL)) can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame self.last_button_frame = self.frame
else: else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, lat_active, can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, lat_active,

@ -3,7 +3,7 @@ from selfdrive.car.hyundai.values import HyundaiFlags
def get_e_can_bus(CP): def get_e_can_bus(CP):
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. HDA2 cars # On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. HDA2 cars
# have a a different harness than the HDA1 and non-HDA variants in order to split # have a different harness than the HDA1 and non-HDA variants in order to split
# a different bus, since the steering is done by different ECUs. # a different bus, since the steering is done by different ECUs.
return 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4 return 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4
@ -39,13 +39,15 @@ def create_cam_0x2a4(packer, camera_values):
}) })
return packer.make_can_msg("CAM_0x2a4", 4, camera_values) return packer.make_can_msg("CAM_0x2a4", 4, camera_values)
def create_buttons(packer, cnt, btn): def create_buttons(packer, CP, cnt, btn):
values = { values = {
"COUNTER": cnt, "COUNTER": cnt,
"SET_ME_1": 1, "SET_ME_1": 1,
"CRUISE_BUTTONS": btn, "CRUISE_BUTTONS": btn,
} }
return packer.make_can_msg("CRUISE_BUTTONS", 5, values)
bus = 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 6
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
def create_acc_cancel(packer, CP, cruise_info_copy): def create_acc_cancel(packer, CP, cruise_info_copy):
values = cruise_info_copy values = cruise_info_copy

@ -8,6 +8,7 @@ from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia,
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
Ecu = car.CarParams.Ecu
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL) ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
@ -33,8 +34,8 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30}
if candidate in CANFD_CAR: if candidate in CANFD_CAR:
# detect HDA2 with LKAS message # detect HDA2 with ADAS Driving ECU
if 0x50 in fingerprint[6]: if Ecu.adas in [fw.ecu for fw in car_fw]:
ret.flags |= HyundaiFlags.CANFD_HDA2.value ret.flags |= HyundaiFlags.CANFD_HDA2.value
else: else:
# non-HDA2 # non-HDA2
@ -247,6 +248,11 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16. ret.steerRatio = 16.
tire_stiffness_factor = 0.65 tire_stiffness_factor = 0.65
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.KIA_SPORTAGE_HYBRID_5TH_GEN:
ret.mass = 1767. + STD_CARGO_KG # SX Prestige trim support only
ret.wheelbase = 2.756
ret.steerRatio = 13.6
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Genesis # Genesis
elif candidate == CAR.GENESIS_G70: elif candidate == CAR.GENESIS_G70:

@ -36,7 +36,7 @@ class CarControllerParams:
# If the max stock LKAS request is <384, add your car to this list. # 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.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, 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.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO):
self.STEER_MAX = 255 self.STEER_MAX = 255
# Default for most HKG # Default for most HKG
@ -90,6 +90,7 @@ class CAR:
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019" KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
KIA_SELTOS = "KIA SELTOS 2021" KIA_SELTOS = "KIA SELTOS 2021"
KIA_SORENTO = "KIA SORENTO GT LINE 2018" KIA_SORENTO = "KIA SORENTO GT LINE 2018"
KIA_SPORTAGE_HYBRID_5TH_GEN = "KIA SPORTAGE HYBRID 5TH GEN"
KIA_STINGER = "KIA STINGER GT2 2018" KIA_STINGER = "KIA STINGER GT2 2018"
KIA_CEED = "KIA CEED INTRO ED 2019" KIA_CEED = "KIA CEED INTRO ED 2019"
KIA_EV6 = "KIA EV6 2022" KIA_EV6 = "KIA EV6 2022"
@ -172,6 +173,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e),
], ],
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n),
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c),
CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e), CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e),
CAR.KIA_EV6: [ CAR.KIA_EV6: [
@ -304,7 +306,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request( Request(
[HYUNDAI_VERSION_REQUEST_LONG], [HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE], [HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdRadar], whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar],
bus=4, bus=4,
), ),
Request( Request(
@ -1367,6 +1369,14 @@ FW_VERSIONS = {
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ', b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
], ],
}, },
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.00 99211-P1060 665',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NQ5__ 1.01 1.03 99110-CH000 ',
],
},
} }
CHECKSUM = { CHECKSUM = {
@ -1384,12 +1394,12 @@ FEATURES = {
"use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022}, "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022},
} }
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN} CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN}
# The camera does SCC on these cars, rather than the radar # The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN} # these cars use a different gas signal HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN} # these cars use a different gas signal
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5} EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5}
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
@ -1442,4 +1452,5 @@ DBC = {
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None), CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None), CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: dbc_dict('hyundai_canfd', None),
} }

@ -91,13 +91,14 @@ routes = [
CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4), CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0), CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS), CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
CarTestRoute("b3537035ffe6a7d6|2022-10-17--15-23-49", HYUNDAI.KIA_SPORTAGE_HYBRID_5TH_GEN),
CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON), CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN), CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN),
CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
CarTestRoute("22de8111a8c5463c|2022-07-29--13-34-49", HYUNDAI.IONIQ_5), CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.IONIQ_5), # HDA2
CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019), CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019),
CarTestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV), CarTestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV),
CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020), CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020),
@ -110,7 +111,7 @@ routes = [
CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV), CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV),
CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER), CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER), CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER),
CarTestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6), # HDA2 CarTestRoute("d545129f3ca90f28|2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # HDA2
CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1 CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1
CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021), CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),

@ -45,6 +45,7 @@ class TestFwFingerprint(unittest.TestCase):
self.assertFalse(len(duplicates), f"{car_model}: Duplicate FW versions: Ecu.{ECU_NAME[ecu[0]]}, {duplicates}") self.assertFalse(len(duplicates), f"{car_model}: Duplicate FW versions: Ecu.{ECU_NAME[ecu[0]]}, {duplicates}")
def test_data_collection_ecus(self): def test_data_collection_ecus(self):
# Asserts no extra ECUs are in the fingerprinting database
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():
for car_model, ecus in VERSIONS[brand].items(): for car_model, ecus in VERSIONS[brand].items():
bad_ecus = set(ecus).intersection(config.extra_ecus) bad_ecus = set(ecus).intersection(config.extra_ecus)
@ -80,10 +81,11 @@ class TestFwFingerprint(unittest.TestCase):
def test_fw_request_ecu_whitelist(self): def test_fw_request_ecu_whitelist(self):
for brand, config in FW_QUERY_CONFIGS.items(): for brand, config in FW_QUERY_CONFIGS.items():
with self.subTest(brand=brand): with self.subTest(brand=brand):
whitelisted_ecus = set([ecu for r in config.requests for ecu in r.whitelist_ecus]) whitelisted_ecus = {ecu for r in config.requests for ecu in r.whitelist_ecus}
brand_ecus = set([fw[0] for car_fw in VERSIONS[brand].values() for fw in car_fw]) brand_ecus = {fw[0] for car_fw in VERSIONS[brand].values() for fw in car_fw}
brand_ecus |= {ecu[0] for ecu in config.extra_ecus}
# each ecu in brand's fw versions needs to be whitelisted at least once # each ecu in brand's fw versions + extra ecus needs to be whitelisted at least once
ecus_not_whitelisted = brand_ecus - whitelisted_ecus ecus_not_whitelisted = brand_ecus - whitelisted_ecus
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_whitelisted]) ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_whitelisted])

@ -32,6 +32,7 @@ CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05]
VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1] VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1]
VOLKSWAGEN SHARAN 2ND GEN: [2.5, 2.5, 0.1] VOLKSWAGEN SHARAN 2ND GEN: [2.5, 2.5, 0.1]
HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0] HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0]
KIA SPORTAGE HYBRID 5TH GEN: [2.5, 2.5, 0.0]
# Dashcam or fallback configured as ideal car # Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0] mock: [10.0, 10, 0.0]

@ -811,7 +811,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
ET.NO_ENTRY: NoEntryAlert("Cruise Faulted"), ET.NO_ENTRY: NoEntryAlert("Cruise Faulted"),
}, },
EventName.gmAccFaultedTemp: { EventName.accFaultedTemp: {
ET.NO_ENTRY: NoEntryAlert("Cruise Temporarily Faulted"), ET.NO_ENTRY: NoEntryAlert("Cruise Temporarily Faulted"),
}, },

@ -17,7 +17,7 @@ from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
# friction in the steering wheel that needs to be overcome to # friction in the steering wheel that needs to be overcome to
# move it at all, this is compensated for too. # move it at all, this is compensated for too.
LOW_SPEED_FACTOR = 100 LOW_SPEED_FACTOR = 200
class LatControlTorque(LatControl): class LatControlTorque(LatControl):

@ -22,8 +22,7 @@ LATERAL_JERK_COST = 0.05
# TODO this cost should be lowered when low # TODO this cost should be lowered when low
# speed lateral control is stable on all cars # speed lateral control is stable on all cars
STEERING_RATE_COST = 800.0 STEERING_RATE_COST = 800.0
MIN_SPEED = 1.5
MIN_SPEED = .3
class LateralPlanner: class LateralPlanner:

@ -1 +1 @@
6bb7d8baae51d88dd61f0baf561e386664ddd266 a87455caf93e91fae0f3704aa476e0732d066b77

@ -18,7 +18,7 @@ from tools.lib.logreader import LogReader
source_segments = [ source_segments = [
("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY ("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY
("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA
("HYUNDAI2", "d824e27e8c60172c|2022-09-13--11-26-50--2"), # HYUNDAI.KIA_EV6 ("HYUNDAI2", "d545129f3ca90f28|2022-10-19--09-22-54--9"), # HYUNDAI.KIA_EV6
("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI) ("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI)
("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR) ("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR)
("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2 ("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2
@ -40,7 +40,7 @@ source_segments = [
segments = [ segments = [
("BODY", "regenFA002A80700|2022-09-27--15-37-02--0"), ("BODY", "regenFA002A80700|2022-09-27--15-37-02--0"),
("HYUNDAI", "regenBE53A59065B|2022-09-27--16-52-03--0"), ("HYUNDAI", "regenBE53A59065B|2022-09-27--16-52-03--0"),
("HYUNDAI2", "regenFA8B5CA9840|2022-10-12--21-47-06--0"), ("HYUNDAI2", "d545129f3ca90f28|2022-10-19--09-22-54--9"),
("TOYOTA", "regen929C5790007|2022-09-27--16-27-47--0"), ("TOYOTA", "regen929C5790007|2022-09-27--16-27-47--0"),
("TOYOTA2", "regenEA3950D7F22|2022-09-27--15-43-24--0"), ("TOYOTA2", "regenEA3950D7F22|2022-09-27--15-43-24--0"),
("TOYOTA3", "regen89026F6BD8D|2022-09-27--15-45-37--0"), ("TOYOTA3", "regen89026F6BD8D|2022-09-27--15-45-37--0"),

@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from functools import lru_cache
import sys import sys
import subprocess import subprocess
from tqdm import tqdm
from azure.storage.blob import BlockBlobService # pylint: disable=import-error from azure.storage.blob import BlockBlobService # pylint: disable=import-error
from selfdrive.car.tests.routes import routes as test_car_models_routes from selfdrive.car.tests.routes import routes as test_car_models_routes
@ -10,11 +12,11 @@ from xx.chffr.lib.storage import _DATA_ACCOUNT_PRODUCTION, _DATA_ACCOUNT_CI, _DA
SOURCES = [ SOURCES = [
(_DATA_ACCOUNT_PRODUCTION, _DATA_BUCKET_PRODUCTION), (_DATA_ACCOUNT_PRODUCTION, _DATA_BUCKET_PRODUCTION),
(_DATA_ACCOUNT_PRODUCTION, "preserve"),
(_DATA_ACCOUNT_CI, "commadataci"), (_DATA_ACCOUNT_CI, "commadataci"),
] ]
@lru_cache
def get_azure_keys(): def get_azure_keys():
dest_key = azureutil.get_user_token(_DATA_ACCOUNT_CI, "openpilotci") dest_key = azureutil.get_user_token(_DATA_ACCOUNT_CI, "openpilotci")
source_keys = [azureutil.get_user_token(account, bucket) for account, bucket in SOURCES] source_keys = [azureutil.get_user_token(account, bucket) for account, bucket in SOURCES]
@ -82,7 +84,7 @@ if __name__ == "__main__":
to_sync.extend([rt.route for rt in test_car_models_routes]) 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]) to_sync.extend([s[1].rsplit('--', 1)[0] for s in replay_segments])
for r in to_sync: for r in tqdm(to_sync):
if not sync_to_ci_public(r): if not sync_to_ci_public(r):
failed_routes.append(r) failed_routes.append(r)

@ -3,3 +3,5 @@ moc_*
_cabana _cabana
settings settings
car_fingerprint_to_dbc.json

@ -6,4 +6,19 @@ Cabana is a tool developed to view raw CAN data. One use for this is creating an
## Usage Instructions ## Usage Instructions
```bash
$ ./cabana -h
Usage: ./_cabana [options] route
Options:
-h, --help Displays this help.
--demo use a demo route instead of providing your own
--qcam load qcamera
--data_dir <data_dir> local directory with routes
Arguments:
route the drive to replay. find your drives at
connect.comma.ai
```
See [openpilot wiki](https://github.com/commaai/openpilot/wiki/Cabana) See [openpilot wiki](https://github.com/commaai/openpilot/wiki/Cabana)

@ -12,5 +12,6 @@ else:
qt_libs = ['qt_util', 'Qt5Charts'] + base_libs qt_libs = ['qt_util', 'Qt5Charts'] + base_libs
cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, opendbc,'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, opendbc,'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs
qt_env.Execute('./generate_dbc_json.py --out car_fingerprint_to_dbc.json')
qt_env.Program('_cabana', ['cabana.cc', 'mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', qt_env.Program('_cabana', ['cabana.cc', 'mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc',
'canmessages.cc', 'messageswidget.cc', 'detailwidget.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) 'canmessages.cc', 'messageswidget.cc', 'detailwidget.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)

@ -1,8 +1,11 @@
#include "tools/cabana/binaryview.h" #include "tools/cabana/binaryview.h"
#include <QApplication> #include <QApplication>
#include <QFontDatabase>
#include <QHeaderView> #include <QHeaderView>
#include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QToolTip>
#include "tools/cabana/canmessages.h" #include "tools/cabana/canmessages.h"
@ -18,6 +21,7 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
horizontalHeader()->hide(); horizontalHeader()->hide();
verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setMouseTracking(true);
// replace selection model // replace selection model
auto old_model = selectionModel(); auto old_model = selectionModel();
@ -29,6 +33,24 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
}); });
} }
void BinaryView::highlight(const Signal *sig) {
if (sig != hovered_sig) {
hovered_sig = sig;
model->dataChanged(model->index(0, 0), model->index(model->rowCount() - 1, model->columnCount() - 1));
emit signalHovered(hovered_sig);
}
}
void BinaryView::mouseMoveEvent(QMouseEvent *event) {
if (auto index = indexAt(event->pos()); index.isValid()) {
auto item = (BinaryViewModel::Item *)index.internalPointer();
highlight(item->sig);
if (item->sig)
QToolTip::showText(event->globalPos(), item->sig->name.c_str(), this, rect());
}
QTableView::mouseMoveEvent(event);
}
void BinaryView::mouseReleaseEvent(QMouseEvent *event) { void BinaryView::mouseReleaseEvent(QMouseEvent *event) {
QTableView::mouseReleaseEvent(event); QTableView::mouseReleaseEvent(event);
@ -39,6 +61,11 @@ void BinaryView::mouseReleaseEvent(QMouseEvent *event) {
} }
} }
void BinaryView::leaveEvent(QEvent *event) {
highlight(nullptr);
QTableView::leaveEvent(event);
}
void BinaryView::setMessage(const QString &message_id) { void BinaryView::setMessage(const QString &message_id) {
msg_id = message_id; msg_id = message_id;
model->setMessage(message_id); model->setMessage(message_id);
@ -79,7 +106,8 @@ void BinaryViewModel::setMessage(const QString &message_id) {
} else if (j == end) { } else if (j == end) {
sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true;
} }
items[idx].bg_color = QColor(getColor(i)); items[idx].bg_color = getColor(i);
items[idx].sig = &dbc_msg->sigs[i];
} }
} }
} }
@ -157,7 +185,8 @@ void BinarySelectionModel::select(const QItemSelection &selection, QItemSelectio
BinaryItemDelegate::BinaryItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { BinaryItemDelegate::BinaryItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {
// cache fonts and color // cache fonts and color
small_font.setPointSize(6); small_font.setPointSize(6);
bold_font.setBold(true); hex_font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
hex_font.setBold(true);
highlight_color = QApplication::style()->standardPalette().color(QPalette::Active, QPalette::Highlight); highlight_color = QApplication::style()->standardPalette().color(QPalette::Active, QPalette::Highlight);
} }
@ -168,11 +197,19 @@ QSize BinaryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo
void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto item = (const BinaryViewModel::Item *)index.internalPointer(); auto item = (const BinaryViewModel::Item *)index.internalPointer();
BinaryView *bin_view = (BinaryView *)parent();
painter->save(); painter->save();
// TODO: highlight signal cells on mouse over
painter->fillRect(option.rect, option.state & QStyle::State_Selected ? highlight_color : item->bg_color); // background
QColor bg_color = item->sig && bin_view->hoveredSignal() == item->sig ? hoverColor(item->bg_color) : item->bg_color;
if (option.state & QStyle::State_Selected) {
bg_color = highlight_color;
}
painter->fillRect(option.rect, bg_color);
// text
if (index.column() == 8) { if (index.column() == 8) {
painter->setFont(bold_font); painter->setFont(hex_font);
} }
painter->drawText(option.rect, Qt::AlignCenter, item->val); painter->drawText(option.rect, Qt::AlignCenter, item->val);
if (item->is_msb || item->is_lsb) { if (item->is_msb || item->is_lsb) {

@ -14,7 +14,7 @@ public:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private: private:
QFont small_font, bold_font; QFont small_font, hex_font;
QColor highlight_color; QColor highlight_color;
}; };
@ -37,6 +37,7 @@ public:
bool is_msb = false; bool is_msb = false;
bool is_lsb = false; bool is_lsb = false;
QString val = "0"; QString val = "0";
const Signal *sig = nullptr;
}; };
private: private:
@ -59,14 +60,21 @@ class BinaryView : public QTableView {
public: public:
BinaryView(QWidget *parent = nullptr); BinaryView(QWidget *parent = nullptr);
void mouseReleaseEvent(QMouseEvent *event) override;
void setMessage(const QString &message_id); void setMessage(const QString &message_id);
void updateState(); void updateState();
void highlight(const Signal *sig);
const Signal *hoveredSignal() const { return hovered_sig; }
signals: signals:
void cellsSelected(int start_bit, int size); void cellsSelected(int start_bit, int size);
void signalHovered(const Signal *sig);
private: private:
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
QString msg_id; QString msg_id;
BinaryViewModel *model; BinaryViewModel *model;
const Signal *hovered_sig = nullptr;
}; };

@ -3,6 +3,8 @@
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
#include "tools/cabana/dbcmanager.h"
Q_DECLARE_METATYPE(std::vector<CanData>); Q_DECLARE_METATYPE(std::vector<CanData>);
Settings settings; Settings settings;
@ -38,6 +40,33 @@ bool CANMessages::loadRoute(const QString &route, const QString &data_dir, bool
return false; return false;
} }
QList<QPointF> CANMessages::findSignalValues(const QString &id, const Signal *signal, double value, FindFlags flag, int max_count) {
auto evts = events();
if (!evts) return {};
auto l = id.split(':');
int bus = l[0].toInt();
uint32_t address = l[1].toUInt(nullptr, 16);
QList<QPointF> ret;
ret.reserve(max_count);
for (auto &evt : *evts) {
if (evt->which != cereal::Event::Which::CAN) continue;
for (auto c : evt->event.getCan()) {
if (bus == c.getSrc() && address == c.getAddress()) {
double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), *signal);
if ((flag == EQ && val == value) || (flag == LT && val < value) || (flag == GT && val > value)) {
ret.push_back({(evt->mono_time / (double)1e9) - can->routeStartTime(), val});
}
if (ret.size() >= max_count)
return ret;
}
}
}
return ret;
}
void CANMessages::process(QHash<QString, std::deque<CanData>> *messages) { void CANMessages::process(QHash<QString, std::deque<CanData>> *messages) {
for (auto it = messages->begin(); it != messages->end(); ++it) { for (auto it = messages->begin(); it != messages->end(); ++it) {
++counters[it.key()]; ++counters[it.key()];

@ -4,8 +4,11 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <QColor>
#include <QHash> #include <QHash>
#include <QList>
#include "opendbc/can/common_dbc.h"
#include "tools/replay/replay.h" #include "tools/replay/replay.h"
class Settings : public QObject { class Settings : public QObject {
@ -35,12 +38,14 @@ class CANMessages : public QObject {
Q_OBJECT Q_OBJECT
public: public:
enum FindFlags{ EQ, LT, GT };
CANMessages(QObject *parent); CANMessages(QObject *parent);
~CANMessages(); ~CANMessages();
bool loadRoute(const QString &route, const QString &data_dir, bool use_qcam); bool loadRoute(const QString &route, const QString &data_dir, bool use_qcam);
void seekTo(double ts); void seekTo(double ts);
void resetRange(); void resetRange();
void setRange(double min, double max); void setRange(double min, double max);
QList<QPointF> findSignalValues(const QString&id, const Signal* signal, double value, FindFlags flag, int max_count);
bool eventFilter(const Event *event); bool eventFilter(const Event *event);
inline std::pair<double, double> range() const { return {begin_sec, end_sec}; } inline std::pair<double, double> range() const { return {begin_sec, end_sec}; }
@ -99,6 +104,12 @@ inline const QString &getColor(int i) {
return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)];
} }
inline QColor hoverColor(const QColor &color) {
QColor c = color.convertTo(QColor::Hsv);
c.setHsv(color.hue(), 180, 180);
return c;
}
// A global pointer referring to the unique CANMessages object // A global pointer referring to the unique CANMessages object
extern CANMessages *can; extern CANMessages *can;
extern Settings settings; extern Settings settings;

@ -29,6 +29,17 @@ DetailWidget::DetailWidget(QWidget *parent) : QWidget(parent) {
title_layout->addWidget(edit_btn); title_layout->addWidget(edit_btn);
main_layout->addLayout(title_layout); main_layout->addLayout(title_layout);
// warning
warning_widget = new QWidget(this);
QHBoxLayout *warning_hlayout = new QHBoxLayout(warning_widget);
QLabel *warning_icon = new QLabel(this);
warning_icon->setPixmap(style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap({16, 16}));
warning_hlayout->addWidget(warning_icon);
warning_label = new QLabel(this);
warning_hlayout->addWidget(warning_label,1, Qt::AlignLeft);
warning_widget->hide();
main_layout->addWidget(warning_widget);
// binary view // binary view
binary_view = new BinaryView(this); binary_view = new BinaryView(this);
main_layout->addWidget(binary_view, 0, Qt::AlignTop); main_layout->addWidget(binary_view, 0, Qt::AlignTop);
@ -64,6 +75,7 @@ void DetailWidget::setMessage(const QString &message_id) {
void DetailWidget::dbcMsgChanged() { void DetailWidget::dbcMsgChanged() {
if (msg_id.isEmpty()) return; if (msg_id.isEmpty()) return;
warning_widget->hide();
qDeleteAll(signals_container->findChildren<SignalEdit *>()); qDeleteAll(signals_container->findChildren<SignalEdit *>());
QString msg_name = tr("untitled"); QString msg_name = tr("untitled");
if (auto msg = dbc()->msg(msg_id)) { if (auto msg = dbc()->msg(msg_id)) {
@ -74,8 +86,14 @@ void DetailWidget::dbcMsgChanged() {
QObject::connect(form, &SignalEdit::showFormClicked, this, &DetailWidget::showForm); QObject::connect(form, &SignalEdit::showFormClicked, this, &DetailWidget::showForm);
QObject::connect(form, &SignalEdit::remove, this, &DetailWidget::removeSignal); QObject::connect(form, &SignalEdit::remove, this, &DetailWidget::removeSignal);
QObject::connect(form, &SignalEdit::save, this, &DetailWidget::saveSignal); QObject::connect(form, &SignalEdit::save, this, &DetailWidget::saveSignal);
QObject::connect(form, &SignalEdit::highlight, binary_view, &BinaryView::highlight);
QObject::connect(binary_view, &BinaryView::signalHovered, form, &SignalEdit::signalHovered);
} }
msg_name = msg->name.c_str(); msg_name = msg->name.c_str();
if (msg->size != can->lastMessage(msg_id).dat.size()) {
warning_label->setText(tr("Message size (%1) is incorrect!").arg(msg->size));
warning_widget->show();
}
} }
edit_btn->setVisible(true); edit_btn->setVisible(true);
name_label->setText(msg_name); name_label->setText(msg_name);

@ -46,7 +46,8 @@ private:
void updateState(); void updateState();
QString msg_id; QString msg_id;
QLabel *name_label, *time_label; QLabel *name_label, *time_label, *warning_label;
QWidget *warning_widget;
QPushButton *edit_btn; QPushButton *edit_btn;
QWidget *signals_container; QWidget *signals_container;
HistoryLog *history_log; HistoryLog *history_log;

@ -0,0 +1,24 @@
#!/usr/bin/env python3
import argparse
import json
from selfdrive.car.car_helpers import get_interface_attr
def generate_dbc_json() -> str:
all_cars_by_brand = get_interface_attr("CAR_INFO")
all_dbcs_by_brand = get_interface_attr("DBC")
dbc_map = {car: all_dbcs_by_brand[brand][car]['pt'] for brand, cars in all_cars_by_brand.items() for car in cars if car != 'mock'}
return json.dumps(dict(sorted(dbc_map.items())), indent=2)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate mapping for all car fingerprints to DBC names and outputs json file",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--out", required=True, help="Generated json filepath")
args = parser.parse_args()
with open(args.out, 'w') as f:
f.write(generate_dbc_json())
print(f"Generated and written to {args.out}")

@ -70,7 +70,7 @@ HistoryLog::HistoryLog(QWidget *parent) : QWidget(parent) {
table = new QTableView(this); table = new QTableView(this);
table->setModel(model); table->setModel(model);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
table->setColumnWidth(0, 60); table->setColumnWidth(0, 60);
table->verticalHeader()->setVisible(false); table->verticalHeader()->setVisible(false);
table->setStyleSheet("QTableView::item { border:0px; padding-left:5px; padding-right:5px; }"); table->setStyleSheet("QTableView::item { border:0px; padding-left:5px; padding-right:5px; }");

@ -42,6 +42,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
// message filter // message filter
QLineEdit *filter = new QLineEdit(this); QLineEdit *filter = new QLineEdit(this);
filter->setClearButtonEnabled(true);
filter->setPlaceholderText(tr("filter messages")); filter->setPlaceholderText(tr("filter messages"));
main_layout->addWidget(filter); main_layout->addWidget(filter);
@ -67,9 +68,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
// signals/slots // signals/slots
QObject::connect(filter, &QLineEdit::textChanged, proxy_model, &QSortFilterProxyModel::setFilterFixedString); QObject::connect(filter, &QLineEdit::textChanged, proxy_model, &QSortFilterProxyModel::setFilterFixedString);
QObject::connect(can, &CANMessages::eventsMerged, this, &MessagesWidget::loadDBCFromFingerprint);
QObject::connect(can, &CANMessages::updated, model, &MessageListModel::updateState); QObject::connect(can, &CANMessages::updated, model, &MessageListModel::updateState);
QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(dbcSelectionChanged(const QString &))); QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &)));
QObject::connect(load_from_paste, &QPushButton::clicked, this, &MessagesWidget::loadFromPaste); QObject::connect(load_from_paste, &QPushButton::clicked, this, &MessagesWidget::loadDBCFromPaste);
QObject::connect(save_btn, &QPushButton::clicked, [=]() { QObject::connect(save_btn, &QPushButton::clicked, [=]() {
// TODO: save DBC to file // TODO: save DBC to file
}); });
@ -79,17 +81,20 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
} }
}); });
// For test purpose QFile json_file("./car_fingerprint_to_dbc.json");
dbc_combo->setCurrentText("toyota_nodsu_pt_generated"); if(json_file.open(QIODevice::ReadOnly)) {
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
}
} }
void MessagesWidget::dbcSelectionChanged(const QString &dbc_file) { void MessagesWidget::loadDBCFromName(const QString &name) {
dbc()->open(dbc_file); dbc()->open(name);
// TODO: reset model? dbc_combo->setCurrentText(name);
table_widget->sortByColumn(0, Qt::AscendingOrder); // refresh model
model->updateState();
} }
void MessagesWidget::loadFromPaste() { void MessagesWidget::loadDBCFromPaste() {
LoadDBCDialog dlg(this); LoadDBCDialog dlg(this);
if (dlg.exec()) { if (dlg.exec()) {
dbc()->open("from paste", dlg.dbc_edit->toPlainText()); dbc()->open("from paste", dlg.dbc_edit->toPlainText());
@ -97,6 +102,16 @@ void MessagesWidget::loadFromPaste() {
} }
} }
void MessagesWidget::loadDBCFromFingerprint() {
auto fingerprint = can->carFingerprint();
if (!fingerprint.isEmpty() && dbc()->name().isEmpty()) {
auto dbc_name = fingerprint_to_dbc[fingerprint];
if (dbc_name != QJsonValue::Undefined) {
loadDBCFromName(dbc_name.toString());
}
}
}
// MessageListModel // MessageListModel
QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const {

@ -3,6 +3,7 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QComboBox> #include <QComboBox>
#include <QDialog> #include <QDialog>
#include <QJsonDocument>
#include <QTableView> #include <QTableView>
#include <QTextEdit> #include <QTextEdit>
@ -38,8 +39,9 @@ public:
MessagesWidget(QWidget *parent); MessagesWidget(QWidget *parent);
public slots: public slots:
void dbcSelectionChanged(const QString &dbc_file); void loadDBCFromName(const QString &name);
void loadFromPaste(); void loadDBCFromFingerprint();
void loadDBCFromPaste();
signals: signals:
void msgSelectionChanged(const QString &message_id); void msgSelectionChanged(const QString &message_id);
@ -48,4 +50,5 @@ protected:
QTableView *table_widget; QTableView *table_widget;
QComboBox *dbc_combo; QComboBox *dbc_combo;
MessageListModel *model; MessageListModel *model;
QJsonDocument fingerprint_to_dbc;
}; };

@ -3,8 +3,12 @@
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFormLayout> #include <QFormLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QRadioButton>
#include <QScrollArea>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "selfdrive/ui/qt/util.h"
// SignalForm // SignalForm
SignalForm::SignalForm(const Signal &sig, QWidget *parent) : start_bit(sig.start_bit), QWidget(parent) { SignalForm::SignalForm(const Signal &sig, QWidget *parent) : start_bit(sig.start_bit), QWidget(parent) {
@ -78,7 +82,7 @@ Signal SignalForm::getSignal() {
// SignalEdit // SignalEdit
SignalEdit::SignalEdit(int index, const QString &msg_id, const Signal &sig, QWidget *parent) SignalEdit::SignalEdit(int index, const QString &msg_id, const Signal &sig, QWidget *parent)
: sig_name(sig.name.c_str()), QWidget(parent) { : sig(&sig), form_idx(index), sig_name(sig.name.c_str()), QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setContentsMargins(0, 0, 0, 0);
@ -93,6 +97,12 @@ SignalEdit::SignalEdit(int index, const QString &msg_id, const Signal &sig, QWid
title->setStyleSheet(QString("font-weight:bold; color:%1").arg(getColor(index))); title->setStyleSheet(QString("font-weight:bold; color:%1").arg(getColor(index)));
title_layout->addWidget(title, 1); title_layout->addWidget(title, 1);
QPushButton *seek_btn = new QPushButton("");
seek_btn->setStyleSheet("font-weight:bold;font-size:20px");
seek_btn->setToolTip(tr("Find signal values"));
seek_btn->setFixedSize(20, 20);
title_layout->addWidget(seek_btn);
QPushButton *plot_btn = new QPushButton("📈"); QPushButton *plot_btn = new QPushButton("📈");
plot_btn->setToolTip(tr("Show Plot")); plot_btn->setToolTip(tr("Show Plot"));
plot_btn->setFixedSize(20, 20); plot_btn->setFixedSize(20, 20);
@ -124,13 +134,17 @@ SignalEdit::SignalEdit(int index, const QString &msg_id, const Signal &sig, QWid
main_layout->addWidget(hline); main_layout->addWidget(hline);
QObject::connect(remove_btn, &QPushButton::clicked, this, &SignalEdit::remove); QObject::connect(remove_btn, &QPushButton::clicked, this, &SignalEdit::remove);
QObject::connect(title, &ElidedLabel::clicked, this, &SignalEdit::showFormClicked);
QObject::connect(save_btn, &QPushButton::clicked, [=]() { QObject::connect(save_btn, &QPushButton::clicked, [=]() {
QString new_name = form->getSignal().name.c_str(); QString new_name = form->getSignal().name.c_str();
title->setText(QString("%1. %2").arg(index + 1).arg(new_name)); title->setText(QString("%1. %2").arg(index + 1).arg(new_name));
emit save(); emit save();
sig_name = new_name; sig_name = new_name;
}); });
QObject::connect(title, &ElidedLabel::clicked, this, &SignalEdit::showFormClicked); QObject::connect(seek_btn, &QPushButton::clicked, [this, msg_id, s = &sig]() {
SignalFindDlg dlg(msg_id, s, this);
dlg.exec();
});
} }
void SignalEdit::setFormVisible(bool visible) { void SignalEdit::setFormVisible(bool visible) {
@ -138,6 +152,21 @@ void SignalEdit::setFormVisible(bool visible) {
icon->setText(visible ? "" : ">"); icon->setText(visible ? "" : ">");
} }
void SignalEdit::signalHovered(const Signal *s) {
auto color = sig == s ? hoverColor(getColor(form_idx)) : QColor(getColor(form_idx));
title->setStyleSheet(QString("font-weight:bold; color:%1").arg(color.name()));
}
void SignalEdit::enterEvent(QEvent *event) {
emit highlight(sig);
QWidget::enterEvent(event);
}
void SignalEdit::leaveEvent(QEvent *event) {
emit highlight(nullptr);
QWidget::leaveEvent(event);
}
// AddSignalDialog // AddSignalDialog
AddSignalDialog::AddSignalDialog(const QString &id, int start_bit, int size, QWidget *parent) : QDialog(parent) { AddSignalDialog::AddSignalDialog(const QString &id, int start_bit, int size, QWidget *parent) : QDialog(parent) {
@ -159,3 +188,66 @@ AddSignalDialog::AddSignalDialog(const QString &id, int start_bit, int size, QWi
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
} }
SignalFindDlg::SignalFindDlg(const QString &id, const Signal *signal, QWidget *parent) : QDialog(parent) {
setWindowTitle(tr("Find signal values"));
QVBoxLayout *main_layout = new QVBoxLayout(this);
QHBoxLayout *h = new QHBoxLayout();
h->addWidget(new QLabel(signal->name.c_str()));
QComboBox *comp_box = new QComboBox();
comp_box->addItems({">", "=", "<"});
h->addWidget(comp_box);
QLineEdit *value_edit = new QLineEdit("0", this);
value_edit->setValidator( new QDoubleValidator(-500000, 500000, 6, this) );
h->addWidget(value_edit, 1);
QPushButton *search_btn = new QPushButton(tr("Find"), this);
h->addWidget(search_btn);
main_layout->addLayout(h);
QWidget *container = new QWidget(this);
QVBoxLayout *signals_layout = new QVBoxLayout(container);
QScrollArea *scroll = new QScrollArea(this);
scroll->setWidget(container);
scroll->setWidgetResizable(true);
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
main_layout->addWidget(scroll);
QObject::connect(search_btn, &QPushButton::clicked, [=]() {
clearLayout(signals_layout);
CANMessages::FindFlags comp = CANMessages::EQ;
if (comp_box->currentIndex() == 0) {
comp = CANMessages::GT;
} else if (comp_box->currentIndex() == 2) {
comp = CANMessages::LT;
}
double value = value_edit->text().toDouble();
const int limit_results = 50;
auto values = can->findSignalValues(id, signal, value, comp, limit_results);
for (auto &v : values) {
QHBoxLayout *item_layout = new QHBoxLayout();
item_layout->addWidget(new QLabel(QString::number(v.x(), 'f', 2)));
item_layout->addWidget(new QLabel(QString::number(v.y())));
item_layout->addStretch(1);
QPushButton *goto_btn = new QPushButton(tr("Goto"), this);
QObject::connect(goto_btn, &QPushButton::clicked, [sec = v.x()]() { can->seekTo(sec); });
item_layout->addWidget(goto_btn);
signals_layout->addLayout(item_layout);
}
if (values.size() == limit_results) {
QFrame *hline = new QFrame();
hline->setFrameShape(QFrame::HLine);
hline->setFrameShadow(QFrame::Sunken);
signals_layout->addWidget(hline);
QLabel *info = new QLabel(tr("Only display the first %1 results").arg(limit_results));
info->setAlignment(Qt::AlignCenter);
signals_layout->addWidget(info);
}
if (values.size() * 30 > container->height()) {
scroll->setFixedHeight(std::min(values.size() * 30, 300));
}
});
}

@ -30,17 +30,24 @@ class SignalEdit : public QWidget {
public: public:
SignalEdit(int index, const QString &msg_id, const Signal &sig, QWidget *parent = nullptr); SignalEdit(int index, const QString &msg_id, const Signal &sig, QWidget *parent = nullptr);
void setFormVisible(bool show); void setFormVisible(bool show);
void signalHovered(const Signal *sig);
inline bool isFormVisible() const { return form_container->isVisible(); } inline bool isFormVisible() const { return form_container->isVisible(); }
QString sig_name; QString sig_name;
SignalForm *form; SignalForm *form;
int form_idx = 0;
const Signal *sig = nullptr;
signals: signals:
void highlight(const Signal *sig);
void showChart(); void showChart();
void showFormClicked(); void showFormClicked();
void remove(); void remove();
void save(); void save();
protected: protected:
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
ElidedLabel *title; ElidedLabel *title;
QWidget *form_container; QWidget *form_container;
QLabel *icon; QLabel *icon;
@ -51,3 +58,10 @@ public:
AddSignalDialog(const QString &id, int start_bit, int size, QWidget *parent); AddSignalDialog(const QString &id, int start_bit, int size, QWidget *parent);
SignalForm *form; SignalForm *form;
}; };
class SignalFindDlg : public QDialog {
Q_OBJECT
public:
SignalFindDlg(const QString &id, const Signal *signal, QWidget *parent);
};

Loading…
Cancel
Save