diff --git a/openpilot/selfdrive/car/card.py b/openpilot/selfdrive/car/card.py
index 2e45b1821..137c6f503 100755
--- a/openpilot/selfdrive/car/card.py
+++ b/openpilot/selfdrive/car/card.py
@@ -178,7 +178,7 @@ class Car:
self.v_cruise_helper = VCruiseHelper(self.CP, self.CP_SP)
self.is_metric = self.params.get_bool("IsMetric")
- self.experimental_mode = self.params.get_bool("ExperimentalMode")
+ self.initial_cruise_speed = self.params.get("InitialCruiseSpeedKph", return_default=True)
# card is driven by can recv, expected at 100Hz
self.rk = Ratekeeper(100, print_delay_threshold=None)
@@ -211,7 +211,7 @@ class Car:
self.v_cruise_helper.update_v_cruise(CS, self.sm['carControl'].enabled, self.is_metric)
if self.sm['carControl'].enabled and not self.CC_prev.enabled:
# Use CarState w/ buttons from the step selfdrived enables on
- self.v_cruise_helper.initialize_v_cruise(self.CS_prev, self.experimental_mode)
+ self.v_cruise_helper.initialize_v_cruise(self.CS_prev, self.initial_cruise_speed)
# TODO: mirror the carState.cruiseState struct?
CS.vCruise = float(self.v_cruise_helper.v_cruise_kph)
@@ -296,7 +296,7 @@ class Car:
def params_thread(self, evt):
while not evt.is_set():
self.is_metric = self.params.get_bool("IsMetric")
- self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
+ self.initial_cruise_speed = self.params.get("InitialCruiseSpeedKph", return_default=True)
# sunnypilot
self.v_cruise_helper.read_custom_set_speed_params()
diff --git a/openpilot/selfdrive/car/cruise.py b/openpilot/selfdrive/car/cruise.py
index 9aed674e3..ab2eaba02 100644
--- a/openpilot/selfdrive/car/cruise.py
+++ b/openpilot/selfdrive/car/cruise.py
@@ -13,7 +13,6 @@ V_CRUISE_MIN = 8
V_CRUISE_MAX = 145
V_CRUISE_UNSET = 255
V_CRUISE_INITIAL = 40
-V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 105
IMPERIAL_INCREMENT = round(CV.MPH_TO_KPH, 1) # round here to avoid rounding errors incrementing set speed
ButtonEvent = car.CarState.ButtonEvent
@@ -138,16 +137,14 @@ class VCruiseHelper(VCruiseHelperSP):
self.button_timers[b.type.raw] = 1 if b.pressed else 0
self.button_change_states[b.type.raw] = {"standstill": CS.cruiseState.standstill, "enabled": enabled}
- def initialize_v_cruise(self, CS, experimental_mode: bool) -> None:
+ def initialize_v_cruise(self, CS, initial_speed: int) -> None:
# initializing is handled by the PCM
if self.CP.pcmCruise:
return
- initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode else V_CRUISE_INITIAL
-
if any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents) and self.v_cruise_initialized:
self.v_cruise_kph = self.v_cruise_kph_last
else:
- self.v_cruise_kph = int(round(np.clip(CS.vEgo * CV.MS_TO_KPH, initial, V_CRUISE_MAX)))
+ self.v_cruise_kph = int(round(np.clip(max(CS.vEgo * CV.MS_TO_KPH, initial_speed), V_CRUISE_INITIAL, V_CRUISE_MAX)))
self.v_cruise_cluster_kph = self.v_cruise_kph
diff --git a/openpilot/selfdrive/car/tests/test_cruise_speed.py b/openpilot/selfdrive/car/tests/test_cruise_speed.py
index c38c7c717..1f8d37cf7 100644
--- a/openpilot/selfdrive/car/tests/test_cruise_speed.py
+++ b/openpilot/selfdrive/car/tests/test_cruise_speed.py
@@ -14,7 +14,7 @@ ButtonEvent = car.CarState.ButtonEvent
ButtonType = car.CarState.ButtonEvent.Type
-def run_cruise_simulation(cruise, e2e, personality, t_end=20.):
+def run_cruise_simulation(cruise, personality, t_end=20.):
man = Maneuver(
'',
duration=t_end,
@@ -24,7 +24,6 @@ def run_cruise_simulation(cruise, e2e, personality, t_end=20.):
cruise_values=[cruise],
prob_lead_values=[0.0],
breakpoints=[0.],
- e2e=e2e,
personality=personality,
)
valid, output = man.evaluate()
@@ -32,8 +31,7 @@ def run_cruise_simulation(cruise, e2e, personality, t_end=20.):
return output[-1, 3]
-@parameterized_class(("e2e", "personality", "speed"), itertools.product(
- [True, False], # e2e
+@parameterized_class(("personality", "speed"), itertools.product(
log.LongitudinalPersonality.schema.enumerants, # personality
[5,35])) # speed
class TestCruiseSpeed(OpenpilotTestCase):
@@ -41,7 +39,7 @@ class TestCruiseSpeed(OpenpilotTestCase):
print(f'Testing {self.speed} m/s')
cruise_speed = float(self.speed)
- simulation_steady_state = run_cruise_simulation(cruise_speed, self.e2e, self.personality)
+ simulation_steady_state = run_cruise_simulation(cruise_speed, self.personality)
self.assertAlmostEqual(simulation_steady_state, cruise_speed, delta=.01, msg=f'Did not reach {self.speed} m/s')
@@ -59,16 +57,16 @@ class TestVCruiseHelper(OpenpilotTestCase):
for _ in range(2):
self.v_cruise_helper.update_v_cruise(car.CarState(cruiseState={"available": False}), enabled=False, is_metric=False)
- def enable(self, v_ego, experimental_mode):
+ def enable(self, v_ego):
# Simulates user pressing set with a current speed
- self.v_cruise_helper.initialize_v_cruise(car.CarState(vEgo=v_ego), experimental_mode)
+ self.v_cruise_helper.initialize_v_cruise(car.CarState(vEgo=v_ego), V_CRUISE_INITIAL)
def test_adjust_speed(self):
"""
Asserts speed changes on falling edges of buttons.
"""
- self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False)
+ self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS)
for btn in (ButtonType.accelCruise, ButtonType.decelCruise):
for pressed in (True, False):
@@ -92,7 +90,7 @@ class TestVCruiseHelper(OpenpilotTestCase):
CS.buttonEvents = [ButtonEvent(type=ButtonType.decelCruise, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(CS, enabled=enabled, is_metric=False)
if pressed:
- self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False)
+ self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS)
# Expected diff on enabling. Speed should not change on falling edge of pressed
assert (not pressed) == (self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last)
@@ -102,7 +100,7 @@ class TestVCruiseHelper(OpenpilotTestCase):
Asserts we don't increment set speed if user presses resume/accel to exit cruise standstill.
"""
- self.enable(0, False)
+ self.enable(0)
for standstill in (True, False):
for pressed in (True, False):
@@ -122,7 +120,7 @@ class TestVCruiseHelper(OpenpilotTestCase):
for v_ego in np.linspace(0, 100, 101):
self.reset_cruise_speed_state()
- self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False)
+ self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS)
# first decrement speed, then perform gas pressed logic
expected_v_cruise_kph = self.v_cruise_helper.v_cruise_kph - IMPERIAL_INCREMENT
@@ -143,11 +141,10 @@ class TestVCruiseHelper(OpenpilotTestCase):
Asserts allowed cruise speeds on enabling with SET.
"""
- for experimental_mode in (True, False):
- for v_ego in np.linspace(0, 100, 101):
- self.reset_cruise_speed_state()
- assert not self.v_cruise_helper.v_cruise_initialized
+ for v_ego in np.linspace(0, 100, 101):
+ self.reset_cruise_speed_state()
+ assert not self.v_cruise_helper.v_cruise_initialized
- self.enable(float(v_ego), experimental_mode)
- assert V_CRUISE_INITIAL <= self.v_cruise_helper.v_cruise_kph <= V_CRUISE_MAX
- assert self.v_cruise_helper.v_cruise_initialized
+ self.enable(float(v_ego))
+ assert V_CRUISE_INITIAL <= self.v_cruise_helper.v_cruise_kph <= V_CRUISE_MAX
+ assert self.v_cruise_helper.v_cruise_initialized
diff --git a/openpilot/selfdrive/selfdrived/selfdrived.py b/openpilot/selfdrive/selfdrived/selfdrived.py
index 9507abb68..fddcae6d3 100755
--- a/openpilot/selfdrive/selfdrived/selfdrived.py
+++ b/openpilot/selfdrive/selfdrived/selfdrived.py
@@ -29,7 +29,6 @@ from openpilot.common.hardware import HARDWARE
from openpilot.sunnypilot.mads.mads import ModularAssistiveDrivingSystem
from openpilot.sunnypilot import get_sanitize_int_param
from openpilot.sunnypilot.selfdrive.car.car_specific import CarSpecificEventsSP
-from openpilot.sunnypilot.selfdrive.car.cruise_helpers import CruiseHelper
from openpilot.sunnypilot.selfdrive.car.intelligent_cruise_button_management.controller import IntelligentCruiseButtonManagement
from openpilot.sunnypilot.selfdrive.selfdrived.button_state_tracker import ButtonStateTracker
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
@@ -55,7 +54,7 @@ TurnDirection = custom.ModelDataV2SP.TurnDirection
IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
-class SelfdriveD(CruiseHelper):
+class SelfdriveD:
def __init__(self, CP=None, CP_SP=None):
self.params = Params()
@@ -123,8 +122,6 @@ class SelfdriveD(CruiseHelper):
# cleanup old params
if not self.CP.alphaLongitudinalAvailable:
self.params.remove("AlphaLongitudinalEnabled")
- if not self.CP.openpilotLongitudinalControl:
- self.params.remove("ExperimentalMode")
self.CS_prev = car.CarState.new_message()
self.AM = AlertManager()
@@ -141,7 +138,6 @@ class SelfdriveD(CruiseHelper):
self.events_prev = []
self.logged_comm_issue = None
self.not_running_prev = None
- self.experimental_mode = False
self.personality = get_sanitize_int_param(
"LongitudinalPersonality",
min(log.LongitudinalPersonality.schema.enumerants.values()),
@@ -183,7 +179,6 @@ class SelfdriveD(CruiseHelper):
self.car_events_sp = CarSpecificEventsSP(self.CP, self.CP_SP)
- CruiseHelper.__init__(self, self.CP)
self.button_state_tracker = ButtonStateTracker()
def update_events(self, CS):
@@ -514,16 +509,12 @@ class SelfdriveD(CruiseHelper):
if CS.gearShifter == car.CarState.GearShifter.park and self.mads.enabled:
self.events.remove(EventName.canBusMissing)
- CruiseHelper.update(self, CS, self.events_sp, self.experimental_mode)
-
# decrement personality on distance button press
if self.CP.openpilotLongitudinalControl:
if any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents):
- if not self.experimental_mode_switched:
- self.personality = (self.personality - 1) % 3
- self.params.put('LongitudinalPersonality', self.personality)
- self.events.add(EventName.personalityChanged)
- self.experimental_mode_switched = False
+ self.personality = (self.personality - 1) % 3
+ self.params.put('LongitudinalPersonality', self.personality)
+ self.events.add(EventName.personalityChanged)
self.icbm.run(CS, self.sm['carControl'], self.sm['longitudinalPlanSP'], self.is_metric)
@@ -600,7 +591,6 @@ class SelfdriveD(CruiseHelper):
ss.active = self.active
ss.state = self.state_machine.state
ss.engageable = not self.events.contains(ET.NO_ENTRY)
- ss.experimentalMode = self.experimental_mode
ss.personality = self.personality
ss.alertText1 = self.AM.current_alert.alert_text_1
@@ -667,7 +657,6 @@ class SelfdriveD(CruiseHelper):
self.is_metric = self.params.get_bool("IsMetric")
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
- self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
self.personality = self.params.get("LongitudinalPersonality", return_default=True)
self.mads.read_params()
diff --git a/openpilot/selfdrive/ui/layouts/home.py b/openpilot/selfdrive/ui/layouts/home.py
index 78a4fb8a2..ee3083012 100644
--- a/openpilot/selfdrive/ui/layouts/home.py
+++ b/openpilot/selfdrive/ui/layouts/home.py
@@ -1,10 +1,8 @@
import time
import pyray as rl
-from collections.abc import Callable
from enum import IntEnum
from openpilot.common.params import Params
from openpilot.selfdrive.ui.widgets.offroad_alerts import UpdateAlert, OffroadAlert
-from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButton
from openpilot.selfdrive.ui.widgets.prime import PrimeWidget
from openpilot.selfdrive.ui.widgets.setup import SetupWidget
from openpilot.system.ui.lib.text_measure import measure_text_cached
@@ -39,7 +37,6 @@ class HomeLayout(Widget):
self.current_state = HomeLayoutState.HOME
self.last_refresh = 0
- self.settings_callback: Callable[[], None] | None = None
self.update_available = False
self.alert_count = 0
@@ -60,29 +57,20 @@ class HomeLayout(Widget):
self._prime_widget = PrimeWidget()
self._setup_widget = SetupWidget()
- self._exp_mode_button = ExperimentalModeButton()
self._setup_callbacks()
def show_event(self):
super().show_event()
- self._exp_mode_button.show_event()
self.last_refresh = time.monotonic()
self._refresh()
def _setup_callbacks(self):
self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
- self._exp_mode_button.set_click_callback(lambda: self.settings_callback() if self.settings_callback else None)
-
- def set_settings_callback(self, callback: Callable):
- self.settings_callback = callback
def _set_state(self, state: HomeLayoutState):
# propagate show/hide events
if state != self.current_state:
- if state == HomeLayoutState.HOME:
- self._exp_mode_button.show_event()
-
if state in self._layout_widgets:
self._layout_widgets[state].show_event()
if self.current_state in self._layout_widgets:
@@ -235,19 +223,7 @@ class HomeLayout(Widget):
self._prime_widget.render(self.left_column_rect)
def _render_right_column(self):
- exp_height = 125
- exp_rect = rl.Rectangle(
- self.right_column_rect.x, self.right_column_rect.y, self.right_column_rect.width, exp_height
- )
- self._exp_mode_button.render(exp_rect)
-
- setup_rect = rl.Rectangle(
- self.right_column_rect.x,
- self.right_column_rect.y + exp_height + SPACING,
- self.right_column_rect.width,
- self.right_column_rect.height - exp_height - SPACING,
- )
- self._setup_widget.render(setup_rect)
+ self._setup_widget.render(self.right_column_rect)
def _refresh(self):
self._version_text = self._get_version_text()
diff --git a/openpilot/selfdrive/ui/layouts/main.py b/openpilot/selfdrive/ui/layouts/main.py
index c4224d646..2e002b031 100644
--- a/openpilot/selfdrive/ui/layouts/main.py
+++ b/openpilot/selfdrive/ui/layouts/main.py
@@ -63,7 +63,6 @@ class MainLayout(Widget):
on_flag=self._on_bookmark_clicked,
open_settings=lambda: self.open_settings(PanelType.TOGGLES))
self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE))
- self._layouts[MainState.HOME].set_settings_callback(lambda: self.open_settings(PanelType.TOGGLES))
self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state)
for layout in (self._layouts[MainState.ONROAD], self._home_body_layout):
diff --git a/openpilot/selfdrive/ui/layouts/settings/developer.py b/openpilot/selfdrive/ui/layouts/settings/developer.py
index ad71fcb27..b25385efd 100644
--- a/openpilot/selfdrive/ui/layouts/settings/developer.py
+++ b/openpilot/selfdrive/ui/layouts/settings/developer.py
@@ -178,7 +178,6 @@ class DeveloperLayout(Widget):
def _on_lat_maneuver_mode(self, state: bool):
self._params.put_bool("LateralManeuverMode", state, block=True)
- self._params.put_bool("ExperimentalMode", False, block=True)
self._params.put_bool("JoystickDebugMode", False, block=True)
self._joystick_toggle.action_item.set_state(False)
self._params.put_bool("LongitudinalManeuverMode", False, block=True)
diff --git a/openpilot/selfdrive/ui/layouts/settings/toggles.py b/openpilot/selfdrive/ui/layouts/settings/toggles.py
index 7c420d5f6..07af3b394 100644
--- a/openpilot/selfdrive/ui/layouts/settings/toggles.py
+++ b/openpilot/selfdrive/ui/layouts/settings/toggles.py
@@ -46,13 +46,6 @@ class TogglesLayout(Widget):
# param, title, desc, icon, needs_restart
self._toggle_defs = {
- "ExperimentalMode": (
- lambda: tr("Experimental Mode"),
- "",
- "experimental_white.png",
- False,
- ),
-
"IsLdwEnabled": (
lambda: tr("Enable Lane Departure Warnings"),
DESCRIPTIONS["IsLdwEnabled"],
@@ -109,11 +102,7 @@ class TogglesLayout(Widget):
self._toggles[param] = toggle
- # insert longitudinal personality after NDOG toggle
- if param == "ExperimentalMode":
- self._toggles["LongitudinalPersonality"] = self._long_personality_setting
-
- self._update_experimental_mode_icon()
+ self._toggles["LongitudinalPersonality"] = self._long_personality_setting
self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0)
ui_state.add_engaged_transition_callback(self._update_toggles)
@@ -133,45 +122,8 @@ class TogglesLayout(Widget):
def _update_toggles(self):
ui_state.update_params()
- e2e_description = tr(
- "sunnypilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " +
- "Experimental features are listed below:
" +
- "
{self._toggles['ExperimentalMode'].description}
") - dlg = ConfirmDialog(content, tr("Enable"), rich=True, callback=confirm_callback) - gui_app.push_widget(dlg) - else: - self._update_experimental_mode_icon() - self._params.put_bool("ExperimentalMode", state, block=True) - def _toggle_callback(self, state: bool, param: str): - if param == "ExperimentalMode": - self._handle_experimental_mode_toggle(state) - return - self._params.put_bool(param, state, block=True) if self._toggle_defs[param][3]: self._params.put_bool("OnroadCycleRequested", True, block=True) diff --git a/openpilot/selfdrive/ui/mici/layouts/home.py b/openpilot/selfdrive/ui/mici/layouts/home.py index 419475a60..3e90a7d1a 100644 --- a/openpilot/selfdrive/ui/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/mici/layouts/home.py @@ -2,7 +2,6 @@ from __future__ import annotations import datetime import math -import time from openpilot.cereal import log import pyray as rl @@ -127,13 +126,8 @@ class MiciHomeLayout(Widget): self._on_alerts_click: Callable | None = None self._alert_count_callback: Callable[[], int] | None = None - self._mouse_down_t: None | float = None - self._did_long_press = False - self._is_pressed_prev = False - self._version_text = self._get_version_text() - self._experimental_icon = IconWidget("icons_mici/experimental_mode.png", (48, 48)) self._usb_icon = IconWidget("icons_mici/usb.png", (62, 40)) self._chestnut_icon = IconWidget("icons_mici/chestnut_green.png", (54, 40)) self._chestnut_loading_icon = IconWidget("icons_mici/chestnut.png", (68, 40)) @@ -146,7 +140,6 @@ class MiciHomeLayout(Widget): self._status_bar_layout = HBoxLayout([ IconWidget("icons_mici/settings.png", (48, 48), opacity=0.9), NetworkIcon(), - self._experimental_icon, self._usb_icon, self._chestnut_icon, self._chestnut_loading_icon, @@ -162,23 +155,6 @@ class MiciHomeLayout(Widget): self._branch_label = UnifiedLabel("", font_size=36, text_color=rl.GRAY, font_weight=FontWeight.ROMAN, scroll=True) self._version_commit_label = UnifiedLabel("", font_size=36, text_color=rl.GRAY, font_weight=FontWeight.ROMAN, max_width=480, wrap_text=False) - def _update_state(self): - if self.is_pressed and not self._is_pressed_prev: - self._mouse_down_t = time.monotonic() - elif not self.is_pressed and self._is_pressed_prev: - self._mouse_down_t = None - self._did_long_press = False - self._is_pressed_prev = self.is_pressed - - if self._mouse_down_t is not None: - if time.monotonic() - self._mouse_down_t > 0.5: - # long gating for experimental mode - only allow toggle if longitudinal control is available - if ui_state.has_longitudinal_control and ui_state.experimental_mode_confirmed: - ui_state.experimental_mode = not ui_state.experimental_mode - ui_state.params.put("ExperimentalMode", ui_state.experimental_mode, block=True) - self._mouse_down_t = None - self._did_long_press = True - def set_callbacks(self, on_settings: Callable | None = None, on_alerts: Callable | None = None, alert_count_callback: Callable[[], int] | None = None, alert_icon_callback: Callable[[], rl.Texture | None] | None = None): @@ -188,15 +164,13 @@ class MiciHomeLayout(Widget): self._alerts_pill.set_alert_count_callback(alert_count_callback, alert_icon_callback) def _handle_mouse_release(self, mouse_pos: MousePos): - if not self._did_long_press: - relative_x = mouse_pos.x - self.rect.x - has_alerts = self._alert_count_callback and self._alert_count_callback() > 0 - if has_alerts and relative_x > self.rect.width - ALERTS_ZONE_WIDTH: - if self._on_alerts_click: - self._on_alerts_click() - elif self._on_settings_click: - self._on_settings_click() - self._did_long_press = False + relative_x = mouse_pos.x - self.rect.x + has_alerts = self._alert_count_callback and self._alert_count_callback() > 0 + if has_alerts and relative_x > self.rect.width - ALERTS_ZONE_WIDTH: + if self._on_alerts_click: + self._on_alerts_click() + elif self._on_settings_click: + self._on_settings_click() def _get_version_text(self) -> tuple[str, str, str, str] | None: version = ui_state.params.get("Version") @@ -249,7 +223,6 @@ class MiciHomeLayout(Widget): usb_connected = ui_state.usb_connected usb_unknown = ui_state.usb_unknown chestnut_state = ui_state.chestnut_state - self._experimental_icon.set_visible(ui_state.experimental_mode) if gui_app.sunnypilot_ui(): self._set_chestnut_visibility() else: diff --git a/openpilot/selfdrive/ui/mici/layouts/settings/developer.py b/openpilot/selfdrive/ui/mici/layouts/settings/developer.py index 0b52e48bf..3c1121953 100644 --- a/openpilot/selfdrive/ui/mici/layouts/settings/developer.py +++ b/openpilot/selfdrive/ui/mici/layouts/settings/developer.py @@ -83,6 +83,8 @@ class DeveloperLayoutMici(NavScroller): toggle_callback=self._on_alpha_long_enabled, description="Use alpha openpilot longitudinal control instead of stock ACC. This may disable Automatic Emergency " + "Braking (AEB).") + self._navigation_toggle = BigParamControl("navigation (alpha)", "NavigationEnabled", + description="Turn-by-turn navigation with a route map. Set a destination from comma connect.") self._debug_mode_toggle = BigParamControl("ui debug mode", "ShowDebugInfo", toggle_callback=lambda checked: (gui_app.set_show_touches(checked), gui_app.set_show_fps(checked)), description="Show touch locations and the UI frame rate.") @@ -96,6 +98,7 @@ class DeveloperLayoutMici(NavScroller): self._lat_maneuver_toggle, self._alpha_long_toggle, self._debug_mode_toggle, + self._navigation_toggle, ]) # Toggle lists @@ -107,9 +110,10 @@ class DeveloperLayoutMici(NavScroller): ("LateralManeuverMode", self._lat_maneuver_toggle), ("AlphaLongitudinalEnabled", self._alpha_long_toggle), ("ShowDebugInfo", self._debug_mode_toggle), + ("NavigationEnabled", self._navigation_toggle), ) onroad_blocked_toggles = (self._adb_toggle, self._joystick_toggle) - release_blocked_toggles = (self._joystick_toggle, self._long_maneuver_toggle, self._lat_maneuver_toggle, self._alpha_long_toggle) + release_blocked_toggles = (self._joystick_toggle, self._long_maneuver_toggle, self._lat_maneuver_toggle, self._alpha_long_toggle, self._navigation_toggle) engaged_blocked_toggles = (self._long_maneuver_toggle, self._lat_maneuver_toggle, self._alpha_long_toggle) # Hide non-release toggles on release builds @@ -180,7 +184,6 @@ class DeveloperLayoutMici(NavScroller): def _on_lat_maneuver_mode(self, state: bool): ui_state.params.put_bool("LateralManeuverMode", state, block=True) - ui_state.params.put_bool("ExperimentalMode", False, block=True) ui_state.params.put_bool("JoystickDebugMode", False, block=True) self._joystick_toggle.set_checked(False) ui_state.params.put_bool("LongitudinalManeuverMode", False, block=True) diff --git a/openpilot/selfdrive/ui/mici/layouts/settings/toggles.py b/openpilot/selfdrive/ui/mici/layouts/settings/toggles.py index 78f6085e8..406e0a77d 100644 --- a/openpilot/selfdrive/ui/mici/layouts/settings/toggles.py +++ b/openpilot/selfdrive/ui/mici/layouts/settings/toggles.py @@ -1,59 +1,27 @@ -from collections.abc import Callable - from openpilot.cereal import log from openpilot.system.ui.widgets.scroller import NavScroller -from openpilot.selfdrive.ui.mici.widgets.button import BigParamControl, BigMultiParamToggle, BigToggle, GreyBigButton -from openpilot.selfdrive.ui.mici.widgets.dialog import BigConfirmationCircleButton +from openpilot.selfdrive.ui.mici.widgets.button import BigToggle, BigParamControl, BigMultiParamToggle from openpilot.system.ui.lib.application import gui_app from openpilot.selfdrive.ui.layouts.settings.common import restart_needed_callback from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.mici.layouts.settings.initial_cruise_speed import InitialCruiseSpeedButton PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants -class ExperimentalModeConfirmPage(NavScroller): - def __init__(self, on_confirm: Callable[[], None]): - super().__init__() - - accept = BigConfirmationCircleButton("enable\nexperimental mode", - gui_app.texture("icons_mici/setup/driver_monitoring/dm_check.png", 64, 64), - lambda: self.dismiss(on_confirm)) - - self._scroller.add_widgets([ - GreyBigButton("enabling\nexperimental mode", "scroll to continue", - gui_app.texture("icons_mici/setup/warning.png", 64, 64)), - GreyBigButton("", "openpilot defaults to driving in chill mode."), - GreyBigButton("", "Experimental mode enables alpha-level features that aren't ready for chill mode."), - GreyBigButton("End-to-End Longitudinal Control"), - GreyBigButton("", "Let the driving model control the gas and brakes."), - GreyBigButton("", "openpilot will drive as it thinks a human would, including stopping for red lights and stop signs."), - GreyBigButton("", "The set speed will only act as an upper bound."), - GreyBigButton("", "This is an alpha quality feature; mistakes should be expected."), - GreyBigButton("New Driving Visualization"), - GreyBigButton("", "The path will change colors to communicate acceleration intent."), - GreyBigButton("", "Red for braking, green for acceleration, and gray for coasting."), - accept, - ]) - - class TogglesLayoutMici(NavScroller): def __init__(self): super().__init__() - self._personality_toggle = BigMultiParamToggle("driving personality", "LongitudinalPersonality", ["aggressive", "standard", "relaxed"], + self._personality_toggle = BigMultiParamToggle("following distance", "LongitudinalPersonality", ["aggressive", "standard", "relaxed"], description="Standard is recommended.\n" + "Aggressive follows closer, with firmer gas and braking.\n" + "Relaxed leaves more space.\n" + "Use the steering wheel distance button on supported cars.") - self._experimental_btn = BigToggle("experimental mode", description_icon=gui_app.texture("icons_mici/experimental_mode.png", 64, 64), - initial_state=ui_state.params.get_bool("ExperimentalMode"), toggle_callback=self._on_experimental_mode, - description="Let the driving model control gas and brakes.\n" + - "Includes stopping for red lights and stop signs.\n" + - "Set speed is a maximum, not a target.\n" + - "These are alpha features. Expect mistakes.\n" + - "The path colors show acceleration and braking.") - is_metric_toggle = BigParamControl("use metric units", "IsMetric") + self._initial_cruise_speed = InitialCruiseSpeedButton() + self._initial_cruise_speed.set_visible(lambda: ui_state.CP is not None and ui_state.has_longitudinal_control and not ui_state.CP.pcmCruise) + is_metric_toggle = BigToggle("use metric units", initial_state=ui_state.params.get_bool("IsMetric"), toggle_callback=self._on_metric_toggle) ldw_toggle = BigParamControl("lane departure warnings", "IsLdwEnabled", description="Warn when you drift across a detected lane line.\n" + "Only above 31 mph (50 km/h), with no turn signal.") @@ -72,7 +40,7 @@ class TogglesLayoutMici(NavScroller): self._scroller.add_widgets([ self._personality_toggle, - self._experimental_btn, + self._initial_cruise_speed, is_metric_toggle, ldw_toggle, always_on_dm_toggle, @@ -83,7 +51,6 @@ class TogglesLayoutMici(NavScroller): # Toggle lists self._refresh_toggles = ( - ("ExperimentalMode", self._experimental_btn), ("IsMetric", is_metric_toggle), ("IsLdwEnabled", ldw_toggle), ("AlwaysOnDM", always_on_dm_toggle), @@ -115,35 +82,17 @@ class TogglesLayoutMici(NavScroller): super().show_event() self._update_toggles() + def _on_metric_toggle(self, state: bool): + ui_state.params.put_bool("IsMetric", state, block=True) + self._update_toggles() + def _update_toggles(self): ui_state.update_params() + self._initial_cruise_speed.refresh() - # CP gating for experimental mode if ui_state.CP is not None: - if ui_state.has_longitudinal_control: - self._experimental_btn.set_visible(True) - self._personality_toggle.set_visible(True) - else: - # no long for now - self._experimental_btn.set_visible(False) - self._experimental_btn.set_checked(False) - self._personality_toggle.set_visible(False) - ui_state.params.remove("ExperimentalMode") + self._personality_toggle.set_visible(ui_state.has_longitudinal_control) # Refresh toggles from params to mirror external changes for key, item in self._refresh_toggles: item.set_checked(ui_state.params.get_bool(key)) - - def _on_experimental_mode(self, state: bool): - if state and not ui_state.params.get_bool("ExperimentalModeConfirmed"): - # Don't show enabled state until confirm - self._experimental_btn.set_checked(False) - - def on_confirm(): - ui_state.params.put_bool("ExperimentalModeConfirmed", True) - ui_state.params.put_bool("ExperimentalMode", True) - self._experimental_btn.set_checked(True) - - gui_app.push_widget(ExperimentalModeConfirmPage(on_confirm)) - else: - ui_state.params.put_bool("ExperimentalMode", state) diff --git a/openpilot/selfdrive/ui/onroad/hud_renderer.py b/openpilot/selfdrive/ui/onroad/hud_renderer.py index 6f3f1256a..ba179919d 100644 --- a/openpilot/selfdrive/ui/onroad/hud_renderer.py +++ b/openpilot/selfdrive/ui/onroad/hud_renderer.py @@ -1,7 +1,6 @@ import pyray as rl from dataclasses import dataclass from openpilot.common.constants import CV -from openpilot.selfdrive.ui.onroad.exp_button import ExpButton from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.multilang import tr @@ -18,11 +17,9 @@ CRUISE_DISABLED_CHAR = '–' class UIConfig: header_height: int = 300 border_size: int = 30 - button_size: int = 192 set_speed_width_metric: int = 200 set_speed_width_imperial: int = 172 set_speed_height: int = 204 - wheel_icon_size: int = 144 @dataclass(frozen=True) @@ -71,7 +68,6 @@ class HudRenderer(Widget): self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) self._font_speed: rl.Font = gui_app.high_res_speed_font() - self._exp_button: ExpButton = ExpButton(UI_CONFIG.button_size, UI_CONFIG.wheel_icon_size) def _update_state(self) -> None: """Update HUD state based on car state and controls state.""" @@ -118,13 +114,6 @@ class HudRenderer(Widget): self._draw_current_speed(rect) - button_x = rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size - button_y = rect.y + UI_CONFIG.border_size - self._exp_button.render(rl.Rectangle(button_x, button_y, UI_CONFIG.button_size, UI_CONFIG.button_size)) - - def user_interacting(self) -> bool: - return self._exp_button.is_pressed - def _draw_set_speed(self, rect: rl.Rectangle) -> None: """Draw the MAX speed indicator box.""" set_speed_width = UI_CONFIG.set_speed_width_metric if ui_state.is_metric else UI_CONFIG.set_speed_width_imperial diff --git a/openpilot/selfdrive/ui/sunnypilot/layouts/settings/cruise.py b/openpilot/selfdrive/ui/sunnypilot/layouts/settings/cruise.py index 1b78200d3..e126ec210 100644 --- a/openpilot/selfdrive/ui/sunnypilot/layouts/settings/cruise.py +++ b/openpilot/selfdrive/ui/sunnypilot/layouts/settings/cruise.py @@ -85,7 +85,6 @@ class CruiseLayout(Widget): ) items = [ - self._toggles_layout._toggles["ExperimentalMode"], self._toggles_layout._toggles["LongitudinalPersonality"], self._toggles_layout._toggles["IsMetric"], self.scc_v_toggle, diff --git a/openpilot/selfdrive/ui/sunnypilot/ui_state.py b/openpilot/selfdrive/ui/sunnypilot/ui_state.py index f70da2b48..e5ddd7040 100644 --- a/openpilot/selfdrive/ui/sunnypilot/ui_state.py +++ b/openpilot/selfdrive/ui/sunnypilot/ui_state.py @@ -8,6 +8,7 @@ from enum import Enum from openpilot.cereal import messaging, log, custom from opendbc.car.structs import car +from opendbc.sunnypilot.car.interfaces import has_bsm from openpilot.common.params import Params from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import OnroadBrightness from openpilot.sunnypilot.models.helpers import ACTIVE_BUNDLE_KEYS, get_active_source @@ -35,7 +36,7 @@ class UIStateSP: self.is_sp_release: bool = self.params.get_bool("IsReleaseSpBranch") self.sm_services_ext = [ "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP", - "gpsLocation", "lateralTorqueParameters", "carStateSP", "liveMapDataSP", "carParamsSP", "lateralDelay" + "lateralTorqueParameters", "carStateSP", "liveMapDataSP", "carParamsSP", "lateralDelay" ] self.sunnylink_state = SunnylinkState() @@ -212,7 +213,7 @@ class UIStateSP: self.params.remove("AlphaLongitudinalEnabled") # BSM not available: clear BSM-dependent settings - if not CP.enableBsm: + if not has_bsm(CP, self.CP_SP): self.params.remove("AutoLaneChangeBsmDelay") else: # No CarParams: clear all car-dependent params as safety default @@ -221,10 +222,6 @@ class UIStateSP: self.params.remove("LateralJerkTorqueController") self.params.remove("AlphaLongitudinalEnabled") - # No longitudinal control: no experimental mode - if not has_long: - self.params.remove("ExperimentalMode") - # ICBM: clear if not available or if full longitudinal control is active if self.CP_SP is not None: if not self.CP_SP.intelligentCruiseButtonManagementAvailable or has_long: diff --git a/openpilot/selfdrive/ui/ui_state.py b/openpilot/selfdrive/ui/ui_state.py index a101f93d5..0a92c9767 100644 --- a/openpilot/selfdrive/ui/ui_state.py +++ b/openpilot/selfdrive/ui/ui_state.py @@ -12,8 +12,9 @@ from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState from openpilot.system.ui.lib.application import gui_app from openpilot.common.hardware import HARDWARE, PC -from openpilot.common.hardware.usb import TYPEC_CC_ORIENTATION_PATH, get_usb_state, is_chestnut_usb_id, read_int +from openpilot.common.hardware.usb import cable_connected, get_usb_state, is_chestnut_usb_id from openpilot.selfdrive.modeld.helpers import chestnut_compiled +from openpilot.selfdrive.selfdrived.alertmanager import OFFROAD_ALERTS from openpilot.selfdrive.ui.sunnypilot.ui_state import UIStateSP, DeviceSP @@ -69,6 +70,8 @@ class UIState(UIStateSP): "selfdriveState", "longitudinalPlan", "gpsLocationExternal", + "gpsLocation", + "navInstruction", "carOutput", "carControl", "vehicleParameters", @@ -88,14 +91,16 @@ class UIState(UIStateSP): # Core state variables self.is_metric: bool = self.params.get_bool("IsMetric") + self.initial_cruise_speed: int = self.params.get("InitialCruiseSpeedKph", return_default=True) self.is_release = False # self.params.get_bool("IsReleaseBranch") self.always_on_dm: bool = self.params.get_bool("AlwaysOnDM") - self.experimental_mode: bool = self.params.get_bool("ExperimentalMode") - self.experimental_mode_confirmed: bool = self.params.get_bool("ExperimentalModeConfirmed") + self.navigation_enabled: bool = self.params.get_bool("NavigationEnabled") + self.nav_destination_set: bool = self.params.get("NavDestination") is not None self.chestnut_present: bool = False self.chestnut_compiled: bool = chestnut_compiled() self.chestnut_active: bool | None = None self.chestnut_loading: bool = False + self.chestnut_alert: bool = False self.usb_connected: bool = False self.usb_connected_ts: float | None = None self.usb_disconnected_ts: float | None = None @@ -210,7 +215,7 @@ class UIState(UIStateSP): self.status = UIStatus.DISENGAGED self.started_frame = self.sm.frame self.started_time = time.monotonic() - self.chestnut_present = self.sm["deviceState"].chestnutPresent + self.chestnut_present = self.sm["deviceState"].chestnutPresent or (self.chestnut_compiled and self.usb_connected) for callback in self._offroad_transition_callbacks: callback() @@ -221,7 +226,8 @@ class UIState(UIStateSP): detected = self.sm["deviceState"].chestnutPresent if not self.started: self.chestnut_present = detected - self.chestnut_state = (ChestnutState.READY if detected and self.chestnut_compiled else + self.chestnut_state = (ChestnutState.FAILED if self.chestnut_alert else + ChestnutState.READY if detected and self.chestnut_compiled else ChestnutState.UNCOMPILED if detected else ChestnutState.DISCONNECTED) return @@ -230,10 +236,10 @@ class UIState(UIStateSP): self.chestnut_state = ChestnutState.DISCONNECTED elif not self.chestnut_compiled: self.chestnut_state = ChestnutState.UNCOMPILED - elif self.chestnut_state == ChestnutState.FAILED or not detected or (model_seen and (not self.sm.alive["modelV2"] or not self.sm["modelV2"].big)): - self.chestnut_state = ChestnutState.FAILED elif self.chestnut_loading or not model_seen: self.chestnut_state = ChestnutState.LOADING + elif self.chestnut_state == ChestnutState.FAILED or not detected or (model_seen and (not self.sm.alive["modelV2"] or not self.sm["modelV2"].big)): + self.chestnut_state = ChestnutState.FAILED elif self.chestnut_active is False: self.chestnut_state = ChestnutState.FAILED else: @@ -252,15 +258,15 @@ class UIState(UIStateSP): self.recording_audio = self.params.get_bool("RecordAudio") and self.started self.is_metric = self.params.get_bool("IsMetric") + self.initial_cruise_speed = self.params.get("InitialCruiseSpeedKph", return_default=True) self.always_on_dm = self.params.get_bool("AlwaysOnDM") - self.experimental_mode = self.params.get_bool("ExperimentalMode") - self.experimental_mode_confirmed = self.params.get_bool("ExperimentalModeConfirmed") - if not self.chestnut_compiled: - self.chestnut_compiled = chestnut_compiled() + self.navigation_enabled = self.params.get_bool("NavigationEnabled") + self.nav_destination_set = self.params.get("NavDestination") is not None self.chestnut_active = self.params.get("ChestnutActive") self.chestnut_loading = self.params.get_bool("ChestnutLoading") + self.chestnut_alert = any(self.params.get(k) is not None for k, a in OFFROAD_ALERTS.items() if k.startswith("Offroad_Chestnut") and a["severity"] >= 0) now = time.monotonic() - if read_int(TYPEC_CC_ORIENTATION_PATH) != 0: + if cable_connected(): self.usb_disconnected_ts = None if not self.usb_connected: self.usb_connected = True diff --git a/openpilot/selfdrive/ui/widgets/exp_mode_button.py b/openpilot/selfdrive/ui/widgets/exp_mode_button.py deleted file mode 100644 index 0b5bff7da..000000000 --- a/openpilot/selfdrive/ui/widgets/exp_mode_button.py +++ /dev/null @@ -1,65 +0,0 @@ -import pyray as rl -from openpilot.common.params import Params -from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE -from openpilot.system.ui.lib.multilang import tr -from openpilot.system.ui.widgets import Widget - - -class ExperimentalModeButton(Widget): - def __init__(self): - super().__init__() - - self.img_width = 80 - self.horizontal_padding = 25 - self.button_height = 125 - - self.params = Params() - self.experimental_mode = self.params.get_bool("ExperimentalMode") - - self.chill_pixmap = gui_app.texture("icons/couch.png", self.img_width, self.img_width) - self.experimental_pixmap = gui_app.texture("icons/experimental_grey.png", self.img_width, self.img_width) - - def show_event(self): - super().show_event() - self.experimental_mode = self.params.get_bool("ExperimentalMode") - - def _get_gradient_colors(self): - alpha = 0xCC if self.is_pressed else 0xFF - - if self.experimental_mode: - return rl.Color(255, 155, 63, alpha), rl.Color(219, 56, 34, alpha) - else: - return rl.Color(20, 255, 171, alpha), rl.Color(35, 149, 255, alpha) - - def _draw_gradient_background(self, rect): - start_color, end_color = self._get_gradient_colors() - rl.draw_rectangle_gradient_h(int(rect.x), int(rect.y), int(rect.width), int(rect.height), - start_color, end_color) - - def _render(self, rect): - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - self._draw_gradient_background(rect) - rl.draw_rectangle_rounded_lines_ex(self._rect, 0.19, 10, 5, rl.BLACK) - rl.end_scissor_mode() - - # Draw vertical separator line - line_x = rect.x + rect.width - self.img_width - (2 * self.horizontal_padding) - separator_color = rl.Color(0, 0, 0, 77) # 0x4d = 77 - rl.draw_line_ex(rl.Vector2(line_x, rect.y), rl.Vector2(line_x, rect.y + rect.height), 3, separator_color) - - # Draw text label (left aligned) - text = tr("EXPERIMENTAL MODE ON") if self.experimental_mode else tr("CHILL MODE ON") - text_x = rect.x + self.horizontal_padding - text_y = rect.y + rect.height / 2 - 45 * FONT_SCALE // 2 # Center vertically - - rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), text, rl.Vector2(int(text_x), int(text_y)), 45, 0, rl.BLACK) - - # Draw icon (right aligned) - icon_x = rect.x + rect.width - self.horizontal_padding - self.img_width - icon_y = rect.y + (rect.height - self.img_width) / 2 - icon_rect = rl.Rectangle(icon_x, icon_y, self.img_width, self.img_width) - - # Draw current mode icon - current_icon = self.experimental_pixmap if self.experimental_mode else self.chill_pixmap - source_rect = rl.Rectangle(0, 0, current_icon.width, current_icon.height) - rl.draw_texture_pro(current_icon, source_rect, icon_rect, rl.Vector2(0, 0), 0, rl.WHITE) diff --git a/openpilot/tools/replay/logreader.cc b/openpilot/tools/replay/logreader.cc index 315aed974..cafd862e1 100644 --- a/openpilot/tools/replay/logreader.cc +++ b/openpilot/tools/replay/logreader.cc @@ -4,7 +4,7 @@ #include