From af04cd6c65f4e5810f450cd1b1e1555cec666539 Mon Sep 17 00:00:00 2001 From: FrogAi <91348155+FrogAi@users.noreply.github.com> Date: Wed, 6 Mar 2024 18:30:45 -0700 Subject: [PATCH] Experimental Mode activation via steering wheel / onroad UI Added toggle to enable or disable Experimental Mode from the steering wheel or onroad UI. --- common/params.cc | 4 + panda/python/__init__.py | 1 + selfdrive/car/gm/carstate.py | 14 ++++ selfdrive/car/honda/carstate.py | 10 +++ selfdrive/car/hyundai/carstate.py | 20 +++++ selfdrive/car/hyundai/interface.py | 4 + selfdrive/car/hyundai/values.py | 1 + selfdrive/car/interfaces.py | 6 ++ selfdrive/car/toyota/carstate.py | 29 ++++++++ selfdrive/controls/controlsd.py | 4 + .../conditional_experimental_mode.py | 13 +++- .../functions/frogpilot_functions.py | 19 +++++ selfdrive/frogpilot/ui/control_settings.cc | 16 ++++ selfdrive/frogpilot/ui/control_settings.h | 1 + selfdrive/ui/qt/onroad.cc | 74 ++++++++++++++++--- selfdrive/ui/qt/onroad.h | 3 + selfdrive/ui/ui.cc | 1 + selfdrive/ui/ui.h | 1 + 18 files changed, 207 insertions(+), 14 deletions(-) diff --git a/common/params.cc b/common/params.cc index 5f1f906..35e9ea7 100644 --- a/common/params.cc +++ b/common/params.cc @@ -255,6 +255,10 @@ std::unordered_map keys = { {"DriveStats", PERSISTENT}, {"DynamicPathWidth", PERSISTENT}, {"EngageVolume", PERSISTENT}, + {"ExperimentalModeActivation", PERSISTENT}, + {"ExperimentalModeViaDistance", PERSISTENT}, + {"ExperimentalModeViaLKAS", PERSISTENT}, + {"ExperimentalModeViaScreen", PERSISTENT}, {"FrogPilotTogglesUpdated", PERSISTENT}, {"FrogsGoMoo", PERSISTENT}, {"GoatScream", PERSISTENT}, diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 1e60947..ee270e6 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -213,6 +213,7 @@ class Panda: FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32 FLAG_HYUNDAI_ALT_LIMITS = 64 FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128 + FLAG_HYUNDAI_LFA_BTN = 256 FLAG_TESLA_POWERTRAIN = 1 FLAG_TESLA_LONG_CONTROL = 2 diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index d886fc0..c319795 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -152,6 +152,20 @@ class CarState(CarStateBase): ret.leftBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1 ret.rightBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1 + # Toggle Experimental Mode from steering wheel function + if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available: + if self.CP.carFingerprint in SDGM_CAR: + lkas_pressed = cam_cp.vl["ASCMSteeringButton"]["LKAButton"] + else: + lkas_pressed = pt_cp.vl["ASCMSteeringButton"]["LKAButton"] + + if lkas_pressed and not self.lkas_previously_pressed: + if frogpilot_variables.conditional_experimental_mode: + self.fpf.update_cestatus_lkas() + else: + self.fpf.update_experimental_mode() + self.lkas_previously_pressed = lkas_pressed + return ret @staticmethod diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index 0d6f945..80cce77 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -263,6 +263,16 @@ class CarState(CarStateBase): ret.leftBlindspot = cp_body.vl["BSM_STATUS_LEFT"]["BSM_ALERT"] == 1 ret.rightBlindspot = cp_body.vl["BSM_STATUS_RIGHT"]["BSM_ALERT"] == 1 + # Toggle Experimental Mode from steering wheel function + if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available: + lkas_pressed = self.cruise_setting == 1 + if lkas_pressed and not self.lkas_previously_pressed: + if frogpilot_variables.conditional_experimental_mode: + self.fpf.update_cestatus_lkas() + else: + self.fpf.update_experimental_mode() + self.lkas_previously_pressed = lkas_pressed + return ret def get_can_parser(self, CP): diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 0687a65..0326f6b 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -170,6 +170,16 @@ class CarState(CarStateBase): if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0: self.main_enabled = not self.main_enabled + # Toggle Experimental Mode from steering wheel function + if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available and self.CP.flags & HyundaiFlags.CAN_LFA_BTN: + lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"] + if lkas_pressed and not self.lkas_previously_pressed: + if frogpilot_variables.conditional_experimental_mode: + self.fpf.update_cestatus_lkas() + else: + self.fpf.update_experimental_mode() + self.lkas_previously_pressed = lkas_pressed + return ret def update_canfd(self, cp, cp_cam, frogpilot_variables): @@ -255,6 +265,13 @@ class CarState(CarStateBase): self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else cp_cam.vl["CAM_0x2a4"]) + # Toggle Experimental Mode from steering wheel function + if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available: + lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"] + if lkas_pressed and not self.lkas_previously_pressed: + self.fpf.lkas_button_function(frogpilot_variables.conditional_experimental_mode) + self.lkas_previously_pressed = lkas_pressed + return ret def get_can_parser(self, CP): @@ -304,6 +321,9 @@ class CarState(CarStateBase): else: messages.append(("LVR12", 100)) + if CP.flags & HyundaiFlags.CAN_LFA_BTN: + messages.append(("BCM_PO_11", 50)) + return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0) @staticmethod diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index b6a9853..c573767 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -316,6 +316,10 @@ class CarInterface(CarInterfaceBase): if candidate in CAMERA_SCC_CAR: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC + if 0x391 in fingerprint[0]: + ret.flags |= HyundaiFlags.CAN_LFA_BTN.value + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LFA_BTN + if ret.openpilotLongitudinalControl: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG if ret.flags & HyundaiFlags.HYBRID: diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1eefc30..a742125 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -67,6 +67,7 @@ class HyundaiFlags(IntFlag): CANFD_HDA2_ALT_STEERING = 512 HYBRID = 1024 EV = 2048 + CAN_LFA_BTN = 4096 class CAR(StrEnum): diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index a5a42de..1572e44 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -373,6 +373,12 @@ class CarStateBase(ABC): # FrogPilot variables self.fpf = FrogPilotFunctions() + self.distance_button = False + self.distance_previously_pressed = False + self.lkas_previously_pressed = False + + self.distance_pressed_counter = 0 + def update_speed_kf(self, v_ego_raw): if abs(v_ego_raw - self.v_ego_kf.x[0][0]) > 2.0: # Prevent large accelerations when car starts at non zero speed self.v_ego_kf.set_x([[v_ego_raw], [0.0]]) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 7182ea4..f021de9 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -10,6 +10,7 @@ from opendbc.can.parser import CANParser from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, \ TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR +from openpilot.selfdrive.controls.lib.drive_helpers import CRUISE_LONG_PRESS SteerControlType = car.CarParams.SteerControlType @@ -166,6 +167,34 @@ class CarState(CarStateBase): if self.CP.carFingerprint != CAR.PRIUS_V: self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"]) + # Experimental Mode via double clicking the LKAS button function + if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available and self.CP.carFingerprint != CAR.PRIUS_V: + message_keys = ["LDA_ON_MESSAGE", "SET_ME_X02"] + lkas_pressed = any(self.lkas_hud.get(key) == 1 for key in message_keys) + + if lkas_pressed and not self.lkas_previously_pressed: + if frogpilot_variables.conditional_experimental_mode: + self.fpf.update_cestatus_distance() + else: + self.fpf.update_experimental_mode() + + self.lkas_previously_pressed = lkas_pressed + + # Experimental Mode via long pressing the distance button function + if ret.cruiseState.available: + if distance_pressed: + self.distance_pressed_counter += 1 + elif self.distance_previously_pressed: + self.distance_pressed_counter = 0 + + if self.distance_pressed_counter == CRUISE_LONG_PRESS and frogpilot_variables.experimental_mode_via_distance: + if frogpilot_variables.conditional_experimental_mode: + self.fpf.update_cestatus_lkas() + else: + self.fpf.update_experimental_mode() + + self.distance_previously_pressed = distance_pressed + return ret @staticmethod diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 8c9e48f..7e569d8 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -1023,6 +1023,10 @@ class Controls: frog_sounds = custom_sounds == 1 self.goat_scream = frog_sounds and self.params.get_bool("GoatScream") + experimental_mode_activation = self.params.get_bool("ExperimentalModeActivation") + self.frogpilot_variables.experimental_mode_via_distance = experimental_mode_activation and self.params.get_bool("ExperimentalModeViaDistance") + self.frogpilot_variables.experimental_mode_via_lkas = experimental_mode_activation and self.params.get_bool("ExperimentalModeViaLKAS") + lateral_tune = self.params.get_bool("LateralTune") longitudinal_tune = self.params.get_bool("LongitudinalTune") diff --git a/selfdrive/frogpilot/functions/conditional_experimental_mode.py b/selfdrive/frogpilot/functions/conditional_experimental_mode.py index be98665..a0dbdcf 100644 --- a/selfdrive/frogpilot/functions/conditional_experimental_mode.py +++ b/selfdrive/frogpilot/functions/conditional_experimental_mode.py @@ -37,15 +37,22 @@ class ConditionalExperimentalMode: lead = radarState.leadOne v_lead = lead.vLead + # Set the value of "overridden" + if self.experimental_mode_via_press: + overridden = self.params_memory.get_int("CEStatus") + else: + overridden = 0 + # Update Experimental Mode based on the current driving conditions condition_met = self.check_conditions(carState, frogpilotNavigation, lead, modelData, stop_distance, v_ego) - if (not self.experimental_mode and condition_met) and enabled: + if ((not self.experimental_mode and condition_met and overridden not in (1, 3, 5)) or overridden in (2, 4, 6)) and enabled: self.experimental_mode = True - elif (self.experimental_mode and not condition_met) or not enabled: + elif (self.experimental_mode and not condition_met and overridden not in (2, 4, 6)) or overridden in (1, 3, 5) or not enabled: self.experimental_mode = False self.status_value = 0 # Update the onroad status bar + self.status_value = overridden if overridden in (1, 2, 3, 4, 5, 6) else self.status_value if self.status_value != self.previous_status_value: self.params_memory.put_int("CEStatus", self.status_value) self.previous_status_value = self.status_value @@ -177,6 +184,8 @@ class ConditionalExperimentalMode: self.curves = params.get_bool("CECurves") self.curves_lead = self.curves and params.get_bool("CECurvesLead") + self.experimental_mode_via_press = params.get_bool("ExperimentalModeActivation") + self.limit = params.get_int("CESpeed") * (CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS) self.limit_lead = params.get_int("CESpeedLead") * (CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS) diff --git a/selfdrive/frogpilot/functions/frogpilot_functions.py b/selfdrive/frogpilot/functions/frogpilot_functions.py index 59f0c75..2581a3f 100644 --- a/selfdrive/frogpilot/functions/frogpilot_functions.py +++ b/selfdrive/frogpilot/functions/frogpilot_functions.py @@ -80,6 +80,25 @@ class FrogPilotFunctions: return min(distance_to_lane, distance_to_road_edge) + @staticmethod + def update_cestatus_distance(self): + # Set "CEStatus" to work with "Conditional Experimental Mode" + conditional_status = params_memory.get_int("CEStatus") + override_value = 0 if conditional_status in (1, 2, 3, 4, 5, 6) else 3 if conditional_status >= 7 else 4 + params_memory.put_int("CEStatus", override_value) + + @staticmethod + def update_cestatus_lkas(self): + # Set "CEStatus" to work with "Conditional Experimental Mode" + conditional_status = params_memory.get_int("CEStatus") + override_value = 0 if conditional_status in (1, 2, 3, 4, 5, 6) else 5 if conditional_status >= 7 else 6 + params_memory.put_int("CEStatus", override_value) + + def update_experimental_mode(self): + experimental_mode = self.params.get_bool("ExperimentalMode") + # Invert the value of "ExperimentalMode" + self.params.put_bool("ExperimentalMode", not experimental_mode) + @staticmethod def road_curvature(modelData, v_ego): predicted_velocities = np.array(modelData.velocity.x) diff --git a/selfdrive/frogpilot/ui/control_settings.cc b/selfdrive/frogpilot/ui/control_settings.cc index 0578cf4..6587b37 100644 --- a/selfdrive/frogpilot/ui/control_settings.cc +++ b/selfdrive/frogpilot/ui/control_settings.cc @@ -14,6 +14,11 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil {"CustomPersonalities", "Custom Driving Personalities", "Customize the driving personality profiles to your driving style.", "../frogpilot/assets/toggle_icons/icon_custom.png"}, {"DeviceShutdown", "Device Shutdown Timer", "Configure the timer for automatic device shutdown when offroad conserving energy and preventing battery drain.", "../frogpilot/assets/toggle_icons/icon_time.png"}, + {"ExperimentalModeActivation", "Experimental Mode Activation", "Toggle Experimental Mode with either buttons on ethe steering wheel or the screen.\n\nOverrides 'Conditional Experimental Mode'.", "../assets/img_experimental_white.svg"}, + {"ExperimentalModeViaLKAS", "Double Clicking the LKAS Button", "Enable/disable 'Experimental Mode' by double clicking the 'LKAS' button on your steering wheel.", ""}, + {"ExperimentalModeViaScreen", "Double Taping the Onroad UI", "Enable/disable 'Experimental Mode' by double taping the onroad UI within a 0.5 second time frame.", ""}, + {"ExperimentalModeViaDistance", "Long Pressing the Distance Button", "Enable/disable 'Experimental Mode' by holding down the 'distance' button on your steering wheel for 0.5 seconds.", ""}, + {"LateralTune", "Lateral Tuning", "Modify openpilot's steering behavior.", "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"}, {"LongitudinalTune", "Longitudinal Tuning", "Modify openpilot's acceleration and braking behavior.", "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"}, @@ -142,6 +147,16 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil } toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 33, shutdownLabels, this, false); + } else if (param == "ExperimentalModeActivation") { + FrogPilotParamManageControl *experimentalModeActivationToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(experimentalModeActivationToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(experimentalModeActivationKeys.find(key.c_str()) != experimentalModeActivationKeys.end()); + } + }); + toggle = experimentalModeActivationToggle; + } else if (param == "LateralTune") { FrogPilotParamManageControl *lateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(lateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { @@ -293,6 +308,7 @@ void FrogPilotControlsPanel::hideSubToggles() { for (auto &[key, toggle] : toggles) { bool subToggles = conditionalExperimentalKeys.find(key.c_str()) != conditionalExperimentalKeys.end() || + experimentalModeActivationKeys.find(key.c_str()) != experimentalModeActivationKeys.end() || fireTheBabysitterKeys.find(key.c_str()) != fireTheBabysitterKeys.end() || laneChangeKeys.find(key.c_str()) != laneChangeKeys.end() || lateralTuneKeys.find(key.c_str()) != lateralTuneKeys.end() || diff --git a/selfdrive/frogpilot/ui/control_settings.h b/selfdrive/frogpilot/ui/control_settings.h index 11149e8..68899b1 100644 --- a/selfdrive/frogpilot/ui/control_settings.h +++ b/selfdrive/frogpilot/ui/control_settings.h @@ -36,6 +36,7 @@ private: FrogPilotDualParamControl *relaxedProfile; std::set conditionalExperimentalKeys = {"CECurves", "CECurvesLead", "CESlowerLead", "CENavigation", "CEStopLights", "CESignal"}; + std::set experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaScreen"}; std::set fireTheBabysitterKeys = {}; std::set laneChangeKeys = {}; std::set lateralTuneKeys = {}; diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index ccb7de7..dffbaff 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -66,6 +67,12 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()-> QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState); QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition); QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged); + + QObject::connect(&clickTimer, &QTimer::timeout, this, [this]() { + clickTimer.stop(); + QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, timeoutPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QApplication::postEvent(this, event); + }); } void OnroadWindow::updateState(const UIState &s) { @@ -98,12 +105,33 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) { // FrogPilot clickable widgets bool widgetClicked = false; + // If the click wasn't for anything specific, change the value of "ExperimentalMode" + if (scene.experimental_mode_via_screen && e->pos() != timeoutPoint) { + if (clickTimer.isActive()) { + clickTimer.stop(); + + if (scene.conditional_experimental) { + int override_value = (scene.conditional_status >= 1 && scene.conditional_status <= 6) ? 0 : scene.conditional_status >= 7 ? 1 : 2; + paramsMemory.putIntNonBlocking("CEStatus", override_value); + } else { + bool experimentalMode = params.getBool("ExperimentalMode"); + params.putBoolNonBlocking("ExperimentalMode", !experimentalMode); + } + + } else { + clickTimer.start(500); + } + widgetClicked = true; + } + #ifdef ENABLE_MAPS if (map != nullptr && !widgetClicked) { // Switch between map and sidebar when using navigate on openpilot bool sidebarVisible = geometry().x() > 0; bool show_map = uiState()->scene.navigate_on_openpilot ? sidebarVisible : !sidebarVisible; - map->setVisible(show_map && !map->isVisible()); + if (!clickTimer.isActive()) { + map->setVisible(show_map && !map->isVisible()); + } } #endif // propagation event to parent(HomeWindow) @@ -234,9 +262,14 @@ void ExperimentalButton::changeMode() { Params paramsMemory = Params("/dev/shm/params"); const auto cp = (*uiState()->sm)["carParams"].getCarParams(); - bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed"); + bool can_change = hasLongitudinalControl(cp) && (params.getBool("ExperimentalModeConfirmed") || scene.experimental_mode_via_screen); if (can_change) { - params.putBool("ExperimentalMode", !experimental_mode); + if (scene.conditional_experimental) { + int override_value = (scene.conditional_status >= 1 && scene.conditional_status <= 4) ? 0 : scene.conditional_status >= 5 ? 3 : 4; + paramsMemory.putIntNonBlocking("ConditionalStatus", override_value); + } else { + params.putBool("ExperimentalMode", !experimental_mode); + } } } @@ -1136,15 +1169,17 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) { {2, "Experimental Mode manually activated"}, {3, "Conditional Experimental overridden"}, {4, "Experimental Mode manually activated"}, - {5, "Experimental Mode activated for" + (mapOpen ? " intersection" : QString(" upcoming intersection"))}, - {6, "Experimental Mode activated for" + (mapOpen ? " turn" : QString(" upcoming turn"))}, - {7, "Experimental Mode activated due to" + (mapOpen ? "SLC" : QString(" no speed limit set"))}, - {8, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeedLead) + (is_metric ? " kph" : " mph"))}, - {9, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeed) + (is_metric ? " kph" : " mph"))}, - {10, "Experimental Mode activated for slower lead"}, - {11, "Experimental Mode activated for turn" + (mapOpen ? "" : QString(" / lane change"))}, - {12, "Experimental Mode activated for curve"}, - {13, "Experimental Mode activated for stop" + (mapOpen ? "" : QString(" sign / stop light"))}, + {5, "Conditional Experimental overridden"}, + {6, "Experimental Mode manually activated"}, + {7, "Experimental Mode activated for" + (mapOpen ? " intersection" : QString(" upcoming intersection"))}, + {8, "Experimental Mode activated for" + (mapOpen ? " turn" : QString(" upcoming turn"))}, + {9, "Experimental Mode activated due to" + (mapOpen ? "SLC" : QString(" no speed limit set"))}, + {10, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeedLead) + (is_metric ? " kph" : " mph"))}, + {11, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeed) + (is_metric ? " kph" : " mph"))}, + {12, "Experimental Mode activated for slower lead"}, + {13, "Experimental Mode activated for turn" + (mapOpen ? "" : QString(" / lane change"))}, + {14, "Experimental Mode activated for curve"}, + {15, "Experimental Mode activated for stop" + (mapOpen ? "" : QString(" sign / stop light"))}, }; // Update status text @@ -1154,6 +1189,21 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) { newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0]; } + // Append suffix to the status + QString distanceSuffix = ". Long press the \"distance\" button to revert"; + QString lkasSuffix = ". Double press the \"LKAS\" button to revert"; + QString screenSuffix = ". Double tap the screen to revert"; + + if (!alwaysOnLateral && !mapOpen && status != STATUS_DISENGAGED && !newStatus.isEmpty()) { + if (conditionalStatus == 1 || conditionalStatus == 2) { + newStatus += screenSuffix; + } else if (conditionalStatus == 3 || conditionalStatus == 4) { + newStatus += distanceSuffix; + } else if (conditionalStatus == 5 || conditionalStatus == 6) { + newStatus += lkasSuffix; + } + } + // Check if status has changed if (newStatus != lastShownStatus) { displayStatusText = true; diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 10656c2..dd29007 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -201,6 +201,9 @@ private: UIScene &scene; Params paramsMemory{"/dev/shm/params"}; + QPoint timeoutPoint = QPoint(420, 69); + QTimer clickTimer; + private slots: void offroadTransition(bool offroad); void primeChanged(bool prime); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 9f3a39f..0ff71b5 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -306,6 +306,7 @@ void ui_update_frogpilot_params(UIState *s) { scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0; scene.driver_camera = params.getBool("DriverCamera"); + scene.experimental_mode_via_screen = params.getBool("ExperimentalModeViaScreen") && params.getBool("ExperimentalModeActivation"); scene.model_ui = params.getBool("ModelUI"); scene.dynamic_path_width = scene.model_ui && params.getBool("DynamicPathWidth"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index c066723..e66f1c0 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -182,6 +182,7 @@ typedef struct UIScene { bool dynamic_path_width; bool enabled; bool experimental_mode; + bool experimental_mode_via_screen; bool lead_info; bool map_open; bool model_ui;