openpilot v0.9.6 release
date: 2024-01-12T10:13:37 master commit: ba792d576a49a0899b88a753fa1c52956bedf9e6
This commit is contained in:
0
selfdrive/car/subaru/__init__.py
Normal file
0
selfdrive/car/subaru/__init__.py
Normal file
144
selfdrive/car/subaru/carcontroller.py
Normal file
144
selfdrive/car/subaru/carcontroller.py
Normal file
@@ -0,0 +1,144 @@
|
||||
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.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
|
||||
|
||||
# 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
|
||||
MAX_STEER_RATE = 25 # deg/s
|
||||
MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut
|
||||
|
||||
|
||||
class CarController:
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.apply_steer_last = 0
|
||||
self.frame = 0
|
||||
|
||||
self.cruise_button_prev = 0
|
||||
self.steer_rate_counter = 0
|
||||
|
||||
self.p = CarControllerParams(CP)
|
||||
self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
|
||||
can_sends = []
|
||||
|
||||
# *** steering ***
|
||||
if (self.frame % self.p.STEER_STEP) == 0:
|
||||
apply_steer = int(round(actuators.steer * self.p.STEER_MAX))
|
||||
|
||||
# limits due to driver torque
|
||||
|
||||
new_steer = int(round(apply_steer))
|
||||
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p)
|
||||
|
||||
if not CC.latActive:
|
||||
apply_steer = 0
|
||||
|
||||
if self.CP.carFingerprint in PREGLOBAL_CARS:
|
||||
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:
|
||||
# Steering rate fault prevention
|
||||
self.steer_rate_counter, apply_steer_req = \
|
||||
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
|
||||
self.steer_rate_counter, MAX_STEER_RATE_FRAMES)
|
||||
|
||||
can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, apply_steer_req))
|
||||
|
||||
self.apply_steer_last = apply_steer
|
||||
|
||||
# *** longitudinal ***
|
||||
|
||||
if CC.longActive:
|
||||
apply_throttle = int(round(interp(actuators.accel, CarControllerParams.THROTTLE_LOOKUP_BP, CarControllerParams.THROTTLE_LOOKUP_V)))
|
||||
apply_rpm = int(round(interp(actuators.accel, CarControllerParams.RPM_LOOKUP_BP, CarControllerParams.RPM_LOOKUP_V)))
|
||||
apply_brake = int(round(interp(actuators.accel, CarControllerParams.BRAKE_LOOKUP_BP, CarControllerParams.BRAKE_LOOKUP_V)))
|
||||
|
||||
# limit min and max values
|
||||
cruise_throttle = clip(apply_throttle, CarControllerParams.THROTTLE_MIN, CarControllerParams.THROTTLE_MAX)
|
||||
cruise_rpm = clip(apply_rpm, CarControllerParams.RPM_MIN, CarControllerParams.RPM_MAX)
|
||||
cruise_brake = clip(apply_brake, CarControllerParams.BRAKE_MIN, CarControllerParams.BRAKE_MAX)
|
||||
else:
|
||||
cruise_throttle = CarControllerParams.THROTTLE_INACTIVE
|
||||
cruise_rpm = CarControllerParams.RPM_MIN
|
||||
cruise_brake = CarControllerParams.BRAKE_MIN
|
||||
|
||||
# *** alerts and pcm cancel ***
|
||||
if self.CP.carFingerprint in PREGLOBAL_CARS:
|
||||
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
|
||||
if pcm_cancel_cmd:
|
||||
cruise_button = 1
|
||||
# turn main on if off and past start-up state
|
||||
elif not CS.out.cruiseState.available and CS.ready:
|
||||
cruise_button = 1
|
||||
else:
|
||||
cruise_button = CS.cruise_button
|
||||
|
||||
# unstick previous mocked button press
|
||||
if cruise_button == 1 and self.cruise_button_prev == 1:
|
||||
cruise_button = 0
|
||||
self.cruise_button_prev = cruise_button
|
||||
|
||||
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
|
||||
|
||||
else:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
|
||||
self.CP.openpilotLongitudinalControl, CC.longActive, hud_control.leadVisible))
|
||||
|
||||
can_sends.append(subarucan.create_es_lkas_state(self.packer, self.frame // 10, CS.es_lkas_state_msg, CC.enabled, hud_control.visualAlert,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
hud_control.leftLaneDepart, hud_control.rightLaneDepart))
|
||||
|
||||
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 5 == 0:
|
||||
can_sends.append(subarucan.create_es_status(self.packer, self.frame // 5, CS.es_status_msg,
|
||||
self.CP.openpilotLongitudinalControl, CC.longActive, cruise_rpm))
|
||||
|
||||
can_sends.append(subarucan.create_es_brake(self.packer, self.frame // 5, CS.es_brake_msg,
|
||||
self.CP.openpilotLongitudinalControl, CC.longActive, cruise_brake))
|
||||
|
||||
can_sends.append(subarucan.create_es_distance(self.packer, self.frame // 5, CS.es_distance_msg, 0, pcm_cancel_cmd,
|
||||
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
|
||||
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:
|
||||
# Tester present (keeps eyesight disabled)
|
||||
if self.frame % 100 == 0:
|
||||
can_sends.append([GLOBAL_ES_ADDR, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", CanBus.camera])
|
||||
|
||||
# Create all of the other eyesight messages to keep the rest of the car happy when eyesight is disabled
|
||||
if self.frame % 5 == 0:
|
||||
can_sends.append(subarucan.create_es_highbeamassist(self.packer))
|
||||
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(subarucan.create_es_static_1(self.packer))
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(subarucan.create_es_static_2(self.packer))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
220
selfdrive/car/subaru/carstate.py
Normal file
220
selfdrive/car/subaru/carstate.py
Normal file
@@ -0,0 +1,220 @@
|
||||
import copy
|
||||
from cereal import car
|
||||
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 import CanSignalRateCalculator
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
|
||||
self.shifter_values = can_define.dv["Transmission"]["Gear"]
|
||||
|
||||
self.angle_rate_calulator = CanSignalRateCalculator(50)
|
||||
|
||||
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"]
|
||||
ret.gas = throttle_msg["Throttle_Pedal"] / 255.
|
||||
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
if self.car_fingerprint in PREGLOBAL_CARS:
|
||||
ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0
|
||||
else:
|
||||
cp_brakes = cp_body if self.car_fingerprint in 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
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp_wheels.vl["Wheel_Speeds"]["FL"],
|
||||
cp_wheels.vl["Wheel_Speeds"]["FR"],
|
||||
cp_wheels.vl["Wheel_Speeds"]["RL"],
|
||||
cp_wheels.vl["Wheel_Speeds"]["RR"],
|
||||
)
|
||||
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = ret.vEgoRaw == 0
|
||||
|
||||
# continuous blinker signals for assisted lane change
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["Dashlights"]["LEFT_BLINKER"],
|
||||
cp.vl["Dashlights"]["RIGHT_BLINKER"])
|
||||
|
||||
if self.CP.enableBsm:
|
||||
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
|
||||
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:
|
||||
# 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
|
||||
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:
|
||||
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
|
||||
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
|
||||
else:
|
||||
ret.cruiseState.enabled = cp_cruise.vl["CruiseControl"]["Cruise_Activated"] != 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
|
||||
|
||||
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):
|
||||
ret.cruiseState.speed *= CV.MPH_TO_KPH
|
||||
|
||||
ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1
|
||||
ret.doorOpen = any([cp.vl["BodyInfo"]["DOOR_OPEN_RR"],
|
||||
cp.vl["BodyInfo"]["DOOR_OPEN_RL"],
|
||||
cp.vl["BodyInfo"]["DOOR_OPEN_FR"],
|
||||
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:
|
||||
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
|
||||
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
|
||||
else:
|
||||
ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
|
||||
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
|
||||
ret.cruiseState.standstill = cp_cam.vl["ES_DashStatus"]["Cruise_State"] == 3
|
||||
ret.stockFcw = (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 1) or \
|
||||
(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
|
||||
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
|
||||
|
||||
# TODO: Hybrid cars don't have ES_Distance, need a replacement
|
||||
if self.car_fingerprint not in HYBRID_CARS:
|
||||
# 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)
|
||||
|
||||
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:
|
||||
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
|
||||
|
||||
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
|
||||
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
self.es_infotainment_msg = copy.copy(cp_cam.vl["ES_Infotainment"])
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_common_global_body_messages(CP):
|
||||
messages = [
|
||||
("Wheel_Speeds", 50),
|
||||
("Brake_Status", 50),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
messages.append(("CruiseControl", 20))
|
||||
|
||||
return messages
|
||||
|
||||
@staticmethod
|
||||
def get_common_global_es_messages(CP):
|
||||
messages = [
|
||||
("ES_Brake", 20),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
messages += [
|
||||
("ES_Distance", 20),
|
||||
("ES_Status", 20)
|
||||
]
|
||||
|
||||
return messages
|
||||
|
||||
@staticmethod
|
||||
def get_common_preglobal_body_messages():
|
||||
messages = [
|
||||
("CruiseControl", 50),
|
||||
("Wheel_Speeds", 50),
|
||||
("Dash_State2", 1),
|
||||
]
|
||||
|
||||
return messages
|
||||
|
||||
@staticmethod
|
||||
def get_can_parser(CP):
|
||||
messages = [
|
||||
# sig_address, frequency
|
||||
("Dashlights", 10),
|
||||
("Steering_Torque", 50),
|
||||
("BodyInfo", 1),
|
||||
("Brake_Pedal", 50),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
messages += [
|
||||
("Throttle", 100),
|
||||
("Transmission", 100)
|
||||
]
|
||||
|
||||
if CP.enableBsm:
|
||||
messages.append(("BSD_RCTA", 17))
|
||||
|
||||
if CP.carFingerprint not in PREGLOBAL_CARS:
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
messages += CarState.get_common_global_body_messages(CP)
|
||||
else:
|
||||
messages += CarState.get_common_preglobal_body_messages()
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.main)
|
||||
|
||||
@staticmethod
|
||||
def get_cam_can_parser(CP):
|
||||
if CP.carFingerprint in PREGLOBAL_CARS:
|
||||
messages = [
|
||||
("ES_DashStatus", 20),
|
||||
("ES_Distance", 20),
|
||||
]
|
||||
else:
|
||||
messages = [
|
||||
("ES_DashStatus", 10),
|
||||
("ES_LKAS_State", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
messages += CarState.get_common_global_es_messages(CP)
|
||||
|
||||
if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
messages.append(("ES_Infotainment", 10))
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.camera)
|
||||
|
||||
@staticmethod
|
||||
def get_body_can_parser(CP):
|
||||
messages = []
|
||||
|
||||
if CP.carFingerprint in GLOBAL_GEN2:
|
||||
messages += CarState.get_common_global_body_messages(CP)
|
||||
messages += CarState.get_common_global_es_messages(CP)
|
||||
|
||||
if CP.carFingerprint in HYBRID_CARS:
|
||||
messages += [
|
||||
("Throttle_Hybrid", 40),
|
||||
("Transmission", 100)
|
||||
]
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus.alt)
|
||||
|
||||
563
selfdrive/car/subaru/fingerprints.py
Normal file
563
selfdrive/car/subaru/fingerprints.py
Normal file
@@ -0,0 +1,563 @@
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.subaru.values import CAR
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.ASCENT: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa5 \x19\x02\x00',
|
||||
b'\xa5 !\x02\x00',
|
||||
b'\xf1\x82\xa5 \x19\x02\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\x05\xc0\xd0\x00',
|
||||
b'\x85\xc0\xd0\x00',
|
||||
b'\x95\xc0\xd0\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00d\xb9\x00\x00\x00\x00',
|
||||
b'\x00\x00d\xb9\x1f@ \x10',
|
||||
b'\x00\x00e@\x00\x00\x00\x00',
|
||||
b'\x00\x00e@\x1f@ $',
|
||||
b"\x00\x00e~\x1f@ '",
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xbb,\xa0t\x07',
|
||||
b'\xd1,\xa0q\x07',
|
||||
b'\xf1\x82\xbb,\xa0t\x07',
|
||||
b'\xf1\x82\xbb,\xa0t\x87',
|
||||
b'\xf1\x82\xd1,\xa0q\x07',
|
||||
b'\xf1\x82\xd9,\xa0@\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x00\xfe\xf7\x00\x00',
|
||||
b'\x01\xfe\xf7\x00\x00',
|
||||
b'\x01\xfe\xf9\x00\x00',
|
||||
b'\x01\xfe\xfa\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ASCENT_2023: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa5 #\x03\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'%\xc0\xd0\x11',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x05!\x08\x1dK\x05!\x08\x01/',
|
||||
],
|
||||
(Ecu.engine, 0x7a2, None): [
|
||||
b'\xe5,\xa0P\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7a3, None): [
|
||||
b'\x04\xfe\xf3\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.LEGACY: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 \x02\x01',
|
||||
b'\xa1 \x02\x02',
|
||||
b'\xa1 \x03\x03',
|
||||
b'\xa1\\ x04\x01',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\x9b\xc0\x11\x00',
|
||||
b'\x9b\xc0\x11\x02',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00e\x80\x00\x1f@ \x19\x00',
|
||||
b'\x00\x00e\x9a\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xde"a0\x07',
|
||||
b'\xde,\xa0@\x07',
|
||||
b'\xe2"aq\x07',
|
||||
b'\xe2,\xa0@\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xa5\xf6\x05@\x00',
|
||||
b'\xa5\xfe\xc7@\x00',
|
||||
b'\xa7\xf6\x04@\x00',
|
||||
b'\xa7\xfe\xc4@\x00',
|
||||
],
|
||||
},
|
||||
CAR.IMPREZA: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'z\x84\x19\x90\x00',
|
||||
b'z\x94\x08\x90\x00',
|
||||
b'z\x94\x08\x90\x01',
|
||||
b'z\x94\x0c\x90\x00',
|
||||
b'z\x94\x0c\x90\x01',
|
||||
b'z\x94.\x90\x00',
|
||||
b'z\x94?\x90\x00',
|
||||
b'z\x9c\x19\x80\x01',
|
||||
b'\xa2 \x185\x00',
|
||||
b'\xa2 \x193\x00',
|
||||
b'\xa2 \x194\x00',
|
||||
b'\xa2 \x19`\x00',
|
||||
b'\xf1\x00\xb2\x06\x04',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'z\xc0\x00\x00',
|
||||
b'z\xc0\x04\x00',
|
||||
b'z\xc0\x08\x00',
|
||||
b'z\xc0\n\x00',
|
||||
b'z\xc0\x0c\x00',
|
||||
b'\x8a\xc0\x00\x00',
|
||||
b'\x8a\xc0\x10\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00c\xf4\x00\x00\x00\x00',
|
||||
b'\x00\x00c\xf4\x1f@ \x07',
|
||||
b'\x00\x00d)\x00\x00\x00\x00',
|
||||
b'\x00\x00d)\x1f@ \x07',
|
||||
b'\x00\x00dd\x00\x00\x00\x00',
|
||||
b'\x00\x00dd\x1f@ \x0e',
|
||||
b'\x00\x00d\xb5\x1f@ \x0e',
|
||||
b'\x00\x00d\xdc\x00\x00\x00\x00',
|
||||
b'\x00\x00d\xdc\x1f@ \x0e',
|
||||
b'\x00\x00e\x02\x1f@ \x14',
|
||||
b'\x00\x00e\x1c\x00\x00\x00\x00',
|
||||
b'\x00\x00e\x1c\x1f@ \x14',
|
||||
b'\x00\x00e+\x00\x00\x00\x00',
|
||||
b'\x00\x00e+\x1f@ \x14',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xaa\x00Bu\x07',
|
||||
b'\xaa\x01bt\x07',
|
||||
b'\xaa!`u\x07',
|
||||
b'\xaa!au\x07',
|
||||
b'\xaa!av\x07',
|
||||
b'\xaa!aw\x07',
|
||||
b'\xaa!dq\x07',
|
||||
b'\xaa!ds\x07',
|
||||
b'\xaa!dt\x07',
|
||||
b'\xaaafs\x07',
|
||||
b'\xbe!as\x07',
|
||||
b'\xbe!at\x07',
|
||||
b'\xbeacr\x07',
|
||||
b'\xc5!`r\x07',
|
||||
b'\xc5!`s\x07',
|
||||
b'\xc5!ap\x07',
|
||||
b'\xc5!ar\x07',
|
||||
b'\xc5!as\x07',
|
||||
b'\xc5!dr\x07',
|
||||
b'\xc5!ds\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xe3\xd0\x081\x00',
|
||||
b'\xe3\xd5\x161\x00',
|
||||
b'\xe3\xe5F1\x00',
|
||||
b'\xe3\xf5\x06\x00\x00',
|
||||
b'\xe3\xf5\x07\x00\x00',
|
||||
b'\xe3\xf5C\x00\x00',
|
||||
b'\xe3\xf5F\x00\x00',
|
||||
b'\xe3\xf5G\x00\x00',
|
||||
b'\xe4\xe5\x061\x00',
|
||||
b'\xe4\xf5\x02\x00\x00',
|
||||
b'\xe4\xf5\x07\x00\x00',
|
||||
b'\xe5\xf5\x04\x00\x00',
|
||||
b'\xe5\xf5$\x00\x00',
|
||||
b'\xe5\xf5B\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.IMPREZA_2020: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa2 \x193\x00',
|
||||
b'\xa2 \x194\x00',
|
||||
b'\xa2 `\x00',
|
||||
b'\xa2 !3\x00',
|
||||
b'\xa2 !`\x00',
|
||||
b'\xa2 !i\x00',
|
||||
b'\xf1\x00\xb2\x06\x04',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\n\xc0\x04\x00',
|
||||
b'\n\xc0\x04\x01',
|
||||
b'\x9a\xc0\x00\x00',
|
||||
b'\x9a\xc0\x04\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00eb\x1f@ "',
|
||||
b'\x00\x00eq\x00\x00\x00\x00',
|
||||
b'\x00\x00eq\x1f@ "',
|
||||
b'\x00\x00e\x8f\x00\x00\x00\x00',
|
||||
b'\x00\x00e\x8f\x1f@ )',
|
||||
b'\x00\x00e\x92\x00\x00\x00\x00',
|
||||
b'\x00\x00e\xa4\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xca!`0\x07',
|
||||
b'\xca!`p\x07',
|
||||
b'\xca!ap\x07',
|
||||
b'\xca!f@\x07',
|
||||
b'\xca!fp\x07',
|
||||
b'\xcc!`p\x07',
|
||||
b'\xcc!fp\x07',
|
||||
b'\xcc"f0\x07',
|
||||
b'\xe6!`@\x07',
|
||||
b'\xe6!fp\x07',
|
||||
b'\xe6"f0\x07',
|
||||
b'\xe6"fp\x07',
|
||||
b'\xf3"f@\x07',
|
||||
b'\xf3"fp\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xe6\xf5\x04\x00\x00',
|
||||
b'\xe6\xf5$\x00\x00',
|
||||
b'\xe6\xf5D0\x00',
|
||||
b'\xe7\xf5\x04\x00\x00',
|
||||
b'\xe7\xf5D0\x00',
|
||||
b'\xe7\xf6B0\x00',
|
||||
b'\xe9\xf5"\x00\x00',
|
||||
b'\xe9\xf5B0\x00',
|
||||
b'\xe9\xf6B0\x00',
|
||||
b'\xe9\xf6F0\x00',
|
||||
b'\xf1\x00\xd7\x10@',
|
||||
],
|
||||
},
|
||||
CAR.CROSSTREK_HYBRID: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa2 \x19e\x01',
|
||||
b'\xa2 !e\x01',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\n\xc2\x01\x00',
|
||||
b'\x9a\xc2\x01\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00el\x1f@ #',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xd7!`@\x07',
|
||||
b'\xd7!`p\x07',
|
||||
b'\xf4!`0\x07',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 \x18\x14\x00',
|
||||
b'\xa3 \x18&\x00',
|
||||
b'\xa3 \x19\x14\x00',
|
||||
b'\xa3 \x19&\x00',
|
||||
b'\xa3 \x14\x00',
|
||||
b'\xa3 \x14\x01',
|
||||
b'\xf1\x00\xbb\r\x05',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\x8d\xc0\x00\x00',
|
||||
b'\x8d\xc0\x04\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00e!\x00\x00\x00\x00',
|
||||
b'\x00\x00e!\x1f@ \x11',
|
||||
b'\x00\x00e^\x00\x00\x00\x00',
|
||||
b'\x00\x00e^\x1f@ !',
|
||||
b'\x00\x00e`\x1f@ ',
|
||||
b'\x00\x00e\x97\x00\x00\x00\x00',
|
||||
b'\x00\x00e\x97\x1f@ 0',
|
||||
b'\xf1\x00\xac\x02\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xb6"`A\x07',
|
||||
b'\xb6\xa2`A\x07',
|
||||
b'\xcb"`@\x07',
|
||||
b'\xcb"`p\x07',
|
||||
b'\xcf"`0\x07',
|
||||
b'\xcf"`p\x07',
|
||||
b'\xf1\x00\xa2\x10\n',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x1a\xe6B1\x00',
|
||||
b'\x1a\xe6F1\x00',
|
||||
b'\x1a\xf6B0\x00',
|
||||
b'\x1a\xf6B`\x00',
|
||||
b'\x1a\xf6F`\x00',
|
||||
b'\x1a\xf6b0\x00',
|
||||
b'\x1a\xf6b`\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_HYBRID: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 \x19T\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\x8d\xc2\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00eY\x1f@ !',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xd2\xa1`r\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x1b\xa7@a\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'm\x97\x14@',
|
||||
b'}\x97\x14@',
|
||||
b'\xf1\x00\xbb\x0c\x04',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'm\xc0\x10\x00',
|
||||
b'}\xc0\x10\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00c\xe9\x00\x00\x00\x00',
|
||||
b'\x00\x00c\xe9\x1f@ \x03',
|
||||
b'\x00\x00d5\x1f@ \t',
|
||||
b'\x00\x00d\xd3\x1f@ \t',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xa7"@p\x07',
|
||||
b'\xa7)\xa0q\x07',
|
||||
b'\xba"@@\x07',
|
||||
b'\xba"@p\x07',
|
||||
b'\xf1\x82\xa7)\xa0q\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x1a\xf6F`\x00',
|
||||
b'\xda\xf2`\x80\x00',
|
||||
b'\xda\xfd\xe0\x80\x00',
|
||||
b'\xdc\xf2@`\x00',
|
||||
b'\xdc\xf2``\x00',
|
||||
b'\xdc\xf2`\x80\x00',
|
||||
b'\xdc\xf2`\x81\x00',
|
||||
],
|
||||
},
|
||||
CAR.LEGACY_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'[\x97D\x00',
|
||||
b'[\xba\xc4\x03',
|
||||
b'k\x97D\x00',
|
||||
b'k\x9aD\x00',
|
||||
b'{\x97D\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'K\xb0\x00\x01',
|
||||
b'[\xb0\x00\x01',
|
||||
b'k\xb0\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00c\x94\x1f@\x10\x08',
|
||||
b'\x00\x00c\xb7\x1f@\x10\x16',
|
||||
b'\x00\x00c\xec\x1f@ \x04',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xa0"@q\x07',
|
||||
b'\xa0+@p\x07',
|
||||
b'\xab*@r\x07',
|
||||
b'\xab+@p\x07',
|
||||
b'\xb4"@0\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xbd\xf2\x00`\x00',
|
||||
b'\xbe\xf2\x00p\x00',
|
||||
b'\xbe\xfb\xc0p\x00',
|
||||
b'\xbf\xf2\x00\x80\x00',
|
||||
b'\xbf\xfb\xc0\x80\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'[\xba\xac\x03',
|
||||
b'[\xf7\xac\x00',
|
||||
b'[\xf7\xac\x03',
|
||||
b'[\xf7\xbc\x03',
|
||||
b'k\x97\xac\x00',
|
||||
b'k\x9a\xac\x00',
|
||||
b'{\x97\xac\x00',
|
||||
b'{\x9a\xac\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'K\xb0\x00\x00',
|
||||
b'K\xb0\x00\x02',
|
||||
b'[\xb0\x00\x00',
|
||||
b'k\xb0\x00\x00',
|
||||
b'{\xb0\x00\x01',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00c\x90\x1f@\x10\x0e',
|
||||
b'\x00\x00c\x94\x00\x00\x00\x00',
|
||||
b'\x00\x00c\x94\x1f@\x10\x08',
|
||||
b'\x00\x00c\xb7\x1f@\x10\x16',
|
||||
b'\x00\x00c\xd1\x1f@\x10\x17',
|
||||
b'\x00\x00c\xec\x1f@ \x04',
|
||||
b'\x00\x00c\xec7@\x04',
|
||||
b'\xf1\x00\xf0\xe0\x0e',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xa0"@\x80\x07',
|
||||
b'\xa0*@q\x07',
|
||||
b'\xa0*@u\x07',
|
||||
b'\xa0+@@\x07',
|
||||
b'\xa0bAq\x07',
|
||||
b'\xab"@@\x07',
|
||||
b'\xab"@s\x07',
|
||||
b'\xab*@@\x07',
|
||||
b'\xab+@@\x07',
|
||||
b'\xb4"@0\x07',
|
||||
b'\xb4"@p\x07',
|
||||
b'\xb4"@r\x07',
|
||||
b'\xb4+@p\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xbd\xf2@`\x00',
|
||||
b'\xbd\xf2@\x81\x00',
|
||||
b'\xbd\xfb\xe0\x80\x00',
|
||||
b'\xbe\xf2@p\x00',
|
||||
b'\xbe\xf2@\x80\x00',
|
||||
b'\xbe\xfb\xe0p\x00',
|
||||
b'\xbf\xe2@\x80\x00',
|
||||
b'\xbf\xf2@\x80\x00',
|
||||
b'\xbf\xfb\xe0b\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_PREGLOBAL_2018: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\x8b\x97\xac\x00',
|
||||
b'\x8b\x97\xbc\x00',
|
||||
b'\x8b\x99\xac\x00',
|
||||
b'\x8b\x9a\xac\x00',
|
||||
b'\x9b\x97\xac\x00',
|
||||
b'\x9b\x97\xbe\x10',
|
||||
b'\x9b\x9a\xac\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'{\xb0\x00\x00',
|
||||
b'{\xb0\x00\x01',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00df\x1f@ \n',
|
||||
b'\x00\x00d\x95\x00\x00\x00\x00',
|
||||
b'\x00\x00d\x95\x1f@ \x0f',
|
||||
b'\x00\x00d\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00d\xfe\x1f@ \x15',
|
||||
b'\x00\x00e\x19\x1f@ \x15',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xb5"@P\x07',
|
||||
b'\xb5"@p\x07',
|
||||
b'\xb5+@@\x07',
|
||||
b'\xb5b@1\x07',
|
||||
b'\xb5q\xe0@\x07',
|
||||
b'\xc4"@0\x07',
|
||||
b'\xc4+@0\x07',
|
||||
b'\xc4b@p\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xbb\xf2@`\x00',
|
||||
b'\xbb\xfb\xe0`\x00',
|
||||
b'\xbc\xaf\xe0`\x00',
|
||||
b'\xbc\xe2@\x80\x00',
|
||||
b'\xbc\xf2@\x80\x00',
|
||||
b'\xbc\xf2@\x81\x00',
|
||||
b'\xbc\xfb\xe0`\x00',
|
||||
b'\xbc\xfb\xe0\x80\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 \x06\x02',
|
||||
b'\xa1 \x06\x00',
|
||||
b'\xa1 \x06\x01',
|
||||
b'\xa1 \x07\x00',
|
||||
b'\xa1 \x07\x02',
|
||||
b'\xa1 \x08\x00',
|
||||
b'\xa1 \x08\x01',
|
||||
b'\xa1 \x08\x02',
|
||||
b'\xa1 "\t\x00',
|
||||
b'\xa1 "\t\x01',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'\x1b\xc0\x10\x00',
|
||||
b'\x9b\xc0\x10\x00',
|
||||
b'\x9b\xc0 \x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00eJ\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00eJ\x00\x1f@ \x19\x00',
|
||||
b'\x00\x00e\x80\x00\x1f@ \x19\x00',
|
||||
b'\x00\x00e\x9a\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00e\x9a\x00\x1f@ 1\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xbc"`@\x07',
|
||||
b'\xbc"`q\x07',
|
||||
b'\xbc,\xa0q\x07',
|
||||
b'\xbc,\xa0u\x07',
|
||||
b'\xde"`0\x07',
|
||||
b'\xde,\xa0@\x07',
|
||||
b'\xe2"`0\x07',
|
||||
b'\xe2"`p\x07',
|
||||
b'\xe3,\xa0@\x07',
|
||||
b'\xf1\x82\xbc,\xa0q\x07',
|
||||
b'\xf1\x82\xe2,\xa0@\x07',
|
||||
b'\xf1\x82\xe3,\xa0@\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xa5\xf6D@\x00',
|
||||
b'\xa5\xfe\xf6@\x00',
|
||||
b'\xa5\xfe\xf7@\x00',
|
||||
b'\xa5\xfe\xf8@\x00',
|
||||
b'\xa7\x8e\xf40\x00',
|
||||
b'\xa7\xf6D@\x00',
|
||||
b'\xa7\xfe\xf4@\x00',
|
||||
b'\xf1\x82\xa7\xf6D@\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_2022: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 !v\x00',
|
||||
b'\xa3 !x\x00',
|
||||
b'\xa3 "v\x00',
|
||||
b'\xa3 "x\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'-\xc0\x040',
|
||||
b'-\xc0%0',
|
||||
b'=\xc0%\x02',
|
||||
b'=\xc04\x02',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x04!\x01\x1eD\x07!\x00\x04,',
|
||||
b'\x04!\x08\x01.\x07!\x08\x022',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xd5"`0\x07',
|
||||
b'\xd5"a0\x07',
|
||||
b'\xf1"`q\x07',
|
||||
b'\xf1"aq\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x1d\x86B0\x00',
|
||||
b'\x1d\xf6B0\x00',
|
||||
b'\x1e\x86B0\x00',
|
||||
b'\x1e\xf6D0\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_2023: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 #\x14\x00',
|
||||
b'\xa1 #\x17\x00',
|
||||
],
|
||||
(Ecu.eps, 0x746, None): [
|
||||
b'+\xc0\x10\x11\x00',
|
||||
b'+\xc0\x12\x11\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\t!\x08\x046\x05!\x08\x01/',
|
||||
],
|
||||
(Ecu.engine, 0x7a2, None): [
|
||||
b'\xed,\xa0q\x07',
|
||||
b'\xed,\xa2q\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7a3, None): [
|
||||
b'\xa8\x8e\xf41\x00',
|
||||
b'\xa8\xfe\xf41\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
153
selfdrive/car/subaru/interface.py
Normal file
153
selfdrive/car/subaru/interface.py
Normal file
@@ -0,0 +1,153 @@
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
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
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
|
||||
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.autoResumeSng = False
|
||||
|
||||
# Detect infotainment message sent from the camera
|
||||
if candidate not in PREGLOBAL_CARS and 0x323 in fingerprint[2]:
|
||||
ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
|
||||
|
||||
if candidate in PREGLOBAL_CARS:
|
||||
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:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
if candidate in LKAS_ANGLE:
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
else:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if candidate in (CAR.ASCENT, CAR.ASCENT_2023):
|
||||
ret.mass = 2031.
|
||||
ret.wheelbase = 2.89
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 13.5
|
||||
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00003
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]]
|
||||
|
||||
elif candidate == CAR.IMPREZA:
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 15
|
||||
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00005
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.3], [0.02, 0.03]]
|
||||
|
||||
elif candidate == CAR.IMPREZA_2020:
|
||||
ret.mass = 1480.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17 # learned, 14 stock
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00005
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]]
|
||||
|
||||
elif candidate == CAR.CROSSTREK_HYBRID:
|
||||
ret.mass = 1668.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
elif candidate in (CAR.FORESTER, CAR.FORESTER_2022, CAR.FORESTER_HYBRID):
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17 # learned, 14 stock
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.000038
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
|
||||
|
||||
elif candidate in (CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023):
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018):
|
||||
ret.safetyConfigs[0].safetyParam = Panda.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE # Outback 2018-2019 and Forester have reversed driver torque signal
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 20 # learned, 14 stock
|
||||
|
||||
elif candidate == CAR.LEGACY_PREGLOBAL:
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 12.5 # 14.5 stock
|
||||
ret.steerActuatorDelay = 0.15
|
||||
|
||||
elif candidate == CAR.OUTBACK_PREGLOBAL:
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 20 # learned, 14 stock
|
||||
else:
|
||||
raise ValueError(f"unknown car: {candidate}")
|
||||
|
||||
ret.experimentalLongitudinalAvailable = candidate not in (GLOBAL_GEN2 | PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS)
|
||||
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
|
||||
|
||||
if candidate in GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
|
||||
ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.longitudinalTuning.kpBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kpV = [0.8, 1.0, 1.5]
|
||||
ret.longitudinalTuning.kiBP = [0., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.54, 0.36]
|
||||
|
||||
ret.stoppingControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_LONG
|
||||
|
||||
return ret
|
||||
|
||||
# returns a car.CarState
|
||||
def _update(self, c):
|
||||
|
||||
ret = self.CS.update(self.cp, self.cp_cam, self.cp_body)
|
||||
|
||||
ret.events = self.create_common_events(ret).to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def init(CP, logcan, sendcan):
|
||||
if CP.flags & SubaruFlags.DISABLE_EYESIGHT:
|
||||
disable_ecu(logcan, sendcan, bus=2, addr=GLOBAL_ES_ADDR, com_cont_req=b'\x28\x03\x01')
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
4
selfdrive/car/subaru/radar_interface.py
Normal file
4
selfdrive/car/subaru/radar_interface.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
pass
|
||||
312
selfdrive/car/subaru/subarucan.py
Normal file
312
selfdrive/car/subaru/subarucan.py
Normal file
@@ -0,0 +1,312 @@
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.subaru.values import CanBus
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
|
||||
def create_steering_control(packer, apply_steer, steer_req):
|
||||
values = {
|
||||
"LKAS_Output": apply_steer,
|
||||
"LKAS_Request": steer_req,
|
||||
"SET_1": 1
|
||||
}
|
||||
return packer.make_can_msg("ES_LKAS", 0, values)
|
||||
|
||||
|
||||
def create_steering_status(packer):
|
||||
return packer.make_can_msg("ES_LKAS_State", 0, {})
|
||||
|
||||
def create_es_distance(packer, frame, es_distance_msg, bus, pcm_cancel_cmd, long_enabled = False, brake_cmd = False, cruise_throttle = 0):
|
||||
values = {s: es_distance_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"Signal1",
|
||||
"Cruise_Fault",
|
||||
"Cruise_Throttle",
|
||||
"Signal2",
|
||||
"Car_Follow",
|
||||
"Low_Speed_Follow",
|
||||
"Cruise_Soft_Disable",
|
||||
"Signal7",
|
||||
"Cruise_Brake_Active",
|
||||
"Distance_Swap",
|
||||
"Cruise_EPB",
|
||||
"Signal4",
|
||||
"Close_Distance",
|
||||
"Signal5",
|
||||
"Cruise_Cancel",
|
||||
"Cruise_Set",
|
||||
"Cruise_Resume",
|
||||
"Signal6",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
if long_enabled:
|
||||
values["Cruise_Throttle"] = cruise_throttle
|
||||
|
||||
# Do not disable openpilot on Eyesight Soft Disable, if openpilot is controlling long
|
||||
values["Cruise_Soft_Disable"] = 0
|
||||
values["Cruise_Fault"] = 0
|
||||
|
||||
values["Cruise_Brake_Active"] = brake_cmd
|
||||
|
||||
if pcm_cancel_cmd:
|
||||
values["Cruise_Cancel"] = 1
|
||||
values["Cruise_Throttle"] = 1818 # inactive throttle
|
||||
|
||||
return packer.make_can_msg("ES_Distance", bus, values)
|
||||
|
||||
|
||||
def create_es_lkas_state(packer, frame, es_lkas_state_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart):
|
||||
values = {s: es_lkas_state_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"LKAS_Alert_Msg",
|
||||
"Signal1",
|
||||
"LKAS_ACTIVE",
|
||||
"LKAS_Dash_State",
|
||||
"Signal2",
|
||||
"Backward_Speed_Limit_Menu",
|
||||
"LKAS_Left_Line_Enable",
|
||||
"LKAS_Left_Line_Light_Blink",
|
||||
"LKAS_Right_Line_Enable",
|
||||
"LKAS_Right_Line_Light_Blink",
|
||||
"LKAS_Left_Line_Visible",
|
||||
"LKAS_Right_Line_Visible",
|
||||
"LKAS_Alert",
|
||||
"Signal3",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
# Filter the stock LKAS "Keep hands on wheel" alert
|
||||
if values["LKAS_Alert_Msg"] == 1:
|
||||
values["LKAS_Alert_Msg"] = 0
|
||||
|
||||
# Filter the stock LKAS sending an audible alert when it turns off LKAS
|
||||
if values["LKAS_Alert"] == 27:
|
||||
values["LKAS_Alert"] = 0
|
||||
|
||||
# Filter the stock LKAS sending an audible alert when "Keep hands on wheel" alert is active (2020+ models)
|
||||
if values["LKAS_Alert"] == 28 and values["LKAS_Alert_Msg"] == 7:
|
||||
values["LKAS_Alert"] = 0
|
||||
|
||||
# Filter the stock LKAS sending an audible alert when "Keep hands on wheel OFF" alert is active (2020+ models)
|
||||
if values["LKAS_Alert"] == 30:
|
||||
values["LKAS_Alert"] = 0
|
||||
|
||||
# Filter the stock LKAS sending "Keep hands on wheel OFF" alert (2020+ models)
|
||||
if values["LKAS_Alert_Msg"] == 7:
|
||||
values["LKAS_Alert_Msg"] = 0
|
||||
|
||||
# Show Keep hands on wheel alert for openpilot steerRequired alert
|
||||
if visual_alert == VisualAlert.steerRequired:
|
||||
values["LKAS_Alert_Msg"] = 1
|
||||
|
||||
# Ensure we don't overwrite potentially more important alerts from stock (e.g. FCW)
|
||||
if visual_alert == VisualAlert.ldw and values["LKAS_Alert"] == 0:
|
||||
if left_lane_depart:
|
||||
values["LKAS_Alert"] = 12 # Left lane departure dash alert
|
||||
elif right_lane_depart:
|
||||
values["LKAS_Alert"] = 11 # Right lane departure dash alert
|
||||
|
||||
if enabled:
|
||||
values["LKAS_ACTIVE"] = 1 # Show LKAS lane lines
|
||||
values["LKAS_Dash_State"] = 2 # Green enabled indicator
|
||||
else:
|
||||
values["LKAS_Dash_State"] = 0 # LKAS Not enabled
|
||||
|
||||
values["LKAS_Left_Line_Visible"] = int(left_line)
|
||||
values["LKAS_Right_Line_Visible"] = int(right_line)
|
||||
|
||||
return packer.make_can_msg("ES_LKAS_State", CanBus.main, values)
|
||||
|
||||
def create_es_dashstatus(packer, frame, dashstatus_msg, enabled, long_enabled, long_active, lead_visible):
|
||||
values = {s: dashstatus_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"PCB_Off",
|
||||
"LDW_Off",
|
||||
"Signal1",
|
||||
"Cruise_State_Msg",
|
||||
"LKAS_State_Msg",
|
||||
"Signal2",
|
||||
"Cruise_Soft_Disable",
|
||||
"Cruise_Status_Msg",
|
||||
"Signal3",
|
||||
"Cruise_Distance",
|
||||
"Signal4",
|
||||
"Conventional_Cruise",
|
||||
"Signal5",
|
||||
"Cruise_Disengaged",
|
||||
"Cruise_Activated",
|
||||
"Signal6",
|
||||
"Cruise_Set_Speed",
|
||||
"Cruise_Fault",
|
||||
"Cruise_On",
|
||||
"Display_Own_Car",
|
||||
"Brake_Lights",
|
||||
"Car_Follow",
|
||||
"Signal7",
|
||||
"Far_Distance",
|
||||
"Cruise_State",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
if long_enabled:
|
||||
values["Cruise_State"] = 0
|
||||
values["Cruise_Activated"] = enabled
|
||||
values["Cruise_Disengaged"] = 0
|
||||
values["Car_Follow"] = int(lead_visible)
|
||||
|
||||
values["PCB_Off"] = 1 # AEB is not presevered, so show the PCB_Off on dash
|
||||
values["LDW_Off"] = 0
|
||||
values["Cruise_Fault"] = 0
|
||||
|
||||
# Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts
|
||||
if values["LKAS_State_Msg"] in (2, 3):
|
||||
values["LKAS_State_Msg"] = 0
|
||||
|
||||
return packer.make_can_msg("ES_DashStatus", CanBus.main, values)
|
||||
|
||||
def create_es_brake(packer, frame, es_brake_msg, long_enabled, long_active, brake_value):
|
||||
values = {s: es_brake_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"Signal1",
|
||||
"Brake_Pressure",
|
||||
"AEB_Status",
|
||||
"Cruise_Brake_Lights",
|
||||
"Cruise_Brake_Fault",
|
||||
"Cruise_Brake_Active",
|
||||
"Cruise_Activated",
|
||||
"Signal3",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
if long_enabled:
|
||||
values["Cruise_Brake_Fault"] = 0
|
||||
values["Cruise_Activated"] = long_active
|
||||
|
||||
values["Brake_Pressure"] = brake_value
|
||||
|
||||
values["Cruise_Brake_Active"] = brake_value > 0
|
||||
values["Cruise_Brake_Lights"] = brake_value >= 70
|
||||
|
||||
return packer.make_can_msg("ES_Brake", CanBus.main, values)
|
||||
|
||||
def create_es_status(packer, frame, es_status_msg, long_enabled, long_active, cruise_rpm):
|
||||
values = {s: es_status_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"Signal1",
|
||||
"Cruise_Fault",
|
||||
"Cruise_RPM",
|
||||
"Cruise_Activated",
|
||||
"Brake_Lights",
|
||||
"Cruise_Hold",
|
||||
"Signal3",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
if long_enabled:
|
||||
values["Cruise_RPM"] = cruise_rpm
|
||||
values["Cruise_Fault"] = 0
|
||||
|
||||
values["Cruise_Activated"] = long_active
|
||||
|
||||
return packer.make_can_msg("ES_Status", CanBus.main, values)
|
||||
|
||||
|
||||
def create_es_infotainment(packer, frame, es_infotainment_msg, visual_alert):
|
||||
# Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts
|
||||
values = {s: es_infotainment_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"LKAS_State_Infotainment",
|
||||
"LKAS_Blue_Lines",
|
||||
"Signal1",
|
||||
"Signal2",
|
||||
]}
|
||||
|
||||
values["COUNTER"] = frame % 0x10
|
||||
|
||||
if values["LKAS_State_Infotainment"] in (3, 4):
|
||||
values["LKAS_State_Infotainment"] = 0
|
||||
|
||||
# Show Keep hands on wheel alert for openpilot steerRequired alert
|
||||
if visual_alert == VisualAlert.steerRequired:
|
||||
values["LKAS_State_Infotainment"] = 3
|
||||
|
||||
# Show Obstacle Detected for fcw
|
||||
if visual_alert == VisualAlert.fcw:
|
||||
values["LKAS_State_Infotainment"] = 2
|
||||
|
||||
return packer.make_can_msg("ES_Infotainment", CanBus.main, values)
|
||||
|
||||
|
||||
def create_es_highbeamassist(packer):
|
||||
values = {
|
||||
"HBA_Available": False,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ES_HighBeamAssist", CanBus.main, values)
|
||||
|
||||
|
||||
def create_es_static_1(packer):
|
||||
values = {
|
||||
"SET_3": 3,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ES_STATIC_1", CanBus.main, values)
|
||||
|
||||
|
||||
def create_es_static_2(packer):
|
||||
values = {
|
||||
"SET_3": 3,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ES_STATIC_2", CanBus.main, values)
|
||||
|
||||
|
||||
# *** Subaru Pre-global ***
|
||||
|
||||
def subaru_preglobal_checksum(packer, values, addr, checksum_byte=7):
|
||||
dat = packer.make_can_msg(addr, 0, values)[2]
|
||||
return (sum(dat[:checksum_byte]) + sum(dat[checksum_byte+1:])) % 256
|
||||
|
||||
|
||||
def create_preglobal_steering_control(packer, frame, apply_steer, steer_req):
|
||||
values = {
|
||||
"COUNTER": frame % 0x08,
|
||||
"LKAS_Command": apply_steer,
|
||||
"LKAS_Active": steer_req,
|
||||
}
|
||||
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_LKAS")
|
||||
|
||||
return packer.make_can_msg("ES_LKAS", CanBus.main, values)
|
||||
|
||||
|
||||
def create_preglobal_es_distance(packer, cruise_button, es_distance_msg):
|
||||
values = {s: es_distance_msg[s] for s in [
|
||||
"Cruise_Throttle",
|
||||
"Signal1",
|
||||
"Car_Follow",
|
||||
"Signal2",
|
||||
"Cruise_Brake_Active",
|
||||
"Distance_Swap",
|
||||
"Standstill",
|
||||
"Signal3",
|
||||
"Close_Distance",
|
||||
"Signal4",
|
||||
"Standstill_2",
|
||||
"Cruise_Fault",
|
||||
"Signal5",
|
||||
"COUNTER",
|
||||
"Signal6",
|
||||
"Cruise_Button",
|
||||
"Signal7",
|
||||
]}
|
||||
|
||||
values["Cruise_Button"] = cruise_button
|
||||
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_Distance")
|
||||
|
||||
return packer.make_can_msg("ES_Distance", CanBus.main, values)
|
||||
203
selfdrive/car/subaru/values.py
Normal file
203
selfdrive/car/subaru/values.py
Normal file
@@ -0,0 +1,203 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag, StrEnum
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Tool, Column
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
def __init__(self, CP):
|
||||
self.STEER_STEP = 2 # how often we update the steer cmd
|
||||
self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
|
||||
self.STEER_DELTA_DOWN = 70 # torque decrease per refresh
|
||||
self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
|
||||
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
|
||||
self.STEER_DRIVER_FACTOR = 1 # from dbc
|
||||
|
||||
if CP.carFingerprint in GLOBAL_GEN2:
|
||||
self.STEER_MAX = 1000
|
||||
self.STEER_DELTA_UP = 40
|
||||
self.STEER_DELTA_DOWN = 40
|
||||
elif CP.carFingerprint == CAR.IMPREZA_2020:
|
||||
self.STEER_MAX = 1439
|
||||
else:
|
||||
self.STEER_MAX = 2047
|
||||
|
||||
THROTTLE_MIN = 808
|
||||
THROTTLE_MAX = 3400
|
||||
|
||||
THROTTLE_INACTIVE = 1818 # corresponds to zero acceleration
|
||||
THROTTLE_ENGINE_BRAKE = 808 # while braking, eyesight sets throttle to this, probably for engine braking
|
||||
|
||||
BRAKE_MIN = 0
|
||||
BRAKE_MAX = 600 # about -3.5m/s2 from testing
|
||||
|
||||
RPM_MIN = 0
|
||||
RPM_MAX = 2400
|
||||
|
||||
RPM_INACTIVE = 600 # a good base rpm for zero acceleration
|
||||
|
||||
THROTTLE_LOOKUP_BP = [0, 2]
|
||||
THROTTLE_LOOKUP_V = [THROTTLE_INACTIVE, THROTTLE_MAX]
|
||||
|
||||
RPM_LOOKUP_BP = [0, 2]
|
||||
RPM_LOOKUP_V = [RPM_INACTIVE, RPM_MAX]
|
||||
|
||||
BRAKE_LOOKUP_BP = [-3.5, 0]
|
||||
BRAKE_LOOKUP_V = [BRAKE_MAX, BRAKE_MIN]
|
||||
|
||||
|
||||
class SubaruFlags(IntFlag):
|
||||
SEND_INFOTAINMENT = 1
|
||||
DISABLE_EYESIGHT = 2
|
||||
|
||||
|
||||
GLOBAL_ES_ADDR = 0x787
|
||||
GEN2_ES_BUTTONS_DID = b'\x11\x30'
|
||||
|
||||
|
||||
class CanBus:
|
||||
main = 0
|
||||
alt = 1
|
||||
camera = 2
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
# Global platform
|
||||
ASCENT = "SUBARU ASCENT LIMITED 2019"
|
||||
ASCENT_2023 = "SUBARU ASCENT 2023"
|
||||
IMPREZA = "SUBARU IMPREZA LIMITED 2019"
|
||||
IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
|
||||
FORESTER = "SUBARU FORESTER 2019"
|
||||
OUTBACK = "SUBARU OUTBACK 6TH GEN"
|
||||
CROSSTREK_HYBRID = "SUBARU CROSSTREK HYBRID 2020"
|
||||
FORESTER_HYBRID = "SUBARU FORESTER HYBRID 2020"
|
||||
LEGACY = "SUBARU LEGACY 7TH GEN"
|
||||
FORESTER_2022 = "SUBARU FORESTER 2022"
|
||||
OUTBACK_2023 = "SUBARU OUTBACK 7TH GEN"
|
||||
|
||||
# Pre-global
|
||||
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
|
||||
LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018"
|
||||
OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017"
|
||||
OUTBACK_PREGLOBAL_2018 = "SUBARU OUTBACK 2018 - 2019"
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
GLOBAL = CarFootnote(
|
||||
"In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.",
|
||||
Column.PACKAGE)
|
||||
EXP_LONG = CarFootnote(
|
||||
"Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.",
|
||||
Column.LONGITUDINAL)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SubaruCarInfo(CarInfo):
|
||||
package: str = "EyeSight Driver Assistance"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
|
||||
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
|
||||
if CP.experimentalLongitudinalAvailable:
|
||||
self.footnotes.append(Footnote.EXP_LONG)
|
||||
|
||||
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
|
||||
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"),
|
||||
CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.IMPREZA: [
|
||||
SubaruCarInfo("Subaru Impreza 2017-19"),
|
||||
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
],
|
||||
CAR.IMPREZA_2020: [
|
||||
SubaruCarInfo("Subaru Impreza 2020-22"),
|
||||
SubaruCarInfo("Subaru Crosstrek 2020-23"),
|
||||
SubaruCarInfo("Subaru XV 2020-21"),
|
||||
],
|
||||
# TODO: is there an XV and Impreza too?
|
||||
CAR.CROSSTREK_HYBRID: SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.FORESTER_HYBRID: SubaruCarInfo("Subaru Forester Hybrid 2020"),
|
||||
CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"),
|
||||
CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"),
|
||||
CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"),
|
||||
CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"),
|
||||
CAR.OUTBACK_PREGLOBAL_2018: SubaruCarInfo("Subaru Outback 2018-19"),
|
||||
CAR.FORESTER_2022: SubaruCarInfo("Subaru Forester 2022-23", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
|
||||
CAR.OUTBACK_2023: SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
|
||||
CAR.ASCENT_2023: SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
|
||||
}
|
||||
|
||||
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]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
|
||||
),
|
||||
# Some Eyesight modules fail on TESTER_PRESENT_REQUEST
|
||||
# TODO: check if this resolves the fingerprinting issue for the 2023 Ascent and other new Subaru cars
|
||||
Request(
|
||||
[SUBARU_VERSION_REQUEST],
|
||||
[SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.fwdCamera],
|
||||
),
|
||||
# Non-OBD requests
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
|
||||
bus=1,
|
||||
logging=True,
|
||||
obd_multiplexing=False,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
DBC = {
|
||||
CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.ASCENT_2023: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_2022: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
|
||||
CAR.CROSSTREK_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
|
||||
CAR.OUTBACK_2023: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
|
||||
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
|
||||
CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
|
||||
CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None),
|
||||
}
|
||||
|
||||
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}
|
||||
Reference in New Issue
Block a user