diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a24c524047..36bb6aa840 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ FROM ghcr.io/commaai/openpilot-base:latest -RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa libegl1-mesa +RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa libegl1-mesa gdb RUN pip install ipython jupyter jupyterlab RUN cd /tmp && \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a7a63658ed..f1cfc82159 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,7 +18,6 @@ "--volume=${localWorkspaceFolder}/.devcontainer/.host/.Xauthority:/home/batman/.Xauthority", "--volume=${localEnv:HOME}/.comma:/home/batman/.comma", "--volume=/tmp/comma_download_cache:/tmp/comma_download_cache", - "--volume=/tmp/devcontainer_scons_cache:/tmp/scons_cache", "--shm-size=1G", "--add-host=host.docker.internal:host-gateway", // required to use host.docker.internal on linux "--publish=0.0.0.0:8070-8079:8070-8079" // body ZMQ services @@ -43,5 +42,8 @@ "lharri73.dbc" ] } - } + }, + "mounts": [ + "type=volume,source=scons_cache,target=/tmp/scons_cache" + ] } \ No newline at end of file diff --git a/RELEASES.md b/RELEASES.md index d0fd530db1..4ec4c4fad5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,7 +1,7 @@ Version 0.9.7 (2024-XX-XX) ======================== * New driving model -* Support for many hybrid Ford models +* Support for hybrid variants of supported Ford models Version 0.9.6 (2024-02-27) ======================== @@ -11,6 +11,9 @@ Version 0.9.6 (2024-02-27) * Directly outputs curvature for lateral control * New driver monitoring model * Trained on larger dataset +* Model path UI + * Shows where driving model wants to be + * Shows what model is seeing more clearly, but more jittery * AGNOS 9 * comma body streaming and controls over WebRTC * Improved fuzzy fingerprinting for many makes and models diff --git a/SConstruct b/SConstruct index dac3529892..50dbda4b8d 100644 --- a/SConstruct +++ b/SConstruct @@ -386,9 +386,10 @@ SConscript(['third_party/SConscript']) SConscript(['selfdrive/SConscript']) -if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'): +if Dir('#tools/cabana/').exists() and GetOption('extras'): SConscript(['tools/replay/SConscript']) - SConscript(['tools/cabana/SConscript']) + if arch != "larch64": + SConscript(['tools/cabana/SConscript']) external_sconscript = GetOption('external_sconscript') if external_sconscript: diff --git a/cereal b/cereal index bfbb0cab83..c5c2a60f1a 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit bfbb0cab83e3ad49d85ad1a34ee1241bf1ff782f +Subproject commit c5c2a60f1aa796e7de464015349db3c336b79220 diff --git a/common/params.cc b/common/params.cc index aef2cbdecd..f68e1e4c6b 100644 --- a/common/params.cc +++ b/common/params.cc @@ -188,6 +188,7 @@ std::unordered_map keys = { {"RecordFront", PERSISTENT}, {"RecordFrontLock", PERSISTENT}, // for the internal fleet {"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, + {"RouteCount", PERSISTENT}, {"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"SshEnabled", PERSISTENT}, {"TermsVersion", PERSISTENT}, diff --git a/docs/CARS.md b/docs/CARS.md index 7ad12189cb..591a76e4cb 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 device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 288 Supported Cars +# 289 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -143,6 +143,7 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Hybrid 2018|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -239,9 +240,9 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|Corolla Hatchback 2019-22|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Corolla Hybrid 2020-22|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander 2017-19|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Highlander 2020-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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander Hybrid 2017-19|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Highlander Hybrid 2020-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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Mirai 2021|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Prius 2016|Toyota Safety Sense P|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 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -352,6 +353,7 @@ openpilot does not yet support these Toyota models due to a new message authenti * Toyota Venza 2021+ * Toyota Sequoia 2023+ * Toyota Tundra 2022+ +* Toyota Highlander 2024+ * Toyota Corolla Cross 2022+ (only US model) * Lexus NX 2022+ * Toyota bZ4x 2023+ diff --git a/opendbc b/opendbc index 5f096db742..ff1f1ff335 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 5f096db742b0c5dcd19976afdb07d5dd098f4b07 +Subproject commit ff1f1ff335261c469635c57c81817afd04663eab diff --git a/panda b/panda index ea156f7c62..41e9610ff8 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ea156f7c628a371bea9a15a29f9068d5392534ba +Subproject commit 41e9610ff841e4cf62051c6df09c1870f5d12477 diff --git a/pyproject.toml b/pyproject.toml index 99a8602460..8894d5eca2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ testpaths = [ "selfdrive/thermald", "selfdrive/test/longitudinal_maneuvers", "selfdrive/test/process_replay/test_fuzzy.py", + "selfdrive/updated", "system/camerad", "system/hardware/tici", "system/loggerd", @@ -170,6 +171,7 @@ build-backend = "poetry.core.masonry.api" # https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml [tool.ruff] +indent-width = 2 lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"] lint.ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"] line-length = 160 @@ -194,3 +196,5 @@ lint.flake8-implicit-str-concat.allow-multiline=false [tool.coverage.run] concurrency = ["multiprocessing", "thread"] +[tool.ruff.format] +quote-style = "preserve" diff --git a/rednose_repo b/rednose_repo index c5762e8bc6..1dc61a60e6 160000 --- a/rednose_repo +++ b/rednose_repo @@ -1 +1 @@ -Subproject commit c5762e8bc6f7338c7a30d2cd1cba8cc64e81ba19 +Subproject commit 1dc61a60e684b4bc8c591a8bce7e24e02aa8f400 diff --git a/release/files_common b/release/files_common index 1fb05b43a7..4286c46c90 100644 --- a/release/files_common +++ b/release/files_common @@ -53,9 +53,10 @@ tools/replay/*.h selfdrive/__init__.py selfdrive/sentry.py selfdrive/tombstoned.py -selfdrive/updated.py selfdrive/statsd.py +selfdrive/updated/* + system/logmessaged.py system/micd.py system/version.py @@ -86,6 +87,7 @@ selfdrive/boardd/pandad.py selfdrive/boardd/tests/test_boardd_loopback.py selfdrive/car/__init__.py +selfdrive/car/card.py selfdrive/car/docs_definitions.py selfdrive/car/car_helpers.py selfdrive/car/fingerprints.py @@ -540,7 +542,8 @@ opendbc/vw_golf_mk4.dbc opendbc/vw_mqb_2010.dbc opendbc/tesla_can.dbc -opendbc/tesla_radar.dbc +opendbc/tesla_radar_bosch_generated.dbc +opendbc/tesla_radar_continental_generated.dbc opendbc/tesla_powertrain.dbc tinygrad_repo/openpilot/compile2.py diff --git a/scripts/cell.sh b/scripts/cell.sh index 3f31978af5..4ba007a532 100755 --- a/scripts/cell.sh +++ b/scripts/cell.sh @@ -1,3 +1,3 @@ #!/usr/bin/bash -nmcli connection modify --temporary lte ipv4.route-metric 1 ipv6.route-metric 1 -nmcli con up lte +nmcli connection modify --temporary esim ipv4.route-metric 1 ipv6.route-metric 1 +nmcli con up esim diff --git a/scripts/stop_updater.sh b/scripts/stop_updater.sh index 4243d30e9f..7f82191823 100755 --- a/scripts/stop_updater.sh +++ b/scripts/stop_updater.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh # Stop updater -pkill -2 -f selfdrive.updated +pkill -2 -f selfdrive.updated.updated # Remove pending update rm -f /data/safe_staging/finalized/.overlay_consistent diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index e075887a4d..d738231c7b 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -284,6 +284,13 @@ bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector int: return hash(self.platform_str) @@ -294,10 +299,22 @@ class Platforms(str, ReprEnum): def create_dbc_map(cls) -> dict[str, DbcDict]: return {p: p.config.dbc_dict for p in cls} - @classmethod - def create_carinfo_map(cls) -> dict[str, CarInfos]: - return {p: p.config.car_info for p in cls} - @classmethod def with_flags(cls, flags: IntFlag) -> set['Platforms']: return {p for p in cls if p.config.flags & flags} + + @classmethod + def without_flags(cls, flags: IntFlag) -> set['Platforms']: + return {p for p in cls if not (p.config.flags & flags)} + + @classmethod + def print_debug(cls, flags): + platforms_with_flag = defaultdict(list) + for flag in flags: + for platform in cls: + if platform.config.flags & flag: + assert flag.name is not None + platforms_with_flag[flag.name].append(platform) + + for flag, platforms in platforms_with_flag.items(): + print(f"{flag:32s}: {', '.join(p.name for p in platforms)}") diff --git a/selfdrive/car/body/interface.py b/selfdrive/car/body/interface.py index 12a2d5f304..4d72d2f604 100644 --- a/selfdrive/car/body/interface.py +++ b/selfdrive/car/body/interface.py @@ -14,14 +14,10 @@ class CarInterface(CarInterfaceBase): ret.minSteerSpeed = -math.inf ret.maxLateralAccel = math.inf # TODO: set to a reasonable value - ret.steerRatio = 0.5 ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0. - ret.mass = 9 - ret.wheelbase = 0.406 ret.wheelSpeedFactor = SPEED_FROM_RPM - ret.centerToFront = ret.wheelbase * 0.44 ret.radarUnavailable = True ret.openpilotLongitudinalControl = True diff --git a/selfdrive/car/body/values.py b/selfdrive/car/body/values.py index 441905f28b..46afa857aa 100644 --- a/selfdrive/car/body/values.py +++ b/selfdrive/car/body/values.py @@ -1,5 +1,5 @@ from cereal import car -from openpilot.selfdrive.car import PlatformConfig, Platforms, dbc_dict +from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -23,6 +23,7 @@ class CAR(Platforms): BODY = PlatformConfig( "COMMA BODY", CarInfo("comma body", package="All"), + CarSpecs(mass=9, wheelbase=0.406, steerRatio=0.5, centerToFrontRatio=0.44), dbc_dict('comma_body', None), ) @@ -37,5 +38,4 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 339be1912c..b16e2e5a47 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -11,6 +11,7 @@ from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing +from openpilot.selfdrive.car.mock.values import CAR as MOCK from openpilot.common.swaglog import cloudlog import cereal.messaging as messaging from openpilot.selfdrive.car import gen_empty_fingerprint @@ -191,11 +192,16 @@ def fingerprint(logcan, sendcan, num_pandas): fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus, fingerprints=repr(finger), fw_query_time=fw_query_time, error=True) - car_platform = PLATFORMS.get(car_fingerprint, car_fingerprint) + car_platform = PLATFORMS.get(car_fingerprint, MOCK.MOCK) return car_platform, finger, vin, car_fw, source, exact_match +def get_car_interface(CP): + CarInterface, CarController, CarState = interfaces[CP.carFingerprint] + return CarInterface(CP, CarController, CarState) + + def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1): candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas) @@ -203,23 +209,23 @@ def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1): cloudlog.event("car doesn't match any fingerprints", fingerprints=repr(fingerprints), error=True) candidate = "mock" - CarInterface, CarController, CarState = interfaces[candidate] + CarInterface, _, _ = interfaces[candidate] CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False) CP.carVin = vin CP.carFw = car_fw CP.fingerprintSource = source CP.fuzzyFingerprint = not exact_match - return CarInterface(CP, CarController, CarState), CP + return get_car_interface(CP), CP -def write_car_param(fingerprint="mock"): +def write_car_param(platform=MOCK.MOCK): params = Params() - CarInterface, _, _ = interfaces[fingerprint] - CP = CarInterface.get_non_essential_params(fingerprint) + CarInterface, _, _ = interfaces[platform] + CP = CarInterface.get_non_essential_params(platform) params.put("CarParams", CP.to_bytes()) def get_demo_car_params(): - fingerprint="mock" - CarInterface, _, _ = interfaces[fingerprint] - CP = CarInterface.get_non_essential_params(fingerprint) + platform = MOCK.MOCK + CarInterface, _, _ = interfaces[platform] + CP = CarInterface.get_non_essential_params(platform) return CP diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py new file mode 100755 index 0000000000..9231794a6c --- /dev/null +++ b/selfdrive/car/card.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +import os +import time + +import cereal.messaging as messaging + +from cereal import car + +from panda import ALTERNATIVE_EXPERIENCE + +from openpilot.common.params import Params +from openpilot.common.realtime import DT_CTRL + +from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp +from openpilot.selfdrive.car.car_helpers import get_car, get_one_can +from openpilot.selfdrive.car.interfaces import CarInterfaceBase + + +REPLAY = "REPLAY" in os.environ + + +class CarD: + CI: CarInterfaceBase + CS: car.CarState + + def __init__(self, CI=None): + self.can_sock = messaging.sub_sock('can', timeout=20) + self.sm = messaging.SubMaster(['pandaStates']) + self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput']) + + self.can_rcv_timeout_counter = 0 # conseuctive timeout count + self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count + + self.CC_prev = car.CarControl.new_message() + + self.last_actuators = None + + self.params = Params() + + if CI is None: + # wait for one pandaState and one CAN packet + print("Waiting for CAN messages...") + get_one_can(self.can_sock) + + num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) + experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") + self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas) + else: + self.CI, self.CP = CI, CI.CP + + # set alternative experiences from parameters + disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") + self.CP.alternativeExperience = 0 + if not disengage_on_accelerator: + self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS + + car_recognized = self.CP.carName != 'mock' + openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") + + controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly + + self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly + if self.CP.passive: + safety_config = car.CarParams.SafetyConfig.new_message() + safety_config.safetyModel = car.CarParams.SafetyModel.noOutput + self.CP.safetyConfigs = [safety_config] + + # Write previous route's CarParams + prev_cp = self.params.get("CarParamsPersistent") + if prev_cp is not None: + self.params.put("CarParamsPrevRoute", prev_cp) + + # Write CarParams for radard + cp_bytes = self.CP.to_bytes() + self.params.put("CarParams", cp_bytes) + self.params.put_nonblocking("CarParamsCache", cp_bytes) + self.params.put_nonblocking("CarParamsPersistent", cp_bytes) + + def initialize(self): + """Initialize CarInterface, once controls are ready""" + self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) + + def state_update(self): + """carState update loop, driven by can""" + + # Update carState from CAN + can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) + self.CS = self.CI.update(self.CC_prev, can_strs) + + self.sm.update(0) + + can_rcv_valid = len(can_strs) > 0 + + # Check for CAN timeout + if not can_rcv_valid: + self.can_rcv_timeout_counter += 1 + self.can_rcv_cum_timeout_counter += 1 + else: + self.can_rcv_timeout_counter = 0 + + self.can_rcv_timeout = self.can_rcv_timeout_counter >= 5 + + if can_rcv_valid and REPLAY: + self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime + + self.state_publish() + + return self.CS + + def state_publish(self): + """carState and carParams publish loop""" + + # carState + cs_send = messaging.new_message('carState') + cs_send.valid = self.CS.canValid + cs_send.carState = self.CS + self.pm.send('carState', cs_send) + + # carParams - logged every 50 seconds (> 1 per segment) + if (self.sm.frame % int(50. / DT_CTRL) == 0): + cp_send = messaging.new_message('carParams') + cp_send.valid = True + cp_send.carParams = self.CP + self.pm.send('carParams', cp_send) + + # publish new carOutput + co_send = messaging.new_message('carOutput') + co_send.valid = True + if self.last_actuators is not None: + co_send.carOutput.actuatorsOutput = self.last_actuators + self.pm.send('carOutput', co_send) + + def controls_update(self, CC: car.CarControl): + """control update loop, driven by carControl""" + + # send car controls over can + now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) + self.last_actuators, can_sends = self.CI.apply(CC, now_nanos) + self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid)) + + self.CC_prev = CC + diff --git a/selfdrive/car/chrysler/fingerprints.py b/selfdrive/car/chrysler/fingerprints.py index 1df514e79b..3444b4cc5b 100644 --- a/selfdrive/car/chrysler/fingerprints.py +++ b/selfdrive/car/chrysler/fingerprints.py @@ -38,6 +38,7 @@ FW_VERSIONS = { b'68227902AF', b'68227902AG', b'68227902AH', + b'68227905AG', b'68360252AC', ], (Ecu.srs, 0x744, None): [ @@ -71,6 +72,7 @@ FW_VERSIONS = { b'68340762AD ', b'68340764AD ', b'68352652AE ', + b'68352654AE ', b'68366851AH ', b'68366853AE ', b'68372861AF ', @@ -304,6 +306,7 @@ FW_VERSIONS = { b'68402708AB', b'68402971AD', b'68454144AD', + b'68454145AB', b'68454152AB', b'68454156AB', b'68516650AB', diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 8fa7664b66..7dcfa3749e 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -11,6 +11,7 @@ Ecu = car.CarParams.Ecu class ChryslerFlags(IntFlag): + # Detected flags HIGHER_MIN_STEERING_SPEED = 1 @dataclass @@ -21,7 +22,7 @@ class ChryslerCarInfo(CarInfo): @dataclass class ChryslerPlatformConfig(PlatformConfig): - dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None)) + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion')) @dataclass(frozen=True) @@ -34,22 +35,22 @@ class CAR(Platforms): PACIFICA_2017_HYBRID = ChryslerPlatformConfig( "CHRYSLER PACIFICA HYBRID 2017", ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"), - specs=ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2), + ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2), ) PACIFICA_2018_HYBRID = ChryslerPlatformConfig( "CHRYSLER PACIFICA HYBRID 2018", ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"), - specs=PACIFICA_2017_HYBRID.specs, + PACIFICA_2017_HYBRID.specs, ) PACIFICA_2019_HYBRID = ChryslerPlatformConfig( "CHRYSLER PACIFICA HYBRID 2019", ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"), - specs=PACIFICA_2017_HYBRID.specs, + PACIFICA_2017_HYBRID.specs, ) PACIFICA_2018 = ChryslerPlatformConfig( "CHRYSLER PACIFICA 2018", ChryslerCarInfo("Chrysler Pacifica 2017-18"), - specs=PACIFICA_2017_HYBRID.specs, + PACIFICA_2017_HYBRID.specs, ) PACIFICA_2020 = ChryslerPlatformConfig( "CHRYSLER PACIFICA 2020", @@ -57,35 +58,35 @@ class CAR(Platforms): ChryslerCarInfo("Chrysler Pacifica 2019-20"), ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"), ], - specs=PACIFICA_2017_HYBRID.specs, + PACIFICA_2017_HYBRID.specs, ) # Dodge DODGE_DURANGO = ChryslerPlatformConfig( "DODGE DURANGO 2021", ChryslerCarInfo("Dodge Durango 2020-21"), - specs=PACIFICA_2017_HYBRID.specs, + PACIFICA_2017_HYBRID.specs, ) # Jeep JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 Trailhawk "JEEP GRAND CHEROKEE V6 2018", ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), - specs=ChryslerCarSpecs(mass=1778., wheelbase=2.71, steerRatio=16.7), + ChryslerCarSpecs(mass=1778., wheelbase=2.71, steerRatio=16.7), ) JEEP_GRAND_CHEROKEE_2019 = ChryslerPlatformConfig( # includes 2020 Trailhawk "JEEP GRAND CHEROKEE 2019", ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), - specs=JEEP_GRAND_CHEROKEE.specs, + JEEP_GRAND_CHEROKEE.specs, ) # Ram RAM_1500 = ChryslerPlatformConfig( "RAM 1500 5TH GEN", ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])), + ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5), dbc_dict('chrysler_ram_dt_generated', None), - specs=ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5), ) RAM_HD = ChryslerPlatformConfig( "RAM HD 5TH GEN", @@ -93,8 +94,8 @@ class CAR(Platforms): ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])), ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), ], + ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.), dbc_dict('chrysler_ram_hd_generated', None), - specs=ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.), ) @@ -162,5 +163,4 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index caa79a8f87..ce46bd93c2 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -11,6 +11,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr +from openpilot.selfdrive.car.values import PLATFORMS def get_all_footnotes() -> dict[Enum, int]: @@ -27,9 +28,10 @@ CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md") def get_all_car_info() -> list[CarInfo]: all_car_info: list[CarInfo] = [] footnotes = get_all_footnotes() - for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): + for model, platform in PLATFORMS.items(): + car_info = platform.config.car_info # If available, uses experimental longitudinal limits for the docs - CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), + CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True) if CP.dashcamOnly or car_info is None: @@ -37,7 +39,7 @@ def get_all_car_info() -> list[CarInfo]: # A platform can include multiple car models if not isinstance(car_info, list): - car_info = (car_info,) + car_info = [car_info,] for _car_info in car_info: if not hasattr(_car_info, "row"): diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 61c46674db..390325a8ec 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -1,9 +1,9 @@ from cereal import car -from openpilot.common.numpy_fast import clip from opendbc.can.packer import CANPacker +from openpilot.common.numpy_fast import clip from openpilot.selfdrive.car import apply_std_steer_angle_limits from openpilot.selfdrive.car.ford import fordcan -from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams +from openpilot.selfdrive.car.ford.values import CarControllerParams, FordFlags from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX @@ -70,7 +70,7 @@ class CarController(CarControllerBase): self.apply_curvature_last = apply_curvature - if self.CP.carFingerprint in CANFD_CAR: + if self.CP.flags & FordFlags.CANFD: # TODO: extended mode mode = 1 if CC.latActive else 0 counter = (self.frame // CarControllerParams.STEER_STEP) % 0xF diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index 34006e8da4..b3ee6a4649 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -1,10 +1,10 @@ from cereal import car -from openpilot.common.conversions import Conversions as CV from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser -from openpilot.selfdrive.car.interfaces import CarStateBase +from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.ford.fordcan import CanBus -from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC +from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags +from openpilot.selfdrive.car.interfaces import CarStateBase GearShifter = car.CarState.GearShifter TransmissionType = car.CarParams.TransmissionType @@ -19,6 +19,9 @@ class CarState(CarStateBase): self.vehicle_sensors_valid = False + self.prev_distance_button = 0 + self.distance_button = 0 + def update(self, cp, cp_cam): ret = car.CarState.new_message() @@ -49,7 +52,7 @@ class CarState(CarStateBase): ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3) ret.espDisabled = cp.vl["Cluster_Info1_FD1"]["DrvSlipCtlMde_D_Rq"] != 0 # 0 is default mode - if self.CP.carFingerprint in CANFD_CAR: + if self.CP.flags & FordFlags.CANFD: # this signal is always 0 on non-CAN FD cars ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3) @@ -83,6 +86,8 @@ class CarState(CarStateBase): ret.rightBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 2 # TODO: block this going to the camera otherwise it will enable stock TJA ret.genericToggle = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"]) + self.prev_distance_button = self.distance_button + self.distance_button = cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"] # lock info ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"], @@ -91,7 +96,7 @@ class CarState(CarStateBase): # blindspot sensors if self.CP.enableBsm: - cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CAR else cp + cp_bsm = cp_cam if self.CP.flags & FordFlags.CANFD else cp ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0 ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0 @@ -122,7 +127,7 @@ class CarState(CarStateBase): ("RCMStatusMessage2_FD1", 10), ] - if CP.carFingerprint in CANFD_CAR: + if CP.flags & FordFlags.CANFD: messages += [ ("Lane_Assist_Data3_FD1", 33), ] @@ -137,7 +142,7 @@ class CarState(CarStateBase): ("BCM_Lamp_Stat_FD1", 1), ] - if CP.enableBsm and CP.carFingerprint not in CANFD_CAR: + if CP.enableBsm and not (CP.flags & FordFlags.CANFD): messages += [ ("Side_Detect_L_Stat", 5), ("Side_Detect_R_Stat", 5), @@ -155,7 +160,7 @@ class CarState(CarStateBase): ("IPMA_Data", 1), ] - if CP.enableBsm and CP.carFingerprint in CANFD_CAR: + if CP.enableBsm and CP.flags & FordFlags.CANFD: messages += [ ("Side_Detect_L_Stat", 5), ("Side_Detect_R_Stat", 5), diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index fd4c381f88..ed8b010491 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -1,11 +1,12 @@ from cereal import car from panda import Panda from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car import get_safety_config +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.ford.fordcan import CanBus -from openpilot.selfdrive.car.ford.values import CANFD_CAR, Ecu +from openpilot.selfdrive.car.ford.values import Ecu, FordFlags from openpilot.selfdrive.car.interfaces import CarInterfaceBase +ButtonType = car.CarState.ButtonEvent.Type TransmissionType = car.CarParams.TransmissionType GearShifter = car.CarState.GearShifter @@ -14,7 +15,7 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): ret.carName = "ford" - ret.dashcamOnly = candidate in CANFD_CAR + ret.dashcamOnly = bool(ret.flags & FordFlags.CANFD) ret.radarUnavailable = True ret.steerControlType = car.CarParams.SteerControlType.angle @@ -36,7 +37,7 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_LONG_CONTROL ret.openpilotLongitudinalControl = True - if candidate in CANFD_CAR: + if ret.flags & FordFlags.CANFD: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_CANFD # Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1 @@ -61,6 +62,8 @@ class CarInterface(CarInterfaceBase): def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam) + ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) + events = self.create_common_events(ret, extra_gears=[GearShifter.manumatic]) if not self.CS.vehicle_sensors_valid: events.add(car.CarEvent.EventName.vehicleSensorsInvalid) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index cb36232a18..d52189eb9c 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,7 +1,7 @@ import re from collections import defaultdict from dataclasses import dataclass, field -from enum import Enum +from enum import Enum, IntFlag import panda.python.uds as uds from cereal import car @@ -41,6 +41,11 @@ class CarControllerParams: pass +class FordFlags(IntFlag): + # Static flags + CANFD = 1 + + class RADAR: DELPHI_ESR = 'ford_fusion_2018_adas' DELPHI_MRR = 'FORD_CADS' @@ -59,7 +64,7 @@ class FordCarInfo(CarInfo): package: str = "Co-Pilot360 Assist+" def init_make(self, CP: car.CarParams): - harness = CarHarness.ford_q4 if CP.carFingerprint in CANFD_CAR else CarHarness.ford_q3 + harness = CarHarness.ford_q4 if CP.flags & FordFlags.CANFD else CarHarness.ford_q3 if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1, CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1): self.car_parts = CarParts([Device.threex_angled_mount, harness]) else: @@ -71,11 +76,20 @@ class FordPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) +@dataclass +class FordCANFDPlatformConfig(FordPlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', None)) + + def init(self): + super().init() + self.flags |= FordFlags.CANFD + + class CAR(Platforms): BRONCO_SPORT_MK1 = FordPlatformConfig( "FORD BRONCO SPORT 1ST GEN", FordCarInfo("Ford Bronco Sport 2021-22"), - specs=CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7), + CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7), ) ESCAPE_MK4 = FordPlatformConfig( "FORD ESCAPE 4TH GEN", @@ -87,7 +101,7 @@ class CAR(Platforms): FordCarInfo("Ford Kuga Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"), FordCarInfo("Ford Kuga Plug-in Hybrid 2020-22", "Adaptive Cruise Control with Lane Centering"), ], - specs=CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7), + CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7), ) EXPLORER_MK6 = FordPlatformConfig( "FORD EXPLORER 6TH GEN", @@ -97,22 +111,20 @@ class CAR(Platforms): FordCarInfo("Lincoln Aviator 2020-23", "Co-Pilot360 Plus"), FordCarInfo("Lincoln Aviator Plug-in Hybrid 2020-23", "Co-Pilot360 Plus"), # Grand Touring only ], - specs=CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8), + CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8), ) - F_150_MK14 = FordPlatformConfig( + F_150_MK14 = FordCANFDPlatformConfig( "FORD F-150 14TH GEN", [ FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"), FordCarInfo("Ford F-150 Hybrid 2023", "Co-Pilot360 Active 2.0"), ], - dbc_dict=dbc_dict('ford_lincoln_base_pt', None), - specs=CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0), + CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0), ) - F_150_LIGHTNING_MK1 = FordPlatformConfig( + F_150_LIGHTNING_MK1 = FordCANFDPlatformConfig( "FORD F-150 LIGHTNING 1ST GEN", FordCarInfo("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 2.0"), - dbc_dict=dbc_dict('ford_lincoln_base_pt', None), - specs=CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9), + CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9), ) FOCUS_MK4 = FordPlatformConfig( "FORD FOCUS 4TH GEN", @@ -120,7 +132,7 @@ class CAR(Platforms): FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), FordCarInfo("Ford Focus Hybrid 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]), # mHEV only ], - specs=CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0), + CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0), ) MAVERICK_MK1 = FordPlatformConfig( "FORD MAVERICK 1ST GEN", @@ -130,19 +142,15 @@ class CAR(Platforms): FordCarInfo("Ford Maverick 2023", "Co-Pilot360 Assist"), FordCarInfo("Ford Maverick Hybrid 2023", "Co-Pilot360 Assist"), ], - specs=CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0), + CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0), ) - MUSTANG_MACH_E_MK1 = FordPlatformConfig( + MUSTANG_MACH_E_MK1 = FordCANFDPlatformConfig( "FORD MUSTANG MACH-E 1ST GEN", FordCarInfo("Ford Mustang Mach-E 2021-23", "Co-Pilot360 Active 2.0"), - dbc_dict=dbc_dict('ford_lincoln_base_pt', None), - specs=CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio + CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio ) -CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} - - # FW response contains a combined software and part number # A-Z except no I, O or W # e.g. NZ6A-14C204-AAA @@ -300,5 +308,4 @@ FW_QUERY_CONFIG = FwQueryConfig( match_fw_to_car_fuzzy=match_fw_to_car_fuzzy, ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index 80492b4177..236ade49bb 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -47,6 +47,11 @@ class StdQueries: MANUFACTURER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER) + SUPPLIER_SOFTWARE_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ + p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER) + SUPPLIER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ + p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER) + UDS_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) UDS_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 7673814195..c200528ca6 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -1,19 +1,20 @@ #!/usr/bin/env python3 from collections import defaultdict -from typing import Any, TypeVar from collections.abc import Iterator +from typing import Any, Protocol, TypeVar + from tqdm import tqdm import capnp import panda.python.uds as uds from cereal import car from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs -from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig -from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.fingerprints import FW_VERSIONS +from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions +from openpilot.selfdrive.car.interfaces import get_interface_attr from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery -from openpilot.common.swaglog import cloudlog Ecu = car.CarParams.Ecu ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa] @@ -38,8 +39,7 @@ def is_brand(brand: str, filter_brand: str | None) -> bool: return filter_brand is None or brand == filter_brand -def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], - filter_brand: str = None) -> dict[AddrType, set[bytes]]: +def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], filter_brand: str = None) -> dict[AddrType, set[bytes]]: fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set) for fw in fw_versions: if is_brand(fw.brand, filter_brand) and not fw.logging: @@ -48,7 +48,12 @@ def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], return dict(fw_versions_dict) -def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=None): +class MatchFwToCar(Protocol): + def __call__(self, live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True) -> set[str]: + ... + + +def match_fw_to_car_fuzzy(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, exclude: str = None) -> set[str]: """Do a fuzzy FW match. This function will return a match, and the number of firmware version that were matched uniquely to that specific car. If multiple ECUs uniquely match to different cars the match is rejected.""" @@ -73,7 +78,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude= all_fw_versions[(addr[1], addr[2], f)].append(candidate) matched_ecus = set() - candidate = None + match: str | None = None for addr, versions in live_fw_versions.items(): ecu_key = (addr[0], addr[1]) for version in versions: @@ -82,23 +87,23 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude= if len(candidates) == 1: matched_ecus.add(ecu_key) - if candidate is None: - candidate = candidates[0] + if match is None: + match = candidates[0] # We uniquely matched two different cars. No fuzzy match possible - elif candidate != candidates[0]: + elif match != candidates[0]: return set() # Note that it is possible to match to a candidate without all its ECUs being present # if there are enough matches. FIXME: parameterize this or require all ECUs to exist like exact matching - if len(matched_ecus) >= 2: + if match and len(matched_ecus) >= 2: if log: - cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {len(matched_ecus)} matching ECUs") - return {candidate} + cloudlog.error(f"Fingerprinted {match} using fuzzy match. {len(matched_ecus)} matching ECUs") + return {match} else: return set() -def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw_versions=None) -> set[str]: +def match_fw_to_car_exact(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, extra_fw_versions: dict = None) -> set[str]: """Do an exact FW match. Returns all cars that match the given FW versions for a list of "essential" ECUs. If an ECU is not considered essential the FW version can be missing to get a fingerprint, but if it's present it @@ -139,9 +144,10 @@ def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw return set(candidates.keys()) - invalid -def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True): +def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], allow_exact: bool = True, allow_fuzzy: bool = True, + log: bool = True) -> tuple[bool, set[str]]: # Try exact matching first - exact_matches = [] + exact_matches: list[tuple[bool, MatchFwToCar]] = [] if allow_exact: exact_matches = [(True, match_fw_to_car_exact)] if allow_fuzzy: @@ -149,7 +155,7 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True): for exact_match, match_func in exact_matches: # For each brand, attempt to fingerprint using all FW returned from its queries - matches = set() + matches: set[str] = set() for brand in VERSIONS.keys(): fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand) matches |= match_func(fw_versions_dict, match_brand=brand, log=log) @@ -165,12 +171,12 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True): return True, set() -def get_present_ecus(logcan, sendcan, num_pandas=1) -> set[EcuAddrBusType]: +def get_present_ecus(logcan, sendcan, num_pandas: int = 1) -> set[EcuAddrBusType]: params = Params() # queries are split by OBD multiplexing mode queries: dict[bool, list[list[EcuAddrBusType]]] = {True: [], False: []} parallel_queries: dict[bool, list[EcuAddrBusType]] = {True: [], False: []} - responses = set() + responses: set[EcuAddrBusType] = set() for brand, config, r in REQUESTS: # Skip query if no panda available @@ -231,8 +237,8 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool): cloudlog.warning("OBD multiplexing set successfully") -def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ - list[capnp.lib.capnp._DynamicStructBuilder]: +def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1, + debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]: """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" all_car_fw = [] @@ -254,8 +260,8 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand return all_car_fw -def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ - list[capnp.lib.capnp._DynamicStructBuilder]: +def get_fw_versions(logcan, sendcan, query_brand: str = None, extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, + debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]: versions = VERSIONS.copy() params = Params() diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index e0dde4d0e9..76f6a6b0a6 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -145,15 +145,12 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]] ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594 ret.steerActuatorDelay = 0.1 # Default delay, not measured yet - ret.tireStiffnessFactor = 0.444 # not optimized yet ret.steerLimitTimer = 0.4 ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking if candidate == CAR.VOLT: - ret.tireStiffnessFactor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters - ret.lateralTuning.pid.kpBP = [0., 40.] ret.lateralTuning.pid.kpV = [0., 0.17] ret.lateralTuning.pid.kiBP = [0.] @@ -175,7 +172,6 @@ class CarInterface(CarInterfaceBase): elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019): ret.minEnableSpeed = -1. # engage speed is decided by pcm - ret.tireStiffnessFactor = 1.0 if candidate == CAR.ESCALADE_ESV: ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]] @@ -186,12 +182,10 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.BOLT_EUV: - ret.tireStiffnessFactor = 1.0 ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.SILVERADO: - ret.tireStiffnessFactor = 1.0 # On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop # with foot on brake to allow engagement, but this platform only has that check in the camera. # TODO: check if this is split by EV/ICE with more platforms in the future @@ -203,7 +197,6 @@ class CarInterface(CarInterfaceBase): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.TRAILBLAZER: - ret.tireStiffnessFactor = 1.0 ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 59b4b6ea30..5401963ee6 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -79,6 +79,11 @@ class GMCarInfo(CarInfo): self.footnotes.append(Footnote.OBD_II) +@dataclass(frozen=True, kw_only=True) +class GMCarSpecs(CarSpecs): + tireStiffnessFactor: float = 0.444 # not optimized yet + + @dataclass class GMPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) @@ -88,52 +93,52 @@ class CAR(Platforms): HOLDEN_ASTRA = GMPlatformConfig( "HOLDEN ASTRA RS-V BK 2017", GMCarInfo("Holden Astra 2017"), - specs=CarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4), + GMCarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4), ) VOLT = GMPlatformConfig( "CHEVROLET VOLT PREMIER 2017", GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"), - specs=CarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45), + GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469), ) CADILLAC_ATS = GMPlatformConfig( "CADILLAC ATS Premium Performance 2018", GMCarInfo("Cadillac ATS Premium Performance 2018"), - specs=CarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3), + GMCarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3), ) MALIBU = GMPlatformConfig( "CHEVROLET MALIBU PREMIER 2017", GMCarInfo("Chevrolet Malibu Premier 2017"), - specs=CarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4), + GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4), ) ACADIA = GMPlatformConfig( "GMC ACADIA DENALI 2018", GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), - specs=CarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4), + GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4), ) BUICK_LACROSSE = GMPlatformConfig( "BUICK LACROSSE 2017", GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"), - specs=CarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4), + GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4), ) BUICK_REGAL = GMPlatformConfig( "BUICK REGAL ESSENCE 2018", GMCarInfo("Buick Regal Essence 2018"), - specs=CarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4), + GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4), ) ESCALADE = GMPlatformConfig( "CADILLAC ESCALADE 2017", GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), - specs=CarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3), + GMCarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3), ) ESCALADE_ESV = GMPlatformConfig( "CADILLAC ESCALADE ESV 2016", GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), - specs=CarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3), + GMCarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3, tireStiffnessFactor=1.0), ) ESCALADE_ESV_2019 = GMPlatformConfig( "CADILLAC ESCALADE ESV 2019", GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"), - specs=ESCALADE_ESV.specs, + ESCALADE_ESV.specs, ) BOLT_EUV = GMPlatformConfig( "CHEVROLET BOLT EUV 2022", @@ -141,7 +146,7 @@ class CAR(Platforms): GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"), GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"), ], - specs=CarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4), + GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0), ) SILVERADO = GMPlatformConfig( "CHEVROLET SILVERADO 1500 2020", @@ -149,17 +154,17 @@ class CAR(Platforms): GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"), GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"), ], - specs=CarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3), + GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0), ) EQUINOX = GMPlatformConfig( "CHEVROLET EQUINOX 2019", GMCarInfo("Chevrolet Equinox 2019-22"), - specs=CarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4), + GMCarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4), ) TRAILBLAZER = GMPlatformConfig( "CHEVROLET TRAILBLAZER 2021", GMCarInfo("Chevrolet Trailblazer 2021-22"), - specs=CarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4), + GMCarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0), ) @@ -242,5 +247,4 @@ CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER} STEER_THRESHOLD = 1.0 -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/honda/fingerprints.py b/selfdrive/car/honda/fingerprints.py index 0ae39751f9..a842baac88 100644 --- a/selfdrive/car/honda/fingerprints.py +++ b/selfdrive/car/honda/fingerprints.py @@ -805,6 +805,7 @@ FW_VERSIONS = { b'37805-5MR-3250\x00\x00', b'37805-5MR-4070\x00\x00', b'37805-5MR-4080\x00\x00', + b'37805-5MR-4170\x00\x00', b'37805-5MR-4180\x00\x00', b'37805-5MR-A240\x00\x00', b'37805-5MR-A250\x00\x00', diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index a4cf647c0f..f791d4b639 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -92,10 +92,6 @@ class CarInterface(CarInterfaceBase): eps_modified = True if candidate == CAR.CIVIC: - ret.mass = 1326. - ret.wheelbase = 2.70 - ret.centerToFront = ret.wheelbase * 0.4 - ret.steerRatio = 15.38 # 10.93 is end-to-end spec if eps_modified: # stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE # stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680 @@ -110,20 +106,11 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]] elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022): - ret.mass = 1326. - ret.wheelbase = 2.70 - ret.centerToFront = ret.wheelbase * 0.4 - ret.steerRatio = 15.38 # 10.93 is end-to-end spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate == CAR.ACCORD: - ret.mass = 3279. * CV.LB_TO_KG - ret.wheelbase = 2.83 - ret.centerToFront = ret.wheelbase * 0.39 - ret.steerRatio = 16.33 # 11.82 is spec end-to-end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.8467 if eps_modified: ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]] @@ -131,29 +118,15 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] elif candidate == CAR.ACURA_ILX: - ret.mass = 3095. * CV.LB_TO_KG - ret.wheelbase = 2.67 - ret.centerToFront = ret.wheelbase * 0.37 - ret.steerRatio = 18.61 # 15.3 is spec end-to-end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.72 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate in (CAR.CRV, CAR.CRV_EU): - ret.mass = 3572. * CV.LB_TO_KG - ret.wheelbase = 2.62 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 16.89 # as spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.444 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.wheelSpeedFactor = 1.025 elif candidate == CAR.CRV_5G: - ret.mass = 3410. * CV.LB_TO_KG - ret.wheelbase = 2.66 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 16.0 # 12.3 is spec end-to-end if eps_modified: # stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F # stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400 @@ -163,45 +136,23 @@ class CarInterface(CarInterfaceBase): else: ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]] - ret.tireStiffnessFactor = 0.677 ret.wheelSpeedFactor = 1.025 elif candidate == CAR.CRV_HYBRID: - ret.mass = 1667. # mean of 4 models in kg - ret.wheelbase = 2.66 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 16.0 # 12.3 is spec end-to-end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.677 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] ret.wheelSpeedFactor = 1.025 elif candidate == CAR.FIT: - ret.mass = 2644. * CV.LB_TO_KG - ret.wheelbase = 2.53 - ret.centerToFront = ret.wheelbase * 0.39 - ret.steerRatio = 13.06 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.75 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]] elif candidate == CAR.FREED: - ret.mass = 3086. * CV.LB_TO_KG - ret.wheelbase = 2.74 - # the remaining parameters were copied from FIT - ret.centerToFront = ret.wheelbase * 0.39 - ret.steerRatio = 13.06 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] - ret.tireStiffnessFactor = 0.75 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]] elif candidate in (CAR.HRV, CAR.HRV_3G): - ret.mass = 3125 * CV.LB_TO_KG - ret.wheelbase = 2.61 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 15.2 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] - ret.tireStiffnessFactor = 0.5 if candidate == CAR.HRV: ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.025]] ret.wheelSpeedFactor = 1.025 @@ -209,29 +160,14 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] # TODO: can probably use some tuning elif candidate == CAR.ACURA_RDX: - ret.mass = 3935. * CV.LB_TO_KG - ret.wheelbase = 2.68 - ret.centerToFront = ret.wheelbase * 0.38 - ret.steerRatio = 15.0 # as spec - ret.tireStiffnessFactor = 0.444 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate == CAR.ACURA_RDX_3G: - ret.mass = 4068. * CV.LB_TO_KG - ret.wheelbase = 2.75 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 11.95 # as spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]] - ret.tireStiffnessFactor = 0.677 elif candidate in (CAR.ODYSSEY, CAR.ODYSSEY_CHN): - ret.mass = 1900. - ret.wheelbase = 3.00 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 14.35 # as spec - ret.tireStiffnessFactor = 0.82 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]] if candidate == CAR.ODYSSEY_CHN: ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]] # TODO: determine if there is a dead zone at the top end @@ -239,47 +175,30 @@ class CarInterface(CarInterfaceBase): ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end elif candidate == CAR.PILOT: - ret.mass = 4278. * CV.LB_TO_KG # average weight - ret.wheelbase = 2.86 - ret.centerToFront = ret.wheelbase * 0.428 - ret.steerRatio = 16.0 # as spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.444 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] elif candidate == CAR.RIDGELINE: - ret.mass = 4515. * CV.LB_TO_KG - ret.wheelbase = 3.18 - ret.centerToFront = ret.wheelbase * 0.41 - ret.steerRatio = 15.59 # as spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.444 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] elif candidate == CAR.INSIGHT: - ret.mass = 2987. * CV.LB_TO_KG - ret.wheelbase = 2.7 - ret.centerToFront = ret.wheelbase * 0.39 - ret.steerRatio = 15.0 # 12.58 is spec end-to-end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.82 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] elif candidate == CAR.HONDA_E: - ret.mass = 3338.8 * CV.LB_TO_KG - ret.wheelbase = 2.5 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 16.71 ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - ret.tireStiffnessFactor = 0.82 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning else: raise ValueError(f"unsupported car {candidate}") # These cars use alternate user brake msg (0x1BE) - if 0x1BE in fingerprint[CAN.pt] and candidate in HONDA_BOSCH: + # TODO: Only detect feature for Accord/Accord Hybrid, not all Bosch DBCs have BRAKE_MODULE + if 0x1BE in fingerprint[CAN.pt] and candidate == CAR.ACCORD: ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value + + if ret.flags & HondaFlags.BOSCH_ALT_BRAKE: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE # These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index ce4a531d01..4960380bbc 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1,10 +1,10 @@ from dataclasses import dataclass -from enum import Enum, IntFlag, StrEnum +from enum import Enum, IntFlag from cereal import car from openpilot.common.conversions import Conversions as CV from panda.python import uds -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -46,10 +46,19 @@ class CarControllerParams: class HondaFlags(IntFlag): + # Detected flags # Bosch models with alternate set of LKAS_HUD messages BOSCH_EXT_HUD = 1 BOSCH_ALT_BRAKE = 2 + # Static flags + BOSCH = 4 + BOSCH_RADARLESS = 8 + + NIDEC = 16 + NIDEC_ALT_PCM_ACCEL = 32 + NIDEC_ALT_SCM_MESSAGES = 64 + # Car button codes class CruiseButtons: @@ -72,86 +81,202 @@ VISUAL_HUD = { } -class CAR(StrEnum): - ACCORD = "HONDA ACCORD 2018" - CIVIC = "HONDA CIVIC 2016" - CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" - CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" - CIVIC_2022 = "HONDA CIVIC 2022" - ACURA_ILX = "ACURA ILX 2016" - CRV = "HONDA CR-V 2016" - CRV_5G = "HONDA CR-V 2017" - CRV_EU = "HONDA CR-V EU 2016" - CRV_HYBRID = "HONDA CR-V HYBRID 2019" - FIT = "HONDA FIT 2018" - FREED = "HONDA FREED 2020" - HRV = "HONDA HRV 2019" - HRV_3G = "HONDA HR-V 2023" - ODYSSEY = "HONDA ODYSSEY 2018" - ODYSSEY_CHN = "HONDA ODYSSEY CHN 2019" - ACURA_RDX = "ACURA RDX 2018" - ACURA_RDX_3G = "ACURA RDX 2020" - PILOT = "HONDA PILOT 2017" - RIDGELINE = "HONDA RIDGELINE 2017" - INSIGHT = "HONDA INSIGHT 2019" - HONDA_E = "HONDA E 2020" - - -class Footnote(Enum): - CIVIC_DIESEL = CarFootnote( - "2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.", - Column.FSR_STEERING) - - @dataclass class HondaCarInfo(CarInfo): package: str = "Honda Sensing" def init_make(self, CP: car.CarParams): - if CP.carFingerprint in HONDA_BOSCH: - self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.carFingerprint in HONDA_BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a]) + if CP.flags & HondaFlags.BOSCH: + self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.flags & HondaFlags.BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a]) else: self.car_parts = CarParts.common([CarHarness.nidec]) -CAR_INFO: dict[str, HondaCarInfo | list[HondaCarInfo] | None] = { - CAR.ACCORD: [ - HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS), - HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS), - 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 Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS), - ], - CAR.CIVIC_BOSCH_DIESEL: None, # same platform - CAR.CIVIC_2022: [ - HondaCarInfo("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"), - HondaCarInfo("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"), - ], - CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS), - CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring - CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-20", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.HRV_3G: HondaCarInfo("Honda HR-V 2023", "All"), - CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20"), - CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey - CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), - CAR.PILOT: [ - HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS), - HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS), - ], - CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), - CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS), -} +class Footnote(Enum): + CIVIC_DIESEL = CarFootnote( + "2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.", + Column.FSR_STEERING) + + +class HondaBoschPlatformConfig(PlatformConfig): + def init(self): + self.flags |= HondaFlags.BOSCH + + +class HondaNidecPlatformConfig(PlatformConfig): + def init(self): + self.flags |= HondaFlags.NIDEC + + +class CAR(Platforms): + # Bosch Cars + ACCORD = HondaBoschPlatformConfig( + "HONDA ACCORD 2018", + [ + HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS), + HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS), + HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), + ], + # steerRatio: 11.82 is spec end-to-end + CarSpecs(mass=3279 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=16.33, centerToFrontRatio=0.39, tireStiffnessFactor=0.8467), + dbc_dict('honda_accord_2018_can_generated', None), + ) + CIVIC_BOSCH = HondaBoschPlatformConfig( + "HONDA CIVIC (BOSCH) 2019", + [ + 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), + ], + CarSpecs(mass=1326, wheelbase=2.7, steerRatio=15.38, centerToFrontRatio=0.4), # steerRatio: 10.93 is end-to-end spec + dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None), + ) + CIVIC_BOSCH_DIESEL = HondaBoschPlatformConfig( + "HONDA CIVIC SEDAN 1.6 DIESEL 2019", + None, # don't show in docs + CIVIC_BOSCH.specs, + dbc_dict('honda_accord_2018_can_generated', None), + ) + CIVIC_2022 = HondaBoschPlatformConfig( + "HONDA CIVIC 2022", + [ + HondaCarInfo("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"), + HondaCarInfo("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"), + ], + CIVIC_BOSCH.specs, + dbc_dict('honda_civic_ex_2022_can_generated', None), + flags=HondaFlags.BOSCH_RADARLESS, + ) + CRV_5G = HondaBoschPlatformConfig( + "HONDA CR-V 2017", + HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS), + # steerRatio: 12.3 is spec end-to-end + CarSpecs(mass=3410 * CV.LB_TO_KG, wheelbase=2.66, steerRatio=16.0, centerToFrontRatio=0.41, tireStiffnessFactor=0.677), + dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'), + flags=HondaFlags.BOSCH_ALT_BRAKE, + ) + CRV_HYBRID = HondaBoschPlatformConfig( + "HONDA CR-V HYBRID 2019", + HondaCarInfo("Honda CR-V Hybrid 2017-20", min_steer_speed=12. * CV.MPH_TO_MS), + # mass: mean of 4 models in kg, steerRatio: 12.3 is spec end-to-end + CarSpecs(mass=1667, wheelbase=2.66, steerRatio=16, centerToFrontRatio=0.41, tireStiffnessFactor=0.677), + dbc_dict('honda_accord_2018_can_generated', None), + ) + HRV_3G = HondaBoschPlatformConfig( + "HONDA HR-V 2023", + HondaCarInfo("Honda HR-V 2023", "All"), + CarSpecs(mass=3125 * CV.LB_TO_KG, wheelbase=2.61, steerRatio=15.2, centerToFrontRatio=0.41, tireStiffnessFactor=0.5), + dbc_dict('honda_civic_ex_2022_can_generated', None), + flags=HondaFlags.BOSCH_RADARLESS | HondaFlags.BOSCH_ALT_BRAKE, + ) + ACURA_RDX_3G = HondaBoschPlatformConfig( + "ACURA RDX 2020", + HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), + CarSpecs(mass=4068 * CV.LB_TO_KG, wheelbase=2.75, steerRatio=11.95, centerToFrontRatio=0.41, tireStiffnessFactor=0.677), # as spec + dbc_dict('acura_rdx_2020_can_generated', None), + flags=HondaFlags.BOSCH_ALT_BRAKE, + ) + INSIGHT = HondaBoschPlatformConfig( + "HONDA INSIGHT 2019", + HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), + CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.0, centerToFrontRatio=0.39, tireStiffnessFactor=0.82), # as spec + dbc_dict('honda_insight_ex_2019_can_generated', None), + ) + HONDA_E = HondaBoschPlatformConfig( + "HONDA E 2020", + HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS), + CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82), + dbc_dict('acura_rdx_2020_can_generated', None), + ) + + # Nidec Cars + ACURA_ILX = HondaNidecPlatformConfig( + "ACURA ILX 2016", + HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS), + CarSpecs(mass=3095 * CV.LB_TO_KG, wheelbase=2.67, steerRatio=18.61, centerToFrontRatio=0.37, tireStiffnessFactor=0.72), # 15.3 is spec end-to-end + dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + CRV = HondaNidecPlatformConfig( + "HONDA CR-V 2016", + HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS), + CarSpecs(mass=3572 * CV.LB_TO_KG, wheelbase=2.62, steerRatio=16.89, centerToFrontRatio=0.41, tireStiffnessFactor=0.444), # as spec + dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + CRV_EU = HondaNidecPlatformConfig( + "HONDA CR-V EU 2016", + None, # Euro version of CRV Touring, don't show in docs + CRV.specs, + dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + FIT = HondaNidecPlatformConfig( + "HONDA FIT 2018", + HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS), + CarSpecs(mass=2644 * CV.LB_TO_KG, wheelbase=2.53, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75), + dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + FREED = HondaNidecPlatformConfig( + "HONDA FREED 2020", + HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS), + CarSpecs(mass=3086. * CV.LB_TO_KG, wheelbase=2.74, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75), # mostly copied from FIT + dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + HRV = HondaNidecPlatformConfig( + "HONDA HRV 2019", + HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS), + HRV_3G.specs, + dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + ODYSSEY = HondaNidecPlatformConfig( + "HONDA ODYSSEY 2018", + HondaCarInfo("Honda Odyssey 2018-20"), + CarSpecs(mass=1900, wheelbase=3.0, steerRatio=14.35, centerToFrontRatio=0.41, tireStiffnessFactor=0.82), + dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_PCM_ACCEL, + ) + ODYSSEY_CHN = HondaNidecPlatformConfig( + "HONDA ODYSSEY CHN 2019", + None, # Chinese version of Odyssey, don't show in docs + ODYSSEY.specs, + dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + ACURA_RDX = HondaNidecPlatformConfig( + "ACURA RDX 2018", + HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS), + CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec + dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + PILOT = HondaNidecPlatformConfig( + "HONDA PILOT 2017", + [ + HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS), + HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS), + ], + CarSpecs(mass=4278 * CV.LB_TO_KG, wheelbase=2.86, centerToFrontRatio=0.428, steerRatio=16.0, tireStiffnessFactor=0.444), # as spec + dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + RIDGELINE = HondaNidecPlatformConfig( + "HONDA RIDGELINE 2017", + HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS), + CarSpecs(mass=4515 * CV.LB_TO_KG, wheelbase=3.18, centerToFrontRatio=0.41, steerRatio=15.59, tireStiffnessFactor=0.444), # as spec + dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), + flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES, + ) + CIVIC = HondaNidecPlatformConfig( + "HONDA CIVIC 2016", + HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"), + CarSpecs(mass=1326, wheelbase=2.70, centerToFrontRatio=0.4, steerRatio=15.38), # 10.93 is end-to-end spec + dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'), + ) + HONDA_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ p16(0xF112) @@ -216,41 +341,16 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) - -DBC = { - CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None), - CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'), - CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None), - CAR.CIVIC: dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.CIVIC_BOSCH: dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None), - CAR.CIVIC_BOSCH_DIESEL: dbc_dict('honda_accord_2018_can_generated', None), - CAR.CRV: dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.CRV_5G: dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'), - CAR.CRV_EU: dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.CRV_HYBRID: dbc_dict('honda_accord_2018_can_generated', None), - CAR.FIT: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), - CAR.FREED: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), - CAR.HRV: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), - CAR.HRV_3G: dbc_dict('honda_civic_ex_2022_can_generated', None), - CAR.ODYSSEY: dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'), - CAR.ODYSSEY_CHN: dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'), - CAR.PILOT: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None), - CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None), - CAR.CIVIC_2022: dbc_dict('honda_civic_ex_2022_can_generated', None), -} - STEER_THRESHOLD = { # default is 1200, overrides go here CAR.ACURA_RDX: 400, CAR.CRV_EU: 400, } -HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY} -HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, - CAR.PILOT, CAR.RIDGELINE} -HONDA_BOSCH = {CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, - CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G} -HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G} +HONDA_NIDEC_ALT_PCM_ACCEL = CAR.with_flags(HondaFlags.NIDEC_ALT_PCM_ACCEL) +HONDA_NIDEC_ALT_SCM_MESSAGES = CAR.with_flags(HondaFlags.NIDEC_ALT_SCM_MESSAGES) +HONDA_BOSCH = CAR.with_flags(HondaFlags.BOSCH) +HONDA_BOSCH_RADARLESS = CAR.with_flags(HondaFlags.BOSCH_RADARLESS) + + +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index b0851abf55..ee7f441227 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -135,7 +135,7 @@ class CarController(CarControllerBase): # button presses can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False)) else: - can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req, + can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, apply_steer_req, torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, left_lane_warning, right_lane_warning)) @@ -175,12 +175,12 @@ class CarController(CarControllerBase): can_sends = [] if use_clu11: if CC.cruiseControl.cancel: - can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint)) + can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP)) elif CC.cruiseControl.resume: # send resume at a max freq of 10Hz if (self.frame - self.last_button_frame) * DT_CTRL > 0.1: # send 25 messages at a time to increases the likelihood of resume being accepted - can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25) + can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25) if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15: self.last_button_frame = self.frame else: diff --git a/selfdrive/car/hyundai/fingerprints.py b/selfdrive/car/hyundai/fingerprints.py index d1fc1faabb..6349318fbf 100644 --- a/selfdrive/car/hyundai/fingerprints.py +++ b/selfdrive/car/hyundai/fingerprints.py @@ -5,21 +5,6 @@ from openpilot.selfdrive.car.hyundai.values import CAR Ecu = car.CarParams.Ecu FINGERPRINTS = { - CAR.HYUNDAI_GENESIS: [{ - 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4 - }, - { - 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1378: 4, 1379: 8, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4 - }, - { - 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 912: 7, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1268: 8, 1280: 1, 1281: 3, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1437: 8, 1456: 4 - }, - { - 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1378: 4, 1379: 8, 1384: 5, 1407: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4 - }, - { - 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4 - }], CAR.SANTA_FE: [{ 67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1379: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8 }, @@ -32,33 +17,15 @@ FINGERPRINTS = { CAR.SONATA: [{ 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 546: 8, 549: 8, 550: 8, 576: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 8, 865: 8, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 908: 8, 909: 8, 912: 7, 913: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1089: 5, 1096: 8, 1107: 5, 1108: 8, 1114: 8, 1136: 8, 1145: 8, 1151: 8, 1155: 8, 1156: 8, 1157: 4, 1162: 8, 1164: 8, 1168: 8, 1170: 8, 1173: 8, 1180: 8, 1183: 8, 1184: 8, 1186: 2, 1191: 2, 1193: 8, 1210: 8, 1225: 8, 1227: 8, 1265: 4, 1268: 8, 1280: 8, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1330: 8, 1339: 8, 1342: 6, 1343: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1379: 8, 1384: 8, 1394: 8, 1407: 8, 1419: 8, 1427: 6, 1446: 8, 1456: 4, 1460: 8, 1470: 8, 1485: 8, 1504: 3, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8, 2015: 8 }], - CAR.SONATA_LF: [{ - 66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 447: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 832: 8, 884: 8, 897: 8, 899: 8, 902: 8, 903: 6, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1151: 6, 1168: 7, 1170: 8, 1253: 8, 1254: 8, 1255: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1342: 6, 1345: 8, 1348: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1365: 8, 1366: 8, 1367: 8, 1369: 8, 1397: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1470: 8, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2008: 8, 2009: 8, 2012: 8, 2013: 8, 2014: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 - }], - CAR.KIA_SORENTO: [{ - 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1384: 8, 1407: 8, 1411: 8, 1419: 8, 1425: 2, 1427: 6, 1444: 8, 1456: 4, 1470: 8, 1489: 1 - }], CAR.KIA_STINGER: [{ 67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 576: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 4, 1379: 8, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1456: 4, 1470: 8 }], - CAR.GENESIS_G80: [{ - 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1024: 2, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1191: 2, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1456: 4, 1470: 8 - }, - { - 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 546: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1157: 4, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1437: 8, 1456: 4, 1470: 8 - }, - { - 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1156: 8, 1157: 4, 1162: 8, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1193: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1437: 8, 1456: 4, 1470: 8 - }], CAR.GENESIS_G90: [{ 67: 8, 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 358: 6, 359: 8, 544: 8, 593: 8, 608: 8, 688: 5, 809: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1162: 4, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1281: 3, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 8, 1407: 8, 1419: 8, 1425: 2, 1427: 6, 1434: 2, 1456: 4, 1470: 8, 1988: 8, 2000: 8, 2003: 8, 2004: 8, 2005: 8, 2008: 8, 2011: 8, 2012: 8, 2013: 8 }], CAR.IONIQ_EV_2020: [{ 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 524: 8, 544: 7, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1164: 8, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1379: 8, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1456: 4, 1470: 8, 1473: 8, 1507: 8, 1535: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8, 2013: 8 }], - CAR.IONIQ: [{ - 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 524: 8, 544: 8, 576: 8, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 6, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1164: 8, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1379: 8, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1448: 8, 1456: 4, 1470: 8, 1473: 8, 1476: 8, 1507: 8, 1535: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8, 2013: 8 - }], CAR.KONA_EV: [{ 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 544: 8, 549: 8, 593: 8, 688: 5, 832: 8, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 8, 1151: 6, 1168: 7, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1225: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1307: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1378: 4, 1407: 8, 1419: 8, 1426: 8, 1427: 6, 1429: 8, 1430: 8, 1456: 4, 1470: 8, 1473: 8, 1507: 8, 1535: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8, 1157: 4, 1193: 8, 1379: 8, 1988: 8, 1996: 8 }], @@ -74,9 +41,6 @@ FINGERPRINTS = { { 68: 8, 127: 8, 304: 8, 320: 8, 339: 8, 352: 8, 356: 4, 544: 8, 576: 8, 593: 8, 688: 5, 881: 8, 882: 8, 897: 8, 902: 8, 903: 8, 909: 8, 912: 7, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1136: 6, 1151: 6, 1168: 7, 1173: 8, 1180: 8, 1186: 2, 1191: 2, 1265: 4, 1268: 8, 1280: 1, 1287: 4, 1290: 8, 1291: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1355: 8, 1363: 8, 1369: 8, 1371: 8, 1407: 8, 1419: 8, 1420: 8, 1425: 2, 1427: 6, 1429: 8, 1430: 8, 1448: 8, 1456: 4, 1470: 8, 1476: 8, 1535: 8 }], - CAR.PALISADE: [{ - 67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 546: 8, 547: 8, 548: 8, 549: 8, 576: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 913: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1123: 8, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1157: 4, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1180: 8, 1186: 2, 1191: 2, 1193: 8, 1210: 8, 1225: 8, 1227: 8, 1265: 4, 1280: 8, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1384: 8, 1407: 8, 1419: 8, 1427: 6, 1456: 4, 1470: 8, 1988: 8, 1996: 8, 2000: 8, 2004: 8, 2005: 8, 2008: 8, 2012: 8 - }], } FW_VERSIONS = { @@ -100,15 +64,18 @@ FW_VERSIONS = { CAR.AZERA_HEV_6TH_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.00 99211-G8000 180903', + b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.01 99211-G8000 181109', b'\xf1\x00IGH MFC AT KOR LHD 1.00 1.02 99211-G8100 191029', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00IG MDPS C 1.00 1.00 56310M9600\x00 4IHSC100', b'\xf1\x00IG MDPS C 1.00 1.01 56310M9350\x00 4IH8C101', + b'\xf1\x00IG MDPS C 1.00 1.02 56310M9350\x00 4IH8C102', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00IGhe SCC FHCUP 1.00 1.00 99110-M9100 ', b'\xf1\x00IGhe SCC FHCUP 1.00 1.01 99110-M9000 ', + b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x006T7N0_C2\x00\x006T7Q2051\x00\x00TIG2H24KA2\x12@\x11\xb7', @@ -413,6 +380,7 @@ FW_VERSIONS = { ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24NL1\xb0\x9f\xee\xf5', + b'\xf1\x006T6H0_C2\x00\x006T6B7051\x00\x00TLF0G24SL4;\x08\x12i', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2\x00\x00\x00\x00', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm', b'\xf1\x87LAJSG49645724HF0\x87x\x87\x88\x87www\x88\x99\xa8\x89\x88\x99\xa8\x89\x88\x99\xa8\x89S_\xfb\xff\x87f\x7f\xff^2\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm', @@ -724,6 +692,7 @@ FW_VERSIONS = { (Ecu.abs, 0x7d1, None): [ b'\xf1\x00LX ESC \x01 103\x19\t\x10 58910-S8360', b'\xf1\x00LX ESC \x01 1031\t\x10 58910-S8360', + b'\xf1\x00LX ESC \x01 104 \x10\x16 58910-S8360', b'\xf1\x00LX ESC \x0b 101\x19\x03\x17 58910-S8330', b'\xf1\x00LX ESC \x0b 102\x19\x05\x07 58910-S8330', b'\xf1\x00LX ESC \x0b 103\x19\t\x07 58910-S8330', @@ -745,10 +714,12 @@ FW_VERSIONS = { b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8000 4LXDC103', b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8020 4LXDC103', b'\xf1\x00LX2 MDPS C 1.00 1.04 56310-S8020 4LXDC104', + b'\xf1\x00LX2 MDPS R 1.00 1.02 56370-S8300 9318', b'\xf1\x00ON MDPS C 1.00 1.00 56340-S9000 8B13', b'\xf1\x00ON MDPS C 1.00 1.01 56340-S9000 9201', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00LX2 MFC AT KOR LHD 1.00 1.08 99211-S8100 200903', b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.00 99211-S8110 210226', b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.03 99211-S8100 190125', b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.05 99211-S8100 190909', @@ -762,6 +733,7 @@ FW_VERSIONS = { b'\xf1\x00bcsh8p54 U872\x00\x00\x00\x00\x00\x00TON4G38NB1\x96z28', b'\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX4G38NB3X\xa8\xc08', b'\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00TON4G38NB2[v\\\xb6', + b'\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX2G38NB5X\xfa\xe88', b'\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00TON2G38NB5j\x94.\xde', b'\xf1\x87LBLUFN591307KF25vgvw\x97wwwy\x99\xa7\x99\x99\xaa\xa9\x9af\x88\x96h\x95o\xf7\xff\x99f/\xff\xe4c\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB2\xd7\xc1/\xd1', b"\xf1\x87LBLUFN622950KF36\xa8\x88\x88\x88\x87w\x87xh\x99\x96\x89\x88\x99\x98\x89\x88\x99\x98\x89\x87o\xf6\xff\x98\x88o\xffx'\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB3\xd1\xc3\xf8\xa8", @@ -890,6 +862,7 @@ FW_VERSIONS = { CAR.GENESIS_G80: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00DH__ SCC F-CUP 1.00 1.01 96400-B1120 ', + b'\xf1\x00DH__ SCC F-CUP 1.00 1.02 96400-B1120 ', b'\xf1\x00DH__ SCC FHCUP 1.00 1.01 96400-B1110 ', ], (Ecu.fwdCamera, 0x7c4, None): [ @@ -897,10 +870,12 @@ FW_VERSIONS = { b'\xf1\x00DH LKAS AT USA LHD 1.01 1.01 95895-B1500 161014', b'\xf1\x00DH LKAS AT USA LHD 1.01 1.02 95895-B1500 170810', b'\xf1\x00DH LKAS AT USA LHD 1.01 1.03 95895-B1500 180713', + b'\xf1\x00DH LKAS AT USA LHD 1.01 1.04 95895-B1500 181213', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G33KH2\xae\xde\xd5!', b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G38NH2j\x9dA\x1c', + b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G38NH3\xaf\x1a7\xe2', b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0T33NH3\x97\xe6\xbc\xb8', b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00TDH0G38NH3:-\xa9n', b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SDH0T33NH4\xd7O\x9e\xc9', @@ -1089,6 +1064,7 @@ FW_VERSIONS = { ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00OS MDPS C 1.00 1.03 56310/K4550 4OEDC103', + b'\xf1\x00OS MDPS C 1.00 1.04 56310-XX000 4OEDC104', b'\xf1\x00OS MDPS C 1.00 1.04 56310K4000\x00 4OEDC104', b'\xf1\x00OS MDPS C 1.00 1.04 56310K4050\x00 4OEDC104', ], @@ -1176,11 +1152,13 @@ FW_VERSIONS = { }, CAR.KIA_NIRO_PHEV: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x816H6D0051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6F6051\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006U3H0_C2\x00\x006U3G0051\x00\x00HDE0G16NS2\x00\x00\x00\x00', b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PDE0G16NL2&[\xc3\x01', b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x00\x00\x00\x00', b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x13\xcd\x88\x92', @@ -1192,6 +1170,7 @@ FW_VERSIONS = { b'\xf1\x00DE MDPS C 1.00 1.09 56310G5301\x00 4DEHC109', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DEH MFC AT USA LHD 1.00 1.00 95740-G5010 170117', b'\xf1\x00DEP MFC AT USA LHD 1.00 1.00 95740-G5010 170117', b'\xf1\x00DEP MFC AT USA LHD 1.00 1.01 95740-G5010 170424', b'\xf1\x00DEP MFC AT USA LHD 1.00 1.05 99211-G5000 190826', @@ -1476,11 +1455,13 @@ FW_VERSIONS = { CAR.SONATA_HYBRID: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', + b'\xf1\x00DNhe SCC FHCUP 1.00 1.00 99110-L5000 ', b'\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ', b'\xf1\x8799110L5000\xf1\x00DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', b'\xf1\x8799110L5000\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ', ], (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L5000 4DNHC101', b'\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104', b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102', b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.03 56310-L5450 4DNHC103', @@ -1488,12 +1469,14 @@ FW_VERSIONS = { b'\xf1\x8756310L5450\x00\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DN8HMFC AT KOR LHD 1.00 1.03 99211-L1000 190705', b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.04 99211-L1000 191016', b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.05 99211-L1000 201109', b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.06 99211-L1000 210325', b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.07 99211-L1000 211223', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00PSBG2314 E07\x00\x00\x00\x00\x00\x00\x00TDN2H20KA5\xba\x82\xc7\xc3', b'\xf1\x00PSBG2323 E09\x00\x00\x00\x00\x00\x00\x00TDN2H20SA5\x97R\x88\x9e', b'\xf1\x00PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TDN2H20SA6N\xc2\xeeW', b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TDN2H20SA7\x1a3\xf9\xab', @@ -1503,6 +1486,7 @@ FW_VERSIONS = { ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x87391062J002', + b'\xf1\x87391162J011', b'\xf1\x87391162J012', b'\xf1\x87391162J013', b'\xf1\x87391162J014', @@ -1510,15 +1494,18 @@ FW_VERSIONS = { }, CAR.KIA_SORENTO: { (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00UMP LKAS AT USA LHD 1.00 1.00 95740-C6550 d00', b'\xf1\x00UMP LKAS AT USA LHD 1.01 1.01 95740-C6550 d01', ], (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00UM ESC \x02 12 \x18\x05\x05 58910-C6300', b'\xf1\x00UM ESC \x0c 12 \x18\x05\x06 58910-C6330', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00UM__ SCC F-CUP 1.00 1.00 96400-C6500 ', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00bcsh8p54 U834\x00\x00\x00\x00\x00\x00TUM2G33NL7K\xae\xdd\x1d', b'\xf1\x87LDKUAA0348164HE3\x87www\x87www\x88\x88\xa8\x88w\x88\x97xw\x88\x97x\x86o\xf8\xff\x87f\x7f\xff\x15\xe0\xf1\x81U811\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U811\x00\x00\x00\x00\x00\x00TUM4G33NL3V|DG', ], (Ecu.engine, 0x7e0, None): [ @@ -1546,7 +1533,9 @@ FW_VERSIONS = { b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813', + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI010 230110', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206', b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719', @@ -1564,12 +1553,14 @@ FW_VERSIONS = { b'\xf1\x00CE__ RDR ----- 1.00 1.01 99110-KL000 ', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00CE MFC AT CAN LHD 1.00 1.04 99211-KL000 221213', b'\xf1\x00CE MFC AT EUR LHD 1.00 1.03 99211-KL000 221011', b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213', ], }, CAR.TUCSON_4TH_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.01 99211-N9100 14A', b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9220 14K', b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 2.02 99211-N9000 14E', b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9210 14G', diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index bc29aeb985..0bf29664e8 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -1,9 +1,9 @@ import crcmod -from openpilot.selfdrive.car.hyundai.values import CAR, CHECKSUM, CAMERA_SCC_CAR +from openpilot.selfdrive.car.hyundai.values import CAR, HyundaiFlags hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf) -def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, +def create_lkas11(packer, frame, CP, apply_steer, steer_req, torque_fault, lkas11, sys_warning, sys_state, enabled, left_lane, right_lane, left_lane_depart, right_lane_depart): @@ -33,12 +33,12 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq values["CF_Lkas_MsgCount"] = frame % 0x10 - if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE, - CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020, - CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, - CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, - CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, - CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN): + if CP.carFingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE, + CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020, + CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, + CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, + CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, + CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN): values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1) values["CF_Lkas_LdwsOpt_USM"] = 2 @@ -57,7 +57,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0 # Likely cars lacking the ability to show individual lane lines in the dash - elif car_fingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL): + elif CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL): # SysWarning 4 = keep hands on wheel + beep values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0 @@ -72,18 +72,18 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, values["CF_Lkas_LdwsActivemode"] = 0 values["CF_Lkas_FcwOpt_USM"] = 0 - elif car_fingerprint == CAR.HYUNDAI_GENESIS: + elif CP.carFingerprint == CAR.HYUNDAI_GENESIS: # This field is actually LdwsActivemode # Genesis and Optima fault when forwarding while engaged values["CF_Lkas_LdwsActivemode"] = 2 dat = packer.make_can_msg("LKAS11", 0, values)[2] - if car_fingerprint in CHECKSUM["crc8"]: + if CP.flags & HyundaiFlags.CHECKSUM_CRC8: # CRC Checksum as seen on 2019 Hyundai Santa Fe dat = dat[:6] + dat[7:8] checksum = hyundai_checksum(dat) - elif car_fingerprint in CHECKSUM["6B"]: + elif CP.flags & HyundaiFlags.CHECKSUM_6B: # Checksum of first 6 Bytes, as seen on 2018 Kia Sorento checksum = sum(dat[:6]) % 256 else: @@ -95,7 +95,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, return packer.make_can_msg("LKAS11", 0, values) -def create_clu11(packer, frame, clu11, button, car_fingerprint): +def create_clu11(packer, frame, clu11, button, CP): values = {s: clu11[s] for s in [ "CF_Clu_CruiseSwState", "CF_Clu_CruiseSwMain", @@ -113,7 +113,7 @@ def create_clu11(packer, frame, clu11, button, car_fingerprint): values["CF_Clu_CruiseSwState"] = button values["CF_Clu_AliveCnt1"] = frame % 0x10 # send buttons to camera on camera-scc based cars - bus = 2 if car_fingerprint in CAMERA_SCC_CAR else 0 + bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0 return packer.make_can_msg("CLU11", bus, values) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 049a63399c..69b5132806 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -1,6 +1,5 @@ from cereal import car from panda import Panda -from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \ CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \ @@ -76,196 +75,6 @@ class CarInterface(CarInterfaceBase): ret.steerLimitTimer = 0.4 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - if candidate in (CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN): - ret.mass = 1600. if candidate == CAR.AZERA_6TH_GEN else 1675. # ICE is ~average of 2.5L and 3.5L - ret.wheelbase = 2.885 - ret.steerRatio = 14.5 - elif candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022): - ret.mass = 3982. * CV.LB_TO_KG - ret.wheelbase = 2.766 - # Values from optimizer - ret.steerRatio = 16.55 # 13.8 is spec end-to-end - ret.tireStiffnessFactor = 0.82 - elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID): - ret.mass = 1513. - ret.wheelbase = 2.84 - ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable - ret.tireStiffnessFactor = 0.65 - elif candidate == CAR.SONATA_LF: - ret.mass = 1536. - ret.wheelbase = 2.804 - ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable - elif candidate == CAR.PALISADE: - ret.mass = 1999. - ret.wheelbase = 2.90 - ret.steerRatio = 15.6 * 1.15 - ret.tireStiffnessFactor = 0.63 - elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30): - ret.mass = 1275. - ret.wheelbase = 2.7 - ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535 - ret.tireStiffnessFactor = 0.385 # stiffnessFactor settled on 1.0081302973865127 - ret.minSteerSpeed = 32 * CV.MPH_TO_MS - elif candidate == CAR.ELANTRA_2021: - ret.mass = 2800. * CV.LB_TO_KG - ret.wheelbase = 2.72 - ret.steerRatio = 12.9 - ret.tireStiffnessFactor = 0.65 - elif candidate == CAR.ELANTRA_HEV_2021: - ret.mass = 3017. * CV.LB_TO_KG - ret.wheelbase = 2.72 - ret.steerRatio = 12.9 - ret.tireStiffnessFactor = 0.65 - elif candidate == CAR.HYUNDAI_GENESIS: - ret.mass = 2060. - ret.wheelbase = 3.01 - ret.steerRatio = 16.5 - ret.minSteerSpeed = 60 * CV.KPH_TO_MS - elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN): - ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743., CAR.KONA_EV_2ND_GEN: 1740.}.get(candidate, 1275.) - ret.wheelbase = {CAR.KONA_EV_2ND_GEN: 2.66, }.get(candidate, 2.6) - ret.steerRatio = {CAR.KONA_EV_2ND_GEN: 13.6, }.get(candidate, 13.42) # Spec - ret.tireStiffnessFactor = 0.385 - elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019, CAR.IONIQ_HEV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV): - ret.mass = 1490. # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx - ret.wheelbase = 2.7 - ret.steerRatio = 13.73 # Spec - ret.tireStiffnessFactor = 0.385 - if candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019): - ret.minSteerSpeed = 32 * CV.MPH_TO_MS - elif candidate in (CAR.IONIQ_5, CAR.IONIQ_6): - ret.mass = 1948 - ret.wheelbase = 2.97 - ret.steerRatio = 14.26 - ret.tireStiffnessFactor = 0.65 - elif candidate == CAR.VELOSTER: - ret.mass = 2917. * CV.LB_TO_KG - ret.wheelbase = 2.80 - ret.steerRatio = 13.75 * 1.15 - ret.tireStiffnessFactor = 0.5 - elif candidate == CAR.TUCSON: - ret.mass = 3520. * CV.LB_TO_KG - ret.wheelbase = 2.67 - ret.steerRatio = 14.00 * 1.15 - ret.tireStiffnessFactor = 0.385 - elif candidate == CAR.TUCSON_4TH_GEN: - ret.mass = 1630. # average - ret.wheelbase = 2.756 - ret.steerRatio = 16. - ret.tireStiffnessFactor = 0.385 - elif candidate == CAR.SANTA_CRUZ_1ST_GEN: - ret.mass = 1870. # weight from Limited trim - the only supported trim - ret.wheelbase = 3.000 - # steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf - ret.steerRatio = 14.2 - elif candidate == CAR.CUSTIN_1ST_GEN: - ret.mass = 1690. # from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0 - ret.wheelbase = 3.055 - ret.steerRatio = 17.0 # from learner - elif candidate == CAR.STARIA_4TH_GEN: - ret.mass = 2205. - ret.wheelbase = 3.273 - ret.steerRatio = 11.94 # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf - - # Kia - elif candidate == CAR.KIA_SORENTO: - ret.mass = 1985. - ret.wheelbase = 2.78 - ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable - elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_PHEV_2022): - ret.mass = 3543. * CV.LB_TO_KG # average of all the cars - ret.wheelbase = 2.7 - ret.steerRatio = 13.6 # average of all the cars - ret.tireStiffnessFactor = 0.385 - if candidate == CAR.KIA_NIRO_PHEV: - ret.minSteerSpeed = 32 * CV.MPH_TO_MS - elif candidate == CAR.KIA_SELTOS: - ret.mass = 1337. - ret.wheelbase = 2.63 - ret.steerRatio = 14.56 - elif candidate == CAR.KIA_SPORTAGE_5TH_GEN: - ret.mass = 1725. # weight from SX and above trims, average of FWD and AWD versions - ret.wheelbase = 2.756 - ret.steerRatio = 13.6 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications - elif candidate in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL): - ret.mass = 3558. * CV.LB_TO_KG - ret.wheelbase = 2.80 - ret.steerRatio = 13.75 - ret.tireStiffnessFactor = 0.5 - if candidate == CAR.KIA_OPTIMA_G4: - ret.minSteerSpeed = 32 * CV.MPH_TO_MS - elif candidate in (CAR.KIA_STINGER, CAR.KIA_STINGER_2022): - ret.mass = 1825. - ret.wheelbase = 2.78 - ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable - elif candidate == CAR.KIA_FORTE: - ret.mass = 2878. * CV.LB_TO_KG - ret.wheelbase = 2.80 - ret.steerRatio = 13.75 - ret.tireStiffnessFactor = 0.5 - elif candidate == CAR.KIA_CEED: - ret.mass = 1450. - ret.wheelbase = 2.65 - ret.steerRatio = 13.75 - ret.tireStiffnessFactor = 0.5 - elif candidate in (CAR.KIA_K5_2021, CAR.KIA_K5_HEV_2020): - ret.mass = 3381. * CV.LB_TO_KG - ret.wheelbase = 2.85 - ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims) - ret.tireStiffnessFactor = 0.5 - elif candidate == CAR.KIA_EV6: - ret.mass = 2055 - ret.wheelbase = 2.9 - ret.steerRatio = 16. - ret.tireStiffnessFactor = 0.65 - elif candidate in (CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN): - ret.wheelbase = 2.81 - ret.steerRatio = 13.5 # average of the platforms - if candidate == CAR.KIA_SORENTO_4TH_GEN: - ret.mass = 3957 * CV.LB_TO_KG - else: - ret.mass = 4396 * CV.LB_TO_KG - elif candidate == CAR.KIA_CARNIVAL_4TH_GEN: - ret.mass = 2087. - ret.wheelbase = 3.09 - ret.steerRatio = 14.23 - elif candidate == CAR.KIA_K8_HEV_1ST_GEN: - ret.mass = 1630. # https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid - ret.wheelbase = 2.895 - ret.steerRatio = 13.27 # guesstimate from K5 platform - - # Genesis - elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN: - ret.mass = 2205 - ret.wheelbase = 2.9 - # 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 - ret.wheelbase = 2.84 - ret.steerRatio = 13.56 - elif candidate == CAR.GENESIS_G70_2020: - ret.mass = 3673.0 * CV.LB_TO_KG - ret.wheelbase = 2.83 - ret.steerRatio = 12.9 - elif candidate == CAR.GENESIS_GV70_1ST_GEN: - ret.mass = 1950. - ret.wheelbase = 2.87 - ret.steerRatio = 14.6 - elif candidate == CAR.GENESIS_G80: - ret.mass = 2060. - ret.wheelbase = 3.01 - ret.steerRatio = 16.5 - elif candidate == CAR.GENESIS_G90: - ret.mass = 2200. - ret.wheelbase = 3.15 - ret.steerRatio = 12.069 - elif candidate == CAR.GENESIS_GV80: - ret.mass = 2258. - ret.wheelbase = 2.95 - ret.steerRatio = 14.14 - # *** longitudinal control *** if candidate in CANFD_CAR: ret.longitudinalTuning.kpV = [0.1] diff --git a/selfdrive/car/hyundai/tests/test_hyundai.py b/selfdrive/car/hyundai/tests/test_hyundai.py index c7100d03f0..61b11a1992 100755 --- a/selfdrive/car/hyundai/tests/test_hyundai.py +++ b/selfdrive/car/hyundai/tests/test_hyundai.py @@ -98,6 +98,23 @@ class TestHyundaiFingerprint(unittest.TestCase): fws = data.draw(fw_strategy) get_platform_codes(fws) + def test_expected_platform_codes(self): + # Ensures we don't accidentally add multiple platform codes for a car unless it is intentional + for car_model, ecus in FW_VERSIONS.items(): + with self.subTest(car_model=car_model.value): + for ecu, fws in ecus.items(): + if ecu[0] not in PLATFORM_CODE_ECUS: + continue + + # Third and fourth character are usually EV/hybrid identifiers + codes = {code.split(b"-")[0][:2] for code, _ in get_platform_codes(fws)} + if car_model == CAR.PALISADE: + self.assertEqual(codes, {b"LX", b"ON"}, f"Car has unexpected platform codes: {car_model} {codes}") + elif car_model == CAR.KONA_EV and ecu[0] == Ecu.fwdCamera: + self.assertEqual(codes, {b"OE", b"OS"}, f"Car has unexpected platform codes: {car_model} {codes}") + else: + self.assertEqual(len(codes), 1, f"Car has multiple platform codes: {car_model} {codes}") + # Tests for platform codes, part numbers, and FW dates which Hyundai will use to fuzzy # fingerprint in the absence of full FW matches: def test_platform_code_ecus_available(self): diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 76ff0b39ce..199e0bba4d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1,11 +1,11 @@ import re -from dataclasses import dataclass -from enum import Enum, IntFlag, StrEnum +from dataclasses import dataclass, field +from enum import Enum, IntFlag from cereal import car from panda.python import uds from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car import dbc_dict +from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 @@ -52,92 +52,48 @@ class CarControllerParams: class HyundaiFlags(IntFlag): + # Dynamic Flags CANFD_HDA2 = 1 CANFD_ALT_BUTTONS = 2 - CANFD_ALT_GEARS = 4 - CANFD_CAMERA_SCC = 8 + CANFD_ALT_GEARS = 2 ** 2 + CANFD_CAMERA_SCC = 2 ** 3 - ALT_LIMITS = 16 - ENABLE_BLINKERS = 32 - CANFD_ALT_GEARS_2 = 64 - SEND_LFA = 128 - USE_FCA = 256 - CANFD_HDA2_ALT_STEERING = 512 - HYBRID = 1024 - EV = 2048 + ALT_LIMITS = 2 ** 4 + ENABLE_BLINKERS = 2 ** 5 + CANFD_ALT_GEARS_2 = 2 ** 6 + SEND_LFA = 2 ** 7 + USE_FCA = 2 ** 8 + CANFD_HDA2_ALT_STEERING = 2 ** 9 + # these cars use a different gas signal + HYBRID = 2 ** 10 + EV = 2 ** 11 -class CAR(StrEnum): - # Hyundai - AZERA_6TH_GEN = "HYUNDAI AZERA 6TH GEN" - AZERA_HEV_6TH_GEN = "HYUNDAI AZERA HYBRID 6TH GEN" - ELANTRA = "HYUNDAI ELANTRA 2017" - ELANTRA_GT_I30 = "HYUNDAI I30 N LINE 2019 & GT 2018 DCT" - ELANTRA_2021 = "HYUNDAI ELANTRA 2021" - ELANTRA_HEV_2021 = "HYUNDAI ELANTRA HYBRID 2021" - HYUNDAI_GENESIS = "HYUNDAI GENESIS 2015-2016" - IONIQ = "HYUNDAI IONIQ HYBRID 2017-2019" - IONIQ_HEV_2022 = "HYUNDAI IONIQ HYBRID 2020-2022" - IONIQ_EV_LTD = "HYUNDAI IONIQ ELECTRIC LIMITED 2019" - IONIQ_EV_2020 = "HYUNDAI IONIQ ELECTRIC 2020" - IONIQ_PHEV_2019 = "HYUNDAI IONIQ PLUG-IN HYBRID 2019" - IONIQ_PHEV = "HYUNDAI IONIQ PHEV 2020" - KONA = "HYUNDAI KONA 2020" - KONA_EV = "HYUNDAI KONA ELECTRIC 2019" - KONA_EV_2022 = "HYUNDAI KONA ELECTRIC 2022" - KONA_EV_2ND_GEN = "HYUNDAI KONA ELECTRIC 2ND GEN" - KONA_HEV = "HYUNDAI KONA HYBRID 2020" - SANTA_FE = "HYUNDAI SANTA FE 2019" - SANTA_FE_2022 = "HYUNDAI SANTA FE 2022" - SANTA_FE_HEV_2022 = "HYUNDAI SANTA FE HYBRID 2022" - SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022" - SONATA = "HYUNDAI SONATA 2020" - SONATA_LF = "HYUNDAI SONATA 2019" - STARIA_4TH_GEN = "HYUNDAI STARIA 4TH GEN" - TUCSON = "HYUNDAI TUCSON 2019" - PALISADE = "HYUNDAI PALISADE 2020" - VELOSTER = "HYUNDAI VELOSTER 2019" - SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" - IONIQ_5 = "HYUNDAI IONIQ 5 2022" - IONIQ_6 = "HYUNDAI IONIQ 6 2023" - TUCSON_4TH_GEN = "HYUNDAI TUCSON 4TH GEN" - SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN" - CUSTIN_1ST_GEN = "HYUNDAI CUSTIN 1ST GEN" + # Static flags - # Kia - KIA_FORTE = "KIA FORTE E 2018 & GT 2021" - KIA_K5_2021 = "KIA K5 2021" - KIA_K5_HEV_2020 = "KIA K5 HYBRID 2020" - KIA_K8_HEV_1ST_GEN = "KIA K8 HYBRID 1ST GEN" - KIA_NIRO_EV = "KIA NIRO EV 2020" - KIA_NIRO_EV_2ND_GEN = "KIA NIRO EV 2ND GEN" - KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019" - KIA_NIRO_PHEV_2022 = "KIA NIRO PLUG-IN HYBRID 2022" - KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021" - KIA_NIRO_HEV_2ND_GEN = "KIA NIRO HYBRID 2ND GEN" - KIA_OPTIMA_G4 = "KIA OPTIMA 4TH GEN" - KIA_OPTIMA_G4_FL = "KIA OPTIMA 4TH GEN FACELIFT" - KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019" - KIA_OPTIMA_H_G4_FL = "KIA OPTIMA HYBRID 4TH GEN FACELIFT" - KIA_SELTOS = "KIA SELTOS 2021" - KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN" - KIA_SORENTO = "KIA SORENTO GT LINE 2018" - KIA_SORENTO_4TH_GEN = "KIA SORENTO 4TH GEN" - KIA_SORENTO_HEV_4TH_GEN = "KIA SORENTO HYBRID 4TH GEN" - KIA_STINGER = "KIA STINGER GT2 2018" - KIA_STINGER_2022 = "KIA STINGER 2022" - KIA_CEED = "KIA CEED INTRO ED 2019" - KIA_EV6 = "KIA EV6 2022" - KIA_CARNIVAL_4TH_GEN = "KIA CARNIVAL 4TH GEN" + # If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points. + # If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py + MANDO_RADAR = 2 ** 12 + CANFD = 2 ** 13 - # Genesis - GENESIS_GV60_EV_1ST_GEN = "GENESIS GV60 ELECTRIC 1ST GEN" - GENESIS_G70 = "GENESIS G70 2018" - GENESIS_G70_2020 = "GENESIS G70 2020" - GENESIS_GV70_1ST_GEN = "GENESIS GV70 1ST GEN" - GENESIS_G80 = "GENESIS G80 2017" - GENESIS_G90 = "GENESIS G90 2017" - GENESIS_GV80 = "GENESIS GV80 2023" + # The radar does SCC on these cars when HDA I, rather than the camera + RADAR_SCC = 2 ** 14 + CAMERA_SCC = 2 ** 15 + CHECKSUM_CRC8 = 2 ** 16 + CHECKSUM_6B = 2 ** 17 + + # these cars require a special panda safety mode due to missing counters and checksums in the messages + LEGACY = 2 ** 18 + + # these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc. + UNSUPPORTED_LONGITUDINAL = 2 ** 19 + + CANFD_NO_RADAR_DISABLE = 2 ** 20 + + CLUSTER_GEARS = 2 ** 21 + TCU_GEARS = 2 ** 22 + + MIN_STEER_32_MPH = 2 ** 23 class Footnote(Enum): @@ -152,160 +108,498 @@ class HyundaiCarInfo(CarInfo): package: str = "Smart Cruise Control (SCC)" def init_make(self, CP: car.CarParams): - if CP.carFingerprint in CANFD_CAR: + if CP.flags & HyundaiFlags.CANFD: self.footnotes.insert(0, Footnote.CANFD) -CAR_INFO: dict[str, HyundaiCarInfo | list[HyundaiCarInfo] | None] = { - CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), - CAR.AZERA_HEV_6TH_GEN: [ - HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), - HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), - ], - CAR.ELANTRA: [ - # TODO: 2017-18 could be Hyundai G - HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])), - HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])), - ], - CAR.ELANTRA_GT_I30: [ - HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), - HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), - ], - 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.HYUNDAI_GENESIS: [ - # 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])), - CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness - CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), - CAR.IONIQ_EV_2020: HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), - CAR.IONIQ_PHEV_2019: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), - CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), - 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-23", car_parts=CarParts.common([CarHarness.hyundai_o])), - CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages - # TODO: this is the 2024 US MY, not yet released - CAR.KONA_EV_2ND_GEN: HyundaiCarInfo("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw", - car_parts=CarParts.common([CarHarness.hyundai_r])), - CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s", - 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_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-23", "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.STARIA_4TH_GEN: HyundaiCarInfo("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), - 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])), - HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])), - ], - CAR.PALISADE: [ - HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])), - HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), - ], - CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])), - CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - CAR.IONIQ_5: [ - HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), - HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), - HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), - ], - CAR.IONIQ_6: [ +@dataclass +class HyundaiPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_kia_generic", None)) + + def init(self): + if self.flags & HyundaiFlags.MANDO_RADAR: + self.dbc_dict = dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated') + + if self.flags & HyundaiFlags.MIN_STEER_32_MPH: + self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS) + + +@dataclass +class HyundaiCanFDPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict("hyundai_canfd", None)) + + def init(self): + self.flags |= HyundaiFlags.CANFD + + +class CAR(Platforms): + # Hyundai + AZERA_6TH_GEN = HyundaiPlatformConfig( + "HYUNDAI AZERA 6TH GEN", + HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=1600, wheelbase=2.885, steerRatio=14.5), + ) + AZERA_HEV_6TH_GEN = HyundaiPlatformConfig( + "HYUNDAI AZERA HYBRID 6TH GEN", + [ + HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + ], + CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5), + flags=HyundaiFlags.HYBRID, + ) + ELANTRA = HyundaiPlatformConfig( + "HYUNDAI ELANTRA 2017", + [ + # TODO: 2017-18 could be Hyundai G + HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])), + HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])), + ], + # steerRatio: 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535, stiffnessFactor settled on 1.0081302973865127 + CarSpecs(mass=1275, wheelbase=2.7, steerRatio=15.4, tireStiffnessFactor=0.385), + flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.MIN_STEER_32_MPH, + ) + ELANTRA_GT_I30 = HyundaiPlatformConfig( + "HYUNDAI I30 N LINE 2019 & GT 2018 DCT", + [ + HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), + HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), + ], + ELANTRA.specs, + flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.MIN_STEER_32_MPH, + ) + ELANTRA_2021 = HyundaiPlatformConfig( + "HYUNDAI ELANTRA 2021", + HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=2800 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65), + flags=HyundaiFlags.CHECKSUM_CRC8, + ) + ELANTRA_HEV_2021 = HyundaiPlatformConfig( + "HYUNDAI ELANTRA HYBRID 2021", + HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", + car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=3017 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65), + flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, + ) + HYUNDAI_GENESIS = HyundaiPlatformConfig( + "HYUNDAI GENESIS 2015-2016", + [ + # 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])), + ], + CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5, minSteerSpeed=60 * CV.KPH_TO_MS), + flags=HyundaiFlags.CHECKSUM_6B | HyundaiFlags.LEGACY, + ) + IONIQ = HyundaiPlatformConfig( + "HYUNDAI IONIQ HYBRID 2017-2019", + HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH, + ) + IONIQ_HEV_2022 = HyundaiPlatformConfig( + "HYUNDAI IONIQ HYBRID 2020-2022", + HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness, + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY, + ) + IONIQ_EV_LTD = HyundaiPlatformConfig( + "HYUNDAI IONIQ ELECTRIC LIMITED 2019", + HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV | HyundaiFlags.LEGACY | HyundaiFlags.MIN_STEER_32_MPH, + ) + IONIQ_EV_2020 = HyundaiPlatformConfig( + "HYUNDAI IONIQ ELECTRIC 2020", + HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.EV, + ) + IONIQ_PHEV_2019 = HyundaiPlatformConfig( + "HYUNDAI IONIQ PLUG-IN HYBRID 2019", + HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH, + ) + IONIQ_PHEV = HyundaiPlatformConfig( + "HYUNDAI IONIQ PHEV 2020", + HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), + flags=HyundaiFlags.HYBRID, + ) + KONA = HyundaiPlatformConfig( + "HYUNDAI KONA 2020", + HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])), + CarSpecs(mass=1275, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), + flags=HyundaiFlags.CLUSTER_GEARS, + ) + KONA_EV = HyundaiPlatformConfig( + "HYUNDAI KONA ELECTRIC 2019", + HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])), + CarSpecs(mass=1685, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), + flags=HyundaiFlags.EV, + ) + KONA_EV_2022 = HyundaiPlatformConfig( + "HYUNDAI KONA ELECTRIC 2022", + HyundaiCarInfo("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o])), + CarSpecs(mass=1743, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), + flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV, + ) + KONA_EV_2ND_GEN = HyundaiCanFDPlatformConfig( + "HYUNDAI KONA ELECTRIC 2ND GEN", + HyundaiCarInfo("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw", + car_parts=CarParts.common([CarHarness.hyundai_r])), + CarSpecs(mass=1740, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385), + flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE, + ) + KONA_HEV = HyundaiPlatformConfig( + "HYUNDAI KONA HYBRID 2020", + HyundaiCarInfo("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages, + CarSpecs(mass=1425, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), + flags=HyundaiFlags.HYBRID, + ) + SANTA_FE = HyundaiPlatformConfig( + "HYUNDAI SANTA FE 2019", + HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s", + car_parts=CarParts.common([CarHarness.hyundai_d])), + CarSpecs(mass=3982 * CV.LB_TO_KG, wheelbase=2.766, steerRatio=16.55, tireStiffnessFactor=0.82), + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8, + ) + SANTA_FE_2022 = HyundaiPlatformConfig( + "HYUNDAI SANTA FE 2022", + HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4", + car_parts=CarParts.common([CarHarness.hyundai_l])), + SANTA_FE.specs, + flags=HyundaiFlags.CHECKSUM_CRC8, + ) + SANTA_FE_HEV_2022 = HyundaiPlatformConfig( + "HYUNDAI SANTA FE HYBRID 2022", + HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + SANTA_FE.specs, + flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, + ) + SANTA_FE_PHEV_2022 = HyundaiPlatformConfig( + "HYUNDAI SANTA FE PlUG-IN HYBRID 2022", + HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + SANTA_FE.specs, + flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, + ) + SONATA = HyundaiPlatformConfig( + "HYUNDAI SONATA 2020", + HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", + car_parts=CarParts.common([CarHarness.hyundai_a])), + CarSpecs(mass=1513, wheelbase=2.84, steerRatio=13.27 * 1.15, tireStiffnessFactor=0.65), # 15% higher at the center seems reasonable + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8, + ) + SONATA_LF = HyundaiPlatformConfig( + "HYUNDAI SONATA 2019", + HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])), + CarSpecs(mass=1536, wheelbase=2.804, steerRatio=13.27 * 1.15), # 15% higher at the center seems reasonable + + flags=HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.TCU_GEARS, + ) + STARIA_4TH_GEN = HyundaiCanFDPlatformConfig( + "HYUNDAI STARIA 4TH GEN", + HyundaiCarInfo("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=2205, wheelbase=3.273, steerRatio=11.94), # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf + ) + TUCSON = HyundaiPlatformConfig( + "HYUNDAI TUCSON 2019", + [ + HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])), + ], + CarSpecs(mass=3520 * CV.LB_TO_KG, wheelbase=2.67, steerRatio=16.1, tireStiffnessFactor=0.385), + flags=HyundaiFlags.TCU_GEARS, + ) + PALISADE = HyundaiPlatformConfig( + "HYUNDAI PALISADE 2020", + [ + HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])), + HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + ], + CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63), + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8, + ) + VELOSTER = HyundaiPlatformConfig( + "HYUNDAI VELOSTER 2019", + HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])), + CarSpecs(mass=2917 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75 * 1.15, tireStiffnessFactor=0.5), + flags=HyundaiFlags.LEGACY | HyundaiFlags.TCU_GEARS, + ) + SONATA_HYBRID = HyundaiPlatformConfig( + "HYUNDAI SONATA HYBRID 2021", + HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + SONATA.specs, + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, + ) + IONIQ_5 = HyundaiCanFDPlatformConfig( + "HYUNDAI IONIQ 5 2022", + [ + HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), + HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), + HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), + ], + CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65), + flags=HyundaiFlags.EV, + ) + IONIQ_6 = HyundaiCanFDPlatformConfig( + "HYUNDAI IONIQ 6 2023", HyundaiCarInfo("Hyundai Ioniq 6 (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])), - ], - CAR.TUCSON_4TH_GEN: [ - HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])), - HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), - HyundaiCarInfo("Hyundai Tucson Hybrid 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), - ], - CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])), - CAR.CUSTIN_1ST_GEN: HyundaiCarInfo("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + IONIQ_5.specs, + flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE, + ) + TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig( + "HYUNDAI TUCSON 4TH GEN", + [ + HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])), + HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), + HyundaiCarInfo("Hyundai Tucson Hybrid 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), + ], + CarSpecs(mass=1630, wheelbase=2.756, steerRatio=16, tireStiffnessFactor=0.385), + ) + SANTA_CRUZ_1ST_GEN = HyundaiCanFDPlatformConfig( + "HYUNDAI SANTA CRUZ 1ST GEN", + HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])), + # weight from Limited trim - the only supported trim, steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf + CarSpecs(mass=1870, wheelbase=3, steerRatio=14.2), + ) + CUSTIN_1ST_GEN = HyundaiPlatformConfig( + "HYUNDAI CUSTIN 1ST GEN", + HyundaiCarInfo("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=1690, wheelbase=3.055, steerRatio=17), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner + flags=HyundaiFlags.CHECKSUM_CRC8, + ) # Kia - CAR.KIA_FORTE: [ - HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])), - HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])), - ], - CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])), - CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])), - CAR.KIA_K8_HEV_1ST_GEN: HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), - CAR.KIA_NIRO_EV: [ - HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), - HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])), - HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])), - HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), - ], - CAR.KIA_NIRO_EV_2ND_GEN: HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - CAR.KIA_NIRO_PHEV: [ - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])), - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), - ], - CAR.KIA_NIRO_PHEV_2022: [ - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), - ], - CAR.KIA_NIRO_HEV_2021: [ - HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])), - HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])), - ], - 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_FL: HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])), + KIA_FORTE = HyundaiPlatformConfig( + "KIA FORTE E 2018 & GT 2021", + [ + HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])), + HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])), + ], + CarSpecs(mass=2878 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5) + ) + KIA_K5_2021 = HyundaiPlatformConfig( + "KIA K5 2021", + HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])), + CarSpecs(mass=3381 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5), # 2021 Kia K5 Steering Ratio (all trims) + flags=HyundaiFlags.CHECKSUM_CRC8, + ) + KIA_K5_HEV_2020 = HyundaiPlatformConfig( + "KIA K5 HYBRID 2020", + HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])), + KIA_K5_2021.specs, + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, + ) + KIA_K8_HEV_1ST_GEN = HyundaiCanFDPlatformConfig( + "KIA K8 HYBRID 1ST GEN", + HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), + # mass: https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid, steerRatio: guesstimate from K5 platform + CarSpecs(mass=1630, wheelbase=2.895, steerRatio=13.27) + ) + KIA_NIRO_EV = HyundaiPlatformConfig( + "KIA NIRO EV 2020", + [ + HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), + HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])), + HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), + ], + CarSpecs(mass=3543 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=13.6, tireStiffnessFactor=0.385), # average of all the cars + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV, + ) + KIA_NIRO_EV_2ND_GEN = HyundaiCanFDPlatformConfig( + "KIA NIRO EV 2ND GEN", + HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + KIA_NIRO_EV.specs, + flags=HyundaiFlags.EV, + ) + KIA_NIRO_PHEV = HyundaiPlatformConfig( + "KIA NIRO HYBRID 2019", + [ + HyundaiCarInfo("Kia Niro Hybrid 2018", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), + ], + KIA_NIRO_EV.specs, + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.HYBRID | HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.MIN_STEER_32_MPH, + ) + KIA_NIRO_PHEV_2022 = HyundaiPlatformConfig( + "KIA NIRO PLUG-IN HYBRID 2022", + [ + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), + ], + KIA_NIRO_EV.specs, + flags=HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR, + ) + KIA_NIRO_HEV_2021 = HyundaiPlatformConfig( + "KIA NIRO HYBRID 2021", + [ + HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])), + HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])), + ], + KIA_NIRO_EV.specs, + flags=HyundaiFlags.HYBRID, + ) + KIA_NIRO_HEV_2ND_GEN = HyundaiCanFDPlatformConfig( + "KIA NIRO HYBRID 2ND GEN", + HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])), + KIA_NIRO_EV.specs, + ) + KIA_OPTIMA_G4 = HyundaiPlatformConfig( + "KIA OPTIMA 4TH GEN", + HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", + car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018 + CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.LEGACY | HyundaiFlags.TCU_GEARS | HyundaiFlags.MIN_STEER_32_MPH, + ) + KIA_OPTIMA_G4_FL = HyundaiPlatformConfig( + "KIA OPTIMA 4TH GEN FACELIFT", + HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])), + CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.TCU_GEARS, + ) # TODO: may support adjacent years. may have a non-zero minimum steering speed - CAR.KIA_OPTIMA_H: HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c])), - CAR.KIA_OPTIMA_H_G4_FL: HyundaiCarInfo("Kia Optima Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_h])), - 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])), - HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), - ], - CAR.KIA_SORENTO: [ - HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control & LKAS", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", - car_parts=CarParts.common([CarHarness.hyundai_e])), - 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_HEV_4TH_GEN: [ - HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - ], - 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-23", "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: [ - HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), - HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])), - HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])) - ], - CAR.KIA_CARNIVAL_4TH_GEN: [ - HyundaiCarInfo("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])), - HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k])) - ], + KIA_OPTIMA_H = HyundaiPlatformConfig( + "KIA OPTIMA HYBRID 2017 & SPORTS 2019", + HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY, + ) + KIA_OPTIMA_H_G4_FL = HyundaiPlatformConfig( + "KIA OPTIMA HYBRID 4TH GEN FACELIFT", + HyundaiCarInfo("Kia Optima Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_h])), + CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.HYBRID | HyundaiFlags.UNSUPPORTED_LONGITUDINAL, + ) + KIA_SELTOS = HyundaiPlatformConfig( + "KIA SELTOS 2021", + HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])), + CarSpecs(mass=1337, wheelbase=2.63, steerRatio=14.56), + flags=HyundaiFlags.CHECKSUM_CRC8, + ) + KIA_SPORTAGE_5TH_GEN = HyundaiCanFDPlatformConfig( + "KIA SPORTAGE 5TH GEN", + [ + HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), + HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), + ], + # weight from SX and above trims, average of FWD and AWD version, steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications + CarSpecs(mass=1725, wheelbase=2.756, steerRatio=13.6), + ) + KIA_SORENTO = HyundaiPlatformConfig( + "KIA SORENTO GT LINE 2018", + [ + HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control & LKAS", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", + car_parts=CarParts.common([CarHarness.hyundai_e])), + HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), + ], + CarSpecs(mass=1985, wheelbase=2.78, steerRatio=14.4 * 1.1), # 10% higher at the center seems reasonable + flags=HyundaiFlags.CHECKSUM_6B | HyundaiFlags.UNSUPPORTED_LONGITUDINAL, + ) + KIA_SORENTO_4TH_GEN = HyundaiCanFDPlatformConfig( + "KIA SORENTO 4TH GEN", + HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), + CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms + flags=HyundaiFlags.RADAR_SCC, + ) + KIA_SORENTO_HEV_4TH_GEN = HyundaiCanFDPlatformConfig( + "KIA SORENTO HYBRID 4TH GEN", + [ + HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + ], + CarSpecs(mass=4395 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms + flags=HyundaiFlags.RADAR_SCC, + ) + KIA_STINGER = HyundaiPlatformConfig( + "KIA STINGER GT2 2018", + HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", + car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=1825, wheelbase=2.78, steerRatio=14.4 * 1.15) # 15% higher at the center seems reasonable + ) + KIA_STINGER_2022 = HyundaiPlatformConfig( + "KIA STINGER 2022", + HyundaiCarInfo("Kia Stinger 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + KIA_STINGER.specs, + ) + KIA_CEED = HyundaiPlatformConfig( + "KIA CEED INTRO ED 2019", + HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])), + CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.LEGACY, + ) + KIA_EV6 = HyundaiCanFDPlatformConfig( + "KIA EV6 2022", + [ + HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), + HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])) + ], + CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65), + flags=HyundaiFlags.EV, + ) + KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig( + "KIA CARNIVAL 4TH GEN", + [ + HyundaiCarInfo("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])), + HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k])) + ], + CarSpecs(mass=2087, wheelbase=3.09, steerRatio=14.23), + flags=HyundaiFlags.RADAR_SCC, + ) # Genesis - CAR.GENESIS_GV60_EV_1ST_GEN: [ - HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), - ], - CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), - CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), - CAR.GENESIS_GV70_1ST_GEN: [ - HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), - HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), - ], - CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), - CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), - CAR.GENESIS_GV80: HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), -} + GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig( + "GENESIS GV60 ELECTRIC 1ST GEN", + [ + HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + ], + CarSpecs(mass=2205, wheelbase=2.9, steerRatio=12.6), # steerRatio: https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1. + flags=HyundaiFlags.EV, + ) + GENESIS_G70 = HyundaiPlatformConfig( + "GENESIS G70 2018", + HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), + CarSpecs(mass=1640, wheelbase=2.84, steerRatio=13.56), + flags=HyundaiFlags.LEGACY, + ) + GENESIS_G70_2020 = HyundaiPlatformConfig( + "GENESIS G70 2020", + HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), + CarSpecs(mass=3673 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=12.9), + flags=HyundaiFlags.MANDO_RADAR, + ) + GENESIS_GV70_1ST_GEN = HyundaiCanFDPlatformConfig( + "GENESIS GV70 1ST GEN", + [ + HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), + ], + CarSpecs(mass=1950, wheelbase=2.87, steerRatio=14.6), + flags=HyundaiFlags.RADAR_SCC, + ) + GENESIS_G80 = HyundaiPlatformConfig( + "GENESIS G80 2017", + HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5), + flags=HyundaiFlags.LEGACY, + ) + GENESIS_G90 = HyundaiPlatformConfig( + "GENESIS G90 2017", + HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), + CarSpecs(mass=2200, wheelbase=3.15, steerRatio=12.069), + ) + GENESIS_GV80 = HyundaiCanFDPlatformConfig( + "GENESIS GV80 2023", + HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), + CarSpecs(mass=2258, wheelbase=2.95, steerRatio=14.14), + flags=HyundaiFlags.RADAR_SCC, + ) + class Buttons: NONE = 0 @@ -438,7 +732,7 @@ FW_QUERY_CONFIG = FwQueryConfig( Request( [HYUNDAI_VERSION_REQUEST_LONG], [HYUNDAI_VERSION_RESPONSE], - whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac], + whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac, Ecu.eps], bus=0, auxiliary=True, ), @@ -508,6 +802,13 @@ FW_QUERY_CONFIG = FwQueryConfig( obd_multiplexing=False, ), ], + # We lose these ECUs without the comma power on these cars. + # Note that we still attempt to match with them when they are present + non_essential_ecus={ + Ecu.transmission: [CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.PALISADE, CAR.SONATA], + Ecu.engine: [CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.PALISADE, CAR.SONATA], + Ecu.abs: [CAR.PALISADE, CAR.SONATA], + }, extra_ecus=[ (Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms (Ecu.parkingAdas, 0x7b1, None), # ADAS Parking ECU (may exist on all platforms) @@ -518,118 +819,36 @@ FW_QUERY_CONFIG = FwQueryConfig( match_fw_to_car_fuzzy=match_fw_to_car_fuzzy, ) - 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, CAR.CUSTIN_1ST_GEN], - "6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS], + "crc8": CAR.with_flags(HyundaiFlags.CHECKSUM_CRC8), + "6B": CAR.with_flags(HyundaiFlags.CHECKSUM_6B), } CAN_GEARS = { # which message has the gear. hybrid and EV use ELECT_GEAR - "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, - "use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, + "use_cluster_gears": CAR.with_flags(HyundaiFlags.CLUSTER_GEARS), + "use_tcu_gears": CAR.with_flags(HyundaiFlags.TCU_GEARS), } -CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_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, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KONA_EV_2ND_GEN, CAR.KIA_K8_HEV_1ST_GEN, - CAR.STARIA_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_4TH_GEN, CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN} +CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD) +CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # These CAN FD cars do not accept communication control to disable the ADAS ECU, # responds with 0x7F2822 - 'conditions not correct' -CANFD_UNSUPPORTED_LONGITUDINAL_CAR = {CAR.IONIQ_6, CAR.KONA_EV_2ND_GEN} +CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) # The camera does SCC on these cars, rather than the radar -CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } - -# 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.KIA_K5_HEV_2020, - CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.AZERA_HEV_6TH_GEN, CAR.KIA_NIRO_PHEV_2022} - -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, CAR.KONA_EV_2ND_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_LTD, CAR.KIA_OPTIMA_G4, - CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022, - CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} - -# these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc. -UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4_FL, - CAR.KIA_OPTIMA_H_G4_FL} - -# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points. -# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py -DBC = { - CAR.AZERA_6TH_GEN: dbc_dict('hyundai_kia_generic', None), - CAR.AZERA_HEV_6TH_GEN: dbc_dict('hyundai_kia_generic', None), - CAR.ELANTRA: dbc_dict('hyundai_kia_generic', None), - CAR.ELANTRA_GT_I30: dbc_dict('hyundai_kia_generic', None), - CAR.ELANTRA_2021: dbc_dict('hyundai_kia_generic', None), - CAR.ELANTRA_HEV_2021: dbc_dict('hyundai_kia_generic', None), - CAR.GENESIS_G70: dbc_dict('hyundai_kia_generic', None), - CAR.GENESIS_G70_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.GENESIS_G80: dbc_dict('hyundai_kia_generic', None), - CAR.GENESIS_G90: dbc_dict('hyundai_kia_generic', None), - CAR.HYUNDAI_GENESIS: dbc_dict('hyundai_kia_generic', None), - CAR.IONIQ_PHEV_2019: dbc_dict('hyundai_kia_generic', None), - CAR.IONIQ_PHEV: dbc_dict('hyundai_kia_generic', None), - CAR.IONIQ_EV_2020: dbc_dict('hyundai_kia_generic', None), - CAR.IONIQ_EV_LTD: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.IONIQ: dbc_dict('hyundai_kia_generic', None), - CAR.IONIQ_HEV_2022: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_K5_HEV_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_OPTIMA_G4: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_OPTIMA_G4_FL: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_OPTIMA_H_G4_FL: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format - CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_STINGER_2022: dbc_dict('hyundai_kia_generic', None), - CAR.KONA: dbc_dict('hyundai_kia_generic', None), - CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None), - CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None), - CAR.KONA_HEV: dbc_dict('hyundai_kia_generic', None), - CAR.SANTA_FE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.SANTA_FE_2022: dbc_dict('hyundai_kia_generic', None), - CAR.SANTA_FE_HEV_2022: dbc_dict('hyundai_kia_generic', None), - CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None), - CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format - CAR.TUCSON: dbc_dict('hyundai_kia_generic', None), - CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_EV6: dbc_dict('hyundai_canfd', None), - CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.TUCSON_4TH_GEN: dbc_dict('hyundai_canfd', None), - CAR.IONIQ_5: dbc_dict('hyundai_canfd', None), - CAR.IONIQ_6: dbc_dict('hyundai_canfd', None), - CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None), - CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None), - CAR.GENESIS_GV60_EV_1ST_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_SORENTO_4TH_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_NIRO_HEV_2ND_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_NIRO_EV_2ND_GEN: dbc_dict('hyundai_canfd', None), - CAR.GENESIS_GV80: dbc_dict('hyundai_canfd', None), - CAR.KIA_CARNIVAL_4TH_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_SORENTO_HEV_4TH_GEN: dbc_dict('hyundai_canfd', None), - CAR.KONA_EV_2ND_GEN: dbc_dict('hyundai_canfd', None), - CAR.KIA_K8_HEV_1ST_GEN: dbc_dict('hyundai_canfd', None), - CAR.CUSTIN_1ST_GEN: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_NIRO_PHEV_2022: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), - CAR.STARIA_4TH_GEN: dbc_dict('hyundai_canfd', None), -} +CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC) + +HYBRID_CAR = CAR.with_flags(HyundaiFlags.HYBRID) + +EV_CAR = CAR.with_flags(HyundaiFlags.EV) + +LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY) + +UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL) + +DBC = CAR.create_dbc_map() + +if __name__ == "__main__": + CAR.print_debug(HyundaiFlags) diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 491138f788..9e9e668981 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -112,15 +112,14 @@ class CarInterfaceBase(ABC): def get_params(cls, candidate: Platform, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): ret = CarInterfaceBase.get_std_params(candidate) - if hasattr(candidate, "config"): - if candidate.config.specs is not None: - ret.mass = candidate.config.specs.mass - ret.wheelbase = candidate.config.specs.wheelbase - ret.steerRatio = candidate.config.specs.steerRatio - ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio - ret.minEnableSpeed = candidate.config.specs.minEnableSpeed - ret.minSteerSpeed = candidate.config.specs.minSteerSpeed - ret.flags |= int(candidate.config.flags) + ret.mass = candidate.config.specs.mass + ret.wheelbase = candidate.config.specs.wheelbase + ret.steerRatio = candidate.config.specs.steerRatio + ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio + ret.minEnableSpeed = candidate.config.specs.minEnableSpeed + ret.minSteerSpeed = candidate.config.specs.minSteerSpeed + ret.tireStiffnessFactor = candidate.config.specs.tireStiffnessFactor + ret.flags |= int(candidate.config.flags) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) @@ -335,7 +334,6 @@ class RadarInterfaceBase(ABC): self.delay = 0 self.radar_ts = CP.radarTimeStep self.frame = 0 - self.no_radar_sleep = 'NO_RADAR_SLEEP' in os.environ def update(self, can_strings): self.frame += 1 diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 3f3b7a9494..8d3a59b4ea 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -36,13 +36,13 @@ class CarController(CarControllerBase): if self.frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7): # Cancel Stock ACC if it's enabled while OP is disengaged # Send at a rate of 10hz until we sync with stock ACC state - can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL)) + can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.CANCEL)) else: self.brake_counter = 0 if CC.cruiseControl.resume and self.frame % 5 == 0: # Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds # Send Resume button when planner wants car to move - can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME)) + can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.RESUME)) self.apply_steer_last = apply_steer @@ -55,7 +55,7 @@ class CarController(CarControllerBase): can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required)) # send steering command - can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint, + can_sends.append(mazdacan.create_steering_control(self.packer, self.CP, self.frame, apply_steer, CS.cam_lkas)) new_actuators = CC.actuators.copy() diff --git a/selfdrive/car/mazda/carstate.py b/selfdrive/car/mazda/carstate.py index c0819592d4..37a67ecd93 100644 --- a/selfdrive/car/mazda/carstate.py +++ b/selfdrive/car/mazda/carstate.py @@ -3,7 +3,7 @@ from openpilot.common.conversions import Conversions as CV from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser from openpilot.selfdrive.car.interfaces import CarStateBase -from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, GEN1 +from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags class CarState(CarStateBase): def __init__(self, CP): @@ -116,7 +116,7 @@ class CarState(CarStateBase): ("WHEEL_SPEEDS", 100), ] - if CP.carFingerprint in GEN1: + if CP.flags & MazdaFlags.GEN1: messages += [ ("ENGINE_DATA", 100), ("CRZ_CTRL", 50), @@ -136,7 +136,7 @@ class CarState(CarStateBase): def get_cam_can_parser(CP): messages = [] - if CP.carFingerprint in GEN1: + if CP.flags & MazdaFlags.GEN1: messages += [ # sig_address, frequency ("CAM_LANEINFO", 2), diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index 7ac93d9dee..85be0166ce 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -20,27 +20,9 @@ class CarInterface(CarInterfaceBase): ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.8 - ret.tireStiffnessFactor = 0.70 # not optimized yet CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - if candidate in (CAR.CX5, CAR.CX5_2022): - ret.mass = 3655 * CV.LB_TO_KG - ret.wheelbase = 2.7 - ret.steerRatio = 15.5 - elif candidate in (CAR.CX9, CAR.CX9_2021): - ret.mass = 4217 * CV.LB_TO_KG - ret.wheelbase = 3.1 - ret.steerRatio = 17.6 - elif candidate == CAR.MAZDA3: - ret.mass = 2875 * CV.LB_TO_KG - ret.wheelbase = 2.7 - ret.steerRatio = 14.0 - elif candidate == CAR.MAZDA6: - ret.mass = 3443 * CV.LB_TO_KG - ret.wheelbase = 2.83 - ret.steerRatio = 15.5 - if candidate not in (CAR.CX5_2022, ): ret.minSteerSpeed = LKAS_LIMITS.DISABLE_SPEED * CV.KPH_TO_MS diff --git a/selfdrive/car/mazda/mazdacan.py b/selfdrive/car/mazda/mazdacan.py index e350c5587f..74f6af04c5 100644 --- a/selfdrive/car/mazda/mazdacan.py +++ b/selfdrive/car/mazda/mazdacan.py @@ -1,7 +1,7 @@ -from openpilot.selfdrive.car.mazda.values import GEN1, Buttons +from openpilot.selfdrive.car.mazda.values import Buttons, MazdaFlags -def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas): +def create_steering_control(packer, CP, frame, apply_steer, lkas): tmp = apply_steer + 2048 @@ -45,7 +45,7 @@ def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas): csum = csum % 256 values = {} - if car_fingerprint in GEN1: + if CP.flags & MazdaFlags.GEN1: values = { "LKAS_REQUEST": apply_steer, "CTR": ctr, @@ -88,12 +88,12 @@ def create_alert_command(packer, cam_msg: dict, ldw: bool, steer_required: bool) return packer.make_can_msg("CAM_LANEINFO", 0, values) -def create_button_cmd(packer, car_fingerprint, counter, button): +def create_button_cmd(packer, CP, counter, button): can = int(button == Buttons.CANCEL) res = int(button == Buttons.RESUME) - if car_fingerprint in GEN1: + if CP.flags & MazdaFlags.GEN1: values = { "CAN_OFF": can, "CAN_OFF_INV": (can + 1) % 2, diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index eaf76d6a72..b55690f39a 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -1,8 +1,9 @@ from dataclasses import dataclass, field -from enum import StrEnum +from enum import IntFlag from cereal import car -from openpilot.selfdrive.car import dbc_dict +from openpilot.common.conversions import Conversions as CV +from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -25,29 +26,60 @@ class CarControllerParams: pass -class CAR(StrEnum): - CX5 = "MAZDA CX-5" - CX9 = "MAZDA CX-9" - MAZDA3 = "MAZDA 3" - MAZDA6 = "MAZDA 6" - CX9_2021 = "MAZDA CX-9 2021" - CX5_2022 = "MAZDA CX-5 2022" - - @dataclass class MazdaCarInfo(CarInfo): package: str = "All" car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.mazda])) -CAR_INFO: dict[str, MazdaCarInfo | list[MazdaCarInfo]] = { - CAR.CX5: MazdaCarInfo("Mazda CX-5 2017-21"), - CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"), - CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"), - CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"), - CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4"), - CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022-24"), -} +@dataclass(frozen=True, kw_only=True) +class MazdaCarSpecs(CarSpecs): + tireStiffnessFactor: float = 0.7 # not optimized yet + + +class MazdaFlags(IntFlag): + # Static flags + # Gen 1 hardware: same CAN messages and same camera + GEN1 = 1 + + +@dataclass +class MazdaPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('mazda_2017', None)) + flags: int = MazdaFlags.GEN1 + + +class CAR(Platforms): + CX5 = MazdaPlatformConfig( + "MAZDA CX-5", + MazdaCarInfo("Mazda CX-5 2017-21"), + MazdaCarSpecs(mass=3655 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.5) + ) + CX9 = MazdaPlatformConfig( + "MAZDA CX-9", + MazdaCarInfo("Mazda CX-9 2016-20"), + MazdaCarSpecs(mass=4217 * CV.LB_TO_KG, wheelbase=3.1, steerRatio=17.6) + ) + MAZDA3 = MazdaPlatformConfig( + "MAZDA 3", + MazdaCarInfo("Mazda 3 2017-18"), + MazdaCarSpecs(mass=2875 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=14.0) + ) + MAZDA6 = MazdaPlatformConfig( + "MAZDA 6", + MazdaCarInfo("Mazda 6 2017-20"), + MazdaCarSpecs(mass=3443 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=15.5) + ) + CX9_2021 = MazdaPlatformConfig( + "MAZDA CX-9 2021", + MazdaCarInfo("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4"), + CX9.specs + ) + CX5_2022 = MazdaPlatformConfig( + "MAZDA CX-5 2022", + MazdaCarInfo("Mazda CX-5 2022-24"), + CX5.specs, + ) class LKAS_LIMITS: @@ -75,15 +107,4 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) - -DBC = { - CAR.CX5: dbc_dict('mazda_2017', None), - CAR.CX9: dbc_dict('mazda_2017', None), - CAR.MAZDA3: dbc_dict('mazda_2017', None), - CAR.MAZDA6: dbc_dict('mazda_2017', None), - CAR.CX9_2021: dbc_dict('mazda_2017', None), - CAR.CX5_2022: dbc_dict('mazda_2017', None), -} - -# Gen 1 hardware: same CAN messages and same camera -GEN1 = {CAR.CX5, CAR.CX9, CAR.CX9_2021, CAR.MAZDA3, CAR.MAZDA6, CAR.CX5_2022} +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/mock/values.py b/selfdrive/car/mock/values.py index e75665c98f..ddab599a93 100644 --- a/selfdrive/car/mock/values.py +++ b/selfdrive/car/mock/values.py @@ -1,12 +1,10 @@ -from enum import StrEnum +from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms -from openpilot.selfdrive.car.docs_definitions import CarInfo - -class CAR(StrEnum): - MOCK = 'mock' - - -CAR_INFO: dict[str, CarInfo | list[CarInfo] | None] = { - CAR.MOCK: None, -} +class CAR(Platforms): + MOCK = PlatformConfig( + 'mock', + None, + CarSpecs(mass=1700, wheelbase=2.7, steerRatio=13), + {} + ) diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index 7af1700e0b..c0ccb0febf 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -40,13 +40,13 @@ class CAR(Platforms): XTRAIL = NissanPlaformConfig( "NISSAN X-TRAIL 2017", NissanCarInfo("Nissan X-Trail 2017"), - specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + NissanCarSpecs(mass=1610, wheelbase=2.705) ) LEAF = NissanPlaformConfig( "NISSAN LEAF 2018", NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), - dbc_dict=dbc_dict('nissan_leaf_2018_generated', None), - specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + NissanCarSpecs(mass=1610, wheelbase=2.705), + dbc_dict('nissan_leaf_2018_generated', None), ) # Leaf with ADAS ECU found behind instrument cluster instead of glovebox # Currently the only known difference between them is the inverted seatbelt signal. @@ -54,16 +54,15 @@ class CAR(Platforms): ROGUE = NissanPlaformConfig( "NISSAN ROGUE 2019", NissanCarInfo("Nissan Rogue 2018-20"), - specs=NissanCarSpecs(mass=1610, wheelbase=2.705) + NissanCarSpecs(mass=1610, wheelbase=2.705) ) ALTIMA = NissanPlaformConfig( "NISSAN ALTIMA 2020", NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])), - specs=NissanCarSpecs(mass=1492, wheelbase=2.824) + NissanCarSpecs(mass=1492, wheelbase=2.824) ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() # Default diagnostic session diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index ecf718bb3a..30e186bd09 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -10,47 +10,45 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs): - platform_flags = candidate.config.flags - ret.carName = "subaru" ret.radarUnavailable = True # for HYBRID CARS to be upstreamed, we need: # - replacement for ES_Distance so we can cancel the cruise control # - to find the Cruise_Activated bit from the car # - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) - ret.dashcamOnly = bool(platform_flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID)) + ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID)) ret.autoResumeSng = False # Detect infotainment message sent from the camera - if not (platform_flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]: + if not (ret.flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]: ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value - if platform_flags & SubaruFlags.PREGLOBAL: + if ret.flags & SubaruFlags.PREGLOBAL: ret.enableBsm = 0x25c in fingerprint[0] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)] else: ret.enableBsm = 0x228 in fingerprint[0] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)] - if platform_flags & SubaruFlags.GLOBAL_GEN2: + if ret.flags & SubaruFlags.GLOBAL_GEN2: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2 ret.steerLimitTimer = 0.4 ret.steerActuatorDelay = 0.1 - if platform_flags & SubaruFlags.LKAS_ANGLE: + if ret.flags & SubaruFlags.LKAS_ANGLE: ret.steerControlType = car.CarParams.SteerControlType.angle else: CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate in (CAR.ASCENT, CAR.ASCENT_2023): - ret.steerActuatorDelay = 0.3 # end-to-end angle controller + ret.steerActuatorDelay = 0.3 # end-to-end angle controller ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00003 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]] elif candidate == CAR.IMPREZA: - ret.steerActuatorDelay = 0.4 # end-to-end angle controller + ret.steerActuatorDelay = 0.4 # end-to-end angle controller ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]] @@ -85,12 +83,11 @@ class CarInterface(CarInterfaceBase): else: raise ValueError(f"unknown car: {candidate}") - LONG_UNAVAILABLE = SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL| SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID - - ret.experimentalLongitudinalAvailable = not (platform_flags & LONG_UNAVAILABLE) + ret.experimentalLongitudinalAvailable = not (ret.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL | + SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID)) ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable - if platform_flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl: + if ret.flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl: ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value if ret.openpilotLongitudinalControl: diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index a7c9a22e2c..cc963404b7 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -53,8 +53,11 @@ class CarControllerParams: class SubaruFlags(IntFlag): + # Detected flags SEND_INFOTAINMENT = 1 DISABLE_EYESIGHT = 2 + + # Static flags GLOBAL_GEN2 = 4 # Cars that temporarily fault when steering angle rate is greater than some threshold. @@ -106,24 +109,31 @@ class SubaruPlatformConfig(PlatformConfig): self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None) +@dataclass +class SubaruGen2PlatformConfig(SubaruPlatformConfig): + def init(self): + super().init() + self.flags |= SubaruFlags.GLOBAL_GEN2 + if not (self.flags & SubaruFlags.LKAS_ANGLE): + self.flags |= SubaruFlags.STEER_RATE_LIMITED + + class CAR(Platforms): # Global platform ASCENT = SubaruPlatformConfig( "SUBARU ASCENT LIMITED 2019", SubaruCarInfo("Subaru Ascent 2019-21", "All"), - specs=CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5), + CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5), ) - OUTBACK = SubaruPlatformConfig( + OUTBACK = SubaruGen2PlatformConfig( "SUBARU OUTBACK 6TH GEN", SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), - specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), - flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED, + CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), ) - LEGACY = SubaruPlatformConfig( + LEGACY = SubaruGen2PlatformConfig( "SUBARU LEGACY 7TH GEN", SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), - specs=OUTBACK.specs, - flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED, + OUTBACK.specs, ) IMPREZA = SubaruPlatformConfig( "SUBARU IMPREZA LIMITED 2019", @@ -132,7 +142,7 @@ class CAR(Platforms): SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), ], - specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15), + CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15), ) IMPREZA_2020 = SubaruPlatformConfig( "SUBARU IMPREZA SPORT 2020", @@ -141,75 +151,75 @@ class CAR(Platforms): SubaruCarInfo("Subaru Crosstrek 2020-23"), SubaruCarInfo("Subaru XV 2020-21"), ], - specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17), + CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.STEER_RATE_LIMITED, ) # TODO: is there an XV and Impreza too? CROSSTREK_HYBRID = SubaruPlatformConfig( "SUBARU CROSSTREK HYBRID 2020", SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])), - specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), + CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.HYBRID, ) FORESTER = SubaruPlatformConfig( "SUBARU FORESTER 2019", SubaruCarInfo("Subaru Forester 2019-21", "All"), - specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), + CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.STEER_RATE_LIMITED, ) FORESTER_HYBRID = SubaruPlatformConfig( "SUBARU FORESTER HYBRID 2020", SubaruCarInfo("Subaru Forester Hybrid 2020"), - specs=FORESTER.specs, + FORESTER.specs, flags=SubaruFlags.HYBRID, ) # Pre-global FORESTER_PREGLOBAL = SubaruPlatformConfig( "SUBARU FORESTER 2017 - 2018", SubaruCarInfo("Subaru Forester 2017-18"), + CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20), dbc_dict('subaru_forester_2017_generated', None), - specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20), flags=SubaruFlags.PREGLOBAL, ) LEGACY_PREGLOBAL = SubaruPlatformConfig( "SUBARU LEGACY 2015 - 2018", SubaruCarInfo("Subaru Legacy 2015-18"), + CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5), dbc_dict('subaru_outback_2015_generated', None), - specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5), flags=SubaruFlags.PREGLOBAL, ) OUTBACK_PREGLOBAL = SubaruPlatformConfig( "SUBARU OUTBACK 2015 - 2017", SubaruCarInfo("Subaru Outback 2015-17"), + FORESTER_PREGLOBAL.specs, dbc_dict('subaru_outback_2015_generated', None), - specs=FORESTER_PREGLOBAL.specs, flags=SubaruFlags.PREGLOBAL, ) OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig( "SUBARU OUTBACK 2018 - 2019", SubaruCarInfo("Subaru Outback 2018-19"), + FORESTER_PREGLOBAL.specs, dbc_dict('subaru_outback_2019_generated', None), - specs=FORESTER_PREGLOBAL.specs, flags=SubaruFlags.PREGLOBAL, ) # Angle LKAS FORESTER_2022 = SubaruPlatformConfig( "SUBARU FORESTER 2022", SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])), - specs=FORESTER.specs, + FORESTER.specs, flags=SubaruFlags.LKAS_ANGLE, ) - OUTBACK_2023 = SubaruPlatformConfig( + OUTBACK_2023 = SubaruGen2PlatformConfig( "SUBARU OUTBACK 7TH GEN", SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), - specs=OUTBACK.specs, - flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE, + OUTBACK.specs, + flags=SubaruFlags.LKAS_ANGLE, ) - ASCENT_2023 = SubaruPlatformConfig( + ASCENT_2023 = SubaruGen2PlatformConfig( "SUBARU ASCENT 2023", SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), - specs=ASCENT.specs, - flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE, + ASCENT.specs, + flags=SubaruFlags.LKAS_ANGLE, ) @@ -253,5 +263,7 @@ FW_QUERY_CONFIG = FwQueryConfig( } ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() + +if __name__ == "__main__": + CAR.print_debug(SubaruFlags) diff --git a/selfdrive/car/tesla/carstate.py b/selfdrive/car/tesla/carstate.py index 2cb4f09d79..bee652ff30 100644 --- a/selfdrive/car/tesla/carstate.py +++ b/selfdrive/car/tesla/carstate.py @@ -2,7 +2,7 @@ import copy from collections import deque from cereal import car from openpilot.common.conversions import Conversions as CV -from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS +from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS from openpilot.selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine @@ -37,13 +37,15 @@ class CarState(CarStateBase): ret.brakePressed = bool(cp.vl["BrakeMessage"]["driverBrakeStatus"] != 1) # Steering wheel - self.hands_on_level = cp.vl["EPAS_sysStatus"]["EPAS_handsOnLevel"] - self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacErrorCode"]), None) - steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacStatus"]), None) + epas_status = cp_cam.vl["EPAS3P_sysStatus"] if self.CP.carFingerprint == CAR.MODELS_RAVEN else cp.vl["EPAS_sysStatus"] - ret.steeringAngleDeg = -cp.vl["EPAS_sysStatus"]["EPAS_internalSAS"] + self.hands_on_level = epas_status["EPAS_handsOnLevel"] + self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(epas_status["EPAS_eacErrorCode"]), None) + steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(epas_status["EPAS_eacStatus"]), None) + + ret.steeringAngleDeg = -epas_status["EPAS_internalSAS"] ret.steeringRateDeg = -cp.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"] # This is from a different angle sensor, and at different rate - ret.steeringTorque = -cp.vl["EPAS_sysStatus"]["EPAS_torsionBarTorque"] + ret.steeringTorque = -epas_status["EPAS_torsionBarTorque"] ret.steeringPressed = (self.hands_on_level > 0) ret.steerFaultPermanent = steer_status == "EAC_FAULT" ret.steerFaultTemporary = (self.steer_warning not in ("EAC_ERROR_IDLE", "EAC_ERROR_HANDS_ON")) @@ -85,7 +87,10 @@ class CarState(CarStateBase): ret.rightBlinker = (cp.vl["GTW_carState"]["BC_indicatorRStatus"] == 1) # Seatbelt - ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1) + if self.CP.carFingerprint == CAR.MODELS_RAVEN: + ret.seatbeltUnlatched = (cp.vl["DriverSeat"]["buckleStatus"] != 1) + else: + ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1) # TODO: blindspot @@ -111,9 +116,14 @@ class CarState(CarStateBase): ("DI_state", 10), ("STW_ACTN_RQ", 10), ("GTW_carState", 10), - ("SDM1", 10), ("BrakeMessage", 50), ] + + if CP.carFingerprint == CAR.MODELS_RAVEN: + messages.append(("DriverSeat", 20)) + else: + messages.append(("SDM1", 10)) + return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.chassis) @staticmethod @@ -122,4 +132,8 @@ class CarState(CarStateBase): # sig_address, frequency ("DAS_control", 40), ] + + if CP.carFingerprint == CAR.MODELS_RAVEN: + messages.append(("EPAS3P_sysStatus", 100)) + return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.autopilot_chassis) diff --git a/selfdrive/car/tesla/fingerprints.py b/selfdrive/car/tesla/fingerprints.py index 772ca59efa..9b6f3865be 100644 --- a/selfdrive/car/tesla/fingerprints.py +++ b/selfdrive/car/tesla/fingerprints.py @@ -25,4 +25,15 @@ FW_VERSIONS = { b'\x10#\x01', ], }, + CAR.MODELS_RAVEN: { + (Ecu.electricBrakeBooster, 0x64d, None): [ + b'1037123-00-A', + ], + (Ecu.fwdRadar, 0x671, None): [ + b'\x01\x00\x99\x02\x01\x00\x10\x00\x00AP8.3.03\x00\x10', + ], + (Ecu.eps, 0x730, None): [ + b'SX_0.0.0 (99),SR013.7', + ], + }, } diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index e06139729c..f989886738 100755 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -28,27 +28,20 @@ class CarInterface(CarInterfaceBase): # Check if we have messages on an auxiliary panda, and that 0x2bf (DAS_control) is present on the AP powertrain bus # If so, we assume that it is connected to the longitudinal harness. + flags = (Panda.FLAG_TESLA_RAVEN if candidate == CAR.MODELS_RAVEN else 0) if (CANBUS.autopilot_powertrain in fingerprint.keys()) and (0x2bf in fingerprint[CANBUS.autopilot_powertrain].keys()): ret.openpilotLongitudinalControl = True + flags |= Panda.FLAG_TESLA_LONG_CONTROL ret.safetyConfigs = [ - get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL), - get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN), + get_safety_config(car.CarParams.SafetyModel.tesla, flags), + get_safety_config(car.CarParams.SafetyModel.tesla, flags | Panda.FLAG_TESLA_POWERTRAIN), ] else: ret.openpilotLongitudinalControl = False - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, 0)] + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, flags)] ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0.25 - - if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS): - ret.mass = 2100. - ret.wheelbase = 2.959 - ret.centerToFront = ret.wheelbase * 0.5 - ret.steerRatio = 15.0 - else: - raise ValueError(f"Unsupported car: {candidate}") - return ret def _update(self, c): diff --git a/selfdrive/car/tesla/radar_interface.py b/selfdrive/car/tesla/radar_interface.py index b3e7c7fcb1..599ab31059 100755 --- a/selfdrive/car/tesla/radar_interface.py +++ b/selfdrive/car/tesla/radar_interface.py @@ -1,38 +1,33 @@ #!/usr/bin/env python3 from cereal import car from opendbc.can.parser import CANParser -from openpilot.selfdrive.car.tesla.values import DBC, CANBUS +from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS from openpilot.selfdrive.car.interfaces import RadarInterfaceBase -RADAR_MSGS_A = list(range(0x310, 0x36E, 3)) -RADAR_MSGS_B = list(range(0x311, 0x36F, 3)) -NUM_POINTS = len(RADAR_MSGS_A) - -def get_radar_can_parser(CP): - # Status messages - messages = [ - ('TeslaRadarSguInfo', 10), - ] - - # Radar tracks. There are also raw point clouds available, - # we don't use those. - for i in range(NUM_POINTS): - msg_id_a = RADAR_MSGS_A[i] - msg_id_b = RADAR_MSGS_B[i] - messages.extend([ - (msg_id_a, 8), - (msg_id_b, 8), - ]) - - return CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar) class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) - self.rcp = get_radar_can_parser(CP) + self.CP = CP + + if CP.carFingerprint == CAR.MODELS_RAVEN: + messages = [('RadarStatus', 16)] + self.num_points = 40 + self.trigger_msg = 1119 + else: + messages = [('TeslaRadarSguInfo', 10)] + self.num_points = 32 + self.trigger_msg = 878 + + for i in range(self.num_points): + messages.extend([ + (f'RadarPoint{i}_A', 16), + (f'RadarPoint{i}_B', 16), + ]) + + self.rcp = CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar) self.updated_messages = set() self.track_id = 0 - self.trigger_msg = RADAR_MSGS_B[-1] def update(self, can_strings): if self.rcp is None: @@ -48,17 +43,24 @@ class RadarInterface(RadarInterfaceBase): # Errors errors = [] - sgu_info = self.rcp.vl['TeslaRadarSguInfo'] if not self.rcp.can_valid: errors.append('canError') - if sgu_info['RADC_HWFail'] or sgu_info['RADC_SGUFail'] or sgu_info['RADC_SensorDirty']: - errors.append('fault') + + if self.CP.carFingerprint == CAR.MODELS_RAVEN: + radar_status = self.rcp.vl['RadarStatus'] + if radar_status['sensorBlocked'] or radar_status['shortTermUnavailable'] or radar_status['vehDynamicsError']: + errors.append('fault') + else: + radar_status = self.rcp.vl['TeslaRadarSguInfo'] + if radar_status['RADC_HWFail'] or radar_status['RADC_SGUFail'] or radar_status['RADC_SensorDirty']: + errors.append('fault') + ret.errors = errors # Radar tracks - for i in range(NUM_POINTS): - msg_a = self.rcp.vl[RADAR_MSGS_A[i]] - msg_b = self.rcp.vl[RADAR_MSGS_B[i]] + for i in range(self.num_points): + msg_a = self.rcp.vl[f'RadarPoint{i}_A'] + msg_b = self.rcp.vl[f'RadarPoint{i}_B'] # Make sure msg A and B are together if msg_a['Index'] != msg_b['Index2']: diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py index 2a51d15da8..ca3bb38a7a 100644 --- a/selfdrive/car/tesla/values.py +++ b/selfdrive/car/tesla/values.py @@ -1,8 +1,7 @@ from collections import namedtuple -from enum import StrEnum from cereal import car -from openpilot.selfdrive.car import AngleRateLimit, dbc_dict +from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -10,22 +9,25 @@ Ecu = car.CarParams.Ecu Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) - -class CAR(StrEnum): - AP1_MODELS = 'TESLA AP1 MODEL S' - AP2_MODELS = 'TESLA AP2 MODEL S' - - -CAR_INFO: dict[str, CarInfo | list[CarInfo]] = { - CAR.AP1_MODELS: CarInfo("Tesla AP1 Model S", "All"), - CAR.AP2_MODELS: CarInfo("Tesla AP2 Model S", "All"), -} - - -DBC = { - CAR.AP2_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'), - CAR.AP1_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'), -} +class CAR(Platforms): + AP1_MODELS = PlatformConfig( + 'TESLA AP1 MODEL S', + CarInfo("Tesla AP1 Model S", "All"), + CarSpecs(mass=2100., wheelbase=2.959, steerRatio=15.0), + dbc_dict('tesla_powertrain', 'tesla_radar_bosch_generated', chassis_dbc='tesla_can') + ) + AP2_MODELS = PlatformConfig( + 'TESLA AP2 MODEL S', + CarInfo("Tesla AP2 Model S", "All"), + AP1_MODELS.specs, + AP1_MODELS.dbc_dict + ) + MODELS_RAVEN = PlatformConfig( + 'TESLA MODEL S RAVEN', + CarInfo("Tesla Model S Raven", "All"), + AP1_MODELS.specs, + dbc_dict('tesla_powertrain', 'tesla_radar_continental_generated', chassis_dbc='tesla_can') + ) FW_QUERY_CONFIG = FwQueryConfig( requests=[ @@ -36,6 +38,13 @@ FW_QUERY_CONFIG = FwQueryConfig( rx_offset=0x08, bus=0, ), + Request( + [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.SUPPLIER_SOFTWARE_VERSION_REQUEST], + [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.SUPPLIER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.eps], + rx_offset=0x08, + bus=0, + ), Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.UDS_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.UDS_VERSION_RESPONSE], @@ -46,7 +55,6 @@ FW_QUERY_CONFIG = FwQueryConfig( ] ) - class CANBUS: # Lateral harness chassis = 0 @@ -88,3 +96,6 @@ class CarControllerParams: def __init__(self, CP): pass + + +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 811416ccef..92ee7fa923 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -229,6 +229,7 @@ routes = [ CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), CarTestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1), + CarTestRoute("ffcd23abbbd02219|2024-02-28--14-59-38", VOLKSWAGEN.CADDY_MK3), CarTestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7), # Stock ACC CarTestRoute("3cfdec54aa035f3f|2022-10-13--14-58-58", VOLKSWAGEN.GOLF_MK7), # openpilot longitudinal CarTestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7), @@ -288,6 +289,7 @@ routes = [ CarTestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS), CarTestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS), + CarTestRoute("66c1699b7697267d/2024-03-03--13-09-53", TESLA.MODELS_RAVEN), # Segments that test specific issues # Controls mismatch due to interceptor threshold diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index a454f616cb..02a8d60e3c 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -22,7 +22,7 @@ from openpilot.selfdrive.test.fuzzy_generation import DrawType, FuzzyGenerator ALL_ECUS = list({ecu for ecus in FW_VERSIONS.values() for ecu in ecus.keys()}) -MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '20')) +MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '40')) def get_fuzzy_car_interface_args(draw: DrawType) -> dict: diff --git a/selfdrive/car/tests/test_docs.py b/selfdrive/car/tests/test_docs.py index 0cafb508f7..0ee35dd92d 100755 --- a/selfdrive/car/tests/test_docs.py +++ b/selfdrive/car/tests/test_docs.py @@ -5,10 +5,11 @@ import re import unittest from openpilot.common.basedir import BASEDIR -from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr +from openpilot.selfdrive.car.car_helpers import interfaces from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info from openpilot.selfdrive.car.docs_definitions import Cable, Column, PartType, Star from openpilot.selfdrive.car.honda.values import CAR as HONDA +from openpilot.selfdrive.car.values import PLATFORMS from openpilot.selfdrive.debug.dump_car_info import dump_car_info from openpilot.selfdrive.debug.print_docs_diff import print_car_info_diff @@ -42,10 +43,10 @@ class TestCarDocs(unittest.TestCase): make_model_years[make_model].append(year) def test_missing_car_info(self): - all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys() + all_car_info_platforms = [name for name, config in PLATFORMS.items()] for platform in sorted(interfaces.keys()): with self.subTest(platform=platform): - self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't exist in CarInfo") + self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't have a CarInfo entry") def test_naming_conventions(self): # Asserts market-standard car naming conventions by brand diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index a8bae29391..b9eadc8cd5 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -263,7 +263,7 @@ class TestFwFingerprintTiming(unittest.TestCase): print(f'get_vin {name} case, query time={self.total_time / self.N} seconds') def test_fw_query_timing(self): - total_ref_time = {1: 8.3, 2: 9.2} + total_ref_time = {1: 8.4, 2: 9.3} brand_ref_times = { 1: { 'gm': 1.0, @@ -275,13 +275,14 @@ class TestFwFingerprintTiming(unittest.TestCase): 'mazda': 0.1, 'nissan': 0.8, 'subaru': 0.45, - 'tesla': 0.2, + 'tesla': 0.3, 'toyota': 1.6, 'volkswagen': 0.65, }, 2: { 'ford': 1.6, 'hyundai': 1.85, + 'tesla': 0.3, } } diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 2b29c14f72..b7d20e5a83 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -17,7 +17,7 @@ from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car.fingerprints import all_known_cars from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces -from openpilot.selfdrive.car.honda.values import CAR as HONDA, HONDA_BOSCH +from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags from openpilot.selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute from openpilot.selfdrive.controls.controlsd import Controls from openpilot.selfdrive.test.helpers import read_segment_list @@ -381,7 +381,7 @@ class TestCarModelBase(unittest.TestCase): if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving: self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving()) - if not (self.CP.carName == "honda" and self.CP.carFingerprint not in HONDA_BOSCH): + if not (self.CP.carName == "honda" and not (self.CP.flags & HondaFlags.BOSCH)): if self.safety.get_cruise_engaged_prev() != prev_panda_cruise_engaged: self.assertEqual(CS.cruiseState.enabled, self.safety.get_cruise_engaged_prev()) @@ -442,7 +442,7 @@ class TestCarModelBase(unittest.TestCase): # On most pcmCruise cars, openpilot's state is always tied to the PCM's cruise state. # On Honda Nidec, we always engage on the rising edge of the PCM cruise state, but # openpilot brakes to zero even if the min ACC speed is non-zero (i.e. the PCM disengages). - if self.CP.carName == "honda" and self.CP.carFingerprint not in HONDA_BOSCH: + if self.CP.carName == "honda" and not (self.CP.flags & HondaFlags.BOSCH): # only the rising edges are expected to match if CS.cruiseState.enabled and not CS_prev.cruiseState.enabled: checks['controlsAllowed'] += not self.safety.get_controls_allowed() diff --git a/selfdrive/car/tests/test_platform_configs.py b/selfdrive/car/tests/test_platform_configs.py new file mode 100755 index 0000000000..0b42a2b289 --- /dev/null +++ b/selfdrive/car/tests/test_platform_configs.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +import unittest + +from openpilot.selfdrive.car.values import PLATFORMS + + +class TestPlatformConfigs(unittest.TestCase): + def test_configs(self): + + for platform in PLATFORMS.values(): + with self.subTest(platform=str(platform)): + self.assertTrue(platform.config._frozen) + + if platform != "mock": + self.assertIn("pt", platform.config.dbc_dict) + self.assertTrue(len(platform.config.platform_str) > 0) + + self.assertIsNotNone(platform.config.specs) + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/car/torque_data/override.toml b/selfdrive/car/torque_data/override.toml index 339fc533e5..3de33b88db 100644 --- a/selfdrive/car/torque_data/override.toml +++ b/selfdrive/car/torque_data/override.toml @@ -18,6 +18,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] # Tesla has high torque "TESLA AP1 MODEL S" = [nan, 2.5, nan] "TESLA AP2 MODEL S" = [nan, 2.5, nan] +"TESLA MODEL S RAVEN" = [nan, 2.5, nan] # Guess "FORD BRONCO SPORT 1ST GEN" = [nan, 1.5, nan] @@ -43,6 +44,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "CHEVROLET SILVERADO 1500 2020" = [1.9, 1.9, 0.112] "CHEVROLET TRAILBLAZER 2021" = [1.33, 1.9, 0.16] "CHEVROLET EQUINOX 2019" = [2.5, 2.5, 0.05] +"VOLKSWAGEN CADDY 3RD GEN" = [1.2, 1.2, 0.1] "VOLKSWAGEN PASSAT NMS" = [2.5, 2.5, 0.1] "VOLKSWAGEN SHARAN 2ND GEN" = [2.5, 2.5, 0.1] "HYUNDAI SANTA CRUZ 1ST GEN" = [2.7, 2.7, 0.1] diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index e4ea0d30f9..166f6735a0 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -40,6 +40,9 @@ class CarState(CarStateBase): self.accurate_steer_angle_seen = False self.angle_offset = FirstOrderFilter(None, 60.0, DT_CTRL, initialized=False) + self.prev_distance_button = 0 + self.distance_button = 0 + self.low_speed_lockout = False self.acc_type = 1 self.lkas_hud = {} @@ -163,6 +166,14 @@ class CarState(CarStateBase): if self.CP.carFingerprint != CAR.PRIUS_V: self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"]) + # distance button is wired to the ACC module (camera or radar) + self.prev_distance_button = self.distance_button + if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR): + self.distance_button = cp_acc.vl["ACC_CONTROL"]["DISTANCE"] + + elif self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER: + self.distance_button = cp.vl["SDSU"]["FD_BUTTON"] + return ret @staticmethod @@ -213,6 +224,11 @@ class CarState(CarStateBase): ("PRE_COLLISION", 33), ] + if CP.flags & ToyotaFlags.SMART_DSU and not CP.flags & ToyotaFlags.RADAR_CAN_FILTER: + messages += [ + ("SDSU", 100), + ] + return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0) @staticmethod diff --git a/selfdrive/car/toyota/fingerprints.py b/selfdrive/car/toyota/fingerprints.py index 6b48408a10..86d45532fa 100644 --- a/selfdrive/car/toyota/fingerprints.py +++ b/selfdrive/car/toyota/fingerprints.py @@ -772,16 +772,22 @@ FW_VERSIONS = { b'\x018966353S1000\x00\x00\x00\x00', b'\x018966353S2000\x00\x00\x00\x00', ], + (Ecu.engine, 0x7e0, None): [ + b'\x02353U0000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00', + ], (Ecu.abs, 0x7b0, None): [ b'\x01F15265337200\x00\x00\x00\x00', b'\x01F15265342000\x00\x00\x00\x00', + b'\x01F15265343000\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B53450\x00\x00\x00\x00\x00\x00', + b'8965B53800\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F6201200\x00\x00\x00\x00', b'\x018821F6201300\x00\x00\x00\x00', + b'\x018821F6201400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F5303300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', @@ -1266,6 +1272,7 @@ FW_VERSIONS = { }, CAR.LEXUS_ES: { (Ecu.engine, 0x7e0, None): [ + b'\x02333M4100\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02333M4200\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02333R0000\x00\x00\x00\x00\x00\x00\x00\x00A0C01000\x00\x00\x00\x00\x00\x00\x00\x00', ], diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 988b1b4547..7ad9feed3d 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -1,13 +1,13 @@ from cereal import car -from openpilot.common.conversions import Conversions as CV from panda import Panda from panda.python import uds from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \ MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR -from openpilot.selfdrive.car import get_safety_config +from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase +ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName SteerControlType = car.CarParams.SteerControlType @@ -44,82 +44,37 @@ class CarInterface(CarInterfaceBase): stop_and_go = candidate in TSS2_CAR + # Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it + # 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs + if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR): + ret.flags |= ToyotaFlags.SMART_DSU.value + + if 0x2AA in fingerprint[0] and candidate in NO_DSU_CAR: + ret.flags |= ToyotaFlags.RADAR_CAN_FILTER.value + + # 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) + if candidate == CAR.PRIUS: stop_and_go = True - ret.wheelbase = 2.70 - ret.steerRatio = 15.74 # unknown end-to-end spec - ret.tireStiffnessFactor = 0.6371 # hand-tune - ret.mass = 3045. * CV.LB_TO_KG # Only give steer angle deadzone to for bad angle sensor prius for fw in car_fw: if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00': ret.steerActuatorDelay = 0.25 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2) - elif candidate == CAR.PRIUS_V: - stop_and_go = True - ret.wheelbase = 2.78 - ret.steerRatio = 17.4 - ret.tireStiffnessFactor = 0.5533 - ret.mass = 3340. * CV.LB_TO_KG - - elif candidate in (CAR.RAV4, CAR.RAV4H): - stop_and_go = True if (candidate in CAR.RAV4H) else False - ret.wheelbase = 2.65 - ret.steerRatio = 16.88 # 14.5 is spec end-to-end - ret.tireStiffnessFactor = 0.5533 - ret.mass = 3650. * CV.LB_TO_KG # mean between normal and hybrid - - elif candidate == CAR.COROLLA: - ret.wheelbase = 2.70 - ret.steerRatio = 18.27 - ret.tireStiffnessFactor = 0.444 # not optimized yet - ret.mass = 2860. * CV.LB_TO_KG # mean between normal and hybrid - elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2): stop_and_go = True - ret.wheelbase = 2.79 - ret.steerRatio = 16. # 14.8 is spec end-to-end ret.wheelSpeedFactor = 1.035 - ret.tireStiffnessFactor = 0.5533 - ret.mass = 4481. * CV.LB_TO_KG # mean between min and max - - elif candidate in (CAR.CHR, CAR.CHR_TSS2): - stop_and_go = True - ret.wheelbase = 2.63906 - ret.steerRatio = 13.6 - ret.tireStiffnessFactor = 0.7933 - ret.mass = 3300. * CV.LB_TO_KG - - elif candidate in (CAR.CAMRY, CAR.CAMRY_TSS2): - stop_and_go = True - ret.wheelbase = 2.82448 - ret.steerRatio = 13.7 - ret.tireStiffnessFactor = 0.7933 - ret.mass = 3400. * CV.LB_TO_KG # mean between normal and hybrid - - elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDER_TSS2): - # TODO: TSS-P models can do stop and go, but unclear if it requires sDSU or unplugging DSU - stop_and_go = True - ret.wheelbase = 2.8194 # average of 109.8 and 112.2 in - ret.steerRatio = 16.0 - ret.tireStiffnessFactor = 0.8 - ret.mass = 4516. * CV.LB_TO_KG # mean between normal and hybrid elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALON_TSS2): # starting from 2019, all Avalon variants have stop and go # https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf stop_and_go = candidate != CAR.AVALON - ret.wheelbase = 2.82 - ret.steerRatio = 14.8 # Found at https://pressroom.toyota.com/releases/2016+avalon+product+specs.download - ret.tireStiffnessFactor = 0.7983 - ret.mass = 3505. * CV.LB_TO_KG # mean between normal and hybrid elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023): - ret.wheelbase = 2.68986 - ret.steerRatio = 14.3 - ret.tireStiffnessFactor = 0.7933 - ret.mass = 3585. * CV.LB_TO_KG # Average between ICE and Hybrid ret.lateralTuning.init('pid') ret.lateralTuning.pid.kiBP = [0.0] ret.lateralTuning.pid.kpBP = [0.0] @@ -136,75 +91,14 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kf = 0.00004 break - elif candidate == CAR.COROLLA_TSS2: - ret.wheelbase = 2.67 # Average between 2.70 for sedan and 2.64 for hatchback - ret.steerRatio = 13.9 - ret.tireStiffnessFactor = 0.444 # not optimized yet - ret.mass = 3060. * CV.LB_TO_KG - - elif candidate in (CAR.LEXUS_ES, CAR.LEXUS_ES_TSS2): - ret.wheelbase = 2.8702 - ret.steerRatio = 16.0 # not optimized - ret.tireStiffnessFactor = 0.444 # not optimized yet - ret.mass = 3677. * CV.LB_TO_KG # mean between min and max - - elif candidate == CAR.SIENNA: + elif candidate in (CAR.RAV4H, CAR.CHR, CAR.CAMRY, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_NX): + # TODO: Some of these platforms are not advertised to have full range ACC, are they similar to SNG_WITHOUT_DSU cars? stop_and_go = True - ret.wheelbase = 3.03 - ret.steerRatio = 15.5 - ret.tireStiffnessFactor = 0.444 - ret.mass = 4590. * CV.LB_TO_KG - - elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC): - ret.wheelbase = 2.79908 - ret.steerRatio = 13.3 - ret.tireStiffnessFactor = 0.444 - ret.mass = 3736.8 * CV.LB_TO_KG - - elif candidate == CAR.LEXUS_GS_F: - ret.wheelbase = 2.84988 - ret.steerRatio = 13.3 - ret.tireStiffnessFactor = 0.444 - ret.mass = 4034. * CV.LB_TO_KG - - elif candidate == CAR.LEXUS_CTH: - stop_and_go = True - ret.wheelbase = 2.60 - ret.steerRatio = 18.6 - ret.tireStiffnessFactor = 0.517 - ret.mass = 3108 * CV.LB_TO_KG # mean between min and max - elif candidate in (CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2): - stop_and_go = True - ret.wheelbase = 2.66 - ret.steerRatio = 14.7 - ret.tireStiffnessFactor = 0.444 # not optimized yet - ret.mass = 4070 * CV.LB_TO_KG - - elif candidate == CAR.LEXUS_LC_TSS2: - ret.wheelbase = 2.87 - ret.steerRatio = 13.0 - ret.tireStiffnessFactor = 0.444 # not optimized yet - ret.mass = 4500 * CV.LB_TO_KG - - elif candidate == CAR.PRIUS_TSS2: - ret.wheelbase = 2.70002 # from toyota online sepc. - ret.steerRatio = 13.4 # True steerRatio from older prius - ret.tireStiffnessFactor = 0.6371 # hand-tune - ret.mass = 3115. * CV.LB_TO_KG - - elif candidate == CAR.MIRAI: - stop_and_go = True - ret.wheelbase = 2.91 - ret.steerRatio = 14.8 - ret.tireStiffnessFactor = 0.8 - ret.mass = 4300. * CV.LB_TO_KG - - elif candidate == CAR.ALPHARD_TSS2: - ret.wheelbase = 3.00 - ret.steerRatio = 14.2 - ret.tireStiffnessFactor = 0.444 - ret.mass = 4305. * CV.LB_TO_KG + # TODO: these models can do stop and go, but unclear if it requires sDSU or unplugging DSU. + # For now, don't list stop and go functionality in the docs + if ret.flags & ToyotaFlags.SNG_WITHOUT_DSU: + stop_and_go = stop_and_go or bool(ret.flags & ToyotaFlags.SMART_DSU.value) or (ret.enableDsu and not docs) ret.centerToFront = ret.wheelbase * 0.44 @@ -212,20 +106,10 @@ class CarInterface(CarInterfaceBase): # Detect flipped signals and enable for C-HR and others ret.enableBsm = 0x3F6 in fingerprint[0] and candidate in TSS2_CAR - # Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it - # 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs - if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR): - ret.flags |= ToyotaFlags.SMART_DSU.value - # No radar dbc for cars without DSU which are not TSS 2.0 # TODO: make an adas dbc file for dsu-less models ret.radarUnavailable = DBC[candidate]['radar'] is None or candidate in (NO_DSU_CAR - TSS2_CAR) - # 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) - # if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar. # since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate longitudinal behind experimental toggle use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU) @@ -292,6 +176,9 @@ class CarInterface(CarInterfaceBase): def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam) + if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER): + ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) + # events events = self.create_common_events(ret) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 011d153f70..2be7ca1865 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1,10 +1,11 @@ import re from collections import defaultdict from dataclasses import dataclass, field -from enum import Enum, IntFlag, StrEnum +from enum import Enum, IntFlag from cereal import car from openpilot.common.conversions import Conversions as CV +from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms from openpilot.selfdrive.car import AngleRateLimit, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, CarParts, CarHarness from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -41,50 +42,22 @@ class CarControllerParams: class ToyotaFlags(IntFlag): + # Detected flags HYBRID = 1 SMART_DSU = 2 DISABLE_RADAR = 4 + RADAR_CAN_FILTER = 1024 - -class CAR(StrEnum): - # Toyota - ALPHARD_TSS2 = "TOYOTA ALPHARD 2020" - AVALON = "TOYOTA AVALON 2016" - AVALON_2019 = "TOYOTA AVALON 2019" - AVALON_TSS2 = "TOYOTA AVALON 2022" # TSS 2.5 - CAMRY = "TOYOTA CAMRY 2018" - CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5 - CHR = "TOYOTA C-HR 2018" - CHR_TSS2 = "TOYOTA C-HR 2021" - COROLLA = "TOYOTA COROLLA 2017" - # LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid - COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" - HIGHLANDER = "TOYOTA HIGHLANDER 2017" - HIGHLANDER_TSS2 = "TOYOTA HIGHLANDER 2020" - PRIUS = "TOYOTA PRIUS 2017" - PRIUS_V = "TOYOTA PRIUS v 2017" - PRIUS_TSS2 = "TOYOTA PRIUS TSS2 2021" - RAV4 = "TOYOTA RAV4 2017" - RAV4H = "TOYOTA RAV4 HYBRID 2017" - RAV4_TSS2 = "TOYOTA RAV4 2019" - RAV4_TSS2_2022 = "TOYOTA RAV4 2022" - RAV4_TSS2_2023 = "TOYOTA RAV4 2023" - MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5 - SIENNA = "TOYOTA SIENNA 2018" - - # Lexus - LEXUS_CTH = "LEXUS CT HYBRID 2018" - LEXUS_ES = "LEXUS ES 2018" - LEXUS_ES_TSS2 = "LEXUS ES 2019" - LEXUS_IS = "LEXUS IS 2018" - LEXUS_IS_TSS2 = "LEXUS IS 2023" - LEXUS_NX = "LEXUS NX 2018" - LEXUS_NX_TSS2 = "LEXUS NX 2020" - LEXUS_LC_TSS2 = "LEXUS LC 2024" - LEXUS_RC = "LEXUS RC 2020" - LEXUS_RX = "LEXUS RX 2016" - LEXUS_RX_TSS2 = "LEXUS RX 2020" - LEXUS_GS_F = "LEXUS GS F 2016" + # Static flags + TSS2 = 8 + NO_DSU = 16 + UNSUPPORTED_DSU = 32 + RADAR_ACC = 64 + # these cars use the Lane Tracing Assist (LTA) message for lateral control + ANGLE_CONTROL = 128 + NO_STOP_TIMER = 256 + # these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU + SNG_WITHOUT_DSU = 512 class Footnote(Enum): @@ -99,127 +72,305 @@ class ToyotaCarInfo(CarInfo): car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.toyota_a])) -CAR_INFO: dict[str, ToyotaCarInfo | list[ToyotaCarInfo]] = { +@dataclass +class ToyotaTSS2PlatformConfig(PlatformConfig): + dbc_dict: dict = field(default_factory=lambda: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas')) + + def init(self): + self.flags |= ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU + + if self.flags & ToyotaFlags.RADAR_ACC: + self.dbc_dict = dbc_dict('toyota_nodsu_pt_generated', None) + + +class CAR(Platforms): # Toyota - CAR.ALPHARD_TSS2: [ - ToyotaCarInfo("Toyota Alphard 2019-20"), - ToyotaCarInfo("Toyota Alphard Hybrid 2021"), - ], - CAR.AVALON: [ - ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"), - ToyotaCarInfo("Toyota Avalon 2017-18"), - ], - CAR.AVALON_2019: [ - ToyotaCarInfo("Toyota Avalon 2019-21"), - ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"), - ], - CAR.AVALON_TSS2: [ - ToyotaCarInfo("Toyota Avalon 2022"), - ToyotaCarInfo("Toyota Avalon Hybrid 2022"), - ], - CAR.CAMRY: [ - ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), - ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), - ], - CAR.CAMRY_TSS2: [ - ToyotaCarInfo("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]), - ToyotaCarInfo("Toyota Camry Hybrid 2021-24"), - ], - CAR.CHR: [ - ToyotaCarInfo("Toyota C-HR 2017-20"), - ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"), - ], - CAR.CHR_TSS2: [ - ToyotaCarInfo("Toyota C-HR 2021"), - ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), - ], - CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), - CAR.COROLLA_TSS2: [ - ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), - ToyotaCarInfo("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5), - ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), - # Hybrid platforms - ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"), - ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5), - ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5), - ToyotaCarInfo("Lexus UX Hybrid 2019-23"), - ], - CAR.HIGHLANDER: [ - ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"), - ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"), - ], - CAR.HIGHLANDER_TSS2: [ - ToyotaCarInfo("Toyota Highlander 2020-23"), - ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"), - ], - CAR.PRIUS: [ - ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), - ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), - ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), - ], - CAR.PRIUS_V: ToyotaCarInfo("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED), - CAR.PRIUS_TSS2: [ - ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), - ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), - ], - CAR.RAV4: [ - ToyotaCarInfo("Toyota RAV4 2016", "Toyota Safety Sense P"), - ToyotaCarInfo("Toyota RAV4 2017-18") - ], - CAR.RAV4H: [ - ToyotaCarInfo("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"), - ToyotaCarInfo("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26") - ], - CAR.RAV4_TSS2: [ - ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"), - ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"), - ], - CAR.RAV4_TSS2_2022: [ - ToyotaCarInfo("Toyota RAV4 2022"), - ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), - ], - CAR.RAV4_TSS2_2023: [ - ToyotaCarInfo("Toyota RAV4 2023-24"), - ToyotaCarInfo("Toyota RAV4 Hybrid 2023-24"), - ], - CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"), - CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED), + ALPHARD_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA ALPHARD 2020", + [ + ToyotaCarInfo("Toyota Alphard 2019-20"), + ToyotaCarInfo("Toyota Alphard Hybrid 2021"), + ], + CarSpecs(mass=4305. * CV.LB_TO_KG, wheelbase=3.0, steerRatio=14.2, tireStiffnessFactor=0.444), + ) + AVALON = PlatformConfig( + "TOYOTA AVALON 2016", + [ + ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"), + ToyotaCarInfo("Toyota Avalon 2017-18"), + ], + CarSpecs(mass=3505. * CV.LB_TO_KG, wheelbase=2.82, steerRatio=14.8, tireStiffnessFactor=0.7983), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + ) + AVALON_2019 = PlatformConfig( + "TOYOTA AVALON 2019", + [ + ToyotaCarInfo("Toyota Avalon 2019-21"), + ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"), + ], + AVALON.specs, + dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + ) + AVALON_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA AVALON 2022", # TSS 2.5 + [ + ToyotaCarInfo("Toyota Avalon 2022"), + ToyotaCarInfo("Toyota Avalon Hybrid 2022"), + ], + AVALON.specs, + ) + CAMRY = PlatformConfig( + "TOYOTA CAMRY 2018", + [ + ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), + ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), + ], + CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933), + dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_DSU, + ) + CAMRY_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA CAMRY 2021", # TSS 2.5 + [ + ToyotaCarInfo("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]), + ToyotaCarInfo("Toyota Camry Hybrid 2021-24"), + ], + CAMRY.specs, + ) + CHR = PlatformConfig( + "TOYOTA C-HR 2018", + [ + ToyotaCarInfo("Toyota C-HR 2017-20"), + ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"), + ], + CarSpecs(mass=3300. * CV.LB_TO_KG, wheelbase=2.63906, steerRatio=13.6, tireStiffnessFactor=0.7933), + dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_DSU, + ) + CHR_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA C-HR 2021", + [ + ToyotaCarInfo("Toyota C-HR 2021"), + ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), + ], + CHR.specs, + flags=ToyotaFlags.RADAR_ACC, + ) + COROLLA = PlatformConfig( + "TOYOTA COROLLA 2017", + ToyotaCarInfo("Toyota Corolla 2017-19"), + CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + ) + # LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid + COROLLA_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA COROLLA TSS2 2019", + [ + ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), + ToyotaCarInfo("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5), + ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), + # Hybrid platforms + ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"), + ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5), + ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5), + ToyotaCarInfo("Lexus UX Hybrid 2019-23"), + ], + CarSpecs(mass=3060. * CV.LB_TO_KG, wheelbase=2.67, steerRatio=13.9, tireStiffnessFactor=0.444), + ) + HIGHLANDER = PlatformConfig( + "TOYOTA HIGHLANDER 2017", + [ + ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"), + ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"), + ], + CarSpecs(mass=4516. * CV.LB_TO_KG, wheelbase=2.8194, steerRatio=16.0, tireStiffnessFactor=0.8), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU, + ) + HIGHLANDER_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA HIGHLANDER 2020", + [ + ToyotaCarInfo("Toyota Highlander 2020-23"), + ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"), + ], + HIGHLANDER.specs, + ) + PRIUS = PlatformConfig( + "TOYOTA PRIUS 2017", + [ + ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), + ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), + ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), + ], + CarSpecs(mass=3045. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.74, tireStiffnessFactor=0.6371), + dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + ) + PRIUS_V = PlatformConfig( + "TOYOTA PRIUS v 2017", + ToyotaCarInfo("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED), + CarSpecs(mass=3340. * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.4, tireStiffnessFactor=0.5533), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU, + ) + PRIUS_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA PRIUS TSS2 2021", + [ + ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), + ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"), + ], + CarSpecs(mass=3115. * CV.LB_TO_KG, wheelbase=2.70002, steerRatio=13.4, tireStiffnessFactor=0.6371), + ) + RAV4 = PlatformConfig( + "TOYOTA RAV4 2017", + [ + ToyotaCarInfo("Toyota RAV4 2016", "Toyota Safety Sense P"), + ToyotaCarInfo("Toyota RAV4 2017-18") + ], + CarSpecs(mass=3650. * CV.LB_TO_KG, wheelbase=2.65, steerRatio=16.88, tireStiffnessFactor=0.5533), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + ) + RAV4H = PlatformConfig( + "TOYOTA RAV4 HYBRID 2017", + [ + ToyotaCarInfo("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26") + ], + RAV4.specs, + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_STOP_TIMER, + ) + RAV4_TSS2 = ToyotaTSS2PlatformConfig( + "TOYOTA RAV4 2019", + [ + ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"), + ], + CarSpecs(mass=3585. * CV.LB_TO_KG, wheelbase=2.68986, steerRatio=14.3, tireStiffnessFactor=0.7933), + ) + RAV4_TSS2_2022 = ToyotaTSS2PlatformConfig( + "TOYOTA RAV4 2022", + [ + ToyotaCarInfo("Toyota RAV4 2022"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), + ], + RAV4_TSS2.specs, + flags=ToyotaFlags.RADAR_ACC, + ) + RAV4_TSS2_2023 = ToyotaTSS2PlatformConfig( + "TOYOTA RAV4 2023", + [ + ToyotaCarInfo("Toyota RAV4 2023-24"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2023-24"), + ], + RAV4_TSS2.specs, + flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL, + ) + MIRAI = ToyotaTSS2PlatformConfig( + "TOYOTA MIRAI 2021", # TSS 2.5 + ToyotaCarInfo("Toyota Mirai 2021"), + CarSpecs(mass=4300. * CV.LB_TO_KG, wheelbase=2.91, steerRatio=14.8, tireStiffnessFactor=0.8), + ) + SIENNA = PlatformConfig( + "TOYOTA SIENNA 2018", + ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED), + CarSpecs(mass=4590. * CV.LB_TO_KG, wheelbase=3.03, steerRatio=15.5, tireStiffnessFactor=0.444), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.NO_STOP_TIMER, + ) # Lexus - CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"), - CAR.LEXUS_ES: [ - ToyotaCarInfo("Lexus ES 2017-18"), - ToyotaCarInfo("Lexus ES Hybrid 2017-18"), - ], - CAR.LEXUS_ES_TSS2: [ - ToyotaCarInfo("Lexus ES 2019-24"), - ToyotaCarInfo("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"), - ], - CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"), - CAR.LEXUS_IS_TSS2: ToyotaCarInfo("Lexus IS 2022-23"), - CAR.LEXUS_GS_F: ToyotaCarInfo("Lexus GS F 2016"), - CAR.LEXUS_NX: [ - ToyotaCarInfo("Lexus NX 2018-19"), - ToyotaCarInfo("Lexus NX Hybrid 2018-19"), - ], - CAR.LEXUS_NX_TSS2: [ - ToyotaCarInfo("Lexus NX 2020-21"), - ToyotaCarInfo("Lexus NX Hybrid 2020-21"), - ], - CAR.LEXUS_LC_TSS2: ToyotaCarInfo("Lexus LC 2024"), - CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2018-20"), - CAR.LEXUS_RX: [ - ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"), - ToyotaCarInfo("Lexus RX 2017-19"), - # Hybrid platforms - ToyotaCarInfo("Lexus RX Hybrid 2016", "Lexus Safety System+"), - ToyotaCarInfo("Lexus RX Hybrid 2017-19"), - ], - CAR.LEXUS_RX_TSS2: [ - ToyotaCarInfo("Lexus RX 2020-22"), - ToyotaCarInfo("Lexus RX Hybrid 2020-22"), - ], -} + LEXUS_CTH = PlatformConfig( + "LEXUS CT HYBRID 2018", + ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"), + CarSpecs(mass=3108. * CV.LB_TO_KG, wheelbase=2.6, steerRatio=18.6, tireStiffnessFactor=0.517), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + ) + LEXUS_ES = PlatformConfig( + "LEXUS ES 2018", + [ + ToyotaCarInfo("Lexus ES 2017-18"), + ToyotaCarInfo("Lexus ES Hybrid 2017-18"), + ], + CarSpecs(mass=3677. * CV.LB_TO_KG, wheelbase=2.8702, steerRatio=16.0, tireStiffnessFactor=0.444), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + ) + LEXUS_ES_TSS2 = ToyotaTSS2PlatformConfig( + "LEXUS ES 2019", + [ + ToyotaCarInfo("Lexus ES 2019-24"), + ToyotaCarInfo("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"), + ], + LEXUS_ES.specs, + ) + LEXUS_IS = PlatformConfig( + "LEXUS IS 2018", + ToyotaCarInfo("Lexus IS 2017-19"), + CarSpecs(mass=3736.8 * CV.LB_TO_KG, wheelbase=2.79908, steerRatio=13.3, tireStiffnessFactor=0.444), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.UNSUPPORTED_DSU, + ) + LEXUS_IS_TSS2 = ToyotaTSS2PlatformConfig( + "LEXUS IS 2023", + ToyotaCarInfo("Lexus IS 2022-23"), + LEXUS_IS.specs, + ) + LEXUS_NX = PlatformConfig( + "LEXUS NX 2018", + [ + ToyotaCarInfo("Lexus NX 2018-19"), + ToyotaCarInfo("Lexus NX Hybrid 2018-19"), + ], + CarSpecs(mass=4070. * CV.LB_TO_KG, wheelbase=2.66, steerRatio=14.7, tireStiffnessFactor=0.444), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + ) + LEXUS_NX_TSS2 = ToyotaTSS2PlatformConfig( + "LEXUS NX 2020", + [ + ToyotaCarInfo("Lexus NX 2020-21"), + ToyotaCarInfo("Lexus NX Hybrid 2020-21"), + ], + LEXUS_NX.specs, + ) + LEXUS_LC_TSS2 = ToyotaTSS2PlatformConfig( + "LEXUS LC 2024", + ToyotaCarInfo("Lexus LC 2024"), + CarSpecs(mass=4500. * CV.LB_TO_KG, wheelbase=2.87, steerRatio=13.0, tireStiffnessFactor=0.444), + ) + LEXUS_RC = PlatformConfig( + "LEXUS RC 2020", + ToyotaCarInfo("Lexus RC 2018-20"), + LEXUS_IS.specs, + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.UNSUPPORTED_DSU, + ) + LEXUS_RX = PlatformConfig( + "LEXUS RX 2016", + [ + ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"), + ToyotaCarInfo("Lexus RX 2017-19"), + # Hybrid platforms + ToyotaCarInfo("Lexus RX Hybrid 2016", "Lexus Safety System+"), + ToyotaCarInfo("Lexus RX Hybrid 2017-19"), + ], + CarSpecs(mass=4481. * CV.LB_TO_KG, wheelbase=2.79, steerRatio=16., tireStiffnessFactor=0.5533), + dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), + ) + LEXUS_RX_TSS2 = ToyotaTSS2PlatformConfig( + "LEXUS RX 2020", + [ + ToyotaCarInfo("Lexus RX 2020-22"), + ToyotaCarInfo("Lexus RX Hybrid 2020-22"), + ], + LEXUS_RX.specs, + ) + LEXUS_GS_F = PlatformConfig( + "LEXUS GS F 2016", + ToyotaCarInfo("Lexus GS F 2016"), + CarSpecs(mass=4034. * CV.LB_TO_KG, wheelbase=2.84988, steerRatio=13.3, tireStiffnessFactor=0.444), + dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), + flags=ToyotaFlags.UNSUPPORTED_DSU, + ) + # (addr, cars, bus, 1/freq*100, vl) STATIC_DSU_MSGS = [ @@ -399,7 +550,7 @@ FW_QUERY_CONFIG = FwQueryConfig( Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS, CAR.ALPHARD_TSS2], # On some models, the engine can show on two different addresses Ecu.engine: [CAR.HIGHLANDER, CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS, - CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2], + CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2], }, extra_ecus=[ # All known ECUs on a late-model Toyota vehicle not queried here: @@ -439,62 +590,23 @@ FW_QUERY_CONFIG = FwQueryConfig( STEER_THRESHOLD = 100 -DBC = { - CAR.RAV4H: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.RAV4: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), - CAR.PRIUS: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.PRIUS_V: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), - CAR.COROLLA: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), - CAR.LEXUS_LC_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.LEXUS_RC: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.LEXUS_RX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), - CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.HIGHLANDER: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.HIGHLANDER_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.AVALON: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.AVALON_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.AVALON_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), - CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None), - CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.LEXUS_ES: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), - CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_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.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.LEXUS_GS_F: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), -} - # These cars have non-standard EPS torque scale factors. All others are 73 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.LEXUS_ES_TSS2, - CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2, - CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_LC_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, - CAR.CHR_TSS2} +TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2) -NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY} +NO_DSU_CAR = CAR.with_flags(ToyotaFlags.NO_DSU) # the DSU uses the AEB message for longitudinal on these cars -UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC, CAR.LEXUS_GS_F} +UNSUPPORTED_DSU_CAR = CAR.with_flags(ToyotaFlags.UNSUPPORTED_DSU) # these cars have a radar which sends ACC messages instead of the camera -RADAR_ACC_CAR = {CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2} +RADAR_ACC_CAR = CAR.with_flags(ToyotaFlags.RADAR_ACC) -# these cars use the Lane Tracing Assist (LTA) message for lateral control -ANGLE_CONTROL_CAR = {CAR.RAV4_TSS2_2023} +ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL) # no resume button press required -NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDER, CAR.SIENNA} +NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER) + +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/values.py b/selfdrive/car/values.py index 3e27870915..0c8249838b 100644 --- a/selfdrive/car/values.py +++ b/selfdrive/car/values.py @@ -1,4 +1,4 @@ -from typing import cast +from typing import Any, Callable, cast from openpilot.selfdrive.car.body.values import CAR as BODY from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER from openpilot.selfdrive.car.ford.values import CAR as FORD @@ -6,13 +6,20 @@ from openpilot.selfdrive.car.gm.values import CAR as GM from openpilot.selfdrive.car.honda.values import CAR as HONDA from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI from openpilot.selfdrive.car.mazda.values import CAR as MAZDA +from openpilot.selfdrive.car.mock.values import CAR as MOCK from openpilot.selfdrive.car.nissan.values import CAR as NISSAN from openpilot.selfdrive.car.subaru.values import CAR as SUBARU from openpilot.selfdrive.car.tesla.values import CAR as TESLA from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN -Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN -BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN] +Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | MOCK | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN +BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, MOCK, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN] PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in cast(list[Platform], brand)} + +MapFunc = Callable[[Platform], Any] + + +def create_platform_map(func: MapFunc): + return {str(platform): func(platform) for platform in PLATFORMS.values() if func(platform) is not None} diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index f99e87d5d3..1b1858703d 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -6,7 +6,7 @@ from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.car import apply_driver_steer_torque_limits from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.volkswagen import mqbcan, pqcan -from openpilot.selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams, VolkswagenFlags +from openpilot.selfdrive.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags VisualAlert = car.CarControl.HUDControl.VisualAlert LongCtrlState = car.CarControl.Actuators.LongControlState @@ -16,7 +16,7 @@ class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.CCP = CarControllerParams(CP) - self.CCS = pqcan if CP.carFingerprint in PQ_CARS else mqbcan + self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan self.packer_pt = CANPacker(dbc_name) self.apply_steer_last = 0 diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 25c5bc04bc..9107d41eb3 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -3,13 +3,15 @@ from cereal import car from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser -from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, PQ_CARS, NetworkLocation, TransmissionType, GearShifter, \ +from openpilot.selfdrive.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ CarControllerParams, VolkswagenFlags class CarState(CarStateBase): def __init__(self, CP): super().__init__(CP) + self.frame = 0 + self.eps_init_complete = False self.CCP = CarControllerParams(CP) self.button_states = {button.event_type: False for button in self.CCP.BUTTONS} self.esp_hold_confirmation = False @@ -31,7 +33,7 @@ class CarState(CarStateBase): return button_events def update(self, pt_cp, cam_cp, ext_cp, trans_type): - if self.CP.carFingerprint in PQ_CARS: + if self.CP.flags & VolkswagenFlags.PQ: return self.update_pq(pt_cp, cam_cp, ext_cp, trans_type) ret = car.CarState.new_message() @@ -47,18 +49,14 @@ class CarState(CarStateBase): ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.standstill = ret.vEgoRaw == 0 - # Update steering angle, rate, yaw rate, and driver input torque. VW send - # the sign/direction in a separate signal so they must be recombined. + # Update EPS position and state info. For signed values, VW sends the sign in a separate signal. ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])] ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])] ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])] ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD - - # Verify EPS readiness to accept steering commands hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"]) - ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT") - ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED") + ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status) # VW Emergency Assist status tracking and mitigation self.eps_stock_values = pt_cp.vl["LH_EPS_03"] @@ -151,6 +149,7 @@ class CarState(CarStateBase): # Digital instrument clusters expect the ACC HUD lead car distance to be scaled differently self.upscale_lead_car_signal = bool(pt_cp.vl["Kombi_03"]["KBI_Variante"]) + self.frame += 1 return ret def update_pq(self, pt_cp, cam_cp, ext_cp, trans_type): @@ -168,18 +167,14 @@ class CarState(CarStateBase): ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.standstill = ret.vEgoRaw == 0 - # Update steering angle, rate, yaw rate, and driver input torque. VW send - # the sign/direction in a separate signal so they must be recombined. + # Update EPS position and state info. For signed values, VW sends the sign in a separate signal. ret.steeringAngleDeg = pt_cp.vl["Lenkhilfe_3"]["LH3_BLW"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_BLWSign"])] ret.steeringRateDeg = pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit_S"])] ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])] ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE ret.yawRate = pt_cp.vl["Bremse_5"]["Giergeschwindigkeit"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["Vorzeichen_der_Giergeschwindigk"])] * CV.DEG_TO_RAD - - # Verify EPS readiness to accept steering commands hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"]) - ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT") - ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED") + ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status) # Update gas, brakes, and gearshift. ret.gas = pt_cp.vl["Motor_3"]["Fahrpedal_Rohsignal"] / 100.0 @@ -253,11 +248,20 @@ class CarState(CarStateBase): # Additional safety checks performed in CarInterface. ret.espDisabled = bool(pt_cp.vl["Bremse_1"]["ESP_Passiv_getastet"]) + self.frame += 1 return ret + def update_hca_state(self, hca_status): + # Treat INITIALIZING and FAULT as temporary for worst likely EPS recovery time, for cars without factory Lane Assist + # DISABLED means the EPS hasn't been configured to support Lane Assist + self.eps_init_complete = self.eps_init_complete or (hca_status in ("DISABLED", "READY", "ACTIVE") or self.frame > 600) + perm_fault = hca_status == "DISABLED" or (self.eps_init_complete and hca_status in ("INITIALIZING", "FAULT")) + temp_fault = hca_status == "REJECTED" or not self.eps_init_complete + return temp_fault, perm_fault + @staticmethod def get_can_parser(CP): - if CP.carFingerprint in PQ_CARS: + if CP.flags & VolkswagenFlags.PQ: return CarState.get_can_parser_pq(CP) messages = [ @@ -294,7 +298,7 @@ class CarState(CarStateBase): @staticmethod def get_cam_can_parser(CP): - if CP.carFingerprint in PQ_CARS: + if CP.flags & VolkswagenFlags.PQ: return CarState.get_cam_can_parser_pq(CP) messages = [] diff --git a/selfdrive/car/volkswagen/fingerprints.py b/selfdrive/car/volkswagen/fingerprints.py index eab2bc0090..f6b3c49982 100644 --- a/selfdrive/car/volkswagen/fingerprints.py +++ b/selfdrive/car/volkswagen/fingerprints.py @@ -99,6 +99,17 @@ FW_VERSIONS = { b'\xf1\x875Q0907572P \xf1\x890682', ], }, + CAR.CADDY_MK3: { + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8704E906027T \xf1\x892363', + ], + (Ecu.srs, 0x715, None): [ + b'\xf1\x872K5959655E \xf1\x890018\xf1\x82\x05000P037605', + ], + (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0155', + ], + }, CAR.CRAFTER_MK2: { (Ecu.engine, 0x7e0, None): [ b'\xf1\x8704L906056BP\xf1\x894729', @@ -938,6 +949,7 @@ FW_VERSIONS = { }, CAR.SKODA_KAROQ_MK1: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8705E906013CL\xf1\x892541', b'\xf1\x8705E906013H \xf1\x892407', b'\xf1\x8705E906018P \xf1\x895472', b'\xf1\x8705E906018P \xf1\x896020', @@ -957,6 +969,7 @@ FW_VERSIONS = { (Ecu.eps, 0x712, None): [ b'\xf1\x875Q0910143B \xf1\x892201\xf1\x82\x0563T6090500', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100500', + b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100600', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100700', ], (Ecu.fwdRadar, 0x757, None): [ diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index fa68e81d0d..43a8bcdddc 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -2,7 +2,7 @@ from cereal import car from panda import Panda from openpilot.selfdrive.car import get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase -from openpilot.selfdrive.car.volkswagen.values import PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags +from openpilot.selfdrive.car.volkswagen.values import CAR, CANBUS, NetworkLocation, TransmissionType, GearShifter, VolkswagenFlags ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName @@ -22,11 +22,11 @@ class CarInterface(CarInterfaceBase): self.eps_timer_soft_disable_alert = False @staticmethod - def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): + def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs): ret.carName = "volkswagen" ret.radarUnavailable = True - if candidate in PQ_CARS: + if ret.flags & VolkswagenFlags.PQ: # Set global PQ35/PQ46/NMS parameters ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagenPq)] ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1 @@ -72,7 +72,7 @@ class CarInterface(CarInterfaceBase): # Global lateral tuning defaults, can be overridden per-vehicle ret.steerLimitTimer = 0.4 - if candidate in PQ_CARS: + if ret.flags & VolkswagenFlags.PQ: ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) else: @@ -100,11 +100,8 @@ class CarInterface(CarInterfaceBase): ret.vEgoStopping = 0.5 ret.longitudinalTuning.kpV = [0.1] ret.longitudinalTuning.kiV = [0.0] - - # Per-chassis tuning values, override tuning defaults here if desired - ret.autoResumeSng = ret.minEnableSpeed == -1 - ret.centerToFront = ret.wheelbase * 0.45 + return ret # returns a car.CarState diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 065a340928..a45ddf431f 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -40,7 +40,7 @@ class CarControllerParams: def __init__(self, CP): can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) - if CP.carFingerprint in PQ_CARS: + if CP.flags & VolkswagenFlags.PQ: self.LDW_STEP = 5 # LDW_1 message frequency 20Hz self.ACC_HUD_STEP = 4 # ACC_GRA_Anzeige frequency 25Hz self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm @@ -110,8 +110,12 @@ class CANBUS: class VolkswagenFlags(IntFlag): + # Detected flags STOCK_HCA_PRESENT = 1 + # Static flags + PQ = 2 + @dataclass class VolkswagenMQBPlatformConfig(PlatformConfig): @@ -122,9 +126,13 @@ class VolkswagenMQBPlatformConfig(PlatformConfig): class VolkswagenPQPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) + def init(self): + self.flags |= VolkswagenFlags.PQ + @dataclass(frozen=True, kw_only=True) class VolkswagenCarSpecs(CarSpecs): + centerToFrontRatio: float = 0.45 steerRatio: float = 15.6 @@ -177,7 +185,7 @@ class CAR(Platforms): VWCarInfo("Volkswagen Arteon eHybrid 2020-23", video_link="https://youtu.be/FAomFKPFlDA"), VWCarInfo("Volkswagen CC 2018-22", video_link="https://youtu.be/FAomFKPFlDA"), ], - specs=VolkswagenCarSpecs(mass=1733, wheelbase=2.84), + VolkswagenCarSpecs(mass=1733, wheelbase=2.84), ) ATLAS_MK1 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN ATLAS 1ST GEN", # Chassis CA @@ -188,7 +196,15 @@ class CAR(Platforms): VWCarInfo("Volkswagen Teramont Cross Sport 2021-22"), VWCarInfo("Volkswagen Teramont X 2021-22"), ], - specs=VolkswagenCarSpecs(mass=2011, wheelbase=2.98), + VolkswagenCarSpecs(mass=2011, wheelbase=2.98), + ) + CADDY_MK3 = VolkswagenPQPlatformConfig( + "VOLKSWAGEN CADDY 3RD GEN", # Chassis 2K + [ + VWCarInfo("Volkswagen Caddy 2019"), + VWCarInfo("Volkswagen Caddy Maxi 2019"), + ], + VolkswagenCarSpecs(mass=1613, wheelbase=2.6, minSteerSpeed=21 * CV.KPH_TO_MS), ) CRAFTER_MK2 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN CRAFTER 2ND GEN", # Chassis SY/SZ @@ -199,7 +215,7 @@ class CAR(Platforms): VWCarInfo("MAN TGE 2017-23", video_link="https://youtu.be/4100gLeabmo"), VWCarInfo("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"), ], - specs=VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS), + VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS), ) GOLF_MK7 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN GOLF 7TH GEN", # Chassis 5G/AU/BA/BE @@ -213,7 +229,7 @@ class CAR(Platforms): VWCarInfo("Volkswagen Golf R 2015-19"), VWCarInfo("Volkswagen Golf SportsVan 2015-20"), ], - specs=VolkswagenCarSpecs(mass=1397, wheelbase=2.62), + VolkswagenCarSpecs(mass=1397, wheelbase=2.62), ) JETTA_MK7 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN JETTA 7TH GEN", # Chassis BU @@ -221,7 +237,7 @@ class CAR(Platforms): VWCarInfo("Volkswagen Jetta 2018-24"), VWCarInfo("Volkswagen Jetta GLI 2021-24"), ], - specs=VolkswagenCarSpecs(mass=1328, wheelbase=2.71), + VolkswagenCarSpecs(mass=1328, wheelbase=2.71), ) PASSAT_MK8 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN PASSAT 8TH GEN", # Chassis 3G @@ -230,12 +246,12 @@ class CAR(Platforms): VWCarInfo("Volkswagen Passat Alltrack 2015-22"), VWCarInfo("Volkswagen Passat GTE 2015-22"), ], - specs=VolkswagenCarSpecs(mass=1551, wheelbase=2.79), + VolkswagenCarSpecs(mass=1551, wheelbase=2.79), ) PASSAT_NMS = VolkswagenPQPlatformConfig( "VOLKSWAGEN PASSAT NMS", # Chassis A3 VWCarInfo("Volkswagen Passat NMS 2017-22"), - specs=VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50*CV.KPH_TO_MS, minEnableSpeed=20*CV.KPH_TO_MS), + VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50*CV.KPH_TO_MS, minEnableSpeed=20*CV.KPH_TO_MS), ) POLO_MK6 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN POLO 6TH GEN", # Chassis AW @@ -243,7 +259,7 @@ class CAR(Platforms): VWCarInfo("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]), VWCarInfo("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]), ], - specs=VolkswagenCarSpecs(mass=1230, wheelbase=2.55), + VolkswagenCarSpecs(mass=1230, wheelbase=2.55), ) SHARAN_MK2 = VolkswagenPQPlatformConfig( "VOLKSWAGEN SHARAN 2ND GEN", # Chassis 7N @@ -251,17 +267,17 @@ class CAR(Platforms): VWCarInfo("Volkswagen Sharan 2018-22"), VWCarInfo("SEAT Alhambra 2018-20"), ], - specs=VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50*CV.KPH_TO_MS), + VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50*CV.KPH_TO_MS), ) TAOS_MK1 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN TAOS 1ST GEN", # Chassis B2 VWCarInfo("Volkswagen Taos 2022-23"), - specs=VolkswagenCarSpecs(mass=1498, wheelbase=2.69), + VolkswagenCarSpecs(mass=1498, wheelbase=2.69), ) TCROSS_MK1 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN T-CROSS 1ST GEN", # Chassis C1 - car_info=VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]), - specs=VolkswagenCarSpecs(mass=1150, wheelbase=2.60), + VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]), + VolkswagenCarSpecs(mass=1150, wheelbase=2.60), ) TIGUAN_MK2 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN TIGUAN 2ND GEN", # Chassis AD/BW @@ -269,12 +285,12 @@ class CAR(Platforms): VWCarInfo("Volkswagen Tiguan 2018-24"), VWCarInfo("Volkswagen Tiguan eHybrid 2021-23"), ], - specs=VolkswagenCarSpecs(mass=1715, wheelbase=2.74), + VolkswagenCarSpecs(mass=1715, wheelbase=2.74), ) TOURAN_MK2 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN TOURAN 2ND GEN", # Chassis 1T VWCarInfo("Volkswagen Touran 2016-23"), - specs=VolkswagenCarSpecs(mass=1516, wheelbase=2.79), + VolkswagenCarSpecs(mass=1516, wheelbase=2.79), ) TRANSPORTER_T61 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN TRANSPORTER T6.1", # Chassis 7H/7L @@ -282,12 +298,12 @@ class CAR(Platforms): VWCarInfo("Volkswagen Caravelle 2020"), VWCarInfo("Volkswagen California 2021-23"), ], - specs=VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0), + VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0), ) TROC_MK1 = VolkswagenMQBPlatformConfig( "VOLKSWAGEN T-ROC 1ST GEN", # Chassis A1 VWCarInfo("Volkswagen T-Roc 2018-22", footnotes=[Footnote.VW_MQB_A0]), - specs=VolkswagenCarSpecs(mass=1413, wheelbase=2.63), + VolkswagenCarSpecs(mass=1413, wheelbase=2.63), ) AUDI_A3_MK3 = VolkswagenMQBPlatformConfig( "AUDI A3 3RD GEN", # Chassis 8V/FF @@ -297,47 +313,47 @@ class CAR(Platforms): VWCarInfo("Audi RS3 2018"), VWCarInfo("Audi S3 2015-17"), ], - specs=VolkswagenCarSpecs(mass=1335, wheelbase=2.61), + VolkswagenCarSpecs(mass=1335, wheelbase=2.61), ) AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig( "AUDI Q2 1ST GEN", # Chassis GA VWCarInfo("Audi Q2 2018"), - specs=VolkswagenCarSpecs(mass=1205, wheelbase=2.61), + VolkswagenCarSpecs(mass=1205, wheelbase=2.61), ) AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig( "AUDI Q3 2ND GEN", # Chassis 8U/F3/FS VWCarInfo("Audi Q3 2019-23"), - specs=VolkswagenCarSpecs(mass=1623, wheelbase=2.68), + VolkswagenCarSpecs(mass=1623, wheelbase=2.68), ) SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig( "SEAT ATECA 1ST GEN", # Chassis 5F VWCarInfo("SEAT Ateca 2018"), - specs=VolkswagenCarSpecs(mass=1900, wheelbase=2.64), + VolkswagenCarSpecs(mass=1900, wheelbase=2.64), ) SEAT_LEON_MK3 = VolkswagenMQBPlatformConfig( "SEAT LEON 3RD GEN", # Chassis 5F VWCarInfo("SEAT Leon 2014-20"), - specs=VolkswagenCarSpecs(mass=1227, wheelbase=2.64), + VolkswagenCarSpecs(mass=1227, wheelbase=2.64), ) SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig( "SKODA FABIA 4TH GEN", # Chassis PJ VWCarInfo("Å koda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0]), - specs=VolkswagenCarSpecs(mass=1266, wheelbase=2.56), + VolkswagenCarSpecs(mass=1266, wheelbase=2.56), ) SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig( "SKODA KAMIQ 1ST GEN", # Chassis NW VWCarInfo("Å koda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]), - specs=VolkswagenCarSpecs(mass=1265, wheelbase=2.66), + VolkswagenCarSpecs(mass=1265, wheelbase=2.66), ) SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig( "SKODA KAROQ 1ST GEN", # Chassis NU VWCarInfo("Å koda Karoq 2019-23"), - specs=VolkswagenCarSpecs(mass=1278, wheelbase=2.66), + VolkswagenCarSpecs(mass=1278, wheelbase=2.66), ) SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig( "SKODA KODIAQ 1ST GEN", # Chassis NS VWCarInfo("Å koda Kodiaq 2017-23"), - specs=VolkswagenCarSpecs(mass=1569, wheelbase=2.79), + VolkswagenCarSpecs(mass=1569, wheelbase=2.79), ) SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig( "SKODA OCTAVIA 3RD GEN", # Chassis NE @@ -345,22 +361,20 @@ class CAR(Platforms): VWCarInfo("Å koda Octavia 2015-19"), VWCarInfo("Å koda Octavia RS 2016"), ], - specs=VolkswagenCarSpecs(mass=1388, wheelbase=2.68), + VolkswagenCarSpecs(mass=1388, wheelbase=2.68), ) SKODA_SCALA_MK1 = VolkswagenMQBPlatformConfig( "SKODA SCALA 1ST GEN", # Chassis NW VWCarInfo("Å koda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]), - specs=VolkswagenCarSpecs(mass=1192, wheelbase=2.65), + VolkswagenCarSpecs(mass=1192, wheelbase=2.65), ) SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig( "SKODA SUPERB 3RD GEN", # Chassis 3V/NP VWCarInfo("Å koda Superb 2015-22"), - specs=VolkswagenCarSpecs(mass=1505, wheelbase=2.84), + VolkswagenCarSpecs(mass=1505, wheelbase=2.84), ) -PQ_CARS = {CAR.PASSAT_NMS, CAR.SHARAN_MK2} - # All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars # with a manual trans won't return transmission firmware, but all other cars will. # @@ -402,5 +416,4 @@ FW_QUERY_CONFIG = FwQueryConfig( extra_ecus=[(Ecu.fwdCamera, 0x74f, None)], ) -CAR_INFO = CAR.create_carinfo_map() DBC = CAR.create_dbc_map() diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index bd3dd08179..e4f2542ea5 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -10,7 +10,6 @@ import cereal.messaging as messaging from cereal import car, log from cereal.visionipc import VisionIpcClient, VisionStreamType -from panda import ALTERNATIVE_EXPERIENCE from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import clip @@ -18,9 +17,8 @@ from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL from openpilot.common.swaglog import cloudlog -from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp -from openpilot.selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can -from openpilot.selfdrive.car.interfaces import CarInterfaceBase +from openpilot.selfdrive.car.car_helpers import get_startup_event +from openpilot.selfdrive.car.card import CarD from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature from openpilot.selfdrive.controls.lib.events import Events, ET @@ -61,128 +59,18 @@ ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding) ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES) -class CarD: - CI: CarInterfaceBase - CS: car.CarState - +class Controls: def __init__(self, CI=None): - self.can_sock = messaging.sub_sock('can', timeout=20) - self.sm = messaging.SubMaster(['pandaStates']) - self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams']) - - self.can_rcv_timeout_counter = 0 # conseuctive timeout count - self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count + self.card = CarD(CI) self.params = Params() - if CI is None: - # wait for one pandaState and one CAN packet - print("Waiting for CAN messages...") - get_one_can(self.can_sock) - - num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) - experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") - self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas) - else: - self.CI, self.CP = CI, CI.CP - - # set alternative experiences from parameters - disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") - self.CP.alternativeExperience = 0 - if not disengage_on_accelerator: - self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS - - car_recognized = self.CP.carName != 'mock' - openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") - - controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly - - self.CP.passive = not car_recognized or not controller_available or self.CP.dashcamOnly - if self.CP.passive: - safety_config = car.CarParams.SafetyConfig.new_message() - safety_config.safetyModel = car.CarParams.SafetyModel.noOutput - self.CP.safetyConfigs = [safety_config] - - # Write previous route's CarParams - prev_cp = self.params.get("CarParamsPersistent") - if prev_cp is not None: - self.params.put("CarParamsPrevRoute", prev_cp) - - # Write CarParams for radard - cp_bytes = self.CP.to_bytes() - self.params.put("CarParams", cp_bytes) - self.params.put_nonblocking("CarParamsCache", cp_bytes) - self.params.put_nonblocking("CarParamsPersistent", cp_bytes) - - def initialize(self): - """Initialize CarInterface, once controls are ready""" - self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) - - def state_update(self, CC: car.CarControl): - """carState update loop, driven by can""" - - # TODO: This should not depend on carControl + with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg: + # TODO: this shouldn't need to be a builder + self.CP = msg.as_builder() - # Update carState from CAN - can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) - self.CS = self.CI.update(CC, can_strs) - - self.sm.update(0) - - can_rcv_valid = len(can_strs) > 0 - - # Check for CAN timeout - if not can_rcv_valid: - self.can_rcv_timeout_counter += 1 - self.can_rcv_cum_timeout_counter += 1 - else: - self.can_rcv_timeout_counter = 0 - - self.can_rcv_timeout = self.can_rcv_timeout_counter >= 5 - - if can_rcv_valid and REPLAY: - self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime - - return self.CS - - def state_publish(self, car_events): - """carState and carParams publish loop""" - - # TODO: carState should be independent of the event loop - - # carState - cs_send = messaging.new_message('carState') - cs_send.valid = self.CS.canValid - cs_send.carState = self.CS - cs_send.carState.events = car_events - self.pm.send('carState', cs_send) - - # carParams - logged every 50 seconds (> 1 per segment) - if (self.sm.frame % int(50. / DT_CTRL) == 0): - cp_send = messaging.new_message('carParams') - cp_send.valid = True - cp_send.carParams = self.CP - self.pm.send('carParams', cp_send) - - def controls_update(self, CC: car.CarControl): - """control update loop, driven by carControl""" - - # send car controls over can - now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) - actuators_output, can_sends = self.CI.apply(CC, now_nanos) - self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=self.CS.canValid)) - - return actuators_output - - -class Controls: - def __init__(self, CI=None): - self.card = CarD(CI) - - self.CP = self.card.CP self.CI = self.card.CI - config_realtime_process(4, Priority.CTRL_HIGH) # Ensure the current branch is cached, otherwise the first iteration of controlsd lags self.branch = get_short_branch() @@ -195,12 +83,11 @@ class Controls: self.log_sock = messaging.sub_sock('androidLog') - self.params = Params() ignore = self.sensor_packets + ['testJoystick'] if SIMULATION: ignore += ['driverCameraState', 'managerState'] self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', - 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', + 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'testJoystick'] + self.camera_packets + self.sensor_packets, ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ], @@ -212,15 +99,12 @@ class Controls: self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.is_metric = self.params.get_bool("IsMetric") self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") - openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") # detect sound card presence and ensure successful init sounds_available = HARDWARE.get_sound_card_online() car_recognized = self.CP.carName != 'mock' - controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly - # cleanup old params if not self.CP.experimentalLongitudinalAvailable: self.params.remove("ExperimentalLongitudinalEnabled") @@ -267,7 +151,7 @@ class Controls: self.can_log_mono_time = 0 - self.startup_event = get_startup_event(car_recognized, controller_available, len(self.CP.carFw) > 0) + self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0) if not sounds_available: self.events.add(EventName.soundsUnavailable, static=True) @@ -513,7 +397,7 @@ class Controls: def data_sample(self): """Receive data from sockets and update carState""" - CS = self.card.state_update(self.CC) + CS = self.card.state_update() self.sm.update(0) @@ -771,6 +655,8 @@ class Controls: def publish_logs(self, CS, start_time, CC, lac_log): """Send actuators and hud commands to the car, send controlsstate and MPC logging""" + CO = self.sm['carOutput'] + # Orientation and angle rates can be useful for carcontroller # Only calibrated (car) frame is relevant for the carcontroller orientation_value = list(self.sm['liveLocationKalman'].calibratedOrientationNED.value) @@ -833,13 +719,12 @@ class Controls: hudControl.visualAlert = current_alert.visual_alert if not self.CP.passive and self.initialized: - self.last_actuators = self.card.controls_update(CC) - CC.actuatorsOutput = self.last_actuators + self.card.controls_update(CC) if self.CP.steerControlType == car.CarParams.SteerControlType.angle: - self.steer_limited = abs(CC.actuators.steeringAngleDeg - CC.actuatorsOutput.steeringAngleDeg) > \ + self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \ STEER_ANGLE_SATURATION_THRESHOLD else: - self.steer_limited = abs(CC.actuators.steer - CC.actuatorsOutput.steer) > 1e-2 + self.steer_limited = abs(CC.actuators.steer - CO.actuatorsOutput.steer) > 1e-2 force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \ (self.state == State.softDisabling) @@ -896,15 +781,11 @@ class Controls: self.pm.send('controlsState', dat) - car_events = self.events.to_msg() - - self.card.state_publish(car_events) - # onroadEvents - logged every second or on change if (self.sm.frame % int(1. / DT_CTRL) == 0) or (self.events.names != self.events_prev): ce_send = messaging.new_message('onroadEvents', len(self.events)) ce_send.valid = True - ce_send.onroadEvents = car_events + ce_send.onroadEvents = self.events.to_msg() self.pm.send('onroadEvents', ce_send) self.events_prev = self.events.names.copy() @@ -961,6 +842,7 @@ class Controls: def main(): + config_realtime_process(4, Priority.CTRL_HIGH) controls = Controls() controls.controlsd_thread() diff --git a/selfdrive/controls/tests/test_state_machine.py b/selfdrive/controls/tests/test_state_machine.py index bdeed9fb7a..d49111752d 100755 --- a/selfdrive/controls/tests/test_state_machine.py +++ b/selfdrive/controls/tests/test_state_machine.py @@ -7,6 +7,7 @@ from openpilot.selfdrive.car.car_helpers import interfaces from openpilot.selfdrive.controls.controlsd import Controls, SOFT_DISABLE_TIME from openpilot.selfdrive.controls.lib.events import Events, ET, Alert, Priority, AlertSize, AlertStatus, VisualAlert, \ AudibleAlert, EVENTS +from openpilot.selfdrive.car.mock.values import CAR as MOCK State = log.ControlsState.OpenpilotState @@ -30,8 +31,8 @@ def make_event(event_types): class TestStateMachine(unittest.TestCase): def setUp(self): - CarInterface, CarController, CarState = interfaces["mock"] - CP = CarInterface.get_non_essential_params("mock") + CarInterface, CarController, CarState = interfaces[MOCK.MOCK] + CP = CarInterface.get_non_essential_params(MOCK.MOCK) CI = CarInterface(CP, CarController, CarState) self.controlsd = Controls(CI=CI) diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py index 7735391f4f..e5cae0d470 100755 --- a/selfdrive/debug/hyundai_enable_radar_points.py +++ b/selfdrive/debug/hyundai_enable_radar_points.py @@ -36,6 +36,9 @@ SUPPORTED_FW_VERSIONS = { default_config=b"\x00\x00\x00\x01\x00\x00", tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), # 2021 SONATA HYBRID + b"DNhe SCC FHCUP 1.00 1.00 99110-L5000\x19\x04&\x13' ": ConfigValues( + default_config=b"\x00\x00\x00\x01\x00\x00", + tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), b"DNhe SCC FHCUP 1.00 1.02 99110-L5000 \x01#\x15# ": ConfigValues( default_config=b"\x00\x00\x00\x01\x00\x00", tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py index 441de9ef57..90db14bc9a 100755 --- a/selfdrive/debug/run_process_on_route.py +++ b/selfdrive/debug/run_process_on_route.py @@ -27,4 +27,5 @@ if __name__ == "__main__": outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime) fn = f"{args.route.replace('/', '_')}_{args.process}.bz2" + print(f"Saved log to {fn}") save_log(fn, outputs) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 69bab8d1fa..06f9044738 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -159,8 +159,10 @@ class TorqueEstimator(ParameterEstimator): def handle_log(self, t, which, msg): if which == "carControl": self.raw_points["carControl_t"].append(t + self.lag) - self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer) self.raw_points["active"].append(msg.latActive) + elif which == "carOutput": + self.raw_points["carOutput_t"].append(t + self.lag) + self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer) elif which == "carState": self.raw_points["carState_t"].append(t + self.lag) self.raw_points["vego"].append(msg.vEgo) @@ -172,7 +174,7 @@ class TorqueEstimator(ParameterEstimator): active = np.interp(np.arange(t - MIN_ENGAGE_BUFFER, t, DT_MDL), self.raw_points['carControl_t'], self.raw_points['active']).astype(bool) steer_override = np.interp(np.arange(t - MIN_ENGAGE_BUFFER, t, DT_MDL), self.raw_points['carState_t'], self.raw_points['steer_override']).astype(bool) vego = np.interp(t, self.raw_points['carState_t'], self.raw_points['vego']) - steer = np.interp(t, self.raw_points['carControl_t'], self.raw_points['steer_torque']) + steer = np.interp(t, self.raw_points['carOutput_t'], self.raw_points['steer_torque']) lateral_acc = (vego * yaw_rate) - (np.sin(roll) * ACCELERATION_DUE_TO_GRAVITY) if all(active) and (not any(steer_override)) and (vego > MIN_VEL) and (abs(steer) > STEER_MIN_THRESHOLD) and (abs(lateral_acc) <= LAT_ACC_THRESHOLD): self.filtered_points.add_point(float(steer), float(lateral_acc)) @@ -218,7 +220,7 @@ def main(demo=False): config_realtime_process([0, 1, 2, 3], 5) pm = messaging.PubMaster(['liveTorqueParameters']) - sm = messaging.SubMaster(['carControl', 'carState', 'liveLocationKalman'], poll='liveLocationKalman') + sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveLocationKalman'], poll='liveLocationKalman') params = Params() with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP: diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index cb6dd8883f..4f292917fd 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -78,7 +78,7 @@ procs = [ PythonProcess("radard", "selfdrive.controls.radard", only_onroad), PythonProcess("thermald", "selfdrive.thermald.thermald", always_run), PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC), - PythonProcess("updated", "selfdrive.updated", only_offroad, enabled=not PC), + PythonProcess("updated", "selfdrive.updated.updated", only_offroad, enabled=not PC), PythonProcess("uploader", "system.loggerd.uploader", always_run), PythonProcess("statsd", "selfdrive.statsd", always_run), diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx index ce346a406a..68e39514d9 100644 --- a/selfdrive/modeld/models/supercombo.onnx +++ b/selfdrive/modeld/models/supercombo.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c11f99aab832242a604b1bb4c4cf391c9c3d5e90cbc07ab0d4c133473b56a3a4 +oid sha256:cd4b0cc83d5ff275ee77ec430ea686603ad50fd6ad874f599ea6e95b123afc3e size 48193749 diff --git a/selfdrive/test/helpers.py b/selfdrive/test/helpers.py index 210a283699..b345b929ec 100644 --- a/selfdrive/test/helpers.py +++ b/selfdrive/test/helpers.py @@ -1,3 +1,4 @@ +import contextlib import http.server import os import threading @@ -43,27 +44,34 @@ def release_only(f): f(self, *args, **kwargs) return wrap -def with_processes(processes, init_time=0, ignore_stopped=None): + +@contextlib.contextmanager +def processes_context(processes, init_time=0, ignore_stopped=None): ignore_stopped = [] if ignore_stopped is None else ignore_stopped + # start and assert started + for n, p in enumerate(processes): + managed_processes[p].start() + if n < len(processes) - 1: + time.sleep(init_time) + + assert all(managed_processes[name].proc.exitcode is None for name in processes) + + try: + yield [managed_processes[name] for name in processes] + # assert processes are still started + assert all(managed_processes[name].proc.exitcode is None for name in processes if name not in ignore_stopped) + finally: + for p in processes: + managed_processes[p].stop() + + +def with_processes(processes, init_time=0, ignore_stopped=None): def wrapper(func): @wraps(func) def wrap(*args, **kwargs): - # start and assert started - for n, p in enumerate(processes): - managed_processes[p].start() - if n < len(processes) - 1: - time.sleep(init_time) - assert all(managed_processes[name].proc.exitcode is None for name in processes) - - # call the function - try: - func(*args, **kwargs) - # assert processes are still started - assert all(managed_processes[name].proc.exitcode is None for name in processes if name not in ignore_stopped) - finally: - for p in processes: - managed_processes[p].stop() + with processes_context(processes, init_time, ignore_stopped): + return func(*args, **kwargs) return wrap return wrapper diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index 3b7b04df80..786c2f2731 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -fd6421f7551573c549480f9d29bb0dee4678344d +e8b359a82316e6dfce3b6fb0fb9684431bfa0a1b diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 13f776fbad..46d684211c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -b9d29ac9402cfc04bf3e48867415efa70c144029 +43efe1cf08cba8c86bc1ae8234b3d3d084a40e5d diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 2b917b0f61..88e46abb06 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -156,7 +156,8 @@ if __name__ == "__main__": assert full_test, "Need to run full test when updating refs" try: - ref_commit = open(REF_COMMIT_FN).read().strip() + with open(REF_COMMIT_FN) as f: + ref_commit = f.read().strip() except FileNotFoundError: print("Couldn't find reference commit") sys.exit(1) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 8f8c93ecff..4be9b8a430 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -29,7 +29,7 @@ from openpilot.tools.lib.logreader import LogReader # Baseline CPU usage by process PROCS = { - "selfdrive.controls.controlsd": 41.0, + "selfdrive.controls.controlsd": 46.0, "./loggerd": 14.0, "./encoderd": 17.0, "./camerad": 14.5, diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index 15c022db9a..d12db3f878 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -17,7 +17,7 @@ void SoftwarePanel::checkForUpdates() { - std::system("pkill -SIGUSR1 -f selfdrive.updated"); + std::system("pkill -SIGUSR1 -f selfdrive.updated.updated"); } SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { @@ -36,7 +36,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { if (downloadBtn->text() == tr("CHECK")) { checkForUpdates(); } else { - std::system("pkill -SIGHUP -f selfdrive.updated"); + std::system("pkill -SIGHUP -f selfdrive.updated.updated"); } }); addItem(downloadBtn); diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py new file mode 100755 index 0000000000..ea2ee51a45 --- /dev/null +++ b/selfdrive/ui/ui.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +import os +import signal + +signal.signal(signal.SIGINT, signal.SIG_DFL) + +import cereal.messaging as messaging +from openpilot.system.hardware import HARDWARE + +from PyQt5.QtCore import Qt, QTimer +from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QStackedLayout, QApplication +from openpilot.selfdrive.ui.qt.python_helpers import set_main_window + + +if __name__ == "__main__": + app = QApplication([]) + win = QWidget() + set_main_window(win) + + bg = QLabel("", alignment=Qt.AlignCenter) + + alert1 = QLabel() + alert2 = QLabel() + vlayout = QVBoxLayout() + vlayout.addWidget(alert1, alignment=Qt.AlignCenter) + vlayout.addWidget(alert2, alignment=Qt.AlignCenter) + + tmp = QWidget() + tmp.setLayout(vlayout) + + stack = QStackedLayout(win) + stack.addWidget(tmp) + stack.addWidget(bg) + stack.setStackingMode(QStackedLayout.StackAll) + + win.setObjectName("win") + win.setStyleSheet(""" + #win { + background-color: black; + } + QLabel { + color: white; + font-size: 40px; + } + """) + + sm = messaging.SubMaster(['deviceState', 'controlsState']) + + def update(): + sm.update(0) + + onroad = sm.all_checks(['deviceState']) and sm['deviceState'].started + if onroad: + cs = sm['controlsState'] + color = ("grey" if str(cs.status) in ("overriding", "preEnabled") else "green") if cs.enabled else "blue" + bg.setText("\U0001F44D" if cs.engageable else "\U0001F6D1") + bg.setStyleSheet(f"font-size: 100px; background-color: {color};") + bg.show() + + alert1.setText(cs.alertText1) + alert2.setText(cs.alertText2) + + if not sm.alive['controlsState']: + alert1.setText("waiting for controls...") + else: + bg.hide() + alert1.setText("") + alert2.setText("offroad") + + HARDWARE.set_screen_brightness(100 if onroad else 40) + os.system("echo 0 > /sys/class/backlight/panel0-backlight/bl_power") + + timer = QTimer() + timer.timeout.connect(update) + timer.start(50) + + app.exec_() diff --git a/selfdrive/updated/tests/test_updated.py b/selfdrive/updated/tests/test_updated.py new file mode 100755 index 0000000000..d8ce9f3394 --- /dev/null +++ b/selfdrive/updated/tests/test_updated.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +import os +import pathlib +import shutil +import signal +import subprocess +import tempfile +import time +import unittest +from unittest import mock + +import pytest +from openpilot.selfdrive.manager.process import ManagerProcess + + +from openpilot.selfdrive.test.helpers import processes_context +from openpilot.common.params import Params + + +def run(args, **kwargs): + return subprocess.run(args, **kwargs, check=True) + + +def update_release(directory, name, version, release_notes): + with open(directory / "RELEASES.md", "w") as f: + f.write(release_notes) + + (directory / "common").mkdir(exist_ok=True) + + with open(directory / "common" / "version.h", "w") as f: + f.write(f'#define COMMA_VERSION "{version}"') + + run(["git", "add", "."], cwd=directory) + run(["git", "commit", "-m", f"openpilot release {version}"], cwd=directory) + + +@pytest.mark.slow # TODO: can we test overlayfs in GHA? +class TestUpdateD(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + + run(["sudo", "mount", "-t", "tmpfs", "tmpfs", self.tmpdir]) # overlayfs doesn't work inside of docker unless this is a tmpfs + + self.mock_update_path = pathlib.Path(self.tmpdir) + + self.params = Params() + + self.basedir = self.mock_update_path / "openpilot" + self.basedir.mkdir() + + self.staging_root = self.mock_update_path / "safe_staging" + self.staging_root.mkdir() + + self.remote_dir = self.mock_update_path / "remote" + self.remote_dir.mkdir() + + mock.patch("openpilot.common.basedir.BASEDIR", self.basedir).start() + + os.environ["UPDATER_STAGING_ROOT"] = str(self.staging_root) + os.environ["UPDATER_LOCK_FILE"] = str(self.mock_update_path / "safe_staging_overlay.lock") + + self.MOCK_RELEASES = { + "release3": ("0.1.2", "0.1.2 release notes"), + "master": ("0.1.3", "0.1.3 release notes"), + } + + def set_target_branch(self, branch): + self.params.put("UpdaterTargetBranch", branch) + + def setup_basedir_release(self, release): + self.params = Params() + self.set_target_branch(release) + run(["git", "clone", "-b", release, self.remote_dir, self.basedir]) + + def update_remote_release(self, release): + update_release(self.remote_dir, release, *self.MOCK_RELEASES[release]) + + def setup_remote_release(self, release): + run(["git", "init"], cwd=self.remote_dir) + run(["git", "checkout", "-b", release], cwd=self.remote_dir) + self.update_remote_release(release) + + def tearDown(self): + mock.patch.stopall() + run(["sudo", "umount", "-l", str(self.staging_root / "merged")]) + run(["sudo", "umount", "-l", self.tmpdir]) + shutil.rmtree(self.tmpdir) + + def send_check_for_updates_signal(self, updated: ManagerProcess): + updated.signal(signal.SIGUSR1.value) + + def send_download_signal(self, updated: ManagerProcess): + updated.signal(signal.SIGHUP.value) + + def _test_params(self, branch, fetch_available, update_available): + self.assertEqual(self.params.get("UpdaterTargetBranch", encoding="utf-8"), branch) + self.assertEqual(self.params.get_bool("UpdaterFetchAvailable"), fetch_available) + self.assertEqual(self.params.get_bool("UpdateAvailable"), update_available) + + def _test_update_params(self, branch, version, release_notes): + self.assertTrue(self.params.get("UpdaterNewDescription", encoding="utf-8").startswith(f"{version} / {branch}")) + self.assertEqual(self.params.get("UpdaterNewReleaseNotes", encoding="utf-8"), f"

