Merge remote-tracking branch 'upstream/master' into toyota-pc

pull/31607/head
Shane Smiskol 1 year ago
commit b592eb777e
  1. 22
      .github/workflows/auto_pr_review.yaml
  2. 2
      .github/workflows/tools_tests.yaml
  3. 17
      SConstruct
  4. 2
      cereal
  5. 1
      common/params.cc
  6. 11
      common/utils.py
  7. 2
      docs/CONTRIBUTING.md
  8. 2
      opendbc
  9. 2
      panda
  10. 1
      pyproject.toml
  11. 2
      release/files_common
  12. 7
      selfdrive/SConscript
  13. 8
      selfdrive/car/__init__.py
  14. 21
      selfdrive/car/body/carcontroller.py
  15. 3
      selfdrive/car/chrysler/carcontroller.py
  16. 15
      selfdrive/car/chrysler/interface.py
  17. 131
      selfdrive/car/chrysler/values.py
  18. 3
      selfdrive/car/docs_definitions.py
  19. 3
      selfdrive/car/ford/carcontroller.py
  20. 1
      selfdrive/car/ford/tests/test_ford.py
  21. 50
      selfdrive/car/ford/values.py
  22. 3
      selfdrive/car/gm/carcontroller.py
  23. 15
      selfdrive/car/gm/values.py
  24. 18
      selfdrive/car/honda/carcontroller.py
  25. 12
      selfdrive/car/honda/carstate.py
  26. 85
      selfdrive/car/honda/fingerprints.py
  27. 63
      selfdrive/car/honda/hondacan.py
  28. 16
      selfdrive/car/honda/interface.py
  29. 23
      selfdrive/car/honda/values.py
  30. 3
      selfdrive/car/hyundai/carcontroller.py
  31. 12
      selfdrive/car/interfaces.py
  32. 3
      selfdrive/car/mazda/carcontroller.py
  33. 3
      selfdrive/car/nissan/carcontroller.py
  34. 14
      selfdrive/car/nissan/interface.py
  35. 68
      selfdrive/car/nissan/values.py
  36. 16
      selfdrive/car/subaru/carcontroller.py
  37. 54
      selfdrive/car/subaru/carstate.py
  38. 23
      selfdrive/car/subaru/interface.py
  39. 42
      selfdrive/car/subaru/values.py
  40. 3
      selfdrive/car/tesla/carcontroller.py
  41. 4
      selfdrive/car/tests/routes.py
  42. 8
      selfdrive/car/tests/test_fw_fingerprint.py
  43. 4
      selfdrive/car/tests/test_models_segs.txt
  44. 3
      selfdrive/car/torque_data/params.toml
  45. 3
      selfdrive/car/toyota/carcontroller.py
  46. 3
      selfdrive/car/toyota/fingerprints.py
  47. 3
      selfdrive/car/volkswagen/carcontroller.py
  48. 5
      selfdrive/car/volkswagen/values.py
  49. 3
      selfdrive/controls/lib/lateral_mpc_lib/SConscript
  50. 4
      selfdrive/controls/lib/longitudinal_mpc_lib/SConscript
  51. 12
      selfdrive/locationd/locationd.cc
  52. 2
      selfdrive/test/process_replay/process_replay.py
  53. 2
      selfdrive/test/process_replay/ref_commit
  54. 2
      selfdrive/test/test_onroad.py
  55. 122
      selfdrive/ui/qt/setup/setup.cc
  56. 1
      selfdrive/ui/qt/setup/setup.h
  57. 12
      selfdrive/ui/translations/main_ar.ts
  58. 12
      selfdrive/ui/translations/main_de.ts
  59. 12
      selfdrive/ui/translations/main_fr.ts
  60. 12
      selfdrive/ui/translations/main_ja.ts
  61. 12
      selfdrive/ui/translations/main_ko.ts
  62. 14
      selfdrive/ui/translations/main_pt-BR.ts
  63. 12
      selfdrive/ui/translations/main_th.ts
  64. 12
      selfdrive/ui/translations/main_tr.ts
  65. 12
      selfdrive/ui/translations/main_zh-CHS.ts
  66. 12
      selfdrive/ui/translations/main_zh-CHT.ts
  67. 2
      system/hardware/tici/tests/test_hardware.py
  68. 3
      system/hardware/tici/tests/test_power_draw.py
  69. 2
      system/ubloxd/tests/print_gps_stats.py
  70. 2
      system/ubloxd/ublox_msg.cc
  71. 2
      tools/lib/tests/test_comma_car_segments.py
  72. 2
      tools/plotjuggler/layouts/gps_vs_llk.xml
  73. 2
      tools/plotjuggler/layouts/ublox-debug.xml
  74. 2
      tools/sim/lib/simulated_sensors.py

@ -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: |
<!-- _(run_id **${{ github.run_id }}**)_ -->
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:

@ -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

@ -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'])

@ -1 +1 @@
Subproject commit a4255106b7255e00ae04162f7aa14aa3cae339c3
Subproject commit bfbb0cab83e3ad49d85ad1a34ee1241bf1ff782f

