From 465ac2651a79c5a199cd889e566cd171a5469112 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:55:16 -0500 Subject: [PATCH] ICCU FAILURE --- opendbc_repo/opendbc/car/subaru/carstate.py | 4 +-- opendbc_repo/opendbc/car/subaru/subarucan.py | 2 +- .../opendbc/car/subaru/tests/test_subaru.py | 15 +++++++++++ .../controls/lib/latcontrol_vehicle_tunes.py | 4 +-- selfdrive/ui/lib/starpilot_status.py | 9 +++---- selfdrive/ui/tests/test_starpilot_status.py | 27 ++++++++++++++----- system/hardware/base.py | 3 ++- system/hardware/tici/hardware.py | 2 +- .../hardware/tici/tests/test_modem_config.py | 20 ++++++++++++++ 9 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 system/hardware/tici/tests/test_modem_config.py diff --git a/opendbc_repo/opendbc/car/subaru/carstate.py b/opendbc_repo/opendbc/car/subaru/carstate.py index c29f0b8c4..b91767d67 100644 --- a/opendbc_repo/opendbc/car/subaru/carstate.py +++ b/opendbc_repo/opendbc/car/subaru/carstate.py @@ -4,7 +4,7 @@ from opendbc.can import CANDefine, CANParser from opendbc.car import Bus, structs from opendbc.car.common.conversions import Conversions as CV from opendbc.car.interfaces import CarStateBase -from opendbc.car.subaru.values import CAR, DBC, CanBus, SubaruFlags +from opendbc.car.subaru.values import DBC, CanBus, SUBARU_STOP_START_CARS, SubaruFlags from opendbc.car import CanSignalRateCalculator @@ -26,7 +26,7 @@ class CarState(CarStateBase): cp_angle = cp_main if self.CP.flags & SubaruFlags.D_PLATFORM else cp ret = structs.CarState() - if self.CP.carFingerprint == CAR.SUBARU_OUTBACK_2023: + if self.CP.carFingerprint in SUBARU_STOP_START_CARS: self.dashlights_msg = copy.copy(cp.vl["Dashlights"]) self.stop_start_state = cp.vl["Engine_Stop_Start"]["STOP_START_STATE"] diff --git a/opendbc_repo/opendbc/car/subaru/subarucan.py b/opendbc_repo/opendbc/car/subaru/subarucan.py index 39c352f75..9b8db0d6d 100644 --- a/opendbc_repo/opendbc/car/subaru/subarucan.py +++ b/opendbc_repo/opendbc/car/subaru/subarucan.py @@ -183,7 +183,7 @@ def create_es_dashstatus(packer, frame, dashstatus_msg, enabled, long_enabled, l def create_stop_start_control(packer, dashlights_msg, counter=None, bus=CanBus.alt): - """Create the Outback 2023-24 momentary Stop/Start button request. + """Create the supported Subaru momentary Stop/Start button request. Dashlights is a stock periodic message, so preserve the live frame and only change the event bit. CANPacker calculates the Subaru checksum for us. diff --git a/opendbc_repo/opendbc/car/subaru/tests/test_subaru.py b/opendbc_repo/opendbc/car/subaru/tests/test_subaru.py index bc612530d..bb284144f 100644 --- a/opendbc_repo/opendbc/car/subaru/tests/test_subaru.py +++ b/opendbc_repo/opendbc/car/subaru/tests/test_subaru.py @@ -206,6 +206,21 @@ def test_outback_2023_uses_d_platform_bus_layout(): assert CP.lateralSmoothSeconds == pytest.approx(0.4) +@pytest.mark.parametrize("platform", [CAR.SUBARU_OUTBACK_2023, CAR.SUBARU_LEGACY_2025]) +def test_stop_start_inputs_are_captured_for_supported_models(platform): + CP = CarInterface.get_non_essential_params(platform) + car_state = CarState(CP, None) + parsers = car_state.get_can_parsers(CP) + parsers[Bus.pt].vl["Dashlights"]["COUNTER"] = 6 + parsers[Bus.pt].vl["Dashlights"]["STOP_START"] = 0 + parsers[Bus.pt].vl["Engine_Stop_Start"]["STOP_START_STATE"] = 3 + + car_state.update(parsers, SimpleNamespace(subaru_sng=False)) + + assert car_state.dashlights_msg["COUNTER"] == 6 + assert car_state.stop_start_state == 3 + + @pytest.mark.parametrize("platform, expected_bus", [ (CAR.SUBARU_OUTBACK_2023, CanBus.alt), (CAR.SUBARU_LEGACY_2025, CanBus.main), diff --git a/selfdrive/controls/lib/latcontrol_vehicle_tunes.py b/selfdrive/controls/lib/latcontrol_vehicle_tunes.py index 13231f5a3..ce2b4220b 100644 --- a/selfdrive/controls/lib/latcontrol_vehicle_tunes.py +++ b/selfdrive/controls/lib/latcontrol_vehicle_tunes.py @@ -218,12 +218,12 @@ GENESIS_GV70_FRICTION_CENTER_LAT = 0.28 GENESIS_GV70_FRICTION_CENTER_LAT_WIDTH = 0.12 GENESIS_GV70_FRICTION_CALM_JERK = 0.35 GENESIS_GV70_FRICTION_CALM_JERK_WIDTH = 0.10 -GENESIS_GV70_FRICTION_JERK_DEADZONE_MAX = 0.30 +GENESIS_GV70_FRICTION_JERK_DEADZONE_MAX = 0.36 GENESIS_GV70_FRICTION_JERK_DEADZONE_LAT = 0.30 GENESIS_GV70_FRICTION_JERK_DEADZONE_LAT_WIDTH = 0.08 GENESIS_GV70_FRICTION_JERK_DEADZONE_SPEED = 12.0 * CV.MPH_TO_MS GENESIS_GV70_FRICTION_JERK_DEADZONE_SPEED_WIDTH = 3.5 * CV.MPH_TO_MS -GENESIS_GV70_CENTER_OUTPUT_TAPER_MAX = 0.14 +GENESIS_GV70_CENTER_OUTPUT_TAPER_MAX = 0.20 GENESIS_GV70_CENTER_OUTPUT_TAPER_LAT = 0.30 GENESIS_GV70_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.10 GENESIS_GV70_CENTER_OUTPUT_TAPER_SPEED = 22.0 * CV.MPH_TO_MS diff --git a/selfdrive/ui/lib/starpilot_status.py b/selfdrive/ui/lib/starpilot_status.py index 161c23457..c3d263284 100644 --- a/selfdrive/ui/lib/starpilot_status.py +++ b/selfdrive/ui/lib/starpilot_status.py @@ -18,13 +18,12 @@ LONGITUDINAL_ONLY_COLOR = rl.Color(255, 105, 180, 255) def is_longitudinal_only_active(state: UIState) -> bool: - """Return true when control is enabled but lateral control is inactive. + """Return true when the user has paused lateral while control is enabled. - Do not use carControl.longActive here: stock-cruise cars intentionally leave - that field false while the vehicle's own longitudinal controller is active. + carControl.latActive also becomes false when lateral is temporarily unavailable, + such as below minimum steer speed, so it does not represent user intent. """ - car_control = state.sm["carControl"] - return bool(state.sm["selfdriveState"].enabled and not car_control.latActive) + return bool(state.sm["selfdriveState"].enabled and state.sm["starpilotCarState"].pauseLateral) def _override_color_applies(state: UIState) -> bool: diff --git a/selfdrive/ui/tests/test_starpilot_status.py b/selfdrive/ui/tests/test_starpilot_status.py index e2c4dc3c9..2fcaf4d0b 100644 --- a/selfdrive/ui/tests/test_starpilot_status.py +++ b/selfdrive/ui/tests/test_starpilot_status.py @@ -12,11 +12,12 @@ from openpilot.selfdrive.ui.lib.starpilot_status import ( from openpilot.selfdrive.ui.ui_state import UIStatus -def _state(*, enabled=False, lat_active=False, aol=False, status=None, events=()): +def _state(*, enabled=False, lat_active=False, pause_lateral=False, aol=False, status=None, events=()): return SimpleNamespace( sm={ "selfdriveState": SimpleNamespace(enabled=enabled, experimentalMode=False), "carControl": SimpleNamespace(latActive=lat_active), + "starpilotCarState": SimpleNamespace(pauseLateral=pause_lateral), "onroadEvents": events, }, status=status if status is not None else (UIStatus.ENGAGED if enabled else UIStatus.DISENGAGED), @@ -31,8 +32,22 @@ def _rgb(color): return color.r, color.g, color.b -def test_cruise_only_uses_pink_for_border_and_screen_edge(): - state = _state(enabled=True, lat_active=False) +def test_manual_lateral_pause_uses_pink_for_border_and_screen_edge(): + state = _state(enabled=True, lat_active=False, pause_lateral=True) + + assert _rgb(get_border_color(state)) == _rgb(LONGITUDINAL_ONLY_COLOR) + assert _rgb(get_screen_edge_color(state)) == _rgb(LONGITUDINAL_ONLY_COLOR) + + +def test_temporary_lateral_inactivity_does_not_use_pink(): + state = _state(enabled=True, lat_active=False, pause_lateral=False) + + assert _rgb(get_border_color(state)) == _rgb(ENGAGED_COLOR) + assert _rgb(get_screen_edge_color(state)) == _rgb(ENGAGED_COLOR) + + +def test_manual_pause_signal_controls_pink_independent_of_lat_active_timing(): + state = _state(enabled=True, lat_active=True, pause_lateral=True) assert _rgb(get_border_color(state)) == _rgb(LONGITUDINAL_ONLY_COLOR) assert _rgb(get_screen_edge_color(state)) == _rgb(LONGITUDINAL_ONLY_COLOR) @@ -53,10 +68,10 @@ def test_override_color_matches_active_control_mode(): assert _rgb(get_border_color(_state(enabled=True, lat_active=True, status=override, events=[lateral_override]))) == _rgb(OVERRIDE_COLOR) assert _rgb(get_border_color(_state(enabled=True, lat_active=True, status=override, events=[longitudinal_override]))) == _rgb(OVERRIDE_COLOR) - # Pink: steering is already inactive, so only a longitudinal override is gray. - pink_state = _state(enabled=True, status=override, events=[lateral_override]) + pink_state = _state(enabled=True, pause_lateral=True, status=override, events=[lateral_override]) assert _rgb(get_border_color(pink_state)) == _rgb(LONGITUDINAL_ONLY_COLOR) - assert _rgb(get_border_color(_state(enabled=True, status=override, events=[longitudinal_override]))) == _rgb(OVERRIDE_COLOR) + assert _rgb(get_border_color(_state(enabled=True, pause_lateral=True, status=override, + events=[longitudinal_override]))) == _rgb(OVERRIDE_COLOR) # Blue/AOL: longitudinal override is inactive, so only steering is gray. assert _rgb(get_border_color(_state(aol=True, status=override, events=[lateral_override]))) == _rgb(OVERRIDE_COLOR) diff --git a/system/hardware/base.py b/system/hardware/base.py index d0a4833cc..306f3c5c4 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -94,7 +94,8 @@ class LPABase(ABC): def switch_profile(self, iccid: str) -> None: pass - def is_comma_profile(self, iccid: str) -> bool: + @staticmethod + def is_comma_profile(iccid: str) -> bool: return any(iccid.startswith(prefix) for prefix in ('8985235',)) class HardwareBase(ABC): diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index eacc69b0b..f39160ac1 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -518,7 +518,7 @@ class Tici(HardwareBase): # eSIM prime dest = "/etc/NetworkManager/system-connections/esim.nmconnection" - if self.get_sim_lpa().is_comma_profile(sim_id) and not os.path.exists(dest): + if LPABase.is_comma_profile(sim_id) and not os.path.exists(dest): with open(Path(__file__).parent/'esim.nmconnection') as f, tempfile.NamedTemporaryFile(mode='w') as tf: dat = f.read() dat = dat.replace("sim-id=", f"sim-id={sim_id}") diff --git a/system/hardware/tici/tests/test_modem_config.py b/system/hardware/tici/tests/test_modem_config.py new file mode 100644 index 000000000..6f0deeed0 --- /dev/null +++ b/system/hardware/tici/tests/test_modem_config.py @@ -0,0 +1,20 @@ +from openpilot.system.hardware.base import LPABase +from openpilot.system.hardware.tici.hardware import Tici + + +def test_comma_profile_detection_without_lpa(mocker): + hardware = Tici() + modem = mocker.MagicMock() + modem.Get.return_value = "Quectel" + + mocker.patch.object(hardware, "get_sim_info", return_value={"sim_id": "8985235000000000000"}) + mocker.patch.object(hardware, "get_modem", return_value=modem) + mocker.patch.object(hardware, "get_device_type", return_value="mici") + get_sim_lpa = mocker.patch.object(hardware, "get_sim_lpa") + mocker.patch("openpilot.system.hardware.tici.hardware.os.path.exists", return_value=True) + + hardware.configure_modem() + + get_sim_lpa.assert_not_called() + assert LPABase.is_comma_profile("8985235000000000000") + assert not LPABase.is_comma_profile("8900000000000000000")