From b6b820a275ad23c70266500d7d495e3c7e825fcb Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:26:01 -0500 Subject: [PATCH] Support shared Toyota pedal and brake hold paths --- .../bluepilot_lateral/safety/ford_bp.h | 9 +- .../opendbc/car/toyota/advert_filter.py | 5 + .../opendbc/car/toyota/camera_2aa_filter.py | 5 + .../opendbc/car/toyota/carcontroller.py | 21 +- opendbc_repo/opendbc/car/toyota/carstate.py | 16 +- opendbc_repo/opendbc/car/toyota/dsu_bypass.py | 5 + opendbc_repo/opendbc/car/toyota/eps8_sdsu.py | 5 + opendbc_repo/opendbc/car/toyota/interface.py | 22 +- .../car/toyota/legacy_advert_filter.py | 5 + .../opendbc/car/toyota/legacy_sdsu.py | 5 + opendbc_repo/opendbc/car/toyota/pedal.py | 2 + .../opendbc/car/toyota/pedal_composite.py | 144 ++++++++++-- .../opendbc/car/toyota/prius_advert_filter.py | 5 + .../opendbc/car/toyota/prius_longitudinal.py | 17 +- .../car/toyota/prius_retrofit_advert.py | 2 + opendbc_repo/opendbc/car/toyota/prius_sdsu.py | 5 + .../opendbc/car/toyota/radar_filter.py | 5 + opendbc_repo/opendbc/safety/modes/toyota.h | 94 +++++--- .../opendbc/safety/modes/toyota_aol.h | 25 +- .../safety/tests/test_toyota_pedal_parent.py | 46 ++++ .../pandad/tests/test_aol_protocol.cc | 2 +- openpilot/starpilot/car/toyota/aol.py | 11 + openpilot/starpilot/car/toyota/aol_policy.h | 2 +- .../toyota/tests/test_pedal_parent_callers.py | 159 +++++++++++++ .../tests/test_pedal_parent_configurations.py | 222 ++++++++++++++++++ .../toyota/tests/test_rav4h_caller_native.py | 30 ++- prebuilt.json | 2 +- tools/ci/run_vehicle_tests.py | 6 + 28 files changed, 785 insertions(+), 92 deletions(-) create mode 100644 opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py create mode 100644 openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py create mode 100644 openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py diff --git a/opendbc_repo/opendbc/bluepilot_lateral/safety/ford_bp.h b/opendbc_repo/opendbc/bluepilot_lateral/safety/ford_bp.h index d963b69d5e..cbebed36c1 100644 --- a/opendbc_repo/opendbc/bluepilot_lateral/safety/ford_bp.h +++ b/opendbc_repo/opendbc/bluepilot_lateral/safety/ford_bp.h @@ -66,8 +66,13 @@ static bool ford_bp_configure(uint16_t param) { static bool ford_bp_health(uint32_t now); static bool ford_bp_permission(void) { - return ford_aol_enabled ? ((ford_aol_permission_mask() & 1U) != 0U) : - (lateral_controls_allowed() && controls_allowed && !brake_pressed && !gas_pressed); + bool permitted = false; + if (ford_aol_enabled) { + permitted = (ford_aol_permission_mask() & 1U) != 0U; + } else { + permitted = lateral_controls_allowed() && controls_allowed && !brake_pressed && !gas_pressed; + } + return permitted; } static void ford_bp_rx(const CANPacket_t *msg) { diff --git a/opendbc_repo/opendbc/car/toyota/advert_filter.py b/opendbc_repo/opendbc/car/toyota/advert_filter.py index 2cb87fa3ce..338006fa1e 100644 --- a/opendbc_repo/opendbc/car/toyota/advert_filter.py +++ b/opendbc_repo/opendbc/car/toyota/advert_filter.py @@ -57,6 +57,8 @@ def apply(cp, *, camera_twoaa=False): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) from opendbc.car.toyota import camera_2aa_filter if camera_2aa_filter.configured(cp): return True @@ -94,6 +96,9 @@ def enabled(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) from opendbc.car.toyota import camera_2aa_filter if camera_2aa_filter.configured(cp): return camera_2aa_filter.prepare_stock(cp) diff --git a/opendbc_repo/opendbc/car/toyota/camera_2aa_filter.py b/opendbc_repo/opendbc/car/toyota/camera_2aa_filter.py index 3c05ae9765..e1c87b67bd 100644 --- a/opendbc_repo/opendbc/car/toyota/camera_2aa_filter.py +++ b/opendbc_repo/opendbc/car/toyota/camera_2aa_filter.py @@ -54,6 +54,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) try: if (cp is None or cp.brand != 'toyota' or cp.carFingerprint not in CARS or cp.notCar or cp.passive or cp.dashcamOnly or cp.secOcRequired or not cp.pcmCruise or len(cp.safetyConfigs) != 1 or @@ -74,6 +76,9 @@ def enabled(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/carcontroller.py b/opendbc_repo/opendbc/car/toyota/carcontroller.py index 9cac90ccdf..2c9f2551c1 100644 --- a/opendbc_repo/opendbc/car/toyota/carcontroller.py +++ b/opendbc_repo/opendbc/car/toyota/carcontroller.py @@ -74,7 +74,7 @@ class CarController(CarControllerBase): self.door_locks = DoorLocks() self.reverse_cruise_input = None self.prius_filter_input = None - self.original_prius_sdsu = prius_sdsu.configured(CP) + self.original_prius_sdsu = prius_sdsu.configured(CP) or prius_longitudinal.eps73_status_configured(CP) self.original_prius_filter = self.original_prius_sdsu or prius_advert_filter.configured(CP) self.pedal_input = None self.rav4h_filter_input = None @@ -177,6 +177,20 @@ class CarController(CarControllerBase): if guarded_advert: filter_allowed = filter_allowed and legacy_advert_source_ready pedal_owner = pedal.configured(self.CP) + hold_source_ready = filter_allowed and bool( + CS.out.canValid and not CS.out.canTimeout and CS.out.cruiseState.available and not CS.out.accFaulted and + not CS.out.stockAeb and not CS.out.steerFaultTemporary and not CS.out.steerFaultPermanent and not CS.out.doorOpen and + not CS.out.seatbeltUnlatched and CS.out.gearShifter in (structs.CarState.GearShifter.drive, structs.CarState.GearShifter.sport)) + if pedal_owner: + hold_source_ready = hold_source_ready and bool(self.pedal_input is not None and self.pedal_input.update(now_nanos) and CS.pedal_ready) + if bypass_long: + hold_source_ready = hold_source_ready and bool(self.bypass_input is not None and self.bypass_input.update(now_nanos)) + if pedal_composite.configured(self.CP): + filter_allowed = filter_allowed and bool( + CS.out.canValid and not CS.out.canTimeout and CS.out.cruiseState.available and not CS.out.accFaulted and not CS.out.stockAeb and + not CS.out.steerFaultTemporary and not CS.out.steerFaultPermanent and not CS.out.doorOpen and not CS.out.seatbeltUnlatched and + not CS.out.gasPressed and not CS.out.brakePressed and + CS.out.gearShifter in (structs.CarState.GearShifter.drive, structs.CarState.GearShifter.sport)) if pedal_owner: filter_allowed = filter_allowed and bool(self.pedal_input is not None and self.pedal_input.update(now_nanos) and CS.pedal_ready) if bypass_long: @@ -304,7 +318,7 @@ class CarController(CarControllerBase): lead = hud_control.leadVisible or CS.out.vEgo < 12. # at low speed we always assume the lead is present so ACC can be engaged # *** gas and brake *** - if supports_toyota_auto_hold(self.CP) and CS.out.canValid and not CS.out.canTimeout and (not rav4h_long or filter_allowed): + if supports_toyota_auto_hold(self.CP) and CS.out.canValid and not CS.out.canTimeout and hold_source_ready: self.update_auto_hold_state(CS, pcm_cancel_cmd if not uses_toyota_auto_hold_aeb(self.CP) else False) if (uses_toyota_auto_hold_aeb(self.CP) and self.frame % 2 == 0 and CS.out.standstill and CS.out.cruiseState.available and not CS.out.gasPressed): @@ -432,7 +446,8 @@ class CarController(CarControllerBase): self.permit_braking = True self.standstill_req = True - if (bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and not long_active: + if ((bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and not long_active and + not (self.brake_hold_active and not uses_toyota_auto_hold_aeb(self.CP))): pcm_accel_cmd = 0. main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead, diff --git a/opendbc_repo/opendbc/car/toyota/carstate.py b/opendbc_repo/opendbc/car/toyota/carstate.py index ca927605c6..81f96fc816 100644 --- a/opendbc_repo/opendbc/car/toyota/carstate.py +++ b/opendbc_repo/opendbc/car/toyota/carstate.py @@ -2,7 +2,9 @@ import copy import math from opendbc.can import CANDefine, CANParser -from opendbc.car.toyota import dsu_bypass, pedal_composite, pedal, legacy_sdsu, advert_filter, legacy_advert_filter, prius_sdsu, prius_advert_filter +from opendbc.car.toyota import ( + prius_longitudinal, dsu_bypass, pedal_composite, pedal, legacy_sdsu, advert_filter, legacy_advert_filter, prius_sdsu, prius_advert_filter +) from opendbc.car.toyota.lexus_is import LexusIsCANParser from opendbc.car import Bus, DT_CTRL, create_button_events, structs from opendbc.car.common.conversions import Conversions as CV @@ -291,7 +293,7 @@ class CarState(CarStateBase): elif abs(ret.steeringAngleDeg - (zorro - self.zss_offset)) < 4.0: ret.steeringAngleDeg = zorro - self.zss_offset - original_prius_sdsu = prius_sdsu.configured(self.CP) + original_prius_sdsu = prius_sdsu.configured(self.CP) or prius_longitudinal.eps73_status_configured(self.CP) if rav4h_filter_configured(self.CP) or original_prius_sdsu: self.sdsu_state = int(cp.vl["SDSU"]["STATE"]) source_ns, valid_until_ns = parser_expiry(cp, "SDSU", "STATE") @@ -327,7 +329,8 @@ class CarState(CarStateBase): def get_can_parsers(CP): if pedal_composite.configured(CP): parent = CarState.get_can_parsers(pedal_composite.parent_cp(CP)) - parent[Bus.pt] = pedal_composite.ParentPedalParser(parent[Bus.pt]) + for bus in parent: + parent[bus] = pedal_composite.ParentPedalParser(parent[bus]) parent[Bus.alt] = pedal.PedalCANParser(DBC[CP.carFingerprint][Bus.pt], [('GAS_SENSOR', 50)], 0) return parent if dsu_bypass.configured(CP): @@ -356,9 +359,10 @@ class CarState(CarStateBase): if CP.carFingerprint == CAR.LEXUS_IS or alternate_sdsu_main: pt_messages.append(("DSU_CRUISE", 10)) - if (advert_filter.configured(CP) and not alternate_sdsu_main) or prius_sdsu.configured(CP) or prius_advert_filter.configured(CP): + if ((advert_filter.configured(CP) and not alternate_sdsu_main) or prius_sdsu.configured(CP) or + prius_longitudinal.eps73_status_configured(CP) or prius_advert_filter.configured(CP)): pt_messages.append(("PCM_CRUISE_2", 10)) - if prius_sdsu.configured(CP) or prius_advert_filter.configured(CP): + if prius_sdsu.configured(CP) or prius_longitudinal.eps73_status_configured(CP) or prius_advert_filter.configured(CP): pt_messages.append(("EPS_STATUS", 25)) if CP.flags & ToyotaFlags.LONG_FILTER and not prius_advert_filter.configured(CP): pt_messages.append(("SDSU", 50)) @@ -368,7 +372,7 @@ class CarState(CarStateBase): if pedal.configured(CP): pt_messages.append(("GAS_SENSOR", 50)) pt_parser = (prius_advert_filter.PriusAdvertCANParser if prius_advert_filter.configured(CP) else - prius_sdsu.PriusSDSUCANParser if prius_sdsu.configured(CP) else + prius_sdsu.PriusSDSUCANParser if prius_sdsu.configured(CP) or prius_longitudinal.eps73_status_configured(CP) else camera_2aa_filter.CameraTwoAACANParser if camera_twoaa else legacy_advert_filter.LegacyAdvertCANParser if legacy_advert else legacy_sdsu.AlternateMainSDSUCANParser if alternate_sdsu_main else diff --git a/opendbc_repo/opendbc/car/toyota/dsu_bypass.py b/opendbc_repo/opendbc/car/toyota/dsu_bypass.py index 4df5973280..e574eb0ff7 100644 --- a/opendbc_repo/opendbc/car/toyota/dsu_bypass.py +++ b/opendbc_repo/opendbc/car/toyota/dsu_bypass.py @@ -79,6 +79,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if ( cp is None or cp.brand != 'toyota' @@ -110,6 +112,9 @@ def enabled(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/eps8_sdsu.py b/opendbc_repo/opendbc/car/toyota/eps8_sdsu.py index 3b135d57e8..6d719ee699 100644 --- a/opendbc_repo/opendbc/car/toyota/eps8_sdsu.py +++ b/opendbc_repo/opendbc/car/toyota/eps8_sdsu.py @@ -71,6 +71,8 @@ def apply(cp, alpha_long): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if ( cp is None or cp.brand != "toyota" @@ -101,6 +103,9 @@ def configured(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/interface.py b/opendbc_repo/opendbc/car/toyota/interface.py index 895319f563..602ffa3770 100644 --- a/opendbc_repo/opendbc/car/toyota/interface.py +++ b/opendbc_repo/opendbc/car/toyota/interface.py @@ -12,15 +12,17 @@ SteerControlType = structs.CarParams.SteerControlType def toyota_auto_hold_supported(CP) -> bool: - from opendbc.car.toyota.rav4h_filter import enabled as rav4h_filter_enabled - if CP.carFingerprint == CAR.TOYOTA_RAV4H and rav4h_filter_enabled(CP): - return True - return bool(CP.brand == "toyota" and CP.carFingerprint in TOYOTA_AUTO_HOLD_CARS and - CP.openpilotLongitudinalControl and not CP.flags & ToyotaFlags.SECOC and - not CP.notCar and not CP.dashcamOnly and not CP.passive and len(CP.safetyConfigs) == 1 and - CP.safetyConfigs[0].safetyModel == structs.CarParams.SafetyModel.toyota and - (CP.safetyConfigs[0].safetyParam & 0xFF) == EPS_SCALE[CP.carFingerprint] and - not CP.safetyConfigs[0].safetyParam & ~(0xFF | int(ToyotaSafetyFlags.ALT_BRAKE) | int(ToyotaSafetyFlags.LTA))) + from opendbc.car.toyota import pedal, pedal_composite + if not (CP.brand == "toyota" and CP.carFingerprint in TOYOTA_AUTO_HOLD_CARS and + CP.openpilotLongitudinalControl and not CP.flags & ToyotaFlags.SECOC and + not CP.notCar and not CP.dashcamOnly and not CP.passive and len(CP.safetyConfigs) == 1 and + CP.safetyConfigs[0].safetyModel == structs.CarParams.SafetyModel.toyota): + return False + parent = pedal_composite.parent_cp(CP) + return (any(module.configured(parent) for module in pedal_composite.parent_modules()) or + pedal.configured(CP) or + ((parent.safetyConfigs[0].safetyParam & 0xFF) == EPS_SCALE[parent.carFingerprint] and + not parent.safetyConfigs[0].safetyParam & ~(0xFF | int(ToyotaSafetyFlags.ALT_BRAKE) | int(ToyotaSafetyFlags.LTA)))) def apply_toyota_auto_hold(CP, enabled: bool) -> bool: @@ -184,7 +186,7 @@ class CarInterface(CarInterfaceBase): if smart_dsu: ret.flags |= int(ToyotaFlags.HYBRID | ToyotaFlags.LONG_FILTER) - ret.safetyConfigs[0].safetyParam = 4169 + ret.safetyConfigs[0].safetyParam = 0x8049 # EPS73 status uses the existing EPS8 smartDSU receiver. ret.openpilotLongitudinalControl = True from opendbc.car.toyota import legacy_advert_filter legacy_advert = legacy_advert_filter.hardware_supported(candidate, fingerprint, car_fw) diff --git a/opendbc_repo/opendbc/car/toyota/legacy_advert_filter.py b/opendbc_repo/opendbc/car/toyota/legacy_advert_filter.py index 39af781f42..c91044cd1c 100644 --- a/opendbc_repo/opendbc/car/toyota/legacy_advert_filter.py +++ b/opendbc_repo/opendbc/car/toyota/legacy_advert_filter.py @@ -47,6 +47,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if cp is not None and cp.carFingerprint == CAR.TOYOTA_MATRIX_RETROFIT: from opendbc.car.toyota import matrix_filter return matrix_filter.configured(cp, length=4) @@ -70,6 +72,9 @@ def enabled(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/legacy_sdsu.py b/opendbc_repo/opendbc/car/toyota/legacy_sdsu.py index b475d7deb9..6531dac901 100644 --- a/opendbc_repo/opendbc/car/toyota/legacy_sdsu.py +++ b/opendbc_repo/opendbc/car/toyota/legacy_sdsu.py @@ -58,6 +58,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if cp is not None and cp.carFingerprint == CAR.TOYOTA_MATRIX_RETROFIT: from opendbc.car.toyota import matrix_filter return matrix_filter.configured(cp, length=8) @@ -82,6 +84,9 @@ def configured(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/pedal.py b/opendbc_repo/opendbc/car/toyota/pedal.py index 9ca9e73178..0c9b85916d 100644 --- a/opendbc_repo/opendbc/car/toyota/pedal.py +++ b/opendbc_repo/opendbc/car/toyota/pedal.py @@ -58,6 +58,8 @@ def hardware_supported(candidate, fingerprint, car_fw): def configured(cp): + from opendbc.car.toyota.pedal_composite import hold_base_cp + cp = hold_base_cp(cp) from opendbc.car.toyota import pedal_composite if pedal_composite.configured(cp): return True diff --git a/opendbc_repo/opendbc/car/toyota/pedal_composite.py b/opendbc_repo/opendbc/car/toyota/pedal_composite.py index 3be18e6f86..1362c6ede4 100644 --- a/opendbc_repo/opendbc/car/toyota/pedal_composite.py +++ b/opendbc_repo/opendbc/car/toyota/pedal_composite.py @@ -7,14 +7,70 @@ A valid pedal source supplements that parent; it cannot replace it. from opendbc.car import structs from opendbc.car.toyota.values import CAR, ToyotaFlags -PARENT_WORDS = {0xB049: 0x9049, 0xB249: 0x9249} +PARENT_WORDS = { + 0x3049: 0x1049, + 0x3249: 0x1249, + 0x3449: 0x1449, + 0x3649: 0x1649, + 0xA042: 0x8042, + 0xA049: 0x8049, + 0xA242: 0x8242, + 0xA249: 0x8249, + 0xA449: 0x8449, + 0xA649: 0x8649, + 0xA849: 0x8849, + 0xA949: 0x8949, + 0xA958: 0x8958, + 0xA964: 0x8964, + 0xAA49: 0x8A49, + 0xAB49: 0x8B49, + 0xAB58: 0x8B58, + 0xAB64: 0x8B64, + 0xB049: 0x9049, + 0xB149: 0x9149, + 0xB158: 0x9158, + 0xB164: 0x9164, + 0xB249: 0x9249, + 0xB349: 0x9349, + 0xB358: 0x9358, + 0xB364: 0x9364, + 0xB449: 0x9449, + 0xB549: 0x9549, + 0xB558: 0x9558, + 0xB564: 0x9564, + 0xB649: 0x9649, + 0xB749: 0x9749, + 0xB758: 0x9758, + 0xB764: 0x9764, + 0xB842: 0x9842, + 0xB849: 0x9849, + 0xBA42: 0x9A42, + 0xBA49: 0x9A49, + 0xE042: 0xC042, + 0xE049: 0xC049, + 0xE242: 0xC242, + 0xE249: 0xC249, + 0xE84D: 0xC84D, + 0xE949: 0xC949, + 0xEA4D: 0xCA4D, + 0xEB49: 0xCB49, + 0xF049: 0xD049, + 0xF04D: 0xD04D, + 0xF149: 0xD149, + 0xF249: 0xD249, + 0xF24D: 0xD24D, + 0xF349: 0xD349, + 0xF44D: 0xD44D, + 0xF549: 0xD549, + 0xF64D: 0xD64D, + 0xF749: 0xD749, +} def marked(cp): return bool( cp is not None and cp.brand == 'toyota' - and cp.carFingerprint == CAR.TOYOTA_RAV4H and cp.flags & ToyotaFlags.GAS_INTERCEPTOR and len(cp.safetyConfigs) == 1 and cp.safetyConfigs[0].safetyModel == structs.CarParams.SafetyModel.toyota @@ -22,7 +78,24 @@ def marked(cp): ) +def hold_base_cp(cp): + """Project an eligible, exactly requested hold setting onto its existing owner.""" + from opendbc.car.toyota.values import TOYOTA_AUTO_HOLD_CARS, uses_toyota_auto_hold_aeb + if cp is None or not cp.flags & ToyotaFlags.AUTO_BRAKE_HOLD: + return cp + permission = 256 if uses_toyota_auto_hold_aeb(cp) else 128 + if (cp.brand != 'toyota' or cp.carFingerprint not in TOYOTA_AUTO_HOLD_CARS or + not cp.openpilotLongitudinalControl or cp.secOcRequired or cp.notCar or cp.passive or cp.dashcamOnly or + cp.alternativeExperience not in (permission, permission | 32)): + return cp + parent = cp.as_reader().as_builder() if hasattr(cp, 'as_reader') else cp.as_builder() + parent.flags &= ~int(ToyotaFlags.AUTO_BRAKE_HOLD) + parent.alternativeExperience &= ~(128 | 256) + return parent + + def parent_cp(cp): + cp = hold_base_cp(cp) if not marked(cp): return cp parent = cp.as_reader().as_builder() if hasattr(cp, 'as_reader') else cp.as_builder() @@ -31,45 +104,82 @@ def parent_cp(cp): return parent -def configured(cp): - from opendbc.car.toyota import rav4h_filter +def parent_modules(): + from opendbc.car.toyota import dsu_bypass, prius_longitudinal, advert_filter, rav4h_filter, radar_filter + return (dsu_bypass, prius_longitudinal, advert_filter, rav4h_filter, radar_filter) - return marked(cp) and not cp.secOcRequired and rav4h_filter.configured(parent_cp(cp)) + +def configured(cp): + return marked(cp) and not cp.secOcRequired and any(module.configured(parent_cp(cp)) for module in parent_modules()) def hardware_supported(candidate, fingerprint, car_fw): - from opendbc.car.toyota import rav4h_filter - - if candidate != CAR.TOYOTA_RAV4H or fingerprint.get(0, {}).get(0x201) != 6 or any(0x201 in frames for bus, frames in fingerprint.items() if bus != 0): + from opendbc.car.toyota import dsu_bypass, rav4h_filter, eps8_sdsu, legacy_sdsu, advert_filter, prius_sdsu, prius_advert_filter, radar_filter + if fingerprint.get(0, {}).get(0x201) != 6 or any(0x201 in frames for bus, frames in fingerprint.items() if bus != 0): return False - return rav4h_filter.hardware_supported(candidate, without_pedal(fingerprint), car_fw) + parent = without_pedal(fingerprint) + for module in (dsu_bypass, prius_advert_filter, prius_sdsu, advert_filter, legacy_sdsu, eps8_sdsu, rav4h_filter): + if module.hardware_supported(candidate, parent, car_fw): + return True + if prius_status_hardware(candidate, parent, car_fw): + return True + if not radar_filter.hardware_supported(candidate, parent): + return False + from opendbc.car.toyota.fingerprints import FW_VERSIONS + known = FW_VERSIONS[candidate] + for ecu in (structs.CarParams.Ecu.eps, structs.CarParams.Ecu.fwdCamera): + rows = [fw for fw in car_fw if fw.ecu == ecu] + if not rows or any(fw.logging or fw.bus != 0 or fw.brand != 'toyota' or + bytes(fw.fwVersion) not in known.get((ecu, fw.address, fw.subAddress or None), ()) for fw in rows): + return False + return True + + +def prius_status_hardware(candidate, fingerprint, car_fw): + from opendbc.car.toyota import prius_retrofit_advert + if (candidate not in prius_retrofit_advert.CARS or fingerprint.get(0, {}).get(0x2FF) != 8 or + any(0x2FF in frames and (bus not in (0, 2) or frames[0x2FF] != 8) for bus, frames in fingerprint.items()) or + any(address in frames for frames in fingerprint.values() for address in (0x201, 0x2AA))): + return False + from opendbc.car.toyota.fingerprints import FW_VERSIONS + known = FW_VERSIONS[CAR.TOYOTA_PRIUS] + eps = [fw for fw in car_fw if fw.ecu == structs.CarParams.Ecu.eps] + if not eps or any(fw.logging or fw.bus != 0 or fw.brand != 'toyota' or fw.address != 0x7A1 or fw.subAddress or + bytes(fw.fwVersion) != prius_retrofit_advert.EPS_FIRMWARE for fw in eps): + return False + for ecu in (structs.CarParams.Ecu.fwdCamera, structs.CarParams.Ecu.fwdRadar): + if any(fw.logging or fw.bus != 0 or fw.brand != 'toyota' or + bytes(fw.fwVersion) not in known.get((ecu, fw.address, fw.subAddress or None), ()) for fw in car_fw if fw.ecu == ecu): + return False + return True def without_pedal(fingerprint): return {bus: {address: size for address, size in frames.items() if address != 0x201} for bus, frames in fingerprint.items()} -def apply(cp): - from opendbc.car.toyota import rav4h_filter +def composite_word(cp): + return cp.safetyConfigs[0].safetyParam | 0x2000 - if not rav4h_filter.configured(cp) or cp.carFingerprint != CAR.TOYOTA_RAV4H: + +def apply(cp): + if not any(module.configured(cp) for module in parent_modules()) or composite_word(cp) not in PARENT_WORDS: return False + cp.safetyConfigs[0].safetyParam = composite_word(cp) cp.flags |= int(ToyotaFlags.GAS_INTERCEPTOR) - cp.safetyConfigs[0].safetyParam |= 0x2000 + cp.minEnableSpeed = -1. return True def prepare_stock(cp): - from opendbc.car.toyota import rav4h_filter - if not configured(cp): return False parent = parent_cp(cp) - assert rav4h_filter.prepare_stock(parent) + assert any(module.prepare_stock(parent) for module in parent_modules()) cp.flags = parent.flags | int(ToyotaFlags.GAS_INTERCEPTOR) cp.alternativeExperience = parent.alternativeExperience cp.openpilotLongitudinalControl = False - cp.safetyConfigs[0].safetyParam = parent.safetyConfigs[0].safetyParam | 0x2000 + cp.safetyConfigs[0].safetyParam = composite_word(parent) cp.autoResumeSng = parent.autoResumeSng return True diff --git a/opendbc_repo/opendbc/car/toyota/prius_advert_filter.py b/opendbc_repo/opendbc/car/toyota/prius_advert_filter.py index 37b6f6246a..bddff757a4 100644 --- a/opendbc_repo/opendbc/car/toyota/prius_advert_filter.py +++ b/opendbc_repo/opendbc/car/toyota/prius_advert_filter.py @@ -42,6 +42,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) from opendbc.car.toyota import prius_retrofit_advert if prius_retrofit_advert.configured(cp): return True @@ -57,6 +59,9 @@ def configured(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/prius_longitudinal.py b/opendbc_repo/opendbc/car/toyota/prius_longitudinal.py index f1cce7f7af..a974b721d5 100644 --- a/opendbc_repo/opendbc/car/toyota/prius_longitudinal.py +++ b/opendbc_repo/opendbc/car/toyota/prius_longitudinal.py @@ -13,6 +13,8 @@ TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR = .35 def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) from opendbc.car.toyota import prius_sdsu, prius_advert_filter if prius_sdsu.configured(cp) or prius_advert_filter.configured(cp): return True @@ -21,8 +23,14 @@ def configured(cp): not cp.passive and cp.steerControlType == structs.CarParams.SteerControlType.torque and cp.flags & ~int(ToyotaFlags.ZSS) == int(ToyotaFlags.HYBRID | ToyotaFlags.LONG_FILTER | ToyotaFlags.RAISED_ACCEL_LIMIT) and len(cp.safetyConfigs) == 1 and cp.safetyConfigs[0].safetyModel == structs.CarParams.SafetyModel.toyota and - cp.safetyConfigs[0].safetyParam in (4169, 4681) and - cp.openpilotLongitudinalControl == (cp.safetyConfigs[0].safetyParam == 4169)) + cp.safetyConfigs[0].safetyParam in (0x8049, 0x8249) and + cp.openpilotLongitudinalControl == (cp.safetyConfigs[0].safetyParam == 0x8049)) + + +def eps73_status_configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) + return configured(cp) and cp.carFingerprint in (CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT) and cp.safetyConfigs[0].safetyParam in (0x8049, 0x8249) def enabled(cp): @@ -34,13 +42,16 @@ def stopping_decel_rate(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) from opendbc.car.toyota import prius_sdsu, prius_advert_filter if prius_sdsu.prepare_stock(cp) or prius_advert_filter.prepare_stock(cp): return True if not configured(cp): return False cp.openpilotLongitudinalControl = False - cp.safetyConfigs[0].safetyParam = 4681 + cp.safetyConfigs[0].safetyParam = 0x8249 cp.autoResumeSng = False return True diff --git a/opendbc_repo/opendbc/car/toyota/prius_retrofit_advert.py b/opendbc_repo/opendbc/car/toyota/prius_retrofit_advert.py index 556134d972..ee91e57966 100644 --- a/opendbc_repo/opendbc/car/toyota/prius_retrofit_advert.py +++ b/opendbc_repo/opendbc/car/toyota/prius_retrofit_advert.py @@ -32,6 +32,8 @@ def hardware_supported(candidate, fingerprint, car_fw): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if (cp is None or cp.brand != 'toyota' or cp.carFingerprint not in CARS or cp.notCar or cp.passive or cp.dashcamOnly or cp.secOcRequired or not cp.pcmCruise or cp.steerControlType != structs.CarParams.SteerControlType.torque or len(cp.safetyConfigs) != 1 or cp.alphaLongitudinalAvailable or cp.autoResumeSng): diff --git a/opendbc_repo/opendbc/car/toyota/prius_sdsu.py b/opendbc_repo/opendbc/car/toyota/prius_sdsu.py index 5cbf08468f..cd3198ac65 100644 --- a/opendbc_repo/opendbc/car/toyota/prius_sdsu.py +++ b/opendbc_repo/opendbc/car/toyota/prius_sdsu.py @@ -40,6 +40,8 @@ def apply(cp): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) if (cp is None or cp.brand != 'toyota' or cp.carFingerprint != CAR.TOYOTA_PRIUS or cp.notCar or cp.passive or cp.dashcamOnly or cp.secOcRequired or not cp.pcmCruise or len(cp.safetyConfigs) != 1 or cp.steerControlType != structs.CarParams.SteerControlType.torque): @@ -52,6 +54,9 @@ def configured(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/car/toyota/radar_filter.py b/opendbc_repo/opendbc/car/toyota/radar_filter.py index b8b10ef8e4..e3c020eced 100644 --- a/opendbc_repo/opendbc/car/toyota/radar_filter.py +++ b/opendbc_repo/opendbc/car/toyota/radar_filter.py @@ -29,6 +29,8 @@ def hardware_supported(candidate, fingerprint): def configured(cp): + from opendbc.car.toyota.pedal_composite import parent_cp + cp = parent_cp(cp) profile = PROFILES.get(cp.carFingerprint) if cp is not None else None return bool(profile is not None and cp.brand == 'toyota' and cp.flags & ToyotaFlags.RADAR_CAN_FILTER and not cp.flags & ToyotaFlags.DISABLE_RADAR and @@ -38,6 +40,9 @@ def configured(cp): def prepare_stock(cp): + from opendbc.car.toyota import pedal_composite + if pedal_composite.marked(cp): + return pedal_composite.prepare_stock(cp) if not configured(cp): return False cp.openpilotLongitudinalControl = False diff --git a/opendbc_repo/opendbc/safety/modes/toyota.h b/opendbc_repo/opendbc/safety/modes/toyota.h index d4c67aa08b..72e477e8be 100644 --- a/opendbc_repo/opendbc/safety/modes/toyota.h +++ b/opendbc_repo/opendbc/safety/modes/toyota.h @@ -98,7 +98,8 @@ static bool toyota_legacy_main_current(void) { bool ready = true; if (toyota_legacy_sdsu && toyota_alt_cruise) { ready = false; - if (current_safety_config.rx_checks_len == 6) { + if ((current_safety_config.rx_checks_len == 6) || + (toyota_pedal_composite && (current_safety_config.rx_checks_len > 6))) { const RxStatus *main_status = ¤t_safety_config.rx_checks[5].status; ready = main_status->msg_seen && main_status->valid_checksum && main_status->valid_quality_flag && !main_status->lagging && (main_status->wrong_counters < MAX_WRONG_COUNTERS) && acc_main_on && @@ -126,7 +127,10 @@ static bool toyota_legacy_advert_ready(void) { static bool toyota_hold_ready(int permission) { const int hold_mask = ALT_EXP_TOYOTA_AUTO_HOLD | ALT_EXP_TOYOTA_AEB_HOLD; const uint32_t now = microsecond_timer_get(); - return toyota_rx_ready(now) && ((alternative_experience & hold_mask) == permission) && !toyota_stock_longitudinal && !toyota_secoc && + return toyota_rx_ready(now) && toyota_legacy_main_current() && toyota_legacy_advert_ready() && + (!(toyota_sdsu_status || toyota_pedal) || aol_rx_healthy()) && (!toyota_sdsu_status || toyota_sdsu_control) && + (!toyota_dsu_bypass || (toyota_bypass_control && !toyota_bypass_aeb)) && + ((alternative_experience & hold_mask) == permission) && !toyota_stock_longitudinal && !toyota_secoc && toyota_hold_config_valid && !safety_rx_checks_invalid && !relay_malfunction && !vehicle_moving && !gas_pressed && toyota_hold_main_on && toyota_hold_main_seen && toyota_hold_speed_seen && toyota_hold_gas_seen && (safety_get_ts_elapsed(now, toyota_hold_main_ts) <= 1000000U) && @@ -192,6 +196,28 @@ static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) { return valid; } +static void toyota_hold_observe(const CANPacket_t *msg) { + if ((msg->bus == 2U) && (msg->addr == 0x344U) && (GET_LEN(msg) == 8U) && + (toyota_get_checksum(msg) == toyota_compute_checksum(msg))) { + toyota_hold_camera_seen = true; + toyota_hold_camera_ts = microsecond_timer_get(); + for (uint8_t i = 0U; i < 7U; i++) { + toyota_hold_camera_data[i] = msg->data[i]; + } + } + if (msg->bus == 0U) { + if ((msg->addr == 0x1D3U) && (GET_LEN(msg) == 8U)) { + if (toyota_get_checksum(msg) != toyota_compute_checksum(msg)) { + toyota_hold_main_seen = false; + } else { + toyota_hold_main_on = GET_BIT(msg, 15U); + toyota_hold_main_seen = true; + toyota_hold_main_ts = microsecond_timer_get(); + } + } + } +} + static void toyota_optional_rx_hook(const CANPacket_t *msg) { if (toyota_dsu_bypass) { if (msg_matches(msg, 0x343U, 2U)) { @@ -235,28 +261,11 @@ static void toyota_optional_rx_hook(const CANPacket_t *msg) { toyota_aol_seen[index] = false; } else { } - if ((msg->bus == 2U) && (msg->addr == 0x344U) && (GET_LEN(msg) == 8U) && - (toyota_get_checksum(msg) == toyota_compute_checksum(msg))) { - toyota_hold_camera_seen = true; - toyota_hold_camera_ts = microsecond_timer_get(); - for (uint8_t i = 0U; i < 7U; i++) { - toyota_hold_camera_data[i] = msg->data[i]; - } - } - if (msg->bus == 0U) { - if ((msg->addr == 0x1D3U) && (GET_LEN(msg) == 8U)) { - if (toyota_get_checksum(msg) != toyota_compute_checksum(msg)) { - toyota_hold_main_seen = false; - } else { - toyota_hold_main_on = GET_BIT(msg, 15U); - toyota_hold_main_seen = true; - toyota_hold_main_ts = microsecond_timer_get(); - } - } - } + toyota_hold_observe(msg); } static void toyota_rx_hook(const CANPacket_t *msg) { + toyota_hold_observe(msg); if (toyota_dsu_bypass && msg_matches(msg, 0x343U, 2U)) { toyota_bypass_control = !GET_BIT(msg, 18U) && !GET_BIT(msg, 19U); } @@ -604,10 +613,11 @@ static bool toyota_tx_hook(const CANPacket_t *msg) { } static safety_config toyota_init(uint16_t param) { - const uint16_t base_param = toyota_bypass_base_param(toyota_pedal_parent_param(param)); - toyota_dsu_bypass = toyota_bypass_param(param); - toyota_bypass_eps5 = toyota_dsu_bypass && ((param & 0x1000U) == 0U); - toyota_bypass_alt_main = toyota_dsu_bypass && ((param & 0x4000U) != 0U); + const uint16_t parent_param = toyota_pedal_parent_param(param); + const uint16_t base_param = toyota_bypass_base_param(parent_param); + toyota_dsu_bypass = toyota_bypass_param(parent_param); + toyota_bypass_eps5 = toyota_dsu_bypass && ((parent_param & 0x1000U) == 0U); + toyota_bypass_alt_main = toyota_dsu_bypass && ((parent_param & 0x4000U) != 0U); toyota_bypass_ambiguous = false; toyota_bypass_control = false; toyota_bypass_aeb = false; @@ -619,7 +629,9 @@ static safety_config toyota_init(uint16_t param) { toyota_hold_main_on = false; toyota_hold_host_seen = false; toyota_hold_host_ts = 0U; - toyota_hold_config_valid = (((base_param & ~0x5FFU) == 0U) && ((base_param & 0xFFU) != 0U)) || (base_param == 0x9049U); + toyota_hold_config_valid = (((base_param & ~0x5FFU) == 0U) && ((base_param & 0xFFU) != 0U)) || toyota_aol_rav4h_filter || toyota_eps8_sdsu || toyota_legacy_sdsu || + toyota_legacy_advert_param(base_param) || toyota_camera_2aa_param(base_param) || + toyota_prius_filter_param(base_param) || toyota_pedal; toyota_hold_camera_seen = false; toyota_hold_camera_ts = 0U; toyota_hold_main_seen = false; @@ -760,7 +772,7 @@ static safety_config toyota_init(uint16_t param) { ret.rx_checks_len = 7; } else if ((toyota_legacy_sdsu && toyota_alt_cruise) || toyota_legacy_advert_param(base_param) || toyota_camera_2aa_param(base_param) || toyota_prius_advert_param(base_param) || (base_param == 856U)) { ret.rx_checks_len = 6; - } else if (toyota_pedal || toyota_alt_cruise || toyota_sdsu_status) { + } else if (standalone_pedal || toyota_alt_cruise || toyota_sdsu_status) { ret.rx_checks_len = 5; } else { ret.rx_checks_len = 4; @@ -775,7 +787,7 @@ static safety_config toyota_init(uint16_t param) { .ignore_counter = true, .ignore_quality_flag = true}; ret.rx_checks_len = 7; } - if (toyota_pedal_composite) { + if ((param == 0xB049U) || (param == 0xB249U)) { // Keep all parent checks, including smartDSU status, independently required. toyota_rx_checks[5].msg[0] = (CanMsgCheck){.addr = 0x201, .bus = 0U, .len = 6, .frequency = 50U, .max_counter = 15U, .ignore_quality_flag = false}; @@ -784,6 +796,32 @@ static safety_config toyota_init(uint16_t param) { toyota_rx_checks[7].msg[0] = (CanMsgCheck){.addr = 0x1D3, .bus = 0U, .len = 8, .frequency = 10U, .ignore_counter = true, .ignore_quality_flag = true}; ret.rx_checks_len = 8; + } else if (toyota_pedal_composite) { + // Append independent physical sources and pedal to the unchanged parent + // catalog. Eight rows cover the largest parent without shrinking buffers. + bool eps_required = false; + bool main_required = false; + for (int i = 0; i < ret.rx_checks_len; i++) { + eps_required |= toyota_rx_checks[i].msg[0].addr == 0x262; + main_required |= toyota_rx_checks[i].msg[0].addr == (toyota_alt_cruise ? 0x365 : 0x1D3); + } + if (!eps_required) { + const bool eps5 = (toyota_dsu_bypass && toyota_bypass_eps5) || toyota_aol_rav4h_filter || + toyota_legacy_sdsu || toyota_legacy_advert(); + toyota_rx_checks[ret.rx_checks_len].msg[0] = (CanMsgCheck){.addr = 0x262, .bus = 0U, + .len = eps5 ? 5 : 8, .frequency = 25U, .ignore_counter = true, .ignore_quality_flag = true}; + ret.rx_checks_len++; + } + if (!main_required) { + toyota_rx_checks[ret.rx_checks_len].msg[0] = (CanMsgCheck){.addr = toyota_alt_cruise ? 0x365 : 0x1D3, + .bus = 0U, .len = toyota_alt_cruise ? 7 : 8, .frequency = 10U, .ignore_checksum = toyota_alt_cruise, + .ignore_counter = true, .ignore_quality_flag = true}; + ret.rx_checks_len++; + } + toyota_rx_checks[ret.rx_checks_len].msg[0] = (CanMsgCheck){.addr = 0x201, .bus = 0U, .len = 6, + .frequency = 50U, .max_counter = 15U, .ignore_quality_flag = false}; + ret.rx_checks_len++; + } else { } } diff --git a/opendbc_repo/opendbc/safety/modes/toyota_aol.h b/opendbc_repo/opendbc/safety/modes/toyota_aol.h index dd51bf7d86..de72898202 100644 --- a/opendbc_repo/opendbc/safety/modes/toyota_aol.h +++ b/opendbc_repo/opendbc/safety/modes/toyota_aol.h @@ -1,5 +1,7 @@ #pragma once +static uint16_t toyota_pedal_parent_param(uint16_t param); + static bool toyota_dsu_bypass = false; static bool toyota_bypass_eps5 = false; static bool toyota_bypass_alt_main = false; @@ -20,7 +22,7 @@ static bool toyota_prius_sdsu_param(uint16_t param) { } static bool toyota_prius_sdsu(void) { - return toyota_prius_sdsu_param(current_safety_param); + return toyota_prius_sdsu_param(toyota_pedal_parent_param(current_safety_param)); } static bool toyota_prius_retrofit_advert_param(uint16_t param) { @@ -32,7 +34,7 @@ static bool toyota_prius_advert_param(uint16_t param) { } static bool toyota_prius_advert(void) { - return toyota_prius_advert_param(current_safety_param); + return toyota_prius_advert_param(toyota_pedal_parent_param(current_safety_param)); } static bool toyota_prius_filter_param(uint16_t param) { @@ -40,7 +42,7 @@ static bool toyota_prius_filter_param(uint16_t param) { } static bool toyota_prius_filter(void) { - return toyota_prius_filter_param(current_safety_param); + return toyota_prius_filter_param(toyota_pedal_parent_param(current_safety_param)); } // Disjoint original Prius owners share this sticky receiver identity latch. @@ -51,7 +53,7 @@ static bool toyota_camera_2aa_param(uint16_t param) { } static bool toyota_camera_2aa(void) { - return toyota_camera_2aa_param(current_safety_param); + return toyota_camera_2aa_param(toyota_pedal_parent_param(current_safety_param)); } static bool toyota_camera_2aa_ambiguous = false; @@ -61,7 +63,7 @@ static bool toyota_legacy_advert_param(uint16_t param) { } static bool toyota_legacy_advert(void) { - return toyota_legacy_advert_param(current_safety_param); + return toyota_legacy_advert_param(toyota_pedal_parent_param(current_safety_param)); } @@ -72,7 +74,7 @@ static bool toyota_composite_pcm_gas = false; static bool toyota_composite_pedal_gas = false; static bool toyota_pedal_composite_param(uint16_t param) { - return (param == 0xB049U) || (param == 0xB249U); + return (param == 0x3049U) || (param == 0x3249U) || (param == 0x3449U) || (param == 0x3649U) || (param == 0xA042U) || (param == 0xA049U) || (param == 0xA242U) || (param == 0xA249U) || (param == 0xA449U) || (param == 0xA649U) || (param == 0xA849U) || (param == 0xA949U) || (param == 0xA958U) || (param == 0xA964U) || (param == 0xAA49U) || (param == 0xAB49U) || (param == 0xAB58U) || (param == 0xAB64U) || (param == 0xB049U) || (param == 0xB149U) || (param == 0xB158U) || (param == 0xB164U) || (param == 0xB249U) || (param == 0xB349U) || (param == 0xB358U) || (param == 0xB364U) || (param == 0xB449U) || (param == 0xB549U) || (param == 0xB558U) || (param == 0xB564U) || (param == 0xB649U) || (param == 0xB749U) || (param == 0xB758U) || (param == 0xB764U) || (param == 0xB842U) || (param == 0xB849U) || (param == 0xBA42U) || (param == 0xBA49U) || (param == 0xE042U) || (param == 0xE049U) || (param == 0xE242U) || (param == 0xE249U) || (param == 0xE84DU) || (param == 0xE949U) || (param == 0xEA4DU) || (param == 0xEB49U) || (param == 0xF049U) || (param == 0xF04DU) || (param == 0xF149U) || (param == 0xF249U) || (param == 0xF24DU) || (param == 0xF349U) || (param == 0xF44DU) || (param == 0xF549U) || (param == 0xF64DU) || (param == 0xF749U); } static uint16_t toyota_pedal_parent_param(uint16_t param) { @@ -204,6 +206,7 @@ static void toyota_aol_observe(const CANPacket_t *msg) { } static void toyota_aol_configure(uint16_t param) { + const bool aol_requested = (alternative_experience == 32) || (alternative_experience == 160) || (alternative_experience == 288); toyota_aol_reset(); toyota_camera_2aa_ambiguous = false; toyota_prius_sdsu_ambiguous = false; @@ -220,14 +223,14 @@ static void toyota_aol_configure(uint16_t param) { #else toyota_aol_secoc = false; #endif - toyota_aol_enabled = (toyota_dsu_bypass && (alternative_experience == 32)) || ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && (alternative_experience == 32)) || (toyota_aol_lta && (alternative_experience == 32)) || (toyota_aol_stock && (alternative_experience == 32)) || + toyota_aol_enabled = (toyota_dsu_bypass && aol_requested) || ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && aol_requested) || (toyota_aol_lta && aol_requested) || (toyota_aol_stock && aol_requested) || #ifdef ALLOW_DEBUG (toyota_aol_secoc && (alternative_experience == 32)) || #endif - ((param == 0x9049U) && ((alternative_experience == 32) || (alternative_experience == 160))) || - ((param == 0x2049U) && (alternative_experience == 32)) || - ((param == 4169U) && (alternative_experience == 32)) || - ((param == 73U) && ((alternative_experience == 32) || (alternative_experience == 160) || (alternative_experience == 288))); + ((param == 0x9049U) && (aol_requested || (alternative_experience == 160))) || + ((param == 0x2049U) && aol_requested) || + ((param == 4169U) && aol_requested) || + ((param == 73U) && (aol_requested || (alternative_experience == 160) || (alternative_experience == 288))); if (toyota_aol_enabled) { static const AolSafetyPolicy policy = { .reset = toyota_aol_reset, diff --git a/opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py b/opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py new file mode 100644 index 0000000000..4fbe3954ec --- /dev/null +++ b/opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py @@ -0,0 +1,46 @@ +"""Every exact composite retains its parent's RX contracts and physical gates.""" +import pytest +from opendbc.car import structs +from opendbc.car.toyota.pedal_composite import PARENT_WORDS +from opendbc.safety.tests.libsafety import libsafety_py +from openpilot.starpilot.car.toyota.tests import test_rav4h_caller_native as helpers + +source_matched_native = helpers.source_matched_native +libsafety_py.ffi.cdef('extern safety_config current_safety_config;') + + +def rx_catalog(safety): + cfg = safety.current_safety_config + fields = ('addr', 'bus', 'len', 'frequency', 'ignore_checksum', 'ignore_counter', 'max_counter', 'ignore_quality_flag') + return [tuple(int(getattr(cfg.rx_checks[i].msg[0], key)) for key in fields) for i in range(cfg.rx_checks_len)] + + +@pytest.mark.parametrize('word,parent', sorted(PARENT_WORDS.items())) +def test_parent_catalog_and_warm_switch_are_exact(word, parent): + safety = libsafety_py.libsafety + safety.init_tests() + safety.set_alternative_experience(32) + try: + assert safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, parent) == 0 + original = rx_catalog(safety) + assert safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, word) == 0 + composite = rx_catalog(safety) + assert 5 <= len(composite) <= 8 + assert composite[:len(original)] == original + extra_eps = not any(row[0] == 0x262 and row[1] == 0 for row in original) + extra_main = not any(row[0] in (0x1D3, 0x365) and row[1] == 0 for row in original) + assert len(composite) == len(original) + 1 + int(extra_eps) + int(extra_main) + assert sum(row[0] == 0x2FF for row in composite) == sum(row[0] == 0x2FF for row in original) + assert sum(row[:3] == (0x201, 0, 6) for row in composite) == 1 + assert sum(row[0] == 0x262 and row[1] == 0 for row in composite) == 1 + assert sum(row[0] in (0x1D3, 0x365) and row[1] == 0 for row in composite) == 1 + pedal = next(row for row in composite if row[0] == 0x201) + assert pedal[3:] == (50, 0, 0, 15, 0) + assert not safety.safety_config_valid() and safety.aol_get_permission_mask() == 0 + # A warm switch must not retain the pedal's extra catalog or authority. + assert safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, parent) == 0 + assert rx_catalog(safety) == original + assert not safety.safety_config_valid() and safety.aol_get_permission_mask() == 0 + finally: + safety.set_alternative_experience(0) + safety.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0) diff --git a/openpilot/selfdrive/pandad/tests/test_aol_protocol.cc b/openpilot/selfdrive/pandad/tests/test_aol_protocol.cc index 3c2a5096d2..ff488b16fb 100644 --- a/openpilot/selfdrive/pandad/tests/test_aol_protocol.cc +++ b/openpilot/selfdrive/pandad/tests/test_aol_protocol.cc @@ -179,7 +179,7 @@ int main() { } for (uint32_t word = 0U; word <= 65535U; ++word) { - const bool expected = (word == 0x8949U) || (word == 0x8B49U) || (word == 0x8849U) || (word == 0x8A49U) || (word == 0x9849U) || (word == 0x9A49U) || (word == 0x9842U) || (word == 0x9A42U) || (word == 0x8958U) || (word == 0x8B58U) || (word == 0x8964U) || (word == 0x8B64U) || (word == 0xC84DU) || (word == 0xCA4DU) || (word == 0xC949U) || (word == 0xCB49U) || (word == 0xB049U) || (word == 0xB249U) || (word == 0xD049U) || (word == 0xD249U) || (word == 0xC042U) || (word == 0xC242U) || (word == 0x8042U) || (word == 0x8242U) || (word == 0xC049U) || (word == 0xC249U) || (word == 73U) || (word == 585U) || (word == 856U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2449U) || (word == 0x2649U) || (word == 0x2049U) || (word == 0x2249U) || (word == 0x424DU) || (word == 0x449U) || (word == 0x649U) || (word == 0x1449U) || (word == 0x1649U) || (word == 0x8049U) || (word == 0x8249U) || (word == 0x8449U) || (word == 0x8649U) || (word == 0x9158U) || (word == 0x9358U) || (word == 0x9149U) || (word == 0x9349U) || (word == 0x9164U) || (word == 0x9364U) || (word == 0xd149U) || (word == 0xd349U) || (word == 0xd04dU) || (word == 0xd24dU) || (word == 0x9449U) || (word == 0x9549U) || (word == 0x9558U) || (word == 0x9564U) || (word == 0x9649U) || (word == 0x9749U) || (word == 0x9758U) || (word == 0x9764U) || (word == 0xD44DU) || (word == 0xD549U) || (word == 0xD64DU) || (word == 0xD749U); + const bool expected = (word == 0x3049U) || (word == 0x3249U) || (word == 0x3449U) || (word == 0x3649U) || (word == 0xA042U) || (word == 0xA049U) || (word == 0xA242U) || (word == 0xA249U) || (word == 0xA449U) || (word == 0xA649U) || (word == 0xA849U) || (word == 0xA949U) || (word == 0xA958U) || (word == 0xA964U) || (word == 0xAA49U) || (word == 0xAB49U) || (word == 0xAB58U) || (word == 0xAB64U) || (word == 0xB149U) || (word == 0xB158U) || (word == 0xB164U) || (word == 0xB349U) || (word == 0xB358U) || (word == 0xB364U) || (word == 0xB449U) || (word == 0xB549U) || (word == 0xB558U) || (word == 0xB564U) || (word == 0xB649U) || (word == 0xB749U) || (word == 0xB758U) || (word == 0xB764U) || (word == 0xB842U) || (word == 0xB849U) || (word == 0xBA42U) || (word == 0xBA49U) || (word == 0xE042U) || (word == 0xE049U) || (word == 0xE242U) || (word == 0xE249U) || (word == 0xE84DU) || (word == 0xE949U) || (word == 0xEA4DU) || (word == 0xEB49U) || (word == 0xF049U) || (word == 0xF04DU) || (word == 0xF149U) || (word == 0xF249U) || (word == 0xF24DU) || (word == 0xF349U) || (word == 0xF44DU) || (word == 0xF549U) || (word == 0xF64DU) || (word == 0xF749U) || (word == 0x8949U) || (word == 0x8B49U) || (word == 0x8849U) || (word == 0x8A49U) || (word == 0x9849U) || (word == 0x9A49U) || (word == 0x9842U) || (word == 0x9A42U) || (word == 0x8958U) || (word == 0x8B58U) || (word == 0x8964U) || (word == 0x8B64U) || (word == 0xC84DU) || (word == 0xCA4DU) || (word == 0xC949U) || (word == 0xCB49U) || (word == 0xB049U) || (word == 0xB249U) || (word == 0xD049U) || (word == 0xD249U) || (word == 0xC042U) || (word == 0xC242U) || (word == 0x8042U) || (word == 0x8242U) || (word == 0xC049U) || (word == 0xC249U) || (word == 73U) || (word == 585U) || (word == 856U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2449U) || (word == 0x2649U) || (word == 0x2049U) || (word == 0x2249U) || (word == 0x424DU) || (word == 0x449U) || (word == 0x649U) || (word == 0x1449U) || (word == 0x1649U) || (word == 0x8049U) || (word == 0x8249U) || (word == 0x8449U) || (word == 0x8649U) || (word == 0x9158U) || (word == 0x9358U) || (word == 0x9149U) || (word == 0x9349U) || (word == 0x9164U) || (word == 0x9364U) || (word == 0xd149U) || (word == 0xd349U) || (word == 0xd04dU) || (word == 0xd24dU) || (word == 0x9449U) || (word == 0x9549U) || (word == 0x9558U) || (word == 0x9564U) || (word == 0x9649U) || (word == 0x9749U) || (word == 0x9758U) || (word == 0x9764U) || (word == 0xD44DU) || (word == 0xD549U) || (word == 0xD64DU) || (word == 0xD749U); auto toyota = status(); toyota.safety_mode = 2U; toyota.safety_param = static_cast(word); diff --git a/openpilot/starpilot/car/toyota/aol.py b/openpilot/starpilot/car/toyota/aol.py index f8dc25b22b..f6d6855448 100644 --- a/openpilot/starpilot/car/toyota/aol.py +++ b/openpilot/starpilot/car/toyota/aol.py @@ -6,6 +6,12 @@ from openpilot.starpilot.aol.policy import AolVehiclePolicy from openpilot.starpilot.aol.intent import AolCardIntent, AOL_TOGGLE AOL_WORDS = frozenset(( + 0x3049, 0x3249, 0x3449, 0x3649, 0xA042, 0xA049, 0xA242, 0xA249, 0xA449, 0xA649, + 0xA849, 0xA949, 0xA958, 0xA964, 0xAA49, 0xAB49, 0xAB58, 0xAB64, 0xB149, 0xB158, + 0xB164, 0xB349, 0xB358, 0xB364, 0xB449, 0xB549, 0xB558, 0xB564, 0xB649, 0xB749, + 0xB758, 0xB764, 0xB842, 0xB849, 0xBA42, 0xBA49, 0xE042, 0xE049, 0xE242, 0xE249, + 0xE84D, 0xE949, 0xEA4D, 0xEB49, 0xF049, 0xF04D, 0xF149, 0xF249, 0xF24D, 0xF349, + 0xF44D, 0xF549, 0xF64D, 0xF749, 0x8949, 0x8B49, 0x8849, 0x8A49, 0x9849, 0x9A49, 0x9842, 0x9A42, 0x8958, 0x8B58, 0x8964, 0x8B64, 0xC84D, 0xCA4D, 0xC949, 0xCB49, 0x8042, 0x8242, 0xC042, 0xC242, 0xD049, 0xD249, @@ -99,6 +105,11 @@ def stock_prius_retrofit_qualified(cp, *, marked_only=False): def qualified(cp, *, marked_only=False): + from opendbc.car.toyota.pedal_composite import hold_base_cp + base = hold_base_cp(cp) + if base is not cp: + from opendbc.car.toyota.interface import toyota_auto_hold_supported + return toyota_auto_hold_supported(cp) and qualified(base, marked_only=marked_only) from opendbc.car.toyota import dsu_bypass if dsu_bypass.configured(cp): return cp.alternativeExperience in ((32,) if marked_only else (0, 32)) diff --git a/openpilot/starpilot/car/toyota/aol_policy.h b/openpilot/starpilot/car/toyota/aol_policy.h index bc876a420b..7c03804e17 100644 --- a/openpilot/starpilot/car/toyota/aol_policy.h +++ b/openpilot/starpilot/car/toyota/aol_policy.h @@ -3,7 +3,7 @@ #include "selfdrive/pandad/aol_protocol.h" inline bool toyota_aol_param(uint16_t param) { - return (param == 0x8949U) || (param == 0x8B49U) || (param == 0x8849U) || (param == 0x8A49U) || (param == 0x9849U) || (param == 0x9A49U) || (param == 0x9842U) || (param == 0x9A42U) || (param == 0x8958U) || (param == 0x8B58U) || (param == 0x8964U) || (param == 0x8B64U) || (param == 0xC84DU) || (param == 0xCA4DU) || (param == 0xC949U) || (param == 0xCB49U) || (param == 0xB049U) || (param == 0xB249U) || (param == 0xD049U || param == 0xD249U || param == 0xC042U || param == 0xC242U || param == 0x8042U || param == 0x8242U || param == 0xC049U) || (param == 0xC249U) || (param == 73U) || (param == 585U) || (param == 856U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2449U) || (param == 0x2649U) || (param == 0x2049U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x449U) || (param == 0x649U) || (param == 0x1449U) || (param == 0x1649U) || (param == 0x8049U) || (param == 0x8249U) || (param == 0x8449U) || (param == 0x8649U) || (param == 0x9158U) || (param == 0x9358U) || (param == 0x9149U) || (param == 0x9349U) || (param == 0x9164U) || (param == 0x9364U) || (param == 0xd149U) || (param == 0xd349U) || (param == 0xd04dU) || (param == 0xd24dU) || (param == 0x9449U) || (param == 0x9549U) || (param == 0x9558U) || (param == 0x9564U) || (param == 0x9649U) || (param == 0x9749U) || (param == 0x9758U) || (param == 0x9764U) || (param == 0xD44DU) || (param == 0xD549U) || (param == 0xD64DU) || (param == 0xD749U); + return (param == 0x3049U) || (param == 0x3249U) || (param == 0x3449U) || (param == 0x3649U) || (param == 0xA042U) || (param == 0xA049U) || (param == 0xA242U) || (param == 0xA249U) || (param == 0xA449U) || (param == 0xA649U) || (param == 0xA849U) || (param == 0xA949U) || (param == 0xA958U) || (param == 0xA964U) || (param == 0xAA49U) || (param == 0xAB49U) || (param == 0xAB58U) || (param == 0xAB64U) || (param == 0xB149U) || (param == 0xB158U) || (param == 0xB164U) || (param == 0xB349U) || (param == 0xB358U) || (param == 0xB364U) || (param == 0xB449U) || (param == 0xB549U) || (param == 0xB558U) || (param == 0xB564U) || (param == 0xB649U) || (param == 0xB749U) || (param == 0xB758U) || (param == 0xB764U) || (param == 0xB842U) || (param == 0xB849U) || (param == 0xBA42U) || (param == 0xBA49U) || (param == 0xE042U) || (param == 0xE049U) || (param == 0xE242U) || (param == 0xE249U) || (param == 0xE84DU) || (param == 0xE949U) || (param == 0xEA4DU) || (param == 0xEB49U) || (param == 0xF049U) || (param == 0xF04DU) || (param == 0xF149U) || (param == 0xF249U) || (param == 0xF24DU) || (param == 0xF349U) || (param == 0xF44DU) || (param == 0xF549U) || (param == 0xF64DU) || (param == 0xF749U) || (param == 0x8949U) || (param == 0x8B49U) || (param == 0x8849U) || (param == 0x8A49U) || (param == 0x9849U) || (param == 0x9A49U) || (param == 0x9842U) || (param == 0x9A42U) || (param == 0x8958U) || (param == 0x8B58U) || (param == 0x8964U) || (param == 0x8B64U) || (param == 0xC84DU) || (param == 0xCA4DU) || (param == 0xC949U) || (param == 0xCB49U) || (param == 0xB049U) || (param == 0xB249U) || (param == 0xD049U || param == 0xD249U || param == 0xC042U || param == 0xC242U || param == 0x8042U || param == 0x8242U || param == 0xC049U) || (param == 0xC249U) || (param == 73U) || (param == 585U) || (param == 856U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2449U) || (param == 0x2649U) || (param == 0x2049U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x449U) || (param == 0x649U) || (param == 0x1449U) || (param == 0x1649U) || (param == 0x8049U) || (param == 0x8249U) || (param == 0x8449U) || (param == 0x8649U) || (param == 0x9158U) || (param == 0x9358U) || (param == 0x9149U) || (param == 0x9349U) || (param == 0x9164U) || (param == 0x9364U) || (param == 0xd149U) || (param == 0xd349U) || (param == 0xd04dU) || (param == 0xd24dU) || (param == 0x9449U) || (param == 0x9549U) || (param == 0x9558U) || (param == 0x9564U) || (param == 0x9649U) || (param == 0x9749U) || (param == 0x9758U) || (param == 0x9764U) || (param == 0xD44DU) || (param == 0xD549U) || (param == 0xD64DU) || (param == 0xD749U); } inline constexpr AolSafetyProfile TOYOTA_AOL_PROFILE{2U, toyota_aol_param, true}; diff --git a/openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py b/openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py new file mode 100644 index 0000000000..27dfb68afa --- /dev/null +++ b/openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py @@ -0,0 +1,159 @@ +"""Full production callers for every distinct parent parser/controller topology.""" +import pytest +from opendbc.car.toyota import dsu_bypass, pedal +from opendbc.car.toyota.interface import CarInterface +from opendbc.car.toyota.values import CAR, ToyotaFlags +from openpilot.starpilot.car.toyota.tests import test_rav4h_caller_native as helpers +from openpilot.starpilot.car.toyota.tests.test_pedal_parent_configurations import sources +from openpilot.starpilot.car.toyota.tests.test_toyota_cpp_transport import host_heartbeat_bridge as host_heartbeat_bridge + +source_matched_native = helpers.source_matched_native +daemon_gc_scope = helpers.daemon_gc_scope +PROFILES = ( + ('TOYOTA_RAV4H', '2FF8', 'catalog'), + ('TOYOTA_CAMRY', '2FF8', 'catalog'), + ('LEXUS_GS_F', '2FF8', 'catalog'), + ('TOYOTA_PRIUS', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2FF4', 'catalog'), + ('TOYOTA_COROLLA', '2FF4', 'catalog'), + ('TOYOTA_RAV4_TSS2_2022', '2AA8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2AA8', 'catalog'), + ('TOYOTA_PRIUS', '2FF8', 'EPS73_8965B47070'), + ('TOYOTA_PRIUS_RETROFIT', '2FF4', 'EPS73_8965B47070'), + ('TOYOTA_CAMRY', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_GS_F', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2023', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2023', '2AA8', 'catalog'), +) + + +@pytest.mark.parametrize('name,topology,rack', PROFILES) +@pytest.mark.parametrize('stock', (False, True)) +def test_nominal_parent_pedal_full_callers(name, topology, rack, stock, host_heartbeat_bridge): + run(name, topology, rack, stock, host_heartbeat_bridge) + + +def run(name, topology, rack, stock, bridge, kind='nominal'): + identity = getattr(CAR, name) + fp, fw = sources(identity, topology, rack) + fp[0][0x201] = 6 + cp = CarInterface.get_params(identity, fp, fw, True, False, False) + assert pedal.configured(cp) and cp.openpilotLongitudinalControl + long_word = cp.safetyConfigs[0].safetyParam + tick = 0 + pedal_sent = False + + def physical(packer, **fields): + nonlocal tick + tick += 1 + if kind == 'hold' and 260 <= tick < 430: + fields.update(stopped=True, brake=tick < 400, cruise=False) + if kind == 'hold' and 430 <= tick < 445: + fields.update(gas=True, cruise=False) + parent_drop = kind == 'parent_drop' and 260 <= tick < 311 + pedal_drop = kind == 'pedal_drop' and 260 <= tick < 311 + if topology != '2FF8' or parent_drop: + fields['sdsu'] = None + frames = helpers.physical(packer, **fields) + if identity in dsu_bypass.ALT_MAIN_CARS: + frames += [packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': fields.get('main', True), 'SET_SPEED': 90}), + packer.make_can_msg('PCM_CRUISE_ALT', 0, {'UI_SET_SPEED': 90})] + if topology == '2FF4': + frames.append((0x2FF, bytes.fromhex('00000409'), 0)) + if topology == '2AA8': + frames.append((0x2AA, bytes(8), 0)) + if topology in ('2AA8', '2FF4', 'camera343_or_4CB8', 'standalone2016'): + frames += [packer.make_can_msg('ACC_CONTROL', 2, {'ACC_TYPE': 1}), packer.make_can_msg('PRE_COLLISION', 2, {})] + if topology == '2FF8': + frames.append(packer.make_can_msg('PRE_COLLISION', 2, {})) + if kind == 'hold' or topology == 'standalone2016': + frames.append(packer.make_can_msg('PRE_COLLISION', 2, {})) + frames.append(packer.make_can_msg('PRE_COLLISION_2', 2, {})) + frames.append(packer.make_can_msg('PCS_HUD', 0 if cp.flags & ToyotaFlags.RADAR_ACC else 2, {})) + if identity == CAR.TOYOTA_RAV4_TSS2_2023: + frames = [frame for frame in frames if frame[0] not in (0x260, 0x262)] + frames += [packer.make_can_msg('STEER_TORQUE_SENSOR', 0, {'STEER_ANGLE': .1, 'STEER_ANGLE_INITIALIZING': 0}), + packer.make_can_msg('EPS_STATUS', 0, {'LKA_STATE': fields.get('eps', 1), 'LTA_STATE': fields.get('eps', 1)})] + frames = [frame for frame in frames if frame[0] != 0xAA] + frames.append(packer.make_can_msg('WHEEL_SPEEDS', 0, { + 'WHEEL_SPEED_' + wheel: 0.0 if fields.get('stopped') else 18.0 for wheel in ('FL', 'FR', 'RL', 'RR')})) + a, d, b = packer.make_can_msg('GAS_SENSOR', 0, { + 'INTERCEPTOR_GAS': 806 if fields.get('gas') else 0, + 'INTERCEPTOR_GAS2': 806 if fields.get('gas') else 0, 'COUNTER_PEDAL': tick & 15, 'STATE': 0}) + data = bytearray(d) + data[-1] = pedal.checksum(data) + if not pedal_drop: + frames.append((a, bytes(data), b)) + if parent_drop and topology != '2FF8': + frames = [frame for frame in frames if not (frame[0] == 0x262 and frame[2] == 0)] + return frames + + def tx_check(tick, frame, cp, safety): + nonlocal pedal_sent + if frame.address == 0x200: + data = bytes(frame.dat) + assert len(data) == 6 and data[-1] == pedal.checksum(data) + if data[4] & 128: + assert not stock and safety.aol_get_permission_mask() & 2 + assert not safety.get_gas_pressed_prev() and not safety.get_brake_pressed_prev() + pedal_sent = True + else: + assert data[:4] == bytes(4) + return True + + def observe(tick, host, cs, cc, safety, native): + if kind == 'hold': + assert host.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD + if 380 <= tick < 425: + assert host.CI.CC.brake_hold_active, (tick, cs.to_dict()) + if 431 <= tick < 445: + assert not host.CI.CC.brake_hold_active + if kind.endswith('_drop') and 301 <= tick < 308: + assert not cc.latActive and not cc.longActive + assert safety.aol_get_permission_mask() == 0 + + scenario = 'main_lease' if kind.endswith('_drop') else 'nominal' if kind == 'ordinary' else kind + helpers.run_case( + stock, scenario, candidate=identity, bridge=bridge, input_clock=True, alpha_long=True, + sdsu_words=(long_word, long_word | 0x200), firmware_override=fw, + fingerprint_frames=[(a, bytes(n), bus) for bus, messages in fp.items() for a, n in messages.items()], + physical_frames=physical, native_tx_expected=tx_check, brake_stop_check_start=355, + angle=identity == CAR.TOYOTA_RAV4_TSS2_2023, + manual_selection=rack == 'EPS73_8965B47070', manual_pedal=rack == 'EPS73_8965B47070', + auto_hold=kind == 'hold', aol=kind != 'ordinary', on_sample=observe, filter_advert=topology == '2FF4', + bypass_owner=topology == 'camera343_or_4CB8', + ) + assert pedal_sent == (not stock) + + +@pytest.mark.parametrize('kind,name,topology,rack', [ + ('parent_drop', 'TOYOTA_CAMRY', '2FF8', 'catalog'), + ('parent_drop', 'TOYOTA_COROLLA', '2FF4', 'catalog'), + ('parent_drop', 'TOYOTA_RAV4_TSS2', '2FF4', 'catalog'), + ('parent_drop', 'TOYOTA_RAV4_TSS2_2022', '2AA8', 'catalog'), + ('parent_drop', 'TOYOTA_RAV4_TSS2', '2AA8', 'catalog'), + ('parent_drop', 'TOYOTA_CAMRY', 'camera343_or_4CB8', 'catalog'), + ('physical', 'TOYOTA_CAMRY', '2FF8', 'catalog'), + ('off', 'TOYOTA_CAMRY', '2FF8', 'catalog'), + ('ordinary', 'TOYOTA_CAMRY', '2FF8', 'catalog'), + ('pedal_drop', 'TOYOTA_CAMRY', '2FF8', 'catalog'), +]) +def test_parent_pedal_physical_withdrawal_and_saved_settings(name, topology, rack, kind, host_heartbeat_bridge): + run(name, topology, rack, False, host_heartbeat_bridge, kind) + + +HOLD_PROFILES = ( + ('TOYOTA_RAV4H', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2FF4', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2AA8', 'catalog'), + ('TOYOTA_COROLLA_TSS2', 'standalone2016', 'catalog'), + ('TOYOTA_RAV4_TSS2', 'standalone2016', 'catalog'), + ('TOYOTA_CAMRY_TSS2', '2FF8', 'catalog'), + ('TOYOTA_RAV4', 'camera343_or_4CB8', 'catalog'), +) + + +@pytest.mark.parametrize('name,topology,rack', HOLD_PROFILES) +def test_shared_hold_full_callers(name, topology, rack, host_heartbeat_bridge): + run(name, topology, rack, False, host_heartbeat_bridge, 'hold') diff --git a/openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py b/openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py new file mode 100644 index 0000000000..396ea99e49 --- /dev/null +++ b/openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py @@ -0,0 +1,222 @@ +"""Every scoped Dom pedal/parent combination retains the exact current parent tune.""" +import pytest +from opendbc.car import structs +from opendbc.car.toyota import dsu_bypass, pedal_composite +from opendbc.car.toyota.interface import CarInterface +from opendbc.car.toyota.values import CAR, ToyotaFlags +from openpilot.starpilot.car.toyota.aol import qualified +from openpilot.starpilot.car.toyota.tests.test_dsu_bypass import acquired, fingerprint as bypass_fingerprint + +ROWS = [ + ('LEXUS_NX', '2FF8', 'catalog'), + ('LEXUS_RX', '2FF8', 'catalog'), + ('TOYOTA_AVALON', '2FF8', 'catalog'), + ('TOYOTA_HIGHLANDER', '2FF8', 'catalog'), + ('TOYOTA_RAV4H', '2FF8', 'catalog'), + ('TOYOTA_SIENNA', '2FF8', 'catalog'), + ('LEXUS_ES_TSS2', '2FF8', 'catalog'), + ('LEXUS_IS_TSS2', '2FF8', 'catalog'), + ('LEXUS_LC_TSS2', '2FF8', 'catalog'), + ('LEXUS_LS', '2FF8', 'catalog'), + ('LEXUS_NX_TSS2', '2FF8', 'catalog'), + ('LEXUS_RC_TSS2', '2FF8', 'catalog'), + ('LEXUS_RX_TSS2', '2FF8', 'catalog'), + ('TOYOTA_ALPHARD_TSS2', '2FF8', 'catalog'), + ('TOYOTA_AVALON_2019', '2FF8', 'catalog'), + ('TOYOTA_AVALON_TSS2', '2FF8', 'catalog'), + ('TOYOTA_CAMRY', '2FF8', 'catalog'), + ('TOYOTA_CAMRY_TSS2', '2FF8', 'catalog'), + ('TOYOTA_CHR', '2FF8', 'catalog'), + ('TOYOTA_CHR_TSS2', '2FF8', 'catalog'), + ('TOYOTA_COROLLA_TSS2', '2FF8', 'catalog'), + ('TOYOTA_HIGHLANDER_TSS2', '2FF8', 'catalog'), + ('TOYOTA_MIRAI', '2FF8', 'catalog'), + ('TOYOTA_PRIUS_TSS2', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2022', '2FF8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2023', '2FF8', 'catalog'), + ('LEXUS_CTH', '2FF8', 'catalog'), + ('LEXUS_ES', '2FF8', 'catalog'), + ('LEXUS_GS_F', '2FF8', 'catalog'), + ('LEXUS_IS', '2FF8', 'catalog'), + ('LEXUS_RC', '2FF8', 'catalog'), + ('TOYOTA_COROLLA', '2FF8', 'catalog'), + ('TOYOTA_MATRIX_RETROFIT', '2FF8', 'catalog'), + ('TOYOTA_PRIUS_V', '2FF8', 'catalog'), + ('TOYOTA_RAV4', '2FF8', 'catalog'), + ('TOYOTA_PRIUS', '2FF8', 'catalog'), + ('LEXUS_CTH', '2FF4', 'catalog'), + ('LEXUS_ES', '2FF4', 'catalog'), + ('LEXUS_GS_F', '2FF4', 'catalog'), + ('LEXUS_IS', '2FF4', 'catalog'), + ('LEXUS_NX', '2FF4', 'catalog'), + ('LEXUS_RC', '2FF4', 'catalog'), + ('LEXUS_RX', '2FF4', 'catalog'), + ('TOYOTA_AVALON', '2FF4', 'catalog'), + ('TOYOTA_COROLLA', '2FF4', 'catalog'), + ('TOYOTA_HIGHLANDER', '2FF4', 'catalog'), + ('TOYOTA_MATRIX_RETROFIT', '2FF4', 'catalog'), + ('TOYOTA_PRIUS_V', '2FF4', 'catalog'), + ('TOYOTA_RAV4', '2FF4', 'catalog'), + ('TOYOTA_RAV4H', '2FF4', 'catalog'), + ('TOYOTA_SIENNA', '2FF4', 'catalog'), + ('LEXUS_ES_TSS2', '2FF4', 'catalog'), + ('LEXUS_IS_TSS2', '2FF4', 'catalog'), + ('LEXUS_LC_TSS2', '2FF4', 'catalog'), + ('LEXUS_LS', '2FF4', 'catalog'), + ('LEXUS_NX_TSS2', '2FF4', 'catalog'), + ('LEXUS_RC_TSS2', '2FF4', 'catalog'), + ('LEXUS_RX_TSS2', '2FF4', 'catalog'), + ('TOYOTA_ALPHARD_TSS2', '2FF4', 'catalog'), + ('TOYOTA_AVALON_2019', '2FF4', 'catalog'), + ('TOYOTA_AVALON_TSS2', '2FF4', 'catalog'), + ('TOYOTA_CAMRY_TSS2', '2FF4', 'catalog'), + ('TOYOTA_COROLLA_TSS2', '2FF4', 'catalog'), + ('TOYOTA_HIGHLANDER_TSS2', '2FF4', 'catalog'), + ('TOYOTA_MIRAI', '2FF4', 'catalog'), + ('TOYOTA_PRIUS_TSS2', '2FF4', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2FF4', 'catalog'), + ('TOYOTA_CAMRY', '2FF4', 'catalog'), + ('TOYOTA_CHR', '2FF4', 'catalog'), + ('TOYOTA_CHR_TSS2', '2FF4', 'catalog'), + ('TOYOTA_RAV4_TSS2_2022', '2FF4', 'catalog'), + ('TOYOTA_RAV4_TSS2_2023', '2FF4', 'catalog'), + ('TOYOTA_PRIUS', '2FF4', 'catalog'), + ('LEXUS_ES_TSS2', '2AA8', 'catalog'), + ('LEXUS_IS_TSS2', '2AA8', 'catalog'), + ('LEXUS_LC_TSS2', '2AA8', 'catalog'), + ('LEXUS_NX_TSS2', '2AA8', 'catalog'), + ('LEXUS_RC_TSS2', '2AA8', 'catalog'), + ('LEXUS_RX_TSS2', '2AA8', 'catalog'), + ('TOYOTA_ALPHARD_TSS2', '2AA8', 'catalog'), + ('TOYOTA_AVALON_TSS2', '2AA8', 'catalog'), + ('TOYOTA_CAMRY_TSS2', '2AA8', 'catalog'), + ('TOYOTA_COROLLA_TSS2', '2AA8', 'catalog'), + ('TOYOTA_HIGHLANDER_TSS2', '2AA8', 'catalog'), + ('TOYOTA_MIRAI', '2AA8', 'catalog'), + ('TOYOTA_PRIUS_TSS2', '2AA8', 'catalog'), + ('TOYOTA_RAV4_TSS2', '2AA8', 'catalog'), + ('TOYOTA_CAMRY', '2AA8', 'catalog'), + ('TOYOTA_CHR', '2AA8', 'catalog'), + ('TOYOTA_CHR_TSS2', '2AA8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2022', '2AA8', 'catalog'), + ('TOYOTA_RAV4_TSS2_2023', '2AA8', 'catalog'), + ('TOYOTA_PRIUS', '2FF8', 'EPS73_8965B47070'), + ('TOYOTA_PRIUS_RETROFIT', '2FF8', 'EPS73_8965B47070'), + ('TOYOTA_PRIUS', '2FF4', 'EPS73_8965B47070'), + ('TOYOTA_PRIUS_RETROFIT', '2FF4', 'EPS73_8965B47070'), + ('LEXUS_CTH', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_ES', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_GS_F', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_IS', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_LS', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_NX', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_RC', 'camera343_or_4CB8', 'catalog'), + ('LEXUS_RX', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_AVALON', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_AVALON_2019', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_CAMRY', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_CHR', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_COROLLA', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_HIGHLANDER', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_MATRIX_RETROFIT', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_PRIUS', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_PRIUS_RETROFIT', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_PRIUS_V', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_RAV4', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_RAV4H', 'camera343_or_4CB8', 'catalog'), + ('TOYOTA_SIENNA', 'camera343_or_4CB8', 'catalog'), +] + + +def sources(identity, topology, rack): + fp = bypass_fingerprint(identity) if topology == 'camera343_or_4CB8' else {0: {0x260: 8, 0x262: 8, 0x1D2: 8, 0x1D3: 8, 0x224: 8, 0x226: 8}, 1: {}, 2: {}} + if topology in ('2FF8', '2FF4', '2AA8'): + fp[0][0x2AA if topology == '2AA8' else 0x2FF] = 4 if topology == '2FF4' else 8 + if identity in dsu_bypass.PROFILES: + fp[0][0x262] = 8 if identity in dsu_bypass.EPS8_CARS or identity == CAR.TOYOTA_PRIUS else 5 + if rack == 'EPS73_8965B47070': + fp[0][0x262] = 8 + fw = acquired(identity) + if rack == 'EPS73_8965B47070': + for row in fw: + if row.ecu == structs.CarParams.Ecu.eps: + row.fwVersion = b'8965B47070' + bytes(6) + return fp, fw + + +@pytest.mark.parametrize('name,topology,rack', ROWS) +@pytest.mark.parametrize('stock', (False, True)) +@pytest.mark.parametrize('alpha_long', (False, True)) +def test_all_parent_tunes_native_projection_and_stock_restore(name, topology, rack, stock, alpha_long): + identity = getattr(CAR, name) + fp, fw = sources(identity, topology, rack) + parent = CarInterface.get_params(identity, fp, fw, alpha_long, False, False) + child_fp = {bus: dict(frames) for bus, frames in fp.items()} + child_fp[0][0x201] = 6 + cp = CarInterface.get_params(identity, child_fp, fw, alpha_long, False, False) + assert pedal_composite.configured(cp) and qualified(cp) + assert cp.flags & ToyotaFlags.GAS_INTERCEPTOR + assert cp.minEnableSpeed == -1. + projected = pedal_composite.parent_cp(cp) + assert projected.safetyConfigs[0].safetyParam == parent.safetyConfigs[0].safetyParam + for field in ( + 'lateralTuning', 'longitudinalTuning', 'steerActuatorDelay', 'steerLimitTimer', 'longitudinalActuatorDelay', + 'minSteerSpeed', 'wheelSpeedFactor', 'mass', 'wheelbase', 'steerRatio', 'centerToFront', 'autoResumeSng', + ): + assert cp.to_dict()[field] == parent.to_dict()[field], field + if stock: + assert pedal_composite.prepare_stock(cp) + assert pedal_composite.configured(cp) and qualified(cp) and not cp.openpilotLongitudinalControl + assert cp.safetyConfigs[0].safetyParam == (parent.safetyConfigs[0].safetyParam | 0x2200) + + +@pytest.mark.parametrize('name,topology,rack', ROWS) +def test_dom_low_speed_entry_requires_qualified_pedal(name, topology, rack): + identity = getattr(CAR, name) + fp, fw = sources(identity, topology, rack) + base = CarInterface.get_params(identity, fp, fw, True, False, False) + fp[0][0x201] = 6 + cp = CarInterface.get_params(identity, fp, fw, True, False, False) + assert pedal_composite.configured(cp) and cp.minEnableSpeed == -1. + for field in ('lateralTuning', 'longitudinalTuning', 'longitudinalActuatorDelay', 'stopAccel'): + assert cp.to_dict()[field] == base.to_dict()[field], field + fp[0][0x201] = 7 + rejected = CarInterface.get_params(identity, fp, fw, True, False, False) + assert not pedal_composite.configured(rejected) + assert not rejected.flags & ToyotaFlags.GAS_INTERCEPTOR + + +def test_shared_hold_car_mappings(): + from opendbc.car.toyota.interface import apply_toyota_auto_hold + from opendbc.car.toyota.values import TOYOTA_AUTO_HOLD_CARS, uses_toyota_auto_hold_aeb + for name, topology, rack in ROWS: + identity = getattr(CAR, name) + if identity not in TOYOTA_AUTO_HOLD_CARS: + fp, fw = sources(identity, topology, rack) + fp[0][0x201] = 6 + rejected = CarInterface.get_params(identity, fp, fw, True, False, False) + assert not apply_toyota_auto_hold(rejected, True) + continue + for has_pedal in (False, True): + fp, fw = sources(identity, topology, rack) + if has_pedal: + fp[0][0x201] = 6 + cp = CarInterface.get_params(identity, fp, fw, True, False, False) + original = cp.to_dict() + assert apply_toyota_auto_hold(cp, True), (name, topology, has_pedal) + permission = 256 if uses_toyota_auto_hold_aeb(cp) else 128 + assert cp.alternativeExperience & (128 | 256) == permission + assert qualified(cp) + for key in ('lateralTuning', 'longitudinalTuning', 'longitudinalActuatorDelay', 'stopAccel'): + assert cp.to_dict()[key] == original[key] + assert not apply_toyota_auto_hold(cp, False) + assert not cp.flags & ToyotaFlags.AUTO_BRAKE_HOLD + + from opendbc.car.toyota.pedal import CAMERA_PEDAL_CARS + for identity in CAMERA_PEDAL_CARS & TOYOTA_AUTO_HOLD_CARS: + fp, fw = sources(identity, 'standalone2016', 'catalog') + fp[0][0x201] = 6 + cp = CarInterface.get_params(identity, fp, fw, True, False, False) + assert apply_toyota_auto_hold(cp, True), identity + assert qualified(cp) diff --git a/openpilot/starpilot/car/toyota/tests/test_rav4h_caller_native.py b/openpilot/starpilot/car/toyota/tests/test_rav4h_caller_native.py index b9aefa6fc8..fe5722f7fb 100644 --- a/openpilot/starpilot/car/toyota/tests/test_rav4h_caller_native.py +++ b/openpilot/starpilot/car/toyota/tests/test_rav4h_caller_native.py @@ -217,7 +217,7 @@ def control_inputs(controls, published, state, sd_events, native, now): if name == 'modelV2': v.from_dict(model().to_dict()) elif name == 'lateralManeuverPlan': - v.desiredCurvature = .00005 + v.desiredCurvature = .0002 if controls.CP.steerControlType == structs.CarParams.SteerControlType.angle else .00005 elif name == 'vehicleParameters': v.stiffnessFactor, v.steerRatio = 1.0, controls.CP.steerRatio elif name == 'longitudinalPlan': @@ -232,7 +232,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, sdsu_words=(0x9049, 0x9249), alpha_long=False, disable_setting=None, physical_frames=None, angle=False, firmware_override=None, assert_hybrid_release=False, sdsu_mirror=False, on_sample=None, camera_messages=False, filter_advert=False, brake_pause_mps=None, native_rx_expected=None, on_state=None, fingerprint_frames=None, manual_selection=False, native_tx_expected=None, - manual_filter_length=None, manual_bypass=False, bypass_owner=False): + manual_filter_length=None, manual_bypass=False, bypass_owner=False, manual_pedal=False, auto_hold=False): SYNTHETIC_LAST.clear() NATIVE_LAST.clear() safety = libsafety_py.libsafety @@ -245,14 +245,23 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, ('AlwaysOnLateral', aol), ('DisableOpenpilotLongitudinal', stock if disable_setting is None else disable_setting), ('AlphaLongitudinalEnabled', alpha_long), - ('ToyotaAutoHold', False), + ('ToyotaAutoHold', auto_hold), ): params.put_bool(k, v, block=True) if brake_pause_mps is not None: params.put('AolBrakePauseSpeedMps', float(brake_pause_mps), block=True) fw = firmware(candidate) if firmware_override is None else firmware_override if manual_selection: - if manual_bypass: + if manual_pedal: + from opendbc.car.toyota import pedal_composite + assert candidate in (CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT, CAR.TOYOTA_MATRIX_RETROFIT) + assert fingerprint_frames is not None + acquired_fp = gen_empty_fingerprint() + for address, data, bus in fingerprint_frames: + acquired_fp[bus][address] = len(data) + assert acquired_fp[0].get(0x201) == 6 + assert pedal_composite.hardware_supported(candidate, acquired_fp, fw) + elif manual_bypass: assert fingerprint_frames is not None assert candidate in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT) and bypass_owner assert any(bus == 2 and address in (0x343, 0x4CB) and len(data) == 8 for address, data, bus in fingerprint_frames) @@ -302,7 +311,8 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, host = card_module.Car() assert host.CP.openpilotLongitudinalControl == (not stock) assert host.CP.safetyConfigs[0].safetyParam == sdsu_words[stock] - assert host.CP.alternativeExperience == 32 + from opendbc.car.toyota.values import uses_toyota_auto_hold_aeb + assert host.CP.alternativeExperience == (32 | ((256 if uses_toyota_auto_hold_aeb(host.CP) else 128) if auto_hold else 0)) controls, sd = Controls(), sd_module.SelfdriveD(CP=host.CP) assert controls.CP.to_dict() == host.CP.to_dict() safety.init_tests() @@ -357,7 +367,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, ) try: duration = { - 'nominal': 251, 'physical': 601, 'faults': 801, 'bad_dlc': 801, + 'hold': 501, 'nominal': 251, 'physical': 601, 'faults': 801, 'bad_dlc': 801, 'off': 401, 'cold_low': 251, 'hybrid_stop': 251, 'main_lease': 501, 'identity_refusal': 501, }[scenario] for tick in range(1, duration): @@ -541,7 +551,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, } ) nominal = tick >= 220 and ( - scenario in ('nominal', 'cold_low', 'hybrid_stop') + scenario in ('nominal', 'cold_low', 'hybrid_stop') or scenario == 'hold' and tick < 250 or scenario == 'identity_refusal' and tick < 250 or scenario == 'main_lease' and (tick < 250 or tick >= 480) or scenario == 'off' @@ -583,9 +593,11 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H, assert not host.CP.openpilotLongitudinalControl and not cc.longActive assert host.CP.safetyConfigs[0].safetyParam == sdsu_words[1] else: - preference = host.CI.CC.bypass_input if bypass_owner else host.CI.CC.prius_filter_input if candidate == CAR.TOYOTA_PRIUS else ( + from opendbc.car.toyota import pedal_composite + preference = (host.CI.CC.pedal_input if pedal_composite.configured(host.CP) else + host.CI.CC.bypass_input if bypass_owner else host.CI.CC.prius_filter_input if candidate == CAR.TOYOTA_PRIUS else ( host.CI.CC.advert_filter_input if filter_advert or fingerprint_frames is not None and any( - address == 0x2AA for address, _, _ in fingerprint_frames) else host.CI.CC.rav4h_filter_input) + address == 0x2AA for address, _, _ in fingerprint_frames) else host.CI.CC.rav4h_filter_input)) assert preference is not None and not preference.update(time.monotonic_ns()) for f in outgoing_frames: if f.address == 0x343: diff --git a/prebuilt.json b/prebuilt.json index 592f02b2e5..485d6f1ec2 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "2ee01a31422021061a0529083c7b434e52f1f6dd44d684ec0921074808613513", "schema_version": 1} +{"protected_inventory_sha256": "6b9f162e57c2f68775793717e2f9e4a44f209169384c47a934c2033cd898668a", "schema_version": 1} diff --git a/tools/ci/run_vehicle_tests.py b/tools/ci/run_vehicle_tests.py index 715ed8c88e..46239c915c 100644 --- a/tools/ci/run_vehicle_tests.py +++ b/tools/ci/run_vehicle_tests.py @@ -45,6 +45,9 @@ AOL_LIFECYCLE_TARGETS = ( 'openpilot/starpilot/car/toyota/tests/test_dsu_bypass.py', 'openpilot/starpilot/car/toyota/tests/test_dsu_bypass_callers.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_dsu_bypass.py', + 'openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py', + 'openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py', + 'opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_radar_lta_pedal.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_prius_retrofit_advert.py', 'openpilot/starpilot/aol/tests/test_crossmake_native_admission.py', @@ -252,6 +255,9 @@ RELEASE_TARGETS = ( 'openpilot/starpilot/car/toyota/tests/test_dsu_bypass.py', 'openpilot/starpilot/car/toyota/tests/test_dsu_bypass_callers.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_dsu_bypass.py', + 'openpilot/starpilot/car/toyota/tests/test_pedal_parent_configurations.py', + 'openpilot/starpilot/car/toyota/tests/test_pedal_parent_callers.py', + 'opendbc_repo/opendbc/safety/tests/test_toyota_pedal_parent.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_radar_lta_pedal.py', 'opendbc_repo/opendbc/safety/tests/test_toyota_prius_retrofit_advert.py', 'opendbc_repo/opendbc/car/hyundai/tests/test_long_cp_copy.py',