Subaru: move to flags within PlatformConfig (#31584)

* flags

* update ref

* use the flags directly

* use post_init (don't freeze)

* we can maintain frozen with custom class

* not preglobal

* move to common

* cleanup
pull/31635/head
Justin Newberry 1 year ago committed by GitHub
parent 85eb221e4c
commit 3a6c3315ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 11
      common/utils.py
  2. 22
      selfdrive/car/__init__.py
  3. 2
      selfdrive/car/chrysler/values.py
  4. 2
      selfdrive/car/ford/values.py
  5. 2
      selfdrive/car/gm/values.py
  6. 3
      selfdrive/car/interfaces.py
  7. 2
      selfdrive/car/nissan/values.py
  8. 13
      selfdrive/car/subaru/carcontroller.py
  9. 54
      selfdrive/car/subaru/carstate.py
  10. 23
      selfdrive/car/subaru/interface.py
  11. 42
      selfdrive/car/subaru/values.py
  12. 4
      selfdrive/car/volkswagen/values.py
  13. 2
      selfdrive/test/process_replay/ref_commit

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

@ -1,12 +1,14 @@
# functions common among cars # functions common among cars
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass, field, replace from dataclasses import dataclass, field
from enum import ReprEnum from enum import IntFlag, ReprEnum
from dataclasses import replace
import capnp import capnp
from cereal import car from cereal import car
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
from openpilot.common.utils import Freezable
from openpilot.selfdrive.car.docs_definitions import CarInfo from openpilot.selfdrive.car.docs_definitions import CarInfo
@ -256,11 +258,12 @@ class CarSpecs:
minEnableSpeed: float = field(default=-1.) minEnableSpeed: float = field(default=-1.)
@dataclass(frozen=True, order=True) @dataclass(order=True)
class PlatformConfig: class PlatformConfig(Freezable):
platform_str: str platform_str: str
car_info: CarInfos car_info: CarInfos
dbc_dict: DbcDict dbc_dict: DbcDict
flags: int = 0
specs: CarSpecs | None = None specs: CarSpecs | None = None
@ -270,6 +273,13 @@ class PlatformConfig:
def override(self, **kwargs): def override(self, **kwargs):
return replace(self, **kwargs) return replace(self, **kwargs)
def init(self):
pass
def __post_init__(self):
self.init()
self.freeze()
class Platforms(str, ReprEnum): class Platforms(str, ReprEnum):
config: PlatformConfig config: PlatformConfig
@ -287,3 +297,7 @@ class Platforms(str, ReprEnum):
@classmethod @classmethod
def create_carinfo_map(cls) -> dict[str, CarInfos]: def create_carinfo_map(cls) -> dict[str, CarInfos]:
return {p: p.config.car_info for p in cls} 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}

@ -19,7 +19,7 @@ class ChryslerCarInfo(CarInfo):
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca])) car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
@dataclass(frozen=True) @dataclass
class ChryslerPlatformConfig(PlatformConfig): class ChryslerPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', None))

@ -64,7 +64,7 @@ class FordCarInfo(CarInfo):
self.car_parts = CarParts([Device.threex, harness]) self.car_parts = CarParts([Device.threex, harness])
@dataclass(frozen=True) @dataclass
class FordPlatformConfig(PlatformConfig): class FordPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR))

@ -79,7 +79,7 @@ class GMCarInfo(CarInfo):
self.footnotes.append(Footnote.OBD_II) self.footnotes.append(Footnote.OBD_II)
@dataclass(frozen=True) @dataclass
class GMPlatformConfig(PlatformConfig): 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')) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))

@ -120,6 +120,7 @@ class CarInterfaceBase(ABC):
ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio ret.centerToFront = ret.wheelbase * candidate.config.specs.centerToFrontRatio
ret.minEnableSpeed = candidate.config.specs.minEnableSpeed ret.minEnableSpeed = candidate.config.specs.minEnableSpeed
ret.minSteerSpeed = candidate.config.specs.minSteerSpeed ret.minSteerSpeed = candidate.config.specs.minSteerSpeed
ret.flags |= int(candidate.config.flags)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs)
@ -135,7 +136,7 @@ class CarInterfaceBase(ABC):
@staticmethod @staticmethod
@abstractmethod @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): car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool):
raise NotImplementedError raise NotImplementedError

