diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c13d2475ff..95d78ffde8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: exclude: '^(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)|(poetry.lock)' args: # if you've got a short variable name that's getting flagged, add it here - - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup + - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie - --builtins clear,rare,informal,usage,code,names,en-GB_to_en-US - repo: local hooks: diff --git a/.pylintrc b/.pylintrc index ae91dd3d7c..db61b8703d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -61,7 +61,7 @@ disable=C,R,W0613,W0511,W0212,W0201,W0106,W0603,W0621,W0703,W1201,W1203,E1136,W1 # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member +enable=c-extension-no-member,C0301 [REPORTS] @@ -245,7 +245,7 @@ indent-after-paren=4 indent-string=' ' # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=160 # Maximum number of lines in a module max-module-lines=1000 diff --git a/RELEASES.md b/RELEASES.md index b4dbc3ba60..e9cfd69f7c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ Version 0.9.5 (202X-XX-XX) ======================== - +* Lexus IS 2023 support thanks to L3R5! Version 0.9.4 (2023-07-27) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index 2cc4ce5b0b..d4d925e50e 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -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. Supported vehicles reference the US market unless otherwise specified. -# 256 Supported Cars +# 257 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -144,6 +144,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|IS 2023|All|openpilot|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 RJ45 cable (7 ft)
- 1 Toyota A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/opendbc b/opendbc index 7d61776e2b..3ef5cc8e79 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 7d61776e2b258a028b19d81852a20bc234ec6a37 +Subproject commit 3ef5cc8e794e826d2267afe9cc76cbae1501dc82 diff --git a/release/files_common b/release/files_common index b66a076b2b..90ee2ccd6f 100644 --- a/release/files_common +++ b/release/files_common @@ -60,6 +60,7 @@ release/* tools/__init__.py tools/lib/* +tools/bodyteleop/* tools/joystick/* tools/replay/*.cc tools/replay/*.h diff --git a/selfdrive/athena/tests/helpers.py b/selfdrive/athena/tests/helpers.py index 247aedd67a..8d4fd33198 100644 --- a/selfdrive/athena/tests/helpers.py +++ b/selfdrive/athena/tests/helpers.py @@ -52,7 +52,7 @@ class MockApi(): class MockParams(): default_params = { "DongleId": b"0000000000000000", - "GithubSshKeys": b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC307aE+nuHzTAgaJhzSf5v7ZZQW9gaperjhCmyPyl4PzY7T1mDGenTlVTN7yoVFZ9UfO9oMQqo0n1OwDIiqbIFxqnhrHU0cYfj88rI85m5BEKlNu5RdaVTj1tcbaPpQc5kZEolaI1nDDjzV0lwS7jo5VYDHseiJHlik3HH1SgtdtsuamGR2T80q1SyW+5rHoMOJG73IH2553NnWuikKiuikGHUYBd00K1ilVAK2xSiMWJp55tQfZ0ecr9QjEsJ+J/efL4HqGNXhffxvypCXvbUYAFSddOwXUPo5BTKevpxMtH+2YrkpSjocWA04VnTYFiPG6U4ItKmbLOTFZtPzoez private", # noqa: E501 + "GithubSshKeys": b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC307aE+nuHzTAgaJhzSf5v7ZZQW9gaperjhCmyPyl4PzY7T1mDGenTlVTN7yoVFZ9UfO9oMQqo0n1OwDIiqbIFxqnhrHU0cYfj88rI85m5BEKlNu5RdaVTj1tcbaPpQc5kZEolaI1nDDjzV0lwS7jo5VYDHseiJHlik3HH1SgtdtsuamGR2T80q1SyW+5rHoMOJG73IH2553NnWuikKiuikGHUYBd00K1ilVAK2xSiMWJp55tQfZ0ecr9QjEsJ+J/efL4HqGNXhffxvypCXvbUYAFSddOwXUPo5BTKevpxMtH+2YrkpSjocWA04VnTYFiPG6U4ItKmbLOTFZtPzoez private", # pylint: disable=C0301 "GithubUsername": b"commaci", "GsmMetered": True, "AthenadUploadQueue": '[]', diff --git a/selfdrive/athena/tests/test_athenad.py b/selfdrive/athena/tests/test_athenad.py index 8bd52d3772..7ce6932f2a 100755 --- a/selfdrive/athena/tests/test_athenad.py +++ b/selfdrive/athena/tests/test_athenad.py @@ -253,7 +253,8 @@ class TestAthenadMethods(unittest.TestCase): end_event.set() def test_cancelUpload(self): - item = athenad.UploadItem(path="qlog.bz2", url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='id', allow_cellular=True) + item = athenad.UploadItem(path="qlog.bz2", url="http://localhost:44444/qlog.bz2", headers={}, + created_at=int(time.time()*1000), id='id', allow_cellular=True) athenad.upload_queue.put_nowait(item) dispatcher["cancelUpload"](item.id) @@ -317,7 +318,8 @@ class TestAthenadMethods(unittest.TestCase): end_event.set() def test_listUploadQueue(self): - item = athenad.UploadItem(path="qlog.bz2", url="http://localhost:44444/qlog.bz2", headers={}, created_at=int(time.time()*1000), id='id', allow_cellular=True) + item = athenad.UploadItem(path="qlog.bz2", url="http://localhost:44444/qlog.bz2", headers={}, + created_at=int(time.time()*1000), id='id', allow_cellular=True) athenad.upload_queue.put_nowait(item) items = dispatcher["listUploadQueue"]() diff --git a/selfdrive/car/chrysler/carstate.py b/selfdrive/car/chrysler/carstate.py index fdc5aa338a..3afd45ce96 100644 --- a/selfdrive/car/chrysler/carstate.py +++ b/selfdrive/car/chrysler/carstate.py @@ -80,7 +80,8 @@ class CarState(CarStateBase): ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0 if self.CP.carFingerprint in RAM_CARS: - self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON'] # Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message + # Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message + self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON'] ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1 else: ret.steerFaultTemporary = cp.vl["EPS_2"]["LKAS_TEMPORARY_FAULT"] == 1 diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 28415da23e..266231d216 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -93,6 +93,7 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { # For 924, Trailhawk 2017 has length 3, whereas 2018 V6 has length 8. FINGERPRINTS = { + # pylint: disable=C0301 CAR.PACIFICA_2017_HYBRID: [{ 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 840: 8, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 3, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1892: 8, 2016: 8, 2024: 8 }], diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index ab2b0d7e0b..02075a5e11 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -30,7 +30,8 @@ def get_all_car_info() -> List[CarInfo]: footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): # If available, uses experimental longitudinal limits for the docs - CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True) + CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), + car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True) if CP.dashcamOnly or car_info is None: continue diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index 5b6e644b5d..da7b33d089 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -128,7 +128,8 @@ class Device(EnumBase): class Kit(EnumBase): - red_panda_kit = BasePart("CAN FD panda kit", parts=[Device.red_panda, Accessory.harness_box, Cable.usb_a_2_a_cable, Cable.usbc_otg_cable, Cable.obd_c_cable_1_5ft]) + red_panda_kit = BasePart("CAN FD panda kit", parts=[Device.red_panda, Accessory.harness_box, + Cable.usb_a_2_a_cable, Cable.usbc_otg_cable, Cable.obd_c_cable_1_5ft]) class Tool(EnumBase): @@ -173,10 +174,12 @@ CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only", "shop_fo class CommonFootnote(Enum): EXP_LONG_AVAIL = CarFootnote( - "Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. ", + "Experimental openpilot longitudinal control is available behind a toggle; " + + "the toggle is only available in non-release branches such as `devel` or `master-ci`. ", Column.LONGITUDINAL, docs_only=True) EXP_LONG_DSU = CarFootnote( - "By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace " + + "By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. " + + "If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace " + "stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).", Column.LONGITUDINAL) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index f439eab486..1b2424afaf 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -111,7 +111,8 @@ class CarController: # GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop)) - can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake, idx, CC.enabled, near_stop, at_full_stop, self.CP)) + can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake, + idx, CC.enabled, near_stop, at_full_stop, self.CP)) # Send dashboard UI commands (ACC status) send_fcw = hud_alert == VisualAlert.fcw @@ -162,7 +163,8 @@ class CarController: lka_icon_status = (lka_active, lka_critical) # SW_GMLAN not yet on cam harness, no HUD alerts - if self.CP.networkLocation != NetworkLocation.fwdCamera and (self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last): + if self.CP.networkLocation != NetworkLocation.fwdCamera and \ + (self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last): steer_alert = hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw) can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert)) self.lka_icon_status_last = lka_icon_status diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 4919a13288..8190cc9f52 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -142,6 +142,7 @@ class CanBus: DROPPED = 192 FINGERPRINTS = { + # pylint: disable=C0301 CAR.HOLDEN_ASTRA: [ # Astra BK MY17, ASCM unplugged { diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 35d6279902..196f233a3b 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -103,7 +103,8 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): else: checks.append(("CRUISE_PARAMS", 50)) - if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): + if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, + CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): signals.append(("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK")) elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): signals.append(("DRIVERS_DOOR_OPEN", "SCM_BUTTONS")) @@ -178,7 +179,8 @@ class CarState(CarStateBase): # panda checks if the signal is non-zero ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 # TODO: find a common signal across all cars - if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): + if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, + CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"]) diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 11eff61b33..29c6fa0327 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -3,7 +3,8 @@ from cereal import car from panda import Panda from common.conversions import Conversions as CV from common.numpy_fast import interp -from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS +from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \ + HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index ba7a42d2a1..b149fd2a02 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -123,7 +123,8 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC_BOSCH: [ - HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS), + HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", + footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS), HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS), ], CAR.CIVIC_BOSCH_DIESEL: None, # same platform diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 66bf303def..46f86fdc11 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -3,7 +3,8 @@ from cereal import car from panda import Panda from common.conversions import Conversions as CV from selfdrive.car.hyundai.hyundaicanfd import CanBus -from 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, Buttons +from 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, Buttons from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase @@ -132,7 +133,8 @@ class CarInterface(CarInterfaceBase): elif candidate == CAR.SANTA_CRUZ_1ST_GEN: ret.mass = 1870. + STD_CARGO_KG # weight from Limited trim - the only supported trim ret.wheelbase = 3.000 - ret.steerRatio = 14.2 # steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf + # steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf + ret.steerRatio = 14.2 # Kia elif candidate == CAR.KIA_SORENTO: @@ -211,7 +213,8 @@ class CarInterface(CarInterfaceBase): elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN: ret.mass = 2205 + STD_CARGO_KG ret.wheelbase = 2.9 - ret.steerRatio = 12.6 # https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1. + # https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1. + ret.steerRatio = 12.6 elif candidate == CAR.GENESIS_G70: ret.steerActuatorDelay = 0.1 ret.mass = 1640.0 + STD_CARGO_KG diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 508dc5886d..9d8c385591 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -154,9 +154,11 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), ], CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])), - CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])), + CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", + car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.HYUNDAI_GENESIS: [ - HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])), # TODO: check 2015 packages + # TODO: check 2015 packages + HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])), HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])), ], CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])), @@ -168,12 +170,15 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])), CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])), CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", car_parts=CarParts.common([CarHarness.hyundai_o])), - CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages + CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", + car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), - CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4", car_parts=CarParts.common([CarHarness.hyundai_l])), + CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4", + car_parts=CarParts.common([CarHarness.hyundai_l])), CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), - CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", + car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])), CAR.TUCSON: [ HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])), @@ -191,7 +196,8 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), ], CAR.IONIQ_6: [ - HyundaiCarInfo("Hyundai Ioniq 6 (without HDA II) 2023", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), # TODO: unknown + # TODO: unknown + HyundaiCarInfo("Hyundai Ioniq 6 (without HDA II) 2023", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), HyundaiCarInfo("Hyundai Ioniq 6 (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])), ], CAR.TUCSON_4TH_GEN: [ @@ -223,7 +229,8 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Niro Hybrid 2021-22", car_parts=CarParts.common([CarHarness.hyundai_f])), # TODO: 2021 could be hyundai_d, verify ], CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])), - CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018 + CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", + car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018 CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])), CAR.KIA_OPTIMA_H: [ HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control"), # TODO: may support adjacent years @@ -232,13 +239,15 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), CAR.KIA_SORENTO: [ - HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", + car_parts=CarParts.common([CarHarness.hyundai_c])), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), ], CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), - CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", + car_parts=CarParts.common([CarHarness.hyundai_c])), CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])), CAR.KIA_EV6: [ @@ -275,6 +284,7 @@ class Buttons: CANCEL = 4 # on newer models, this is a pause/resume button FINGERPRINTS = { + # pylint: disable=C0301 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 }], @@ -508,6 +518,7 @@ FW_QUERY_CONFIG = FwQueryConfig( ) FW_VERSIONS = { + # pylint: disable=C0301 CAR.HYUNDAI_GENESIS: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00DH LKAS 1.1 -150210', @@ -1892,7 +1903,8 @@ FW_VERSIONS = { } CHECKSUM = { - "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.KIA_K5_HEV_2020], + "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, + CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.KIA_K5_HEV_2020], "6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS], } @@ -1900,10 +1912,16 @@ CAN_GEARS = { # which message has the gear "use_cluster_gears": {CAR.ELANTRA, CAR.KONA}, "use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, - "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, + "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, + CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, + CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, + CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, } -CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN, CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN} +CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, + CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, + CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN, + CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN} # 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} @@ -1911,11 +1929,17 @@ CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, C # The camera does SCC on these cars, rather than the radar 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, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020, CAR.KIA_NIRO_HEV_2ND_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.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_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, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020, CAR.KIA_NIRO_HEV_2ND_GEN} + +EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022, + CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN} # 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_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER, +LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, + CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022} # If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points. diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index ec9f4a0f06..99df4cf4b4 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -115,7 +115,8 @@ class CarInterfaceBase(ABC): @staticmethod @abstractmethod - def _get_params(ret: car.CarParams, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool): + def _get_params(ret: car.CarParams, candidate: str, fingerprint: Dict[int, Dict[int, int]], + car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool): raise NotImplementedError @staticmethod diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py index 4e99d24903..2c628b102d 100644 --- a/selfdrive/car/nissan/carcontroller.py +++ b/selfdrive/car/nissan/carcontroller.py @@ -66,8 +66,8 @@ class CarController: # Below are the HUD messages. We copy the stock message and modify if self.CP.carFingerprint != CAR.ALTIMA: if self.frame % 2 == 0: - can_sends.append(nissancan.create_lkas_hud_msg( - self.packer, CS.lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)) + can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, + hud_control.leftLaneDepart, hud_control.rightLaneDepart)) if self.frame % 50 == 0: can_sends.append(nissancan.create_lkas_hud_info_msg( diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index d4e10e11ca..4c2caa2664 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -45,6 +45,7 @@ CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = { } FINGERPRINTS = { + # pylint: disable=C0301 CAR.XTRAIL: [ { 2: 5, 42: 6, 346: 6, 347: 5, 348: 8, 349: 7, 361: 8, 386: 8, 389: 8, 397: 8, 398: 8, 403: 8, 520: 2, 523: 6, 548: 8, 645: 8, 658: 8, 665: 8, 666: 8, 674: 2, 682: 8, 683: 8, 689: 8, 723: 8, 758: 3, 768: 2, 783: 3, 851: 8, 855: 8, 1041: 8, 1055: 2, 1104: 4, 1105: 6, 1107: 4, 1108: 8, 1111: 4, 1227: 8, 1228: 8, 1247: 4, 1266: 8, 1273: 7, 1342: 1, 1376: 6, 1401: 8, 1474: 2, 1497: 3, 1821: 8, 1823: 8, 1837: 8, 2015: 8, 2016: 8, 2024: 8 diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py index f79e5f0097..1bc537f6d4 100644 --- a/selfdrive/car/tesla/values.py +++ b/selfdrive/car/tesla/values.py @@ -22,6 +22,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { } FINGERPRINTS = { + # pylint: disable=C0301 CAR.AP1_MODELS: [ { 1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 267: 5, 277: 6, 280: 6, 283: 5, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 577: 8, 582: 5, 584: 4, 585: 8, 590: 8, 606: 8, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 660: 5, 693: 8, 696: 8, 697: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 809: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 841: 8, 845: 8, 846: 5, 852: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 873: 8, 877: 8, 878: 8, 879: 8, 880: 8, 884: 8, 888: 8, 889: 8, 893: 8, 896: 8, 901: 6, 904: 3, 905: 8, 908: 2, 909: 8, 920: 8, 921: 8, 925: 4, 936: 8, 937: 8, 941: 8, 949: 8, 952: 8, 953: 6, 957: 8, 968: 8, 973: 8, 984: 8, 987: 8, 989: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1016: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1064: 8, 1070: 8, 1080: 8, 1160: 4, 1281: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1368: 8, 1412: 8, 1436: 8, 1465: 8, 1476: 8, 1497: 8, 1524: 8, 1527: 8, 1601: 8, 1605: 8, 1611: 8, 1614: 8, 1617: 8, 1621: 8, 1627: 8, 1630: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5, 2045: 4 diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index c593277b00..803e110a3f 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -191,6 +191,7 @@ routes = [ CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH), CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA), CarTestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS), + CarTestRoute("649bf2997ada6e3a|2023-08-08--18-04-22", TOYOTA.LEXUS_IS_TSS2), CarTestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2), CarTestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI), CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR), diff --git a/selfdrive/car/tests/test_lateral_limits.py b/selfdrive/car/tests/test_lateral_limits.py index 7ccd5e3c97..9e9043d236 100755 --- a/selfdrive/car/tests/test_lateral_limits.py +++ b/selfdrive/car/tests/test_lateral_limits.py @@ -95,7 +95,8 @@ if __name__ == "__main__": _jerks["down_jerk"] > MAX_LAT_JERK_DOWN violation_str = " - VIOLATION" if violation else "" - print(f"{car_model:{max_car_model_len}} - up jerk: {round(_jerks['up_jerk'], 2):5} m/s^3, down jerk: {round(_jerks['down_jerk'], 2):5} m/s^3{violation_str}") + print(f"{car_model:{max_car_model_len}} - up jerk: {round(_jerks['up_jerk'], 2):5} \ + m/s^3, down jerk: {round(_jerks['down_jerk'], 2):5} m/s^3{violation_str}") # exit with test result sys.exit(not result.result.wasSuccessful()) diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index d56e96ca6e..fa4bb6a171 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -54,6 +54,7 @@ KIA NIRO EV 2ND GEN: [2.05, 2.5, 0.14] GENESIS GV80 2023: [2.5, 2.5, 0.1] KIA CARNIVAL 4TH GEN: [1.75, 1.75, 0.15] GMC ACADIA DENALI 2018: [1.6, 1.6, 0.2] +LEXUS IS 2023: [2.0, 2.0, 0.1] # Dashcam or fallback configured as ideal car mock: [10.0, 10, 0.0] diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index b6ecbe5e5f..a63ba2ae7c 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -132,7 +132,7 @@ class CarState(CarStateBase): if self.CP.carFingerprint in (TSS2_CAR | RADAR_ACC_CAR): if not (self.CP.flags & ToyotaFlags.SMART_DSU.value): self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"] - ret.stockFcw = bool(cp_acc.vl["ACC_HUD"]["FCW"]) + ret.stockFcw = bool(cp_acc.vl["PCS_HUD"]["FCW"]) # some TSS2 cars have low speed lockout permanently set, so ignore on those cars # these cars are identified by an ACC_TYPE value of 2. @@ -263,10 +263,10 @@ class CarState(CarStateBase): ("ACC_CONTROL", 33), ] signals += [ - ("FCW", "ACC_HUD"), + ("FCW", "PCS_HUD"), ] checks += [ - ("ACC_HUD", 1), + ("PCS_HUD", 1), ] if CP.carFingerprint not in (TSS2_CAR - RADAR_ACC_CAR) and not CP.enableDsu: @@ -302,12 +302,12 @@ class CarState(CarStateBase): ("PRECOLLISION_ACTIVE", "PRE_COLLISION"), ("FORCE", "PRE_COLLISION"), ("ACC_TYPE", "ACC_CONTROL"), - ("FCW", "ACC_HUD"), + ("FCW", "PCS_HUD"), ] checks += [ ("PRE_COLLISION", 33), ("ACC_CONTROL", 33), - ("ACC_HUD", 1), + ("PCS_HUD", 1), ] return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 75f61609db..e293de0cf9 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -159,7 +159,7 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.444 ret.mass = 4590. * CV.LB_TO_KG + STD_CARGO_KG - elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_RC): + elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC): ret.wheelbase = 2.79908 ret.steerRatio = 13.3 tire_stiffness_factor = 0.444 @@ -219,7 +219,8 @@ class CarInterface(CarInterfaceBase): # In TSS2 cars, the camera does long control found_ecus = [fw.ecu for fw in car_fw] - ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) and not (ret.flags & ToyotaFlags.SMART_DSU) + ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \ + and not (ret.flags & ToyotaFlags.SMART_DSU) ret.enableGasInterceptor = 0x201 in fingerprint[0] # if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar. diff --git a/selfdrive/car/toyota/toyotacan.py b/selfdrive/car/toyota/toyotacan.py index 01861c534a..0c3330efa2 100644 --- a/selfdrive/car/toyota/toyotacan.py +++ b/selfdrive/car/toyota/toyotacan.py @@ -63,7 +63,7 @@ def create_fcw_command(packer, fcw): "PCS_OFF": 1, "PCS_SENSITIVITY": 0, } - return packer.make_can_msg("ACC_HUD", 0, values) + return packer.make_can_msg("PCS_HUD", 0, values) def create_ui_command(packer, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart, enabled, stock_lkas_hud): diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index c2a4f81f70..ad6a2c639c 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -90,6 +90,7 @@ class CAR: LEXUS_ES_TSS2 = "LEXUS ES 2019" LEXUS_ESH_TSS2 = "LEXUS ES HYBRID 2019" LEXUS_IS = "LEXUS IS 2018" + LEXUS_IS_TSS2 = "LEXUS IS 2023" LEXUS_NX = "LEXUS NX 2018" LEXUS_NXH = "LEXUS NX HYBRID 2018" LEXUS_NX_TSS2 = "LEXUS NX 2020" @@ -183,6 +184,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"), CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-23", video_link="https://youtu.be/BZ29osRVJeg?t=12"), CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"), + CAR.LEXUS_IS_TSS2: ToyotaCarInfo("Lexus IS 2023"), CAR.LEXUS_NX: ToyotaCarInfo("Lexus NX 2018-19"), CAR.LEXUS_NXH: ToyotaCarInfo("Lexus NX Hybrid 2018-19"), CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"), @@ -204,23 +206,31 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { STATIC_DSU_MSGS = [ (0x128, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON), 1, 3, b'\xf4\x01\x90\x83\x00\x37'), (0x128, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH), 1, 3, b'\x03\x00\x20\x00\x00\x52'), - (0x141, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 2, b'\x00\x00\x00\x46'), - (0x160, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 7, b'\x00\x00\x08\x12\x01\x31\x9c\x51'), - (0x161, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON, CAR.LEXUS_RX, CAR.PRIUS_V, CAR.LEXUS_ES), 1, 7, b'\x00\x1e\x00\x00\x00\x80\x07'), + (0x141, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, + CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 2, b'\x00\x00\x00\x46'), + (0x160, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, + CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 7, b'\x00\x00\x08\x12\x01\x31\x9c\x51'), + (0x161, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON, CAR.LEXUS_RX, CAR.PRIUS_V, CAR.LEXUS_ES), + 1, 7, b'\x00\x1e\x00\x00\x00\x80\x07'), (0X161, (CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH), 1, 7, b'\x00\x1e\x00\xd4\x00\x00\x5b'), - (0x283, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 3, b'\x00\x00\x00\x00\x00\x00\x8c'), + (0x283, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, + CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 3, b'\x00\x00\x00\x00\x00\x00\x8c'), (0x2E6, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, b'\xff\xf8\x00\x08\x7f\xe0\x00\x4e'), (0x2E7, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, b'\xa8\x9c\x31\x9c\x00\x00\x00\x02'), (0x33E, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 20, b'\x0f\xff\x26\x40\x00\x1f\x00'), - (0x344, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 5, b'\x00\x00\x01\x00\x00\x00\x00\x50'), + (0x344, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, + CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 5, b'\x00\x00\x01\x00\x00\x00\x00\x50'), (0x365, (CAR.PRIUS, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.HIGHLANDERH), 0, 20, b'\x00\x00\x00\x80\x03\x00\x08'), - (0x365, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 20, b'\x00\x00\x00\x80\xfc\x00\x08'), + (0x365, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, + CAR.PRIUS_V), 0, 20, b'\x00\x00\x00\x80\xfc\x00\x08'), (0x366, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.HIGHLANDERH), 0, 20, b'\x00\x00\x4d\x82\x40\x02\x00'), - (0x366, (CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 20, b'\x00\x72\x07\xff\x09\xfe\x00'), + (0x366, (CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), + 0, 20, b'\x00\x72\x07\xff\x09\xfe\x00'), (0x366, (CAR.LEXUS_ES,), 0, 20, b'\x00\x95\x07\xfe\x08\x05\x00'), (0x470, (CAR.PRIUS, CAR.LEXUS_RXH), 1, 100, b'\x00\x00\x02\x7a'), (0x470, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.RAV4H, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.PRIUS_V), 1, 100, b'\x00\x00\x01\x79'), - (0x4CB, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 100, b'\x0c\x00\x00\x00\x00\x00\x00\x00'), + (0x4CB, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON, + CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 100, b'\x0c\x00\x00\x00\x00\x00\x00\x00'), ] # Some ECUs that use KWP2000 have their FW versions on non-standard data identifiers. @@ -1244,6 +1254,23 @@ FW_VERSIONS = { b'8646F5301400\x00\x00\x00\x00', ], }, + CAR.LEXUS_IS_TSS2: { + (Ecu.engine, 0x700, None): [ + b'\x018966353S1000\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7b0, None): [ + b'\x01F15265342000\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'8965B53450\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821F6201300\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F5303400\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', + ], + }, CAR.PRIUS: { (Ecu.engine, 0x700, None): [ b'\x02896634761000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', @@ -2261,6 +2288,7 @@ DBC = { CAR.LEXUS_ESH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), CAR.SIENNA: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), 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: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.RAV4H_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), @@ -2279,9 +2307,10 @@ DBC = { EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.PRIUS_V: 100}) # 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.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, - CAR.RAV4H_TSS2_2023, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, - CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2} +TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, + CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, + CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, CAR.LEXUS_IS_TSS2, CAR.MIRAI, CAR.LEXUS_NX_TSS2, + CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2} NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} @@ -2294,8 +2323,9 @@ RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2_2023, C # these cars use the Lane Tracing Assist (LTA) message for lateral control ANGLE_CONTROL_CAR = {CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023} -EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.CHRH_TSS2, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, - CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, +EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.CHRH_TSS2, CAR.COROLLAH_TSS2, + CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, + CAR.RAV4H_TSS2_2023, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, CAR.LEXUS_RXH_TSS2, CAR.LEXUS_NXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2} # no resume button press required diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 4db5606217..ddcf80f006 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -89,7 +89,8 @@ class CarController: if hud_control.leadVisible and self.frame * DT_CTRL > 1.0: # Don't display lead until we know the scaling factor lead_distance = 512 if CS.upscale_lead_car_signal else 8 acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive) - set_speed = hud_control.setSpeed * CV.MS_TO_KPH # FIXME: follow the recent displayed-speed updates, also use mph_kmh toggle to fix display rounding problem? + # FIXME: follow the recent displayed-speed updates, also use mph_kmh toggle to fix display rounding problem? + set_speed = hud_control.setSpeed * CV.MS_TO_KPH can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, CANBUS.pt, acc_hud_status, set_speed, lead_distance)) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index fe5bb36714..79ec310d58 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -572,7 +572,8 @@ class Controls: if self.CP.lateralTuning.which() == 'torque': torque_params = self.sm['liveTorqueParameters'] if self.sm.all_checks(['liveTorqueParameters']) and torque_params.useParams: - self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered, torque_params.frictionCoefficientFiltered) + self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered, + torque_params.frictionCoefficientFiltered) lat_plan = self.sm['lateralPlan'] long_plan = self.sm['longitudinalPlan'] diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 7cbcbc3d49..ab65da2e1d 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -194,7 +194,8 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates): return safe_desired_curvature, safe_desired_curvature_rate -def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float, torque_params: car.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float: +def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float, + torque_params: car.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float: friction_interp = interp( apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), [-friction_threshold, friction_threshold], diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index 3769be56d3..41c58a613c 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -423,7 +423,8 @@ class LongitudinalMpc: self.time_integrator = float(self.solver.get_stats('time_sim')[0]) # qp_iter = self.solver.get_stats('statistics')[-1][-1] # SQP_RTI specific - # print(f"long_mpc timings: tot {self.solve_time:.2e}, qp {self.time_qp_solution:.2e}, lin {self.time_linearization:.2e}, integrator {self.time_integrator:.2e}, qp_iter {qp_iter}") + # print(f"long_mpc timings: tot {self.solve_time:.2e}, qp {self.time_qp_solution:.2e}, lin {self.time_linearization:.2e}, \ + # integrator {self.time_integrator:.2e}, qp_iter {qp_iter}") # res = self.solver.get_residuals() # print(f"long_mpc residuals: {res[0]:.2e}, {res[1]:.2e}, {res[2]:.2e}, {res[3]:.2e}") # self.solver.print_statistics() @@ -446,7 +447,8 @@ class LongitudinalMpc: cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}") self.reset() # reset = 1 - # print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(sec_since_boot() - t0):.2e} qp {self.time_qp_solution:.2e}, lin {self.time_linearization:.2e} qp_iter {qp_iter}, reset {reset}") + # print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(sec_since_boot() - t0):.2e} qp {self.time_qp_solution:.2e}, \ + # lin {self.time_linearization:.2e} qp_iter {qp_iter}, reset {reset}") if __name__ == "__main__": diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index bba3ad7cff..cce866a093 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -163,7 +163,8 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa } -def get_lead(v_ego: float, ready: bool, tracks: Dict[int, Track], lead_msg: capnp._DynamicStructReader, model_v_ego: float, low_speed_override: bool = True) -> Dict[str, Any]: +def get_lead(v_ego: float, ready: bool, tracks: Dict[int, Track], lead_msg: capnp._DynamicStructReader, + model_v_ego: float, low_speed_override: bool = True) -> Dict[str, Any]: # Determine leads, this is where the essential logic happens if len(tracks) > 0 and ready and lead_msg.prob > .5: track = match_vision_to_track(v_ego, lead_msg, tracks) diff --git a/selfdrive/debug/print_docs_diff.py b/selfdrive/debug/print_docs_diff.py index 103dd52dd8..1cb6c3cdda 100755 --- a/selfdrive/debug/print_docs_diff.py +++ b/selfdrive/debug/print_docs_diff.py @@ -9,7 +9,8 @@ from selfdrive.car.docs_definitions import Column FOOTNOTE_TAG = "{}" STAR_ICON = '' -VIDEO_ICON = '' +VIDEO_ICON = '\ + ' COLUMNS = "|" + "|".join([column.value for column in Column]) + "|" COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3))) ARROW_SYMBOL = "➡️" @@ -99,7 +100,8 @@ def print_car_info_diff(path): if any(len(c) for c in changes.values()): markdown_builder = ["### ⚠️ This PR makes changes to [CARS.md](../blob/master/docs/CARS.md) ⚠️"] - for title, category in (("## 🔀 Column Changes", "column"), ("## ❌ Removed", "removals"), ("## ➕ Added", "additions"), ("## 📖 Detail Sentence Changes", "detail")): + for title, category in (("## 🔀 Column Changes", "column"), ("## ❌ Removed", "removals"), + ("## ➕ Added", "additions"), ("## 📖 Detail Sentence Changes", "detail")): if len(changes[category]): markdown_builder.append(title) if category not in ("detail",): diff --git a/selfdrive/debug/show_matching_cars.py b/selfdrive/debug/show_matching_cars.py index d5199b2a9e..539b6fc51c 100755 --- a/selfdrive/debug/show_matching_cars.py +++ b/selfdrive/debug/show_matching_cars.py @@ -4,7 +4,7 @@ import cereal.messaging as messaging # rav4 2019 and corolla tss2 -fingerprint = {896: 8, 898: 8, 900: 6, 976: 1, 1541: 8, 902: 6, 905: 8, 810: 2, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1552: 8, 1553: 8, 1556: 8, 1571: 8, 921: 8, 1056: 8, 544: 4, 1570: 8, 1059: 1, 36: 8, 37: 8, 550: 8, 935: 8, 552: 4, 170: 8, 812: 8, 944: 8, 945: 8, 562: 6, 180: 8, 1077: 8, 951: 8, 1592: 8, 1076: 8, 186: 4, 955: 8, 956: 8, 1001: 8, 705: 8, 452: 8, 1788: 8, 464: 8, 824: 8, 466: 8, 467: 8, 761: 8, 728: 8, 1572: 8, 1114: 8, 933: 8, 800: 8, 608: 8, 865: 8, 610: 8, 1595: 8, 934: 8, 998: 5, 1745: 8, 1000: 8, 764: 8, 1002: 8, 999: 7, 1789: 8, 1649: 8, 1779: 8, 1568: 8, 1017: 8, 1786: 8, 1787: 8, 1020: 8, 426: 6, 1279: 8} +fingerprint = {896: 8, 898: 8, 900: 6, 976: 1, 1541: 8, 902: 6, 905: 8, 810: 2, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1552: 8, 1553: 8, 1556: 8, 1571: 8, 921: 8, 1056: 8, 544: 4, 1570: 8, 1059: 1, 36: 8, 37: 8, 550: 8, 935: 8, 552: 4, 170: 8, 812: 8, 944: 8, 945: 8, 562: 6, 180: 8, 1077: 8, 951: 8, 1592: 8, 1076: 8, 186: 4, 955: 8, 956: 8, 1001: 8, 705: 8, 452: 8, 1788: 8, 464: 8, 824: 8, 466: 8, 467: 8, 761: 8, 728: 8, 1572: 8, 1114: 8, 933: 8, 800: 8, 608: 8, 865: 8, 610: 8, 1595: 8, 934: 8, 998: 5, 1745: 8, 1000: 8, 764: 8, 1002: 8, 999: 7, 1789: 8, 1649: 8, 1779: 8, 1568: 8, 1017: 8, 1786: 8, 1787: 8, 1020: 8, 426: 6, 1279: 8} # pylint: disable=C0301 candidate_cars = all_legacy_fingerprint_cars() diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py index 0bb9aa5e54..51cd426315 100755 --- a/selfdrive/debug/test_fw_query_on_routes.py +++ b/selfdrive/debug/test_fw_query_on_routes.py @@ -18,9 +18,9 @@ SUPPORTED_CARS = [brand for brand in SUPPORTED_BRANDS for brand in interface_nam UNKNOWN_BRAND = "unknown" try: - from xx.pipeline.c.CarState import migration + from xx.pipeline.lib.fingerprint import MIGRATION except ImportError: - migration = {} + MIGRATION = {} if __name__ == "__main__": parser = argparse.ArgumentParser(description='Run FW fingerprint on Qlog of route or list of routes') @@ -78,7 +78,7 @@ if __name__ == "__main__": break live_fingerprint = CP.carFingerprint - live_fingerprint = migration.get(live_fingerprint, live_fingerprint) + live_fingerprint = MIGRATION.get(live_fingerprint, live_fingerprint) if args.car is not None: live_fingerprint = args.car @@ -112,7 +112,8 @@ if __name__ == "__main__": padding = max([len(fw.brand or UNKNOWN_BRAND) for fw in car_fw]) for version in sorted(car_fw, key=lambda fw: fw.brand): subaddr = None if version.subAddress == 0 else hex(version.subAddress) - print(f" Brand: {version.brand or UNKNOWN_BRAND:{padding}}, bus: {version.bus} - (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}],") + print(f" Brand: {version.brand or UNKNOWN_BRAND:{padding}}, bus: {version.bus} - \ + (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}],") print("Mismatches") found = False diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index e990a46f12..7f8fd03b98 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -211,7 +211,8 @@ class Calibrator: new_height = HEIGHT_INIT self.rpys[self.block_idx] = moving_avg_with_linear_decay(self.rpys[self.block_idx], new_rpy, self.idx, float(BLOCK_SIZE)) - self.wide_from_device_eulers[self.block_idx] = moving_avg_with_linear_decay(self.wide_from_device_eulers[self.block_idx], new_wide_from_device_euler, self.idx, float(BLOCK_SIZE)) + self.wide_from_device_eulers[self.block_idx] = moving_avg_with_linear_decay(self.wide_from_device_eulers[self.block_idx], + new_wide_from_device_euler, self.idx, float(BLOCK_SIZE)) self.heights[self.block_idx] = moving_avg_with_linear_decay(self.heights[self.block_idx], new_height, self.idx, float(BLOCK_SIZE)) self.idx = (self.idx + 1) % BLOCK_SIZE diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index 5d8641a91b..cbc1822e50 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -81,7 +81,8 @@ class Laikad: 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=DOWNLOADS_CACHE_FOLDER) + self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, + clear_old_ephemeris=True, cache_dir=DOWNLOADS_CACHE_FOLDER) self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True, erratic_clock=use_qcom) self.auto_fetch_navs = auto_fetch_navs diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py index 3faf4f8d4e..a5abf8514a 100755 --- a/selfdrive/locationd/models/car_kf.py +++ b/selfdrive/locationd/models/car_kf.py @@ -171,7 +171,8 @@ class CarKalman(KalmanFilter): if P_initial is not None: self.P_initial = P_initial # init filter - self.filter = EKF_sym_pyx(generated_dir, self.name, self.Q, self.initial_x, self.P_initial, dim_state, dim_state_err, global_vars=self.global_vars, logger=cloudlog) + self.filter = EKF_sym_pyx(generated_dir, self.name, self.Q, self.initial_x, self.P_initial, + dim_state, dim_state_err, global_vars=self.global_vars, logger=cloudlog) if __name__ == "__main__": diff --git a/selfdrive/locationd/models/loc_kf.py b/selfdrive/locationd/models/loc_kf.py index 1486023a0b..697f9b2364 100755 --- a/selfdrive/locationd/models/loc_kf.py +++ b/selfdrive/locationd/models/loc_kf.py @@ -33,7 +33,8 @@ class States(): ACCELEROMETER_BIAS = slice(30, 33) # bias of mems accelerometer # TODO the offset is likely a translation of the sensor, not a rotation of the camera WIDE_FROM_DEVICE_EULER = slice(33, 36) # wide camera offset angles in radians (tici only) - # We currently do not use ACCELEROMETER_SCALE to avoid instability due to too many free variables (ACCELEROMETER_SCALE, ACCELEROMETER_BIAS, IMU_FROM_DEVICE_EULER). + # We currently do not use ACCELEROMETER_SCALE to avoid instability due to too many free variables + # (ACCELEROMETER_SCALE, ACCELEROMETER_BIAS, IMU_FROM_DEVICE_EULER). # From experiments we see that ACCELEROMETER_BIAS is more correct than ACCELEROMETER_SCALE # Error-state has different slices because it is an ESKF @@ -324,7 +325,8 @@ class LocKalman(): obs_eqs.append([h_track_sym, ObservationKind.ORB_FEATURES, track_epos_sym]) obs_eqs.append([h_track_wide_cam_sym, ObservationKind.ORB_FEATURES_WIDE, track_epos_sym]) obs_eqs.append([h_track_sym, ObservationKind.FEATURE_TRACK_TEST, track_epos_sym]) - msckf_params = [dim_main, dim_augment, dim_main_err, dim_augment_err, N, [ObservationKind.MSCKF_TEST, ObservationKind.ORB_FEATURES, ObservationKind.ORB_FEATURES_WIDE]] + msckf_params = [dim_main, dim_augment, dim_main_err, dim_augment_err, N, + [ObservationKind.MSCKF_TEST, ObservationKind.ORB_FEATURES, ObservationKind.ORB_FEATURES_WIDE]] else: msckf_params = None gen_code(generated_dir, name, f_sym, dt, state_sym, obs_eqs, dim_state, dim_state_err, eskf_params, msckf_params, maha_test_kinds) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 1826ea4563..de1265f0d7 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -198,8 +198,10 @@ def main(sm=None, pm=None): learner = ParamsLearner(CP, CP.steerRatio, 1.0, 0.0) x = learner.kf.x - angle_offset_average = clip(math.degrees(x[States.ANGLE_OFFSET]), angle_offset_average - MAX_ANGLE_OFFSET_DELTA, angle_offset_average + MAX_ANGLE_OFFSET_DELTA) - angle_offset = clip(math.degrees(x[States.ANGLE_OFFSET] + x[States.ANGLE_OFFSET_FAST]), angle_offset - MAX_ANGLE_OFFSET_DELTA, angle_offset + MAX_ANGLE_OFFSET_DELTA) + angle_offset_average = clip(math.degrees(x[States.ANGLE_OFFSET]), + angle_offset_average - MAX_ANGLE_OFFSET_DELTA, angle_offset_average + MAX_ANGLE_OFFSET_DELTA) + angle_offset = clip(math.degrees(x[States.ANGLE_OFFSET] + x[States.ANGLE_OFFSET_FAST]), + angle_offset - MAX_ANGLE_OFFSET_DELTA, angle_offset + MAX_ANGLE_OFFSET_DELTA) roll = clip(float(x[States.ROAD_ROLL]), roll - ROLL_MAX_DELTA, roll + ROLL_MAX_DELTA) roll_std = float(P[States.ROAD_ROLL]) # Account for the opposite signs of the yaw rates diff --git a/selfdrive/locationd/test/test_calibrationd.py b/selfdrive/locationd/test/test_calibrationd.py index 96f996413d..e3e8c3ca7c 100755 --- a/selfdrive/locationd/test/test_calibrationd.py +++ b/selfdrive/locationd/test/test_calibrationd.py @@ -7,7 +7,8 @@ import numpy as np import cereal.messaging as messaging from cereal import log from common.params import Params -from selfdrive.locationd.calibrationd import Calibrator, INPUTS_NEEDED, INPUTS_WANTED, BLOCK_SIZE, MIN_SPEED_FILTER, MAX_YAW_RATE_FILTER, SMOOTH_CYCLES, HEIGHT_INIT +from selfdrive.locationd.calibrationd import Calibrator, INPUTS_NEEDED, INPUTS_WANTED, BLOCK_SIZE, MIN_SPEED_FILTER, \ + MAX_YAW_RATE_FILTER, SMOOTH_CYCLES, HEIGHT_INIT class TestCalibrationd(unittest.TestCase): diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index f151a51d10..2fd786875c 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -72,7 +72,7 @@ procs = [ # debug procs NativeProcess("bridge", "cereal/messaging", ["./bridge"], onroad=False, callback=notcar), - PythonProcess("webjoystick", "tools.joystick.web", onroad=False, callback=notcar), + PythonProcess("webjoystick", "tools.bodyteleop.web", onroad=False, callback=notcar), ] managed_processes = {p.name: p for p in procs} diff --git a/selfdrive/monitoring/driver_monitor.py b/selfdrive/monitoring/driver_monitor.py index a2cddc2462..144920b354 100644 --- a/selfdrive/monitoring/driver_monitor.py +++ b/selfdrive/monitoring/driver_monitor.py @@ -212,7 +212,8 @@ class DriverStatus(): distracted_types.append(DistractedType.DISTRACTED_BLINK) if self.ee1_calibrated: - ee1_dist = self.eev1 > max(min(self.ee1_offseter.filtered_stat.M, self.settings._EE_MAX_OFFSET1), self.settings._EE_MIN_OFFSET1) * self.settings._EE_THRESH12 + ee1_dist = self.eev1 > max(min(self.ee1_offseter.filtered_stat.M, self.settings._EE_MAX_OFFSET1), self.settings._EE_MIN_OFFSET1) \ + * self.settings._EE_THRESH12 else: ee1_dist = self.eev1 > self.settings._EE_THRESH11 # if self.ee2_calibrated: @@ -263,14 +264,17 @@ class DriverStatus(): self.pose.yaw_std = driver_data.faceOrientationStd[1] model_std_max = max(self.pose.pitch_std, self.pose.yaw_std) self.pose.low_std = model_std_max < self.settings._POSESTD_THRESHOLD - self.blink.left_blink = driver_data.leftBlinkProb * (driver_data.leftEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) - self.blink.right_blink = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) + self.blink.left_blink = driver_data.leftBlinkProb * (driver_data.leftEyeProb > self.settings._EYE_THRESHOLD) \ + * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) + self.blink.right_blink = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \ + * (driver_data.sunglassesProb < self.settings._SG_THRESHOLD) self.eev1 = driver_data.notReadyProb[0] self.eev2 = driver_data.readyProb[0] self.distracted_types = self._get_distracted_types() - self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types or DistractedType.DISTRACTED_BLINK in self.distracted_types) and \ - driver_data.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std + self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types + or DistractedType.DISTRACTED_BLINK in self.distracted_types) \ + and driver_data.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std self.driver_distraction_filter.update(self.driver_distracted) # update offseter @@ -306,7 +310,8 @@ class DriverStatus(): self._reset_awareness() return # only restore awareness when paying attention and alert is not red - self.awareness = min(self.awareness + ((self.settings._RECOVERY_FACTOR_MAX-self.settings._RECOVERY_FACTOR_MIN)*(1.-self.awareness)+self.settings._RECOVERY_FACTOR_MIN)*self.step_change, 1.) + self.awareness = min(self.awareness + ((self.settings._RECOVERY_FACTOR_MAX-self.settings._RECOVERY_FACTOR_MIN)* + (1.-self.awareness)+self.settings._RECOVERY_FACTOR_MIN)*self.step_change, 1.) if self.awareness == 1.: self.awareness_passive = min(self.awareness_passive + self.step_change, 1.) # don't display alert banner when awareness is recovering and has cleared orange diff --git a/selfdrive/monitoring/test_monitoring.py b/selfdrive/monitoring/test_monitoring.py index f72b4a3aaa..497630f2ee 100755 --- a/selfdrive/monitoring/test_monitoring.py +++ b/selfdrive/monitoring/test_monitoring.py @@ -82,7 +82,8 @@ class TestMonitoring(unittest.TestCase): events, d_status = self._run_seq(always_distracted, always_false, always_true, always_false) self.assertEqual(len(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]), 0) self.assertEqual(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL + - ((d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.preDriverDistracted) + ((d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], + EventName.preDriverDistracted) self.assertEqual(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL + ((d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.promptDriverDistracted) self.assertEqual(events[int((d_status.settings._DISTRACTED_TIME + @@ -94,7 +95,8 @@ class TestMonitoring(unittest.TestCase): events, d_status = self._run_seq(always_no_face, always_false, always_true, always_false) self.assertTrue(len(events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]) == 0) self.assertEqual(events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL + - ((d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.preDriverUnresponsive) + ((d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], + EventName.preDriverUnresponsive) self.assertEqual(events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL + ((d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0], EventName.promptDriverUnresponsive) self.assertEqual(events[int((d_status.settings._AWARENESS_TIME + @@ -125,10 +127,14 @@ class TestMonitoring(unittest.TestCase): ds_vector = always_distracted[:] interaction_vector = always_false[:] op_vector = always_true[:] - ds_vector[int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON):int((DISTRACTED_SECONDS_TO_ORANGE+_invisible_time)/DT_DMON)] = [msg_NO_FACE_DETECTED] * int(_invisible_time/DT_DMON) - ds_vector[int((DISTRACTED_SECONDS_TO_RED+_invisible_time)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time)/DT_DMON)] = [msg_NO_FACE_DETECTED] * int(_invisible_time/DT_DMON) - interaction_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+0.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)] = [True] * int(1/DT_DMON) - op_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+2.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3)/DT_DMON)] = [False] * int(0.5/DT_DMON) + ds_vector[int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON):int((DISTRACTED_SECONDS_TO_ORANGE+_invisible_time)/DT_DMON)] \ + = [msg_NO_FACE_DETECTED] * int(_invisible_time/DT_DMON) + ds_vector[int((DISTRACTED_SECONDS_TO_RED+_invisible_time)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time)/DT_DMON)] \ + = [msg_NO_FACE_DETECTED] * int(_invisible_time/DT_DMON) + interaction_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+0.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)] \ + = [True] * int(1/DT_DMON) + op_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+2.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3)/DT_DMON)] \ + = [False] * int(0.5/DT_DMON) events, _ = self._run_seq(ds_vector, interaction_vector, op_vector, always_false) self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE+0.5*_invisible_time)/DT_DMON)].names[0], EventName.promptDriverDistracted) self.assertEqual(events[int((DISTRACTED_SECONDS_TO_RED+1.5*_invisible_time)/DT_DMON)].names[0], EventName.driverDistracted) @@ -141,7 +147,8 @@ class TestMonitoring(unittest.TestCase): _visible_time = np.random.choice([0.5, 10]) ds_vector = always_no_face[:]*2 interaction_vector = always_false[:]*2 - ds_vector[int((2*INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON):int((2*INVISIBLE_SECONDS_TO_ORANGE+1+_visible_time)/DT_DMON)] = [msg_ATTENTIVE] * int(_visible_time/DT_DMON) + ds_vector[int((2*INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON):int((2*INVISIBLE_SECONDS_TO_ORANGE+1+_visible_time)/DT_DMON)] = \ + [msg_ATTENTIVE] * int(_visible_time/DT_DMON) interaction_vector[int((INVISIBLE_SECONDS_TO_ORANGE)/DT_DMON):int((INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON)] = [True] * int(1/DT_DMON) events, _ = self._run_seq(ds_vector, interaction_vector, 2*always_true, 2*always_false) self.assertTrue(len(events[int(INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0) @@ -185,7 +192,8 @@ class TestMonitoring(unittest.TestCase): standstill_vector = always_true[:] standstill_vector[int(_redlight_time/DT_DMON):] = [False] * int((TEST_TIMESPAN-_redlight_time)/DT_DMON) events, d_status = self._run_seq(always_distracted, always_false, always_true, standstill_vector) - self.assertEqual(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL+1)/DT_DMON)].names[0], EventName.preDriverDistracted) + self.assertEqual(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL+1)/DT_DMON)].names[0], + EventName.preDriverDistracted) self.assertEqual(events[int((_redlight_time-0.1)/DT_DMON)].names[0], EventName.preDriverDistracted) self.assertEqual(events[int((_redlight_time+0.5)/DT_DMON)].names[0], EventName.promptDriverDistracted) @@ -195,9 +203,12 @@ class TestMonitoring(unittest.TestCase): ds_vector = [msg_DISTRACTED_BUT_SOMEHOW_UNCERTAIN] * int(TEST_TIMESPAN/DT_DMON) interaction_vector = always_false[:] events, d_status = self._run_seq(ds_vector, interaction_vector, always_true, always_false) - self.assertTrue(EventName.preDriverUnresponsive in events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME-0.1)/DT_DMON)].names) - self.assertTrue(EventName.promptDriverUnresponsive in events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names) - self.assertTrue(EventName.driverUnresponsive in events[int((INVISIBLE_SECONDS_TO_RED-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names) + self.assertTrue(EventName.preDriverUnresponsive in + events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME-0.1)/DT_DMON)].names) + self.assertTrue(EventName.promptDriverUnresponsive in + events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names) + self.assertTrue(EventName.driverUnresponsive in + events[int((INVISIBLE_SECONDS_TO_RED-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names) if __name__ == "__main__": diff --git a/selfdrive/navd/map_renderer.py b/selfdrive/navd/map_renderer.py index aa5682169f..520fa31147 100755 --- a/selfdrive/navd/map_renderer.py +++ b/selfdrive/navd/map_renderer.py @@ -74,7 +74,7 @@ if __name__ == "__main__": renderer = lib.map_renderer_init(ffi.NULL, ffi.NULL) wait_ready(lib, renderer) - geometry = r"{yxk}@|obn~Eg@@eCFqc@J{RFw@?kA@gA?q|@Riu@NuJBgi@ZqVNcRBaPBkG@iSD{I@_H@cH?gG@mG@gG?aD@{LDgDDkVVyQLiGDgX@q_@@qI@qKhS{R~[}NtYaDbGoIvLwNfP_b@|f@oFnF_JxHel@bf@{JlIuxAlpAkNnLmZrWqFhFoh@jd@kX|TkJxH_RnPy^|[uKtHoZ~Um`DlkCorC``CuShQogCtwB_ThQcr@fk@sVrWgRhVmSb\\oj@jxA{Qvg@u]tbAyHzSos@xjBeKbWszAbgEc~@~jCuTrl@cYfo@mRn\\_m@v}@ij@jp@om@lk@y|A`pAiXbVmWzUod@xj@wNlTw}@|uAwSn\\kRfYqOdS_IdJuK`KmKvJoOhLuLbHaMzGwO~GoOzFiSrEsOhD}PhCqw@vJmnAxSczA`Vyb@bHk[fFgl@pJeoDdl@}}@zIyr@hG}X`BmUdBcM^aRR}Oe@iZc@mR_@{FScHxAn_@vz@zCzH~GjPxAhDlB~DhEdJlIbMhFfG|F~GlHrGjNjItLnGvQ~EhLnBfOn@p`@AzAAvn@CfC?fc@`@lUrArStCfSxEtSzGxM|ElFlBrOzJlEbDnC~BfDtCnHjHlLvMdTnZzHpObOf^pKla@~G|a@dErg@rCbj@zArYlj@ttJ~AfZh@r]LzYg@`TkDbj@gIdv@oE|i@kKzhA{CdNsEfOiGlPsEvMiDpLgBpHyB`MkB|MmArPg@|N?|P^rUvFz~AWpOCdAkB|PuB`KeFfHkCfGy@tAqC~AsBPkDs@uAiAcJwMe@s@eKkPMoXQux@EuuCoH?eI?Kas@}Dy@wAUkMOgDL" + geometry = r"{yxk}@|obn~Eg@@eCFqc@J{RFw@?kA@gA?q|@Riu@NuJBgi@ZqVNcRBaPBkG@iSD{I@_H@cH?gG@mG@gG?aD@{LDgDDkVVyQLiGDgX@q_@@qI@qKhS{R~[}NtYaDbGoIvLwNfP_b@|f@oFnF_JxHel@bf@{JlIuxAlpAkNnLmZrWqFhFoh@jd@kX|TkJxH_RnPy^|[uKtHoZ~Um`DlkCorC``CuShQogCtwB_ThQcr@fk@sVrWgRhVmSb\\oj@jxA{Qvg@u]tbAyHzSos@xjBeKbWszAbgEc~@~jCuTrl@cYfo@mRn\\_m@v}@ij@jp@om@lk@y|A`pAiXbVmWzUod@xj@wNlTw}@|uAwSn\\kRfYqOdS_IdJuK`KmKvJoOhLuLbHaMzGwO~GoOzFiSrEsOhD}PhCqw@vJmnAxSczA`Vyb@bHk[fFgl@pJeoDdl@}}@zIyr@hG}X`BmUdBcM^aRR}Oe@iZc@mR_@{FScHxAn_@vz@zCzH~GjPxAhDlB~DhEdJlIbMhFfG|F~GlHrGjNjItLnGvQ~EhLnBfOn@p`@AzAAvn@CfC?fc@`@lUrArStCfSxEtSzGxM|ElFlBrOzJlEbDnC~BfDtCnHjHlLvMdTnZzHpObOf^pKla@~G|a@dErg@rCbj@zArYlj@ttJ~AfZh@r]LzYg@`TkDbj@gIdv@oE|i@kKzhA{CdNsEfOiGlPsEvMiDpLgBpHyB`MkB|MmArPg@|N?|P^rUvFz~AWpOCdAkB|PuB`KeFfHkCfGy@tAqC~AsBPkDs@uAiAcJwMe@s@eKkPMoXQux@EuuCoH?eI?Kas@}Dy@wAUkMOgDL" # pylint: disable=C0301 lib.map_renderer_update_route(renderer, geometry.encode()) POSITIONS = [ diff --git a/selfdrive/test/openpilotci.py b/selfdrive/test/openpilotci.py index 5f4b9f1f35..3a7868aaa6 100755 --- a/selfdrive/test/openpilotci.py +++ b/selfdrive/test/openpilotci.py @@ -17,8 +17,9 @@ def get_sas_token(): sas_token = open(TOKEN_PATH).read().strip() if sas_token is None: - sas_token = subprocess.check_output("az storage container generate-sas --account-name commadataci --name openpilotci --https-only --permissions lrw \ - --expiry $(date -u '+%Y-%m-%dT%H:%M:%SZ' -d '+1 hour') --auth-mode login --as-user --output tsv", shell=True).decode().strip("\n") + sas_token = subprocess.check_output("az storage container generate-sas --account-name commadataci --name openpilotci \ + --https-only --permissions lrw --expiry $(date -u '+%Y-%m-%dT%H:%M:%SZ' -d '+1 hour') \ + --auth-mode login --as-user --output tsv", shell=True).decode().strip("\n") return sas_token diff --git a/selfdrive/test/process_replay/__init__.py b/selfdrive/test/process_replay/__init__.py index a9dbc71830..a872c310d7 100644 --- a/selfdrive/test/process_replay/__init__.py +++ b/selfdrive/test/process_replay/__init__.py @@ -1 +1,2 @@ -from selfdrive.test.process_replay.process_replay import CONFIGS, get_process_config, get_custom_params_from_lr, replay_process, replay_process_with_name # noqa: F401 +from selfdrive.test.process_replay.process_replay import CONFIGS, get_process_config, get_custom_params_from_lr, \ + replay_process, replay_process_with_name # noqa: F401 diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index ddcc0b2fe8..19beb06d6d 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -323,7 +323,8 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): canmsgs = [msg for msg in msgs if msg.which() == "can"] has_cached_cp = params.get("CarParamsCache") is not None assert len(canmsgs) != 0, "CAN messages are required for fingerprinting" - assert os.environ.get("SKIP_FW_QUERY", False) or has_cached_cp, "CarParamsCache is required for fingerprinting. Make sure to keep carParams msgs in the logs." + assert os.environ.get("SKIP_FW_QUERY", False) or has_cached_cp, \ + "CarParamsCache is required for fingerprinting. Make sure to keep carParams msgs in the logs." for m in canmsgs[:300]: can.send(m.as_builder().to_bytes()) @@ -606,7 +607,8 @@ def get_custom_params_from_lr(lr: Union[LogReader, List[capnp._DynamicStructRead return custom_params -def replay_process_with_name(name: Union[str, Iterable[str]], lr: Union[LogReader, List[capnp._DynamicStructReader]], *args, **kwargs) -> List[capnp._DynamicStructReader]: +def replay_process_with_name(name: Union[str, Iterable[str]], lr: Union[LogReader, + List[capnp._DynamicStructReader]], *args, **kwargs) -> List[capnp._DynamicStructReader]: if isinstance(name, str): cfgs = [get_process_config(name)] elif isinstance(name, Iterable): @@ -660,7 +662,8 @@ def _replay_multi_process( continue assert frs is not None, "frs must be provided when replaying process using vision streams" - assert all(meta_from_camera_state(st) is not None for st in cfg.vision_pubs),f"undefined vision stream spotted, probably misconfigured process: {cfg.vision_pubs}" + assert all(meta_from_camera_state(st) is not None for st in cfg.vision_pubs),\ + f"undefined vision stream spotted, probably misconfigured process: {cfg.vision_pubs}" assert all(st in frs for st in cfg.vision_pubs), f"frs for this process must contain following vision streams: {cfg.vision_pubs}" all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) diff --git a/selfdrive/test/process_replay/regen.py b/selfdrive/test/process_replay/regen.py index 5afc55c406..d410ffd4fa 100755 --- a/selfdrive/test/process_replay/regen.py +++ b/selfdrive/test/process_replay/regen.py @@ -6,7 +6,8 @@ import capnp from typing import Union, Iterable, Optional, List, Any, Dict, Tuple -from selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, replay_process, get_process_config, check_openpilot_enabled, get_custom_params_from_lr +from selfdrive.test.process_replay.process_replay import CONFIGS, FAKEDATA, replay_process, get_process_config, \ + check_openpilot_enabled, get_custom_params_from_lr from selfdrive.test.update_ci_routes import upload_route from tools.lib.route import Route from tools.lib.framereader import FrameReader diff --git a/selfdrive/thermald/tests/test_power_monitoring.py b/selfdrive/thermald/tests/test_power_monitoring.py index 6b1be2d7ef..01dd553702 100755 --- a/selfdrive/thermald/tests/test_power_monitoring.py +++ b/selfdrive/thermald/tests/test_power_monitoring.py @@ -120,7 +120,8 @@ class TestPowerMonitoring(unittest.TestCase): POWER_DRAW = 0 # To stop shutting down for other reasons TEST_TIME = 350 VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S = 50 - with pm_patch("VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S", VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S, constant=True), pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): + with pm_patch("VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S", VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S, constant=True), \ + pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): pm = PowerMonitoring() pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh ignition = False diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index e76b202dba..572c205ec7 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -36,7 +36,8 @@ DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect PANDA_STATES_TIMEOUT = int(1000 * 1.5 * DT_TRML) # 1.5x the expected pandaState frequency ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp']) -HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats', 'network_metered', 'nvme_temps', 'modem_temps']) +HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats', + 'network_metered', 'nvme_temps', 'modem_temps']) # List of thermal bands. We will stay within this region as long as we are within the bounds. # When exiting the bounds, we'll jump to the lower or higher band. Bands are ordered in the dict. diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py index 575584dd50..08cf0743d4 100755 --- a/selfdrive/ui/translations/create_badges.py +++ b/selfdrive/ui/translations/create_badges.py @@ -54,7 +54,8 @@ if __name__ == "__main__": badge_svg.extend([f'', content_svg, ""]) - badge_svg.insert(0, f'') + badge_svg.insert(0, f'') badge_svg.append("") with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f: diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py index 3de6e0f27c..e8460c4274 100755 --- a/selfdrive/ui/update_translations.py +++ b/selfdrive/ui/update_translations.py @@ -46,7 +46,8 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description="Update translation files for UI", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("--vanish", action="store_true", help="Remove translations with source text no longer found") - parser.add_argument("--plural-only", type=str, nargs="*", default=["main_en"], help="Translation codes to only create plural translations for (ie. the base language)") + parser.add_argument("--plural-only", type=str, nargs="*", default=["main_en"], + help="Translation codes to only create plural translations for (ie. the base language)") args = parser.parse_args() update_translations(args.vanish, args.plural_only) diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 74ac77482e..6f4a134438 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -143,7 +143,7 @@ void CameraState::sensors_start() { void CameraState::sensors_poke(int request_id) { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet); - struct cam_packet *pkt = (struct cam_packet *)mm.alloc(size, &cam_packet_handle); + auto pkt = mm.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 0; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -156,15 +156,13 @@ void CameraState::sensors_poke(int request_id) { enabled = false; return; } - - mm.free(pkt); } void CameraState::sensors_i2c(struct i2c_random_wr_payload* dat, int len, int op_code, bool data_word) { // LOGD("sensors_i2c: %d", len); uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1; - struct cam_packet *pkt = (struct cam_packet *)mm.alloc(size, &cam_packet_handle); + auto pkt = mm.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 1; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -174,7 +172,7 @@ void CameraState::sensors_i2c(struct i2c_random_wr_payload* dat, int len, int op buf_desc[0].size = buf_desc[0].length = sizeof(struct i2c_rdwr_header) + len*sizeof(struct i2c_random_wr_payload); buf_desc[0].type = CAM_CMD_BUF_I2C; - struct cam_cmd_i2c_random_wr *i2c_random_wr = (struct cam_cmd_i2c_random_wr *)mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto i2c_random_wr = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); i2c_random_wr->header.count = len; i2c_random_wr->header.op_code = 1; i2c_random_wr->header.cmd_type = CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR; @@ -188,9 +186,6 @@ void CameraState::sensors_i2c(struct i2c_random_wr_payload* dat, int len, int op enabled = false; return; } - - mm.free(i2c_random_wr); - mm.free(pkt); } static cam_cmd_power *power_set_wait(cam_cmd_power *power, int16_t delay_ms) { @@ -204,7 +199,7 @@ static cam_cmd_power *power_set_wait(cam_cmd_power *power, int16_t delay_ms) { int CameraState::sensors_init() { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*2; - struct cam_packet *pkt = (struct cam_packet *)mm.alloc(size, &cam_packet_handle); + auto pkt = mm.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 2; pkt->kmd_cmd_buf_index = -1; pkt->header.op_code = 0x1000000 | CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE; @@ -213,8 +208,8 @@ int CameraState::sensors_init() { buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_cmd_i2c_info) + sizeof(struct cam_cmd_probe); buf_desc[0].type = CAM_CMD_BUF_LEGACY; - struct cam_cmd_i2c_info *i2c_info = (struct cam_cmd_i2c_info *)mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); - auto probe = (struct cam_cmd_probe *)(i2c_info + 1); + auto i2c_info = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto probe = (struct cam_cmd_probe *)(i2c_info.get() + 1); probe->camera_id = camera_num; switch (camera_num) { @@ -255,11 +250,11 @@ int CameraState::sensors_init() { //buf_desc[1].size = buf_desc[1].length = 148; buf_desc[1].size = buf_desc[1].length = 196; buf_desc[1].type = CAM_CMD_BUF_I2C; - struct cam_cmd_power *power_settings = (struct cam_cmd_power *)mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); - memset(power_settings, 0, buf_desc[1].size); + auto power_settings = mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); + memset(power_settings.get(), 0, buf_desc[1].size); // power on - struct cam_cmd_power *power = power_settings; + struct cam_cmd_power *power = power_settings.get(); power->count = 4; power->cmd_type = CAMERA_SENSOR_CMD_TYPE_PWR_UP; power->power_settings[0].power_seq_type = 3; // clock?? @@ -315,11 +310,6 @@ int CameraState::sensors_init() { int ret = do_cam_control(sensor_fd, CAM_SENSOR_PROBE_CMD, (void *)(uintptr_t)cam_packet_handle, 0); LOGD("probing the sensor: %d", ret); - - mm.free(i2c_info); - mm.free(power_settings); - mm.free(pkt); - return ret; } @@ -329,7 +319,7 @@ void CameraState::config_isp(int io_mem_handle, int fence, int request_id, int b if (io_mem_handle != 0) { size += sizeof(struct cam_buf_io_cfg); } - struct cam_packet *pkt = (struct cam_packet *)mm.alloc(size, &cam_packet_handle); + auto pkt = mm.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 2; pkt->kmd_cmd_buf_index = 0; // YUV has kmd_cmd_buf_offset = 1780 @@ -424,8 +414,8 @@ void CameraState::config_isp(int io_mem_handle, int fence, int request_id, int b buf_desc[1].length = buf_desc[1].size - buf_desc[1].offset; buf_desc[1].type = CAM_CMD_BUF_GENERIC; buf_desc[1].meta_data = CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON; - uint32_t *buf2 = (uint32_t *)mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); - memcpy(buf2, &tmp, sizeof(tmp)); + auto buf2 = mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); + memcpy(buf2.get(), &tmp, sizeof(tmp)); if (io_mem_handle != 0) { io_cfg[0].mem_handle[0] = io_mem_handle; @@ -459,9 +449,6 @@ void CameraState::config_isp(int io_mem_handle, int fence, int request_id, int b if (ret != 0) { LOGE("isp config failed"); } - - mm.free(buf2); - mm.free(pkt); } void CameraState::enqueue_buffer(int i, bool dp) { @@ -746,7 +733,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1; - struct cam_packet *pkt = (struct cam_packet *)mm.alloc(size, &cam_packet_handle); + auto pkt = mm.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 1; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -755,7 +742,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_csiphy_info); buf_desc[0].type = CAM_CMD_BUF_GENERIC; - struct cam_csiphy_info *csiphy_info = (struct cam_csiphy_info *)mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto csiphy_info = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); csiphy_info->lane_mask = 0x1f; csiphy_info->lane_assign = 0x3210;// skip clk. How is this 16 bit for 5 channels?? csiphy_info->csiphy_3phase = 0x0; // no 3 phase, only 2 conductors per lane @@ -767,9 +754,6 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num int ret_ = device_config(csiphy_fd, session_handle, csiphy_dev_handle, cam_packet_handle); assert(ret_ == 0); - - mm.free(csiphy_info); - mm.free(pkt); } // link devices diff --git a/system/camerad/cameras/camera_util.cc b/system/camerad/cameras/camera_util.cc index 6d139590e4..5e8640e99a 100644 --- a/system/camerad/cameras/camera_util.cc +++ b/system/camerad/cameras/camera_util.cc @@ -98,7 +98,7 @@ void release_fd(int video0_fd, uint32_t handle) { release(video0_fd, handle); } -void *MemoryManager::alloc(int size, uint32_t *handle) { +void *MemoryManager::alloc_buf(int size, uint32_t *handle) { lock.lock(); void *ptr; if (!cached_allocations[size].empty()) { diff --git a/system/camerad/cameras/camera_util.h b/system/camerad/cameras/camera_util.h index e408f6c0e2..b36f404c0f 100644 --- a/system/camerad/cameras/camera_util.h +++ b/system/camerad/cameras/camera_util.h @@ -1,6 +1,8 @@ #pragma once + #include #include +#include #include #include #include @@ -18,10 +20,17 @@ void release(int video0_fd, uint32_t handle); class MemoryManager { public: void init(int _video0_fd) { video0_fd = _video0_fd; } - void *alloc(int len, uint32_t *handle); - void free(void *ptr); ~MemoryManager(); + + template + auto alloc(int len, uint32_t *handle) { + return std::unique_ptr>((T*)alloc_buf(len, handle), [this](void *ptr) { this->free(ptr); }); + } + private: + void *alloc_buf(int len, uint32_t *handle); + void free(void *ptr); + std::mutex lock; std::map handle_lookup; std::map size_lookup; diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 018bc30004..8dc74ce3db 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -319,7 +319,8 @@ class Tici(HardwareBase): (True, tc + ["class", "add", "dev", adapter, "parent", "1:", "classid", "1:20", "htb", "rate", f"{upload_speed_kbps}kbit"]), # Create universal 32 bit filter on adapter that sends all outbound ip traffic through the class - (True, tc + ["filter", "add", "dev", adapter, "parent", "1:", "protocol", "ip", "prio", "10", "u32", "match", "ip", "dst", "0.0.0.0/0", "flowid", "1:20"]), + (True, tc + ["filter", "add", "dev", adapter, "parent", "1:", "protocol", "ip", "prio", \ + "10", "u32", "match", "ip", "dst", "0.0.0.0/0", "flowid", "1:20"]), ] download = [ @@ -328,7 +329,8 @@ class Tici(HardwareBase): # Redirect ingress (incoming) to egress ifb0 (True, tc + ["qdisc", "add", "dev", adapter, "handle", "ffff:", "ingress"]), - (True, tc + ["filter", "add", "dev", adapter, "parent", "ffff:", "protocol", "ip", "u32", "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", ifb]), + (True, tc + ["filter", "add", "dev", adapter, "parent", "ffff:", "protocol", "ip", "u32", \ + "match", "u32", "0", "0", "action", "mirred", "egress", "redirect", "dev", ifb]), # Add class and rules for virtual interface (True, tc + ["qdisc", "add", "dev", ifb, "root", "handle", "2:", "htb"]), diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index 245e5cbcf9..31ece0ae9c 100644 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -211,7 +211,8 @@ class Uploader: else: content_length = int(stat.request.headers.get("Content-Length", 0)) self.last_speed = (content_length / 1e6) / self.last_time - cloudlog.event("upload_success", key=key, fn=fn, sz=sz, content_length=content_length, network_type=network_type, metered=metered, speed=self.last_speed) + cloudlog.event("upload_success", key=key, fn=fn, sz=sz, content_length=content_length, + network_type=network_type, metered=metered, speed=self.last_speed) success = True else: success = False diff --git a/system/sensord/pigeond.py b/system/sensord/pigeond.py index c9ad7ff22a..9155c7d9bc 100755 --- a/system/sensord/pigeond.py +++ b/system/sensord/pigeond.py @@ -164,12 +164,12 @@ def initialize_pigeon(pigeon: TTYPigeon) -> bool: pigeon.send_with_ack(b"\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10") # UBX-CFG-NAV5 (0x06 0x24) - pigeon.send_with_ack(b"\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63") + pigeon.send_with_ack(b"\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63") # pylint: disable=C0301 # UBX-CFG-ODO (0x06 0x1E) pigeon.send_with_ack(b"\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37") pigeon.send_with_ack(b"\xB5\x62\x06\x39\x08\x00\xFF\xAD\x62\xAD\x1E\x63\x00\x00\x83\x0C") - pigeon.send_with_ack(b"\xB5\x62\x06\x23\x28\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x24") + pigeon.send_with_ack(b"\xB5\x62\x06\x23\x28\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x24") # pylint: disable=C0301 # UBX-CFG-NAV5 (0x06 0x24) pigeon.send_with_ack(b"\xB5\x62\x06\x24\x00\x00\x2A\x84") diff --git a/tools/bodyteleop/.gitignore b/tools/bodyteleop/.gitignore new file mode 100644 index 0000000000..adeab99a95 --- /dev/null +++ b/tools/bodyteleop/.gitignore @@ -0,0 +1,4 @@ +av +av-10.0.0/* +key.pem +cert.pem \ No newline at end of file diff --git a/tools/bodyteleop/bodyav.py b/tools/bodyteleop/bodyav.py new file mode 100644 index 0000000000..fc9559f8dc --- /dev/null +++ b/tools/bodyteleop/bodyav.py @@ -0,0 +1,159 @@ +import asyncio +import io +import numpy as np +import pyaudio +import wave + +from aiortc.contrib.media import MediaBlackhole +from aiortc.mediastreams import AudioStreamTrack, MediaStreamError, MediaStreamTrack +from aiortc.mediastreams import VIDEO_CLOCK_RATE, VIDEO_TIME_BASE +from aiortc.rtcrtpsender import RTCRtpSender +from av import CodecContext, Packet +from pydub import AudioSegment +import cereal.messaging as messaging + +AUDIO_RATE = 16000 +SOUNDS = { + 'engage': '../../selfdrive/assets/sounds/engage.wav', + 'disengage': '../../selfdrive/assets/sounds/disengage.wav', + 'error': '../../selfdrive/assets/sounds/warning_immediate.wav', +} + + +def force_codec(pc, sender, forced_codec='video/VP9', stream_type="video"): + codecs = RTCRtpSender.getCapabilities(stream_type).codecs + codec = [codec for codec in codecs if codec.mimeType == forced_codec] + transceiver = next(t for t in pc.getTransceivers() if t.sender == sender) + transceiver.setCodecPreferences(codec) + + +class EncodedBodyVideo(MediaStreamTrack): + kind = "video" + + _start: float + _timestamp: int + + def __init__(self): + super().__init__() + sock_name = 'livestreamDriverEncodeData' + messaging.context = messaging.Context() + self.sock = messaging.sub_sock(sock_name, None, conflate=True) + self.pts = 0 + + async def recv(self) -> Packet: + while True: + msg = messaging.recv_one_or_none(self.sock) + if msg is not None: + break + await asyncio.sleep(0.005) + + evta = getattr(msg, msg.which()) + self.last_idx = evta.idx.encodeId + + packet = Packet(evta.header + evta.data) + packet.time_base = VIDEO_TIME_BASE + packet.pts = self.pts + self.pts += 0.05 * VIDEO_CLOCK_RATE + return packet + + +class WebClientSpeaker(MediaBlackhole): + def __init__(self): + super().__init__() + self.p = pyaudio.PyAudio() + self.buffer = io.BytesIO() + self.channels = 2 + self.stream = self.p.open(format=pyaudio.paInt16, channels=self.channels, rate=48000, frames_per_buffer=9600, + output=True, stream_callback=self.pyaudio_callback) + + def pyaudio_callback(self, in_data, frame_count, time_info, status): + if self.buffer.getbuffer().nbytes < frame_count * self.channels * 2: + buff = np.zeros((frame_count, 2), dtype=np.int16).tobytes() + elif self.buffer.getbuffer().nbytes > 115200: # 3x the usual read size + self.buffer.seek(0) + buff = self.buffer.read(frame_count * self.channels * 4) + buff = buff[:frame_count * self.channels * 2] + self.buffer.seek(2) + else: + self.buffer.seek(0) + buff = self.buffer.read(frame_count * self.channels * 2) + self.buffer.seek(2) + return (buff, pyaudio.paContinue) + + async def consume(self, track): + while True: + try: + frame = await track.recv() + except MediaStreamError: + return + bio = bytes(frame.planes[0]) + self.buffer.write(bio) + + async def start(self): + for track, task in self._MediaBlackhole__tracks.items(): # pylint: disable=access-member-before-definition + if task is None: + self._MediaBlackhole__tracks[track] = asyncio.ensure_future(self.consume(track)) + + async def stop(self): + for task in self._MediaBlackhole__tracks.values(): # pylint: disable=access-member-before-definition + if task is not None: + task.cancel() + self._MediaBlackhole__tracks = {} + self.stream.stop_stream() + self.stream.close() + self.p.terminate() + + +class BodyMic(AudioStreamTrack): + def __init__(self): + super().__init__() + + self.sample_rate = AUDIO_RATE + self.AUDIO_PTIME = 0.020 # 20ms audio packetization + self.samples = int(self.AUDIO_PTIME * self.sample_rate) + self.FORMAT = pyaudio.paInt16 + self.CHANNELS = 2 + self.RATE = self.sample_rate + self.CHUNK = int(AUDIO_RATE * 0.020) + self.p = pyaudio.PyAudio() + self.mic_stream = self.p.open(format=self.FORMAT, channels=1, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK) + + self.codec = CodecContext.create('pcm_s16le', 'r') + self.codec.sample_rate = self.RATE + self.codec.channels = 2 + self.audio_samples = 0 + self.chunk_number = 0 + + async def recv(self): + mic_data = self.mic_stream.read(self.CHUNK) + mic_sound = AudioSegment(mic_data, sample_width=2, channels=1, frame_rate=self.RATE) + mic_sound = AudioSegment.from_mono_audiosegments(mic_sound, mic_sound) + mic_sound += 3 # increase volume by 3db + packet = Packet(mic_sound.raw_data) + frame = self.codec.decode(packet)[0] + frame.pts = self.audio_samples + self.audio_samples += frame.samples + self.chunk_number = self.chunk_number + 1 + return frame + + +async def play_sound(sound): + chunk = 5120 + with wave.open(SOUNDS[sound], 'rb') as wf: + def callback(in_data, frame_count, time_info, status): + data = wf.readframes(frame_count) + return data, pyaudio.paContinue + + p = pyaudio.PyAudio() + stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), + channels=wf.getnchannels(), + rate=wf.getframerate(), + output=True, + frames_per_buffer=chunk, + stream_callback=callback) + stream.start_stream() + while stream.is_active(): + await asyncio.sleep(0) + stream.stop_stream() + stream.close() + p.terminate() diff --git a/tools/bodyteleop/static/index.html b/tools/bodyteleop/static/index.html new file mode 100644 index 0000000000..3654769756 --- /dev/null +++ b/tools/bodyteleop/static/index.html @@ -0,0 +1,103 @@ + + + + + commabody + + + + + + + + + + +
+

comma body

+ + +
+
+
+
+
+ + + +
+

body

+
+
+
+ + +
+

you

+
+
+
+
+
+
+
+ - +
+

ping time

+
+
+
+ - +
+

battery

+
+
+
+ +
+
+
+
+
+
W
+
0,0x,y
+
+
+
A
+
S
+
D
+
+
+
+ + + + +
+
+
+
+
+
+

Play Sounds

+
+
+ + + +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/tools/bodyteleop/static/js/controls.js b/tools/bodyteleop/static/js/controls.js new file mode 100644 index 0000000000..b1e0e7ee70 --- /dev/null +++ b/tools/bodyteleop/static/js/controls.js @@ -0,0 +1,54 @@ +const keyVals = {w: 0, a: 0, s: 0, d: 0} + +export function getXY() { + let x = -keyVals.w + keyVals.s + let y = -keyVals.d + keyVals.a + return {x, y} +} + +export const handleKeyX = (key, setValue) => { + if (['w', 'a', 's', 'd'].includes(key)){ + keyVals[key] = setValue; + let color = "#333"; + if (setValue === 1){ + color = "#e74c3c"; + } + $("#key-"+key).css('background', color); + const {x, y} = getXY(); + $("#pos-vals").text(x+","+y); + } +}; + +export async function executePlan() { + let plan = $("#plan-text").val(); + const planList = []; + plan.split("\n").forEach(function(e){ + let line = e.split(",").map(k=>parseInt(k)); + if (line.length != 5 || line.slice(0, 4).map(e=>[1, 0].includes(e)).includes(false) || line[4] < 0 || line[4] > 10){ + console.log("invalid plan"); + } + else{ + planList.push(line) + } + }); + + async function execute() { + for (var i = 0; i < planList.length; i++) { + let [w, a, s, d, t] = planList[i]; + while(t > 0){ + console.log(w, a, s, d, t); + if(w==1){$("#key-w").mousedown();} + if(a==1){$("#key-a").mousedown();} + if(s==1){$("#key-s").mousedown();} + if(d==1){$("#key-d").mousedown();} + await sleep(50); + $("#key-w").mouseup(); + $("#key-a").mouseup(); + $("#key-s").mouseup(); + $("#key-d").mouseup(); + t = t - 0.05; + } + } + } + execute(); +} \ No newline at end of file diff --git a/tools/bodyteleop/static/js/jsmain.js b/tools/bodyteleop/static/js/jsmain.js new file mode 100644 index 0000000000..f521905724 --- /dev/null +++ b/tools/bodyteleop/static/js/jsmain.js @@ -0,0 +1,23 @@ +import { handleKeyX, executePlan } from "./controls.js"; +import { start, stop, last_ping } from "./webrtc.js"; + +export var pc = null; +export var dc = null; + +document.addEventListener('keydown', (e)=>(handleKeyX(e.key.toLowerCase(), 1))); +document.addEventListener('keyup', (e)=>(handleKeyX(e.key.toLowerCase(), 0))); +$(".keys").bind("mousedown touchstart", (e)=>handleKeyX($(e.target).attr('id').replace('key-', ''), 1)); +$(".keys").bind("mouseup touchend", (e)=>handleKeyX($(e.target).attr('id').replace('key-', ''), 0)); +$("#plan-button").click(executePlan); + +setInterval( () => { + const dt = new Date().getTime(); + if ((dt - last_ping) > 1000) { + $(".pre-blob").removeClass('blob'); + $("#battery").text("-"); + $("#ping-time").text('-'); + $("video")[0].load(); + } +}, 5000); + +start(pc, dc); \ No newline at end of file diff --git a/tools/bodyteleop/static/js/plots.js b/tools/bodyteleop/static/js/plots.js new file mode 100644 index 0000000000..5327bf71be --- /dev/null +++ b/tools/bodyteleop/static/js/plots.js @@ -0,0 +1,53 @@ +export const pingPoints = []; +export const batteryPoints = []; + +function getChartConfig(pts, color, title, ymax=100) { + return { + type: 'line', + data: { + datasets: [{ + label: title, + data: pts, + borderWidth: 1, + borderColor: color, + backgroundColor: color, + fill: 'origin' + }] + }, + options: { + scales: { + x: { + type: 'time', + time: { + unit: 'minute', + displayFormats: { + second: 'h:mm a' + } + }, + grid: { + color: '#222', // Grid lines color + }, + ticks: { + source: 'data', + fontColor: 'rgba(255, 255, 255, 1.0)', // Y-axis label color + } + }, + y: { + beginAtZero: true, + max: ymax, + grid: { + color: 'rgba(255, 255, 255, 0.1)', // Grid lines color + }, + ticks: { + fontColor: 'rgba(255, 255, 255, 0.7)', // Y-axis label color + } + } + } + } + } +} + +const ctxPing = document.getElementById('chart-ping'); +const ctxBattery = document.getElementById('chart-battery'); +export const chartPing = new Chart(ctxPing, getChartConfig(pingPoints, 'rgba(192, 57, 43, 0.7)', 'Controls Ping Time (ms)', 250)); +export const chartBattery = new Chart(ctxBattery, getChartConfig(batteryPoints, 'rgba(41, 128, 185, 0.7)', 'Battery %', 100)); diff --git a/tools/bodyteleop/static/js/webrtc.js b/tools/bodyteleop/static/js/webrtc.js new file mode 100644 index 0000000000..8bc8e77317 --- /dev/null +++ b/tools/bodyteleop/static/js/webrtc.js @@ -0,0 +1,217 @@ +import { getXY } from "./controls.js"; +import { pingPoints, batteryPoints, chartPing, chartBattery } from "./plots.js"; + +export let dcInterval = null; +export let batteryInterval = null; +export let last_ping = null; + + +export function createPeerConnection(pc) { + var config = { + sdpSemantics: 'unified-plan' + }; + + pc = new RTCPeerConnection(config); + + // connect audio / video + pc.addEventListener('track', function(evt) { + console.log("Adding Tracks!") + if (evt.track.kind == 'video') + document.getElementById('video').srcObject = evt.streams[0]; + else + document.getElementById('audio').srcObject = evt.streams[0]; + }); + return pc; +} + + +export function negotiate(pc) { + return pc.createOffer({offerToReceiveAudio:true, offerToReceiveVideo:true}).then(function(offer) { + return pc.setLocalDescription(offer); + }).then(function() { + return new Promise(function(resolve) { + if (pc.iceGatheringState === 'complete') { + resolve(); + } + else { + function checkState() { + if (pc.iceGatheringState === 'complete') { + pc.removeEventListener('icegatheringstatechange', checkState); + resolve(); + } + } + pc.addEventListener('icegatheringstatechange', checkState); + } + }); + }).then(function() { + var offer = pc.localDescription; + return fetch('/offer', { + body: JSON.stringify({ + sdp: offer.sdp, + type: offer.type, + }), + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST' + }); + }).then(function(response) { + console.log(response); + return response.json(); + }).then(function(answer) { + return pc.setRemoteDescription(answer); + }).catch(function(e) { + alert(e); + }); +} + + +function isMobile() { + let check = false; + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera); + return check; +}; + + +export const constraints = { + audio: { + autoGainControl: false, + sampleRate: 48000, + sampleSize: 16, + echoCancellation: true, + noiseSuppression: true, + channelCount: 1 + }, + video: isMobile() +}; + + +export function createDummyVideoTrack() { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + + const frameWidth = 5; // Set the width of the frame + const frameHeight = 5; // Set the height of the frame + canvas.width = frameWidth; + canvas.height = frameHeight; + + context.fillStyle = 'black'; + context.fillRect(0, 0, frameWidth, frameHeight); + + const stream = canvas.captureStream(); + const videoTrack = stream.getVideoTracks()[0]; + + return videoTrack; +} + + +export function start(pc, dc) { + pc = createPeerConnection(pc); + + if (constraints.audio || constraints.video) { + // add audio track + navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { + stream.getTracks().forEach(function(track) { + pc.addTrack(track, stream); + // only audio? + // if (track.kind === 'audio'){ + // pc.addTrack(track, stream); + // } + }); + return negotiate(pc); + }, function(err) { + alert('Could not acquire media: ' + err); + }); + + // add a fake video? + // const dummyVideoTrack = createDummyVideoTrack(); + // const dummyMediaStream = new MediaStream(); + // dummyMediaStream.addTrack(dummyVideoTrack); + // pc.addTrack(dummyVideoTrack, dummyMediaStream); + + } else { + negotiate(pc); + } + + // setInterval(() => {pc.getStats(null).then((stats) => {stats.forEach((report) => console.log(report))})}, 10000) + // var video = document.querySelector('video'); + // var print = function (e, f){console.log(e, f); video.requestVideoFrameCallback(print);}; + // video.requestVideoFrameCallback(print); + + + var parameters = {"ordered": true}; + dc = pc.createDataChannel('data', parameters); + dc.onclose = function() { + console.log("data channel closed"); + clearInterval(dcInterval); + clearInterval(batteryInterval); + }; + function controlCommand() { + const {x, y} = getXY(); + const dt = new Date().getTime(); + var message = JSON.stringify({type: 'control_command', x, y, dt}); + dc.send(message); + } + + function batteryLevel() { + var message = JSON.stringify({type: 'battery_level'}); + dc.send(message); + } + + dc.onopen = function() { + dcInterval = setInterval(controlCommand, 50); + batteryInterval = setInterval(batteryLevel, 10000); + controlCommand(); + batteryLevel(); + $(".sound").click((e)=>{ + const sound = $(e.target).attr('id').replace('sound-', '') + dc.send(JSON.stringify({type: 'play_sound', sound})); + }); + }; + + let val_print_idx = 0; + dc.onmessage = function(evt) { + const data = JSON.parse(evt.data); + if(val_print_idx == 0 && data.type === 'ping_time') { + const dt = new Date().getTime(); + const pingtime = dt - data.incoming_time; + pingPoints.push({'x': dt, 'y': pingtime}); + if (pingPoints.length > 1000) { + pingPoints.shift(); + } + chartPing.update(); + $("#ping-time").text((pingtime) + "ms"); + last_ping = dt; + $(".pre-blob").addClass('blob'); + } + val_print_idx = (val_print_idx + 1 ) % 20; + if(data.type === 'battery_level') { + $("#battery").text(data.value + "%"); + batteryPoints.push({'x': new Date().getTime(), 'y': data.value}); + if (batteryPoints.length > 1000) { + batteryPoints.shift(); + } + chartBattery.update(); + } + }; +} + + +export function stop(pc, dc) { + if (dc) { + dc.close(); + } + if (pc.getTransceivers) { + pc.getTransceivers().forEach(function(transceiver) { + if (transceiver.stop) { + transceiver.stop(); + } + }); + } + pc.getSenders().forEach(function(sender) { + sender.track.stop(); + }); + setTimeout(function() { + pc.close(); + }, 500); +} diff --git a/tools/bodyteleop/static/main.css b/tools/bodyteleop/static/main.css new file mode 100644 index 0000000000..1bfb5982b4 --- /dev/null +++ b/tools/bodyteleop/static/main.css @@ -0,0 +1,185 @@ +body { + background: #333 !important; + color: #fff !important; + display: flex; + justify-content: center; + align-items: start; +} + +p { + margin: 0px !important; +} + +i { + font-style: normal; +} + +.small { + font-size: 1em !important +} + +.jumbo { + font-size: 8rem; +} + + +@media (max-width: 600px) { + .small { + font-size: 0.5em !important + } + .jumbo { + display: none; + } + +} + +#main { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + align-items: center; + font-size: 30px; + width: 100%; + max-width: 1200px; +} + +video { + width: 95%; +} + +.pre-blob { + display: flex; + background: #333; + border-radius: 50%; + margin: 10px; + height: 45px; + width: 45px; + justify-content: center; + align-items: center; + font-size: 1rem; +} + +.blob { + background: rgba(231, 76, 60,1.0); + box-shadow: 0 0 0 0 rgba(231, 76, 60,1.0); + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0px rgba(192, 57, 43, 1); + } + 100% { + box-shadow: 0 0 0 20px rgba(192, 57, 43, 0); + } +} + + +.icon-sup-panel { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; + background: #222; + border-radius: 10px; + padding: 5px; + margin: 5px 0px 5px 0px; +} + +.icon-sub-panel { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +#icon-panel { + display: flex; + width: 100%; + justify-content: space-between; + margin-top: 5px; +} + +.icon-sub-sub-panel { + display: flex; + flex-direction: row; +} + +.keys, #key-val { + background: #333; + padding: 2rem; + margin: 5px; + color: #fff; + display: flex; + justify-content: center; + align-items: center; + border-radius: 10px; + cursor: pointer; +} + +#key-val { + pointer-events: none; + background: #fff; + color: #333; + line-height: 1; + font-size: 20px; + flex-direction: column; +} + +.wasd-row { + display: flex; + flex-direction: row; + justify-content: center; + align-items: stretch; +} + +#wasd { + margin: 5px 0px 5px 0px; + background: #222; + border-radius: 10px; + width: 100%; + padding: 20px; + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: stretch; + + user-select: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + touch-action: manipulation; +} + +.panel { + display: flex; + justify-content: center; + margin: 5px 0px 5px 0px !important; + background: #222; + border-radius: 10px; + width: 100%; + padding: 10px; +} + +#ping-time, #battery { + font-size: 25px; +} + +#stop { + display: none; +} + +.plan-form { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.details { + display: flex; + padding: 0px 10px 0px 10px; +} diff --git a/tools/bodyteleop/static/poster.png b/tools/bodyteleop/static/poster.png new file mode 100644 index 0000000000..ba0e846b22 Binary files /dev/null and b/tools/bodyteleop/static/poster.png differ diff --git a/tools/bodyteleop/web.py b/tools/bodyteleop/web.py new file mode 100644 index 0000000000..e39756964f --- /dev/null +++ b/tools/bodyteleop/web.py @@ -0,0 +1,202 @@ +import asyncio +import json +import logging +import os +import ssl +import uuid +import time + +from common.basedir import BASEDIR +from aiohttp import web +from aiortc import RTCPeerConnection, RTCSessionDescription + +import cereal.messaging as messaging +from tools.bodyteleop.bodyav import BodyMic, WebClientSpeaker, force_codec, play_sound, MediaBlackhole, EncodedBodyVideo + +logger = logging.getLogger("pc") +logging.basicConfig(level=logging.INFO) + +pcs = set() +pm, sm = None, None +TELEOPDIR = f"{BASEDIR}/tools/bodyteleop" + + +async def index(request): + content = open(TELEOPDIR + "/static/index.html", "r").read() + now = time.monotonic() + request.app['mutable_vals']['last_send_time'] = now + request.app['mutable_vals']['last_override_time'] = now + request.app['mutable_vals']['prev_command'] = [] + request.app['mutable_vals']['find_person'] = False + + return web.Response(content_type="text/html", text=content) + + +async def control_body(data, app): + now = time.monotonic() + if (data['type'] == 'dummy_controls') and (now < (app['mutable_vals']['last_send_time'] + 0.2)): + return + if (data['type'] == 'control_command') and (app['mutable_vals']['prev_command'] == [data['x'], data['y']] and data['x'] == 0 and data['y'] == 0): + return + + logger.info(str(data)) + x = max(-1.0, min(1.0, data['x'])) + y = max(-1.0, min(1.0, data['y'])) + dat = messaging.new_message('testJoystick') + dat.testJoystick.axes = [x, y] + dat.testJoystick.buttons = [False] + pm.send('testJoystick', dat) + app['mutable_vals']['last_send_time'] = now + if (data['type'] == 'control_command'): + app['mutable_vals']['last_override_time'] = now + app['mutable_vals']['prev_command'] = [data['x'], data['y']] + + +async def dummy_controls_msg(app): + while True: + if 'last_send_time' in app['mutable_vals']: + this_time = time.monotonic() + if (app['mutable_vals']['last_send_time'] + 0.2) < this_time: + await control_body({'type': 'dummy_controls', 'x': 0, 'y': 0}, app) + await asyncio.sleep(0.2) + + +async def start_background_tasks(app): + app['bgtask_dummy_controls_msg'] = asyncio.create_task(dummy_controls_msg(app)) + + +async def stop_background_tasks(app): + app['bgtask_dummy_controls_msg'].cancel() + await app['bgtask_dummy_controls_msg'] + + +async def offer(request): + logger.info("\n\n\nnewoffer!\n\n") + + params = await request.json() + offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) + speaker = WebClientSpeaker() + blackhole = MediaBlackhole() + + pc = RTCPeerConnection() + pc_id = "PeerConnection(%s)" % uuid.uuid4() + pcs.add(pc) + + def log_info(msg, *args): + logger.info(pc_id + " " + msg, *args) + + log_info("Created for %s", request.remote) + + @pc.on("datachannel") + def on_datachannel(channel): + request.app['mutable_vals']['remote_channel'] = channel + + @channel.on("message") + async def on_message(message): + data = json.loads(message) + if data['type'] == 'control_command': + await control_body(data, request.app) + times = { + 'type': 'ping_time', + 'incoming_time': data['dt'], + 'outgoing_time': int(time.time() * 1000), + } + channel.send(json.dumps(times)) + if data['type'] == 'battery_level': + sm.update(timeout=0) + if sm.updated['carState']: + channel.send(json.dumps({'type': 'battery_level', 'value': int(sm['carState'].fuelGauge * 100)})) + if data['type'] == 'play_sound': + logger.info(f"Playing sound: {data['sound']}") + await play_sound(data['sound']) + if data['type'] == 'find_person': + request.app['mutable_vals']['find_person'] = data['value'] + + @pc.on("connectionstatechange") + async def on_connectionstatechange(): + log_info("Connection state is %s", pc.connectionState) + if pc.connectionState == "failed": + await pc.close() + pcs.discard(pc) + + @pc.on('track') + def on_track(track): + logger.info(f"Track received: {track.kind}") + if track.kind == "audio": + speaker.addTrack(track) + elif track.kind == "video": + blackhole.addTrack(track) + + @track.on("ended") + async def on_ended(): + log_info("Remote %s track ended", track.kind) + if track.kind == "audio": + await speaker.stop() + elif track.kind == "video": + await blackhole.stop() + + video_sender = pc.addTrack(EncodedBodyVideo()) + force_codec(pc, video_sender, forced_codec='video/H264') + _ = pc.addTrack(BodyMic()) + + await pc.setRemoteDescription(offer) + await speaker.start() + await blackhole.start() + answer = await pc.createAnswer() + await pc.setLocalDescription(answer) + + return web.Response( + content_type="application/json", + text=json.dumps( + {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} + ), + ) + + +async def on_shutdown(app): + coros = [pc.close() for pc in pcs] + await asyncio.gather(*coros) + pcs.clear() + + +async def run(cmd): + proc = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await proc.communicate() + logger.info("Created key and cert!") + if stdout: + logger.info(f'[stdout]\n{stdout.decode()}') + if stderr: + logger.info(f'[stderr]\n{stderr.decode()}') + + +def main(): + global pm, sm + pm = messaging.PubMaster(['testJoystick']) + sm = messaging.SubMaster(['carState', 'logMessage']) + # App needs to be HTTPS for microphone and audio autoplay to work on the browser + cert_path = TELEOPDIR + '/cert.pem' + key_path = TELEOPDIR + '/key.pem' + if (not os.path.exists(cert_path)) or (not os.path.exists(key_path)): + asyncio.run(run(f'openssl req -x509 -newkey rsa:4096 -nodes -out {cert_path} -keyout {key_path} \ + -days 365 -subj "/C=US/ST=California/O=commaai/OU=comma body"')) + else: + logger.info("Certificate exists!") + ssl_context = ssl.SSLContext() + ssl_context.load_cert_chain(cert_path, key_path) + app = web.Application() + app['mutable_vals'] = {} + app.on_shutdown.append(on_shutdown) + app.router.add_post("/offer", offer) + app.router.add_get("/", index) + app.router.add_static('/static', TELEOPDIR + '/static') + app.on_startup.append(start_background_tasks) + app.on_cleanup.append(stop_background_tasks) + web.run_app(app, access_log=None, host="0.0.0.0", port=5000, ssl_context=ssl_context) + + +if __name__ == "__main__": + main() diff --git a/tools/camerastream/compressed_vipc.py b/tools/camerastream/compressed_vipc.py index 0e524c5a04..495397d242 100755 --- a/tools/camerastream/compressed_vipc.py +++ b/tools/camerastream/compressed_vipc.py @@ -95,7 +95,9 @@ def decoder(addr, vipc_server, vst, nvidia, debug=False): pc_latency = (time.monotonic()-time_q[0])*1000 time_q = time_q[1:] if debug: - print("%2d %4d %.3f %.3f roll %6.2f ms latency %6.2f ms + %6.2f ms + %6.2f ms = %6.2f ms" % (len(msgs), evta.idx.encodeId, evt.logMonoTime/1e9, evta.idx.timestampEof/1e6, frame_latency, process_latency, network_latency, pc_latency, process_latency+network_latency+pc_latency ), len(evta.data), sock_name) + print("%2d %4d %.3f %.3f roll %6.2f ms latency %6.2f ms + %6.2f ms + %6.2f ms = %6.2f ms" + % (len(msgs), evta.idx.encodeId, evt.logMonoTime/1e9, evta.idx.timestampEof/1e6, frame_latency, + process_latency, network_latency, pc_latency, process_latency+network_latency+pc_latency ), len(evta.data), sock_name) class CompressedVipc: def __init__(self, addr, vision_streams, nvidia=False, debug=False): diff --git a/tools/joystick/web.py b/tools/joystick/web.py deleted file mode 100755 index 5cba4e938d..0000000000 --- a/tools/joystick/web.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -import time -import threading -from flask import Flask - -import cereal.messaging as messaging - -app = Flask(__name__) -pm = messaging.PubMaster(['testJoystick']) - -index = """ - - - - - -
- -""" - -@app.route("/") -def hello_world(): - return index - -last_send_time = time.monotonic() -@app.route("/control//") -def control(x, y): - global last_send_time - x,y = float(x), float(y) - x = max(-1, min(1, x)) - y = max(-1, min(1, y)) - dat = messaging.new_message('testJoystick') - dat.testJoystick.axes = [y,x] - dat.testJoystick.buttons = [False] - pm.send('testJoystick', dat) - last_send_time = time.monotonic() - return "" - -def handle_timeout(): - while 1: - this_time = time.monotonic() - if (last_send_time+0.5) < this_time: - #print("timeout, no web in %.2f s" % (this_time-last_send_time)) - dat = messaging.new_message('testJoystick') - dat.testJoystick.axes = [0,0] - dat.testJoystick.buttons = [False] - pm.send('testJoystick', dat) - time.sleep(0.1) - -def main(): - threading.Thread(target=handle_timeout, daemon=True).start() - app.run(host="0.0.0.0") - -if __name__ == '__main__': - main() diff --git a/tools/sim/tests/test_carla_integration.py b/tools/sim/tests/test_carla_integration.py index f467391967..cfaf95ca01 100755 --- a/tools/sim/tests/test_carla_integration.py +++ b/tools/sim/tests/test_carla_integration.py @@ -68,7 +68,8 @@ class TestCarlaIntegration(unittest.TestCase): no_car_events_issues_once = True break - self.assertTrue(no_car_events_issues_once, f"Failed because no messages received, or CarEvents '{car_event_issues}' or processes not running '{not_running}'") + self.assertTrue(no_car_events_issues_once, + f"Failed because no messages received, or CarEvents '{car_event_issues}' or processes not running '{not_running}'") start_time = time.monotonic() min_counts_control_active = 100 diff --git a/tools/tuning/measure_steering_accuracy.py b/tools/tuning/measure_steering_accuracy.py index 4819be770f..b816d82f91 100755 --- a/tools/tuning/measure_steering_accuracy.py +++ b/tools/tuning/measure_steering_accuracy.py @@ -105,7 +105,10 @@ class SteeringAccuracyTool: print(f" {'-'*118}") for k in sorted(self.speed_group_stats[group].keys()): v = self.speed_group_stats[group][k] - print(f' {k:#2}° | actuator:{int(v["steer"] / v["cnt"] * 100):#3}% | error: {round(v["err"] / v["cnt"], 2):2.2f}° | -:{int(v["-"] / v["cnt"] * 100):#3}% | =:{int(v["="] / v["cnt"] * 100):#3}% | +:{int(v["+"] / v["cnt"] * 100):#3}% | lim:{v["limited"]:#5} | sat:{v["saturated"]:#5} | path dev: {round(v["dpp"] / v["cnt"], 2):2.2f}m | total: {v["cnt"]:#5}') + print(f' {k:#2}° | actuator:{int(v["steer"] / v["cnt"] * 100):#3}% \ + | error: {round(v["err"] / v["cnt"], 2):2.2f}° | -:{int(v["-"] / v["cnt"] * 100):#3}% \ + | =:{int(v["="] / v["cnt"] * 100):#3}% | +:{int(v["+"] / v["cnt"] * 100):#3}% | lim:{v["limited"]:#5} \ + | sat:{v["saturated"]:#5} | path dev: {round(v["dpp"] / v["cnt"], 2):2.2f}m | total: {v["cnt"]:#5}') print("")