diff --git a/opendbc_repo/opendbc/car/hyundai/carcontroller.py b/opendbc_repo/opendbc/car/hyundai/carcontroller.py index 5993ef900..c71a73ee1 100644 --- a/opendbc_repo/opendbc/car/hyundai/carcontroller.py +++ b/opendbc_repo/opendbc/car/hyundai/carcontroller.py @@ -415,7 +415,7 @@ class CarController(CarControllerBase): self._ev9_long_tuning = EV9LongitudinalTuningState() self._left_blindspot_warning = BlindspotWarningState() self._right_blindspot_warning = BlindspotWarningState() - self.long_active_ecu = self.CP.openpilotLongitudinalControl + self.long_active_ecu = self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.NON_SCC) self._ioniq_6_lane_change_ui_side = None self._ioniq_6_lane_change_ui_frames = 0 self._ioniq_6_long_tuning = Ioniq6LongitudinalTuningState() @@ -594,7 +594,7 @@ class CarController(CarControllerBase): # When ECU disable was skipped (car started in READY mode), don't send any # longitudinal messages - stock ECU is still active and these would conflict - self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed + self.long_active_ecu = self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.NON_SCC) and not self.ecu_disable_failed use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \ actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping) diff --git a/opendbc_repo/opendbc/car/hyundai/carstate.py b/opendbc_repo/opendbc/car/hyundai/carstate.py index 4ab4f5434..b9b316fdd 100644 --- a/opendbc_repo/opendbc/car/hyundai/carstate.py +++ b/opendbc_repo/opendbc/car/hyundai/carstate.py @@ -312,19 +312,19 @@ class CarState(CarStateBase): # cruise state no_scc = bool(self.CP.flags & HyundaiFlags.NON_SCC) - if self.CP.openpilotLongitudinalControl: - # These are not used for engage/disengage since openpilot keeps track of state using the buttons - ret.cruiseState.available = cp.vl["TCS13"]["ACCEnable"] == 0 - ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1 - ret.cruiseState.standstill = False - ret.cruiseState.nonAdaptive = False - elif no_scc: + if no_scc: cruise_available_msg, cruise_available_sig, cruise_enabled_msg, cruise_enabled_sig, cruise_speed_msg, cruise_speed_sig = get_non_scc_cruise_signals(self.CP) ret.cruiseState.available = cp.vl[cruise_available_msg][cruise_available_sig] != 0 ret.cruiseState.enabled = cp.vl[cruise_enabled_msg][cruise_enabled_sig] != 0 ret.cruiseState.standstill = False ret.cruiseState.nonAdaptive = False ret.cruiseState.speed = cp.vl[cruise_speed_msg][cruise_speed_sig] * speed_conv + elif self.CP.openpilotLongitudinalControl: + # These are not used for engage/disengage since openpilot keeps track of state using the buttons + ret.cruiseState.available = cp.vl["TCS13"]["ACCEnable"] == 0 + ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1 + ret.cruiseState.standstill = False + ret.cruiseState.nonAdaptive = False else: scc_msg = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED else "SCC11" ret.cruiseState.available = cp_cruise.vl[scc_msg]["MainMode_ACC"] == 1 diff --git a/opendbc_repo/opendbc/car/hyundai/interface.py b/opendbc_repo/opendbc/car/hyundai/interface.py index e7244462b..a1d056266 100644 --- a/opendbc_repo/opendbc/car/hyundai/interface.py +++ b/opendbc_repo/opendbc/car/hyundai/interface.py @@ -334,7 +334,7 @@ class CarInterface(CarInterfaceBase): ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===") - if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)): + if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.NON_SCC | HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)): addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & (HyundaiFlags.CANFD | HyundaiFlags.CAN_CANFD_BLENDED) else 0 if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value: addr, bus = 0x730, CanBus(CP).ECAN diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py index 7c3b228ef..4d96e7dbd 100644 --- a/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_hyundai.py @@ -613,12 +613,35 @@ class TestHyundaiFingerprint: non_scc_fpcp = CarInterface.get_starpilot_params(CAR.KIA_FORTE_2021_NON_SCC, gen_empty_fingerprint(), [], non_scc_cp, toggles) assert non_scc_fpcp.redneckCruiseAvailable assert not non_scc_fpcp.pcmCruiseSpeed + assert non_scc_cp.openpilotLongitudinalControl + assert non_scc_cp.pcmCruise + assert not non_scc_cp.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG + assert not CarInterface(non_scc_cp, non_scc_fpcp).CC.long_active_ecu canfd_alt_buttons_cp = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], False, False, False, toggles) canfd_alt_buttons_fpcp = CarInterface.get_starpilot_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], canfd_alt_buttons_cp, toggles) assert canfd_alt_buttons_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS assert not canfd_alt_buttons_fpcp.redneckCruiseAvailable + def test_hyundai_non_scc_without_redneck_keeps_stock_longitudinal_mode(self, monkeypatch): + class FakeParams: + def __init__(self, *args, **kwargs): + pass + + @staticmethod + def get_bool(key): + return False + + toggles = get_test_toggles() + monkeypatch.setattr("opendbc.car.interfaces.Params", FakeParams) + + CP = CarInterface.get_params(CAR.KIA_FORTE_2021_NON_SCC, gen_empty_fingerprint(), [], True, False, False, toggles) + FPCP = CarInterface.get_starpilot_params(CAR.KIA_FORTE_2021_NON_SCC, gen_empty_fingerprint(), [], CP, toggles) + + assert not CP.openpilotLongitudinalControl + assert CP.pcmCruise + assert FPCP.pcmCruiseSpeed + def test_hyundai_full_long_keeps_redneck_cruise_disabled(self, monkeypatch): class FakeParams: def __init__(self, *args, **kwargs): diff --git a/opendbc_repo/opendbc/car/interfaces.py b/opendbc_repo/opendbc/car/interfaces.py index e208d5ff4..47a005f4b 100644 --- a/opendbc_repo/opendbc/car/interfaces.py +++ b/opendbc_repo/opendbc/car/interfaces.py @@ -238,9 +238,9 @@ class CarInterfaceBase(ABC): fp_ret.flags |= HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE.value fp_ret.redneckCruiseAvailable = bool(CP.flags & HyundaiFlags.NON_SCC) and not bool(CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS) - if fp_ret.redneckCruiseAvailable and params.get_bool("RedneckCruise") and \ - not CP.openpilotLongitudinalControl: + if fp_ret.redneckCruiseAvailable and params.get_bool("RedneckCruise"): fp_ret.pcmCruiseSpeed = False + CP.openpilotLongitudinalControl = True hyundai_has_lda_button = ( 0x391 in fingerprint[0] or diff --git a/scripts/diagnose_slc_mapd.py b/scripts/diagnose_slc_mapd.py index 8b7a99973..d11773545 100644 --- a/scripts/diagnose_slc_mapd.py +++ b/scripts/diagnose_slc_mapd.py @@ -55,7 +55,7 @@ from openpilot.tools.lib.logreader import LogReader, ReadMode # Constants # ============================================================================ -OVERPASS_API_URL = "https://overpass-api.de/api/interpreter" +OVERPASS_API_URL = "https://maps.mail.ru/osm/tools/overpass/api/interpreter" OVERPASS_UA = "starpilot-diagnose-slc/1.0 (https://github.com/FrogAi/StarPilot)" JWT_HELP_URL = "https://jwt.comma.ai/" @@ -672,7 +672,8 @@ class OverpassClient: if not gps: return {} - key = hashlib.sha256(json.dumps(gps, sort_keys=True).encode()).hexdigest()[:16] + cache_input = {"endpoint": OVERPASS_API_URL, "gps": gps} + key = hashlib.sha256(json.dumps(cache_input, sort_keys=True).encode()).hexdigest()[:16] cached = self._load_cache(key) if cached is not None: return cached diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index df18e44b1..f6fe149de 100644 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -421,20 +421,32 @@ class Car: if self.redneck_cruise is None: return - v_target_ms, lead_present = self._get_redneck_target_speed(CS) + v_target_ms, lead_present = self._get_redneck_target_speed(CS, CC) send_button, v_target = self.redneck_cruise.run(CS, CC, v_target_ms, self.is_metric, lead_present=lead_present) self.CI.CS.redneck_send_button = send_button self.CI.CS.redneck_v_target = v_target - def _get_redneck_target_speed(self, CS: car.CarState) -> tuple[float, bool]: + def _get_redneck_target_speed(self, CS: car.CarState, CC: car.CarControl) -> tuple[float, bool]: + # With openpilot longitudinal active, project its acceleration demand into the stock cruise setpoint. + if self.CP.openpilotLongitudinalControl: + return CS.vEgo * 1.01 + 3 * CC.actuators.accel, bool(CC.hudControl.leadVisible) + + # Preserve the existing button-only SLC path when LongControl is disabled. starpilot_target_speed = 0.0 + slc_target_speed = 0.0 allow_plan_decrease = False lead_present = False lead_distance_m = 0.0 lead_rel_speed_ms = 0.0 lookahead_points = REDNECK_DECREASE_LOOKAHEAD_POINTS if self.sm.seen['starpilotPlan'] and self.sm.valid['starpilotPlan']: - starpilot_target_speed = float(self.sm['starpilotPlan'].vCruise) + starpilot_plan = self.sm['starpilotPlan'] + starpilot_target_speed = float(starpilot_plan.vCruise) + if self.starpilot_toggles.speed_limit_controller: + slc_target_speed = max( + float(starpilot_plan.slcOverriddenSpeed), + float(starpilot_plan.slcSpeedLimit) + float(starpilot_plan.slcSpeedLimitOffset), + ) plan_speeds = [] if self.sm.seen['longitudinalPlan'] and self.sm.valid['longitudinalPlan']: @@ -461,6 +473,7 @@ class Car: lead_present=lead_present, lead_distance_m=lead_distance_m, lead_rel_speed_ms=lead_rel_speed_ms, + slc_target_speed_ms=slc_target_speed, ), lead_present def step(self): diff --git a/selfdrive/car/redneck_cruise.py b/selfdrive/car/redneck_cruise.py index 464e3ae51..15fe5a4cf 100644 --- a/selfdrive/car/redneck_cruise.py +++ b/selfdrive/car/redneck_cruise.py @@ -45,9 +45,12 @@ def select_redneck_target_speed(v_cruise_kph: float, speed_cluster_ms: float, starpilot_target_speed_ms: float, plan_speeds_ms: list[float], lookahead_points: int, allow_plan_decrease: bool = True, lead_present: bool = False, lead_distance_m: float = 0.0, - lead_rel_speed_ms: float = 0.0) -> float: + lead_rel_speed_ms: float = 0.0, + slc_target_speed_ms: float = 0.0) -> float: target_speed_ms = float(speed_cluster_ms) - if v_cruise_kph > 0: + if slc_target_speed_ms > 0: + target_speed_ms = float(slc_target_speed_ms) + elif v_cruise_kph > 0: target_speed_ms = float(v_cruise_kph) * CV.KPH_TO_MS elif starpilot_target_speed_ms > 0: target_speed_ms = float(starpilot_target_speed_ms) diff --git a/selfdrive/car/tests/test_redneck_cruise.py b/selfdrive/car/tests/test_redneck_cruise.py index fcdaf4cfb..012ac8d02 100644 --- a/selfdrive/car/tests/test_redneck_cruise.py +++ b/selfdrive/car/tests/test_redneck_cruise.py @@ -4,6 +4,7 @@ from types import SimpleNamespace from cereal import car from openpilot.common.constants import CV from openpilot.common.realtime import DT_CTRL +from openpilot.selfdrive.car.card import Car from openpilot.selfdrive.car.redneck_cruise import ( DECREASE_INACTIVE_TIMER, INCREASE_INACTIVE_TIMER, @@ -168,6 +169,33 @@ class TestRedneckCruise(unittest.TestCase): ) self.assertAlmostEqual(104.4 * CV.KPH_TO_MS, target_speed) + def test_target_speed_follows_resolved_slc_target(self): + for internal_mph, slc_mph in ((55.0, 65.0), (65.0, 55.0)): + with self.subTest(internal_mph=internal_mph, slc_mph=slc_mph): + target_speed = select_redneck_target_speed( + internal_mph * CV.MPH_TO_KPH, + internal_mph * CV.MPH_TO_MS, + 0.0, + [], + 10, + allow_plan_decrease=False, + slc_target_speed_ms=slc_mph * CV.MPH_TO_MS, + ) + self.assertAlmostEqual(slc_mph * CV.MPH_TO_MS, target_speed) + + def test_card_target_speed_uses_longitudinal_acceleration(self): + card = SimpleNamespace(CP=SimpleNamespace(openpilotLongitudinalControl=True)) + car_state = SimpleNamespace(vEgo=55.0 * CV.MPH_TO_MS) + car_control = SimpleNamespace( + actuators=SimpleNamespace(accel=0.5), + hudControl=SimpleNamespace(leadVisible=True), + ) + + target_speed, lead_present = Car._get_redneck_target_speed(card, car_state, car_control) + + self.assertAlmostEqual(55.0 * CV.MPH_TO_MS * 1.01 + 1.5, target_speed) + self.assertTrue(lead_present) + def test_target_speed_returns_plan_minimum_when_slowing_down(self): target_speed = select_redneck_target_speed( 120.0, diff --git a/selfdrive/ui/layouts/settings/starpilot/vehicle.py b/selfdrive/ui/layouts/settings/starpilot/vehicle.py index c9e8c7b68..905f34eff 100644 --- a/selfdrive/ui/layouts/settings/starpilot/vehicle.py +++ b/selfdrive/ui/layouts/settings/starpilot/vehicle.py @@ -46,6 +46,7 @@ from openpilot.selfdrive.ui.lib.fingerprint_catalog import ( shorten_model_label, ) from openpilot.starpilot.common.starpilot_variables import migrate_cancel_button_controls +from openpilot.selfdrive.ui.layouts.settings.common import restart_needed_callback ACTION_OPTIONS = [ @@ -420,6 +421,13 @@ class VehicleSettingsManagerView(PanelManagerView): "get_state": lambda: self._controller._params.get_bool("NostalgiaMode"), "set_state": lambda s: self._controller._on_toggle("NostalgiaMode"), }) + if cs.redneckCruiseAvailable: + toggles.append({ + "title": tr("Redneck Cruise"), + "subtitle": tr("Use RES/SET button presses to match the stock cruise set speed to StarPilot's target."), + "get_state": lambda: self._controller._params.get_bool("RedneckCruise"), + "set_state": lambda s: self._controller._on_toggle("RedneckCruise"), + }) return toggles @@ -679,6 +687,11 @@ class StarPilotVehicleSettingsLayout(_SettingsPage): migrate_cancel_button_controls(self._params) starpilot_state.update(force=True) return + if param_key == "RedneckCruise": + self._params.put_bool("RedneckCruise", not self._params.get_bool("RedneckCruise")) + starpilot_state.update(force=True) + restart_needed_callback(None) + return current = self._params.get_bool(param_key) if self._params.get(param_key) is not None else False self._params.put_bool(param_key, not current) starpilot_state.update(force=True) diff --git a/selfdrive/ui/lib/starpilot_state.py b/selfdrive/ui/lib/starpilot_state.py index c03aa1257..c1430b089 100644 --- a/selfdrive/ui/lib/starpilot_state.py +++ b/selfdrive/ui/lib/starpilot_state.py @@ -38,6 +38,7 @@ class StarPilotCarState: hasZSS: bool = False canUsePedal: bool = False canUseSDSU: bool = False + redneckCruiseAvailable: bool = False # ========== Device/Car State ========== isFrogsGoMoo: bool = False @@ -206,12 +207,14 @@ class StarPilotState: # 2. Parse StarPilotCarParamsPersistent fpcp_bytes = self.params.get("StarPilotCarParamsPersistent") + self.car_state.redneckCruiseAvailable = False if fpcp_bytes is not None: try: FPCP = messaging.log_from_bytes(fpcp_bytes, custom.StarPilotCarParams) self.car_state.canUsePedal = FPCP.canUsePedal self.car_state.canUseSDSU = FPCP.canUseSDSU self.car_state.openpilotLongitudinalControlDisabled = FPCP.openpilotLongitudinalControlDisabled + self.car_state.redneckCruiseAvailable = bool(FPCP.redneckCruiseAvailable) except Exception: pass diff --git a/selfdrive/ui/mici/layouts/settings/vehicle.py b/selfdrive/ui/mici/layouts/settings/vehicle.py index 769897844..f3706cc06 100644 --- a/selfdrive/ui/mici/layouts/settings/vehicle.py +++ b/selfdrive/ui/mici/layouts/settings/vehicle.py @@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.lib.fingerprint_catalog import ( shorten_model_label, ) from openpilot.selfdrive.ui.layouts.settings.common import restart_needed_callback +from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.multilang import tr @@ -195,10 +196,10 @@ class VehicleLayoutMici(NavScroller): fingerprint_btn = BigButton("fingerprint", "",gui_app.texture("icons_mici/settings/vehicle/fingerprint.png", 58, 64)) fingerprint_btn.set_click_callback(lambda: gui_app.push_widget(fingerprint_panel)) - # TODO: make it reloadable without restarting ui - match ui_state.params.get("CarModel"): - case _: - vehicle_specific_widgets = tuple() + starpilot_state.update(force=True) + vehicle_specific_widgets = ( + BigParamControl("redneck cruise", "RedneckCruise", toggle_callback=restart_needed_callback), + ) if starpilot_state.car_state.redneckCruiseAvailable else tuple() self._scroller.add_widgets([ fingerprint_btn, @@ -206,4 +207,3 @@ class VehicleLayoutMici(NavScroller): ]) self._font_medium = gui_app.font(FontWeight.MEDIUM) - diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index aa1396981..4141181ce 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -379,6 +379,10 @@ def set_speed_limit_available(openpilot_longitudinal: bool, has_cc_long: bool, p return openpilot_longitudinal or has_cc_long or not pcm_cruise_speed +def speed_limit_controller_available(openpilot_longitudinal: bool, redneck_cruise: bool) -> bool: + return openpilot_longitudinal or redneck_cruise + + def migrate_cancel_button_controls(params: Params | None = None) -> bool: params = params or Params(return_defaults=True) if params.get_bool(CANCEL_BUTTON_MIGRATION_KEY) or not params.get_bool("RemapCancelToDistance"): @@ -642,12 +646,14 @@ class StarPilotVariables: toggle.lkas_allowed_for_aol = hyundai_can_use_lkas_for_aol or toggle.car_make == "honda" longitudinalActuatorDelay = CP.longitudinalActuatorDelay toggle.openpilot_longitudinal = CP.openpilotLongitudinalControl and not toggle.disable_openpilot_long - if not toggle.redneck_cruise_available or toggle.openpilot_longitudinal: + if not toggle.redneck_cruise_available or (toggle.openpilot_longitudinal and FPCP.pcmCruiseSpeed): self.params.put_bool("RedneckCruise", False) toggle.redneck_cruise = self.get_value( "RedneckCruise", condition=toggle.redneck_cruise_available and not toggle.openpilot_longitudinal, ) + if toggle.redneck_cruise_available and not FPCP.pcmCruiseSpeed: + toggle.redneck_cruise = True pcm_cruise = CP.pcmCruise prohibited_main_aol = not toggle.openpilot_longitudinal and hyundai_can_use_lkas_for_aol startAccel = CP.startAccel @@ -1292,7 +1298,8 @@ class StarPilotVariables: toggle.sng_hack = self.get_value("SNGHack", condition=toggle.openpilot_longitudinal and toggle.car_make == "toyota" and not toggle.has_pedal and not has_sng) toggle.toyota_auto_hold = self.get_value("ToyotaAutoHold", condition=toggle.car_make == "toyota") - toggle.speed_limit_controller = toggle.openpilot_longitudinal and self.get_value("SpeedLimitController") + slc_available = speed_limit_controller_available(toggle.openpilot_longitudinal, toggle.redneck_cruise) + toggle.speed_limit_controller = slc_available and self.get_value("SpeedLimitController") set_speed_limit_on_engage = set_speed_limit_available(toggle.openpilot_longitudinal, toggle.has_cc_long, FPCP.pcmCruiseSpeed) speed_limit_display = toggle.show_speed_limits or toggle.speed_limit_controller toggle.map_speed_lookahead_higher = self.get_value("SLCLookaheadHigher", cast=float, condition=speed_limit_display) diff --git a/starpilot/common/tests/test_starpilot_variables.py b/starpilot/common/tests/test_starpilot_variables.py index 23e263eae..a078d4a65 100644 --- a/starpilot/common/tests/test_starpilot_variables.py +++ b/starpilot/common/tests/test_starpilot_variables.py @@ -222,3 +222,9 @@ def test_set_speed_limit_available_on_redneck_helper_path(): def test_set_speed_limit_unavailable_on_stock_pcm_without_helper(): assert spv.set_speed_limit_available(openpilot_longitudinal=False, has_cc_long=False, pcm_cruise_speed=True) is False + + +def test_speed_limit_controller_available_on_openpilot_longitudinal_or_redneck(): + assert spv.speed_limit_controller_available(openpilot_longitudinal=True, redneck_cruise=False) is True + assert spv.speed_limit_controller_available(openpilot_longitudinal=False, redneck_cruise=True) is True + assert spv.speed_limit_controller_available(openpilot_longitudinal=False, redneck_cruise=False) is False diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json index c7738b865..b09e8aec9 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json @@ -1346,15 +1346,6 @@ "parent_key": "QOLLongitudinal", "settings_tier": "simple" }, - { - "key": "RedneckCruise", - "label": "Redneck Cruise", - "description": "On supported Hyundai stock-long cars, use RES/SET button presses to match the cluster set speed to StarPilot's target.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "QOLLongitudinal", - "settings_tier": "simple" - }, { "key": "WeatherPresets", "label": "Weather Condition Offsets", diff --git a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py index ae462087f..36907b95e 100644 --- a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py +++ b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py @@ -50,6 +50,7 @@ def test_galaxy_layout_contains_basic_mode_controls(): "HumanLaneChanges", "QOLLongitudinal", } <= sections["Longitudinal (Speed & Following)"].keys() + assert "RedneckCruise" not in sections["Longitudinal (Speed & Following)"].keys() assert {"GalaxyDeveloperMode", "UseOldUI"} <= sections["Developer"].keys()