From b32ae5228902e6180cb16a1fb15b42721792e65b Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Fri, 17 Oct 2025 14:21:43 -0500 Subject: [PATCH 01/46] Add support for Subaru Crosstrek 2025 and LKAS angle-based steering control --- opendbc/car/fingerprints.py | 1 + opendbc/car/subaru/carcontroller.py | 55 ++++++++++++++++++----------- opendbc/car/subaru/carstate.py | 16 ++++++--- opendbc/car/subaru/fingerprints.py | 15 ++++++++ opendbc/car/subaru/values.py | 19 +++++++++- 5 files changed, 80 insertions(+), 26 deletions(-) diff --git a/opendbc/car/fingerprints.py b/opendbc/car/fingerprints.py index 206177f8ce7..084e6ca19df 100644 --- a/opendbc/car/fingerprints.py +++ b/opendbc/car/fingerprints.py @@ -259,6 +259,7 @@ def all_legacy_fingerprint_cars(): "SUBARU IMPREZA LIMITED 2019": SUBARU.SUBARU_IMPREZA, "SUBARU IMPREZA SPORT 2020": SUBARU.SUBARU_IMPREZA_2020, "SUBARU CROSSTREK HYBRID 2020": SUBARU.SUBARU_CROSSTREK_HYBRID, + "SUBARU CROSSTREK 2025": SUBARU.SUBARU_CROSSTREK_2025 "SUBARU FORESTER 2019": SUBARU.SUBARU_FORESTER, "SUBARU FORESTER HYBRID 2020": SUBARU.SUBARU_FORESTER_HYBRID, "SUBARU FORESTER 2017 - 2018": SUBARU.SUBARU_FORESTER_PREGLOBAL, diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 3b9abf5917b..988b0244871 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -1,7 +1,7 @@ import numpy as np from opendbc.can import CANPacker from opendbc.car import Bus, make_tester_present_msg -from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance +from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags @@ -16,6 +16,7 @@ class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) self.apply_torque_last = 0 + self.apply_steer_last = 0 self.cruise_button_prev = 0 self.steer_rate_counter = 0 @@ -32,30 +33,41 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: - apply_torque = int(round(actuators.torque * self.p.STEER_MAX)) + apply_steer = 0 + apply_torque = 0 - # limits due to driver torque + if self.CP.flags & SubaruFlags.LKAS_ANGLE: + apply_steer = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_steer_last, CS.out.vEgoRaw, + CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS) - new_torque = int(round(apply_torque)) - apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.p) + if not CC.latActive: + apply_steer = CS.out.steeringAngleDeg - if not CC.latActive: - apply_torque = 0 - - if self.CP.flags & SubaruFlags.PREGLOBAL: - can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive)) + can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) + self.apply_steer_last = apply_steer else: - apply_steer_req = CC.latActive + apply_torque = int(round(actuators.torque * self.p.STEER_MAX)) + + new_torque = int(round(apply_torque)) + apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.p) + + if not CC.latActive: + apply_torque = 0 - if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED: - # Steering rate fault prevention - self.steer_rate_counter, apply_steer_req = \ - common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, - self.steer_rate_counter, MAX_STEER_RATE_FRAMES) + if self.CP.flags & SubaruFlags.PREGLOBAL: + can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive)) + else: + apply_steer_req = CC.latActive + + if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED: + # Steering rate fault prevention + self.steer_rate_counter, apply_steer_req = \ + common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, + self.steer_rate_counter, MAX_STEER_RATE_FRAMES) - can_sends.append(subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req)) + can_sends.append(subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req)) - self.apply_torque_last = apply_torque + self.apply_torque_last = apply_torque # *** longitudinal *** @@ -137,8 +149,11 @@ def update(self, CC, CS, now_nanos): can_sends.append(subarucan.create_es_static_2(self.packer)) new_actuators = actuators.as_builder() - new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX - new_actuators.torqueOutputCan = self.apply_torque_last + if self.CP.flags & SubaruFlags.LKAS_ANGLE: + new_actuators.steeringAngleDeg = self.apply_steer_last + else: + new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX + new_actuators.torqueOutputCan = self.apply_torque_last self.frame += 1 return new_actuators, can_sends diff --git a/opendbc/car/subaru/carstate.py b/opendbc/car/subaru/carstate.py index 76bc4ff8f77..36698b74854 100644 --- a/opendbc/car/subaru/carstate.py +++ b/opendbc/car/subaru/carstate.py @@ -60,11 +60,17 @@ def update(self, can_parsers) -> structs.CarState: 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.CP.flags & SubaruFlags.LKAS_ANGLE: + ret.steeringAngleDeg = cp.vl["Steering_2"]["Steering_Angle"] + counter = cp.vl["Steering_2"]["COUNTER"] - if not (self.CP.flags & SubaruFlags.PREGLOBAL): - # ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it - ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"]) + ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, counter) + else: + ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] + + if not (self.CP.flags & SubaruFlags.PREGLOBAL): + # ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it + ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"]) ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] @@ -73,7 +79,7 @@ def update(self, can_parsers) -> structs.CarState: ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold cp_cruise = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp - if self.CP.flags & SubaruFlags.HYBRID: + if self.CP.flags & SubaruFlags.HYBRID or self.CP.flags & SubaruFlags.LKAS_ANGLE: ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0 ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0 else: diff --git a/opendbc/car/subaru/fingerprints.py b/opendbc/car/subaru/fingerprints.py index 695eff8a158..e14f77f2325 100644 --- a/opendbc/car/subaru/fingerprints.py +++ b/opendbc/car/subaru/fingerprints.py @@ -240,6 +240,21 @@ b'\xd7!`p\x07', b'\xf4!`0\x07', ], + }, + CAR.SUBARU_CROSSTREK_2025: { + (Ecu.abs, 0x7b0, None): [ + b'\xa2 $\x17\x06', + ], + (Ecu.eps, 0x746, None): [ + b'\xc2 $\x00\x01', + ], + (Ecu.fwdCamera, 0x787, None): [ + b'\x1d!\x08\x00F\x14!\x08\x00=', + b' \x02\x0e', + ], + (Ecu.engine, 0x7a2, None): [ + b'\x04"cP\x07', + ], }, CAR.SUBARU_FORESTER: { (Ecu.abs, 0x7b0, None): [ diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index a702d39430f..13103dd534c 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from enum import Enum, IntFlag -from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car import AngleSteeringLimits, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -10,6 +10,12 @@ class CarControllerParams: + ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( + 100, + ([0., 15., 15.], [5., .8, .8]), + ([0., 15., 15.], [5., .4, 0.4]), + ) + 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 @@ -57,6 +63,7 @@ class SubaruSafetyFlags(IntFlag): GEN2 = 1 LONG = 2 PREGLOBAL_REVERSED_DRIVER_TORQUE = 4 + LKAS_ANGLE = 8 class SubaruFlags(IntFlag): @@ -160,6 +167,11 @@ class CAR(Platforms): CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.HYBRID, ) + SUBARU_CROSSTREK_2025 = SubaruPlatformConfig( + [SubaruCarDocs("Subaru Crosstrek 2025", car_parts=CarParts.common([CarHarness.subaru_d]))], + CarSpecs(mass=1529, wheelbase=2.5781, steerRatio=13.5), + flags=SubaruFlags.HYBRID, + ) SUBARU_FORESTER = SubaruPlatformConfig( [SubaruCarDocs("Subaru Forester 2019-21", "All")], CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), @@ -211,6 +223,11 @@ class CAR(Platforms): SUBARU_ASCENT.specs, flags=SubaruFlags.LKAS_ANGLE, ) + SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig( + [SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))], + SUBARU_CROSSTREK_2025.specs, + flags=SubaruFlags.LKAS_ANGLE + ) SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ From 7e4c6f2c08683dfcf2dd121b81f9436b99e5f0db Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Fri, 17 Oct 2025 15:15:45 -0500 Subject: [PATCH 02/46] Introduce LKAS angle-based control for Subaru Crosstrek 2025 --- opendbc/car/fingerprints.py | 2 +- opendbc/car/subaru/interface.py | 4 ++ opendbc/car/subaru/values.py | 11 ++-- opendbc/car/torque_data/override.toml | 1 + opendbc/safety/modes/subaru.h | 90 ++++++++++++++++++++++----- 5 files changed, 88 insertions(+), 20 deletions(-) diff --git a/opendbc/car/fingerprints.py b/opendbc/car/fingerprints.py index 084e6ca19df..c0ea3e2169b 100644 --- a/opendbc/car/fingerprints.py +++ b/opendbc/car/fingerprints.py @@ -259,7 +259,7 @@ def all_legacy_fingerprint_cars(): "SUBARU IMPREZA LIMITED 2019": SUBARU.SUBARU_IMPREZA, "SUBARU IMPREZA SPORT 2020": SUBARU.SUBARU_IMPREZA_2020, "SUBARU CROSSTREK HYBRID 2020": SUBARU.SUBARU_CROSSTREK_HYBRID, - "SUBARU CROSSTREK 2025": SUBARU.SUBARU_CROSSTREK_2025 + "SUBARU CROSSTREK 2025": SUBARU.SUBARU_CROSSTREK_2025, "SUBARU FORESTER 2019": SUBARU.SUBARU_FORESTER, "SUBARU FORESTER HYBRID 2020": SUBARU.SUBARU_FORESTER_HYBRID, "SUBARU FORESTER 2017 - 2018": SUBARU.SUBARU_FORESTER_PREGLOBAL, diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index b11a987d55d..39ad6daf3de 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -65,6 +65,10 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_CROSSTREK_HYBRID: ret.steerActuatorDelay = 0.1 + elif candidate in CAR.SUBARU_CROSSTREK_2025: + ret.dashcamOnly = False + ret.steerActuatorDelay = 0.3 + elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID): ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.000038 diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 13103dd534c..1c004b8732f 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -1,7 +1,8 @@ from dataclasses import dataclass, field from enum import Enum, IntFlag -from opendbc.car import AngleSteeringLimits, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car.lateral import AngleSteeringLimits from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -167,10 +168,10 @@ class CAR(Platforms): CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.HYBRID, ) - SUBARU_CROSSTREK_2025 = SubaruPlatformConfig( - [SubaruCarDocs("Subaru Crosstrek 2025", car_parts=CarParts.common([CarHarness.subaru_d]))], + SUBARU_CROSSTREK_GEN_3 = SubaruPlatformConfig( + [SubaruCarDocs("Subaru Crosstrek 2024-25", car_parts=CarParts.common([CarHarness.subaru_d]))], CarSpecs(mass=1529, wheelbase=2.5781, steerRatio=13.5), - flags=SubaruFlags.HYBRID, + flags=SubaruFlags.LKAS_ANGLE, ) SUBARU_FORESTER = SubaruPlatformConfig( [SubaruCarDocs("Subaru Forester 2019-21", "All")], @@ -225,7 +226,7 @@ class CAR(Platforms): ) SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig( [SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))], - SUBARU_CROSSTREK_2025.specs, + SUBARU_CROSSTREK_GEN_3.specs, flags=SubaruFlags.LKAS_ANGLE ) diff --git a/opendbc/car/torque_data/override.toml b/opendbc/car/torque_data/override.toml index 87d8b985e18..27e0a60e1a1 100644 --- a/opendbc/car/torque_data/override.toml +++ b/opendbc/car/torque_data/override.toml @@ -14,6 +14,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "SUBARU_FORESTER_2022" = [nan, 3.0, nan] "SUBARU_OUTBACK_2023" = [nan, 3.0, nan] "SUBARU_ASCENT_2023" = [nan, 3.0, nan] +"SUBARU_CROSSTREK_2025" = [nan, 3.0, nan] # Toyota LTA also has torque "TOYOTA_RAV4_TSS2_2023" = [nan, 3.0, nan] diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 057a21162d4..8684b4cfabd 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -22,10 +22,12 @@ #define MSG_SUBARU_Brake_Status 0x13cU #define MSG_SUBARU_CruiseControl 0x240U #define MSG_SUBARU_Throttle 0x40U +#define MSG_SUBARU_Steering_2 0x11aU #define MSG_SUBARU_Steering_Torque 0x119U #define MSG_SUBARU_Wheel_Speeds 0x13aU #define MSG_SUBARU_ES_LKAS 0x122U +#define MSG_SUBARU_ES_LKAS_ANGLE 0x124U #define MSG_SUBARU_ES_Brake 0x220U #define MSG_SUBARU_ES_Distance 0x221U #define MSG_SUBARU_ES_Status 0x222U @@ -70,8 +72,17 @@ {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_CruiseControl, alt_bus, 8, 20U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ +#define SUBARU_LKAS_ANGLE_RX_CHECKS(alt_bus) \ + {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, .max_counter = 15U, .frequency = 10U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 50U }, { 0 }, { 0 }}}, \ + static bool subaru_gen2 = false; static bool subaru_longitudinal = false; +static bool subaru_lkas_angle = false; static uint32_t subaru_get_checksum(const CANPacket_t *msg) { return (uint8_t)msg->data[0]; @@ -91,24 +102,39 @@ static uint32_t subaru_compute_checksum(const CANPacket_t *msg) { } static void subaru_rx_hook(const CANPacket_t *msg) { - const unsigned int alt_main_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; - - if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { - int torque_driver_new; - torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); - torque_driver_new = -1 * to_signed(torque_driver_new, 11); - update_sample(&torque_driver, torque_driver_new); + const unsigned int alt_main_bus = (subaru_gen2 || subaru_lkas_angle) ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; - int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); - // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units - angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); + if (subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { + // Steering_Angle is 17 bits at bytes 3–5, little-endian, scale -0.01 deg/bit + uint32_t raw = GET_BYTES(msg, 3, 3); // bytes 3,4,5 + raw &= 0x1FFFFU; // keep only 17 bits + int angle_meas_new = ROUND(to_signed(raw, 17) * -0.01); update_sample(&angle_meas, angle_meas_new); + } else { + if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { + int torque_driver_new; + torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); + torque_driver_new = -1 * to_signed(torque_driver_new, 11); + update_sample(&torque_driver, torque_driver_new); + + int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); + // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units + angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); + update_sample(&angle_meas, angle_meas_new); + } } // enter controls on rising edge of ACC, exit controls on ACC off - if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { - bool cruise_engaged = (msg->data[5] >> 1) & 1U; - pcm_cruise_check(cruise_engaged); + if (subaru_lkas_angle) { + if ((msg->addr == MSG_SUBARU_ES_DashStatus) && (msg->bus == SUBARU_CAM_BUS)) { + bool cruise_engaged = (msg->data[4] >> 4) & 1U; + pcm_cruise_check(cruise_engaged); + } + } else { + if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { + bool cruise_engaged = (msg->data[5] >> 1) & 1U; + pcm_cruise_check(cruise_engaged); + } } // update vehicle moving with any non-zero wheel speed @@ -136,6 +162,20 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { const TorqueSteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); const TorqueSteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); + const AngleSteeringLimits SUBARU_ANGLE_STEERING_LIMITS = { + .max_angle = 545*100, + .angle_deg_to_can = 100., + .angle_rate_up_lookup = { + {0., 15., 15.}, + {5., .8, .8} + }, + .angle_rate_down_lookup = { + {0., 15., 15.}, + {5., .4, .4} + }, + .inactive_angle_is_zero = true, + }; + const LongitudinalLimits SUBARU_LONG_LIMITS = { .min_gas = 808, // appears to be engine braking .max_gas = 3400, // approx 2 m/s^2 when maxing cruise_rpm and cruise_throttle @@ -160,6 +200,14 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { violation |= steer_torque_cmd_checks(desired_torque, steer_req, limits); } + if (msg->addr == MSG_SUBARU_ES_LKAS_ANGLE) { + int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; + desired_angle = -1 * to_signed(desired_angle, 17); + bool lkas_request = GET_BIT(msg, 12U); + + violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); + } + // check es_brake brake_pressure limits if (msg->addr == MSG_SUBARU_ES_Brake) { int es_brake_pressure = GET_BYTES(msg, 2, 2); @@ -225,6 +273,11 @@ static safety_config subaru_init(uint16_t param) { SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() }; + static const CanMsg subaru_lkas_angle_tx_msgs[] = { + SUBARU_BASE_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS_ANGLE) + SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS) + }; + static RxCheck subaru_rx_checks[] = { SUBARU_COMMON_RX_CHECKS(SUBARU_MAIN_BUS) }; @@ -233,9 +286,16 @@ static safety_config subaru_init(uint16_t param) { SUBARU_COMMON_RX_CHECKS(SUBARU_ALT_BUS) }; + static RxCheck subaru_lkas_angle_rx_checks[] = { + // Does this need to be 1? + SUBARU_LKAS_ANGLE_RX_CHECKS(SUBARU_MAIN_BUS) + }; + const uint16_t SUBARU_PARAM_GEN2 = 1; + const uint16_t SUBARU_PARAM_LKAS_ANGLE = 8; subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2); + subaru_lkas_angle = GET_FLAG(param, SUBARU_PARAM_LKAS_ANGLE); #ifdef ALLOW_DEBUG const uint16_t SUBARU_PARAM_LONGITUDINAL = 2; @@ -243,7 +303,9 @@ static safety_config subaru_init(uint16_t param) { #endif safety_config ret; - if (subaru_gen2) { + if (subaru_lkas_angle) { + ret = BUILD_SAFETY_CFG(subaru_lkas_angle_rx_checks, subaru_lkas_angle_tx_msgs); + } else if (subaru_gen2) { ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_LONG_TX_MSGS) : \ BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS); } else { From ac6cf1f63c76bd198ca9606917e4b14702c33d23 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Fri, 17 Oct 2025 16:05:47 -0500 Subject: [PATCH 03/46] Configure panda safety for LKAS angle platforms and fix Crosstrek 2025 candidate check --- opendbc/car/subaru/interface.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 39ad6daf3de..d7673ca257b 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -42,6 +42,10 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp else: CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + # Ensure panda safety mode is configured for LKAS angle platforms + if ret.flags & SubaruFlags.LKAS_ANGLE: + ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value + if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023): ret.steerActuatorDelay = 0.3 # end-to-end angle controller ret.lateralTuning.init('pid') @@ -65,7 +69,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_CROSSTREK_HYBRID: ret.steerActuatorDelay = 0.1 - elif candidate in CAR.SUBARU_CROSSTREK_2025: + elif candidate == CAR.SUBARU_CROSSTREK_2025: ret.dashcamOnly = False ret.steerActuatorDelay = 0.3 From 20ad3632f584cb32126588fb2e4e064f1257e058 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Fri, 17 Oct 2025 17:49:22 -0500 Subject: [PATCH 04/46] Remove incorrect `inactive_angle_is_zero` from Subaru safety config --- opendbc/safety/modes/subaru.h | 1 - 1 file changed, 1 deletion(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index d92c67607d0..5020d43c21b 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -173,7 +173,6 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { {0., 15., 15.}, {5., .4, .4} }, - .inactive_angle_is_zero = true, }; const LongitudinalLimits SUBARU_LONG_LIMITS = { From fe411cd939491a412482ff42a81d9ab61eb3b67f Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 18 Oct 2025 16:44:26 -0500 Subject: [PATCH 05/46] Actually use the damn alt-bus --- opendbc/safety/modes/subaru.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 5020d43c21b..bd969736bd9 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -286,8 +286,7 @@ static safety_config subaru_init(uint16_t param) { }; static RxCheck subaru_lkas_angle_rx_checks[] = { - // Does this need to be 1? - SUBARU_LKAS_ANGLE_RX_CHECKS(SUBARU_MAIN_BUS) + SUBARU_LKAS_ANGLE_RX_CHECKS(SUBARU_ALT_BUS) }; const uint16_t SUBARU_PARAM_GEN2 = 1; From 2715055ae45c26a5db2a777652df421d7fa0e45c Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sun, 19 Oct 2025 18:41:55 -0500 Subject: [PATCH 06/46] Move freq, don't re-convert to centidegrees --- opendbc/safety/modes/subaru.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index bd969736bd9..0f1e8ff0c8e 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -73,12 +73,12 @@ {.msg = {{MSG_SUBARU_CruiseControl, alt_bus, 8, 20U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ #define SUBARU_LKAS_ANGLE_RX_CHECKS(alt_bus) \ - {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, .max_counter = 15U, .frequency = 10U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, .max_counter = 15U, .frequency = 50U }, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true }, { 0 }, { 0 }}}, \ static bool subaru_gen2 = false; static bool subaru_longitudinal = false; @@ -105,10 +105,9 @@ static void subaru_rx_hook(const CANPacket_t *msg) { const unsigned int alt_main_bus = (subaru_gen2 || subaru_lkas_angle) ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; if (subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { - // Steering_Angle is 17 bits at bytes 3–5, little-endian, scale -0.01 deg/bit - uint32_t raw = GET_BYTES(msg, 3, 3); // bytes 3,4,5 - raw &= 0x1FFFFU; // keep only 17 bits - int angle_meas_new = ROUND(to_signed(raw, 17) * -0.01); + uint32_t raw = GET_BYTES(msg, 3, 3); + raw &= 0x1FFFFU; + int angle_meas_new = ROUND(to_signed(raw, 17) * -1); update_sample(&angle_meas, angle_meas_new); } else { if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { From 3af8ccdcf496a945d45626fa5aac209e4581e46c Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sun, 19 Oct 2025 21:28:39 -0500 Subject: [PATCH 07/46] Fix Subaru Crosstrek 2025 recognition and adjust LKAS angle safety logic --- opendbc/car/subaru/fingerprints.py | 1 - opendbc/car/subaru/values.py | 12 ++++----- opendbc/car/tests/routes.py | 1 + opendbc/safety/tests/test_subaru.py | 38 +++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/opendbc/car/subaru/fingerprints.py b/opendbc/car/subaru/fingerprints.py index 2100474677b..72a72c84292 100644 --- a/opendbc/car/subaru/fingerprints.py +++ b/opendbc/car/subaru/fingerprints.py @@ -253,7 +253,6 @@ ], (Ecu.fwdCamera, 0x787, None): [ b'\x1d!\x08\x00F\x14!\x08\x00=', - b' \x02\x0e', ], (Ecu.engine, 0x7a2, None): [ b'\x04"cP\x07', diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 1c004b8732f..ba553540c25 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -168,11 +168,11 @@ class CAR(Platforms): CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.HYBRID, ) - SUBARU_CROSSTREK_GEN_3 = SubaruPlatformConfig( - [SubaruCarDocs("Subaru Crosstrek 2024-25", car_parts=CarParts.common([CarHarness.subaru_d]))], - CarSpecs(mass=1529, wheelbase=2.5781, steerRatio=13.5), - flags=SubaruFlags.LKAS_ANGLE, - ) + # SUBARU_CROSSTREK_GEN_3 = SubaruPlatformConfig( + # [SubaruCarDocs("Subaru Crosstrek 2024-25", car_parts=CarParts.common([CarHarness.subaru_d]))], + # , + # flags=SubaruFlags.LKAS_ANGLE, + # ) SUBARU_FORESTER = SubaruPlatformConfig( [SubaruCarDocs("Subaru Forester 2019-21", "All")], CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), @@ -226,7 +226,7 @@ class CAR(Platforms): ) SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig( [SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))], - SUBARU_CROSSTREK_GEN_3.specs, + CarSpecs(mass=1529, wheelbase=2.5781, steerRatio=13.5), flags=SubaruFlags.LKAS_ANGLE ) diff --git a/opendbc/car/tests/routes.py b/opendbc/car/tests/routes.py index 6e98dea40b9..70def968ebc 100644 --- a/opendbc/car/tests/routes.py +++ b/opendbc/car/tests/routes.py @@ -299,6 +299,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("1bbe6bf2d62f58a8/2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10), CarTestRoute("c56e69bbc74b8fad/2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3), CarTestRoute("f4e3a0c511a076f4/2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2), + CarTestRoute("38b065e31c0a9ed7/0000015c--78def376ae", SUBARU.SUBARU_CROSSTREK_2025, segment=0), CarTestRoute("7fd1e4f3a33c1673/2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4), CarTestRoute("f3b34c0d2632aa83/2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7), CarTestRoute("99437cef6d5ff2ee/2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7), diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index a0ad42d8d67..b1ce3bbf827 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -172,6 +172,41 @@ def _torque_cmd_msg(self, torque, steer_req=1): return self.packer.make_can_msg_safety("ES_LKAS", SUBARU_MAIN_BUS, values) +class TestSubaruAngleSafetyBase(TestSubaruSafetyBase, common.AngleSteeringSafetyTest): + ALT_MAIN_BUS = SUBARU_ALT_BUS + + TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS, SubaruMsg.ES_LKAS_ANGLE) + RELAY_MALFUNCTION_ADDRS = {SUBARU_MAIN_BUS: (SubaruMsg.ES_LKAS_ANGLE, SubaruMsg.ES_DashStatus, + SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment)} + FWD_BLACKLISTED_ADDRS = fwd_blacklisted_addr(SubaruMsg.ES_LKAS_ANGLE) + + FLAGS = SubaruSafetyFlags.LKAS_ANGLE | SubaruSafetyFlags.GEN2 + + STEER_ANGLE_MAX = 545 + # Avoid overflow of ES_LKAS_ANGLE's 17-bit signed field (0.01 deg resolution): limit test angles + STEER_ANGLE_TEST_MAX = 545 + ANGLE_RATE_BP = [0, 15, 15] + ANGLE_RATE_UP = [5, 0.15, 0.15] + ANGLE_RATE_DOWN = [5, 0.4, 0.4] + + def _angle_cmd_msg(self, angle, enabled=1): + values = {"LKAS_Output": angle, "LKAS_Request": enabled, "SET_3": 3} + return self.packer.make_can_msg_safety("ES_LKAS_ANGLE", SUBARU_MAIN_BUS, values) + + def _angle_meas_msg(self, angle): + values = {"Steering_Angle": angle} + return self.packer.make_can_msg_safety("Steering_2", SUBARU_MAIN_BUS, values) + + def _speed_msg(self, speed): + # convert meters-per-second to kilometers per hour for message + values = {s: speed * 3.6 for s in ["FR", "FL", "RR", "RL"]} + return self.packer.make_can_msg_safety("Wheel_Speeds", self.ALT_MAIN_BUS, values) + + # need to use ES_DashStatus Message + def _pcm_status_msg(self, enable): + values = {"Cruise_Activated": enable} + return self.packer.make_can_msg_safety("ES_DashStatus", self.ALT_CAM_BUS, values) + class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): FLAGS = 0 TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) @@ -198,6 +233,9 @@ class TestSubaruGen1LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSub SubaruMsg.ES_Infotainment, SubaruMsg.ES_Brake, SubaruMsg.ES_Status, SubaruMsg.ES_Distance)} +class TestSubaruGen2AngleStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruAngleSafetyBase): + ALT_MAIN_BUS = SUBARU_ALT_BUS + FLAGS = SubaruSafetyFlags.GEN2 | SubaruSafetyFlags.LKAS_ANGLE class TestSubaruGen2LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): FLAGS = SubaruSafetyFlags.LONG | SubaruSafetyFlags.GEN2 From db2a23e97cb6ee21357be438d266bebe5d19a440 Mon Sep 17 00:00:00 2001 From: jacobwaller Date: Mon, 20 Oct 2025 08:11:47 +0000 Subject: [PATCH 08/46] docs: Scheduled auto-update CARS.md --- docs/CARS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CARS.md b/docs/CARS.md index 60f5f600eee..e32a6d1b503 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -1,6 +1,6 @@ -# Support Information for 383 Known Cars +# Support Information for 384 Known Cars |Make|Model|Package|Support Level| |---|---|---|:---:| @@ -259,6 +259,7 @@ |Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)| +|Subaru|Crosstrek 2025|All|[Upstream](#upstream)| |Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2019-21|All|[Upstream](#upstream)| From 2d812ae84e3baa4f49727711089eade983b83808 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Mon, 20 Oct 2025 20:04:10 -0500 Subject: [PATCH 09/46] Update angle limits, add .idea to gitignore --- .gitignore | 1 + opendbc/car/subaru/carcontroller.py | 16 ++++++++++++---- opendbc/car/subaru/values.py | 9 +++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 5a595d00afa..9de89119a25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ .cache/ /build/ .mypy_cache/ diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 988b0244871..0c4b7b56c80 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -1,10 +1,11 @@ import numpy as np from opendbc.can import CANPacker from opendbc.car import Bus, make_tester_present_msg -from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits +from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags +from opendbc.car.vehicle_model import VehicleModel # 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 @@ -23,6 +24,7 @@ def __init__(self, dbc_names, CP): self.p = CarControllerParams(CP) self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt]) + self.VM = VehicleModel(CP) def update(self, CC, CS, now_nanos): actuators = CC.actuators @@ -37,9 +39,15 @@ def update(self, CC, CS, now_nanos): apply_torque = 0 if self.CP.flags & SubaruFlags.LKAS_ANGLE: - apply_steer = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_steer_last, CS.out.vEgoRaw, - CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS) - + apply_steer = apply_steer_angle_limits_vm( + actuators.steeringAngleDeg, + self.apply_steer_last, + CS.out.vEgoRaw, + CS.out.steeringAngleDeg, + CC.latActive, + self.p, # holds ANGLE_LIMITS + MAX_LATERAL_ACCEL/JERK/ANGLE_RATE + self.VM # set this in __init__(...), or fetch via interfaces + ) if not CC.latActive: apply_steer = CS.out.steeringAngleDeg diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index ba553540c25..321e1233700 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -1,8 +1,8 @@ from dataclasses import dataclass, field from enum import Enum, IntFlag -from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds -from opendbc.car.lateral import AngleSteeringLimits +from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car.lateral import ISO_LATERAL_ACCEL, AngleSteeringLimits from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -15,6 +15,11 @@ class CarControllerParams: 100, ([0., 15., 15.], [5., .8, .8]), ([0., 15., 15.], [5., .4, 0.4]), + MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * 0.06), # ~3.6 m/s^2 + MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * 0.06), # ~3.6 m/s^3 + + # limit angle rate to both prevent a fault and for low speed comfort (~12 mph rate down to 0 mph) + MAX_ANGLE_RATE=5 ) def __init__(self, CP): From 3847543c7eab1cd2c63fe1fea52049e39f7850e7 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Tue, 21 Oct 2025 10:18:56 -0500 Subject: [PATCH 10/46] Update routes.py --- opendbc/car/tests/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc/car/tests/routes.py b/opendbc/car/tests/routes.py index 70def968ebc..6e512dbcc09 100644 --- a/opendbc/car/tests/routes.py +++ b/opendbc/car/tests/routes.py @@ -299,7 +299,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("1bbe6bf2d62f58a8/2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10), CarTestRoute("c56e69bbc74b8fad/2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3), CarTestRoute("f4e3a0c511a076f4/2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2), - CarTestRoute("38b065e31c0a9ed7/0000015c--78def376ae", SUBARU.SUBARU_CROSSTREK_2025, segment=0), + CarTestRoute("38b065e31c0a9ed7/00000178--0287f77ede", SUBARU.SUBARU_CROSSTREK_2025, segment=1), CarTestRoute("7fd1e4f3a33c1673/2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4), CarTestRoute("f3b34c0d2632aa83/2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7), CarTestRoute("99437cef6d5ff2ee/2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7), From cc0d9ee11ceb4e3d6550a5601f594b450535a8a3 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Tue, 21 Oct 2025 15:55:16 -0500 Subject: [PATCH 11/46] attempt to add steering msgs to replay_drive --- opendbc/safety/tests/safety_replay/helpers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/opendbc/safety/tests/safety_replay/helpers.py b/opendbc/safety/tests/safety_replay/helpers.py index 4f91a9bf68c..0a55354e911 100644 --- a/opendbc/safety/tests/safety_replay/helpers.py +++ b/opendbc/safety/tests/safety_replay/helpers.py @@ -1,5 +1,6 @@ from opendbc.car.ford.values import FordSafetyFlags from opendbc.car.hyundai.values import HyundaiSafetyFlags +from opendbc.car.subaru.values import SubaruSafetyFlags from opendbc.car.toyota.values import ToyotaSafetyFlags from opendbc.car.structs import CarParams from opendbc.safety.tests.libsafety import libsafety_py @@ -29,7 +30,7 @@ def is_steering_msg(mode, param, addr): elif mode == CarParams.SafetyModel.chrysler: ret = addr == 0x292 elif mode == CarParams.SafetyModel.subaru: - ret = addr == 0x122 + ret = addr == 0x122 or addr == 0x124 elif mode == CarParams.SafetyModel.ford: ret = addr == 0x3d6 if param & FordSafetyFlags.CANFD else addr == 0x3d3 elif mode == CarParams.SafetyModel.nissan: @@ -64,8 +65,11 @@ def get_steer_value(mode, param, msg): elif mode == CarParams.SafetyModel.chrysler: torque = (((msg.data[0] & 0x7) << 8) | msg.data[1]) - 1024 elif mode == CarParams.SafetyModel.subaru: - torque = ((msg.data[3] & 0x1F) << 8) | msg.data[2] - torque = -to_signed(torque, 13) + if param & SubaruSafetyFlags.LKAS_ANGLE: + angle = -to_signed(((msg.data[5] | (msg.data[6] << 8) | (msg.data[7] << 16)) & 0x1FFFF), 17) + else: + torque = ((msg.data[3] & 0x1F) << 8) | msg.data[2] + torque = -to_signed(torque, 13) elif mode == CarParams.SafetyModel.ford: if param & FordSafetyFlags.CANFD: angle = ((msg.data[2] << 3) | (msg.data[3] >> 5)) - 1000 From eb7b7cbe293dd4b02460e4916fdfd072e6f91749 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Tue, 21 Oct 2025 19:13:36 -0500 Subject: [PATCH 12/46] Revert to V1 Angle limiting. Run the limits on whatever angle the steering is at if latActive is false --- opendbc/car/subaru/carcontroller.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 0c4b7b56c80..fb189f15bd6 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -1,7 +1,7 @@ import numpy as np from opendbc.can import CANPacker from opendbc.car import Bus, make_tester_present_msg -from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance +from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags @@ -35,21 +35,18 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: - apply_steer = 0 - apply_torque = 0 - if self.CP.flags & SubaruFlags.LKAS_ANGLE: - apply_steer = apply_steer_angle_limits_vm( - actuators.steeringAngleDeg, + # Use current angle as target when inactive + target_angle = actuators.steeringAngleDeg if CC.latActive else CS.out.steeringAngleDeg + + apply_steer = apply_std_steer_angle_limits( + target_angle, self.apply_steer_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg, CC.latActive, - self.p, # holds ANGLE_LIMITS + MAX_LATERAL_ACCEL/JERK/ANGLE_RATE - self.VM # set this in __init__(...), or fetch via interfaces + self.p.ANGLE_LIMITS, ) - if not CC.latActive: - apply_steer = CS.out.steeringAngleDeg can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) self.apply_steer_last = apply_steer From 86cdba3d4afe0c8bdbb19bf7143ea52f73925355 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Tue, 21 Oct 2025 20:10:01 -0500 Subject: [PATCH 13/46] Revert to completely normal angle rate limiting, reduce the angles allowable in values.py --- opendbc/car/subaru/carcontroller.py | 18 +++++------------- opendbc/car/subaru/values.py | 13 ++++--------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index fb189f15bd6..2e6a3f2be63 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -5,7 +5,6 @@ from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags -from opendbc.car.vehicle_model import VehicleModel # 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 @@ -24,7 +23,6 @@ def __init__(self, dbc_names, CP): self.p = CarControllerParams(CP) self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt]) - self.VM = VehicleModel(CP) def update(self, CC, CS, now_nanos): actuators = CC.actuators @@ -36,17 +34,11 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: if self.CP.flags & SubaruFlags.LKAS_ANGLE: - # Use current angle as target when inactive - target_angle = actuators.steeringAngleDeg if CC.latActive else CS.out.steeringAngleDeg - - apply_steer = apply_std_steer_angle_limits( - target_angle, - self.apply_steer_last, - CS.out.vEgoRaw, - CS.out.steeringAngleDeg, - CC.latActive, - self.p.ANGLE_LIMITS, - ) + apply_steer = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_steer_last, CS.out.vEgoRaw, + CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS) + + if not CC.latActive: + apply_steer = CS.out.steeringAngleDeg can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) self.apply_steer_last = apply_steer diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 321e1233700..f156f9b45c2 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -1,8 +1,8 @@ from dataclasses import dataclass, field from enum import Enum, IntFlag -from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds -from opendbc.car.lateral import ISO_LATERAL_ACCEL, AngleSteeringLimits +from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car.lateral import AngleSteeringLimits from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -13,13 +13,8 @@ class CarControllerParams: ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( 100, - ([0., 15., 15.], [5., .8, .8]), - ([0., 15., 15.], [5., .4, 0.4]), - MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * 0.06), # ~3.6 m/s^2 - MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * 0.06), # ~3.6 m/s^3 - - # limit angle rate to both prevent a fault and for low speed comfort (~12 mph rate down to 0 mph) - MAX_ANGLE_RATE=5 + ([0., 15., 15.], [4.5, .6, .6]), + ([0., 15., 15.], [4.5, .3, .3]), ) def __init__(self, CP): From a3ea3ca20386524a31d50cc451aab84707271039 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Tue, 21 Oct 2025 20:14:27 -0500 Subject: [PATCH 14/46] Horrible debugging idea --- opendbc/safety/modes/subaru.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 0f1e8ff0c8e..8831cc7de51 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -202,8 +202,10 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; desired_angle = -1 * to_signed(desired_angle, 17); bool lkas_request = GET_BIT(msg, 12U); - - violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); + steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); + + // DO NOT OPEN A PR WITH THIS. HORRIBLE IDEA. DEBUGGING ONLY + violation |= false; } // check es_brake brake_pressure limits From 0076d262f3701b405d7999a31201c1b106e76ed6 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 22 Oct 2025 16:58:56 -0500 Subject: [PATCH 15/46] undo bad debugging idea --- opendbc/safety/modes/subaru.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 8831cc7de51..d5a99fe4362 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -202,10 +202,8 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; desired_angle = -1 * to_signed(desired_angle, 17); bool lkas_request = GET_BIT(msg, 12U); - steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); - // DO NOT OPEN A PR WITH THIS. HORRIBLE IDEA. DEBUGGING ONLY - violation |= false; + violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); } // check es_brake brake_pressure limits From 73d7b5a647228d3363ce2b7bc2e2c6594064edba Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 22 Oct 2025 17:36:42 -0500 Subject: [PATCH 16/46] Use steering offset in car controller, re-up angle limits in values.py --- opendbc/car/subaru/carcontroller.py | 15 ++++++++++++--- opendbc/car/subaru/values.py | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 2e6a3f2be63..f8c00a67df2 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -34,11 +34,20 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: if self.CP.flags & SubaruFlags.LKAS_ANGLE: - apply_steer = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_steer_last, CS.out.vEgoRaw, - CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS) + actual_steering_angle_deg = CS.out.steeringAngleDeg + CS.out.steeringAngleOffsetDeg + desired_steering_angle_deg = actuators.steeringAngleDeg + CS.out.steeringAngleOffsetDeg + + apply_steer = apply_std_steer_angle_limits( + desired_steering_angle_deg, + self.apply_steer_last, + CS.out.vEgoRaw, + actual_steering_angle_deg, + CC.latActive, + CarControllerParams.ANGLE_LIMITS + ) if not CC.latActive: - apply_steer = CS.out.steeringAngleDeg + apply_steer = actual_steering_angle_deg can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) self.apply_steer_last = apply_steer diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index f156f9b45c2..6d2bcefeab2 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -13,8 +13,8 @@ class CarControllerParams: ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( 100, - ([0., 15., 15.], [4.5, .6, .6]), - ([0., 15., 15.], [4.5, .3, .3]), + ([0., 15., 15.], [5., .8, .8]), + ([0., 15., 15.], [5., .4, .4]), ) def __init__(self, CP): From ed1019d672ce2c474e4ef84996ccbc4417414723 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Thu, 23 Oct 2025 17:50:41 -0500 Subject: [PATCH 17/46] Don't add steeringOffset, i don't think we need to do that --- opendbc/car/subaru/carcontroller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index f8c00a67df2..147638ababf 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -34,8 +34,8 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: if self.CP.flags & SubaruFlags.LKAS_ANGLE: - actual_steering_angle_deg = CS.out.steeringAngleDeg + CS.out.steeringAngleOffsetDeg - desired_steering_angle_deg = actuators.steeringAngleDeg + CS.out.steeringAngleOffsetDeg + actual_steering_angle_deg = CS.out.steeringAngleDeg + desired_steering_angle_deg = actuators.steeringAngleDeg apply_steer = apply_std_steer_angle_limits( desired_steering_angle_deg, From 6d827fbdbd08f654a1b7603614f2cc666529e407 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 25 Oct 2025 13:46:53 -0500 Subject: [PATCH 18/46] Try making sure the limits are accesible --- opendbc/car/subaru/carcontroller.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 147638ababf..dd777591070 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -1,7 +1,7 @@ import numpy as np from opendbc.can import CANPacker from opendbc.car import Bus, make_tester_present_msg -from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits +from opendbc.car.lateral import AngleSteeringLimits, apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags @@ -11,6 +11,11 @@ MAX_STEER_RATE = 25 # deg/s MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut +ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( + 100, + ([0., 15., 15.], [5., .8, .8]), + ([0., 15., 15.], [5., .4, .4]), + ) class CarController(CarControllerBase): def __init__(self, dbc_names, CP): @@ -43,7 +48,7 @@ def update(self, CC, CS, now_nanos): CS.out.vEgoRaw, actual_steering_angle_deg, CC.latActive, - CarControllerParams.ANGLE_LIMITS + ANGLE_LIMITS ) if not CC.latActive: From e00ea8249d1593c71fd6300e171232c01ef15839 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 25 Oct 2025 15:03:24 -0500 Subject: [PATCH 19/46] Cleanup, add long messages, available... not now --- opendbc/car/subaru/interface.py | 1 - opendbc/car/subaru/values.py | 12 ------------ opendbc/car/tests/routes.py | 2 +- opendbc/safety/modes/subaru.h | 12 ++++++++++-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index d7673ca257b..a940a0a5856 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -42,7 +42,6 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp else: CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - # Ensure panda safety mode is configured for LKAS angle platforms if ret.flags & SubaruFlags.LKAS_ANGLE: ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 6d2bcefeab2..183ee607e29 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -2,7 +2,6 @@ from enum import Enum, IntFlag from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds -from opendbc.car.lateral import AngleSteeringLimits from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -11,12 +10,6 @@ class CarControllerParams: - ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( - 100, - ([0., 15., 15.], [5., .8, .8]), - ([0., 15., 15.], [5., .4, .4]), - ) - 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 @@ -168,11 +161,6 @@ class CAR(Platforms): CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17), flags=SubaruFlags.HYBRID, ) - # SUBARU_CROSSTREK_GEN_3 = SubaruPlatformConfig( - # [SubaruCarDocs("Subaru Crosstrek 2024-25", car_parts=CarParts.common([CarHarness.subaru_d]))], - # , - # flags=SubaruFlags.LKAS_ANGLE, - # ) SUBARU_FORESTER = SubaruPlatformConfig( [SubaruCarDocs("Subaru Forester 2019-21", "All")], CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17), diff --git a/opendbc/car/tests/routes.py b/opendbc/car/tests/routes.py index 6e512dbcc09..6d91d309d31 100644 --- a/opendbc/car/tests/routes.py +++ b/opendbc/car/tests/routes.py @@ -299,7 +299,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("1bbe6bf2d62f58a8/2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10), CarTestRoute("c56e69bbc74b8fad/2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3), CarTestRoute("f4e3a0c511a076f4/2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2), - CarTestRoute("38b065e31c0a9ed7/00000178--0287f77ede", SUBARU.SUBARU_CROSSTREK_2025, segment=1), + CarTestRoute("38b065e31c0a9ed7/00000001--5656806473", SUBARU.SUBARU_CROSSTREK_2025, segment=2), CarTestRoute("7fd1e4f3a33c1673/2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4), CarTestRoute("f3b34c0d2632aa83/2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7), CarTestRoute("99437cef6d5ff2ee/2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7), diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index d5a99fe4362..e354a317c70 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -271,11 +271,18 @@ static safety_config subaru_init(uint16_t param) { SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() }; - static const CanMsg subaru_lkas_angle_tx_msgs[] = { + static const CanMsg SUBARU_LKAS_ANGLE_TX_MSGS[] = { SUBARU_BASE_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS_ANGLE) SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS) }; + static const CanMsg SUBARU_LKAS_ANGLE_LONG_TX_MSGS[] = { + SUBARU_BASE_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS_ANGLE) // lat + SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS) + SUBARU_COMMON_LONG_TX_MSGS(SUBARU_ALT_BUS) // long + SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() + }; + static RxCheck subaru_rx_checks[] = { SUBARU_COMMON_RX_CHECKS(SUBARU_MAIN_BUS) }; @@ -301,7 +308,8 @@ static safety_config subaru_init(uint16_t param) { safety_config ret; if (subaru_lkas_angle) { - ret = BUILD_SAFETY_CFG(subaru_lkas_angle_rx_checks, subaru_lkas_angle_tx_msgs); + ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_lkas_angle_rx_checks, SUBARU_LKAS_ANGLE_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(subaru_lkas_angle_rx_checks, SUBARU_LKAS_ANGLE_TX_MSGS); } else if (subaru_gen2) { ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_LONG_TX_MSGS) : \ BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS); From a5b1f73fd31b5c32176be38492a458760c96b912 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 25 Oct 2025 22:29:33 -0500 Subject: [PATCH 20/46] Clean up the looks of angle limits + reduce limits for car controller to reduce chance of faults --- opendbc/car/subaru/carcontroller.py | 4 ++-- opendbc/safety/modes/subaru.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index dd777591070..7c52c929610 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -13,8 +13,8 @@ ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( 100, - ([0., 15., 15.], [5., .8, .8]), - ([0., 15., 15.], [5., .4, .4]), + ([0., 5., 15.], [4., .6, .1,]), + ([0., 5., 15.], [4., .3, .1,]), ) class CarController(CarControllerBase): diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index e354a317c70..9cfed6a9678 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -165,12 +165,12 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { .max_angle = 545*100, .angle_deg_to_can = 100., .angle_rate_up_lookup = { - {0., 15., 15.}, - {5., .8, .8} + {0.0, 5.0, 15.0}, + {5.0, 0.8, 0.15} }, .angle_rate_down_lookup = { - {0., 15., 15.}, - {5., .4, .4} + {0.0, 5.0, 15.0}, + {5.0, 0.4, 0.15} }, }; From 7b4bf17f57d61e425484594614f228ba9fc77d37 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sun, 26 Oct 2025 12:40:25 -0500 Subject: [PATCH 21/46] Set limits to be less horizontal at normal driving speeds --- opendbc/car/subaru/carcontroller.py | 4 ++-- opendbc/safety/modes/subaru.h | 4 ++-- opendbc/safety/tests/test_subaru.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 7c52c929610..b7ccfeca197 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -13,8 +13,8 @@ ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( 100, - ([0., 5., 15.], [4., .6, .1,]), - ([0., 5., 15.], [4., .3, .1,]), + ([0., 5., 35.], [5., .8, .15,]), + ([0., 5., 35.], [5., .4, .15,]), ) class CarController(CarControllerBase): diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 9cfed6a9678..b456a7daadf 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -165,11 +165,11 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { .max_angle = 545*100, .angle_deg_to_can = 100., .angle_rate_up_lookup = { - {0.0, 5.0, 15.0}, + {0.0, 5.0, 35.0}, {5.0, 0.8, 0.15} }, .angle_rate_down_lookup = { - {0.0, 5.0, 15.0}, + {0.0, 5.0, 35.0}, {5.0, 0.4, 0.15} }, }; diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index b1ce3bbf827..ff17da96f5e 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -185,9 +185,9 @@ class TestSubaruAngleSafetyBase(TestSubaruSafetyBase, common.AngleSteeringSafety STEER_ANGLE_MAX = 545 # Avoid overflow of ES_LKAS_ANGLE's 17-bit signed field (0.01 deg resolution): limit test angles STEER_ANGLE_TEST_MAX = 545 - ANGLE_RATE_BP = [0, 15, 15] - ANGLE_RATE_UP = [5, 0.15, 0.15] - ANGLE_RATE_DOWN = [5, 0.4, 0.4] + ANGLE_RATE_BP = [0, 5, 35] + ANGLE_RATE_UP = [5, 0.8, 0.15] + ANGLE_RATE_DOWN = [5, 0.4, 0.15] def _angle_cmd_msg(self, angle, enabled=1): values = {"LKAS_Output": angle, "LKAS_Request": enabled, "SET_3": 3} From 167361ced8feca1a7bc20f82df9623cc899ccb53 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sun, 26 Oct 2025 15:47:57 -0500 Subject: [PATCH 22/46] Make car controller slightly more restrictive with angle rates --- opendbc/car/lateral.py | 17 +++++++++++++++++ opendbc/car/subaru/carcontroller.py | 10 ++-------- opendbc/car/subaru/values.py | 9 +++++++++ opendbc/safety/modes/subaru.h | 2 +- opendbc/safety/tests/test_subaru.py | 2 +- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/opendbc/car/lateral.py b/opendbc/car/lateral.py index 9d120e308b7..60f0b4d02a2 100644 --- a/opendbc/car/lateral.py +++ b/opendbc/car/lateral.py @@ -23,6 +23,23 @@ class AngleSteeringLimits: MAX_LATERAL_JERK: float = 0 MAX_ANGLE_RATE: float = math.inf +def make_limits_slightly_more_restrictive(limits: AngleSteeringLimits): + # Reduce angle limits slightly to provide a buffer to avoid safety faults + new_up_rates = ( + limits.ANGLE_RATE_LIMIT_UP[0], + [x * 0.99 for x in limits.ANGLE_RATE_LIMIT_UP[1]] +) + + new_down_rates = ( + limits.ANGLE_RATE_LIMIT_DOWN[0], + [x * 0.99 for x in limits.ANGLE_RATE_LIMIT_DOWN[1]] + ) + + return AngleSteeringLimits( + STEER_ANGLE_MAX=limits.STEER_ANGLE_MAX, + ANGLE_RATE_LIMIT_UP=new_up_rates, + ANGLE_RATE_LIMIT_DOWN=new_down_rates, + ) def apply_driver_steer_torque_limits(apply_torque: int, apply_torque_last: int, driver_torque: float, LIMITS, steer_max: int = None): # some safety modes utilize a dynamic max steer diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index b7ccfeca197..203b6a30053 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -1,7 +1,7 @@ import numpy as np from opendbc.can import CANPacker from opendbc.car import Bus, make_tester_present_msg -from opendbc.car.lateral import AngleSteeringLimits, apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits +from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance, apply_std_steer_angle_limits from opendbc.car.interfaces import CarControllerBase from opendbc.car.subaru import subarucan from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags @@ -11,12 +11,6 @@ MAX_STEER_RATE = 25 # deg/s MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut -ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( - 100, - ([0., 5., 35.], [5., .8, .15,]), - ([0., 5., 35.], [5., .4, .15,]), - ) - class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) @@ -48,7 +42,7 @@ def update(self, CC, CS, now_nanos): CS.out.vEgoRaw, actual_steering_angle_deg, CC.latActive, - ANGLE_LIMITS + self.p.ANGLE_LIMITS ) if not CC.latActive: diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 183ee607e29..2aeeda064c1 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -2,6 +2,7 @@ from enum import Enum, IntFlag from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds +from opendbc.car.lateral import AngleSteeringLimits, make_limits_slightly_more_restrictive from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -18,6 +19,14 @@ def __init__(self, CP): self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily self.STEER_DRIVER_FACTOR = 1 # from dbc + unrestricted_angle_limits: AngleSteeringLimits = AngleSteeringLimits( + 100, + ([0., 5., 35.], [5., .8, .15,]), + ([0., 5., 35.], [5., .8, .15,]), + ) + + self.ANGLE_LIMITS = make_limits_slightly_more_restrictive(unrestricted_angle_limits) + if CP.flags & SubaruFlags.GLOBAL_GEN2: # TODO: lower rate limits, this reaches min/max in 0.5s which negatively affects tuning self.STEER_MAX = 1000 diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index b456a7daadf..b6aa6a9b614 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -170,7 +170,7 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { }, .angle_rate_down_lookup = { {0.0, 5.0, 35.0}, - {5.0, 0.4, 0.15} + {5.0, 0.8, 0.15} }, }; diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index ff17da96f5e..76200f01d4f 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -187,7 +187,7 @@ class TestSubaruAngleSafetyBase(TestSubaruSafetyBase, common.AngleSteeringSafety STEER_ANGLE_TEST_MAX = 545 ANGLE_RATE_BP = [0, 5, 35] ANGLE_RATE_UP = [5, 0.8, 0.15] - ANGLE_RATE_DOWN = [5, 0.4, 0.15] + ANGLE_RATE_DOWN = [5, 0.8, 0.15] def _angle_cmd_msg(self, angle, enabled=1): values = {"LKAS_Output": angle, "LKAS_Request": enabled, "SET_3": 3} From 62fa9c4e640b2e04919851a7945891656dbf04fd Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Mon, 27 Oct 2025 20:35:04 -0500 Subject: [PATCH 23/46] Remove slight angle limit restrictions and cleanup unused function in lateral.py --- opendbc/car/lateral.py | 18 ------------------ opendbc/car/subaru/values.py | 8 +++----- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/opendbc/car/lateral.py b/opendbc/car/lateral.py index 60f0b4d02a2..0a4b46652c8 100644 --- a/opendbc/car/lateral.py +++ b/opendbc/car/lateral.py @@ -23,24 +23,6 @@ class AngleSteeringLimits: MAX_LATERAL_JERK: float = 0 MAX_ANGLE_RATE: float = math.inf -def make_limits_slightly_more_restrictive(limits: AngleSteeringLimits): - # Reduce angle limits slightly to provide a buffer to avoid safety faults - new_up_rates = ( - limits.ANGLE_RATE_LIMIT_UP[0], - [x * 0.99 for x in limits.ANGLE_RATE_LIMIT_UP[1]] -) - - new_down_rates = ( - limits.ANGLE_RATE_LIMIT_DOWN[0], - [x * 0.99 for x in limits.ANGLE_RATE_LIMIT_DOWN[1]] - ) - - return AngleSteeringLimits( - STEER_ANGLE_MAX=limits.STEER_ANGLE_MAX, - ANGLE_RATE_LIMIT_UP=new_up_rates, - ANGLE_RATE_LIMIT_DOWN=new_down_rates, - ) - def apply_driver_steer_torque_limits(apply_torque: int, apply_torque_last: int, driver_torque: float, LIMITS, steer_max: int = None): # some safety modes utilize a dynamic max steer if steer_max is None: diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index 2aeeda064c1..afa7f6e65eb 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -2,7 +2,7 @@ from enum import Enum, IntFlag from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds -from opendbc.car.lateral import AngleSteeringLimits, make_limits_slightly_more_restrictive +from opendbc.car.lateral import AngleSteeringLimits from opendbc.car.structs import CarParams from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 @@ -19,14 +19,12 @@ def __init__(self, CP): self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily self.STEER_DRIVER_FACTOR = 1 # from dbc - unrestricted_angle_limits: AngleSteeringLimits = AngleSteeringLimits( - 100, + self.ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( + 545, ([0., 5., 35.], [5., .8, .15,]), ([0., 5., 35.], [5., .8, .15,]), ) - self.ANGLE_LIMITS = make_limits_slightly_more_restrictive(unrestricted_angle_limits) - if CP.flags & SubaruFlags.GLOBAL_GEN2: # TODO: lower rate limits, this reaches min/max in 0.5s which negatively affects tuning self.STEER_MAX = 1000 From 32074600e4dc1e53ded60b8c5bae89a78cc7e25e Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 29 Oct 2025 16:17:53 -0500 Subject: [PATCH 24/46] don't send torque and old angle message for angle-lkas --- opendbc/safety/modes/subaru.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index b6aa6a9b614..d9791580512 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -109,20 +109,21 @@ static void subaru_rx_hook(const CANPacket_t *msg) { raw &= 0x1FFFFU; int angle_meas_new = ROUND(to_signed(raw, 17) * -1); update_sample(&angle_meas, angle_meas_new); - } else { - if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { - int torque_driver_new; - torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); - torque_driver_new = -1 * to_signed(torque_driver_new, 11); - update_sample(&torque_driver, torque_driver_new); - - int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); - // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units - angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); - update_sample(&angle_meas, angle_meas_new); - } } + if (!subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { + int torque_driver_new; + torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); + torque_driver_new = -1 * to_signed(torque_driver_new, 11); + update_sample(&torque_driver, torque_driver_new); + + int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); + // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units + angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); + update_sample(&angle_meas, angle_meas_new); + } + + // enter controls on rising edge of ACC, exit controls on ACC off if (subaru_lkas_angle) { if ((msg->addr == MSG_SUBARU_ES_DashStatus) && (msg->bus == SUBARU_CAM_BUS)) { From f8f1ff086e56ead1c2acba7774cb52436ff38cd2 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 29 Oct 2025 19:33:26 -0500 Subject: [PATCH 25/46] Update route --- opendbc/car/tests/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc/car/tests/routes.py b/opendbc/car/tests/routes.py index c7a71dcfd2e..a321c1bae80 100644 --- a/opendbc/car/tests/routes.py +++ b/opendbc/car/tests/routes.py @@ -299,7 +299,7 @@ class CarTestRoute(NamedTuple): CarTestRoute("1bbe6bf2d62f58a8/2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10), CarTestRoute("c56e69bbc74b8fad/2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3), CarTestRoute("f4e3a0c511a076f4/2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2), - CarTestRoute("38b065e31c0a9ed7/00000001--5656806473", SUBARU.SUBARU_CROSSTREK_2025, segment=2), + CarTestRoute("38b065e31c0a9ed7/0000000b--eab0d07145", SUBARU.SUBARU_CROSSTREK_2025, segment=39), CarTestRoute("7fd1e4f3a33c1673/2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4), CarTestRoute("f3b34c0d2632aa83/2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7), CarTestRoute("99437cef6d5ff2ee/2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7), From 9a81668774c86c922c469345c528a4642f2ae1dc Mon Sep 17 00:00:00 2001 From: jacobwaller Date: Tue, 11 Nov 2025 08:11:20 +0000 Subject: [PATCH 26/46] docs: Scheduled auto-update CARS.md --- docs/CARS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CARS.md b/docs/CARS.md index 75cf2a719d3..607e45835d4 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -1,6 +1,6 @@ -# Support Information for 384 Known Cars +# Support Information for 385 Known Cars |Make|Model|Package|Support Level| |---|---|---|:---:| From 8b31db1d51f8c24f891bf069969fbd1b6e272150 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 3 Dec 2025 11:56:18 -0600 Subject: [PATCH 27/46] Respond to PR comments --- .gitignore | 1 - opendbc/car/lateral.py | 1 + opendbc/car/subaru/carcontroller.py | 1 + opendbc/car/subaru/interface.py | 2 +- opendbc/safety/modes/subaru.h | 14 ++++++++++++-- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9de89119a25..5a595d00afa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.idea/ .cache/ /build/ .mypy_cache/ diff --git a/opendbc/car/lateral.py b/opendbc/car/lateral.py index a14b274ba5f..1f3706874a0 100644 --- a/opendbc/car/lateral.py +++ b/opendbc/car/lateral.py @@ -23,6 +23,7 @@ class AngleSteeringLimits: MAX_LATERAL_JERK: float = 0 MAX_ANGLE_RATE: float = math.inf + def apply_driver_steer_torque_limits(apply_torque: int, apply_torque_last: int, driver_torque: float, LIMITS, steer_max: int = None): # some safety modes utilize a dynamic max steer if steer_max is None: diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 203b6a30053..1f98ae23468 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -11,6 +11,7 @@ MAX_STEER_RATE = 25 # deg/s MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut + class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index a940a0a5856..0f598af86b2 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -69,7 +69,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp ret.steerActuatorDelay = 0.1 elif candidate == CAR.SUBARU_CROSSTREK_2025: - ret.dashcamOnly = False + ret.dashcamOnly = is_release ret.steerActuatorDelay = 0.3 elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID): diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index d9791580512..3782882af80 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -104,14 +104,17 @@ static uint32_t subaru_compute_checksum(const CANPacket_t *msg) { static void subaru_rx_hook(const CANPacket_t *msg) { const unsigned int alt_main_bus = (subaru_gen2 || subaru_lkas_angle) ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; + #ifdef ALLOW_DEBUG if (subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { uint32_t raw = GET_BYTES(msg, 3, 3); raw &= 0x1FFFFU; int angle_meas_new = ROUND(to_signed(raw, 17) * -1); update_sample(&angle_meas, angle_meas_new); } - - if (!subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { + + if (!subaru_lkas_angle) { + #endif // I don't like this, but I want to leave the following block completely unchanged until the comma team can get ahold of a subaru with LKAS_ANGLE + if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { int torque_driver_new; torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); torque_driver_new = -1 * to_signed(torque_driver_new, 11); @@ -122,6 +125,9 @@ static void subaru_rx_hook(const CANPacket_t *msg) { angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); update_sample(&angle_meas, angle_meas_new); } + #ifdef ALLOW_DEBUG + } + #endif // enter controls on rising edge of ACC, exit controls on ACC off @@ -162,6 +168,7 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { const TorqueSteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); const TorqueSteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); + #ifdef ALLOW_DEBUG const AngleSteeringLimits SUBARU_ANGLE_STEERING_LIMITS = { .max_angle = 545*100, .angle_deg_to_can = 100., @@ -174,6 +181,7 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { {5.0, 0.8, 0.15} }, }; + #endif const LongitudinalLimits SUBARU_LONG_LIMITS = { .min_gas = 808, // appears to be engine braking @@ -199,6 +207,7 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { violation |= steer_torque_cmd_checks(desired_torque, steer_req, limits); } + #ifdef ALLOW_DEBUG if (msg->addr == MSG_SUBARU_ES_LKAS_ANGLE) { int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; desired_angle = -1 * to_signed(desired_angle, 17); @@ -206,6 +215,7 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); } + #endif // check es_brake brake_pressure limits if (msg->addr == MSG_SUBARU_ES_Brake) { From 4fae595db613408ad073c9e83758e7faa66c4eea Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 3 Dec 2025 12:13:37 -0600 Subject: [PATCH 28/46] Rest of the comments --- opendbc/safety/modes/subaru.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 3782882af80..6667123e770 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -130,18 +130,22 @@ static void subaru_rx_hook(const CANPacket_t *msg) { #endif - // enter controls on rising edge of ACC, exit controls on ACC off + #ifdef ALLOW_DEBUG if (subaru_lkas_angle) { if ((msg->addr == MSG_SUBARU_ES_DashStatus) && (msg->bus == SUBARU_CAM_BUS)) { bool cruise_engaged = (msg->data[4] >> 4) & 1U; pcm_cruise_check(cruise_engaged); } } else { - if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { - bool cruise_engaged = (msg->data[5] >> 1) & 1U; - pcm_cruise_check(cruise_engaged); - } + #endif + // enter controls on rising edge of ACC, exit controls on ACC off + if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { + bool cruise_engaged = (msg->data[5] >> 1) & 1U; + pcm_cruise_check(cruise_engaged); + } + #ifdef ALLOW_DEBUG } + #endif // update vehicle moving with any non-zero wheel speed if ((msg->addr == MSG_SUBARU_Wheel_Speeds) && (msg->bus == alt_main_bus)) { @@ -307,14 +311,14 @@ static safety_config subaru_init(uint16_t param) { }; const uint16_t SUBARU_PARAM_GEN2 = 1; - const uint16_t SUBARU_PARAM_LKAS_ANGLE = 8; - subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2); - subaru_lkas_angle = GET_FLAG(param, SUBARU_PARAM_LKAS_ANGLE); - + #ifdef ALLOW_DEBUG const uint16_t SUBARU_PARAM_LONGITUDINAL = 2; subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL); + + const uint16_t SUBARU_PARAM_LKAS_ANGLE = 8; + subaru_lkas_angle = GET_FLAG(param, SUBARU_PARAM_LKAS_ANGLE); #endif safety_config ret; From 266e4eb92c0c2089f644708355d822a131b2bffd Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 10 Dec 2025 12:12:26 -0600 Subject: [PATCH 29/46] Update Crosstrek 2025 entry to include Dashcam mode --- docs/CARS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 1b1c8ff83d8..daec284d001 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -260,7 +260,7 @@ |Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)| -|Subaru|Crosstrek 2025|All|[Upstream](#upstream)| +|Subaru|Crosstrek 2025|All|[Dashcam mode](#dashcam)| |Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2019-21|All|[Upstream](#upstream)| @@ -445,4 +445,4 @@ Toyota, and the GM Global B platform. All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars -may one day be supported, but we have no immediate plans to support FlexRay. \ No newline at end of file +may one day be supported, but we have no immediate plans to support FlexRay. From 6fe0c03ee738cc179987e6b2aa198f65707303e2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 10 Dec 2025 10:31:12 -0800 Subject: [PATCH 30/46] lil cleanup --- opendbc/car/fingerprints.py | 1 - opendbc/car/subaru/interface.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/opendbc/car/fingerprints.py b/opendbc/car/fingerprints.py index c0ea3e2169b..206177f8ce7 100644 --- a/opendbc/car/fingerprints.py +++ b/opendbc/car/fingerprints.py @@ -259,7 +259,6 @@ def all_legacy_fingerprint_cars(): "SUBARU IMPREZA LIMITED 2019": SUBARU.SUBARU_IMPREZA, "SUBARU IMPREZA SPORT 2020": SUBARU.SUBARU_IMPREZA_2020, "SUBARU CROSSTREK HYBRID 2020": SUBARU.SUBARU_CROSSTREK_HYBRID, - "SUBARU CROSSTREK 2025": SUBARU.SUBARU_CROSSTREK_2025, "SUBARU FORESTER 2019": SUBARU.SUBARU_FORESTER, "SUBARU FORESTER HYBRID 2020": SUBARU.SUBARU_FORESTER_HYBRID, "SUBARU FORESTER 2017 - 2018": SUBARU.SUBARU_FORESTER_PREGLOBAL, diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 0f598af86b2..583a100e462 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -43,6 +43,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if ret.flags & SubaruFlags.LKAS_ANGLE: + ret.steerActuatorDelay = 0.3 ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023): @@ -68,10 +69,6 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_CROSSTREK_HYBRID: ret.steerActuatorDelay = 0.1 - elif candidate == CAR.SUBARU_CROSSTREK_2025: - ret.dashcamOnly = is_release - ret.steerActuatorDelay = 0.3 - elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID): ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.000038 From 0330288e30fd101cd2d6abb6abd78f373d7615d1 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 10 Dec 2025 16:43:46 -0600 Subject: [PATCH 31/46] Refactor dashcamOnly flag assignment Remove unnecessary flag check for dashcamOnly. --- opendbc/car/subaru/interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 583a100e462..55a2f40ec72 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -18,7 +18,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp # - replacement for ES_Distance so we can cancel the cruise control # - to find the Cruise_Activated bit from the car # - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) - ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID)) + ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID)) ret.autoResumeSng = False # Detect infotainment message sent from the camera @@ -45,6 +45,8 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp if ret.flags & SubaruFlags.LKAS_ANGLE: ret.steerActuatorDelay = 0.3 ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value + ret.dashcamOnly = is_release + if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023): ret.steerActuatorDelay = 0.3 # end-to-end angle controller From d5259649735de31d781aa648338fb6e3b5ff7333 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 10 Dec 2025 17:43:52 -0600 Subject: [PATCH 32/46] Shane's Comments --- opendbc/car/subaru/carcontroller.py | 23 ++++----- opendbc/car/subaru/fingerprints.py | 2 +- opendbc/car/subaru/interface.py | 3 +- opendbc/car/subaru/values.py | 6 +-- opendbc/safety/modes/subaru.h | 79 +++++++++++++---------------- 5 files changed, 48 insertions(+), 65 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 1f98ae23468..09eabeee5cd 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -16,7 +16,7 @@ class CarController(CarControllerBase): def __init__(self, dbc_names, CP): super().__init__(dbc_names, CP) self.apply_torque_last = 0 - self.apply_steer_last = 0 + self.apply_angle_last = 0 self.cruise_button_prev = 0 self.steer_rate_counter = 0 @@ -34,23 +34,20 @@ def update(self, CC, CS, now_nanos): # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: if self.CP.flags & SubaruFlags.LKAS_ANGLE: - actual_steering_angle_deg = CS.out.steeringAngleDeg - desired_steering_angle_deg = actuators.steeringAngleDeg - apply_steer = apply_std_steer_angle_limits( - desired_steering_angle_deg, - self.apply_steer_last, + actuators.steeringAngleDeg, + self.apply_angle_last, CS.out.vEgoRaw, - actual_steering_angle_deg, + CS.out.steeringAngleDeg, CC.latActive, self.p.ANGLE_LIMITS ) if not CC.latActive: - apply_steer = actual_steering_angle_deg + apply_steer = CS.out.steeringAngleDeg can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) - self.apply_steer_last = apply_steer + self.apply_angle_last = apply_steer else: apply_torque = int(round(actuators.torque * self.p.STEER_MAX)) @@ -155,11 +152,9 @@ def update(self, CC, CS, now_nanos): can_sends.append(subarucan.create_es_static_2(self.packer)) new_actuators = actuators.as_builder() - if self.CP.flags & SubaruFlags.LKAS_ANGLE: - new_actuators.steeringAngleDeg = self.apply_steer_last - else: - new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX - new_actuators.torqueOutputCan = self.apply_torque_last + new_actuators.steeringAngleDeg = self.apply_angle_last + new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX + new_actuators.torqueOutputCan = self.apply_torque_last self.frame += 1 return new_actuators, can_sends diff --git a/opendbc/car/subaru/fingerprints.py b/opendbc/car/subaru/fingerprints.py index 72a72c84292..dff0c274367 100644 --- a/opendbc/car/subaru/fingerprints.py +++ b/opendbc/car/subaru/fingerprints.py @@ -244,7 +244,7 @@ b'\xf4!`0\x07', ], }, - CAR.SUBARU_CROSSTREK_2025: { + CAR.SUBARU_CROSSTREK_2025: { (Ecu.abs, 0x7b0, None): [ b'\xa2 $\x17\x06', ], diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 55a2f40ec72..a77d54c7a2c 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -46,7 +46,6 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp ret.steerActuatorDelay = 0.3 ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value ret.dashcamOnly = is_release - if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023): ret.steerActuatorDelay = 0.3 # end-to-end angle controller @@ -87,7 +86,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_LEGACY_PREGLOBAL: ret.steerActuatorDelay = 0.15 - elif candidate == CAR.SUBARU_OUTBACK_PREGLOBAL: + elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL, CAR.SUBARU_CROSSTREK_2025): pass else: raise ValueError(f"unknown car: {candidate}") diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index afa7f6e65eb..e758aa077bf 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -20,9 +20,9 @@ def __init__(self, CP): self.STEER_DRIVER_FACTOR = 1 # from dbc self.ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits( - 545, - ([0., 5., 35.], [5., .8, .15,]), - ([0., 5., 35.], [5., .8, .15,]), + 545, + ([0., 5., 35.], [5., .8, .15,]), + ([0., 5., 35.], [5., .8, .15,]), ) if CP.flags & SubaruFlags.GLOBAL_GEN2: diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 6667123e770..a5c1a0be899 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -102,50 +102,42 @@ static uint32_t subaru_compute_checksum(const CANPacket_t *msg) { } static void subaru_rx_hook(const CANPacket_t *msg) { - const unsigned int alt_main_bus = (subaru_gen2 || subaru_lkas_angle) ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; - - #ifdef ALLOW_DEBUG - if (subaru_lkas_angle && (msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { - uint32_t raw = GET_BYTES(msg, 3, 3); - raw &= 0x1FFFFU; - int angle_meas_new = ROUND(to_signed(raw, 17) * -1); - update_sample(&angle_meas, angle_meas_new); - } - - if (!subaru_lkas_angle) { - #endif // I don't like this, but I want to leave the following block completely unchanged until the comma team can get ahold of a subaru with LKAS_ANGLE - if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { - int torque_driver_new; - torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); - torque_driver_new = -1 * to_signed(torque_driver_new, 11); - update_sample(&torque_driver, torque_driver_new); - - int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); - // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units - angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); - update_sample(&angle_meas, angle_meas_new); - } - #ifdef ALLOW_DEBUG + const unsigned int alt_main_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; + + if (subaru_lkas_angle) { + if ((msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { + uint32_t raw = GET_BYTES(msg, 3, 3); + raw &= 0x1FFFFU; + int angle_meas_new = to_signed(raw, 17) * -1; + update_sample(&angle_meas, angle_meas_new); + } + } else { + if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { + int torque_driver_new; + torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); + torque_driver_new = -1 * to_signed(torque_driver_new, 11); + update_sample(&torque_driver, torque_driver_new); + + int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU); + // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units + angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); + update_sample(&angle_meas, angle_meas_new); + } } - #endif - #ifdef ALLOW_DEBUG if (subaru_lkas_angle) { if ((msg->addr == MSG_SUBARU_ES_DashStatus) && (msg->bus == SUBARU_CAM_BUS)) { bool cruise_engaged = (msg->data[4] >> 4) & 1U; pcm_cruise_check(cruise_engaged); } } else { - #endif - // enter controls on rising edge of ACC, exit controls on ACC off - if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { - bool cruise_engaged = (msg->data[5] >> 1) & 1U; - pcm_cruise_check(cruise_engaged); - } - #ifdef ALLOW_DEBUG + // enter controls on rising edge of ACC, exit controls on ACC off + if ((msg->addr == MSG_SUBARU_CruiseControl) && (msg->bus == alt_main_bus)) { + bool cruise_engaged = (msg->data[5] >> 1) & 1U; + pcm_cruise_check(cruise_engaged); + } } - #endif // update vehicle moving with any non-zero wheel speed if ((msg->addr == MSG_SUBARU_Wheel_Speeds) && (msg->bus == alt_main_bus)) { @@ -171,8 +163,6 @@ static void subaru_rx_hook(const CANPacket_t *msg) { static bool subaru_tx_hook(const CANPacket_t *msg) { const TorqueSteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); const TorqueSteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); - - #ifdef ALLOW_DEBUG const AngleSteeringLimits SUBARU_ANGLE_STEERING_LIMITS = { .max_angle = 545*100, .angle_deg_to_can = 100., @@ -185,7 +175,6 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { {5.0, 0.8, 0.15} }, }; - #endif const LongitudinalLimits SUBARU_LONG_LIMITS = { .min_gas = 808, // appears to be engine braking @@ -211,15 +200,15 @@ static bool subaru_tx_hook(const CANPacket_t *msg) { violation |= steer_torque_cmd_checks(desired_torque, steer_req, limits); } - #ifdef ALLOW_DEBUG - if (msg->addr == MSG_SUBARU_ES_LKAS_ANGLE) { - int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; - desired_angle = -1 * to_signed(desired_angle, 17); - bool lkas_request = GET_BIT(msg, 12U); - - violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); + if (subaru_lkas_angle) { + if (msg->addr == MSG_SUBARU_ES_LKAS_ANGLE) { + int desired_angle = GET_BYTES(msg, 5, 3) & 0x1FFFFU; + desired_angle = -1 * to_signed(desired_angle, 17); + bool lkas_request = GET_BIT(msg, 12U); + + violation |= steer_angle_cmd_checks(desired_angle, lkas_request, SUBARU_ANGLE_STEERING_LIMITS); + } } - #endif // check es_brake brake_pressure limits if (msg->addr == MSG_SUBARU_ES_Brake) { From b276dfdb0863c9d035e7d59b4f396f24517b5101 Mon Sep 17 00:00:00 2001 From: jacobwaller Date: Thu, 11 Dec 2025 08:12:50 +0000 Subject: [PATCH 33/46] docs: Scheduled auto-update CARS.md --- docs/CARS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index daec284d001..69967b5c264 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -257,14 +257,14 @@ |SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)| |SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)| |Subaru|Ascent 2019-21|All|[Upstream](#upstream)| -|Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)| +|Subaru|Ascent 2023|All|[Upstream](#upstream)| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)| -|Subaru|Crosstrek 2025|All|[Dashcam mode](#dashcam)| +|Subaru|Crosstrek 2025|All|[Upstream](#upstream)| |Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2019-21|All|[Upstream](#upstream)| -|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)| +|Subaru|Forester 2022-24|All|[Upstream](#upstream)| |Subaru|Forester Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Impreza 2017-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Impreza 2020-22|EyeSight Driver Assistance|[Upstream](#upstream)| @@ -273,7 +273,7 @@ |Subaru|Outback 2015-17|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Outback 2018-19|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Outback 2020-22|All|[Upstream](#upstream)| -|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)| +|Subaru|Outback 2023|All|[Upstream](#upstream)| |Subaru|Solterra 2023-25|Any|[Not compatible](#can-bus-security)| |Subaru|XV 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|XV 2020-21|EyeSight Driver Assistance|[Upstream](#upstream)| @@ -445,4 +445,4 @@ Toyota, and the GM Global B platform. All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars -may one day be supported, but we have no immediate plans to support FlexRay. +may one day be supported, but we have no immediate plans to support FlexRay. \ No newline at end of file From 9f33e18f215c147428e994a20c41323b838bd412 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 28 Jan 2026 11:20:11 -0600 Subject: [PATCH 34/46] Fix the tests --- opendbc/car/subaru/interface.py | 3 +++ opendbc/safety/modes/subaru.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index a77d54c7a2c..74c95227995 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -88,6 +88,9 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL, CAR.SUBARU_CROSSTREK_2025): pass + + elif candidate == CAR.SUBARU_FORESTER_2022: + ret.dashcamOnly = True else: raise ValueError(f"unknown car: {candidate}") diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index a5c1a0be899..4abac448581 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -108,7 +108,7 @@ static void subaru_rx_hook(const CANPacket_t *msg) { if ((msg->addr == MSG_SUBARU_Steering_2) && (msg->bus == SUBARU_MAIN_BUS)) { uint32_t raw = GET_BYTES(msg, 3, 3); raw &= 0x1FFFFU; - int angle_meas_new = to_signed(raw, 17) * -1; + int angle_meas_new = -to_signed(raw, 17); update_sample(&angle_meas, angle_meas_new); } } else { From f221b45902c19fd0533b862f6a6bd119fc7969a7 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Wed, 28 Jan 2026 11:30:59 -0600 Subject: [PATCH 35/46] Should not allow forester --- opendbc/car/subaru/interface.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 74c95227995..00c10c8ff12 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -70,7 +70,10 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_CROSSTREK_HYBRID: ret.steerActuatorDelay = 0.1 - elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID): + elif candidate == CAR.SUBARU_FORESTER_2022: + ret.dashcamOnly = True + + elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_HYBRID): ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.000038 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] @@ -89,8 +92,6 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL, CAR.SUBARU_CROSSTREK_2025): pass - elif candidate == CAR.SUBARU_FORESTER_2022: - ret.dashcamOnly = True else: raise ValueError(f"unknown car: {candidate}") From 9106cd271898c97894ef585add30894e2edc1b41 Mon Sep 17 00:00:00 2001 From: jacobwaller Date: Thu, 29 Jan 2026 08:18:26 +0000 Subject: [PATCH 36/46] docs: Scheduled auto-update CARS.md --- docs/CARS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 080a323c44b..3a385280bf9 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -1,6 +1,6 @@ -# Support Information for 387 Known Cars +# Support Information for 388 Known Cars |Make|Model|Package|Support Level| |---|---|---|:---:| @@ -260,14 +260,14 @@ |SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)| |SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)| |Subaru|Ascent 2019-21|All|[Upstream](#upstream)| -|Subaru|Ascent 2023|All|[Upstream](#upstream)| +|Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)| -|Subaru|Crosstrek 2025|All|[Upstream](#upstream)| +|Subaru|Crosstrek 2025|All|[Dashcam mode](#dashcam)| |Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Forester 2019-21|All|[Upstream](#upstream)| -|Subaru|Forester 2022-24|All|[Upstream](#upstream)| +|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)| |Subaru|Forester Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)| |Subaru|Impreza 2017-19|EyeSight Driver Assistance|[Upstream](#upstream)| |Subaru|Impreza 2020-22|EyeSight Driver Assistance|[Upstream](#upstream)| From 9a727f5c058ff73b42e500ec3ad0d37d05573bb9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 30 Jan 2026 19:23:07 -0800 Subject: [PATCH 37/46] spacing --- opendbc/safety/tests/test_subaru.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index 76200f01d4f..a90280ebccc 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -207,6 +207,7 @@ def _pcm_status_msg(self, enable): values = {"Cruise_Activated": enable} return self.packer.make_can_msg_safety("ES_DashStatus", self.ALT_CAM_BUS, values) + class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): FLAGS = 0 TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) @@ -233,10 +234,12 @@ class TestSubaruGen1LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSub SubaruMsg.ES_Infotainment, SubaruMsg.ES_Brake, SubaruMsg.ES_Status, SubaruMsg.ES_Distance)} + class TestSubaruGen2AngleStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruAngleSafetyBase): ALT_MAIN_BUS = SUBARU_ALT_BUS FLAGS = SubaruSafetyFlags.GEN2 | SubaruSafetyFlags.LKAS_ANGLE + class TestSubaruGen2LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): FLAGS = SubaruSafetyFlags.LONG | SubaruSafetyFlags.GEN2 TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS) + long_tx_msgs(SUBARU_ALT_BUS) + gen2_long_additional_tx_msgs() From 8c7ffe29cbcf5f4ddca4faaf14eac8d4bac7a49e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 30 Jan 2026 20:56:10 -0800 Subject: [PATCH 38/46] simpler and comment --- opendbc/car/subaru/carstate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opendbc/car/subaru/carstate.py b/opendbc/car/subaru/carstate.py index f0d663e0c70..eeeaf611423 100644 --- a/opendbc/car/subaru/carstate.py +++ b/opendbc/car/subaru/carstate.py @@ -81,7 +81,8 @@ def update(self, can_parsers) -> structs.CarState: ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold cp_cruise = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp - if self.CP.flags & SubaruFlags.HYBRID or self.CP.flags & SubaruFlags.LKAS_ANGLE: + if self.CP.flags & (SubaruFlags.HYBRID | self.CP.flags & SubaruFlags.LKAS_ANGLE): + # These cars may have CruiseControl, but is always zeroed out ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0 ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0 else: From f55b9846e3309594304f9ca788894a1cba85c85c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 31 Jan 2026 02:01:27 -0800 Subject: [PATCH 39/46] who left all these platforms in here --- opendbc/car/subaru/interface.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 00c10c8ff12..6adcaa50c67 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -18,7 +18,15 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp # - replacement for ES_Distance so we can cancel the cruise control # - to find the Cruise_Activated bit from the car # - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) - ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID)) + # for LKAS_ANGLE CARS to be upstreamed, we need: + # - validate angle safety + ret.dashcamOnly = ((ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID)) or + ((ret.flags & SubaruFlags.LKAS_ANGLE) and is_release)) + + if candidate == CAR.SUBARU_FORESTER_2022: + # Gen 1 LKAS angle not tested, can undashcam if not release once we see a test route + ret.dashcamOnly = True + ret.autoResumeSng = False # Detect infotainment message sent from the camera @@ -37,17 +45,15 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp ret.steerLimitTimer = 0.4 ret.steerActuatorDelay = 0.1 - if ret.flags & SubaruFlags.LKAS_ANGLE: - ret.steerControlType = structs.CarParams.SteerControlType.angle - else: + if not (ret.flags & SubaruFlags.LKAS_ANGLE): CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if ret.flags & SubaruFlags.LKAS_ANGLE: ret.steerActuatorDelay = 0.3 + ret.steerControlType = structs.CarParams.SteerControlType.angle ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value - ret.dashcamOnly = is_release - if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023): + elif candidate in (CAR.SUBARU_ASCENT,): ret.steerActuatorDelay = 0.3 # end-to-end angle controller ret.lateralTuning.init('pid') ret.lateralTuning.pid.kf = 0.00003 @@ -70,16 +76,13 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_CROSSTREK_HYBRID: ret.steerActuatorDelay = 0.1 - elif candidate == CAR.SUBARU_FORESTER_2022: - ret.dashcamOnly = True - elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_HYBRID): 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.SUBARU_OUTBACK, CAR.SUBARU_LEGACY, CAR.SUBARU_OUTBACK_2023): + elif candidate in (CAR.SUBARU_OUTBACK, CAR.SUBARU_LEGACY,): ret.steerActuatorDelay = 0.1 elif candidate in (CAR.SUBARU_FORESTER_PREGLOBAL, CAR.SUBARU_OUTBACK_PREGLOBAL_2018): @@ -89,7 +92,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_LEGACY_PREGLOBAL: ret.steerActuatorDelay = 0.15 - elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL, CAR.SUBARU_CROSSTREK_2025): + elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL,): pass else: From 6435db853f0ab9139692539122b854c36e6f06f0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 31 Jan 2026 02:09:50 -0800 Subject: [PATCH 40/46] rev --- opendbc/car/subaru/interface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index ab8f26a3c84..79805192129 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -91,9 +91,8 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp elif candidate == CAR.SUBARU_LEGACY_PREGLOBAL: ret.steerActuatorDelay = 0.15 - elif candidate in (CAR.SUBARU_OUTBACK_PREGLOBAL,): + elif candidate == CAR.SUBARU_OUTBACK_PREGLOBAL: pass - else: raise ValueError(f"unknown car: {candidate}") From 5f8072e50294572d3340a5bdf5f83456dec95a02 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 31 Jan 2026 12:06:49 -0600 Subject: [PATCH 41/46] Refactor Subaru car controller and interface for improved steering control handling --- opendbc/car/subaru/carcontroller.py | 74 ++++++++++++++++------------- opendbc/car/subaru/interface.py | 2 +- opendbc/safety/modes/subaru.h | 29 +++++------ opendbc/safety/tests/test_subaru.py | 11 +++-- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/opendbc/car/subaru/carcontroller.py b/opendbc/car/subaru/carcontroller.py index 09eabeee5cd..52859c4c6db 100644 --- a/opendbc/car/subaru/carcontroller.py +++ b/opendbc/car/subaru/carcontroller.py @@ -24,18 +24,9 @@ def __init__(self, dbc_names, CP): self.p = CarControllerParams(CP) self.packer = CANPacker(DBC[CP.carFingerprint][Bus.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: - if self.CP.flags & SubaruFlags.LKAS_ANGLE: - apply_steer = apply_std_steer_angle_limits( - actuators.steeringAngleDeg, + def handle_angle_lateral(self, CC, CS): + apply_steer = apply_std_steer_angle_limits( + CC.actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg, @@ -43,34 +34,51 @@ def update(self, CC, CS, now_nanos): self.p.ANGLE_LIMITS ) - if not CC.latActive: - apply_steer = CS.out.steeringAngleDeg + if not CC.latActive: + apply_steer = CS.out.steeringAngleDeg - can_sends.append(subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive)) - self.apply_angle_last = apply_steer - else: - apply_torque = int(round(actuators.torque * self.p.STEER_MAX)) + self.apply_angle_last = apply_steer + return subarucan.create_steering_control_angle(self.packer, apply_steer, CC.latActive) - new_torque = int(round(apply_torque)) - apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.p) + def handle_torque_lateral(self, CC, CS): + apply_torque = int(round(CC.actuators.torque * self.p.STEER_MAX)) - if not CC.latActive: - apply_torque = 0 + new_torque = int(round(apply_torque)) + apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.p) - if self.CP.flags & SubaruFlags.PREGLOBAL: - can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive)) - else: - apply_steer_req = CC.latActive + if not CC.latActive: + apply_torque = 0 - if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED: - # Steering rate fault prevention - self.steer_rate_counter, apply_steer_req = \ - common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, - self.steer_rate_counter, MAX_STEER_RATE_FRAMES) + msg = None + if self.CP.flags & SubaruFlags.PREGLOBAL: + msg = subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive) + else: + apply_steer_req = CC.latActive - can_sends.append(subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req)) + if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED: + # Steering rate fault prevention + self.steer_rate_counter, apply_steer_req = \ + common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req, + self.steer_rate_counter, MAX_STEER_RATE_FRAMES) - self.apply_torque_last = apply_torque + msg = subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req) + + self.apply_torque_last = apply_torque + return msg + + 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: + if self.CP.flags & SubaruFlags.LKAS_ANGLE: + can_sends.append(self.handle_angle_lateral(CC, CS)) + else: + can_sends.append(self.handle_torque_lateral(CC, CS)) # *** longitudinal *** diff --git a/opendbc/car/subaru/interface.py b/opendbc/car/subaru/interface.py index 79805192129..96bfd36b767 100644 --- a/opendbc/car/subaru/interface.py +++ b/opendbc/car/subaru/interface.py @@ -20,7 +20,7 @@ def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alp # - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc) # for LKAS_ANGLE CARS to be upstreamed, we need: # - validate angle safety - ret.dashcamOnly = ((ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID)) or + ret.dashcamOnly = bool((ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID)) or ((ret.flags & SubaruFlags.LKAS_ANGLE) and is_release)) if candidate == CAR.SUBARU_FORESTER_2022: diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index f3f828644a2..a9cc0f6ff80 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -37,9 +37,9 @@ #define MSG_SUBARU_ES_UDS_Request 0x787U -#define MSG_SUBARU_ES_HighBeamAssist 0x121U -#define MSG_SUBARU_ES_STATIC_1 0x22aU -#define MSG_SUBARU_ES_STATIC_2 0x325U +#define MSG_SUBARU_ES_HighBeamAssist 0x22AU +#define MSG_SUBARU_ES_STATIC_1 0x325U +#define MSG_SUBARU_ES_STATIC_2 0x121U #define SUBARU_MAIN_BUS 0U #define SUBARU_ALT_BUS 1U @@ -77,7 +77,8 @@ {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_ES_Brake, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_ES_DashStatus, alt_bus, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true }, { 0 }, { 0 }}}, \ static bool subaru_gen2 = false; @@ -111,19 +112,19 @@ static void subaru_rx_hook(const CANPacket_t *msg) { int angle_meas_new = -to_signed(raw, 17); update_sample(&angle_meas, angle_meas_new); } - } else { - if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { - int torque_driver_new; - torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); - torque_driver_new = -1 * to_signed(torque_driver_new, 11); - update_sample(&torque_driver, torque_driver_new); - } + } + + // non-zero, even for Angle-LKAS cars + if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == SUBARU_MAIN_BUS)) { + int torque_driver_new; + torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU); + torque_driver_new = -1 * to_signed(torque_driver_new, 11); + update_sample(&torque_driver, torque_driver_new); } - if (subaru_lkas_angle) { - if ((msg->addr == MSG_SUBARU_ES_DashStatus) && (msg->bus == SUBARU_CAM_BUS)) { - bool cruise_engaged = (msg->data[4] >> 4) & 1U; + if ((msg->addr == MSG_SUBARU_ES_Brake) && (msg->bus == SUBARU_ALT_BUS)) { + bool cruise_engaged = (msg->data[4] >> 7) & 1U; pcm_cruise_check(cruise_engaged); } } else { diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index 6cceb253a7e..73ddff59aab 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -25,9 +25,9 @@ class SubaruMsg(enum.IntEnum): ES_LKAS_State = 0x322 ES_Infotainment = 0x323 ES_UDS_Request = 0x787 - ES_HighBeamAssist = 0x121 - ES_STATIC_1 = 0x22a - ES_STATIC_2 = 0x325 + ES_HighBeamAssist = 0x22A + ES_STATIC_1 = 0x325 + ES_STATIC_2 = 0x121 SUBARU_MAIN_BUS = 0 @@ -170,6 +170,7 @@ def _torque_cmd_msg(self, torque, steer_req=1): class TestSubaruAngleSafetyBase(TestSubaruSafetyBase, common.AngleSteeringSafetyTest): ALT_MAIN_BUS = SUBARU_ALT_BUS + ALT_CAM_BUS = SUBARU_ALT_BUS TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS, SubaruMsg.ES_LKAS_ANGLE) RELAY_MALFUNCTION_ADDRS = {SUBARU_MAIN_BUS: (SubaruMsg.ES_LKAS_ANGLE, SubaruMsg.ES_DashStatus, @@ -198,10 +199,10 @@ def _speed_msg(self, speed): values = {s: speed * 3.6 for s in ["FR", "FL", "RR", "RL"]} return self.packer.make_can_msg_safety("Wheel_Speeds", self.ALT_MAIN_BUS, values) - # need to use ES_DashStatus Message + # need to use ES_Brake Message def _pcm_status_msg(self, enable): values = {"Cruise_Activated": enable} - return self.packer.make_can_msg_safety("ES_DashStatus", self.ALT_CAM_BUS, values) + return self.packer.make_can_msg_safety("ES_Brake", self.ALT_CAM_BUS, values) class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): From a1bfcf6d3023d1c9cfdb728600b2abf338d93a20 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 31 Jan 2026 12:25:11 -0600 Subject: [PATCH 42/46] MSG_SUBARU_ES_DashStatus actually on bus 2 --- opendbc/safety/modes/subaru.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index a9cc0f6ff80..75e2a507dda 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -78,7 +78,7 @@ {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_ES_Brake, alt_bus, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_ES_DashStatus, alt_bus, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true }, { 0 }, { 0 }}}, \ static bool subaru_gen2 = false; From 0bf83a17a041633c9fcd5926ff56296874047668 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 31 Jan 2026 23:02:47 -0600 Subject: [PATCH 43/46] rvt rotate of messages --- opendbc/safety/modes/subaru.h | 6 +++--- opendbc/safety/tests/test_subaru.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opendbc/safety/modes/subaru.h b/opendbc/safety/modes/subaru.h index 75e2a507dda..488acb0148c 100644 --- a/opendbc/safety/modes/subaru.h +++ b/opendbc/safety/modes/subaru.h @@ -37,9 +37,9 @@ #define MSG_SUBARU_ES_UDS_Request 0x787U -#define MSG_SUBARU_ES_HighBeamAssist 0x22AU -#define MSG_SUBARU_ES_STATIC_1 0x325U -#define MSG_SUBARU_ES_STATIC_2 0x121U +#define MSG_SUBARU_ES_HighBeamAssist 0x121U +#define MSG_SUBARU_ES_STATIC_1 0x22aU +#define MSG_SUBARU_ES_STATIC_2 0x325U #define SUBARU_MAIN_BUS 0U #define SUBARU_ALT_BUS 1U diff --git a/opendbc/safety/tests/test_subaru.py b/opendbc/safety/tests/test_subaru.py index 73ddff59aab..29242b0e17a 100755 --- a/opendbc/safety/tests/test_subaru.py +++ b/opendbc/safety/tests/test_subaru.py @@ -25,9 +25,9 @@ class SubaruMsg(enum.IntEnum): ES_LKAS_State = 0x322 ES_Infotainment = 0x323 ES_UDS_Request = 0x787 - ES_HighBeamAssist = 0x22A - ES_STATIC_1 = 0x325 - ES_STATIC_2 = 0x121 + ES_HighBeamAssist = 0x121 + ES_STATIC_1 = 0x22a + ES_STATIC_2 = 0x325 SUBARU_MAIN_BUS = 0 From 251c56a5201ffec2b6ffaf8d5732d1547504d143 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 31 Jan 2026 23:10:18 -0600 Subject: [PATCH 44/46] Remove Subaru Crosstrek 2025 configuration from fingerprints and values --- opendbc/car/subaru/fingerprints.py | 14 -------------- opendbc/car/subaru/values.py | 5 ----- 2 files changed, 19 deletions(-) diff --git a/opendbc/car/subaru/fingerprints.py b/opendbc/car/subaru/fingerprints.py index 01026448edb..db73598005e 100644 --- a/opendbc/car/subaru/fingerprints.py +++ b/opendbc/car/subaru/fingerprints.py @@ -250,20 +250,6 @@ b'\xf4!`0\x07', ], }, - CAR.SUBARU_CROSSTREK_2025: { - (Ecu.abs, 0x7b0, None): [ - b'\xa2 $\x17\x06', - ], - (Ecu.eps, 0x746, None): [ - b'\xc2 $\x00\x01', - ], - (Ecu.fwdCamera, 0x787, None): [ - b'\x1d!\x08\x00F\x14!\x08\x00=', - ], - (Ecu.engine, 0x7a2, None): [ - b'\x04"cP\x07', - ], - }, CAR.SUBARU_FORESTER: { (Ecu.abs, 0x7b0, None): [ b'\xa3 \x18\x14\x00', diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py index e758aa077bf..7ba447dc1e4 100644 --- a/opendbc/car/subaru/values.py +++ b/opendbc/car/subaru/values.py @@ -219,11 +219,6 @@ class CAR(Platforms): SUBARU_ASCENT.specs, flags=SubaruFlags.LKAS_ANGLE, ) - SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig( - [SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))], - CarSpecs(mass=1529, wheelbase=2.5781, steerRatio=13.5), - flags=SubaruFlags.LKAS_ANGLE - ) SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ From 45a671ec9a0c6b7ccd78b87d6ff8839d0f3f9d98 Mon Sep 17 00:00:00 2001 From: Jacob Waller Date: Sat, 31 Jan 2026 23:21:34 -0600 Subject: [PATCH 45/46] port without platform --- opendbc/car/tests/routes.py | 1 - opendbc/car/torque_data/override.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/opendbc/car/tests/routes.py b/opendbc/car/tests/routes.py index 91d7f375577..1120026d006 100644 --- a/opendbc/car/tests/routes.py +++ b/opendbc/car/tests/routes.py @@ -311,7 +311,6 @@ class CarTestRoute(NamedTuple): CarTestRoute("1bbe6bf2d62f58a8/2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10), CarTestRoute("c56e69bbc74b8fad/2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3), CarTestRoute("f4e3a0c511a076f4/2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2), - CarTestRoute("38b065e31c0a9ed7/0000000b--eab0d07145", SUBARU.SUBARU_CROSSTREK_2025, segment=39), CarTestRoute("7fd1e4f3a33c1673/2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4), CarTestRoute("f3b34c0d2632aa83/2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7), CarTestRoute("99437cef6d5ff2ee/2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7), diff --git a/opendbc/car/torque_data/override.toml b/opendbc/car/torque_data/override.toml index 07d89da1f8d..d2a6ece905e 100644 --- a/opendbc/car/torque_data/override.toml +++ b/opendbc/car/torque_data/override.toml @@ -14,7 +14,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "SUBARU_FORESTER_2022" = [nan, 3.0, nan] "SUBARU_OUTBACK_2023" = [nan, 3.0, nan] "SUBARU_ASCENT_2023" = [nan, 3.0, nan] -"SUBARU_CROSSTREK_2025" = [nan, 3.0, nan] # Toyota LTA also has torque "TOYOTA_RAV4_TSS2_2023" = [nan, 3.0, nan] From 521735507f2ae1d7ee1d6fae95a85f3f77c3abec Mon Sep 17 00:00:00 2001 From: elkoled Date: Mon, 2 Feb 2026 15:29:34 -0800 Subject: [PATCH 46/46] remove unneeded changes --- docs/CARS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CARS.md b/docs/CARS.md index f9c72b667c4..848f20bde98 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -1,6 +1,6 @@ -# Support Information for 391 Known Cars +# Support Information for 390 Known Cars |Make|Model|Package|Support Level| |---|---|---|:---:|