{release_notes}

\n") + + def wait_for_idle(self, timeout=5, min_wait_time=2): + start = time.monotonic() + time.sleep(min_wait_time) + + while True: + waited = time.monotonic() - start + if self.params.get("UpdaterState", encoding="utf-8") == "idle": + print(f"waited {waited}s for idle") + break + + if waited > timeout: + raise TimeoutError("timed out waiting for idle") + + time.sleep(1) + + def test_new_release(self): + # Start on release3, simulate a release3 commit, ensure we fetch that update properly + self.setup_remote_release("release3") + self.setup_basedir_release("release3") + + with processes_context(["updated"]) as [updated]: + self._test_params("release3", False, False) + time.sleep(1) + self._test_params("release3", False, False) + + self.MOCK_RELEASES["release3"] = ("0.1.3", "0.1.3 release notes") + self.update_remote_release("release3") + + self.send_check_for_updates_signal(updated) + + self.wait_for_idle() + + self._test_params("release3", True, False) + + self.send_download_signal(updated) + + self.wait_for_idle() + + self._test_params("release3", False, True) + self._test_update_params("release3", *self.MOCK_RELEASES["release3"]) + + def test_switch_branches(self): + # Start on release3, request to switch to master manually, ensure we switched + self.setup_remote_release("release3") + self.setup_remote_release("master") + self.setup_basedir_release("release3") + + with processes_context(["updated"]) as [updated]: + self._test_params("release3", False, False) + self.wait_for_idle() + self._test_params("release3", False, False) + + self.set_target_branch("master") + self.send_check_for_updates_signal(updated) + + self.wait_for_idle() + + self._test_params("master", True, False) + + self.send_download_signal(updated) + + self.wait_for_idle() + + self._test_params("master", False, True) + self._test_update_params("master", *self.MOCK_RELEASES["master"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/updated.py b/selfdrive/updated/updated.py similarity index 100% rename from selfdrive/updated.py rename to selfdrive/updated/updated.py diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index 91c6d44f84..fff4a62d31 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -79,8 +79,12 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, int nv12_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, rgb_height); assert(nv12_width == VENUS_UV_STRIDE(COLOR_FMT_NV12, rgb_width)); assert(nv12_height/2 == VENUS_UV_SCANLINES(COLOR_FMT_NV12, rgb_height)); - size_t nv12_size = 2346 * nv12_width; // comes from v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage size_t nv12_uv_offset = nv12_width * nv12_height; + + // the encoder HW tells us the size it wants after setting it up. + // TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings? + size_t nv12_size = (rgb_width >= 2688 ? 2900 : 2346)*nv12_width; + vipc_server->create_buffers_with_sizes(stream_type, YUV_BUFFER_COUNT, false, rgb_width, rgb_height, nv12_size, nv12_width, nv12_uv_offset); LOGD("created %d YUV vipc buffers with size %dx%d", YUV_BUFFER_COUNT, nv12_width, nv12_height); diff --git a/system/hardware/tici/amplifier.py b/system/hardware/tici/amplifier.py index 5725cb8170..af82067467 100755 --- a/system/hardware/tici/amplifier.py +++ b/system/hardware/tici/amplifier.py @@ -97,7 +97,6 @@ CONFIGS = { AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001), AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000), ], - "mici": [], } class Amplifier: diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 5bb1032ba9..c1cb9da438 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -94,11 +94,7 @@ def get_device_type(): # lru_cache and cache can cause memory leaks when used in classes with open("/sys/firmware/devicetree/base/model") as f: model = f.read().strip('\x00') - model = model.split('comma ')[-1] - # TODO: remove this with AGNOS 7+ - if model.startswith('Qualcomm'): - model = 'tici' - return model + return model.split('comma ')[-1] class Tici(HardwareBase): @cached_property @@ -116,6 +112,8 @@ class Tici(HardwareBase): @cached_property def amplifier(self): + if self.get_device_type() == "mici": + return None return Amplifier() def get_os_version(self): @@ -374,9 +372,10 @@ class Tici(HardwareBase): def set_power_save(self, powersave_enabled): # amplifier, 100mW at idle - self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled) - if not powersave_enabled: - self.amplifier.initialize_configuration(self.get_device_type()) + if self.amplifier is not None: + self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled) + if not powersave_enabled: + self.amplifier.initialize_configuration(self.get_device_type()) # *** CPU config *** @@ -414,7 +413,8 @@ class Tici(HardwareBase): return 0 def initialize_hardware(self): - self.amplifier.initialize_configuration(self.get_device_type()) + if self.amplifier is not None: + self.amplifier.initialize_configuration(self.get_device_type()) # Allow thermald to write engagement status to kmsg os.system("sudo chmod a+w /dev/kmsg") @@ -478,6 +478,12 @@ class Tici(HardwareBase): 'AT+QNVFW="/nv/item_files/ims/IMS_enable",00', 'AT+QNVFW="/nv/item_files/modem/mmode/ue_usage_setting",01', ] + if self.get_device_type() == "tizi": + cmds += [ + # SIM hot swap + 'AT+QSIMDET=1,0', + 'AT+QSIMSTAT=1', + ] # clear out old blue prime initial APN os.system('mmcli -m any --3gpp-set-initial-eps-bearer-settings="apn="') diff --git a/system/loggerd/encoder/v4l_encoder.cc b/system/loggerd/encoder/v4l_encoder.cc index 2bd2863126..853a17abbe 100644 --- a/system/loggerd/encoder/v4l_encoder.cc +++ b/system/loggerd/encoder/v4l_encoder.cc @@ -16,7 +16,12 @@ #define V4L2_QCOM_BUF_FLAG_CODECCONFIG 0x00020000 #define V4L2_QCOM_BUF_FLAG_EOS 0x02000000 -// echo 0x7fffffff > /sys/kernel/debug/msm_vidc/debug_level +/* + kernel debugging: + echo 0xff > /sys/module/videobuf2_core/parameters/debug + echo 0x7fffffff > /sys/kernel/debug/msm_vidc/debug_level + echo 0xff > /sys/devices/platform/soc/aa00000.qcom,vidc/video4linux/video33/dev_debug +*/ const int env_debug_encoder = (getenv("DEBUG_ENCODER") != NULL) ? atoi(getenv("DEBUG_ENCODER")) : 0; static void checked_ioctl(int fd, unsigned long request, void *argp) { diff --git a/system/loggerd/logger.cc b/system/loggerd/logger.cc index 7a829a2f1f..f1b187df7f 100644 --- a/system/loggerd/logger.cc +++ b/system/loggerd/logger.cc @@ -89,15 +89,6 @@ kj::Array logger_build_init_data() { return capnp::messageToFlatArray(msg); } -std::string logger_get_route_name() { - char route_name[64] = {'\0'}; - time_t rawtime = time(NULL); - struct tm timeinfo; - localtime_r(&rawtime, &timeinfo); - strftime(route_name, sizeof(route_name), "%Y-%m-%d--%H-%M-%S", &timeinfo); - return route_name; -} - std::string logger_get_identifier(std::string key) { // a log identifier is a 32 bit counter, plus a 10 character unique ID. // e.g. 000001a3--c20ba54385 @@ -131,7 +122,7 @@ static void log_sentinel(LoggerState *log, SentinelType type, int eixt_signal = } LoggerState::LoggerState(const std::string &log_root) { - route_name = logger_get_route_name(); + route_name = logger_get_identifier("RouteCount"); route_path = log_root + "/" + route_name; init_data = logger_build_init_data(); } diff --git a/system/loggerd/logger.h b/system/loggerd/logger.h index dd3bee150c..7a8490d57a 100644 --- a/system/loggerd/logger.h +++ b/system/loggerd/logger.h @@ -52,5 +52,4 @@ protected: }; kj::Array logger_build_init_data(); -std::string logger_get_route_name(); std::string logger_get_identifier(std::string key); diff --git a/system/loggerd/tests/loggerd_tests_common.py b/system/loggerd/tests/loggerd_tests_common.py index 42eec2a0f4..877c872b6b 100644 --- a/system/loggerd/tests/loggerd_tests_common.py +++ b/system/loggerd/tests/loggerd_tests_common.py @@ -72,8 +72,8 @@ class UploaderTestCase(unittest.TestCase): uploader.force_wifi = True uploader.allow_sleep = False self.seg_num = random.randint(1, 300) - self.seg_format = "2019-04-18--12-52-54--{}" - self.seg_format2 = "2019-05-18--11-22-33--{}" + self.seg_format = "00000004--0ac3964c96--{}" + self.seg_format2 = "00000005--4c4e99b08b--{}" self.seg_dir = self.seg_format.format(self.seg_num) self.params = Params() diff --git a/system/webrtc/tests/test_webrtcd.py b/system/webrtc/tests/test_webrtcd.py index c31b63d02b..e5742dba07 100755 --- a/system/webrtc/tests/test_webrtcd.py +++ b/system/webrtc/tests/test_webrtcd.py @@ -11,8 +11,15 @@ from openpilot.system.webrtc.webrtcd import get_stream import aiortc from teleoprtc import WebRTCOfferBuilder +from parameterized import parameterized_class +@parameterized_class(("in_services", "out_services"), [ + (["testJoystick"], ["carState"]), + ([], ["carState"]), + (["testJoystick"], []), + ([], []), +]) class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase): async def assertCompletesWithTimeout(self, awaitable, timeout=1): try: @@ -24,7 +31,7 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase): async def test_webrtcd(self): mock_request = MagicMock() async def connect(offer): - body = {'sdp': offer.sdp, 'cameras': offer.video, 'bridge_services_in': [], 'bridge_services_out': ['carState']} + body = {'sdp': offer.sdp, 'cameras': offer.video, 'bridge_services_in': self.in_services, 'bridge_services_out': self.out_services} mock_request.json.side_effect = AsyncMock(return_value=body) response = await get_stream(mock_request) response_json = json.loads(response.text) @@ -33,7 +40,8 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase): builder = WebRTCOfferBuilder(connect) builder.offer_to_receive_video_stream("road") builder.offer_to_receive_audio_stream() - builder.add_messaging() + if len(self.in_services) > 0 or len(self.out_services) > 0: + builder.add_messaging() stream = builder.stream() @@ -42,7 +50,7 @@ class TestWebrtcdProc(unittest.IsolatedAsyncioTestCase): self.assertTrue(stream.has_incoming_video_track("road")) self.assertTrue(stream.has_incoming_audio_track()) - self.assertTrue(stream.has_messaging_channel()) + self.assertEqual(stream.has_messaging_channel(), len(self.in_services) > 0 or len(self.out_services) > 0) video_track, audio_track = stream.get_incoming_video_track("road"), stream.get_incoming_audio_track() await self.assertCompletesWithTimeout(video_track.recv()) diff --git a/system/webrtc/webrtcd.py b/system/webrtc/webrtcd.py index 6c1370eae9..51c86aacc6 100755 --- a/system/webrtc/webrtcd.py +++ b/system/webrtc/webrtcd.py @@ -102,7 +102,21 @@ class CerealProxyRunner: await asyncio.sleep(0.01) +class DynamicPubMaster(messaging.PubMaster): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.lock = asyncio.Lock() + + async def add_services_if_needed(self, services): + async with self.lock: + for service in services: + if service not in self.sock: + self.sock[service] = messaging.pub_sock(service) + + class StreamSession: + shared_pub_master = DynamicPubMaster([]) + def __init__(self, sdp: str, cameras: list[str], incoming_services: list[str], outgoing_services: list[str], debug_mode: bool = False): from aiortc.mediastreams import VideoStreamTrack, AudioStreamTrack from aiortc.contrib.media import MediaBlackhole @@ -128,9 +142,15 @@ class StreamSession: self.stream = builder.stream() self.identifier = str(uuid.uuid4()) - self.outgoing_bridge = CerealOutgoingMessageProxy(messaging.SubMaster(outgoing_services)) - self.incoming_bridge = CerealIncomingMessageProxy(messaging.PubMaster(incoming_services)) - self.outgoing_bridge_runner = CerealProxyRunner(self.outgoing_bridge) + self.incoming_bridge: CerealIncomingMessageProxy | None = None + self.incoming_bridge_services = incoming_services + self.outgoing_bridge: CerealOutgoingMessageProxy | None = None + self.outgoing_bridge_runner: CerealProxyRunner | None = None + if len(incoming_services) > 0: + self.incoming_bridge = CerealIncomingMessageProxy(self.shared_pub_master) + if len(outgoing_services) > 0: + self.outgoing_bridge = CerealOutgoingMessageProxy(messaging.SubMaster(outgoing_services)) + self.outgoing_bridge_runner = CerealProxyRunner(self.outgoing_bridge) self.audio_output: AudioOutputSpeaker | MediaBlackhole | None = None self.run_task: asyncio.Task | None = None @@ -152,6 +172,7 @@ class StreamSession: return await self.stream.start() async def message_handler(self, message: bytes): + assert self.incoming_bridge is not None try: self.incoming_bridge.send(message) except Exception as ex: @@ -161,10 +182,13 @@ class StreamSession: try: await self.stream.wait_for_connection() if self.stream.has_messaging_channel(): - self.stream.set_message_handler(self.message_handler) - channel = self.stream.get_messaging_channel() - self.outgoing_bridge_runner.proxy.add_channel(channel) - self.outgoing_bridge_runner.start() + if self.incoming_bridge is not None: + await self.shared_pub_master.add_services_if_needed(self.incoming_bridge_services) + self.stream.set_message_handler(self.message_handler) + if self.outgoing_bridge_runner is not None: + channel = self.stream.get_messaging_channel() + self.outgoing_bridge_runner.proxy.add_channel(channel) + self.outgoing_bridge_runner.start() if self.stream.has_incoming_audio_track(): track = self.stream.get_incoming_audio_track(buffered=False) self.audio_output = self.audio_output_cls() @@ -181,7 +205,8 @@ class StreamSession: async def post_run_cleanup(self): await self.stream.stop() - self.outgoing_bridge_runner.stop() + if self.outgoing_bridge is not None: + self.outgoing_bridge_runner.stop() if self.audio_output: self.audio_output.stop() diff --git a/teleoprtc_repo b/teleoprtc_repo index 3f9e8176d1..ab2f09706e 160000 --- a/teleoprtc_repo +++ b/teleoprtc_repo @@ -1 +1 @@ -Subproject commit 3f9e8176d1be3d217528baee09fc418fa980a0c3 +Subproject commit ab2f09706e8f64390e196f079ac69e67131b07f5 diff --git a/tools/cabana/dbc/generate_dbc_json.py b/tools/cabana/dbc/generate_dbc_json.py index da19e27b77..5a8ef21d8b 100755 --- a/tools/cabana/dbc/generate_dbc_json.py +++ b/tools/cabana/dbc/generate_dbc_json.py @@ -2,13 +2,11 @@ import argparse import json -from openpilot.selfdrive.car.car_helpers import get_interface_attr +from openpilot.selfdrive.car.values import create_platform_map def generate_dbc_json() -> str: - all_cars_by_brand = get_interface_attr("CAR_INFO") - all_dbcs_by_brand = get_interface_attr("DBC") - dbc_map = {car: all_dbcs_by_brand[brand][car]['pt'] for brand, cars in all_cars_by_brand.items() for car in cars if car != 'mock'} + dbc_map = create_platform_map(lambda platform: platform.config.dbc_dict["pt"] if platform != "mock" else None) return json.dumps(dict(sorted(dbc_map.items())), indent=2) diff --git a/tools/camerastream/compressed_vipc.py b/tools/camerastream/compressed_vipc.py index f4b8862a84..df9de096f0 100755 --- a/tools/camerastream/compressed_vipc.py +++ b/tools/camerastream/compressed_vipc.py @@ -25,7 +25,7 @@ ENCODE_SOCKETS = { def decoder(addr, vipc_server, vst, nvidia, W, H, debug=False): sock_name = ENCODE_SOCKETS[vst] if debug: - print("start decoder for %s" % sock_name) + print(f"start decoder for {sock_name}, {W}x{H}") if nvidia: os.environ["NV_LOW_LATENCY"] = "3" # both bLowLatency and CUVID_PKT_ENDOFPICTURE @@ -124,7 +124,7 @@ class CompressedVipc: self.procs = [] for vst in vision_streams: ed = sm[ENCODE_SOCKETS[vst]] - p = multiprocessing.Process(target=decoder, args=(addr, self.vipc_server, vst, nvidia, debug, ed.width, ed.height)) + p = multiprocessing.Process(target=decoder, args=(addr, self.vipc_server, vst, nvidia, ed.width, ed.height, debug)) p.start() self.procs.append(p) diff --git a/tools/car_porting/examples/subaru_fuzzy_fingerprint.ipynb b/tools/car_porting/examples/subaru_fuzzy_fingerprint.ipynb index fbd88a769e..dd223667d8 100644 --- a/tools/car_porting/examples/subaru_fuzzy_fingerprint.ipynb +++ b/tools/car_porting/examples/subaru_fuzzy_fingerprint.ipynb @@ -2,15 +2,23 @@ "cells": [ { "cell_type": "code", - "execution_count": 148, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "kj/filesystem-disk-unix.c++:1703: warning: PWD environment variable doesn't match current directory; pwd = /home/batman\n" + ] + } + ], "source": [ "from cereal import car\n", - "from openpilot.selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS\n", + "from openpilot.selfdrive.car.subaru.values import CAR, SubaruFlags\n", "from openpilot.selfdrive.car.subaru.fingerprints import FW_VERSIONS\n", "\n", - "TEST_PLATFORMS = set(c.value for c in CAR) - PREGLOBAL_CARS # preglobal cars seem to have a different format for fingerprints, ignore for now\n", + "TEST_PLATFORMS = CAR.without_flags(SubaruFlags.PREGLOBAL)\n", "\n", "Ecu = car.CarParams.Ecu\n", "\n", @@ -19,22 +27,7 @@ }, { "cell_type": "code", - "execution_count": 149, - "metadata": {}, - "outputs": [], - "source": [ - "from openpilot.selfdrive.car.subaru.values import CAR_INFO\n", - "\n", - "def get_carinfo(model: CAR):\n", - " c = CAR_INFO[model]\n", - " if isinstance(c, list):\n", - " c = c[0]\n", - " return c" - ] - }, - { - "cell_type": "code", - "execution_count": 150, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -90,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -103,38 +96,9 @@ "SUBARU IMPREZA LIMITED 2019 0c not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", "SUBARU IMPREZA LIMITED 2019 2e not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", "SUBARU IMPREZA LIMITED 2019 3f not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", - "correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2019 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2021 years=[2019, 2020, 2021]\n", - "correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n", - "correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2020 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", - "correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n", - "correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2018 years=[2017, 2018, 2019]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", - "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", "correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n", "correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n", - "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", - "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", - "correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023]\n", - "correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023]\n", - "correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023]\n", - "correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023]\n", - "correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2019 years=[2020]\n", - "correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2021 years=[2020]\n", + "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", @@ -145,14 +109,47 @@ "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n", + "correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2019 years=[2020]\n", + "correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2021 years=[2020]\n", + "correct_year=False platform=SUBARU FORESTER HYBRID 2020 year=2019 years=[2020]\n", + "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2018 years=[2017, 2018, 2019]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", + "correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n", + "correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023, 2024]\n", + "correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023, 2024]\n", + "correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023, 2024]\n", + "correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023, 2024]\n", + "correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n", + "correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2020 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", + "correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU ASCENT 2023 year=2023 years=[2023]\n", - "correct_year=False platform=SUBARU FORESTER HYBRID 2020 year=2019 years=[2020]\n" + "correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2019 years=[2019, 2020, 2021]\n", + "correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2021 years=[2019, 2020, 2021]\n", + "correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n", + "correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n", + "correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n", + "correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n", + "correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n", + "correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n" ] } ], "source": [ "def test_year_code(platform, year):\n", - " years = [int(y) for y in get_carinfo(platform).year_list]\n", + " car_info = CAR(platform).config.car_info\n", + " if isinstance(car_info, list):\n", + " car_info = car_info[0]\n", + " years = [int(y) for y in car_info.year_list]\n", " correct_year = year in years\n", " print(f\"{correct_year=!s: <6} {platform=: <32} {year=: <5} {years=}\")\n", "\n", @@ -163,27 +160,33 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", - "in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU FORESTER HYBRID 2020 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", @@ -196,30 +199,25 @@ "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", - "in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", - "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", + "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU ASCENT 2023 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", - "in_possible_platforms=True platform=SUBARU FORESTER HYBRID 2020 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n" + "in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", + "in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", + "in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n" ] } ], diff --git a/tools/lib/route.py b/tools/lib/route.py index bd0ccc1fc3..06a3596d69 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -263,6 +263,10 @@ class SegmentRange: def timestamp(self) -> str: return self.m.group("timestamp") + @property + def log_id(self) -> str: + return self.m.group("log_id") + @property def slice(self) -> str: return self.m.group("slice") or "" @@ -291,7 +295,7 @@ class SegmentRange: return list(range(end + 1))[s] def __str__(self) -> str: - return f"{self.dongle_id}/{self.timestamp}" + (f"/{self.slice}" if self.slice else "") + (f"/{self.selector}" if self.selector else "") + return f"{self.dongle_id}/{self.log_id}" + (f"/{self.slice}" if self.slice else "") + (f"/{self.selector}" if self.selector else "") def __repr__(self) -> str: return self.__str__() diff --git a/tools/plotjuggler/layouts/max-torque-debug.xml b/tools/plotjuggler/layouts/max-torque-debug.xml index 20a49c2181..8cfd30599e 100644 --- a/tools/plotjuggler/layouts/max-torque-debug.xml +++ b/tools/plotjuggler/layouts/max-torque-debug.xml @@ -24,7 +24,7 @@ - + diff --git a/tools/plotjuggler/layouts/torque-controller.xml b/tools/plotjuggler/layouts/torque-controller.xml index 443255968a..d6e4684d63 100644 --- a/tools/plotjuggler/layouts/torque-controller.xml +++ b/tools/plotjuggler/layouts/torque-controller.xml @@ -24,8 +24,8 @@ - - + + diff --git a/tools/replay/can_replay.py b/tools/replay/can_replay.py index bf6b1e9ae2..c7da8caf71 100755 --- a/tools/replay/can_replay.py +++ b/tools/replay/can_replay.py @@ -26,12 +26,10 @@ def send_thread(j: PandaJungle, flock): with flock: j.flash() + j.reset() for i in [0, 1, 2, 3, 0xFFFF]: j.can_clear(i) j.set_can_speed_kbps(i, 500) - j.set_can_data_speed_kbps(i, 500) - j.set_ignition(False) - time.sleep(5) j.set_ignition(True) j.set_panda_power(True) j.set_can_loopback(False) @@ -98,6 +96,10 @@ if __name__ == "__main__": sr = LogReader(args.route_or_segment_name) + CP = sr.first("carParams") + + print(f"carFingerprint (for hardcoding fingerprint): '{CP.carFingerprint}'") + CAN_MSGS = sr.run_across_segments(24, process) print("Finished loading...") diff --git a/tools/sim/bridge/common.py b/tools/sim/bridge/common.py index 10e2a055a3..34d11bb98c 100644 --- a/tools/sim/bridge/common.py +++ b/tools/sim/bridge/common.py @@ -13,7 +13,6 @@ from openpilot.selfdrive.car.honda.values import CruiseButtons from openpilot.tools.sim.lib.common import SimulatorState, World from openpilot.tools.sim.lib.simulated_car import SimulatedCar from openpilot.tools.sim.lib.simulated_sensors import SimulatedSensors -from openpilot.tools.sim.lib.keyboard_ctrl import KEYBOARD_HELP def rk_loop(function, hz, exit_event: threading.Event): @@ -57,14 +56,14 @@ class SimulatorBridge(ABC): try: self._run(q) finally: - self.close() + self.close("bridge terminated") - def close(self): + def close(self, reason): self.started.value = False self._exit_event.set() if self.world is not None: - self.world.close() + self.world.close(reason) def run(self, queue, retries=-1): bridge_p = Process(name="bridge", target=self.bridge_keep_alive, args=(queue, retries)) @@ -74,9 +73,6 @@ class SimulatorBridge(ABC): def print_status(self): print( f""" -Keyboard Commands: -{KEYBOARD_HELP} - State: Ignition: {self.simulator_state.ignition} Engaged: {self.simulator_state.is_engaged} """) @@ -171,8 +167,12 @@ Ignition: {self.simulator_state.ignition} Engaged: {self.simulator_state.is_enga steer_out = steer_op if self.simulator_state.is_engaged else steer_manual self.world.apply_controls(steer_out, throttle_out, brake_out) + self.world.read_state() self.world.read_sensors(self.simulator_state) + if self.world.exit_event.is_set(): + self.shutdown() + if self.rk.frame % self.TICKS_PER_FRAME == 0: self.world.tick() self.world.read_cameras() diff --git a/tools/sim/bridge/metadrive/metadrive_bridge.py b/tools/sim/bridge/metadrive/metadrive_bridge.py index c94fca2b95..c2ea92798a 100644 --- a/tools/sim/bridge/metadrive/metadrive_bridge.py +++ b/tools/sim/bridge/metadrive/metadrive_bridge.py @@ -1,48 +1,14 @@ -import numpy as np +from multiprocessing import Queue -from metadrive.component.sensors.rgb_camera import RGBCamera from metadrive.component.sensors.base_camera import _cuda_enable from metadrive.component.map.pg_map import MapGenerateMethod -from panda3d.core import Texture, GraphicsOutput from openpilot.tools.sim.bridge.common import SimulatorBridge +from openpilot.tools.sim.bridge.metadrive.metadrive_common import RGBCameraRoad, RGBCameraWide from openpilot.tools.sim.bridge.metadrive.metadrive_world import MetaDriveWorld from openpilot.tools.sim.lib.camerad import W, H - -class CopyRamRGBCamera(RGBCamera): - """Camera which copies its content into RAM during the render process, for faster image grabbing.""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.cpu_texture = Texture() - self.buffer.addRenderTexture(self.cpu_texture, GraphicsOutput.RTMCopyRam) - - def get_rgb_array_cpu(self): - origin_img = self.cpu_texture - img = np.frombuffer(origin_img.getRamImage().getData(), dtype=np.uint8) - img = img.reshape((origin_img.getYSize(), origin_img.getXSize(), -1)) - img = img[:,:,:3] # RGBA to RGB - # img = np.swapaxes(img, 1, 0) - img = img[::-1] # Flip on vertical axis - return img - - -class RGBCameraWide(CopyRamRGBCamera): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - lens = self.get_lens() - lens.setFov(120) - lens.setNear(0.1) - -class RGBCameraRoad(CopyRamRGBCamera): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - lens = self.get_lens() - lens.setFov(40) - lens.setNear(0.1) - - def straight_block(length): return { "id": "S", @@ -116,4 +82,4 @@ class MetaDriveBridge(SimulatorBridge): preload_models=False ) - return MetaDriveWorld(config, self.dual_camera) + return MetaDriveWorld(Queue(), config, self.dual_camera) diff --git a/tools/sim/bridge/metadrive/metadrive_common.py b/tools/sim/bridge/metadrive/metadrive_common.py new file mode 100644 index 0000000000..42a7eb60dd --- /dev/null +++ b/tools/sim/bridge/metadrive/metadrive_common.py @@ -0,0 +1,37 @@ +import numpy as np + +from metadrive.component.sensors.rgb_camera import RGBCamera +from panda3d.core import Texture, GraphicsOutput + + +class CopyRamRGBCamera(RGBCamera): + """Camera which copies its content into RAM during the render process, for faster image grabbing.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.cpu_texture = Texture() + self.buffer.addRenderTexture(self.cpu_texture, GraphicsOutput.RTMCopyRam) + + def get_rgb_array_cpu(self): + origin_img = self.cpu_texture + img = np.frombuffer(origin_img.getRamImage().getData(), dtype=np.uint8) + img = img.reshape((origin_img.getYSize(), origin_img.getXSize(), -1)) + img = img[:,:,:3] # RGBA to RGB + # img = np.swapaxes(img, 1, 0) + img = img[::-1] # Flip on vertical axis + return img + + +class RGBCameraWide(CopyRamRGBCamera): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + lens = self.get_lens() + lens.setFov(120) + lens.setNear(0.1) + + +class RGBCameraRoad(CopyRamRGBCamera): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + lens = self.get_lens() + lens.setFov(40) + lens.setNear(0.1) diff --git a/tools/sim/bridge/metadrive/metadrive_process.py b/tools/sim/bridge/metadrive/metadrive_process.py index aa6ae57976..0e659c7a2d 100644 --- a/tools/sim/bridge/metadrive/metadrive_process.py +++ b/tools/sim/bridge/metadrive/metadrive_process.py @@ -19,7 +19,8 @@ C3_POSITION = Vec3(0.0, 0, 1.22) C3_HPR = Vec3(0, 0,0) -metadrive_state = namedtuple("metadrive_state", ["velocity", "position", "bearing", "steering_angle"]) +metadrive_simulation_state = namedtuple("metadrive_simulation_state", ["running", "done", "done_info"]) +metadrive_vehicle_state = namedtuple("metadrive_vehicle_state", ["velocity", "position", "bearing", "steering_angle"]) def apply_metadrive_patches(): # By default, metadrive won't try to use cuda images unless it's used as a sensor for vehicles, so patch that in @@ -46,7 +47,8 @@ def apply_metadrive_patches(): MetaDriveEnv._is_arrive_destination = arrive_destination_patch def metadrive_process(dual_camera: bool, config: dict, camera_array, wide_camera_array, image_lock, - controls_recv: Connection, state_send: Connection, exit_event): + controls_recv: Connection, simulation_state_send: Connection, vehicle_state_send: Connection, + exit_event): apply_metadrive_patches() road_image = np.frombuffer(camera_array.get_obj(), dtype=np.uint8).reshape((H, W, 3)) @@ -60,6 +62,13 @@ def metadrive_process(dual_camera: bool, config: dict, camera_array, wide_camera env.reset() env.vehicle.config["max_speed_km_h"] = 1000 + simulation_state = metadrive_simulation_state( + running=True, + done=False, + done_info=None, + ) + simulation_state_send.send(simulation_state) + reset() def get_cam_as_rgb(cam): @@ -78,14 +87,14 @@ def metadrive_process(dual_camera: bool, config: dict, camera_array, wide_camera vc = [0,0] while not exit_event.is_set(): - state = metadrive_state( + vehicle_state = metadrive_vehicle_state( velocity=vec3(x=float(env.vehicle.velocity[0]), y=float(env.vehicle.velocity[1]), z=0), position=env.vehicle.position, bearing=float(math.degrees(env.vehicle.heading_theta)), steering_angle=env.vehicle.steering * env.vehicle.MAX_STEERING ) - state_send.send(state) + vehicle_state_send.send(vehicle_state) if controls_recv.poll(0): while controls_recv.poll(0): @@ -103,7 +112,13 @@ def metadrive_process(dual_camera: bool, config: dict, camera_array, wide_camera obs, _, terminated, _, info = env.step(vc) if terminated: - reset() + done_result = env.done_function("default_agent") + simulation_state = metadrive_simulation_state( + running=False, + done=done_result[0], + done_info=done_result[1], + ) + simulation_state_send.send(simulation_state) if dual_camera: wide_road_image[...] = get_cam_as_rgb("rgb_wide") diff --git a/tools/sim/bridge/metadrive/metadrive_world.py b/tools/sim/bridge/metadrive/metadrive_world.py index 4cbe64393e..3570205069 100644 --- a/tools/sim/bridge/metadrive/metadrive_world.py +++ b/tools/sim/bridge/metadrive/metadrive_world.py @@ -5,14 +5,16 @@ import numpy as np import time from multiprocessing import Pipe, Array -from openpilot.tools.sim.bridge.metadrive.metadrive_process import metadrive_process, metadrive_state +from openpilot.tools.sim.bridge.metadrive.metadrive_process import (metadrive_process, metadrive_simulation_state, + metadrive_vehicle_state) from openpilot.tools.sim.lib.common import SimulatorState, World from openpilot.tools.sim.lib.camerad import W, H class MetaDriveWorld(World): - def __init__(self, config, dual_camera = False): + def __init__(self, status_q, config, dual_camera = False): super().__init__(dual_camera) + self.status_q = status_q self.camera_array = Array(ctypes.c_uint8, W*H*3) self.road_image = np.frombuffer(self.camera_array.get_obj(), dtype=np.uint8).reshape((H, W, 3)) self.wide_camera_array = None @@ -21,22 +23,26 @@ class MetaDriveWorld(World): self.wide_road_image = np.frombuffer(self.wide_camera_array.get_obj(), dtype=np.uint8).reshape((H, W, 3)) self.controls_send, self.controls_recv = Pipe() - self.state_send, self.state_recv = Pipe() + self.simulation_state_send, self.simulation_state_recv = Pipe() + self.vehicle_state_send, self.vehicle_state_recv = Pipe() self.exit_event = multiprocessing.Event() self.metadrive_process = multiprocessing.Process(name="metadrive process", target= functools.partial(metadrive_process, dual_camera, config, self.camera_array, self.wide_camera_array, self.image_lock, - self.controls_recv, self.state_send, self.exit_event)) + self.controls_recv, self.simulation_state_send, + self.vehicle_state_send, self.exit_event)) self.metadrive_process.start() + self.status_q.put({"status": "starting"}) print("----------------------------------------------------------") print("---- Spawning Metadrive world, this might take awhile ----") print("----------------------------------------------------------") - self.state_recv.recv() # wait for a state message to ensure metadrive is launched + self.vehicle_state_recv.recv() # wait for a state message to ensure metadrive is launched + self.status_q.put({"status": "started"}) self.steer_ratio = 15 self.vc = [0.0,0.0] @@ -58,13 +64,24 @@ class MetaDriveWorld(World): self.controls_send.send([*self.vc, self.should_reset]) self.should_reset = False + def read_state(self): + while self.simulation_state_recv.poll(0): + md_state: metadrive_simulation_state = self.simulation_state_recv.recv() + if md_state.done: + self.status_q.put({ + "status": "terminating", + "reason": "done", + "done_info": md_state.done_info + }) + self.exit_event.set() + def read_sensors(self, state: SimulatorState): - while self.state_recv.poll(0): - md_state: metadrive_state = self.state_recv.recv() - state.velocity = md_state.velocity - state.bearing = md_state.bearing - state.steering_angle = md_state.steering_angle - state.gps.from_xy(md_state.position) + while self.vehicle_state_recv.poll(0): + md_vehicle: metadrive_vehicle_state = self.vehicle_state_recv.recv() + state.velocity = md_vehicle.velocity + state.bearing = md_vehicle.bearing + state.steering_angle = md_vehicle.steering_angle + state.gps.from_xy(md_vehicle.position) state.valid = True def read_cameras(self): @@ -76,6 +93,10 @@ class MetaDriveWorld(World): def reset(self): self.should_reset = True - def close(self): + def close(self, reason: str): + self.status_q.put({ + "status": "terminating", + "reason": reason, + }) self.exit_event.set() self.metadrive_process.join() diff --git a/tools/sim/lib/common.py b/tools/sim/lib/common.py index 795d7505c7..168b3aa324 100644 --- a/tools/sim/lib/common.py +++ b/tools/sim/lib/common.py @@ -69,6 +69,8 @@ class World(ABC): self.road_image = np.zeros((H, W, 3), dtype=np.uint8) self.wide_road_image = np.zeros((H, W, 3), dtype=np.uint8) + self.exit_event = multiprocessing.Event() + @abstractmethod def apply_controls(self, steer_sim, throttle_out, brake_out): pass @@ -77,6 +79,10 @@ class World(ABC): def tick(self): pass + @abstractmethod + def read_state(self): + pass + @abstractmethod def read_sensors(self, simulator_state: SimulatorState): pass @@ -86,7 +92,7 @@ class World(ABC): pass @abstractmethod - def close(self): + def close(self, reason: str): pass @abstractmethod diff --git a/tools/sim/lib/keyboard_ctrl.py b/tools/sim/lib/keyboard_ctrl.py index 339f4ea6bb..ea255d9ce4 100644 --- a/tools/sim/lib/keyboard_ctrl.py +++ b/tools/sim/lib/keyboard_ctrl.py @@ -49,7 +49,12 @@ def getch() -> str: termios.tcsetattr(STDIN_FD, termios.TCSADRAIN, old_settings) return ch +def print_keyboard_help(): + print(f"Keyboard Commands:\n{KEYBOARD_HELP}") + def keyboard_poll_thread(q: 'Queue[str]'): + print_keyboard_help() + while True: c = getch() if c == '1': @@ -77,6 +82,8 @@ def keyboard_poll_thread(q: 'Queue[str]'): elif c == 'q': q.put("quit") break + else: + print_keyboard_help() def test(q: 'Queue[str]') -> NoReturn: while True: diff --git a/tools/sim/scenarios/metadrive/stay_in_lane.py b/tools/sim/scenarios/metadrive/stay_in_lane.py new file mode 100755 index 0000000000..6d5f680247 --- /dev/null +++ b/tools/sim/scenarios/metadrive/stay_in_lane.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +from multiprocessing import Queue + +from metadrive.component.sensors.base_camera import _cuda_enable +from metadrive.component.map.pg_map import MapGenerateMethod + +from openpilot.tools.sim.bridge.common import SimulatorBridge +from openpilot.tools.sim.bridge.metadrive.metadrive_common import RGBCameraRoad, RGBCameraWide +from openpilot.tools.sim.bridge.metadrive.metadrive_world import MetaDriveWorld +from openpilot.tools.sim.lib.camerad import W, H + + +def create_map(): + return dict( + type=MapGenerateMethod.PG_MAP_FILE, + lane_num=2, + lane_width=3.5, + config=[ + { + "id": "S", + "pre_block_socket_index": 0, + "length": 60, + }, + { + "id": "C", + "pre_block_socket_index": 0, + "length": 60, + "radius": 600, + "angle": 45, + "dir": 0, + }, + ] + ) + + +class MetaDriveBridge(SimulatorBridge): + TICKS_PER_FRAME = 5 + + def __init__(self, world_status_q, dual_camera, high_quality): + self.world_status_q = world_status_q + self.should_render = False + + super().__init__(dual_camera, high_quality) + + def spawn_world(self): + sensors = { + "rgb_road": (RGBCameraRoad, W, H, ) + } + + if self.dual_camera: + sensors["rgb_wide"] = (RGBCameraWide, W, H) + + config = dict( + use_render=self.should_render, + vehicle_config=dict( + enable_reverse=False, + image_source="rgb_road", + ), + sensors=sensors, + image_on_cuda=_cuda_enable, + image_observation=True, + interface_panel=[], + out_of_route_done=True, + on_continuous_line_done=True, + crash_vehicle_done=True, + crash_object_done=True, + traffic_density=0.0, + map_config=create_map(), + decision_repeat=1, + physics_world_step_size=self.TICKS_PER_FRAME/100, + preload_models=False + ) + + return MetaDriveWorld(world_status_q, config, self.dual_camera) + + +if __name__ == "__main__": + command_queue: Queue = Queue() + world_status_q: Queue = Queue() + simulator_bridge = MetaDriveBridge(world_status_q, True, False) + simulator_process = simulator_bridge.run(command_queue) + + while True: + world_status = world_status_q.get() + print(f"World Status: {str(world_status)}") + if world_status["status"] == "terminating": + break + + simulator_process.join() diff --git a/tools/tuning/measure_steering_accuracy.py b/tools/tuning/measure_steering_accuracy.py index f804b328de..6abf1338dc 100755 --- a/tools/tuning/measure_steering_accuracy.py +++ b/tools/tuning/measure_steering_accuracy.py @@ -47,7 +47,7 @@ class SteeringAccuracyTool: v_ego = sm['carState'].vEgo active = sm['controlsState'].active - steer = sm['carControl'].actuatorsOutput.steer + steer = sm['carOutput'].actuatorsOutput.steer standstill = sm['carState'].standstill steer_limited = abs(sm['carControl'].actuators.steer - sm['carControl'].actuatorsOutput.steer) > 1e-2 overriding = sm['carState'].steeringPressed @@ -150,7 +150,7 @@ if __name__ == "__main__": messaging.context = messaging.Context() carControl = messaging.sub_sock('carControl', addr=args.addr, conflate=True) - sm = messaging.SubMaster(['carState', 'carControl', 'controlsState', 'modelV2'], addr=args.addr) + sm = messaging.SubMaster(['carState', 'carControl', 'carOutput', 'controlsState', 'modelV2'], addr=args.addr) time.sleep(1) # Make sure all submaster data is available before going further print("waiting for messages...")