@ -31,7 +31,7 @@ class NissanCarSpecs(CarSpecs):
steerRatio: float = 17. steerRatio: float = 17.
@dataclass(frozen=True) @dataclass
class NissanPlaformConfig(PlatformConfig): class NissanPlaformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None))

@ -3,8 +3,7 @@ from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.subaru import subarucan 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, \ from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags
CanBus, CarControllerParams, SubaruFlags
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and # 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 # involves the total steering angle change rather than rate, but these limits work well for now
@ -43,12 +42,12 @@ class CarController(CarControllerBase):
if not CC.latActive: if not CC.latActive:
apply_steer = 0 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)) can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive))
else: else:
apply_steer_req = CC.latActive 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 # Steering rate fault prevention
self.steer_rate_counter, apply_steer_req = \ self.steer_rate_counter, apply_steer_req = \
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
@ -75,7 +74,7 @@ class CarController(CarControllerBase):
cruise_brake = CarControllerParams.BRAKE_MIN cruise_brake = CarControllerParams.BRAKE_MIN
# *** alerts and pcm cancel *** # *** alerts and pcm cancel ***
if self.CP.carFingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
if self.frame % 5 == 0: if self.frame % 5 == 0:
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep # 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
# disengage ACC when OP is disengaged # disengage ACC when OP is disengaged
@ -118,8 +117,8 @@ class CarController(CarControllerBase):
self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle)) self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle))
else: else:
if pcm_cancel_cmd: if pcm_cancel_cmd:
if self.CP.carFingerprint not in HYBRID_CARS: if not (self.CP.flags & SubaruFlags.HYBRID):
bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main 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)) 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: 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.common.conversions import Conversions as CV
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from opendbc.can.parser import CANParser 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 from openpilot.selfdrive.car import CanSignalRateCalculator
@ -19,17 +19,17 @@ class CarState(CarStateBase):
def update(self, cp, cp_cam, cp_body): def update(self, cp, cp_cam, cp_body):
ret = car.CarState.new_message() 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.gas = throttle_msg["Throttle_Pedal"] / 255.
ret.gasPressed = ret.gas > 1e-5 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 ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0
else: 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 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( ret.wheelSpeeds = self.get_wheel_speeds(
cp_wheels.vl["Wheel_Speeds"]["FL"], cp_wheels.vl["Wheel_Speeds"]["FL"],
cp_wheels.vl["Wheel_Speeds"]["FR"], 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.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) 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"]) can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] 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 # 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.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] 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 ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp cp_cruise = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
if self.car_fingerprint in HYBRID_CARS: if self.CP.flags & SubaruFlags.HYBRID:
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0 ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0 ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
else: else:
@ -73,8 +73,8 @@ class CarState(CarStateBase):
ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0 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 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 \ if (self.CP.flags & SubaruFlags.PREGLOBAL and cp.vl["Dash_State2"]["UNITS"] == 1) or \
(self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1): (not (self.CP.flags & SubaruFlags.PREGLOBAL) and cp.vl["Dashlights"]["UNITS"] == 1):
ret.cruiseState.speed *= CV.MPH_TO_KPH ret.cruiseState.speed *= CV.MPH_TO_KPH
ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1 ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1
@ -84,8 +84,8 @@ class CarState(CarStateBase):
cp.vl["BodyInfo"]["DOOR_OPEN_FL"]]) cp.vl["BodyInfo"]["DOOR_OPEN_FL"]])
ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1 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 cp_es_distance = cp_body if self.CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID) else cp_cam
if self.car_fingerprint in PREGLOBAL_CARS: if self.CP.flags & SubaruFlags.PREGLOBAL:
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"] self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"] self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
else: else:
@ -96,12 +96,12 @@ class CarState(CarStateBase):
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2) (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"]) 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"]) 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 # 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 # 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 \ ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0) (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.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"]) 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_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"]) self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
@ -125,7 +125,7 @@ class CarState(CarStateBase):
("Brake_Status", 50), ("Brake_Status", 50),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages.append(("CruiseControl", 20)) messages.append(("CruiseControl", 20))
return messages return messages
@ -136,7 +136,7 @@ class CarState(CarStateBase):
("ES_Brake", 20), ("ES_Brake", 20),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages += [ messages += [
("ES_Distance", 20), ("ES_Distance", 20),
("ES_Status", 20) ("ES_Status", 20)
@ -164,7 +164,7 @@ class CarState(CarStateBase):
("Brake_Pedal", 50), ("Brake_Pedal", 50),
] ]
if CP.carFingerprint not in HYBRID_CARS: if not (CP.flags & SubaruFlags.HYBRID):
messages += [ messages += [
("Throttle", 100), ("Throttle", 100),
("Transmission", 100) ("Transmission", 100)
@ -173,8 +173,8 @@ class CarState(CarStateBase):
if CP.enableBsm: if CP.enableBsm:
messages.append(("BSD_RCTA", 17)) messages.append(("BSD_RCTA", 17))
if CP.carFingerprint not in PREGLOBAL_CARS: if not (CP.flags & SubaruFlags.PREGLOBAL):
if CP.carFingerprint not in GLOBAL_GEN2: if not (CP.flags & SubaruFlags.GLOBAL_GEN2):
messages += CarState.get_common_global_body_messages(CP) messages += CarState.get_common_global_body_messages(CP)
else: else:
messages += CarState.get_common_preglobal_body_messages() messages += CarState.get_common_preglobal_body_messages()
@ -183,7 +183,7 @@ class CarState(CarStateBase):
@staticmethod @staticmethod
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
if CP.carFingerprint in PREGLOBAL_CARS: if CP.flags & SubaruFlags.PREGLOBAL:
messages = [ messages = [
("ES_DashStatus", 20), ("ES_DashStatus", 20),
("ES_Distance", 20), ("ES_Distance", 20),
@ -194,7 +194,7 @@ class CarState(CarStateBase):
("ES_LKAS_State", 10), ("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) messages += CarState.get_common_global_es_messages(CP)
if CP.flags & SubaruFlags.SEND_INFOTAINMENT: if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
@ -206,11 +206,11 @@ class CarState(CarStateBase):
def get_body_can_parser(CP): def get_body_can_parser(CP):
messages = [] 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_body_messages(CP)
messages += CarState.get_common_global_es_messages(CP) messages += CarState.get_common_global_es_messages(CP)
if CP.carFingerprint in HYBRID_CARS: if CP.flags & SubaruFlags.HYBRID:
messages += [ messages += [
("Throttle_Hybrid", 40), ("Throttle_Hybrid", 40),
("Transmission", 100) ("Transmission", 100)

@ -1,42 +1,43 @@
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from openpilot.selfdrive.car import get_safety_config 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.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase 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): class CarInterface(CarInterfaceBase):
@staticmethod @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.carName = "subaru"
ret.radarUnavailable = True ret.radarUnavailable = True
# for HYBRID CARS to be upstreamed, we need: # for HYBRID CARS to be upstreamed, we need:
# - replacement for ES_Distance so we can cancel the cruise control # - replacement for ES_Distance so we can cancel the cruise control
# - to find the Cruise_Activated bit from the car # - to find the Cruise_Activated bit from the car
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) # - 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 ret.autoResumeSng = False
# Detect infotainment message sent from the camera # 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 ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
if candidate in PREGLOBAL_CARS: if platform_flags & SubaruFlags.PREGLOBAL:
ret.enableBsm = 0x25c in fingerprint[0] ret.enableBsm = 0x25c in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)]
else: else:
ret.enableBsm = 0x228 in fingerprint[0] ret.enableBsm = 0x228 in fingerprint[0]
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)] 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.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
if candidate in LKAS_ANGLE: if platform_flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
else: else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@ -84,10 +85,12 @@ class CarInterface(CarInterfaceBase):
else: else:
raise ValueError(f"unknown car: {candidate}") 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 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 ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
if ret.openpilotLongitudinalControl: if ret.openpilotLongitudinalControl:

@ -19,7 +19,7 @@ class CarControllerParams:
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
self.STEER_DRIVER_FACTOR = 1 # from dbc 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_MAX = 1000
self.STEER_DELTA_UP = 40 self.STEER_DELTA_UP = 40
self.STEER_DELTA_DOWN = 40 self.STEER_DELTA_DOWN = 40
@ -55,6 +55,14 @@ class CarControllerParams:
class SubaruFlags(IntFlag): class SubaruFlags(IntFlag):
SEND_INFOTAINMENT = 1 SEND_INFOTAINMENT = 1
DISABLE_EYESIGHT = 2 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 GLOBAL_ES_ADDR = 0x787
@ -89,10 +97,14 @@ class SubaruCarInfo(CarInfo):
self.footnotes.append(Footnote.EXP_LONG) self.footnotes.append(Footnote.EXP_LONG)
@dataclass(frozen=True) @dataclass
class SubaruPlatformConfig(PlatformConfig): class SubaruPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None)) 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): class CAR(Platforms):
# Global platform # Global platform
@ -105,11 +117,13 @@ class CAR(Platforms):
"SUBARU OUTBACK 6TH GEN", "SUBARU OUTBACK 6TH GEN",
SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
) )
LEGACY = SubaruPlatformConfig( LEGACY = SubaruPlatformConfig(
"SUBARU LEGACY 7TH GEN", "SUBARU LEGACY 7TH GEN",
SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
specs=OUTBACK.specs, specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.STEER_RATE_LIMITED,
) )
IMPREZA = SubaruPlatformConfig( IMPREZA = SubaruPlatformConfig(
"SUBARU IMPREZA LIMITED 2019", "SUBARU IMPREZA LIMITED 2019",
@ -128,24 +142,26 @@ class CAR(Platforms):
SubaruCarInfo("Subaru XV 2020-21"), SubaruCarInfo("Subaru XV 2020-21"),
], ],
specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
) )
# TODO: is there an XV and Impreza too? # TODO: is there an XV and Impreza too?
CROSSTREK_HYBRID = SubaruPlatformConfig( CROSSTREK_HYBRID = SubaruPlatformConfig(
"SUBARU CROSSTREK HYBRID 2020", "SUBARU CROSSTREK HYBRID 2020",
SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])), 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), specs=CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.HYBRID,
) )
FORESTER = SubaruPlatformConfig( FORESTER = SubaruPlatformConfig(
"SUBARU FORESTER 2019", "SUBARU FORESTER 2019",
SubaruCarInfo("Subaru Forester 2019-21", "All"), SubaruCarInfo("Subaru Forester 2019-21", "All"),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
flags=SubaruFlags.STEER_RATE_LIMITED,
) )
FORESTER_HYBRID = SubaruPlatformConfig( FORESTER_HYBRID = SubaruPlatformConfig(
"SUBARU FORESTER HYBRID 2020", "SUBARU FORESTER HYBRID 2020",
SubaruCarInfo("Subaru Forester Hybrid 2020"), SubaruCarInfo("Subaru Forester Hybrid 2020"),
dbc_dict('subaru_global_2020_hybrid_generated', None),
specs=FORESTER.specs, specs=FORESTER.specs,
flags=SubaruFlags.HYBRID,
) )
# Pre-global # Pre-global
FORESTER_PREGLOBAL = SubaruPlatformConfig( FORESTER_PREGLOBAL = SubaruPlatformConfig(
@ -153,52 +169,50 @@ class CAR(Platforms):
SubaruCarInfo("Subaru Forester 2017-18"), SubaruCarInfo("Subaru Forester 2017-18"),
dbc_dict('subaru_forester_2017_generated', None), dbc_dict('subaru_forester_2017_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
flags=SubaruFlags.PREGLOBAL,
) )
LEGACY_PREGLOBAL = SubaruPlatformConfig( LEGACY_PREGLOBAL = SubaruPlatformConfig(
"SUBARU LEGACY 2015 - 2018", "SUBARU LEGACY 2015 - 2018",
SubaruCarInfo("Subaru Legacy 2015-18"), SubaruCarInfo("Subaru Legacy 2015-18"),
dbc_dict('subaru_outback_2015_generated', None), dbc_dict('subaru_outback_2015_generated', None),
specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5), specs=CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
flags=SubaruFlags.PREGLOBAL,
) )
OUTBACK_PREGLOBAL = SubaruPlatformConfig( OUTBACK_PREGLOBAL = SubaruPlatformConfig(
"SUBARU OUTBACK 2015 - 2017", "SUBARU OUTBACK 2015 - 2017",
SubaruCarInfo("Subaru Outback 2015-17"), SubaruCarInfo("Subaru Outback 2015-17"),
dbc_dict('subaru_outback_2015_generated', None), dbc_dict('subaru_outback_2015_generated', None),
specs=FORESTER_PREGLOBAL.specs, specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
) )
OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig( OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig(
"SUBARU OUTBACK 2018 - 2019", "SUBARU OUTBACK 2018 - 2019",
SubaruCarInfo("Subaru Outback 2018-19"), SubaruCarInfo("Subaru Outback 2018-19"),
dbc_dict('subaru_outback_2019_generated', None), dbc_dict('subaru_outback_2019_generated', None),
specs=FORESTER_PREGLOBAL.specs, specs=FORESTER_PREGLOBAL.specs,
flags=SubaruFlags.PREGLOBAL,
) )
# Angle LKAS # Angle LKAS
FORESTER_2022 = SubaruPlatformConfig( FORESTER_2022 = SubaruPlatformConfig(
"SUBARU FORESTER 2022", "SUBARU FORESTER 2022",
SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])), SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
specs=FORESTER.specs, specs=FORESTER.specs,
flags=SubaruFlags.LKAS_ANGLE,
) )
OUTBACK_2023 = SubaruPlatformConfig( OUTBACK_2023 = SubaruPlatformConfig(
"SUBARU OUTBACK 7TH GEN", "SUBARU OUTBACK 7TH GEN",
SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=OUTBACK.specs, specs=OUTBACK.specs,
flags=SubaruFlags.GLOBAL_GEN2 | SubaruFlags.LKAS_ANGLE,
) )
ASCENT_2023 = SubaruPlatformConfig( ASCENT_2023 = SubaruPlatformConfig(
"SUBARU ASCENT 2023", "SUBARU ASCENT 2023",
SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])), SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
specs=ASCENT.specs, 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]) + \ SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ 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 # 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={ non_essential_ecus={
Ecu.eps: list(GLOBAL_GEN2), Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)),
} }
) )

@ -112,11 +112,11 @@ class CANBUS:
class VolkswagenFlags(IntFlag): class VolkswagenFlags(IntFlag):
STOCK_HCA_PRESENT = 1 STOCK_HCA_PRESENT = 1
@dataclass(frozen=True) @dataclass
class VolkswagenMQBPlatformConfig(PlatformConfig): class VolkswagenMQBPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_mqb_2010', None))
@dataclass(frozen=True) @dataclass
class VolkswagenPQPlatformConfig(PlatformConfig): class VolkswagenPQPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None)) dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('vw_golf_mk4', None))

@ -1 +1 @@
dab93e072903e80f91029a03600430c43c722cb7 b9d29ac9402cfc04bf3e48867415efa70c144029

Loading…
Cancel
Save