diff --git a/.github/workflows/auto_pr_review.yaml b/.github/workflows/auto_pr_review.yaml index abb6c38d6b..42ef4dbc1c 100644 --- a/.github/workflows/auto_pr_review.yaml +++ b/.github/workflows/auto_pr_review.yaml @@ -34,6 +34,26 @@ jobs: already-exists-action: close_this already-exists-comment: "Your PR should be made against the `master` branch" + comment: + runs-on: ubuntu-latest + steps: + - name: comment + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 + if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot' + with: + message: | + + Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following: + * Convert your PR to a draft unless it's ready to review + * Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md) + * Before marking as "ready for review", ensure: + * the goal is clearly stated in the description + * all the tests are passing + * the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged) + * include a route or your device' dongle ID if relevant + comment_tag: run_id + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + check-pr-template: runs-on: ubuntu-latest permissions: @@ -41,7 +61,7 @@ jobs: issues: write pull-requests: write actions: read - if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot' + if: false && github.event.pull_request.head.repo.full_name != 'commaai/openpilot' steps: - uses: actions/github-script@v7 with: @@ -91,7 +111,7 @@ jobs: // Utility function to check if a list of checkboxes is a subset of another list of checkboxes isCheckboxSubset = (templateCheckBoxTexts, prTextCheckBoxTexts) => { - // Check if each template checkbox text is a substring of at least one PR checkbox text + // Check if each template checkbox text is a substring of at least one PR checkbox text // (user should be allowed to add additional text) return templateCheckBoxTexts.every((item) => prTextCheckBoxTexts.some((element) => element.includes(item))) } diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index d15ab8cd60..a051cb1241 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -86,7 +86,7 @@ jobs: notebooks: name: notebooks runs-on: ubuntu-20.04 - if: github.repository == 'commaai/openpilot' + if: false && github.repository == 'commaai/openpilot' timeout-minutes: 45 steps: - uses: actions/checkout@v4 diff --git a/SConstruct b/SConstruct index b04e3903ed..dac3529892 100644 --- a/SConstruct +++ b/SConstruct @@ -96,8 +96,6 @@ lenv = { rpath = lenv["LD_LIBRARY_PATH"].copy() if arch == "larch64": - lenv["LD_LIBRARY_PATH"] += ['/data/data/com.termux/files/usr/lib'] - cpppath = [ "#third_party/opencl/include", ] @@ -360,13 +358,6 @@ Export('common', 'gpucommon') # Build cereal and messaging SConscript(['cereal/SConscript']) -cereal = [File('#cereal/libcereal.a')] -messaging = [File('#cereal/libmessaging.a')] -visionipc = [File('#cereal/libvisionipc.a')] -messaging_python = [File('#cereal/messaging/messaging_pyx.so')] - -Export('cereal', 'messaging', 'messaging_python', 'visionipc') - # Build other submodules SConscript([ 'body/board/SConscript', @@ -393,13 +384,7 @@ if arch != "Darwin": # Build openpilot SConscript(['third_party/SConscript']) -SConscript(['selfdrive/boardd/SConscript']) -SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript']) -SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript']) -SConscript(['selfdrive/locationd/SConscript']) -SConscript(['selfdrive/navd/SConscript']) -SConscript(['selfdrive/modeld/SConscript']) -SConscript(['selfdrive/ui/SConscript']) +SConscript(['selfdrive/SConscript']) if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'): SConscript(['tools/replay/SConscript']) diff --git a/cereal b/cereal index a4255106b7..bfbb0cab83 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit a4255106b7255e00ae04162f7aa14aa3cae339c3 +Subproject commit bfbb0cab83e3ad49d85ad1a34ee1241bf1ff782f diff --git a/common/params.cc b/common/params.cc index eb75705ca3..aef2cbdecd 100644 --- a/common/params.cc +++ b/common/params.cc @@ -207,7 +207,6 @@ std::unordered_map keys = { {"UpdaterLastFetchTime", PERSISTENT}, {"Version", PERSISTENT}, {"VisionRadarToggle", PERSISTENT}, - {"WheeledBody", PERSISTENT}, }; } // namespace diff --git a/common/utils.py b/common/utils.py new file mode 100644 index 0000000000..b9de020ee6 --- /dev/null +++ b/common/utils.py @@ -0,0 +1,11 @@ +class Freezable: + _frozen: bool = False + + def freeze(self): + if not self._frozen: + self._frozen = True + + def __setattr__(self, *args, **kwargs): + if self._frozen: + raise Exception("cannot modify frozen object") + super().__setattr__(*args, **kwargs) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 30f4e0dfd3..755ca82220 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -62,4 +62,4 @@ A good pull request has all of the following: * Consider opting into driver camera uploads to improve the driver monitoring model. * Connect your device to Wi-Fi regularly, so that we can pull data for training better driving models. * Run the `nightly` branch and report issues. This branch is like `master` but it's built just like a release. -* Annotate images in the [comma10k dateset](https://github.com/commaai/comma10k). +* Annotate images in the [comma10k dataset](https://github.com/commaai/comma10k). diff --git a/opendbc b/opendbc index 0ac21652f2..5f096db742 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 0ac21652f2e643e29aa471ad6b238bf74b22e356 +Subproject commit 5f096db742b0c5dcd19976afdb07d5dd098f4b07 diff --git a/panda b/panda index 0c7d5f11d7..ea156f7c62 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 0c7d5f11d7187904022ea49b6a76b54d7b280345 +Subproject commit ea156f7c628a371bea9a15a29f9068d5392534ba diff --git a/pyproject.toml b/pyproject.toml index ac5bb0922c..99a8602460 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,6 +190,7 @@ lint.flake8-implicit-str-concat.allow-multiline=false "system".msg = "Use openpilot.system" "third_party".msg = "Use openpilot.third_party" "tools".msg = "Use openpilot.tools" +"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!" [tool.coverage.run] concurrency = ["multiprocessing", "thread"] diff --git a/release/files_common b/release/files_common index d62ef8d1bb..1fb05b43a7 100644 --- a/release/files_common +++ b/release/files_common @@ -60,6 +60,8 @@ system/logmessaged.py system/micd.py system/version.py +selfdrive/SConscript + selfdrive/athena/__init__.py selfdrive/athena/athenad.py selfdrive/athena/manage_athenad.py diff --git a/selfdrive/SConscript b/selfdrive/SConscript new file mode 100644 index 0000000000..6b72177d8e --- /dev/null +++ b/selfdrive/SConscript @@ -0,0 +1,7 @@ +SConscript(['boardd/SConscript']) +SConscript(['controls/lib/lateral_mpc_lib/SConscript']) +SConscript(['controls/lib/longitudinal_mpc_lib/SConscript']) +SConscript(['locationd/SConscript']) +SConscript(['navd/SConscript']) +SConscript(['modeld/SConscript']) +SConscript(['ui/SConscript']) \ No newline at end of file diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index 2db1e31b4a..af3b3a1d09 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -1,12 +1,14 @@ # functions common among cars from collections import namedtuple from dataclasses import dataclass, field -from enum import ReprEnum +from enum import IntFlag, ReprEnum +from dataclasses import replace import capnp from cereal import car from openpilot.common.numpy_fast import clip, interp +from openpilot.common.utils import Freezable from openpilot.selfdrive.car.docs_definitions import CarInfo @@ -292,3 +294,7 @@ class Platforms(str, ReprEnum): @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} diff --git a/selfdrive/car/body/carcontroller.py b/selfdrive/car/body/carcontroller.py index 1dad8e796a..db34320caa 100644 --- a/selfdrive/car/body/carcontroller.py +++ b/selfdrive/car/body/carcontroller.py @@ -1,10 +1,10 @@ import numpy as np -from openpilot.common.params import Params from openpilot.common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from openpilot.selfdrive.car.body import bodycan from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.controls.lib.pid import PIDController @@ -15,23 +15,18 @@ MAX_POS_INTEGRATOR = 0.2 # meters MAX_TURN_INTEGRATOR = 0.1 # meters -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.frame = 0 self.packer = CANPacker(dbc_name) - # Speed, balance and turn PIDs - self.speed_pid = PIDController(0.115, k_i=0.23, rate=1/DT_CTRL) - self.balance_pid = PIDController(1300, k_i=0, k_d=280, rate=1/DT_CTRL) + # PIDs self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL) self.torque_r_filtered = 0. self.torque_l_filtered = 0. - params = Params() - self.wheeled_body = params.get("WheeledBody") - @staticmethod def deadband_filter(torque, deadband): if torque > 0: @@ -55,17 +50,7 @@ class CarController: speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2. speed_error = speed_desired - speed_measured - if self.wheeled_body is None: - freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or - (speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR)) - angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator) - - # Clip angle error, this is enough to get up from stands - angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR) - angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.) - torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate) - else: - torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False) + torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False) speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr) turn_error = speed_diff_measured - speed_diff_desired diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index 050eb41b1a..39248f3f75 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -3,9 +3,10 @@ from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.car import apply_meas_steer_torque_limits from openpilot.selfdrive.car.chrysler import chryslercan from openpilot.selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags +from openpilot.selfdrive.car.interfaces import CarControllerBase -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.apply_steer_last = 0 diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 32a4f5dfcf..eb40bc6f6e 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -24,7 +24,6 @@ class CarInterface(CarInterfaceBase): elif candidate in RAM_DT: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT - ret.minSteerSpeed = 3.8 # m/s CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate not in RAM_CARS: # Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed. @@ -35,10 +34,6 @@ class CarInterface(CarInterfaceBase): # Chrysler if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO): - ret.mass = 2242. - ret.wheelbase = 3.089 - ret.steerRatio = 16.2 # Pacifica Hybrid 2017 - ret.lateralTuning.init('pid') ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] @@ -46,9 +41,6 @@ class CarInterface(CarInterfaceBase): # Jeep elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019): - ret.mass = 1778 - ret.wheelbase = 2.71 - ret.steerRatio = 16.7 ret.steerActuatorDelay = 0.2 ret.lateralTuning.init('pid') @@ -60,19 +52,12 @@ class CarInterface(CarInterfaceBase): elif candidate == CAR.RAM_1500: ret.steerActuatorDelay = 0.2 ret.wheelbase = 3.88 - ret.steerRatio = 16.3 - ret.mass = 2493. - ret.minSteerSpeed = 14.5 # Older EPS FW allow steer to zero if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw): ret.minSteerSpeed = 0. elif candidate == CAR.RAM_HD: ret.steerActuatorDelay = 0.2 - ret.wheelbase = 3.785 - ret.steerRatio = 15.61 - ret.mass = 3405. - ret.minSteerSpeed = 16 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False) else: diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index a7eec8fe5a..8fa7664b66 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -1,9 +1,9 @@ -from enum import IntFlag, StrEnum +from enum import IntFlag from dataclasses import dataclass, field from cereal import car from panda.python import uds -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 CarHarness, CarInfo, CarParts from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 @@ -13,25 +13,89 @@ Ecu = car.CarParams.Ecu class ChryslerFlags(IntFlag): HIGHER_MIN_STEERING_SPEED = 1 +@dataclass +class ChryslerCarInfo(CarInfo): + package: str = "Adaptive Cruise Control (ACC)" + car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca])) + -class CAR(StrEnum): +@dataclass +class ChryslerPlatformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None)) + + +@dataclass(frozen=True) +class ChryslerCarSpecs(CarSpecs): + minSteerSpeed: float = 3.8 # m/s + + +class CAR(Platforms): # Chrysler - PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" - PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" - PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" - PACIFICA_2018 = "CHRYSLER PACIFICA 2018" - PACIFICA_2020 = "CHRYSLER PACIFICA 2020" + PACIFICA_2017_HYBRID = ChryslerPlatformConfig( + "CHRYSLER PACIFICA HYBRID 2017", + ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"), + specs=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_2019_HYBRID = ChryslerPlatformConfig( + "CHRYSLER PACIFICA HYBRID 2019", + ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"), + specs=PACIFICA_2017_HYBRID.specs, + ) + PACIFICA_2018 = ChryslerPlatformConfig( + "CHRYSLER PACIFICA 2018", + ChryslerCarInfo("Chrysler Pacifica 2017-18"), + specs=PACIFICA_2017_HYBRID.specs, + ) + PACIFICA_2020 = ChryslerPlatformConfig( + "CHRYSLER PACIFICA 2020", + [ + ChryslerCarInfo("Chrysler Pacifica 2019-20"), + ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"), + ], + specs=PACIFICA_2017_HYBRID.specs, + ) # Dodge - DODGE_DURANGO = "DODGE DURANGO 2021" + DODGE_DURANGO = ChryslerPlatformConfig( + "DODGE DURANGO 2021", + ChryslerCarInfo("Dodge Durango 2020-21"), + specs=PACIFICA_2017_HYBRID.specs, + ) # Jeep - JEEP_GRAND_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk - JEEP_GRAND_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk + 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), + ) + + 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, + ) # Ram - RAM_1500 = "RAM 1500 5TH GEN" - RAM_HD = "RAM HD 5TH GEN" + RAM_1500 = ChryslerPlatformConfig( + "RAM 1500 5TH GEN", + ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])), + 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", + [ + ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])), + ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), + ], + dbc_dict('chrysler_ram_hd_generated', None), + specs=ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.), + ) class CarControllerParams: @@ -59,32 +123,6 @@ RAM_HD = {CAR.RAM_HD, } RAM_CARS = RAM_DT | RAM_HD -@dataclass -class ChryslerCarInfo(CarInfo): - package: str = "Adaptive Cruise Control (ACC)" - car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca])) - - -CAR_INFO: dict[str, ChryslerCarInfo | list[ChryslerCarInfo] | None] = { - CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"), - CAR.PACIFICA_2018_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"), - CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"), - CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"), - CAR.PACIFICA_2020: [ - ChryslerCarInfo("Chrysler Pacifica 2019-20"), - ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"), - ], - CAR.JEEP_GRAND_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), - CAR.JEEP_GRAND_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), - CAR.DODGE_DURANGO: ChryslerCarInfo("Dodge Durango 2020-21"), - CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])), - CAR.RAM_HD: [ - ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])), - ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), - ], -} - - CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ p16(0xf132) CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ @@ -124,16 +162,5 @@ FW_QUERY_CONFIG = FwQueryConfig( ], ) - -DBC = { - CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.DODGE_DURANGO: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_GRAND_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.JEEP_GRAND_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), - CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None), - CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None), -} +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index 841cc3af2f..1adf78b1c8 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -118,7 +118,8 @@ class CarHarness(EnumBase): nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler]) mazda = BaseCarHarness("Mazda connector") ford_q3 = BaseCarHarness("Ford Q3 connector") - ford_q4 = BaseCarHarness("Ford Q4 connector") + ford_q4 = BaseCarHarness("Ford Q4 connector", parts=[Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft, Cable.long_obdc_cable, + Cable.usbc_coupler]) class Device(EnumBase): diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 45516d6035..61c46674db 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -4,6 +4,7 @@ from opendbc.can.packer import CANPacker 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.interfaces import CarControllerBase from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX LongCtrlState = car.CarControl.Actuators.LongControlState @@ -22,7 +23,7 @@ def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_c return clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX) -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.VM = VM diff --git a/selfdrive/car/ford/tests/test_ford.py b/selfdrive/car/ford/tests/test_ford.py index fb5d07f4bf..2ad3f5db1b 100755 --- a/selfdrive/car/ford/tests/test_ford.py +++ b/selfdrive/car/ford/tests/test_ford.py @@ -19,6 +19,7 @@ ECU_ADDRESSES = { Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA) Ecu.engine: 0x7E0, # Powertrain Control Module (PCM) Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM) + Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM) } diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 30a5bdbaaf..e60b7f0612 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,11 +1,12 @@ from dataclasses import dataclass, field from enum import Enum +import panda.python.uds as uds from cereal import car from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ Device -from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries +from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 Ecu = car.CarParams.Ecu @@ -57,13 +58,13 @@ class FordCarInfo(CarInfo): def init_make(self, CP: car.CarParams): harness = CarHarness.ford_q4 if CP.carFingerprint in CANFD_CAR else CarHarness.ford_q3 - if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1, CAR.F_150_MK14): + 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: self.car_parts = CarParts([Device.threex, harness]) -@dataclass(frozen=False) +@dataclass class FordPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) @@ -140,6 +141,33 @@ class CAR(Platforms): CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1} +DATA_IDENTIFIER_FORD_ASBUILT = 0xDE00 + +ASBUILT_BLOCKS: list[tuple[int, list]] = [ + (1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]), + (2, [Ecu.abs, Ecu.debug, Ecu.eps]), + (3, [Ecu.abs, Ecu.debug, Ecu.eps]), + (4, [Ecu.debug, Ecu.fwdCamera]), + (5, [Ecu.debug]), + (6, [Ecu.debug]), + (7, [Ecu.debug]), + (8, [Ecu.debug]), + (9, [Ecu.debug]), + (16, [Ecu.debug, Ecu.fwdCamera]), + (18, [Ecu.fwdCamera]), + (20, [Ecu.fwdCamera]), + (21, [Ecu.fwdCamera]), +] + + +def ford_asbuilt_block_request(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + + +def ford_asbuilt_block_response(block_id: int): + return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1) + + FW_QUERY_CONFIG = FwQueryConfig( requests=[ # CAN and CAN FD queries are combined. @@ -147,19 +175,29 @@ FW_QUERY_CONFIG = FwQueryConfig( Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], logging=True, ), Request( [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE], + whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire], bus=0, auxiliary=True, ), + *[Request( + [StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)], + [StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)], + whitelist_ecus=ecus, + bus=0, + logging=True, + ) for block_id, ecus in ASBUILT_BLOCKS], ], extra_ecus=[ - # We are unlikely to get a response from the PCM from behind the gateway - (Ecu.engine, 0x7e0, None), - (Ecu.shiftByWire, 0x732, None), + (Ecu.engine, 0x7e0, None), # Powertrain Control Module + # Note: We are unlikely to get a response from behind the gateway + (Ecu.shiftByWire, 0x732, None), # Gear Shift Module + (Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module ], ) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 6c7e8007f2..e010c56536 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -6,6 +6,7 @@ from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_driver_steer_torque_limits from openpilot.selfdrive.car.gm import gmcan from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons +from openpilot.selfdrive.car.interfaces import CarControllerBase VisualAlert = car.CarControl.HUDControl.VisualAlert NetworkLocation = car.CarParams.NetworkLocation @@ -17,7 +18,7 @@ CAMERA_CANCEL_DELAY_FRAMES = 10 MIN_STEER_MSG_INTERVAL_MS = 15 -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.start_time = 0. diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 23f1feaadf..59b4b6ea30 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -79,7 +79,7 @@ class GMCarInfo(CarInfo): self.footnotes.append(Footnote.OBD_II) -@dataclass(frozen=False) +@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')) @@ -189,22 +189,35 @@ class CanBus: # In a Data Module, an identifier is a string used to recognize an object, # either by itself or together with the identifiers of parent objects. # Each returns a 4 byte hex representation of the decimal part number. `b"\x02\x8c\xf0'"` -> 42790951 +GM_BOOT_SOFTWARE_PART_NUMER_REQUEST = b'\x1a\xc0' # likely does not contain anything useful GM_SOFTWARE_MODULE_1_REQUEST = b'\x1a\xc1' GM_SOFTWARE_MODULE_2_REQUEST = b'\x1a\xc2' GM_SOFTWARE_MODULE_3_REQUEST = b'\x1a\xc3' + +# Part number of XML data file that is used to configure ECU +GM_XML_DATA_FILE_PART_NUMBER = b'\x1a\x9c' +GM_XML_CONFIG_COMPAT_ID = b'\x1a\x9b' # used to know if XML file is compatible with the ECU software/hardware + # This DID is for identifying the part number that reflects the mix of hardware, # software, and calibrations in the ECU when it first arrives at the vehicle assembly plant. # If there's an Alpha Code, it's associated with this part number and stored in the DID $DB. GM_END_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcb' +GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdb' GM_BASE_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcc' +GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdc' GM_FW_RESPONSE = b'\x5a' GM_FW_REQUESTS = [ + GM_BOOT_SOFTWARE_PART_NUMER_REQUEST, GM_SOFTWARE_MODULE_1_REQUEST, GM_SOFTWARE_MODULE_2_REQUEST, GM_SOFTWARE_MODULE_3_REQUEST, + GM_XML_DATA_FILE_PART_NUMBER, + GM_XML_CONFIG_COMPAT_ID, GM_END_MODEL_PART_NUMBER_REQUEST, + GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST, GM_BASE_MODEL_PART_NUMBER_REQUEST, + GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST, ] GM_RX_OFFSET = 0x400 diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index 056b47c4b3..547abcd9b9 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -7,6 +7,7 @@ from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import create_gas_interceptor_command from openpilot.selfdrive.car.honda import hondacan from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.controls.lib.drive_helpers import rate_limit VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -104,11 +105,12 @@ def rate_limit_steer(new_steer, last_steer): return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA) -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.packer = CANPacker(dbc_name) self.params = CarControllerParams(CP) + self.CAN = hondacan.CanBus(CP) self.frame = 0 self.braking = False @@ -167,7 +169,7 @@ class CarController: can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1)) # Send steering command. - can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint, + can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_steer, CC.latActive, self.CP.carFingerprint, CS.CP.openpilotLongitudinalControl)) # wind brake from air resistance decel at high speed @@ -201,12 +203,12 @@ class CarController: if not self.CP.openpilotLongitudinalControl: if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message - can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CP.carFingerprint)) + can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CAN, self.CP.carFingerprint)) # If using stock ACC, spam cancel command to kill gas when OP disengages. if pcm_cancel_cmd: - can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.CANCEL, self.CP.carFingerprint)) + can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.CANCEL, self.CP.carFingerprint)) elif CC.cruiseControl.resume: - can_sends.append(hondacan.spam_buttons_command(self.packer, CruiseButtons.RES_ACCEL, self.CP.carFingerprint)) + can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint)) else: # Send gas and brake commands. @@ -219,7 +221,7 @@ class CarController: stopping = actuators.longControlState == LongCtrlState.stopping self.stopping_counter = self.stopping_counter + 1 if stopping else 0 - can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas, + can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas, self.stopping_counter, self.CP.carFingerprint)) else: apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0) @@ -227,7 +229,7 @@ class CarController: pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts) pcm_override = True - can_sends.append(hondacan.create_brake_command(self.packer, apply_brake, pump_on, + can_sends.append(hondacan.create_brake_command(self.packer, self.CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw_display, self.CP.carFingerprint, CS.stock_brake)) self.apply_brake_last = apply_brake @@ -250,7 +252,7 @@ class CarController: if self.frame % 10 == 0: hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible, hud_control.lanesVisible, fcw_display, acc_alert, steer_required) - can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud)) + can_sends.extend(hondacan.create_ui_commands(self.packer, self.CAN, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud)) if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH: self.speed = pcm_speed diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 9025f72397..7784581e1c 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -5,7 +5,7 @@ from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import interp from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser -from openpilot.selfdrive.car.honda.hondacan import get_cruise_speed_conversion, get_pt_bus +from openpilot.selfdrive.car.honda.hondacan import CanBus, get_cruise_speed_conversion from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \ HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \ HondaFlags @@ -64,7 +64,7 @@ def get_can_messages(CP, gearbox_msg): messages.append(("CRUISE_PARAMS", 50)) # TODO: clean this up - if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, + if CP.carFingerprint in (CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): pass elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): @@ -129,7 +129,7 @@ class CarState(CarStateBase): # panda checks if the signal is non-zero ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 # TODO: find a common signal across all cars - if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, + if self.CP.carFingerprint in (CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): @@ -267,7 +267,7 @@ class CarState(CarStateBase): def get_can_parser(self, CP): messages = get_can_messages(CP, self.gearbox_msg) - return CANParser(DBC[CP.carFingerprint]["pt"], messages, get_pt_bus(CP.carFingerprint)) + return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).pt) @staticmethod def get_cam_can_parser(CP): @@ -287,7 +287,7 @@ class CarState(CarStateBase): ("BRAKE_COMMAND", 50), ] - return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2) + return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).camera) @staticmethod def get_body_can_parser(CP): @@ -296,6 +296,6 @@ class CarState(CarStateBase): ("BSM_STATUS_LEFT", 3), ("BSM_STATUS_RIGHT", 3), ] - bus_body = 0 # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port) + bus_body = CanBus(CP).radar # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port) return CANParser(DBC[CP.carFingerprint]["body"], messages, bus_body) return None diff --git a/selfdrive/car/honda/fingerprints.py b/selfdrive/car/honda/fingerprints.py index 359ae83b15..0ae39751f9 100644 --- a/selfdrive/car/honda/fingerprints.py +++ b/selfdrive/car/honda/fingerprints.py @@ -48,6 +48,7 @@ FW_VERSIONS = { ], (Ecu.shiftByWire, 0x18da0bf1, None): [ b'54008-TVC-A910\x00\x00', + b'54008-TWA-A910\x00\x00', ], (Ecu.transmission, 0x18da1ef1, None): [ b'28101-6A7-A220\x00\x00', @@ -89,6 +90,12 @@ FW_VERSIONS = { b'57114-TVA-C530\x00\x00', b'57114-TVA-E520\x00\x00', b'57114-TVE-H250\x00\x00', + b'57114-TWA-A040\x00\x00', + b'57114-TWA-A050\x00\x00', + b'57114-TWA-A530\x00\x00', + b'57114-TWA-B520\x00\x00', + b'57114-TWA-C510\x00\x00', + b'57114-TWB-H030\x00\x00', ], (Ecu.eps, 0x18da30f1, None): [ b'39990-TBX-H120\x00\x00', @@ -100,6 +107,7 @@ FW_VERSIONS = { b'39990-TVA-X030\x00\x00', b'39990-TVA-X040\x00\x00', b'39990-TVE-H130\x00\x00', + b'39990-TWB-H120\x00\x00', ], (Ecu.srs, 0x18da53f1, None): [ b'77959-TBX-H230\x00\x00', @@ -108,6 +116,9 @@ FW_VERSIONS = { b'77959-TVA-H230\x00\x00', b'77959-TVA-L420\x00\x00', b'77959-TVA-X330\x00\x00', + b'77959-TWA-A440\x00\x00', + b'77959-TWA-L420\x00\x00', + b'77959-TWB-H220\x00\x00', ], (Ecu.combinationMeter, 0x18da60f1, None): [ b'78109-TBX-H310\x00\x00', @@ -141,7 +152,19 @@ FW_VERSIONS = { b'78109-TVC-M510\x00\x00', b'78109-TVC-YF10\x00\x00', b'78109-TVE-H610\x00\x00', + b'78109-TWA-A010\x00\x00', + b'78109-TWA-A020\x00\x00', + b'78109-TWA-A030\x00\x00', + b'78109-TWA-A110\x00\x00', + b'78109-TWA-A120\x00\x00', + b'78109-TWA-A130\x00\x00', b'78109-TWA-A210\x00\x00', + b'78109-TWA-A220\x00\x00', + b'78109-TWA-A230\x00\x00', + b'78109-TWA-A610\x00\x00', + b'78109-TWA-H210\x00\x00', + b'78109-TWA-L010\x00\x00', + b'78109-TWA-L210\x00\x00', ], (Ecu.hud, 0x18da61f1, None): [ b'78209-TVA-A010\x00\x00', @@ -158,6 +181,9 @@ FW_VERSIONS = { b'36802-TVE-H070\x00\x00', b'36802-TWA-A070\x00\x00', b'36802-TWA-A080\x00\x00', + b'36802-TWA-A210\x00\x00', + b'36802-TWA-A330\x00\x00', + b'36802-TWB-H060\x00\x00', ], (Ecu.fwdCamera, 0x18dab5f1, None): [ b'36161-TBX-H130\x00\x00', @@ -166,72 +192,17 @@ FW_VERSIONS = { b'36161-TVC-A330\x00\x00', b'36161-TVE-H050\x00\x00', b'36161-TWA-A070\x00\x00', + b'36161-TWA-A330\x00\x00', + b'36161-TWB-H040\x00\x00', ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-TVA-A010\x00\x00', b'38897-TVA-A020\x00\x00', b'38897-TVA-A230\x00\x00', b'38897-TVA-A240\x00\x00', - ], - }, - CAR.ACCORDH: { - (Ecu.gateway, 0x18daeff1, None): [ b'38897-TWA-A120\x00\x00', b'38897-TWD-J020\x00\x00', ], - (Ecu.vsa, 0x18da28f1, None): [ - b'57114-TWA-A040\x00\x00', - b'57114-TWA-A050\x00\x00', - b'57114-TWA-A530\x00\x00', - b'57114-TWA-B520\x00\x00', - b'57114-TWA-C510\x00\x00', - b'57114-TWB-H030\x00\x00', - ], - (Ecu.srs, 0x18da53f1, None): [ - b'77959-TWA-A440\x00\x00', - b'77959-TWA-L420\x00\x00', - b'77959-TWB-H220\x00\x00', - ], - (Ecu.combinationMeter, 0x18da60f1, None): [ - b'78109-TWA-A010\x00\x00', - b'78109-TWA-A020\x00\x00', - b'78109-TWA-A030\x00\x00', - b'78109-TWA-A110\x00\x00', - b'78109-TWA-A120\x00\x00', - b'78109-TWA-A130\x00\x00', - b'78109-TWA-A210\x00\x00', - b'78109-TWA-A220\x00\x00', - b'78109-TWA-A230\x00\x00', - b'78109-TWA-A610\x00\x00', - b'78109-TWA-H210\x00\x00', - b'78109-TWA-L010\x00\x00', - b'78109-TWA-L210\x00\x00', - ], - (Ecu.shiftByWire, 0x18da0bf1, None): [ - b'54008-TWA-A910\x00\x00', - ], - (Ecu.hud, 0x18da61f1, None): [ - b'78209-TVA-A010\x00\x00', - b'78209-TVA-A110\x00\x00', - ], - (Ecu.fwdCamera, 0x18dab5f1, None): [ - b'36161-TWA-A070\x00\x00', - b'36161-TWA-A330\x00\x00', - b'36161-TWB-H040\x00\x00', - ], - (Ecu.fwdRadar, 0x18dab0f1, None): [ - b'36802-TWA-A070\x00\x00', - b'36802-TWA-A080\x00\x00', - b'36802-TWA-A210\x00\x00', - b'36802-TWA-A330\x00\x00', - b'36802-TWB-H060\x00\x00', - ], - (Ecu.eps, 0x18da30f1, None): [ - b'39990-TVA-A150\x00\x00', - b'39990-TVA-A160\x00\x00', - b'39990-TVA-A340\x00\x00', - b'39990-TWB-H120\x00\x00', - ], }, CAR.CIVIC: { (Ecu.programmedFuelInjection, 0x18da10f1, None): [ diff --git a/selfdrive/car/honda/hondacan.py b/selfdrive/car/honda/hondacan.py index a8cbad78ce..d10d5576d9 100644 --- a/selfdrive/car/honda/hondacan.py +++ b/selfdrive/car/honda/hondacan.py @@ -1,4 +1,5 @@ from openpilot.common.conversions import Conversions as CV +from openpilot.selfdrive.car import CanBusBase from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams # CAN bus layout with relay @@ -8,15 +9,34 @@ from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_ # 3 = F-CAN A - OBDII port -def get_pt_bus(car_fingerprint): - return 1 if car_fingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) else 0 +class CanBus(CanBusBase): + def __init__(self, CP=None, fingerprint=None) -> None: + # use fingerprint if specified + super().__init__(CP if fingerprint is None else None, fingerprint) + + if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS): + self._pt, self._radar = self.offset + 1, self.offset + else: + self._pt, self._radar = self.offset, self.offset + 1 + + @property + def pt(self) -> int: + return self._pt + + @property + def radar(self) -> int: + return self._radar + + @property + def camera(self) -> int: + return self.offset + 2 -def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False): +def get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled=False): no_radar = car_fingerprint in HONDA_BOSCH_RADARLESS if radar_disabled or no_radar: # when radar is disabled, steering commands are sent directly to powertrain bus - return get_pt_bus(car_fingerprint) + return CAN.pt # normally steering commands are sent to radar, which forwards them to powertrain bus return 0 @@ -26,7 +46,7 @@ def get_cruise_speed_conversion(car_fingerprint: str, is_metric: bool) -> float: return CV.MPH_TO_MS if car_fingerprint in HONDA_BOSCH_RADARLESS and not is_metric else CV.KPH_TO_MS -def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake): +def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake): # TODO: do we loose pressure if we keep pump off for long? brakelights = apply_brake > 0 brake_rq = apply_brake > 0 @@ -47,13 +67,11 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_ "AEB_REQ_2": 0, "AEB_STATUS": 0, } - bus = get_pt_bus(car_fingerprint) - return packer.make_can_msg("BRAKE_COMMAND", bus, values) + return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values) -def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, car_fingerprint): +def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint): commands = [] - bus = get_pt_bus(car_fingerprint) min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0] control_on = 5 if enabled else 0 @@ -90,37 +108,36 @@ def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, c "SET_TO_75": 0x75, "SET_TO_30": 0x30, } - commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values)) + commands.append(packer.make_can_msg("ACC_CONTROL_ON", CAN.pt, acc_control_on_values)) - commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values)) + commands.append(packer.make_can_msg("ACC_CONTROL", CAN.pt, acc_control_values)) return commands -def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, radar_disabled): +def create_steering_control(packer, CAN, apply_steer, lkas_active, car_fingerprint, radar_disabled): values = { "STEER_TORQUE": apply_steer if lkas_active else 0, "STEER_TORQUE_REQUEST": lkas_active, } - bus = get_lkas_cmd_bus(car_fingerprint, radar_disabled) + bus = get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled) return packer.make_can_msg("STEERING_CONTROL", bus, values) -def create_bosch_supplemental_1(packer, car_fingerprint): +def create_bosch_supplemental_1(packer, CAN, car_fingerprint): # non-active params values = { "SET_ME_X04": 0x04, "SET_ME_X80": 0x80, "SET_ME_X10": 0x10, } - bus = get_lkas_cmd_bus(car_fingerprint) + bus = get_lkas_cmd_bus(CAN, car_fingerprint) return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values) -def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud): +def create_ui_commands(packer, CAN, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud): commands = [] - bus_pt = get_pt_bus(CP.carFingerprint) radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl - bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled) + bus_lkas = get_lkas_cmd_bus(CAN, CP.carFingerprint, radar_disabled) if CP.openpilotLongitudinalControl: acc_hud_values = { @@ -144,7 +161,7 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, acc_hud_values['FCM_OFF_2'] = acc_hud['FCM_OFF_2'] acc_hud_values['FCM_PROBLEM'] = acc_hud['FCM_PROBLEM'] acc_hud_values['ICONS'] = acc_hud['ICONS'] - commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values)) + commands.append(packer.make_can_msg("ACC_HUD", CAN.pt, acc_hud_values)) lkas_hud_values = { 'SET_ME_X41': 0x41, @@ -173,19 +190,19 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, 'CMBS_OFF': 0x01, 'SET_TO_1': 0x01, } - commands.append(packer.make_can_msg('RADAR_HUD', bus_pt, radar_hud_values)) + commands.append(packer.make_can_msg('RADAR_HUD', CAN.pt, radar_hud_values)) if CP.carFingerprint == CAR.CIVIC_BOSCH: - commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", bus_pt, {})) + commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", CAN.pt, {})) return commands -def spam_buttons_command(packer, button_val, car_fingerprint): +def spam_buttons_command(packer, CAN, button_val, car_fingerprint): values = { 'CRUISE_BUTTONS': button_val, 'CRUISE_SETTING': 0, } # send buttons to camera on radarless cars - bus = 2 if car_fingerprint in HONDA_BOSCH_RADARLESS else get_pt_bus(car_fingerprint) + bus = CAN.camera if car_fingerprint in HONDA_BOSCH_RADARLESS else CAN.pt return packer.make_can_msg("SCM_BUTTONS", bus, values) diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 153fa1e635..a4cf647c0f 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -3,7 +3,7 @@ from cereal import car from panda import Panda from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import interp -from openpilot.selfdrive.car.honda.hondacan import get_pt_bus +from openpilot.selfdrive.car.honda.hondacan import CanBus from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \ HONDA_BOSCH_RADARLESS from openpilot.selfdrive.car import create_button_events, get_safety_config @@ -36,6 +36,8 @@ class CarInterface(CarInterfaceBase): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): ret.carName = "honda" + CAN = CanBus(ret, fingerprint) + if candidate in HONDA_BOSCH: ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] ret.radarUnavailable = True @@ -47,20 +49,20 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = not ret.openpilotLongitudinalControl else: ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaNidec)] - ret.enableGasInterceptor = 0x201 in fingerprint[0] + ret.enableGasInterceptor = 0x201 in fingerprint[CAN.pt] ret.openpilotLongitudinalControl = True ret.pcmCruise = not ret.enableGasInterceptor if candidate == CAR.CRV_5G: - ret.enableBsm = 0x12f8bfa7 in fingerprint[0] + ret.enableBsm = 0x12f8bfa7 in fingerprint[CAN.radar] # Detect Bosch cars with new HUD msgs if any(0x33DA in f for f in fingerprint.values()): ret.flags |= HondaFlags.BOSCH_EXT_HUD.value - # Accord 1.5T CVT has different gearbox message - if candidate == CAR.ACCORD and 0x191 in fingerprint[1]: + # Accord ICE 1.5T CVT has different gearbox message + if candidate == CAR.ACCORD and 0x191 in fingerprint[CAN.pt]: ret.transmissionType = TransmissionType.cvt # Certain Hondas have an extra steering sensor at the bottom of the steering rack, @@ -115,7 +117,7 @@ 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 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] - elif candidate in (CAR.ACCORD, CAR.ACCORDH): + elif candidate == CAR.ACCORD: ret.mass = 3279. * CV.LB_TO_KG ret.wheelbase = 2.83 ret.centerToFront = ret.wheelbase * 0.39 @@ -276,7 +278,7 @@ class CarInterface(CarInterfaceBase): raise ValueError(f"unsupported car {candidate}") # These cars use alternate user brake msg (0x1BE) - if 0x1BE in fingerprint[get_pt_bus(candidate)] and candidate in HONDA_BOSCH: + if 0x1BE in fingerprint[CAN.pt] and candidate in HONDA_BOSCH: ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index a2ef757d15..ce4a531d01 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -74,7 +74,6 @@ VISUAL_HUD = { class CAR(StrEnum): ACCORD = "HONDA ACCORD 2018" - ACCORDH = "HONDA ACCORD HYBRID 2018" CIVIC = "HONDA CIVIC 2016" CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" @@ -119,8 +118,8 @@ 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.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC_BOSCH: [ HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", @@ -188,7 +187,6 @@ FW_QUERY_CONFIG = FwQueryConfig( [StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_RESPONSE], bus=0, - logging=True, ), # Bosch PT bus Request( @@ -201,12 +199,16 @@ FW_QUERY_CONFIG = FwQueryConfig( # 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.programmedFuelInjection: [CAR.CIVIC_BOSCH, CAR.CRV_5G], - Ecu.transmission: [CAR.CIVIC_BOSCH, CAR.CRV_5G], - Ecu.vsa: [CAR.CIVIC_BOSCH, CAR.CRV_5G], - Ecu.combinationMeter: [CAR.CIVIC_BOSCH, CAR.CRV_5G], - Ecu.gateway: [CAR.CIVIC_BOSCH, CAR.CRV_5G], - Ecu.electricBrakeBooster: [CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.programmedFuelInjection: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.transmission: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.srs: [CAR.ACCORD], + Ecu.eps: [CAR.ACCORD], + Ecu.vsa: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.combinationMeter: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.gateway: [CAR.ACCORD, CAR.CIVIC, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.electricBrakeBooster: [CAR.ACCORD, CAR.CIVIC_BOSCH, CAR.CRV_5G], + Ecu.shiftByWire: [CAR.ACCORD], # existence correlates with transmission type for ICE + Ecu.hud: [CAR.ACCORD], # existence correlates with trim level }, extra_ecus=[ # The only other ECU on PT bus accessible by camera on radarless Civic @@ -217,7 +219,6 @@ FW_QUERY_CONFIG = FwQueryConfig( DBC = { CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None), - CAR.ACCORDH: 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), @@ -250,6 +251,6 @@ STEER_THRESHOLD = { 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.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, +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} diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 0b5f724ab9..b0851abf55 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -7,6 +7,7 @@ from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fau from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR +from openpilot.selfdrive.car.interfaces import CarControllerBase VisualAlert = car.CarControl.HUDControl.VisualAlert LongCtrlState = car.CarControl.Actuators.LongControlState @@ -42,7 +43,7 @@ def process_hud_alert(enabled, fingerprint, hud_control): return sys_warning, sys_state, left_lane_warning, right_lane_warning -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.CAN = CanBus(CP) diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 0a5ec94aa0..491138f788 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -120,6 +120,7 @@ class CarInterfaceBase(ABC): 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 = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) @@ -135,7 +136,7 @@ class CarInterfaceBase(ABC): @staticmethod @abstractmethod - def _get_params(ret: car.CarParams, candidate: Platform, fingerprint: dict[int, dict[int, int]], + def _get_params(ret: car.CarParams, candidate, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool): raise NotImplementedError @@ -455,6 +456,15 @@ class CarStateBase(ABC): return None +SendCan = tuple[int, int, bytes, int] + + +class CarControllerBase(ABC): + @abstractmethod + def update(self, CC, CS, now_nanos) -> tuple[car.CarControl.Actuators, list[SendCan]]: + pass + + INTERFACE_ATTR_FILE = { "FINGERPRINTS": "fingerprints", "FW_VERSIONS": "fingerprints", diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 320ad19bb4..3f3b7a9494 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -1,13 +1,14 @@ from cereal import car from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_driver_steer_torque_limits +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.mazda import mazdacan from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons VisualAlert = car.CarControl.HUDControl.VisualAlert -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.apply_steer_last = 0 diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py index 2da518bbf1..6aa4bb9438 100644 --- a/selfdrive/car/nissan/carcontroller.py +++ b/selfdrive/car/nissan/carcontroller.py @@ -1,13 +1,14 @@ from cereal import car from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_std_steer_angle_limits +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.nissan import nissancan from openpilot.selfdrive.car.nissan.values import CAR, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.car_fingerprint = CP.carFingerprint diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index aedcaa1887..a94b97de20 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -16,25 +16,13 @@ class CarInterface(CarInterfaceBase): ret.steerLimitTimer = 1.0 ret.steerActuatorDelay = 0.1 - ret.steerRatio = 17 ret.steerControlType = car.CarParams.SteerControlType.angle ret.radarUnavailable = True - if candidate in (CAR.ROGUE, CAR.XTRAIL): - ret.mass = 1610 - ret.wheelbase = 2.705 - ret.centerToFront = ret.wheelbase * 0.44 - elif candidate in (CAR.LEAF, CAR.LEAF_IC): - ret.mass = 1610 - ret.wheelbase = 2.705 - ret.centerToFront = ret.wheelbase * 0.44 - elif candidate == CAR.ALTIMA: + if candidate == CAR.ALTIMA: # Altima has EPS on C-CAN unlike the others that have it on V-CAN ret.safetyConfigs[0].safetyParam |= Panda.FLAG_NISSAN_ALT_EPS_BUS - ret.mass = 1492 - ret.wheelbase = 2.824 - ret.centerToFront = ret.wheelbase * 0.44 return ret diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index c0308f9e0d..7af1700e0b 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -1,9 +1,8 @@ from dataclasses import dataclass, field -from enum import StrEnum from cereal import car from panda.python import uds -from openpilot.selfdrive.car import AngleRateLimit, dbc_dict +from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict from openpilot.selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries @@ -20,29 +19,52 @@ class CarControllerParams: pass -class CAR(StrEnum): - XTRAIL = "NISSAN X-TRAIL 2017" - LEAF = "NISSAN LEAF 2018" - # Leaf with ADAS ECU found behind instrument cluster instead of glovebox - # Currently the only known difference between them is the inverted seatbelt signal. - LEAF_IC = "NISSAN LEAF 2018 Instrument Cluster" - ROGUE = "NISSAN ROGUE 2019" - ALTIMA = "NISSAN ALTIMA 2020" - - @dataclass class NissanCarInfo(CarInfo): package: str = "ProPILOT Assist" car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a])) -CAR_INFO: dict[str, NissanCarInfo | list[NissanCarInfo] | None] = { - CAR.XTRAIL: NissanCarInfo("Nissan X-Trail 2017"), - CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), - CAR.LEAF_IC: None, # same platforms - CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"), - CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])), -} +@dataclass(frozen=True) +class NissanCarSpecs(CarSpecs): + centerToFrontRatio: float = 0.44 + steerRatio: float = 17. + + +@dataclass +class NissanPlaformConfig(PlatformConfig): + dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None)) + + +class CAR(Platforms): + XTRAIL = NissanPlaformConfig( + "NISSAN X-TRAIL 2017", + NissanCarInfo("Nissan X-Trail 2017"), + specs=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) + ) + # Leaf with ADAS ECU found behind instrument cluster instead of glovebox + # Currently the only known difference between them is the inverted seatbelt signal. + LEAF_IC = LEAF.override(platform_str="NISSAN LEAF 2018 Instrument Cluster", car_info=None) + ROGUE = NissanPlaformConfig( + "NISSAN ROGUE 2019", + NissanCarInfo("Nissan Rogue 2018-20"), + specs=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) + ) + + +CAR_INFO = CAR.create_carinfo_map() +DBC = CAR.create_dbc_map() # Default diagnostic session NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81]) @@ -88,11 +110,3 @@ FW_QUERY_CONFIG = FwQueryConfig( ), ]], ) - -DBC = { - CAR.XTRAIL: dbc_dict('nissan_x_trail_2017_generated', None), - CAR.LEAF: dbc_dict('nissan_leaf_2018_generated', None), - CAR.LEAF_IC: dbc_dict('nissan_leaf_2018_generated', None), - CAR.ROGUE: dbc_dict('nissan_x_trail_2017_generated', None), - CAR.ALTIMA: dbc_dict('nissan_x_trail_2017_generated', None), -} diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index cc8ce4f722..22a1475b5b 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -1,9 +1,9 @@ from openpilot.common.numpy_fast import clip, interp from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.subaru import subarucan -from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, STEER_RATE_LIMITED, \ - CanBus, CarControllerParams, SubaruFlags +from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags # FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and # involves the total steering angle change rather than rate, but these limits work well for now @@ -11,7 +11,7 @@ MAX_STEER_RATE = 25 # deg/s MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.apply_steer_last = 0 @@ -42,12 +42,12 @@ class CarController: if not CC.latActive: apply_steer = 0 - if self.CP.carFingerprint in PREGLOBAL_CARS: + if self.CP.flags & SubaruFlags.PREGLOBAL: can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive)) else: apply_steer_req = CC.latActive - if self.CP.carFingerprint in STEER_RATE_LIMITED: + if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED: # Steering rate fault prevention self.steer_rate_counter, apply_steer_req = \ common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, @@ -74,7 +74,7 @@ class CarController: cruise_brake = CarControllerParams.BRAKE_MIN # *** alerts and pcm cancel *** - if self.CP.carFingerprint in PREGLOBAL_CARS: + if self.CP.flags & SubaruFlags.PREGLOBAL: if self.frame % 5 == 0: # 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep # disengage ACC when OP is disengaged @@ -117,8 +117,8 @@ class CarController: self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle)) else: if pcm_cancel_cmd: - if self.CP.carFingerprint not in HYBRID_CARS: - bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main + if not (self.CP.flags & SubaruFlags.HYBRID): + bus = CanBus.alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else CanBus.main can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd)) if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT: diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py index c8a6dfe1e6..ebf0ca9062 100644 --- a/selfdrive/car/subaru/carstate.py +++ b/selfdrive/car/subaru/carstate.py @@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine 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.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, CanBus, SubaruFlags +from openpilot.selfdrive.car.subaru.values import DBC, CanBus, SubaruFlags from openpilot.selfdrive.car import CanSignalRateCalculator @@ -19,17 +19,17 @@ class CarState(CarStateBase): def update(self, cp, cp_cam, cp_body): ret = car.CarState.new_message() - throttle_msg = cp.vl["Throttle"] if self.car_fingerprint not in HYBRID_CARS else cp_body.vl["Throttle_Hybrid"] + throttle_msg = cp.vl["Throttle"] if not (self.CP.flags & SubaruFlags.HYBRID) else cp_body.vl["Throttle_Hybrid"] ret.gas = throttle_msg["Throttle_Pedal"] / 255. ret.gasPressed = ret.gas > 1e-5 - if self.car_fingerprint in PREGLOBAL_CARS: + if self.CP.flags & SubaruFlags.PREGLOBAL: ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0 else: - cp_brakes = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp + cp_brakes = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp ret.brakePressed = cp_brakes.vl["Brake_Status"]["Brake"] == 1 - cp_wheels = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp + cp_wheels = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp ret.wheelSpeeds = self.get_wheel_speeds( cp_wheels.vl["Wheel_Speeds"]["FL"], cp_wheels.vl["Wheel_Speeds"]["FR"], @@ -48,24 +48,24 @@ class CarState(CarStateBase): ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1) ret.rightBlindspot = (cp.vl["BSD_RCTA"]["R_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["R_APPROACHING"] == 1) - cp_transmission = cp_body if self.car_fingerprint in HYBRID_CARS else cp + cp_transmission = cp_body if self.CP.flags & SubaruFlags.HYBRID else cp can_gear = int(cp_transmission.vl["Transmission"]["Gear"]) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] - if self.car_fingerprint not in PREGLOBAL_CARS: + if not (self.CP.flags & SubaruFlags.PREGLOBAL): # ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"]) ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] - steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80 + steer_threshold = 75 if self.CP.flags & SubaruFlags.PREGLOBAL else 80 ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold - cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp - if self.car_fingerprint in HYBRID_CARS: + cp_cruise = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp + if self.CP.flags & SubaruFlags.HYBRID: ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0 ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0 else: @@ -73,8 +73,8 @@ class CarState(CarStateBase): ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0 ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS - if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \ - (self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1): + if (self.CP.flags & SubaruFlags.PREGLOBAL and cp.vl["Dash_State2"]["UNITS"] == 1) or \ + (not (self.CP.flags & SubaruFlags.PREGLOBAL) and cp.vl["Dashlights"]["UNITS"] == 1): ret.cruiseState.speed *= CV.MPH_TO_KPH ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1 @@ -84,8 +84,8 @@ class CarState(CarStateBase): cp.vl["BodyInfo"]["DOOR_OPEN_FL"]]) ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1 - cp_es_distance = cp_body if self.car_fingerprint in (GLOBAL_GEN2 | HYBRID_CARS) else cp_cam - if self.car_fingerprint in PREGLOBAL_CARS: + cp_es_distance = cp_body if self.CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID) else cp_cam + if self.CP.flags & SubaruFlags.PREGLOBAL: self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"] self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"] else: @@ -96,12 +96,12 @@ class CarState(CarStateBase): (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2) self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"]) - cp_es_brake = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam + cp_es_brake = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"]) - cp_es_status = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam + cp_es_status = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam # TODO: Hybrid cars don't have ES_Distance, need a replacement - if self.car_fingerprint not in HYBRID_CARS: + if not (self.CP.flags & SubaruFlags.HYBRID): # 8 is known AEB, there are a few other values related to AEB we ignore ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \ (cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0) @@ -109,7 +109,7 @@ class CarState(CarStateBase): self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"]) self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"]) - if self.car_fingerprint not in HYBRID_CARS: + if not (self.CP.flags & SubaruFlags.HYBRID): self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"]) self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"]) @@ -125,7 +125,7 @@ class CarState(CarStateBase): ("Brake_Status", 50), ] - if CP.carFingerprint not in HYBRID_CARS: + if not (CP.flags & SubaruFlags.HYBRID): messages.append(("CruiseControl", 20)) return messages @@ -136,7 +136,7 @@ class CarState(CarStateBase): ("ES_Brake", 20), ] - if CP.carFingerprint not in HYBRID_CARS: + if not (CP.flags & SubaruFlags.HYBRID): messages += [ ("ES_Distance", 20), ("ES_Status", 20) @@ -164,7 +164,7 @@ class CarState(CarStateBase): ("Brake_Pedal", 50), ] - if CP.carFingerprint not in HYBRID_CARS: + if not (CP.flags & SubaruFlags.HYBRID): messages += [ ("Throttle", 100), ("Transmission", 100) @@ -173,8 +173,8 @@ class CarState(CarStateBase): if CP.enableBsm: messages.append(("BSD_RCTA", 17)) - if CP.carFingerprint not in PREGLOBAL_CARS: - if CP.carFingerprint not in GLOBAL_GEN2: + if not (CP.flags & SubaruFlags.PREGLOBAL): + if not (CP.flags & SubaruFlags.GLOBAL_GEN2): messages += CarState.get_common_global_body_messages(CP) else: messages += CarState.get_common_preglobal_body_messages() @@ -183,7 +183,7 @@ class CarState(CarStateBase): @staticmethod def get_cam_can_parser(CP): - if CP.carFingerprint in PREGLOBAL_CARS: + if CP.flags & SubaruFlags.PREGLOBAL: messages = [ ("ES_DashStatus", 20), ("ES_Distance", 20), @@ -194,7 +194,7 @@ class CarState(CarStateBase): ("ES_LKAS_State", 10), ] - if CP.carFingerprint not in GLOBAL_GEN2: + if not (CP.flags & SubaruFlags.GLOBAL_GEN2): messages += CarState.get_common_global_es_messages(CP) if CP.flags & SubaruFlags.SEND_INFOTAINMENT: @@ -206,11 +206,11 @@ class CarState(CarStateBase): def get_body_can_parser(CP): messages = [] - if CP.carFingerprint in GLOBAL_GEN2: + if CP.flags & SubaruFlags.GLOBAL_GEN2: messages += CarState.get_common_global_body_messages(CP) messages += CarState.get_common_global_es_messages(CP) - if CP.carFingerprint in HYBRID_CARS: + if CP.flags & SubaruFlags.HYBRID: messages += [ ("Throttle_Hybrid", 40), ("Transmission", 100) diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 2dfb4f9565..ecf718bb3a 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -1,42 +1,43 @@ from cereal import car from panda import Panda from openpilot.selfdrive.car import get_safety_config -from openpilot.selfdrive.car.values import Platform from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase -from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, LKAS_ANGLE, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, SubaruFlags +from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags class CarInterface(CarInterfaceBase): @staticmethod - def _get_params(ret, candidate: Platform, fingerprint, car_fw, experimental_long, docs): + 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 = candidate in (PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS) + ret.dashcamOnly = bool(platform_flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID)) ret.autoResumeSng = False # Detect infotainment message sent from the camera - if candidate not in PREGLOBAL_CARS and 0x323 in fingerprint[2]: + if not (platform_flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]: ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value - if candidate in PREGLOBAL_CARS: + if platform_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 candidate in GLOBAL_GEN2: + if platform_flags & SubaruFlags.GLOBAL_GEN2: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2 ret.steerLimitTimer = 0.4 ret.steerActuatorDelay = 0.1 - if candidate in LKAS_ANGLE: + if platform_flags & SubaruFlags.LKAS_ANGLE: ret.steerControlType = car.CarParams.SteerControlType.angle else: CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) @@ -84,10 +85,12 @@ class CarInterface(CarInterfaceBase): else: raise ValueError(f"unknown car: {candidate}") - ret.experimentalLongitudinalAvailable = candidate not in (GLOBAL_GEN2 | PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS) + LONG_UNAVAILABLE = SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL| SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID + + ret.experimentalLongitudinalAvailable = not (platform_flags & LONG_UNAVAILABLE) ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable - if candidate in GLOBAL_GEN2 and ret.openpilotLongitudinalControl: + if platform_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 b17b134904..a7c9a22e2c 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -19,7 +19,7 @@ class CarControllerParams: self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily self.STEER_DRIVER_FACTOR = 1 # from dbc - if CP.carFingerprint in GLOBAL_GEN2: + if CP.flags & SubaruFlags.GLOBAL_GEN2: self.STEER_MAX = 1000 self.STEER_DELTA_UP = 40 self.STEER_DELTA_DOWN = 40 @@ -55,6 +55,14 @@ class CarControllerParams: class SubaruFlags(IntFlag): SEND_INFOTAINMENT = 1 DISABLE_EYESIGHT = 2 + GLOBAL_GEN2 = 4 + + # Cars that temporarily fault when steering angle rate is greater than some threshold. + # Appears to be all torque-based cars produced around 2019 - present + STEER_RATE_LIMITED = 8 + PREGLOBAL = 16 + HYBRID = 32 + LKAS_ANGLE = 64 GLOBAL_ES_ADDR = 0x787 @@ -89,10 +97,14 @@ class SubaruCarInfo(CarInfo): self.footnotes.append(Footnote.EXP_LONG) -@dataclass(frozen=False) +@dataclass class SubaruPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None)) + def init(self): + if self.flags & SubaruFlags.HYBRID: + self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None) + class CAR(Platforms): # Global platform @@ -105,11 +117,13 @@ class CAR(Platforms): "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, ) LEGACY = SubaruPlatformConfig( "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, ) IMPREZA = SubaruPlatformConfig( "SUBARU IMPREZA LIMITED 2019", @@ -128,24 +142,26 @@ class CAR(Platforms): SubaruCarInfo("Subaru XV 2020-21"), ], specs=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])), - dbc_dict('subaru_global_2020_hybrid_generated', None), specs=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), + flags=SubaruFlags.STEER_RATE_LIMITED, ) FORESTER_HYBRID = SubaruPlatformConfig( "SUBARU FORESTER HYBRID 2020", SubaruCarInfo("Subaru Forester Hybrid 2020"), - dbc_dict('subaru_global_2020_hybrid_generated', None), specs=FORESTER.specs, + flags=SubaruFlags.HYBRID, ) # Pre-global FORESTER_PREGLOBAL = SubaruPlatformConfig( @@ -153,52 +169,50 @@ class CAR(Platforms): SubaruCarInfo("Subaru Forester 2017-18"), 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"), 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"), 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"), 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, + flags=SubaruFlags.LKAS_ANGLE, ) OUTBACK_2023 = SubaruPlatformConfig( "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, ) ASCENT_2023 = SubaruPlatformConfig( "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, ) -LKAS_ANGLE = {CAR.FORESTER_2022, CAR.OUTBACK_2023, CAR.ASCENT_2023} -GLOBAL_GEN2 = {CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023, CAR.ASCENT_2023} -PREGLOBAL_CARS = {CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018} -HYBRID_CARS = {CAR.CROSSTREK_HYBRID, CAR.FORESTER_HYBRID} - -# Cars that temporarily fault when steering angle rate is greater than some threshold. -# Appears to be all torque-based cars produced around 2019 - present -STEER_RATE_LIMITED = GLOBAL_GEN2 | {CAR.IMPREZA_2020, CAR.FORESTER} - SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ @@ -235,7 +249,7 @@ FW_QUERY_CONFIG = FwQueryConfig( ], # We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists non_essential_ecus={ - Ecu.eps: list(GLOBAL_GEN2), + Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)), } ) diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py index 95a248a614..f217c4692d 100644 --- a/selfdrive/car/tesla/carcontroller.py +++ b/selfdrive/car/tesla/carcontroller.py @@ -1,11 +1,12 @@ from openpilot.common.numpy_fast import clip from opendbc.can.packer import CANPacker from openpilot.selfdrive.car import apply_std_steer_angle_limits +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.tesla.teslacan import TeslaCAN from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, CarControllerParams -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.frame = 0 diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 9c14c0d252..811416ccef 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -82,8 +82,8 @@ routes = [ CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs - CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH), - CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs + CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORD), # hybrid + CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORD), # hybrid, 2021 with new style HUD msgs CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G), CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY), CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL), diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index 1a745b4447..a8bae29391 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -263,13 +263,13 @@ 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: 6.5, 2: 7.4} + total_ref_time = {1: 8.3, 2: 9.2} brand_ref_times = { 1: { - 'gm': 0.5, + 'gm': 1.0, 'body': 0.1, 'chrysler': 0.3, - 'ford': 0.2, + 'ford': 1.5, 'honda': 0.55, 'hyundai': 1.05, 'mazda': 0.1, @@ -280,7 +280,7 @@ class TestFwFingerprintTiming(unittest.TestCase): 'volkswagen': 0.65, }, 2: { - 'ford': 0.3, + 'ford': 1.6, 'hyundai': 1.85, } } diff --git a/selfdrive/car/tests/test_models_segs.txt b/selfdrive/car/tests/test_models_segs.txt index ca089bbdde..c983fb08e7 100644 --- a/selfdrive/car/tests/test_models_segs.txt +++ b/selfdrive/car/tests/test_models_segs.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cadad21e1230729c70cffb4c46dd0a5dda4eec1a262b1bcd9f1b6b98265c20b5 -size 125104 +oid sha256:0810a361ec5b5f5f9a2ee73b89ffb2df62ef40e8feff7e97ecb62f80fa53f6f5 +size 124950 diff --git a/selfdrive/car/torque_data/params.toml b/selfdrive/car/torque_data/params.toml index 568646c84c..142332b220 100644 --- a/selfdrive/car/torque_data/params.toml +++ b/selfdrive/car/torque_data/params.toml @@ -11,8 +11,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "CHRYSLER PACIFICA HYBRID 2018" = [2.08887, 1.2943025830995154, 0.114818] "CHRYSLER PACIFICA HYBRID 2019" = [1.90120, 1.1958788168371808, 0.131520] "GENESIS G70 2018" = [3.8520195946707947, 2.354697063349854, 0.06830285485626221] -"HONDA ACCORD 2018" = [1.7135052593468778, 0.3461280068322071, 0.21579936052863807] -"HONDA ACCORD HYBRID 2018" = [1.6651615004829625, 0.30322180951193245, 0.2083000440586149] +"HONDA ACCORD 2018" = [1.6893333799149202, 0.3246749081720698, 0.2120497022936265] "HONDA CIVIC (BOSCH) 2019" = [1.691708637466905, 0.40132900729454185, 0.25460295304024094] "HONDA CIVIC 2016" = [1.6528895627785531, 0.4018518740819229, 0.25458812851328544] "HONDA CR-V 2016" = [0.7667141440182675, 0.5927571534745969, 0.40909087636157127] diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 343b1d3031..d960114f1b 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -2,6 +2,7 @@ from cereal import car from openpilot.common.numpy_fast import clip, interp from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, \ create_gas_interceptor_command, make_can_msg +from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.toyota import toyotacan from openpilot.selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \ MIN_ACC_SPEED, PEDAL_TRANSITION, CarControllerParams, ToyotaFlags, \ @@ -25,7 +26,7 @@ MAX_LTA_ANGLE = 94.9461 # deg MAX_LTA_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.params = CarControllerParams(self.CP) diff --git a/selfdrive/car/toyota/fingerprints.py b/selfdrive/car/toyota/fingerprints.py index 12a1d46aaf..6b48408a10 100644 --- a/selfdrive/car/toyota/fingerprints.py +++ b/selfdrive/car/toyota/fingerprints.py @@ -573,6 +573,7 @@ FW_VERSIONS = { b'\x018821F6201400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F12010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', @@ -843,6 +844,7 @@ FW_VERSIONS = { b'8965B47023\x00\x00\x00\x00\x00\x00', b'8965B47050\x00\x00\x00\x00\x00\x00', b'8965B47060\x00\x00\x00\x00\x00\x00', + b'8965B47070\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'F152647290\x00\x00\x00\x00\x00\x00', @@ -1024,6 +1026,7 @@ FW_VERSIONS = { b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', + b'\x02896634A13201\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 0cee2c7fce..f99e87d5d3 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -4,6 +4,7 @@ from openpilot.common.numpy_fast import clip from openpilot.common.conversions import Conversions as CV 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 @@ -11,7 +12,7 @@ VisualAlert = car.CarControl.HUDControl.VisualAlert LongCtrlState = car.CarControl.Actuators.LongControlState -class CarController: +class CarController(CarControllerBase): def __init__(self, dbc_name, CP, VM): self.CP = CP self.CCP = CarControllerParams(CP) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index f08c5d550a..3e4a742224 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -112,11 +112,11 @@ class CANBUS: class VolkswagenFlags(IntFlag): STOCK_HCA_PRESENT = 1 -@dataclass(frozen=False) +@dataclass class VolkswagenMQBPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) -@dataclass(frozen=False) +@dataclass class VolkswagenPQPlatformConfig(PlatformConfig): dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) @@ -393,6 +393,7 @@ FW_QUERY_CONFIG = FwQueryConfig( obd_multiplexing=obd_multiplexing, ), ]], + extra_ecus=[(Ecu.fwdCamera, 0x74f, None)], ) CAR_INFO = CAR.create_carinfo_map() diff --git a/selfdrive/controls/lib/lateral_mpc_lib/SConscript b/selfdrive/controls/lib/lateral_mpc_lib/SConscript index 49666abe69..b6603e69fc 100644 --- a/selfdrive/controls/lib/lateral_mpc_lib/SConscript +++ b/selfdrive/controls/lib/lateral_mpc_lib/SConscript @@ -1,4 +1,4 @@ -Import('env', 'envCython', 'arch') +Import('env', 'envCython', 'arch', 'messaging_python', 'common_python', 'opendbc_python') gen = "c_generated_code" @@ -60,6 +60,7 @@ lenv.Clean(generated_files, Dir(gen)) generated_lat = lenv.Command(generated_files, source_list, f"cd {Dir('.').abspath} && python3 lat_mpc.py") +lenv.Depends(generated_lat, [messaging_python, common_python, opendbc_python]) lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES") diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript index 79afa1d918..c00d5cb5a7 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript @@ -1,4 +1,4 @@ -Import('env', 'envCython', 'arch', 'messaging_python', 'common_python') +Import('env', 'envCython', 'arch', 'messaging_python', 'common_python', 'opendbc_python') gen = "c_generated_code" @@ -66,7 +66,7 @@ lenv.Clean(generated_files, Dir(gen)) generated_long = lenv.Command(generated_files, source_list, f"cd {Dir('.').abspath} && python3 long_mpc.py") -lenv.Depends(generated_long, [messaging_python, common_python]) +lenv.Depends(generated_long, [messaging_python, common_python, opendbc_python]) lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES") lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES") diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index e32ed78a3e..26999cd684 100644 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -310,7 +310,7 @@ void Localizer::input_fake_gps_observations(double current_time) { void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) { // ignore the message if the fix is invalid bool gps_invalid_flag = (log.getFlags() % 2 == 0); - bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY); + bool gps_unreasonable = (Vector2d(log.getHorizontalAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY); bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0)); bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK)); bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK); @@ -331,7 +331,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; - float ecef_pos_std = std::sqrt(this->gps_variance_factor * std::pow(log.getAccuracy(), 2) + this->gps_vertical_variance_factor * std::pow(log.getVerticalAccuracy(), 2)); + float ecef_pos_std = std::sqrt(this->gps_variance_factor * std::pow(log.getHorizontalAccuracy(), 2) + this->gps_vertical_variance_factor * std::pow(log.getVerticalAccuracy(), 2)); MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(this->gps_std_factor * ecef_pos_std, 2)).asDiagonal(); MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(this->gps_std_factor * log.getSpeedAccuracy(), 2)).asDiagonal(); @@ -691,10 +691,9 @@ int Localizer::locationd_thread() { this->configure_gnss_source(source); const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", - "carState", "carParams", "accelerometer", "gyroscope"}; + "carState", "accelerometer", "gyroscope"}; - // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); + SubMaster sm(service_list, {}, nullptr, {gps_location_socket}); PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; @@ -718,8 +717,7 @@ int Localizer::locationd_thread() { filterInitialized = sm.allAliveAndValid(); } - // 100Hz publish for notcars, 20Hz for cars - const char* trigger_msg = sm["carParams"].getCarParams().getNotCar() ? "accelerometer" : "cameraOdometry"; + const char* trigger_msg = "cameraOdometry"; if (sm.updated(trigger_msg)) { bool inputsOK = sm.allValid() && this->are_inputs_ok(); bool gpsOK = this->is_gps_ok(); diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 4fb3e3c4de..5119be0a8c 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -512,7 +512,7 @@ CONFIGS = [ proc_name="locationd", pubs=[ "cameraOdometry", "accelerometer", "gyroscope", "gpsLocationExternal", - "liveCalibration", "carState", "carParams", "gpsLocation" + "liveCalibration", "carState", "gpsLocation" ], subs=["liveLocationKalman"], ignore=["logMonoTime"], diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index fc38f87310..13f776fbad 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d0cdea7eb15f3cac8a921f7ace3eaa6baebb4fd5 +b9d29ac9402cfc04bf3e48867415efa70c144029 diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index de8a4420b3..8f8c93ecff 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -424,4 +424,4 @@ class TestOnroad(unittest.TestCase): if __name__ == "__main__": - pytest.main() + unittest.main() diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 4f2e4f48cb..08f4a0f9c0 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -20,7 +20,7 @@ #include "selfdrive/ui/qt/widgets/input.h" const std::string USER_AGENT = "AGNOSSetup-"; -const QString TEST_URL = "https://openpilot.comma.ai"; +const QString OPENPILOT_URL = "https://openpilot.comma.ai"; bool is_elf(char *fname) { FILE *fp = fopen(fname, "rb"); @@ -201,20 +201,7 @@ QWidget * Setup::network_setup() { QPushButton *cont = new QPushButton(); cont->setObjectName("navBtn"); cont->setProperty("primary", true); - QObject::connect(cont, &QPushButton::clicked, [=]() { - auto w = currentWidget(); - QTimer::singleShot(0, [=]() { - setCurrentWidget(downloading_widget); - }); - QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); - if (!url.isEmpty()) { - QTimer::singleShot(1000, this, [=]() { - download(url); - }); - } else { - setCurrentWidget(w); - } - }); + QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); blayout->addWidget(cont); // setup timer for testing internet connection @@ -229,11 +216,11 @@ QWidget * Setup::network_setup() { } repaint(); }); - request->sendRequest(TEST_URL); + request->sendRequest(OPENPILOT_URL); QTimer *timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, [=]() { if (!request->active() && cont->isVisible()) { - request->sendRequest(TEST_URL); + request->sendRequest(OPENPILOT_URL); } }); timer->start(1000); @@ -241,6 +228,106 @@ QWidget * Setup::network_setup() { return widget; } +QWidget * radio_button(QString title, QButtonGroup *group) { + QPushButton *btn = new QPushButton(title); + btn->setCheckable(true); + group->addButton(btn); + btn->setStyleSheet(R"( + QPushButton { + height: 230; + padding-left: 100px; + padding-right: 100px; + text-align: left; + font-size: 80px; + font-weight: 400; + border-radius: 10px; + background-color: #4F4F4F; + } + QPushButton:checked { + background-color: #465BEA; + } + )"); + + // checkmark icon + QPixmap pix(":/img_circled_check.svg"); + btn->setIcon(pix); + btn->setIconSize(QSize(0, 0)); + btn->setLayoutDirection(Qt::RightToLeft); + QObject::connect(btn, &QPushButton::toggled, [=](bool checked) { + btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0)); + }); + return btn; +} + +QWidget * Setup::software_selection() { + QWidget *widget = new QWidget(); + QVBoxLayout *main_layout = new QVBoxLayout(widget); + main_layout->setContentsMargins(55, 50, 55, 50); + main_layout->setSpacing(0); + + // title + QLabel *title = new QLabel(tr("Choose Software to Install")); + title->setStyleSheet("font-size: 90px; font-weight: 500;"); + main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); + + main_layout->addSpacing(50); + + // openpilot + custom radio buttons + QButtonGroup *group = new QButtonGroup(widget); + group->setExclusive(true); + + QWidget *openpilot = radio_button(tr("openpilot"), group); + main_layout->addWidget(openpilot); + + main_layout->addSpacing(30); + + QWidget *custom = radio_button(tr("Custom Software"), group); + main_layout->addWidget(custom); + + main_layout->addStretch(); + + // back + continue buttons + QHBoxLayout *blayout = new QHBoxLayout; + main_layout->addLayout(blayout); + blayout->setSpacing(50); + + QPushButton *back = new QPushButton(tr("Back")); + back->setObjectName("navBtn"); + QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); + blayout->addWidget(back); + + QPushButton *cont = new QPushButton(tr("Continue")); + cont->setObjectName("navBtn"); + cont->setEnabled(false); + cont->setProperty("primary", true); + blayout->addWidget(cont); + + QObject::connect(cont, &QPushButton::clicked, [=]() { + auto w = currentWidget(); + QTimer::singleShot(0, [=]() { + setCurrentWidget(downloading_widget); + }); + QString url = OPENPILOT_URL; + if (group->checkedButton() != openpilot) { + url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); + } + if (!url.isEmpty()) { + QTimer::singleShot(1000, this, [=]() { + download(url); + }); + } else { + setCurrentWidget(w); + } + }); + + connect(group, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { + btn->setChecked(true); + cont->setEnabled(true); + }); + + return widget; +} + QWidget * Setup::downloading() { QWidget *widget = new QWidget(); QVBoxLayout *main_layout = new QVBoxLayout(widget); @@ -326,6 +413,7 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) { addWidget(getting_started()); addWidget(network_setup()); + addWidget(software_selection()); downloading_widget = downloading(); addWidget(downloading_widget); diff --git a/selfdrive/ui/qt/setup/setup.h b/selfdrive/ui/qt/setup/setup.h index 8c33acc380..ee5cc85483 100644 --- a/selfdrive/ui/qt/setup/setup.h +++ b/selfdrive/ui/qt/setup/setup.h @@ -17,6 +17,7 @@ private: QWidget *low_voltage(); QWidget *getting_started(); QWidget *network_setup(); + QWidget *software_selection(); QWidget *downloading(); QWidget *download_failed(QLabel *url, QLabel *body); diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index eb6a580c01..7a431b8bf8 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -762,6 +762,18 @@ This may take up to a minute. Select a language اختر لغة + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 6d814b9625..ae2bdf33cb 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -744,6 +744,18 @@ This may take up to a minute. Select a language Sprache wählen + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index cd96c466e9..ba496c1b89 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -746,6 +746,18 @@ Cela peut prendre jusqu'à une minute. Select a language Choisir une langue + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index d07c7d525a..54ff0fa4ae 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -740,6 +740,18 @@ This may take up to a minute. Select a language 言語を選択 + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index a903978329..3f98db2f9c 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -742,6 +742,18 @@ This may take up to a minute. Select a language 언어를 선택하세요 + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index fce5a8a8ff..c4789ed42e 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -638,7 +638,7 @@ Isso pode levar até um minuto. System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização. + Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização. @@ -746,6 +746,18 @@ Isso pode levar até um minuto. Select a language Selecione o Idioma + + Choose Software to Install + Escolha o Software a ser Instalado + + + openpilot + openpilot + + + Custom Software + Software Customizado + SetupWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index f4d097b2a2..c1f0039a3c 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -742,6 +742,18 @@ This may take up to a minute. Select a language เลือกภาษา + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index ec6f71f5b0..d19c9ff036 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -740,6 +740,18 @@ This may take up to a minute. Select a language Dil seçin + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 91f55c6e94..a27dc18bd2 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -742,6 +742,18 @@ This may take up to a minute. Select a language 选择语言 + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index e8c8854d0e..1e7f1a614b 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -742,6 +742,18 @@ This may take up to a minute. Select a language 選擇語言 + + Choose Software to Install + + + + openpilot + openpilot + + + Custom Software + + SetupWidget diff --git a/system/hardware/tici/tests/test_hardware.py b/system/hardware/tici/tests/test_hardware.py index 0c436595ee..6c41c383a0 100755 --- a/system/hardware/tici/tests/test_hardware.py +++ b/system/hardware/tici/tests/test_hardware.py @@ -25,4 +25,4 @@ class TestHardware(unittest.TestCase): if __name__ == "__main__": - pytest.main() + unittest.main() diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index 352fcdf18a..180ec155e4 100755 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 from collections import defaultdict, deque -import sys import pytest import unittest import time @@ -132,4 +131,4 @@ class TestPowerDraw(unittest.TestCase): if __name__ == "__main__": - pytest.main(sys.argv) + unittest.main() diff --git a/system/ubloxd/tests/print_gps_stats.py b/system/ubloxd/tests/print_gps_stats.py index cfddb7f8b8..edf94c060a 100755 --- a/system/ubloxd/tests/print_gps_stats.py +++ b/system/ubloxd/tests/print_gps_stats.py @@ -13,7 +13,7 @@ if __name__ == "__main__": cnos = [] for m in ug.measurementReport.measurements: cnos.append(m.cno) - print("Sats: %d Accuracy: %.2f m cnos" % (ug.measurementReport.numMeas, gle.accuracy), sorted(cnos)) + print("Sats: %d Accuracy: %.2f m cnos" % (ug.measurementReport.numMeas, gle.horizontalAccuracy), sorted(cnos)) except Exception: pass sm.update() diff --git a/system/ubloxd/ublox_msg.cc b/system/ubloxd/ublox_msg.cc index 7b4505dc81..eb1a1e4b19 100644 --- a/system/ubloxd/ublox_msg.cc +++ b/system/ubloxd/ublox_msg.cc @@ -132,7 +132,7 @@ kj::Array UbloxMsgParser::gen_nav_pvt(ubx_t::nav_pvt_t *msg) { gpsLoc.setAltitude(msg->height() * 1e-03); gpsLoc.setSpeed(msg->g_speed() * 1e-03); gpsLoc.setBearingDeg(msg->head_mot() * 1e-5); - gpsLoc.setAccuracy(msg->h_acc() * 1e-03); + gpsLoc.setHorizontalAccuracy(msg->h_acc() * 1e-03); std::tm timeinfo = std::tm(); timeinfo.tm_year = msg->year() - 1900; timeinfo.tm_mon = msg->month() - 1; diff --git a/tools/lib/tests/test_comma_car_segments.py b/tools/lib/tests/test_comma_car_segments.py index 50d4200b4f..b293251583 100644 --- a/tools/lib/tests/test_comma_car_segments.py +++ b/tools/lib/tests/test_comma_car_segments.py @@ -1,3 +1,4 @@ +import pytest import unittest import requests @@ -6,6 +7,7 @@ from openpilot.tools.lib.logreader import LogReader from openpilot.tools.lib.route import SegmentRange +@pytest.mark.skip(reason="huggingface is flaky, run this test manually to check for issues") class TestCommaCarSegments(unittest.TestCase): def test_database(self): database = get_comma_car_segments_database() diff --git a/tools/plotjuggler/layouts/gps_vs_llk.xml b/tools/plotjuggler/layouts/gps_vs_llk.xml index 44980712ed..69b8f20058 100644 --- a/tools/plotjuggler/layouts/gps_vs_llk.xml +++ b/tools/plotjuggler/layouts/gps_vs_llk.xml @@ -32,7 +32,7 @@ - + diff --git a/tools/plotjuggler/layouts/ublox-debug.xml b/tools/plotjuggler/layouts/ublox-debug.xml index d595a9ecc7..ea4dd35535 100644 --- a/tools/plotjuggler/layouts/ublox-debug.xml +++ b/tools/plotjuggler/layouts/ublox-debug.xml @@ -8,7 +8,7 @@ - + diff --git a/tools/sim/lib/simulated_sensors.py b/tools/sim/lib/simulated_sensors.py index df6a0aeeff..e78f984736 100644 --- a/tools/sim/lib/simulated_sensors.py +++ b/tools/sim/lib/simulated_sensors.py @@ -55,7 +55,7 @@ class SimulatedSensors: dat.gpsLocationExternal = { "unixTimestampMillis": int(time.time() * 1000), "flags": 1, # valid fix - "accuracy": 1.0, + "horizontalAccuracy": 1.0, "verticalAccuracy": 1.0, "speedAccuracy": 0.1, "bearingAccuracyDeg": 0.1,