@ -207,7 +207,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UpdaterLastFetchTime", PERSISTENT},
{"Version", PERSISTENT},
{"VisionRadarToggle", PERSISTENT},
{"WheeledBody", PERSISTENT},
};
} // namespace

@ -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)

@ -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).

@ -1 +1 @@
Subproject commit 0ac21652f2e643e29aa471ad6b238bf74b22e356
Subproject commit 5f096db742b0c5dcd19976afdb07d5dd098f4b07

@ -1 +1 @@
Subproject commit 0c7d5f11d7187904022ea49b6a76b54d7b280345
Subproject commit ea156f7c628a371bea9a15a29f9068d5392534ba

@ -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"]

@ -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

@ -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'])

@ -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}

@ -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,16 +50,6 @@ 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)
speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr)

@ -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

@ -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:

@ -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]))
@dataclass
class ChryslerPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None))
class CAR(StrEnum):
@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()

@ -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):

@ -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

@ -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)
}

@ -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
],
)

@ -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.

@ -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

@ -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

@ -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

@ -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): [

@ -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)

@ -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

@ -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}

@ -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)

@ -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",

@ -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

@ -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

@ -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

@ -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),
}

@ -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:

@ -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)

@ -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:

@ -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)),
}
)

@ -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

@ -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),

@ -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,
}
}

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cadad21e1230729c70cffb4c46dd0a5dda4eec1a262b1bcd9f1b6b98265c20b5
size 125104
oid sha256:0810a361ec5b5f5f9a2ee73b89ffb2df62ef40e8feff7e97ecb62f80fa53f6f5
size 124950

@ -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]

@ -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)

@ -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',

@ -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)

@ -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()

@ -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")

@ -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")

@ -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<const char *> 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();

@ -512,7 +512,7 @@ CONFIGS = [
proc_name="locationd",
pubs=[
"cameraOdometry", "accelerometer", "gyroscope", "gpsLocationExternal",
"liveCalibration", "carState", "carParams", "gpsLocation"
"liveCalibration", "carState", "gpsLocation"
],
subs=["liveLocationKalman"],
ignore=["logMonoTime"],

@ -1 +1 @@
d0cdea7eb15f3cac8a921f7ace3eaa6baebb4fd5
b9d29ac9402cfc04bf3e48867415efa70c144029

@ -424,4 +424,4 @@ class TestOnroad(unittest.TestCase):
if __name__ == "__main__":
pytest.main()
unittest.main()

@ -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<QAbstractButton *>::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);

@ -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);

@ -762,6 +762,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation>اختر لغة</translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -744,6 +744,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation>Sprache wählen</translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -746,6 +746,18 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Select a language</source>
<translation>Choisir une langue</translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -740,6 +740,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation></translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation> </translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -638,7 +638,7 @@ Isso pode levar até um minuto.</translation>
</message>
<message>
<source>System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot.</source>
<translation type="unfinished">Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização.</translation>
<translation>Reinicialização do sistema acionada. Pressione confirmar para apagar todo o conteúdo e configurações. Pressione cancel para retomar a inicialização.</translation>
</message>
</context>
<context>
@ -746,6 +746,18 @@ Isso pode levar até um minuto.</translation>
<source>Select a language</source>
<translation>Selecione o Idioma</translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation>Escolha o Software a ser Instalado</translation>
</message>
<message>
<source>openpilot</source>
<translation>openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation>Software Customizado</translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation></translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -740,6 +740,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation>Dil seçin</translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation></translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -742,6 +742,18 @@ This may take up to a minute.</source>
<source>Select a language</source>
<translation></translation>
</message>
<message>
<source>Choose Software to Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot</source>
<translation type="unfinished">openpilot</translation>
</message>
<message>
<source>Custom Software</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SetupWidget</name>

@ -25,4 +25,4 @@ class TestHardware(unittest.TestCase):
if __name__ == "__main__":
pytest.main()
unittest.main()

@ -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()

@ -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()

@ -132,7 +132,7 @@ kj::Array<capnp::word> 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;

@ -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()

@ -32,7 +32,7 @@
<plot mode="TimeSeries" style="Lines" flip_y="false" flip_x="false">
<range bottom="-0.449385" right="196.811937" left="76.646983" top="7.160833"/>
<limitY/>
<curve name="/gpsLocationExternal/accuracy" color="#ff7f0e"/>
<curve name="/gpsLocationExternal/horizontalAccuracy" color="#ff7f0e"/>
<curve name="/gpsLocationExternal/verticalAccuracy" color="#f14cc1"/>
<curve name="/gpsLocationExternal/speedAccuracy" color="#9467bd"/>
</plot>

@ -8,7 +8,7 @@
<plot style="Lines" flip_x="false" flip_y="false" mode="TimeSeries">
<range right="134.825489" top="4402341.574525" bottom="-107369.555525" left="0.000000"/>
<limitY/>
<curve name="/gpsLocationExternal/accuracy" color="#1f77b4"/>
<curve name="/gpsLocationExternal/horizontalAccuracy" color="#1f77b4"/>
</plot>
</DockArea>
<DockArea name="...">

@ -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,

Loading…
Cancel
Save