diff --git a/SConstruct b/SConstruct index a0119df4eca532..747cd09dd8a5fd 100644 --- a/SConstruct +++ b/SConstruct @@ -288,7 +288,6 @@ if arch == "Darwin": ] qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules] qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")] - qt_env["LINKFLAGS"] += ["-rdynamic"] qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"] qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin")) else: diff --git a/changes.diff b/changes.diff deleted file mode 100644 index f9986a0b456bcf..00000000000000 --- a/changes.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/.github/workflows/update-pr-branch.yaml b/.github/workflows/update-pr-branch.yaml -index ed1d4cb5a..fd0128207 100644 ---- a/.github/workflows/update-pr-branch.yaml -+++ b/.github/workflows/update-pr-branch.yaml -@@ -26,17 +26,19 @@ jobs: - git config --global user.name "${{ github.actor }}" - git config --global user.email "${{ github.actor }}@users.noreply.github.com" - -- - name: Checkout $SOURCE_BRANCH branch -+ - name: Checkout $TARGET_BRANCH branch - run: | -- git checkout $SOURCE_BRANCH -+ git checkout $TARGET_BRANCH - -- - name: Merge $SOURCE_BRANCH into $TARGET_BRANCH -+ - name: Apply changes from $SOURCE_BRANCH - run: | -+ git checkout $SOURCE_BRANCH -+ git diff $TARGET_BRANCH > changes.diff - git checkout $TARGET_BRANCH -- git merge $SOURCE_BRANCH --no-ff --no-commit --allow-unrelated-histories || true -- -- git ls-files -u | cut -f 2 | sort -u | xargs -I{} git checkout --theirs {} -+ git apply changes.diff - -+ - name: Commit changes with the day's date -+ run: | - git add . - - day=$(TZ='America/Phoenix' date '+%-d') diff --git a/common/params.cc b/common/params.cc index aeff1f64f491f4..4b8a5bb0de73f6 100644 --- a/common/params.cc +++ b/common/params.cc @@ -259,7 +259,8 @@ std::unordered_map keys = { {"CertifiedHerbalistDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CertifiedHerbalistLiveTorqueParameters", PERSISTENT}, {"CertifiedHerbalistScore", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CESignal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"CESignalSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"CESignalLaneDetection", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CESlowerLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CESpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CESpeedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, @@ -359,6 +360,7 @@ std::unordered_map keys = { {"HumanAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"HumanFollowing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"IconToDownload", PERSISTENT}, + {"IncreasedStoppedDistance", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"IncreaseThermalLimits", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"JerkInfo", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"LaneChangeCustomizations", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, @@ -543,7 +545,6 @@ std::unordered_map keys = { {"SteerRatio", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"SteerRatioStock", PERSISTENT}, {"StoppedTimer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"StoppingDistance", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"TacoTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"TetheringEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, {"ThemeDownloadProgress", PERSISTENT}, diff --git a/panda/board/safety/safety_toyota.h b/panda/board/safety/safety_toyota.h index 564c2ff22fe2cb..3a67a57bb97942 100644 --- a/panda/board/safety/safety_toyota.h +++ b/panda/board/safety/safety_toyota.h @@ -27,35 +27,6 @@ const SteeringLimits TOYOTA_STEERING_LIMITS = { }, }; -const SteeringLimits TOYOTA_STEERING_LIMITS_TACO = { - .max_steer = 1750, - .max_rate_up = 20, // ramp up slow - .max_rate_down = 30, // ramp down fast - .max_torque_error = 350, // max torque cmd in excess of motor torque - .max_rt_delta = 450, // the real time limit is 1800/sec, a 20% buffer - .max_rt_interval = 250000, - .type = TorqueMotorLimited, - - // the EPS faults when the steering angle rate is above a certain threshold for too long. to prevent this, - // we allow setting STEER_REQUEST bit to 0 while maintaining the requested torque value for a single frame - .min_valid_request_frames = 18, - .max_invalid_request_frames = 1, - .min_valid_request_rt_interval = 170000, // 170ms; a ~10% buffer on cutting every 19 frames - .has_steer_req_tolerance = true, - - // LTA angle limits - // factor for STEER_TORQUE_SENSOR->STEER_ANGLE and STEERING_LTA->STEER_ANGLE_CMD (1 / 0.0573) - .angle_deg_to_can = 17.452007, - .angle_rate_up_lookup = { - {5., 25., 25.}, - {0.3, 0.15, 0.15} - }, - .angle_rate_down_lookup = { - {5., 25., 25.}, - {0.36, 0.26, 0.26} - }, -}; - const int TOYOTA_LTA_MAX_ANGLE = 1657; // EPS only accepts up to 94.9461 const int TOYOTA_LTA_MAX_MEAS_TORQUE = 1500; const int TOYOTA_LTA_MAX_DRIVER_TORQUE = 150; @@ -142,7 +113,6 @@ bool toyota_alt_brake = false; bool toyota_stock_longitudinal = false; bool toyota_lta = false; int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file -float v_ego = 0.; static uint32_t toyota_compute_checksum(const CANPacket_t *to_push) { int addr = GET_ADDR(to_push); @@ -242,7 +212,6 @@ static void toyota_rx_hook(const CANPacket_t *to_push) { // check that all wheel speeds are at zero value vehicle_moving = speed != 0; - v_ego = speed / 4.0 * 0.01 / 3.6; UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 / 3.6); } @@ -340,14 +309,8 @@ static bool toyota_tx_hook(const CANPacket_t *to_send) { } } else { // check angle rate limits and inactive angle - if (v_ego < 11.0) { - if (steer_angle_cmd_checks(lta_angle, steer_control_enabled, TOYOTA_STEERING_LIMITS_TACO)) { - tx = false; - } - } else { - if (steer_angle_cmd_checks(lta_angle, steer_control_enabled, TOYOTA_STEERING_LIMITS)) { - tx = false; - } + if (steer_angle_cmd_checks(lta_angle, steer_control_enabled, TOYOTA_STEERING_LIMITS)) { + tx = false; } if (lta_request != lta_request2) { diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 59c48d6a473de2..a523a1b3b41aae 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -74,7 +74,6 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): can_sends = [] # *** steer torque *** - self.params = CarControllerParams(self.CP, CS.out.vEgoRaw) new_steer = int(round(actuators.steer * self.params.STEER_MAX)) apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.params) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 5a5ab299dc1121..a00a46486c75a0 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -152,7 +152,7 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp tune.kiBP = [0., 5., 35.] tune.kiV = [3.6, 2.4, 1.5] - if params.get_bool("FrogsGoMoosTweak"): + if ret.flags & ToyotaFlags.RAISED_ACCEL_LIMIT and params.get_bool("FrogsGoMoosTweak"): ret.vEgoStopping = 0.15 ret.vEgoStarting = 0.15 ret.stoppingDecelRate = 0.1 # reach stopping target smoothly diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index b74cebdf0c2751..c07123082e561d 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -37,14 +37,8 @@ def __init__(self, CP): self.ACCEL_MIN = -3.5 # m/s2 if CP.lateralTuning.which == 'torque': - if vEgoRaw < 11.0: - self.STEER_DELTA_UP = 20 - self.STEER_DELTA_DOWN = 30 - self.STEER_MAX = 1750 - else: - self.STEER_DELTA_UP = 15 # 1.0s time to peak torque - self.STEER_DELTA_DOWN = 25 # always lower than 45 otherwise the Rav4 faults (Prius seems ok with 50) - self.STEER_MAX = 1500 + self.STEER_DELTA_UP = 15 # 1.0s time to peak torque + self.STEER_DELTA_DOWN = 25 # always lower than 45 otherwise the Rav4 faults (Prius seems ok with 50) else: self.STEER_DELTA_UP = 10 # 1.5s time to peak torque self.STEER_DELTA_DOWN = 25 # always lower than 45 otherwise the Rav4 faults (Prius seems ok with 50) diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index a397216a636f76..b9cd1907433e9b 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -105,13 +105,13 @@ def get_T_FOLLOW(aggressive_follow=1.25, standard_follow=1.45, relaxed_follow=1. def get_stopped_equivalence_factor(v_lead): return (v_lead**2) / (2 * COMFORT_BRAKE) -def get_safe_obstacle_distance(v_ego, t_follow, stopping_distance): - return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + max(stopping_distance, STOP_DISTANCE - 1) +def get_safe_obstacle_distance(v_ego, t_follow): + return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + STOP_DISTANCE def desired_follow_distance(v_ego, v_lead, t_follow=None): if t_follow is None: t_follow = get_T_FOLLOW() - return get_safe_obstacle_distance(v_ego, t_follow, STOP_DISTANCE) - get_stopped_equivalence_factor(v_lead) + return get_safe_obstacle_distance(v_ego, t_follow) - get_stopped_equivalence_factor(v_lead) def gen_long_model(): @@ -181,7 +181,7 @@ def gen_long_ocp(): ocp.cost.yref = np.zeros((COST_DIM, )) ocp.cost.yref_e = np.zeros((COST_E_DIM, )) - desired_dist_comfort = get_safe_obstacle_distance(v_ego, lead_t_follow, STOP_DISTANCE) + desired_dist_comfort = get_safe_obstacle_distance(v_ego, lead_t_follow) # The main cost in normal operation is how close you are to the "desired" distance # from an obstacle at every timestep. This obstacle can be a lead car @@ -326,10 +326,10 @@ def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau): lead_xv = np.column_stack((x_lead_traj, v_lead_traj)) return lead_xv - def process_lead(self, lead, increased_stopping_distance=0): + def process_lead(self, lead, increased_distance=0): v_ego = self.x0[1] if lead is not None and lead.status: - x_lead = lead.dRel - increased_stopping_distance + x_lead = lead.dRel - increased_distance v_lead = lead.vLead a_lead = lead.aLeadK a_lead_tau = lead.aLeadTau @@ -355,11 +355,12 @@ def set_accel_limits(self, min_a, max_a): self.cruise_min_a = min_a self.max_a = max_a - def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_follow, frogpilot_toggles, personality=log.LongitudinalPersonality.standard): + def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_follow, trafficModeActive, frogpilot_toggles, personality=log.LongitudinalPersonality.standard): v_ego = self.x0[1] self.status = lead_one.status or lead_two.status + increased_distance = max(frogpilot_toggles.increase_stopped_distance + min(10 - v_ego, 0), 0) if not trafficModeActive else 0 - lead_xv_0 = self.process_lead(lead_one) + lead_xv_0 = self.process_lead(lead_one, increased_distance) lead_xv_1 = self.process_lead(lead_two) # To estimate a safe distance from a moving lead, we calculate how much stopping @@ -382,7 +383,7 @@ def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_fo v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, v_upper) - cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, t_follow, frogpilot_toggles.stopping_distance) + cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, t_follow) x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle]) self.source = SOURCES[np.argmin(x_obstacles[0])] @@ -428,9 +429,9 @@ def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_fo # Check if it got within lead comfort range # TODO This should be done cleaner if self.mode == 'blended': - if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow, frogpilot_toggles.stopping_distance))- self.x_sol[:,0] < 0.0): + if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow))- self.x_sol[:,0] < 0.0): self.source = 'lead0' - if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow, frogpilot_toggles.stopping_distance))- self.x_sol[:,0] < 0.0) and \ + if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow))- self.x_sol[:,0] < 0.0) and \ (lead_1_obstacle[0] - lead_0_obstacle[0]): self.source = 'lead1' diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 73feadb284015e..027255c94eb207 100644 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -233,7 +233,7 @@ def update(self, radarless_model, sm, frogpilot_toggles): self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error, v_ego, frogpilot_toggles.taco_tune) self.mpc.update(self.lead_one, self.lead_two, sm['frogpilotPlan'].vCruise, x, v, a, j, radarless_model, sm['frogpilotPlan'].tFollow, - frogpilot_toggles, personality=sm['controlsState'].personality) + sm['frogpilotCarState'].trafficModeActive, frogpilot_toggles, personality=sm['controlsState'].personality) self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution) self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution) diff --git a/selfdrive/frogpilot/assets/model_manager.py b/selfdrive/frogpilot/assets/model_manager.py index 0bd71efe2209cc..3d291eadf8fac5 100644 --- a/selfdrive/frogpilot/assets/model_manager.py +++ b/selfdrive/frogpilot/assets/model_manager.py @@ -13,8 +13,8 @@ VERSION = "v8" -DEFAULT_MODEL = "game-boy" -DEFAULT_MODEL_NAME = "Game Boy (Default)" +DEFAULT_MODEL = "north-dakota-v2" +DEFAULT_MODEL_NAME = "North Dakota V2 (Default)" def process_model_name(model_name): cleaned_name = re.sub(r'[🗺️👀📡]', '', model_name) diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png b/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png deleted file mode 100644 index 97c1d7fe5f5229..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_screen.png b/selfdrive/frogpilot/assets/toggle_icons/icon_screen.png deleted file mode 100644 index 201b943eb20746..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_screen.png and /dev/null differ diff --git a/selfdrive/frogpilot/controls/frogpilot_planner.py b/selfdrive/frogpilot/controls/frogpilot_planner.py index 0750baddf9ff84..12283b59aeb56b 100644 --- a/selfdrive/frogpilot/controls/frogpilot_planner.py +++ b/selfdrive/frogpilot/controls/frogpilot_planner.py @@ -57,8 +57,9 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState driving_gear = carState.gearShifter not in NON_DRIVING_GEARS - lead_distance = self.lead_one.dRel - stopping_distance = frogpilot_toggles.stopping_distance if not frogpilotCarState.trafficModeActive else STOP_DISTANCE + distance_offset = max(frogpilot_toggles.increase_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0 + lead_distance = self.lead_one.dRel - distance_offset + stopping_distance = STOP_DISTANCE + distance_offset self.frogpilot_acceleration.update(controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_toggles) diff --git a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py index 1fee2aaba0eabb..4930774b3b66dc 100644 --- a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py +++ b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py @@ -38,8 +38,8 @@ def check_conditions(self, carState, frogpilotNavigation, modelData, following_l return True desired_lane = self.frogpilot_planner.lane_width_left if carState.leftBlinker else self.frogpilot_planner.lane_width_right - lane_available = desired_lane >= frogpilot_toggles.lane_detection_width - if frogpilot_toggles.conditional_signal and v_ego < CITY_SPEED_LIMIT and (carState.leftBlinker or carState.rightBlinker) and not lane_available: + lane_available = desired_lane >= frogpilot_toggles.lane_detection_width or not frogpilot_toggles.conditional_signal_lane_detection or not frogpilot_toggles.lane_detection + if v_ego < frogpilot_toggles.conditional_signal and (carState.leftBlinker or carState.rightBlinker) and not lane_available: self.status_value = 9 return True diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_following.py b/selfdrive/frogpilot/controls/lib/frogpilot_following.py index 53bf18964127eb..36aeaf271133f1 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_following.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_following.py @@ -61,7 +61,7 @@ def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, stopping self.following_lead = self.frogpilot_planner.tracking_lead and lead_distance < (self.t_follow + 1) * v_ego if self.frogpilot_planner.tracking_lead: - self.safe_obstacle_distance = int(get_safe_obstacle_distance(v_ego, self.t_follow, stopping_distance)) + self.safe_obstacle_distance = int(get_safe_obstacle_distance(v_ego, self.t_follow)) self.safe_obstacle_distance_stock = self.safe_obstacle_distance self.stopped_equivalence_factor = int(get_stopped_equivalence_factor(v_lead)) self.update_follow_values(lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles) diff --git a/selfdrive/frogpilot/frogpilot_functions.py b/selfdrive/frogpilot/frogpilot_functions.py index 06b5a105cc3cb7..30d3d70f5e22d1 100644 --- a/selfdrive/frogpilot/frogpilot_functions.py +++ b/selfdrive/frogpilot/frogpilot_functions.py @@ -111,16 +111,22 @@ def cleanup_backup(in_progress_destination, in_progress_compressed_backup): print(f"An unexpected error occurred while trying to delete the incomplete {backup} backup: {e}") def backup_frogpilot(build_metadata, params): + maximum_backups = 5 minimum_backup_size = params.get_int("MinimumBackupSize") backup_path = "/data/backups" - cleanup_backups(backup_path, 4, minimum_backup_size, True) + cleanup_backups(backup_path, maximum_backups - 1, minimum_backup_size, True) - branch = build_metadata.channel - commit = build_metadata.openpilot.git_commit_date[12:-16] + total, used, free = shutil.disk_usage(backup_path) + required_free_space = minimum_backup_size * maximum_backups - backup_dir = os.path.join(backup_path, f"{branch}_{commit}_auto") - backup_directory(BASEDIR, backup_dir, f"Successfully backed up FrogPilot to {backup_dir}.", f"Failed to backup FrogPilot to {backup_dir}.", minimum_backup_size, params, True) + if free > required_free_space: + branch = build_metadata.channel + commit = build_metadata.openpilot.git_commit_date[12:-16] + + backup_dir = os.path.join(backup_path, f"{branch}_{commit}_auto") + + backup_directory(BASEDIR, backup_dir, f"Successfully backed up FrogPilot to {backup_dir}.", f"Failed to backup FrogPilot to {backup_dir}.", minimum_backup_size, params, True) def backup_toggles(params, params_storage): for key in params.all_keys(): @@ -129,8 +135,10 @@ def backup_toggles(params, params_storage): if value is not None: params_storage.put(key, value) + maximum_backups = 10 + backup_path = "/data/toggle_backups" - cleanup_backups(backup_path, 9) + cleanup_backups(backup_path, 10 - 1) backup_dir = os.path.join(backup_path, datetime.datetime.now().strftime('%Y-%m-%d_%I-%M%p').lower() + "_auto") backup_directory("/data/params/d", backup_dir, f"Successfully backed up toggles to {backup_dir}.", f"Failed to backup toggles to {backup_dir}.") @@ -196,33 +204,9 @@ def decrease_param(key): except Exception as e: print(f"An error occurred when converting params: {e}") - for key in ["PathWidth"]: + for key in ["LaneDetectionWidth", "PathWidth"]: decrease_param(key) - def increase_param(key): - try: - metric = params_storage.get_bool("IsMetric") - value = params_storage.get_float(key) - - if metric: - if value <= 5: - value += 6 - params.put_float(key, value) - params_storage.put_float(key, value) - else: - if value <= 10: - value += 20 - params.put_float(key, value) - params_storage.put_float(key, value) - - except (UnknownKeyName, ValueError): - pass - except Exception as e: - print(f"An error occurred when converting params: {e}") - - for key in ["StoppingDistance"]: - increase_param(key) - print("Param conversion completed") def delete_file(file): diff --git a/selfdrive/frogpilot/frogpilot_variables.py b/selfdrive/frogpilot/frogpilot_variables.py index 6215652bd5daf8..5d0856270786bd 100644 --- a/selfdrive/frogpilot/frogpilot_variables.py +++ b/selfdrive/frogpilot/frogpilot_variables.py @@ -78,16 +78,16 @@ def update_frogpilot_params(self, started=True): advanced_lateral_tune = self.params.get_bool("AdvancedLateralTune") stock_steer_friction = self.params.get_float("SteerFrictionStock") toggle.steer_friction = self.params.get_float("SteerFriction") if advanced_lateral_tune else stock_steer_friction - toggle.use_custom_steer_friction = toggle.steer_friction != stock_steer_friction + toggle.use_custom_steer_friction = toggle.steer_friction != stock_steer_friction != 0 stock_steer_lat_accel_factor = self.params.get_float("SteerLatAccelStock") toggle.steer_lat_accel_factor = self.params.get_float("SteerLatAccel") if advanced_lateral_tune else stock_steer_lat_accel_factor - toggle.use_custom_lat_accel_factor = toggle.steer_lat_accel_factor != stock_steer_lat_accel_factor + toggle.use_custom_lat_accel_factor = toggle.steer_lat_accel_factor != stock_steer_lat_accel_factor != 0 stock_steer_kp = self.params.get_float("SteerKPStock") toggle.steer_kp = self.params.get_float("SteerKP") if advanced_lateral_tune else stock_steer_kp - toggle.use_custom_kp = toggle.steer_kp != stock_steer_kp + toggle.use_custom_kp = toggle.steer_kp != stock_steer_kp != 0 stock_steer_ratio = self.params.get_float("SteerRatioStock") toggle.steer_ratio = self.params.get_float("SteerRatio") if advanced_lateral_tune else stock_steer_ratio - toggle.use_custom_steer_ratio = toggle.steer_ratio != stock_steer_ratio + toggle.use_custom_steer_ratio = toggle.steer_ratio != stock_steer_ratio != 0 toggle.taco_tune = advanced_lateral_tune and self.params.get_bool("TacoTune") toggle.turn_desires = advanced_lateral_tune and self.params.get_bool("TurnDesires") @@ -133,7 +133,8 @@ def update_frogpilot_params(self, started=True): toggle.conditional_navigation_intersections = toggle.conditional_navigation and self.params.get_bool("CENavigationIntersections") toggle.conditional_navigation_lead = toggle.conditional_navigation and self.params.get_bool("CENavigationLead") toggle.conditional_navigation_turns = toggle.conditional_navigation and self.params.get_bool("CENavigationTurns") - toggle.conditional_signal = toggle.conditional_experimental_mode and self.params.get_bool("CESignal") + toggle.conditional_signal = self.params.get_int("CESignalSpeed") if toggle.conditional_experimental_mode else 0 + toggle.conditional_signal_lane_detection = toggle.conditional_signal and self.params.get_bool("CESignalLaneDetection") if toggle.conditional_experimental_mode: self.params.put_bool("ExperimentalMode", True) @@ -221,7 +222,7 @@ def update_frogpilot_params(self, started=True): toggle.deceleration_profile = self.params.get_int("DecelerationProfile") if longitudinal_tune else 0 toggle.human_acceleration = longitudinal_tune and self.params.get_bool("HumanAcceleration") toggle.human_following = longitudinal_tune and self.params.get_bool("HumanFollowing") - toggle.stopping_distance = self.params.get_int("StoppingDistance") * distance_conversion if longitudinal_tune else 6.0 + toggle.increase_stopped_distance = self.params.get_int("IncreasedStoppedDistance") * distance_conversion if longitudinal_tune else 0 toggle.model_manager = self.params.get_bool("ModelManagement", block=openpilot_installed) available_models = self.params.get("AvailableModels", block=toggle.model_manager, encoding='utf-8') or "" diff --git a/selfdrive/frogpilot/navigation/ui/navigation_functions.h b/selfdrive/frogpilot/navigation/ui/navigation_functions.h index 1dc409f1fdc3d5..e56f7b8da08db0 100644 --- a/selfdrive/frogpilot/navigation/ui/navigation_functions.h +++ b/selfdrive/frogpilot/navigation/ui/navigation_functions.h @@ -223,7 +223,7 @@ class ButtonSelectionControl : public QWidget { QPushButton *button = new QPushButton(label, this); button->setCheckable(true); button->setStyleSheet(buttonStyle); - connect(button, &QPushButton::clicked, this, [this, button, stateCode] { updateState(stateCode, button); }); + QObject::connect(button, &QPushButton::clicked, this, [this, button, stateCode] { updateState(stateCode, button); }); layout->addWidget(button); return button; } diff --git a/selfdrive/frogpilot/navigation/ui/navigation_settings.cc b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc index 9ed1b0878c66d1..88d68238589aa0 100644 --- a/selfdrive/frogpilot/navigation/ui/navigation_settings.cc +++ b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc @@ -83,8 +83,6 @@ void FrogPilotNavigationPanel::hideEvent(QHideEvent *event) { void FrogPilotNavigationPanel::showEvent(QShowEvent *event) { downloadActive = !paramsMemory.get("OSMDownloadLocations").empty(); lastMapsDownload->setText(QString::fromStdString(params.get("LastMapsUpdate"))); - qint64 fileSize = calculateDirectorySize(offlineFolderPath); - offlineMapsSize->setText(formatSize(fileSize)); removeOfflineMapsButton->setVisible(QDir(offlineFolderPath).exists() && !downloadActive); } diff --git a/selfdrive/frogpilot/screenrecorder/screenrecorder.cc b/selfdrive/frogpilot/screenrecorder/screenrecorder.cc index dcbfeef9c6642b..2c0f73ca2a4b08 100644 --- a/selfdrive/frogpilot/screenrecorder/screenrecorder.cc +++ b/selfdrive/frogpilot/screenrecorder/screenrecorder.cc @@ -19,7 +19,7 @@ ScreenRecorder::ScreenRecorder(QWidget *parent) : QPushButton(parent), recording rgbScaleBuffer = std::make_unique(screenWidth * screenHeight * 4); setFixedSize(btn_size, btn_size); - connect(this, &QPushButton::clicked, this, &ScreenRecorder::toggleRecording); + QObject::connect(this, &QPushButton::clicked, this, &ScreenRecorder::toggleRecording); } ScreenRecorder::~ScreenRecorder() { diff --git a/selfdrive/frogpilot/thepond/.gitignore b/selfdrive/frogpilot/thepond/.gitignore deleted file mode 100644 index 083c276f3b19b3..00000000000000 --- a/selfdrive/frogpilot/thepond/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__ -preview.* -allparams.json -# Screenshots I use to create all the settings -deletethis -# Copy of manager.py I used to start thepond in the background -manager.py -.DS_Store \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/Dockerfile b/selfdrive/frogpilot/thepond/Dockerfile deleted file mode 100644 index 87cad090481b3c..00000000000000 --- a/selfdrive/frogpilot/thepond/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM python:3.11 - -# Install Python -RUN apt-get update -y && \ - apt-get install -y python3-pip python3-dev ffmpeg - -# Install Python dependencies with pip -RUN pip3 install --upgrade pip && \ - pip3 install flask requests gevent - -# Copy the current directory contents into the container at /app -COPY . /thepond - -CMD ["python3", "-m", "thepond.thepond"] diff --git a/selfdrive/frogpilot/thepond/README.md b/selfdrive/frogpilot/thepond/README.md deleted file mode 100644 index 35f20fd798a3cc..00000000000000 --- a/selfdrive/frogpilot/thepond/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# Thepond - -This is a new way to manage your frogpilot device. It allows you to change most settings that you can edit in on the device, as well as view the video stream and download the logs. - - -# Architecture - -ThePond consists of two parts: the python-flask API and the Arrow.js SPA frontend. By using es-modules I've been able to avoid having to introcude any kind of build-system (mostly as that would require node somewhere in the build process, and I have no clue how I would go about adding that :P). - -### API - -The API is fairly simple, there are endpoints for (these are all found in `thepond.py`, and are implemented as simple flask routes which return json): - -- Getting and saving settings -- Fetching lists of driven routes -- Loading a specific route and it's video stream (this implementation was just taken straight from Fleet Manager) -- Fetching error logs -- Saving a navigation destination (which will trigger openpilot to start navigation there) - -### Webapp - -The Webapp is built using [Arrow.js](https://www.arrow-js.com) (since I wanted to have some sort of reactive framework, and this one seemed simple enough and could be used without webpack or similar). It consists of a few components: - -- Navigation -- Settings -- Routes -- Error logs - -There are then a few structural components such as the `sidebar`, `router` and `theme-toggle` which are used to manage the layout and navigation of the app. - ---- - -# Developing - -## Add a new page - -To add a new page, you need to create a new component in the `components` folder, and then add it to the `router` component. The router component is a simple switch statement which renders the correct component based on the current route: - -`router.js` -```js - let routes = [ - createRoute("root", "/", Overview), - createRoute("routes", "/routes", RecordedRoutes), - createRoute("route", "/routes/:routeDate", RecordedRoute), - createRoute("settings", "/settings/:section/:subsection?", SettingsView), - createRoute("navdestination", "/navigation", NavDestination), - createRoute("errorLogs", "/error-logs", ErrorLogs), - createRoute("NAME", "/URL_PATH", ComponentName), <-- Insert your component here - ]; - ] -``` - - -Then add your new page to the sidebar, in the appropriate section (or in a new section if you prefer): - -`sidebar.js` -```js -const MenuItems = { - ... - navigation: [ - { - name: "Set destination", - link: "/navigation", - icon: "bi-globe-americas", - }, - { - name: "Sidebar title", <-- Add your new page here - link: "/URL_PATH", <-- Same url as in router.js - icon: "" <-- Add an icon here (Bootstrap icons are used, see https://icons.getbootstrap.com/ - }, - ], - ... -} -``` - -Once you've added your new page to the router and sidebar, you should be able to navigate to it by clicking the link in the sidebar. At this point it will try to render the component specified in the router, so make sure you've created that component ( and imported it in `router.js`). - -The structure of the components is fairly simple, and you can see how the existing components are built by looking at the files in the `components` folder. It is basically a function that returns some html, and if you need it to be reactive you can defined a state object and use the `reactive` function from Arrow.js to make it reactive. - -`sample-component.js` -```js -import { html, reactive } from "https://esm.sh/@arrow-js/core" - -export function SampleCompoent() { - const state = reactive({ - someValue: "Hello world" - }) - - return html` -
-

${state.someValue}

-
- ` -} -``` - -> Notice how the returned string is a template literal, enabling you to add js expressions using the `${}` syntax. - - - ---- - - -# Running - -### To run using docker: - -```bash -docker build -t thepond . -docker run -v $(pwd):/app --rm -ti -p 8084:8084 thepond -``` - -### Run and debug on comma device (or computer with python) - -```bash -./start.sh -``` diff --git a/selfdrive/frogpilot/thepond/assets/components/error-logs.js b/selfdrive/frogpilot/thepond/assets/components/error-logs.js deleted file mode 100644 index 83aca289d9870e..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/error-logs.js +++ /dev/null @@ -1,88 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { formatSecondsToHuman, parseErrorLogToDate } from "../js/utils.js" - -export function ErrorLogs() { - const state = reactive({ - files: "[]", - selectedLog: undefined, - loading: true, - }) - - async function getErrorLogs() { - const response = await fetch("/api/error-logs", { - headers: { Accept: "application/json" }, - }) - const data = await response.json() - state.files = JSON.stringify(data.map((file) => { - // Dateformat is YYYY-MM-DD--HH-MM-SS - const date = parseErrorLogToDate(file) - // Format it into a string - const formattedDate = date.toLocaleString() - const timeSince = Math.round(Date.now() - date.getTime()) / 1000 - - return { - filename: file, - date: formattedDate, - timeSince: timeSince, - } - })) - state.loading = false - } - - getErrorLogs() - - return html`

Error Logs

-
-
- ${() => { - if (state.loading) { - return html`
-

Loading...

-
` - } else { - return JSON.parse(state.files).map( - (file) => - html`
-

${file.date}

-

${formatSecondsToHuman(file.timeSince, "days")} ago

-
` - ) - } - }} -
- ${() => - state.selectedLog - ? Logviewer(state.selectedLog, () => (state.selectedLog = undefined)) - : ""} -
` -} - -function Logviewer(filename, closeFn) { - const logfile = reactive({ - loading: true, - content: undefined, - error: undefined, - }) - - async function getLogfile() { - const response = await fetch(`/api/error-logs/${filename}`, { - headers: { Accept: "application/json" }, - }) - const data = await response.text() - logfile.content = data - logfile.loading = false - } - - getLogfile() - - return html`
-
-

${filename}

- -
-
${() => (logfile.loading ? "Loading..." : logfile.content)}
-
` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/navigation/nav-destination.js b/selfdrive/frogpilot/thepond/assets/components/navigation/nav-destination.js deleted file mode 100644 index 5a7303ffd6293f..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/navigation/nav-destination.js +++ /dev/null @@ -1,392 +0,0 @@ -import { html, reactive, watch } from "https://esm.sh/@arrow-js/core" -import { - getCoordinatesFromSearch, - getRoute, - addRouteToMap, - formatSecondsToHuman, - formatMetersToHuman, - removeRouteFromMap, -} from "./navigation-utils.js" - -export function NavDestination() { - // Random between 0 and 100 - const random = Math.floor(Math.random() * 100) - let map - let destinationMarker - - const state = reactive({ - mapboxToken: undefined, - initialized: false, - lastPosition: undefined, - destination: undefined, - route: undefined, - suggestions: "[]", - previousDestinations: "[]", - }) - - const searchFieldState = reactive({ - value: "", - }) - - const getNavigationData = async () => { - const response = await fetch("/api/navigation") - const data = await response.json() - state.mapboxToken = data.mapboxToken.trim() - try { - state.lastPosition = JSON.parse(data.lastPosition.trim()) - } catch (e) { - console.log("Failed to parse last position", data.lastPosition) - state.lastPosition = { longitude: 0, latitude: 0 } - } - try { - state.destination = JSON.parse(data.destination?.trim()) - } catch (e) {} - try { - state.suggestions = JSON.stringify( - JSON.parse(data.previousDestinations)?.map((destination) => ({ - name: destination.place_name, - })) - ) - state.previousDestinations = state.suggestions - } catch (e) {} - setupMap() - } - - async function searchInput(e) { - searchFieldState.value = e.target.value - if (window.searchTimeout) { - clearTimeout(window.searchTimeout) - } - window.searchTimeout = setTimeout(async () => { - const searchValue = e.target.value - if (searchValue.length < 3) { - console.log("Search value too short") - return - } - // Remove any route - state.route = undefined - - console.log("Searching for", searchValue) - const lastPosition = `${state.lastPosition.longitude},${state.lastPosition.latitude}` - const params = new URLSearchParams({ - proximity: lastPosition, - access_token: state.mapboxToken, - session_token: "sfsf", - q: searchValue, - limit: 4, - }) - const response = await fetch( - `https://api.mapbox.com/search/searchbox/v1/suggest?${params.toString()}` - ) - const data = await response.json() - const suggestions = JSON.stringify(data.suggestions) - console.log("Got suggestions", suggestions) - - state.suggestions = suggestions - }, 800) - } - - const setupMap = async () => { - if (state.mapboxToken === undefined) { - console.log("Mapbox token not set") - return - } - if (state.initialized === true) { - console.log("Called setupMap, but already initialized") - return - } - state.initialized = true - - console.log("Setting up map") - mapboxgl.accessToken = state.mapboxToken - - map = new mapboxgl.Map({ - container: "map", // container ID - center: [state.lastPosition.longitude, state.lastPosition.latitude], // starting position [lng, lat] - zoom: 15, // starting zoom - pitch: 45, - attributionControl: false, - logoPosition: "bottom-right", - style: "mapbox://styles/mapbox/dark-v11", - }) - - // Add a destination and last position marker - destinationMarker = new mapboxgl.Marker() - new mapboxgl.Marker() - .setLngLat([state.lastPosition.longitude, state.lastPosition.latitude]) - .addTo(map) - - map.on("style.load", async () => { - // Insert the layer beneath any symbol layer. - const layers = map.getStyle().layers - const labelLayerId = layers.find( - (layer) => layer.type === "symbol" && layer.layout["text-field"] - ).id - - // The 'building' layer in the Mapbox Streets - // vector tileset contains building height data - // from OpenStreetMap. - map.addLayer( - { - id: "add-3d-buildings", - source: "composite", - "source-layer": "building", - filter: ["==", "extrude", "true"], - type: "fill-extrusion", - minzoom: 15, - paint: { - "fill-extrusion-color": "#aaa", - - // Use an 'interpolate' expression to - // add a smooth transition effect to - // the buildings as the user zooms in. - "fill-extrusion-height": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 0, - 15.05, - ["get", "height"], - ], - "fill-extrusion-base": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 0, - 15.05, - ["get", "min_height"], - ], - "fill-extrusion-opacity": 0.6, - }, - }, - labelLayerId - ) - - // If a destination is set, add it to the map - console.log("Destination", state.destination) - if (state.destination) { - console.log("Destination set, rendering it") - // Fetch the place, the get the route and add both to the map - const coordinates = [ - state.destination.longitude, - state.destination.latitude, - ] - - // Destionation marker - destinationMarker.setLngLat([coordinates[0], coordinates[1]]).addTo(map) - - const route = await getRoute( - `${state.lastPosition.longitude},${state.lastPosition.latitude}`, - `${coordinates[0]},${coordinates[1]}`, - state.mapboxToken - ) - addRouteToMap( - map, - [state.lastPosition.longitude, state.lastPosition.latitude], - coordinates, - route.geometry.coordinates - ) - - state.route = { - name: state.destination.name, - duration: route.duration, - distance: route.distance, - destinationCoordinates: coordinates, - startingCoordinates: [ - state.lastPosition.longitude, - state.lastPosition.latitude, - ], - confirmed: true, - } - } - }) - } - - getNavigationData() - - console.log("NavDestination rendered ", random) - - return html` -

Navigation

-
-
- -
- ${() => { - console.log(state.suggestions) - if (state.route) { - return NavigationDestination({ - ...state.route, - map: map, - cancelNavigationFn: () => { - state.route = undefined - state.suggestions = state.previousDestinations - }, - }) - } else if (JSON.parse(state.suggestions).length > 0) { - return SearchSuggestions({ - suggestions: JSON.parse(state.suggestions), - mapboxToken: state.mapboxToken, - map: map, - lastPosition: state.lastPosition, - destinationMarker, - setRouteFn: (route) => { - state.route = route - }, - }) - } - }} -
-
-
-
- ` -} - -/** - * A component listing search suggestions below the search field - * @param {string} suggestions An array of suggestions from mapbox - * @param {string} suggestions[].full_address The full address of the suggestion - * @param {string} suggestions[].name The name of the suggestion - * @param {string} suggestions[].place_formatted The place formatted - * @returns {HTMLElement} - */ -function SearchSuggestions({ - suggestions, - mapboxToken, - map, - lastPosition, - destinationMarker, - setRouteFn, -}) { - function formatSuggestion(suggestion) { - return [suggestion.name, suggestion.place_formatted] - .filter((it) => it !== undefined) - .join(", ") - } - - const clickHandler = async (suggestion) => { - const searchQuery = formatSuggestion(suggestion) - console.log("Clicked on", suggestion) - // Update the search field with the suggestion - const searchField = document.getElementById("search-field") - searchField.value = searchQuery - - // Fetch the place, the get the route and add both to the map - const coordinates = await getCoordinatesFromSearch(searchQuery, mapboxToken) - // Destionation marker - destinationMarker.setLngLat([coordinates[0], coordinates[1]]).addTo(map) - - const route = await getRoute( - `${lastPosition.longitude},${lastPosition.latitude}`, - `${coordinates[0]},${coordinates[1]}`, - mapboxToken - ) - console.log(coordinates) - addRouteToMap( - map, - [lastPosition.longitude, lastPosition.latitude], - coordinates, - route.geometry.coordinates - ) - - setRouteFn({ - name: searchQuery, - duration: route.duration, - distance: route.distance, - destinationCoordinates: coordinates, - startingCoordinates: [lastPosition.longitude, lastPosition.latitude], - }) - } - - const suggestionItem = (suggestion) => { - return html` -

${formatSuggestion(suggestion)}

-
` - } - - return html` -
${suggestions.map(suggestionItem)}
- ` -} - -/** - * The component showing the navigation summary - * @param {Object} navigation The navigation object - * @param {string} navigation.name The name of the destination - * @param {number} navigation.duration The duration of the navigation in seconds - * @param {number} navigation.distance The distance of the navigation in meters - * @param {boolean} navigation.confirmed If the navigation is confirmed - * @param {number[]} navigation.coordinates The coordinates of the destination - * @returns - */ -function NavigationDestination({ - name, - duration, - distance, - confirmed, - destinationCoordinates, - startingCoordinates, - map, - cancelNavigationFn, -}) { - const state = reactive({ - confirmed: confirmed ?? false, - }) - - async function confirmDestination() { - console.log("Navigating to", name) - showSnackbar("Navigation saved") - state.confirmed = true - const navDestination = { - name, - longitude: destinationCoordinates[0], - latitude: destinationCoordinates[1], - } - console.log("Setting nav destination", navDestination) - await fetch("/api/navigation", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(navDestination), - }) - } - - async function cancelNavigation() { - console.log("Cancelling navigation") - showSnackbar("Navigation cancelled") - state.confirmed = false - removeRouteFromMap(map, startingCoordinates) - cancelNavigationFn() - await fetch("/api/navigation", { - method: "DELETE", - }) - } - - return html` - - ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/navigation/navigation-utils.js b/selfdrive/frogpilot/thepond/assets/components/navigation/navigation-utils.js deleted file mode 100644 index 4788745c9fd936..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/navigation/navigation-utils.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Get the coordinates for a search value - * @param {string} searchValue The value to search for, can be anything - * @param {string} mapboxToken The mapbox token to use for the search - * @returns {number[]} [longitude, latitude] - */ -export async function getCoordinatesFromSearch(searchValue, mapboxToken) { - const params = new URLSearchParams({ - access_token: mapboxToken, - q: searchValue, - }) - const response = await fetch( - `https://api.mapbox.com/search/geocode/v6/forward?${params.toString()}` - ) - const data = await response.json() - //console.log("Got data", data) - const coordinates = data.features[0].geometry.coordinates - //console.log("Coordinates", coordinates) - return coordinates -} - -/** - * Get a route from point A to point B - * @param {string} from The starting point of the route, in the format "longitude, latitude" - * @param {string} to The destination of the route, in the format "longitude, latitude" - * @param {string} mapboxToken The mapbox token to use for the search - * @returns {{ distance: number, duration: number, geometry: { type: string, coordinates: number[][] }}} - */ -export async function getRoute(from, to, mapboxToken) { - const url = `https://api.mapbox.com/directions/v5/mapbox/driving/${from};${to}?geometries=geojson&access_token=${mapboxToken}` - console.log("Fetching route from", url) - const route = await fetch(url) - const routeData = await route.json() - console.log("Got route data", routeData) - return routeData.routes[0] -} - -/** - * Adds a rendered route to the map - * @param {unknown} map The mapboxgl.Map instance - * @param {number[]} startingPoint The starting point of the route, in the format [longitude, latitude] - * @param {number[]} destinationCoordinates The destination of the route, in the format [longitude, latitude] - * @param {*} lineCoordinates The coordinates of the route line, not really sure of the format - */ -export function addRouteToMap( - map, - startingPoint, - destinationCoordinates, - lineCoordinates -) { - const line = { - type: "Feature", - geometry: { - type: "LineString", - coordinates: lineCoordinates, - }, - } - if (map.getSource("route")) { - map.removeLayer("route") - map.removeSource("route") - } - map.addSource("route", { - type: "geojson", - data: line, - }) - map.addLayer({ - id: "route", - type: "line", - source: "route", - layout: { - "line-join": "round", - "line-cap": "round", - }, - paint: { - "line-color": "#5cd5eb", - "line-width": 4, - }, - }) - - // Set padding to 20 on phones and 100 on desktop - const padding = window.innerWidth < 600 ? 20 : 100 - map.fitBounds( - [ - [startingPoint[0], startingPoint[1]], - [destinationCoordinates[0], destinationCoordinates[1]], - ], - { - padding: padding, - duration: 1000, - } - ) -} - -/** - * If there is any route currenty on the map, remove it - * @param {unknown} map The mapboxgl.Map instance - * @param {number[]} centeringPosition The position to center the map to after removing the route, in the format [longitude, latitude] - */ -export function removeRouteFromMap(map, centeringPosition) { - if (map.getSource("route")) { - map.removeLayer("route") - map.removeSource("route") - } - // Fly to the last known position - map.flyTo({ - center: centeringPosition, - zoom: 15, - speed: 3, - pitch: 45, - }) -} - - -/** - * Formats seconds to human readable time - * @param {number} seconds - * @returns {string} - */ -export function formatSecondsToHuman(seconds) { - const hours = Math.floor(seconds / 3600) - const minutes = Math.floor((seconds % 3600) / 60) - return hours > 0 ? `${hours}h ${minutes} min` : `${minutes} min` -} - - -/** - * Formats meters to human readable distance - * @param {number} meters - * @returns {string} e.g. "1.2 km" or "500 m" - */ -export function formatMetersToHuman(meters) { - if (meters > 1000) { - return `${(meters / 1000).toFixed(1)} km` - } - return `${meters} m` -} \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/assets/components/overview.js b/selfdrive/frogpilot/thepond/assets/components/overview.js deleted file mode 100644 index 2aa399bac128c0..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/overview.js +++ /dev/null @@ -1,176 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { Link } from "./router.js" -import { SettingsView } from "./settings/settings.js" - -/** - * Renders the Overview component that loads when you load - * thepond. Contains one section with the number of rides, - * duration and distance for all time, current week and while - * using frogpilot. - * Then there is a section with the disk usage - * Then there are shortcuts to settings search and nav input - * - * @returns - */ -export function Overview() { - const state = reactive({ - data: "{}", - search: "", - }) - - async function fetchData() { - const response = await fetch("/api/stats") - const data = await response.json() - state.data = JSON.stringify(data) - if (data.diskError) { - showSnackbar(data.diskError.join("
"), "error", 6000) - } - if (data.driveErrors) { - showSnackbar(data.driveErrors.join("
"), "error", 6000) - } - } - - function Search(e) { - state.search = e.target.value - } - - fetchData() - - console.log("rendering overview") - - return html` -
- ${() => { - return html`${() => { - if (state.search) { - return "" - } - return html` -

ThePond

-
- ${() => - DriveStat( - "All Time", - JSON.parse(state.data)?.driveStats?.all ?? {} - )} - ${() => - DriveStat( - "Past Week", - JSON.parse(state.data)?.driveStats?.week ?? {} - )} - ${() => - DriveStat( - "FrogPilot", - JSON.parse(state.data)?.driveStats?.frogpilot ?? {} - )} -
-
-

Disk Usage

- ${() => { - const data = JSON.parse(state.data) - if (data.diskError) { - return html`

${data.diskError.join("
")}

` - } - return data?.diskUsage?.map((disk) => - DiskUsage(disk)) - }} -
- ` - }} -
- ${() => { - return !state.search ? html`

Shortcuts

` : "" - }} - - ${() => { - return !state.search - ? html`${Link( - "/navigation", - html` - - ` - )}` - : "" - }} - ${() => { - if (state.search) { - return SettingsView({ - params: { section: "controls" }, - searchQuery: state.search, - }) - } - }} -
` - }} -
- ` -} - -/** - * - * @param {string} title - * @param {Object} stats - * @param {number} stats.drives - * @param {number} stats.distance - * @param {number} stats.hours - * @returns - */ -function DriveStat(title, stats) { - const formatNumber = (num) => { - return num.toLocaleString('en-US', {minimumFractionDigits: 0, maximumFractionDigits: 0}); - }; - - return html` -
-

${title}

-
-

${formatNumber(stats.drives || 0)}

-

drives

-
-
-

${formatNumber(stats.distance || 0)}

-

${stats.unit}

-
-
-

${formatNumber(stats.hours || 0)}

-

hours

-
-
- `; -} - -/** - * - * @param {Object} disk - * @param {string} disk.mount - * @param {string} disk.used - * @param {string} disk.available - * @param {string} disk.usedPercentage - * @param {string} disk.size - */ -function DiskUsage(disk) { - return html` -
-

${disk.mount}

-

${disk.used} used of ${disk.size}

-
-
-
-
- ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/playground.js b/selfdrive/frogpilot/thepond/assets/components/playground.js deleted file mode 100644 index a40da4904b4aba..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/playground.js +++ /dev/null @@ -1,41 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" - -function Testcomponent() { - let state = reactive({ - count: 0, - foo: false - }) - - //random 1-10 - const random = Math.floor(Math.random() * 10) + 1 - - async function fetchSomething() { - //timeout - await new Promise((resolve) => setTimeout(() => { - state.count++ - state.foo = true - resolve() - }, 2000)) - } - - fetchSomething() - - console.log("Random number", random) - return html` -
-

${() => state.count}

- -
- ` -} - - -html` -
-

Playground

-

Playground for testing out new features

- ${() => Testcomponent()} -
-`(document.getElementById("app")) - - diff --git a/selfdrive/frogpilot/thepond/assets/components/recordings/route.js b/selfdrive/frogpilot/thepond/assets/components/recordings/route.js deleted file mode 100644 index af91cc1753e33b..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/recordings/route.js +++ /dev/null @@ -1,231 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { Link } from "../router.js" - -/** - * @typedef {Object} Route - * @prop {string} date - The date of the route - * @prop {string[]} segment_urls - The URLs for the segments of the route - * @prop {string} gif - The URL for the GIF preview - * @prop {string} png - The URL for the PNG preview - * @prop {string[]} available_cameras - The available cameras for the route - * @prop {number} total_duration - The total duration of the route in seconds - * @prop {string} name - The name of the route - */ - -export function RecordedRoute({ params }) { - let state = reactive({ - route: undefined, - playing: true, - currentIndex: 0, - isSeeking: false, - currentTime: "", - selectedCamera: "forward", - }) - - async function fetchRoute() { - let response = await fetch(`/api/routes/${params.routeDate}`) - let data = await response.json() - state.route = data - } - fetchRoute() - - function playPauseHandler() { - console.log("Play/pause") - const videoElement = document.getElementById("video") - if (videoElement.paused) { - videoElement.play() - } else { - videoElement.pause() - } - state.playing = !state.playing - } - - function fullscreenHandler() { - const videoElement = document.getElementById("video") - if (videoElement.requestFullscreen) { - videoElement.requestFullscreen() - } else if (videoElement.mozRequestFullScreen) { - videoElement.mozRequestFullScreen() - } else if (videoElement.webkitRequestFullscreen) { - videoElement.webkitRequestFullscreen() - } else if (videoElement.msRequestFullscreen) { - videoElement.msRequestFullscreen() - } - } - - function videoEndedHandler(e) { - const videoElement = e.target - state.currentIndex++ - if (state.currentIndex >= state.route.segment_urls.length) { - state.currentIndex = 0 - } - videoElement.src = state.route.segment_urls[state.currentIndex] - videoElement.load() - videoElement.play() - } - - function timeupdateHandler(e) { - const videoElement = e.target - if (state.isSeeking) { - return - } - const currentTime = Math.round(videoElement.currentTime) - const actualTime = state.currentIndex * 60 + currentTime - state.currentTime = actualTime - } - - async function handleSeek(e) { - const value = e.target.value - const videoElement = document.getElementById("video") - // Find which segment the slider is in (each segment is 60 seconds) - const desiredIndex = Math.floor(value / 60) - if (desiredIndex != state.currentIndex) { - console.log("Switching to segment: ", desiredIndex) - state.currentIndex = desiredIndex - videoElement.src = getUrlForIndex( - state.route, - state.selectedCamera, - state.currentIndex - ) - videoElement.load() - videoElement.play() - // Really weird behaviour in FF, when the video is just - // loaded the duration is ~1, so seeking to 30 causes - // the video to end. So we wait for the duration to be more than 2 - for (let i = 0; i < 10; i++) { - await wait(100) - if (videoElement.duration > 2) { - break - } - } - } - // Seek to the correct time in the video based on the slider value - const correctTime = value - state.currentIndex * 60 - videoElement.currentTime = correctTime - } - - function renderRoute(route) { - const isWideAvailable = route.available_cameras.includes("wide") - const isDriverAvailable = route.available_cameras.includes("driver") - const formattedDate = new Date(route.date).toLocaleString() - const formattedDuration = formatSeconds(route.total_duration) - - return html` -

${formattedDate}

-
-
-

Forward Camera

-
-
-

Wide Camera

-
-
-

Driver Camera

-
-
-
- -
- - -

- ${() => formatSeconds(state.currentTime)}/ - ${formattedDuration} -

- -
-
- ` - } - - return html` -
- ${Link("/routes", "Back", undefined, "button")} - ${() => (state.route ? renderRoute(state.route) : "Loading...")} -
- ` -} - -/** - * Gets the URL for the given segment - * @param {Route} route - * @param {"forward"|"wide"|"driver"} selectedCamera - * @param {number} segmentIndex - * @returns {string} - The URL for the given segment and camera - */ -function getUrlForIndex(route, selectedCamera, segmentIndex) { - let url = route.segment_urls[segmentIndex] - - if (selectedCamera === "driver") { - url += "?camera=driver" - } else if (selectedCamera === "wide") { - url += "?camera=wide" - } - return url -} - -/** - * Formats number of seconds to a human readable format, - * with optional hours, minutes and seconds. - * @param {number} seconds - * @returns {string} - The formatted string, e.g. 1:23:45 - */ -function formatSeconds(seconds) { - let hours = Math.floor(seconds / 3600) - let minutes = Math.floor((seconds - hours * 3600) / 60) - let remainingSeconds = seconds - hours * 3600 - minutes * 60 - let formatted = "" - if (hours > 0) { - formatted += hours + ":" - } - if (minutes < 10) { - formatted += "0" - } - formatted += minutes + ":" - if (remainingSeconds < 10) { - formatted += "0" - } - formatted += remainingSeconds - return formatted -} - -async function wait(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms) - }) -} diff --git a/selfdrive/frogpilot/thepond/assets/components/recordings/routes.js b/selfdrive/frogpilot/thepond/assets/components/recordings/routes.js deleted file mode 100644 index 6ed4b3ad1537cc..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/recordings/routes.js +++ /dev/null @@ -1,44 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { Link } from "../router.js" - -export function RecordedRoutes() { - let state = reactive({ - routes: "[]", - }) - - async function fetchRoutes() { - let response = await fetch("/api/routes") - let routes = await response.json() - state.routes = JSON.stringify(routes) - } - fetchRoutes() - - return html` -

Dashcam Routes

-

View & download recorded routes.

-
- ${() => { - const routes = JSON.parse(state.routes) - if (routes.length === 0) { - fetchRoutes() - return html`
Loading...
` - } - return routes.map((route) => { - const formattedDate = new Date(route.date).toLocaleString() - return html` ${Link( - `/routes/${route.name}`, - html` -
- - -
-

${formattedDate}

- `, - undefined, - "route_card" - )}` - }) - }} -
- ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/router.js b/selfdrive/frogpilot/thepond/assets/components/router.js deleted file mode 100644 index 7e8836d037fc83..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/router.js +++ /dev/null @@ -1,107 +0,0 @@ -import { - createBrowserHistory, - createRouter, -} from "https://esm.sh/@remix-run/router@1.3.1" -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { Sidebar } from "./sidebar.js" -import { Overview } from "./overview.js" -import { SettingsView } from "./settings/settings.js" -import { NavDestination } from "./navigation/nav-destination.js" -import { ErrorLogs } from "./error-logs.js" -import { RecordedRoutes } from "./recordings/routes.js" -import { RecordedRoute } from "./recordings/route.js" -import { hideSidebar } from "../js/utils.js" - -let router, routerState - -function createRoute(id, path, component) { - return { - id, - path, - loader: () => {}, - element: component, - } -} - -function Root() { - let routes = [ - createRoute("root", "/", Overview), - createRoute("routes", "/routes", RecordedRoutes), - createRoute("route", "/routes/:routeDate", RecordedRoute), - createRoute("settings", "/settings/:section/:subsection?", SettingsView), - createRoute("navdestination", "/navigation", NavDestination), - createRoute("errorLogs", "/error-logs", ErrorLogs), - ] - - router = createRouter({ - routes, - history: createBrowserHistory(), - }).initialize() - - routerState = reactive({ - //state: router.state, - activePath: "/", - activePathFull: "/", - initialized: false, - navigation: { - state: "loading", - }, - errors: [], - params: {}, - }) - - router.subscribe((state) => { - console.log("Router state updated", state) - routerState.initialized = state.initialized - routerState.activePath = state.matches[0].route.path - routerState.activePathFull = state.matches[0].pathname - routerState.navigation.state = state.navigation.state - routerState.params = state.matches[0].params - routerState.errors = state.errors - //routerState.state = state - }) - - return html` ${() => Sidebar(routerState.activePathFull)} -
- ${() => { - if ( - routerState.initialized === false || - routerState.navigation.state === "loading" - ) { - return html`
Loading...
` - } else if (routerState?.errors?.root?.status === 404) { - return html`

Not Found

` - } else { - console.log("re-render", JSON.stringify(routerState)) - const element = routes.find( - (route) => route.path === routerState.activePath - ) - return element.element({ params: routerState.params }) - } - }} -
` -} - -export function Link(href, children, onClick, classes = "") { - return html`${children}` -} - -export function Navigate(href) { - router.navigate(href) - // since we change the page, scroll to the top of the page - window.scrollTo(0, 0) -} - -Root()(document.getElementById("app")) diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/input-setting.js b/selfdrive/frogpilot/thepond/assets/components/settings/input-setting.js deleted file mode 100644 index 15b48190a14b57..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/input-setting.js +++ /dev/null @@ -1,51 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { saveSetting } from "./settings-queries.js" -import { toggleCollapsed } from "./settings.js" - -/** - * An input setting for arbitrary text input. Will be rendered - * as a text field (which will be password if the key contains "key") - * and a save button next to it. - * @param {Setting} setting - * @param {boolean} isSubsetting - */ -export function InputSetting(setting, isSubsetting = false) { - const state = reactive({ - value: setting.value, - loading: false, - }) - - const inputClass = setting.key.toLowerCase().includes("key") ? "password" : "" - - async function saveValue() { - state.loading = true - await saveSetting(setting.key, state.value) - state.loading = false - } - - return html` -
-

${setting.title}

- - - -
- ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/number-setting.js b/selfdrive/frogpilot/thepond/assets/components/settings/number-setting.js deleted file mode 100644 index f850c9af873d95..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/number-setting.js +++ /dev/null @@ -1,49 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { saveSetting } from "./settings-queries.js" -import { toggleCollapsed } from "./settings.js" - -/** - * An input setting for arbitrary text input. Will be rendered - * as a text field (which will be password if the key contains "key") - * and a save button next to it. - * @param {Setting} setting - * @param {boolean} isSubToggle - */ -export function NumberSetting(setting, isSubToggle = false) { - const state = reactive({ - value: setting.value, - loading: false, - }) - - let timeout = undefined - - async function saveValue(newValue) { - state.value = newValue - // Debounce the save to avoid saving on every key press - clearTimeout(timeout) - timeout = setTimeout(async () => { - state.loading = true - await saveSetting(setting.key, state.value) - state.loading = false - }, 500) - } - - return html` -
-

${setting.title}

- -
- -

${() => Math.round(state.value)} ${setting.unit}

- -
- -
- ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/select-setting.js b/selfdrive/frogpilot/thepond/assets/components/settings/select-setting.js deleted file mode 100644 index c5be008c9a048c..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/select-setting.js +++ /dev/null @@ -1,79 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { saveSetting } from "./settings-queries.js" -import { toggleCollapsed } from "./settings.js" - -/** - * Component for multi-choice options with up to 3 options. - * Will be rendered as selectable radio buttons. - * @param {Setting} setting - * @param {boolean} isSubsetting - */ -export function SelectSetting(setting, isSubsetting = false) { - const state = reactive({ - value: setting.value, - loading: false, - selectedOption: setting.options?.[setting.value] ?? "", - }) - - async function saveValue(option) { - state.selectedOption = option - const value = setting.options.indexOf(option) - state.value = value - state.loading = true - await saveSetting(setting.key, value) - state.loading = false - } - - function createRadioButtons(options) { - return html`
- ${options.map( - (option) => html` - - - ` - )} -
` - } - - function createDropdown(options) { - const name = `select - ${setting.key}` - if (options === undefined || options.length === 0) { - return html`

Options not found

` - } - return html` ` - } - - return html` -
-

${setting.title}

- - ${setting.type === "select" - ? createDropdown(setting.options) - : createRadioButtons(setting.options)} -
- ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/settings-queries.js b/selfdrive/frogpilot/thepond/assets/components/settings/settings-queries.js deleted file mode 100644 index 978ee6a19110eb..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/settings-queries.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @typedef Setting - * @prop {string} setting.key - The key of the setting (the filename on device) - * @prop {string} setting.section - The settings section (replicating the device settings) - * @prop {string} setting.title - Nice title for the setting - * @prop {string | number} setting.value - The actual value. This might be a string or an int - * @prop {"toggle"|"text"|"dropdown"|"select"} type - The type of the setting. Determines the rendering - * @prop {string[]} [setting.options] - An optional list of available options to choose from. - * @prop {Setting[]} subsettings - A list of settings that should be displayed as submenus. - * @prop {Setting[]} toggles - An optional list of nested settings. - */ - -/** - * Getter for all settings - * @returns { Promise } All available settings - */ -export async function getSettings(ignoreCache = false) { - if (!ignoreCache) { - const cachedValue = localStorage.getItem("settings") - if (cachedValue) { - const cache = JSON.parse(cachedValue) - if (new Date().getTime() - cache.time < 1000 * 60) { - return cache.value - } - } - } - - const response = await fetch(`/api/settings`, { - headers: { - Accept: "application/json", - }, - }) - if (response.ok) { - const data = await response.json() - const settings = data - - const cache = { - value: settings, - time: new Date().getTime(), - } - - console.log("Updating settings cache") - localStorage.setItem("settings", JSON.stringify(cache)) - return settings - } -} - -/** - * Setter for a specific settings key - * @param {string} key - * @param {string} value - * @returns {Promise} A promise that resolves to true or false if the saving succeeded - */ -export async function saveSetting(key, value) { - const response = await fetch(`/api/settings/${key}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ value }), - }) - if (!response.ok) { - const data = await response.json() - console.log(`Failed to save setting for ${key}`) - console.log(`Error message: ${data.message}`) - showSnackbar(`Failed to save setting ${key}`) - return false - } - //showSnackbar(`Saved ${key}`) - await getSettings(true) - return true -} diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/settings.js b/selfdrive/frogpilot/thepond/assets/components/settings/settings.js deleted file mode 100644 index 7f6da3aaf1ed9d..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/settings.js +++ /dev/null @@ -1,165 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import FuzzySearch from "https://esm.sh/fuzzy-search" -import { getSettings } from "./settings-queries.js" -import { upperFirst } from "../../js/utils.js" -import { InputSetting } from "./input-setting.js" -import { SelectSetting } from "./select-setting.js" -import { ToggleSetting } from "./toggle-setting.js" -import { NumberSetting } from "./number-setting.js" -import { Link, Navigate } from "../router.js" - -export function SettingsView({ params, searchQuery }) { - // Random number between 0 and 100 - const random = Math.floor(Math.random() * 100) - console.log("rendering settings 1 ", random) - - const state = reactive({ - selectedSection: params.section, - selectedSubsection: params.subsection, - settings: "[]", - loading: true, - heading: params.section, - allSettings: "[]", - isSearching: false, - }) - - getSettings().then((results) => { - state.loading = false - // If there is a subsection, try to find it, otherwise redirect to the first parent - if (state.selectedSubsection) { - const setting = - results.find((item) => item.key === state.selectedSubsection) ?? {} - if (Object.keys(setting.subsettings).length === 0) { - console.log( - `No subsettings found for ${state.selectedSubsection}, redirecting to parent` - ) - return Navigate(`/settings/${state.selectedSection}`) - } - state.heading = setting.key - state.settings = JSON.stringify([ - { - ...setting, - description: - setting.description + - "

Disabling this will make settings below irrelevant", - subsettings: undefined, - expanded: true, - }, - ...Object.entries(setting.subsettings).map(([key, value]) => { - return { key, section: state.selectedSection, ...value } - }), - ]) - return - } - console.log("Updating settings state", random) - state.settings = JSON.stringify(results) - state.allSettings = state.settings - - if (searchQuery !== undefined) { - SearchSettings(searchQuery) - } - }) - - function SearchSettings(query) { - if (JSON.parse(state.settings).length === 0) { - return - } - if (query === "") { - state.settings = state.allSettings - state.isSearching = false - return - } - console.log("Performing search", random) - state.isSearching = true - - const search = query.toLowerCase() - const settings = JSON.parse(state.allSettings) - - const searcher = new FuzzySearch(settings, ["title", "subsettings.title"], { - caseSensitive: false, - }) - const matchingSettings = searcher.search(search) - // Check if the match was found in a subsetting, if so move that to the toggles object instaed - - const result = matchingSettings.map((setting) => { - if (setting.subsettings) { - const searcher = new FuzzySearch(setting.subsettings, ["title"], { - caseSensitive: false, - }) - const matchingSubsettings = searcher.search(search) - if (matchingSubsettings.length > 0) { - setting.toggles = matchingSubsettings - setting.subsettings = undefined - } - } - return setting - }) - console.log("Resulting settings", result, random) - state.settings = JSON.stringify(result) - } - - console.log("rendering settings ", random) - - return html` -
- ${() => - state.selectedSubsection - ? Link( - `/settings/${state.selectedSection}`, - "Back", - undefined, - "button" - ) - : searchQuery === undefined - ? html` - - ` - : ""} -

- ${() => (state.isSearching ? "Matching" : upperFirst(state.heading))} - settings -

- ${() => { - const d = JSON.parse(state.settings).filter( - (el) => el.section === state.selectedSection || state.isSearching - ) - console.log("Rendering settings list", d, random) - return html`
${SettingsList(d)}
` - }} -
- ` -} - -function SettingsList(settings) { - return settings.map((setting) => { - switch (setting.type) { - case "toggle": - return ToggleSetting(setting).key(setting.key) - case "text": - return InputSetting(setting).key(setting.key) - case "select": - case "radio": - return SelectSetting(setting).key(setting.key) - case "number": - return NumberSetting(setting).key(setting.key) - } - }) -} - -/** - * Minor helper function to toggle the hidden class - * on the description field when clicking the title - * @param {Event} e - */ -export function toggleCollapsed(e) { - const description = - e.target.parentNode.getElementsByClassName("description")[0] - description.classList.toggle("collapsed") -} diff --git a/selfdrive/frogpilot/thepond/assets/components/settings/toggle-setting.js b/selfdrive/frogpilot/thepond/assets/components/settings/toggle-setting.js deleted file mode 100644 index 54e17e16d81db8..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/settings/toggle-setting.js +++ /dev/null @@ -1,90 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { saveSetting } from "./settings-queries.js" -import { toggleCollapsed } from "./settings.js" -import { InputSetting } from "./input-setting.js" -import { SelectSetting } from "./select-setting.js" -import { NumberSetting } from "./number-setting.js" -import { Navigate } from "../router.js" - -/** - * A ToggleSetting component. This is for settings that can just be - * set to "on" or "off", so it renders a toggle, or switch - * @param {Setting} setting - * @param {boolean} isSubToggle - */ -export function ToggleSetting(setting, isSubToggle = false) { - const state = reactive({ - enabled: setting.value === 1 ? true : false, - loading: false, - }) - - async function toggled() { - const newState = !state.enabled - state.enabled = newState - state.loading = true - const result = await saveSetting(setting.key, newState ? 1 : 0) - if (!result) { - state.enabled = !newState - } - state.loading = false - } - - function Toggle() { - return html` ` - } - - function SubMenu() { - return html`` - } - - const hasSubsettings = Object.keys(setting.subsettings ?? {}).length > 0 - function gotoSubsettings() { - if (!hasSubsettings) return - console.log("Goto subsettings", setting.key) - Navigate(`/settings/${setting.section}/${setting.key}`) - } - - // Setting expanded to true forces the description to expand by default, - // and settings with subsettings acts as links so they should show description as well - const shouldBeCollapsed = !hasSubsettings && !setting.expanded - return html` -
-

${setting.title}

-
- ${setting.description} -
- ${hasSubsettings ? SubMenu() : Toggle()} -
- ${() => - state.enabled && - Object.values(setting.toggles ?? {})?.map((sub) => { - switch (sub.type) { - case "toggle": - return ToggleSetting(sub, true).key(sub.key) - case "text": - return InputSetting(sub, true).key(sub.key) - case "select": - return SelectSetting(sub, true).key(sub.key) - case "number": - return NumberSetting(sub, true).key(sub.key) - } - })} - ` -} diff --git a/selfdrive/frogpilot/thepond/assets/components/sidebar.js b/selfdrive/frogpilot/thepond/assets/components/sidebar.js deleted file mode 100644 index 52693f3ec20287..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/sidebar.js +++ /dev/null @@ -1,175 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" -import { Link } from "./router.js" -import { upperFirst } from "../js/utils.js" -import { hideSidebar, showSidebar } from "../js/utils.js" -import { ThemeToggle } from "./theme-toggle.js" - -const MenuItems = { - recordings: [ - { - name: "Routes", - link: "/routes", - icon: "bi-camera-reels", - }, - { - name: "Saved routes", - link: "/saved_routes", - icon: "bi-box2-heart", - unimplemented: true, - }, - { - name: "Screen recordings", - link: "/screen_recordings", - icon: "bi-record-circle", - unimplemented: true, - }, - ], - settings: [ - { - name: "Device", - link: "/settings/device", - icon: "bi-phone", - unimplemented: true, - }, - { - name: "Toggles", - link: "/settings/toggles", - icon: "bi-sliders", - }, - { - name: "Software", - link: "/settings/software", - icon: "bi-code-slash", - unimplemented: true, - }, - { - name: "Controls", - link: "/settings/controls", - icon: "bi-gear-wide-connected", - }, - { - name: "Navigation", - link: "/settings/navigation", - icon: "bi-sign-turn-left", - }, - { - name: "Vehicles", - link: "/settings/vehicles", - icon: "bi-car-front", - }, - { - name: "Visuals", - link: "/settings/visuals", - icon: "bi-view-stacked", - unimplemented: true, - }, - { - name: "Error logs", - link: "/error-logs", - icon: "bi-exclamation-triangle", - }, - ], - navigation: [ - { - name: "Set destination", - link: "/navigation", - icon: "bi-globe-americas", - }, - ], -} - -export function Sidebar(activePath) { - const activeRoute = Object.values(MenuItems) - .flat() - .find((item) => item.link === activePath)?.name - - const state = reactive({ - activeRoute: activeRoute ?? "", - }) - - function navigate(link) { - state.activeRoute = link.name - // since we change the page, scroll to the top of the page - window.scrollTo(0, 0) - // Since we navigated, hide the sidebar - hideSidebar() - } - - return html` - - ` -} - -/** - * Helper function run on startup that hooks up the menu button - */ -function setupMenuButton() { - const sidebarUnderlay = document.getElementById("sidebarUnderlay") - const sidebar = document.getElementById("sidebar") - - const button = document.getElementById("menu_button") - button.addEventListener("click", () => { - const isVisible = sidebar.classList.contains("visible") - if (isVisible) { - hideSidebar() - } else { - showSidebar() - } - }) - sidebarUnderlay.addEventListener("click", hideSidebar) -} - -document.addEventListener("DOMContentLoaded", setupMenuButton, false) diff --git a/selfdrive/frogpilot/thepond/assets/components/theme-toggle.js b/selfdrive/frogpilot/thepond/assets/components/theme-toggle.js deleted file mode 100644 index b56d4baacae888..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/components/theme-toggle.js +++ /dev/null @@ -1,58 +0,0 @@ -import { html, reactive } from "https://esm.sh/@arrow-js/core" - -export function ThemeToggle() { - const state = reactive({ - theme: calculateSettingAsThemeString(), - }) - - setTheme(state.theme) - - function setTheme(theme) { - console.log( - `Setting theme to ${theme ?? state.theme === "dark" ? "light" : "dark"}` - ) - state.theme = theme ?? (state.theme === "dark" ? "light" : "dark") - - // update theme attribute on HTML to switch theme in CSS - document.querySelector("html").setAttribute("data-theme", state.theme) - const color = getComputedStyle(document.documentElement).getPropertyValue( - "--main-bg" - ) // #999999 - document - .querySelector('meta[name="theme-color"]') - .setAttribute("content", color) - - // update in local storage - localStorage.setItem("theme", state.theme) - } - - return html` -
- -
- ` -} - -/** - * @returns {"dark"|"light"} The theme setting as a string - */ -function calculateSettingAsThemeString() { - const localStorageTheme = localStorage.getItem("theme") - if (localStorageTheme !== null) { - return localStorageTheme - } - - const systemSettingDark = window.matchMedia("(prefers-color-scheme: dark)") - return systemSettingDark.matches ? "dark" : "light" -} diff --git a/selfdrive/frogpilot/thepond/assets/css/error-logs.css b/selfdrive/frogpilot/thepond/assets/css/error-logs.css deleted file mode 100644 index 50e73c210331b9..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/error-logs.css +++ /dev/null @@ -1,88 +0,0 @@ -#errorLogs p { - margin: 0; - padding: 0; -} - -#errorLogs > div { - display: flex; - flex-direction: column; - overflow: hidden; -} - -#errorLogs #fileList { - max-width: min(95%, 900px); - min-width: 500px; - max-height: 200px; - overflow-y: auto; - background-color: var(--sidebar-bg); - border: 5px solid var(--sidebar-border-color); - border-radius: 10px; - margin-bottom: 1rem; -} -@media only screen and (max-width: 768px) { - #errorLogs #fileList { - width: calc(100% - 2rem); - min-width: 0; - } -} - -#errorLogs .fileEntry { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - display: flex; - cursor: pointer; - border-bottom: 1px solid var(--sidebar-border-color); - justify-content: space-between; -} -#errorLogs .fileEntry:hover { - background-color: var(--sidebar-active-bg); -} - -#errorLogs #fileViewer { - background-color: var(--sidebar-bg); - border: 5px solid var(--sidebar-border-color); - border-radius: 5px; - max-width: min(95%, 860px); - overflow: auto; - max-height: calc(100vh - 200px - 70px); - padding: 20px; - border-radius: 10px; - scrollbar-color: #626365 #1c1d21; - scrollbar-width: thin; -} -@media only screen and (max-width: 768px) { - #errorLogs #fileViewer { - width: calc(100% - 2rem - 40px); - min-width: 0; - } -} - -#errorLogs #fileViewer > div { - display: flex; - justify-content: space-between; -} - -#errorLogs #fileViewer button { - border: none; - background-color: transparent; - color: var(--main-fg); - width: 35px; - height: 35px; - padding: 0.5rem; - border-radius: 50%; - cursor: pointer; - transition: background-color 0.3s ease; -} - -#errorLogs #fileViewer button:hover { - background-color: var(--sidebar-active-bg); -} - -#errorLogs #fileViewer pre { - white-space: pre-wrap; - word-wrap: break-word; - color: var(--main-fg); - font-family: "Courier New", Courier, monospace; -} diff --git a/selfdrive/frogpilot/thepond/assets/css/main.css b/selfdrive/frogpilot/thepond/assets/css/main.css deleted file mode 100644 index 4b382f1e41ce9a..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/main.css +++ /dev/null @@ -1,196 +0,0 @@ -.open-sans-regular { - font-family: "Open Sans", sans-serif; - font-optical-sizing: auto; - font-weight: 600; - font-style: normal; - font-variation-settings: "wdth" 100; -} - -/* Reset CSS */ -html, -body { - min-height: 100dvh; -} -body { - padding-bottom: 2rem !important; - background-color: var(--main-bg); -} - -html, -body, -h1, -h2, -h3 { - padding: 0; - margin: 0; - font-family: "Open Sans", sans-serif; - overflow-x: hidden; -} - -h1, -h2, -h3 { - font-weight: 600; - padding-top: 1rem; - padding-bottom: 10px; -} - -/* Change the default styles to make them easier to work with */ -ul { - list-style-type: none; - margin: 0; - padding: 0; -} - -hr { - height: 1px; - background-color: var(--sidebar-border-color); - border: none; -} - -ul, -li, -a { - display: block; -} -/* End of default styles */ - -[data-theme="light"] { - --main-bg: #fff; - --main-fg: #000; - --secondary-bg: #868484; - --sidebar-bg: #f4f4f4; - --sidebar-fg: rgb(25, 52, 70); - --sidebar-border-color: rgb(234, 234, 234); - --sidebar-active-bg: #4d4c4c21; - --card-bg: #f4f4f4; - --track-color: #4d4c4c; - --thumb-color: #4d4c4c; - --selected-camera-bg: rgb(215, 215, 215); - --switch-inactive-bg: #645f5f; -} - -[data-theme="dark"] { - --main-bg: #151414; - --main-fg: #dfdfdf; - --secondary-bg: #292121; - --sidebar-bg: #292121; - --sidebar-fg: rgb(255, 238, 238); - --sidebar-border-color: rgb(50, 50, 50); - --sidebar-active-bg: #c9c9c921; - --card-bg: #292121; - --track-color: #053a5f; - --thumb-color: #5cd5eb; - --selected-camera-bg: #343333; - --switch-inactive-bg: #514d4d; -} - -.content { - padding-left: 1.5rem; - flex-grow: 1; - color: var(--main-fg); - overflow-y: auto; - margin-left: 250px; -} -@media only screen and (max-width: 768px) { - .content { - margin-left: 0; - } -} - -.no_scroll { - overflow: hidden; -} -.hidden { - display: none; -} - -.not_implemented { - text-decoration: line-through; - cursor: not-allowed; -} - -/* The snackbar - position it at the bottom and in the middle of the screen */ -#snackbar_wrapper { - position: fixed; - bottom: 30px; /* 30px from the bottom */ - z-index: 10; /* Add a z-index if needed */ - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - width: min(300px, 70%); -} -.snackbar { - margin-bottom: 1rem; - visibility: hidden; /* Hidden by default. Visible on click */ - background-color: var(--sidebar-bg); /* Black background color */ - color: var(--main-fg); /* White text color */ - text-align: center; /* Centered text */ - border-radius: 5px; /* Rounded borders */ - padding: 16px; /* Padding */ - transition: 1s; -} - -/* Show the snackbar when clicking on a button (class added with JavaScript) */ -.snackbar.show { - visibility: visible; /* Show the snackbar */ - /* Add animation: Take 0.5 seconds to fade in and out the snackbar. - However, delay the fade out process for 2.5 seconds */ - -webkit-animation: fadein 0.5s /*, fadeout 0.5s 2.5s*/; - animation: fadein 0.5s /*, fadeout 0.5s 2.5s*/; -} - -/* Animations to fade the snackbar in and out */ -@-webkit-keyframes fadein { - from { - bottom: 0; - opacity: 0; - } - to { - bottom: 30px; - opacity: 1; - } -} - -@keyframes fadein { - from { - bottom: 0; - opacity: 0; - } - to { - bottom: 30px; - opacity: 1; - } -} - -@-webkit-keyframes fadeout { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -@keyframes fadeout { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -a.button { - display: inline-block; - padding: 0.5rem 1rem; - margin: 1rem 0; - background-color: var(--sidebar-active-bg); - color: var(--main-fg); - border: none; - border-radius: 5px; - cursor: pointer; - text-align: center; - text-decoration: none; -} diff --git a/selfdrive/frogpilot/thepond/assets/css/navigation.css b/selfdrive/frogpilot/thepond/assets/css/navigation.css deleted file mode 100644 index bb67677a526e01..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/navigation.css +++ /dev/null @@ -1,145 +0,0 @@ -.navigation { - height: 100%; - width: calc(100% - 1rem); - position: relative; -} -.map-wrapper { - min-width: 50%; - max-width: calc(100vw - 250px - 3rem); - height: 85vh; - position: relative; -} -#map { - height: 100%; - width: 100%; - position: absolute; - border-radius: 1rem; -} - -.search-wrapper { - display: flex; - flex-direction: column; - position: absolute; - top: 1rem; - left: 1rem; - width: 30%; - min-width: 350px; - z-index: 1; -} -@media only screen and (max-width: 768px) { - .search-wrapper { - min-width: 0; - width: calc(100% - 2rem); - } - .map-wrapper { - min-width: 50%; - max-width: calc(100vw - 3rem); - height: 85vh; - position: relative; - } -} - -#search-field { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - background-color: var(--main-bg); - border-radius: 1rem; - padding: 1rem; - border: 1px solid var(--sidebar-border-color); - color: var(--main-fg); - font-size: 13px; -} -#search-field:focus { - border: 1px solid var(--thumb-color); - outline-width: 0; - outline: none; -} - -.search-wrapper #infobox #searchSuggestions { - margin-top: 0.5rem; - border-radius: 1rem; - display: flex; - justify-content: center; - align-items: start; - flex-direction: column; - background-color: var(--main-bg); - color: var(--main-fg); -} -.search-wrapper #infobox #searchSuggestions span { - border: 1px solid var(--sidebar-border-color); - width: 100%; -} -.search-wrapper #infobox #searchSuggestions span:hover { - background-color: var(--thumb-color); - color: var(--main-bg); -} -.search-wrapper #infobox #searchSuggestions span:first-child { - border-top-left-radius: 1rem; - border-top-right-radius: 1rem; -} -.search-wrapper #infobox #searchSuggestions span:last-child { - border-bottom-left-radius: 1rem; - border-bottom-right-radius: 1rem; -} - -.search-wrapper #infobox #searchSuggestions p { - font-size: 13px; - padding: 1rem; - border-radius: 0.5rem; - cursor: pointer; - margin: 0; -} - -.search-wrapper #infobox #navigationSummary { - margin-top: 0.5rem; - border-radius: 1rem; - display: flex; - justify-content: center; - align-items: start; - flex-direction: column; - background-color: var(--main-bg); - color: var(--main-fg); -} -.search-wrapper #infobox #navigationSummary p { - margin-left: 1rem; - margin-top: 1rem; - margin-bottom: 0; -} -.search-wrapper #infobox #navigationSummary button { - display: inline-block; - outline: none; - cursor: pointer; - font-size: 14px; - line-height: 1; - border-radius: 500px; - transition-property: background-color, border-color, color, box-shadow, filter; - transition-duration: 0.3s; - border: 1px solid transparent; - letter-spacing: 2px; - white-space: normal; - font-weight: 700; - text-align: center; - justify-self: start; - padding: 10px; - margin-left: 1rem; - margin-top: 1rem; - margin-bottom: 1rem; - color: var(--main-fg); - background-color: var(--sidebar-bg); -} -.search-wrapper #infobox #navigationSummary button:hover { - background-color: var(--thumb-color); - color: var(--main-bg); -} -.search-wrapper #infobox #navigationSummary button.directions { - color: #fff; - background-color: #039226; -} -.search-wrapper #infobox #navigationSummary button.directions:hover { - background-color: #01be2d; -} -.search-wrapper #infobox #navigationSummary button.favorite { - color: #ef1313; -} diff --git a/selfdrive/frogpilot/thepond/assets/css/overview.css b/selfdrive/frogpilot/thepond/assets/css/overview.css deleted file mode 100644 index 5f55ce489c5b65..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/overview.css +++ /dev/null @@ -1,102 +0,0 @@ -.drivingStats { - background-color: var(--secondary-bg); - padding: 1rem; - border-radius: 5px; - max-width: 80%; - color: white; -} - -.drivingStat { - display: grid; - justify-content: space-between; - grid-template-columns: 1fr 1fr 1fr; - margin-bottom: 1rem; -} - -.drivingStat h2 { - grid-row: 1; - grid-column: span 3; - padding-top: 0; - padding-bottom: 0; - font-size: 1rem; -} -.drivingStat:last-of-type h2 { - color: #00a100; -} - -.drivingStat div { - grid-row: 2; -} -.drivingStat div p { - padding: 0; - margin: 0; -} -.drivingStat div p:first-of-type { - font-size: 1.5em; -} - -.diskUsage { - width: calc(80% + 2rem); -} - -.diskUsage h2, -.diskUsage h4 { - font-size: 1rem; - color: white; -} -.diskUsage p { - font-size: 0.8rem; - color: white; -} - -.diskUsage .disk { - background-color: var(--secondary-bg); - padding: 1rem; - border-radius: 5px; -} - -.diskUsage .disk h4, -.diskUsage .disk p { - padding: 0; - margin: 0; -} - -.diskUsage .disk .progress { - background-color: var(--main-bg); - height: 20px; - border-radius: 5px; - margin-top: 5px; -} - -.diskUsage .disk .bar { - background-color: #00a100; - height: 100%; - border-radius: 5px; -} - -.shortcuts { - width: calc(80% + 2rem); -} - -.shortcuts p { - margin: 0; - padding: 0; -} - -.shortcuts .searchBar, -.shortcuts .searchBar .searchfield { - width: calc(100% - 1rem); -} -.shortcuts .shortcutLink { - cursor: pointer; - margin-top: 1rem; - margin-bottom: 1rem; - padding: 1rem; - border: 1px solid #504e4e; - border-radius: 10px; - display: inline-flex; - width: calc(100% - 2rem); - justify-content: space-between; - text-decoration: none; - color: var(--main-fg); -} diff --git a/selfdrive/frogpilot/thepond/assets/css/routes.css b/selfdrive/frogpilot/thepond/assets/css/routes.css deleted file mode 100644 index 953f040798af53..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/routes.css +++ /dev/null @@ -1,154 +0,0 @@ -.route_grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - grid-gap: 40px 20px; - margin: 20px 0; -} -.route_grid .route_card { - width: 300px; - background-color: var(--card-bg); - border-radius: 5px; - border: 1px solid var(--sidebar-border-color); - cursor: pointer; -} - -.route_card { - text-decoration: none; - color: var(--main-fg); -} -.route_card p { - padding: 1rem; -} - -.route_grid .route_preview { - position: relative; - width: 300px; - /* Aspect ratio is 16:10 */ - height: calc(300px * 0.625); -} - -.route_grid .route_preview img { - position: absolute; - display: inline-block; - width: 300px; - /* Aspect ratio is 16:10 */ - height: calc(300px * 0.625); - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - -.route_grid .image_preview:hover { - opacity: 0; -} - -.route .camera_selector { - display: inline-flex; - margin: 1rem 0; - background-color: var(--sidebar-bg); - border-radius: 10px; - overflow: hidden; - max-width: calc(100% - 1.5rem); -} - -.route .camera_selector .selected_camera { - background-color: var(--selected-camera-bg); -} - -.route .camera_selector .unavailable { - cursor: not-allowed; - opacity: 0.5; - text-decoration: line-through; -} - -.route .camera_selector > div { - padding: 0 1rem; - cursor: pointer; -} - -.route .camera_selector > div:first-child { - border-right: 1px solid var(--sidebar-border-color); -} -.route .camera_selector > div:last-child { - border-left: 1px solid var(--sidebar-border-color); -} - -.route #duration { - opacity: 0.5; -} - -.route .video_wrapper { - width: min(526px, calc(100% - 1.5rem)); - background-color: var(--sidebar-bg); - border-radius: 10px 10px; -} - -.route video { - border-radius: 10px 10px 0 0; - width: min(526px, 100%); - /* Aspect ratio is 16:10 */ - height: calc(min(526px, calc(100% - 1.5rem)) * (10 / 16) - 1px); -} - -.route .videocontrols { - display: flex; - gap: 0.6rem; - /*padding-left: 0.5rem; - padding-right: 0.5rem;*/ - background-color: var(--sidebar-bg); - border-radius: 10px; -} -.route .videocontrols p { - margin: 0; -} -.route .videocontrols #seekslider { - flex-grow: 1; - -webkit-appearance: none; - appearance: none; - background: transparent; - cursor: pointer; -} - -.route .videocontrols #seekslider::-webkit-slider-runnable-track { - background: var(--track-color); - height: 0.25rem; - border-radius: 5px; -} - -/***** Track Styles *****/ -/***** Chrome, Safari, Opera, and Edge Chromium *****/ -/******** Firefox ********/ -.route .videocontrols #seekslider::-moz-range-track { - background: var(--track-color); - height: 0.25rem; - border-radius: 5px; -} - -/***** Thumb Styles *****/ -/***** Chrome, Safari, Opera, and Edge Chromium *****/ -.route .videocontrols #seekslider::-webkit-slider-thumb { - -webkit-appearance: none; /* Override default look */ - appearance: none; - margin-top: calc(0.25rem / 2 - 1rem / 2); /* Centers thumb on the track */ - border-radius: 1rem; - background-color: var(--thumb-color); - height: 1rem; - width: 1rem; -} - -/***** Thumb Styles *****/ -/***** Firefox *****/ -.route .videocontrols #seekslider::-moz-range-thumb { - border: none; /*Removes extra border that FF applies*/ - border-radius: 1rem; /*Removes default border-radius that FF applies*/ - background-color: var(--thumb-color); - height: 1rem; - width: 1rem; -} - -.route .videocontrols button { - background: none; - border: none; - cursor: pointer; - font-size: 1.5rem; - color: var(--main-fg); -} diff --git a/selfdrive/frogpilot/thepond/assets/css/settings.css b/selfdrive/frogpilot/thepond/assets/css/settings.css deleted file mode 100644 index a1d725b1609e10..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/settings.css +++ /dev/null @@ -1,390 +0,0 @@ -.settings { - max-width: 700px; -} -.settings h1 { - margin-bottom: 1rem; -} - -.setting { - margin-bottom: 1rem; - padding-right: 1.5rem; - display: grid; - align-items: center; - grid-template-columns: 4fr 4fr 1fr; - grid-template-areas: - "a b b" - "c c d"; -} -@media only screen and (max-width: 768px) { - .setting { - max-width: calc(100vw - 3rem); - margin-bottom: 0.5rem; - } -} - -.setting.subsetting_link { - cursor: pointer; -} - -.setting.subtoggle { - margin-left: 2rem; - margin-bottom: 0; -} -@media only screen and (max-width: 768px) { - .setting.subtoggle { - margin-left: 0; - } -} -.setting.subtoggle p { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - -/* Weird hack to "restore" the bottom margin - after subtoggles has been added, since the - subtoggles are on the same level as all other - settings -*/ -.setting.subtoggle + .setting { - margin-top: 1rem; -} - -/* subtoggle "L" beside title */ -.setting.subtoggle .title:before { - width: 12px; - height: 10px; - position: relative; - content: ""; - display: inline-block; - border-bottom: 2px solid; - border-left: 2px solid; - border-color: gray; - margin-right: 1rem; - margin-bottom: 6px; -} - -.setting .title { - grid-area: a; - font-size: 1.1rem; - margin-right: 1rem; -} -@media only screen and (max-width: 768px) { - .setting .title { - font-size: 0.9em; - } -} -.setting .title.wide { - grid-column: span 2; -} -.setting .options { - grid-area: b; - grid-column: span 2; - justify-self: end; - align-items: center; - width: 100%; -} -@media only screen and (max-width: 768px) { - .setting .description.collapsed { - display: none; - } -} - -.setting .description { - grid-area: c; - grid-column: span 3; - opacity: 0.5; - margin: 0; - padding-top: 0.3rem; - font-size: 0.8em; -} -.setting .description summary { - margin-bottom: 10px; -} - -.setting .textwrapper { - grid-column: span 2; -} -.settings .textinput { - padding: 6px 12px; - background: var(--sidebar-bg); - border: 1px solid rgb(60, 63, 68); - border-radius: 4px; - font-size: 13px; - color: var(--main-fg); - height: 46px; - appearance: none; - transition: border 0.15s ease 0s; -} -.setting .savebutton { - display: inline-block; - outline: none; - cursor: pointer; - font-size: 14px; - line-height: 1; - border-radius: 500px; - transition-property: background-color, border-color, color, box-shadow, filter; - transition-duration: 0.3s; - border: 1px solid transparent; - white-space: normal; - font-weight: bold; - text-align: center; - color: #fff; - background-color: #009d24; - justify-self: start; - padding: 10px; - margin-left: 1rem; - width: 65px; - justify-self: end; -} -.setting .savebutton.loading { - cursor: progress; -} - -.options { - display: flex; - gap: 1rem; - max-width: 336px; - position: relative; - user-select: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - z-index: 1; - font-size: 12px; - color: var(--main-fg); -} -.options > input { - display: none; -} - -.options label { - flex: 1; - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: pointer; - border-radius: 3rem; - background-color: var(--sidebar-bg); - padding: 6px 3px; - transition: color 250ms cubic-bezier(0, 0.95, 0.38, 0.98); -} - -.options > input:checked + label { - background-color: #009d24; - color: #fff; - font-weight: bold; -} - -/* The switch - the box around the slider */ -.switch { - position: relative; - display: inline-block; - width: 60px; - height: 34px; - grid-row: 1; - grid-column: 3; - justify-self: end; -} -i.switch { - /* For subtoggle chevrons, they should kind of line up with toggle - switches, but not exactly*/ - width: 34px; - align-self: end; -} - -/* Hide default HTML checkbox */ -.switch input { - opacity: 0; - width: 0; - height: 0; -} - -/* The slider */ -.slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--switch-inactive-bg); - -webkit-transition: 0.2s; - transition: 0.2s; -} - -.slider:before { - position: absolute; - content: ""; - height: 26px; - width: 26px; - left: 4px; - bottom: 4px; - background-color: var(--main-bg); - -webkit-transition: 0.2s; - transition: 0.2s; -} - -.slider.loading:before { - background-color: none !important; - border: 5px solid #fff; - border-bottom-color: #46439b; - box-sizing: border-box; - animation: rotation 1s linear infinite; -} - -@keyframes rotation { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -input:checked + .slider { - background-color: #33a400; -} - -input:focus + .slider { - box-shadow: 0 0 1px #33a400; -} - -input:checked + .slider:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); -} - -input:checked + .slider.loading:before { - animation: rotationChecked 1s linear infinite; -} -@keyframes rotationChecked { - 0% { - transform: translateX(26px) rotate(0deg); - } - 100% { - transform: translateX(26px) rotate(360deg); - } -} - -/* Rounded sliders */ -.slider.round { - border-radius: 34px; -} - -.slider.round:before { - border-radius: 50%; -} - -/** Dropdown select for strings */ -.dropdown { - position: relative; - grid-area: b; - justify-self: end; -} - -.dropdown > select { - appearance: none; - /* safari */ - -webkit-appearance: none; - /* other styles for aesthetics */ - width: 100%; - font-size: 1.15rem; - padding: 0.675em 2em 0.675em 1em; - background-color: var(--sidebar-bg); - border: 1px solid var(--sidebar-border-color); - border-radius: 0.25rem; - color: var(--main-fg); - cursor: pointer; -} - -.dropdown::before, -.dropdown::after { - --size: 0.3rem; - content: ""; - position: absolute; - right: 1rem; - pointer-events: none; -} - -.dropdown::before { - border-left: var(--size) solid transparent; - border-right: var(--size) solid transparent; - border-bottom: var(--size) solid var(--main-fg); - top: 40%; -} - -.dropdown::after { - border-left: var(--size) solid transparent; - border-right: var(--size) solid transparent; - border-top: var(--size) solid var(--main-fg); - top: 55%; -} - -/* Really weird solution to mask passwords, but it prevents FF from prompting to save - any time you navigate away from a page with api keys, if using type=password fields -*/ -@font-face { - font-family: "password"; - font-style: normal; - font-weight: 400; - src: url(https://jsbin-user-assets.s3.amazonaws.com/rafaelcastrocouto/password.ttf); -} - -input.password { - font-family: "password"; -} - -input.searchfield { - padding: 0.1rem 1rem; - margin-top: 1rem; - background: var(--sidebar-bg); - border: 1px solid rgb(60, 63, 68); - border-radius: 4px; - font-size: 13px; - color: var(--main-fg); - height: 46px; - appearance: none; - transition: border 0.15s ease 0s; - -moz-appearance: none; -} -.settings input.searchfield { - width: calc(100% - 5rem); -} - -input.searchfield:focus { - outline: none; - box-shadow: none; - border-color: #009d24; -} - -.numberInput { - display: flex; - border: 1px solid var(--sidebar-border-color); - border-radius: 5px; - justify-content: space-between; - width: 150px; - height: 50px; - align-items: center; - grid-row: 1; - grid-column: 3; - white-space: nowrap; -} - -.numberInput button { - width: 40px; - height: 100%; - background: none; - color: inherit; - border: none; - padding: 0; - font: inherit; - cursor: pointer; - outline: inherit; -} -.numberInput button:active:not([disabled]) { - background-color: #009d24; -} - -.numberInput button[disabled] { - color: grey; -} diff --git a/selfdrive/frogpilot/thepond/assets/css/sidebar.css b/selfdrive/frogpilot/thepond/assets/css/sidebar.css deleted file mode 100644 index 1666ca0bb12314..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/css/sidebar.css +++ /dev/null @@ -1,166 +0,0 @@ -@media only screen and (max-width: 768px) { - #sidebarUnderlay { - position: fixed; - width: 100vw; - height: 100dvh; - background-color: rgba(0, 0, 0, 0.8); - top: 0; - left: 0; - z-index: 5; - transition: 0.5s; - } -} - -.sidebar { - position: fixed; - top: 0; - left: 0; - display: flex; - flex-direction: column; - /* To get toggles to the bottom of side-bar*/ - justify-content: space-between; - height: 100dvh; - width: 250px; - min-width: 250px; - background-color: var(--sidebar-bg); - border-right: 1px solid var(--sidebar-border-color); - font-size: 1rem; - z-index: 6; -} - -.sidebar li { - color: var(--sidebar-fg); -} - -.sidebar.visible { - left: 0; -} - -.sidebar a, -.sidebar i { - text-decoration: none; - color: var(--sidebar-fg); - font-weight: 300; -} - -.sidebar .active, -.sidebar li li:hover { - background-color: var(--sidebar-active-bg); -} -.sidebar .active a { - font-weight: 500; -} - -.sidebar .title { - font-size: 1.5rem; - font-weight: 600; - color: rgb(25, 52, 70); - padding-top: 10px; - padding-bottom: 10px; -} -.sidebar .title > div:first-of-type a { - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; -} -.sidebar .title div:first-of-type a p { - margin: 0; - padding: 0; -} - -.sidebar .title { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} - -.sidebar .title div:last-of-type a { - font-size: 0.8em; - cursor: pointer; - text-decoration: none; - color: var(--sidebar-fg); -} - -.sidebar .menu_section { - padding-right: 1.5rem; - padding-left: 1.5rem; - padding-bottom: 1rem; - padding-top: 1rem; -} -.sidebar .menu_section > li > a { - font-size: 1.2rem; - padding-bottom: 0.5rem; -} -.sidebar .menu_section > li > ul > li { - display: flex; - flex-direction: row; - align-items: center; - gap: 0.5rem; - padding: 0.4rem; - border-radius: 4px; -} - -.sidebar #theme_button { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; - height: 50px; - background: none; - border: none; - color: var(--sidebar-fg); - cursor: pointer; - margin-left: 1rem; - margin-bottom: 1rem; -} -.sidebar #theme_button i { - font-size: 2rem; -} - -@media only screen and (max-width: 768px) { - .sidebar { - position: fixed; - left: -252px; - transition: 0.2s; - overflow-y: scroll; - } - - .sidebar .title { - font-size: 1rem; - margin: 0; - } - .sidebar .menu_section > li > a { - font-size: 0.6rem; - } - - .sidebar hr { - margin: 0; - } -} - -#menu_button { - position: fixed; - z-index: 9; - width: 50px; - height: 50px; - bottom: 1rem; - right: 1rem; - background-color: rgb(74, 74, 74); - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; -} - -#menu_button i { - font-size: 2rem; - color: white; -} - -@media only screen and (min-width: 768px) { - #menu_button { - display: none; - } -} diff --git a/selfdrive/frogpilot/thepond/assets/images/android-chrome-192x192.png b/selfdrive/frogpilot/thepond/assets/images/android-chrome-192x192.png deleted file mode 100644 index 4341316e54137b..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/android-chrome-192x192.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/android-chrome-512x512.png b/selfdrive/frogpilot/thepond/assets/images/android-chrome-512x512.png deleted file mode 100644 index de91c6e77a44aa..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/android-chrome-512x512.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/apple-touch-icon.png b/selfdrive/frogpilot/thepond/assets/images/apple-touch-icon.png deleted file mode 100644 index 2ed3950a78f76b..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/apple-touch-icon.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/favicon-16x16.png b/selfdrive/frogpilot/thepond/assets/images/favicon-16x16.png deleted file mode 100644 index 984f5a27a35f1d..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/favicon-16x16.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/favicon-32x32.png b/selfdrive/frogpilot/thepond/assets/images/favicon-32x32.png deleted file mode 100644 index f99a47aec49b79..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/favicon-32x32.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/favicon.ico b/selfdrive/frogpilot/thepond/assets/images/favicon.ico deleted file mode 100644 index e83b301e671440..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/favicon.ico and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/frog.png b/selfdrive/frogpilot/thepond/assets/images/frog.png deleted file mode 100644 index 5285f0be6e5b22..00000000000000 Binary files a/selfdrive/frogpilot/thepond/assets/images/frog.png and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/assets/images/safari-pinned-tab.svg b/selfdrive/frogpilot/thepond/assets/images/safari-pinned-tab.svg deleted file mode 100644 index 90c531cc3ab1f7..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/images/safari-pinned-tab.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - -Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - - - diff --git a/selfdrive/frogpilot/thepond/assets/js/snackbar.js b/selfdrive/frogpilot/thepond/assets/js/snackbar.js deleted file mode 100644 index 8bfdd9eeb60c78..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/js/snackbar.js +++ /dev/null @@ -1,32 +0,0 @@ -function showSnackbar(msg, level, timeout = 3000) { - const wrapper = document.getElementById("snackbar_wrapper") - // We want a max of 2 snackbars at a time - if (wrapper.children.length >= 2) { - wrapper.children[0].style.opacity = 0 - setTimeout(() => { - wrapper.children[0].remove() - }, 1000) - } - // Add another div inside the snackbar div - const snackbarId = Math.random().toString(36).substring(5) - const innerDiv = document.createElement("div") - innerDiv.innerHTML = msg - innerDiv.className = "snackbar show" - if (level == "error") { - innerDiv.style.backgroundColor = "#f44336" - } - innerDiv.id = snackbarId - wrapper.appendChild(innerDiv) - - // After X seconds, remove the snackbardiv - setTimeout(() => { - const snackbar = document.getElementById(snackbarId) - if (snackbar) { - snackbar.style.opacity = 0 - setTimeout(() => { - snackbar.remove() - }, 1000) - } - }, timeout - 1000) -} -window.showSnackbar = showSnackbar diff --git a/selfdrive/frogpilot/thepond/assets/js/utils.js b/selfdrive/frogpilot/thepond/assets/js/utils.js deleted file mode 100644 index 6397ee36578163..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/js/utils.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Format a number of seconds to a human readable string - * @param {number} seconds - The number of seconds to format - * @param {"days"|"hours"|"minutes"} precision - The precision of the output, default is minutes - * @returns {string} - The formatted string - */ -export function formatSecondsToHuman(seconds, precision = "minutes") { - const days = Math.floor(seconds / 86400) - seconds -= days * 86400 - const hours = Math.floor(seconds / 3600) - seconds -= hours * 3600 - const minutes = Math.floor(seconds / 60) - - let result = [] - if (days > 0) { - result.push(`${days} days`) - } - if (hours > 0) { - result.push(`${hours} hours`) - } - if (minutes > 0) { - result.push(`${minutes} minutes`) - } - let slice = precision === "days" ? 1 : precision === "hours" ? 2 : 3 - return result.slice(0, slice).join(", ") -} - -/** - * Parse a filename into a date - * @param {string} filename - The filename to parse (format: YYYY-MM-DD--HH-MM-SS) - * @returns {Date} - The parsed date - */ -export function parseErrorLogToDate(filename) { - // Dateformat is YYYY-MM-DD--HH-MM-SS - const date = filename.split("--")[0] - const time = filename.split("--")[1] - // Parse it into a date - const d = new Date() - d.setFullYear(date.slice(0, 4)) - d.setMonth(parseInt(date.slice(5, 7)) - 1) // Months are 0-indexed - d.setDate(date.slice(8, 10)) - d.setHours(time.slice(0, 2)) - d.setMinutes(time.slice(3, 5)) - d.setSeconds(time.slice(6, 8)) - return d -} - -/** - * Converts the first char of a string to upper case - * @param {string} str - */ -export function upperFirst(str) { - return str[0].toUpperCase() + str.slice(1) -} - -/** - * Opens the sidebar on mobile devices - */ -export function showSidebar() { - const sidebarUnderlay = document.getElementById("sidebarUnderlay") - const sidebar = document.getElementById("sidebar") - const html = document.getElementsByTagName("html")[0] - sidebar.classList.add("visible") - sidebarUnderlay.classList.remove("hidden") - html.classList.add("no_scroll") -} - -/** - * Closes the sidebar menu on mobile devices - */ -export function hideSidebar() { - const sidebarUnderlay = document.getElementById("sidebarUnderlay") - const sidebar = document.getElementById("sidebar") - const html = document.getElementsByTagName("html")[0] - sidebar.classList.remove("visible") - sidebarUnderlay.classList.add("hidden") - html.classList.remove("no_scroll") -} diff --git a/selfdrive/frogpilot/thepond/assets/manifest.json b/selfdrive/frogpilot/thepond/assets/manifest.json deleted file mode 100644 index 7b8307f0a26f5d..00000000000000 --- a/selfdrive/frogpilot/thepond/assets/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "ThePond", - "short_name": "", - "icons": [ - { - "src": "/assets/images/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/assets/images/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "id": "46bf2df73deba8e1512c35de", - "background_color": "#151414", - "theme_color": "#151414", - "display": "standalone" -} \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fixtures/Readme.md b/selfdrive/frogpilot/thepond/fixtures/Readme.md deleted file mode 100644 index 8fd7e97e007457..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/Readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# Fixtures - -This directory just contains things needed to run thepond off-device. It has some -fake modules to replace openpilot tools modules and some data that can be used. - diff --git a/selfdrive/frogpilot/thepond/fixtures/__init__.py b/selfdrive/frogpilot/thepond/fixtures/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-18--14-21-15.log b/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-18--14-21-15.log deleted file mode 100644 index 0c97d465735ff6..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-18--14-21-15.log +++ /dev/null @@ -1,296 +0,0 @@ -Traceback (most recent call last): - File "/data/openpilot/openpilot/selfdrive/manager/process.py", line 40, in launcher - mod.main() - File "/data/openpilot/selfdrive/controls/plannerd.py", line 52, in main - plannerd_thread() - File "/data/openpilot/selfdrive/controls/plannerd.py", line 34, in plannerd_thread - longitudinal_planner = LongitudinalPlanner(CP) - ^^^^^^^^^^^^^^^^^^^^^^^ - File "/data/openpilot/openpilot/selfdrive/controls/lib/longitudinal_planner.py", line 52, in __init__ - self.mpc = LongitudinalMpc(CP) - ^^^^^^^^^^^^^^^^^^^ - File "/data/openpilot/openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py", line 250, in __init__ - self.reset() - File "/data/openpilot/openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py", line 280, in reset - self.set_weights() - File "/data/openpilot/openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py", line 311, in set_weights - raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner cost set') -NotImplementedError: Planner mode ( carName = "honda", - carFingerprint = "HONDA CLARITY 2018", - enableGasInterceptor = true, - pcmCruise = false, - enableCameraDEPRECATED = false, - enableDsu = false, - enableApgsDEPRECATED = false, - minEnableSpeed = -1, - minSteerSpeed = 0, - safetyModelDEPRECATED = silent, - safetyParamDEPRECATED = 0, - mass = 1973.9548, - wheelbase = 2.75, - centerToFront = 1.1, - steerRatio = 16.5, - steerRatioRear = 0, - rotationalInertia = 3501.6094, - tireStiffnessFront = 259435.98, - tireStiffnessRear = 273410.31, - longitudinalTuning = ( - kpBP = [0, 5, 35], - kpV = [1.2, 0.8, 0.5], - kiBP = [0, 35], - kiV = [0.18, 0.12], - deadzoneBP = [0], - deadzoneV = [0], - kf = 1 ), - lateralTuning = ( - pid = (kpBP = [0], kpV = [0.1575], kiBP = [0], kiV = [0.05175], kf = 4e-05) ), - steerLimitAlert = false, - vEgoStopping = 0.5, - directAccelControlDEPRECATED = false, - stoppingControl = true, - startAccel = 0, - steerRateCostDEPRECATED = 0, - steerControlType = torque, - radarUnavailable = false, - steerActuatorDelay = 0.1, - openpilotLongitudinalControl = true, - carVin = "JHMZC5F31JC017787", - isPandaBlackDEPRECATED = false, - dashcamOnly = false, - safetyModelPassiveDEPRECATED = silent, - transmissionType = unknown, - carFw = [ - ( ecu = gateway, - fwVersion = "38897-TRW-A010\000\000", - address = 417001457, - subAddress = 0, - responseAddress = 417001967, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = fwdRadar, - fwVersion = "36161-TRW-A040\000\000", - address = 416985329, - subAddress = 0, - responseAddress = 417001904, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = vsa, - fwVersion = "57114-TRW-A010\000\000", - address = 416950513, - subAddress = 0, - responseAddress = 417001768, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = shiftByWire, - fwVersion = "54008-TRW-A910\000\000", - address = 416943089, - subAddress = 0, - responseAddress = 417001739, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = eps, - fwVersion = "39990,TRW,A020\000\000", - address = 416952561, - subAddress = 0, - responseAddress = 417001776, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = combinationMeter, - fwVersion = "78109-TRW-A020\000\000", - address = 416964849, - subAddress = 0, - responseAddress = 417001824, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = srs, - fwVersion = "77959-TRW-A210\000\000", - address = 416961521, - subAddress = 0, - responseAddress = 417001811, - request = ["\"\361\201"], - brand = "honda", - bus = 1, - logging = false, - obdMultiplexing = true ), - ( ecu = vsa, - fwVersion = "57114-TRW-A010\000\000", - address = 416950513, - subAddress = 0, - responseAddress = 417001768, - request = [">\000", "\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ), - ( ecu = eps, - fwVersion = "39990,TRW,A020\000\000", - address = 416952561, - subAddress = 0, - responseAddress = 417001776, - request = [">\000", "\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ), - ( ecu = srs, - fwVersion = "77959-TRW-A210\000\000", - address = 416961521, - subAddress = 0, - responseAddress = 417001811, - request = [">\000", "\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ), - ( ecu = gateway, - fwVersion = "\fJ2122880\000\000\000\000 ", - address = 417001457, - subAddress = 0, - responseAddress = 417001967, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = fwdRadar, - fwVersion = "\016 RWL511911604\000\000", - address = 416985329, - subAddress = 0, - responseAddress = 417001904, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = vsa, - fwVersion = "\v81151987201\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", - address = 416950513, - subAddress = 0, - responseAddress = 417001768, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = combinationMeter, - fwVersion = "\030J2190469\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", - address = 416964849, - subAddress = 0, - responseAddress = 417001824, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = eps, - fwVersion = "\f BK8F162914D ", - address = 416952561, - subAddress = 0, - responseAddress = 417001776, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = shiftByWire, - fwVersion = "\02207221918184B101330\000\000\000\000\000\000\000\000\000\000\000\000", - address = 416943089, - subAddress = 0, - responseAddress = 417001739, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = srs, - fwVersion = "\vC18A00HH00M\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", - address = 416961521, - subAddress = 0, - responseAddress = 417001811, - request = ["\"\361\022"], - brand = "honda", - bus = 1, - logging = true, - obdMultiplexing = true ), - ( ecu = vsa, - fwVersion = "57114-TRW-A010\000\000", - address = 416950513, - subAddress = 0, - responseAddress = 417001768, - request = ["\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ), - ( ecu = eps, - fwVersion = "39990,TRW,A020\000\000", - address = 416952561, - subAddress = 0, - responseAddress = 417001776, - request = ["\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ), - ( ecu = srs, - fwVersion = "77959-TRW-A210\000\000", - address = 416961521, - subAddress = 0, - responseAddress = 417001811, - request = ["\"\361\201"], - brand = "honda", - bus = 0, - logging = true, - obdMultiplexing = true ) ], - radarTimeStep = 0.05, - communityFeatureDEPRECATED = false, - steerLimitTimer = 0.8, - lateralParams = ( - torqueBP = [0, 2560, 15360], - torqueV = [0, 2560, 3840] ), - fingerprintSource = fw, - networkLocation = fwdCamera, - minSpeedCanDEPRECATED = 0, - stoppingDecelRate = 0.8, - startingAccelRateDEPRECATED = 0, - maxSteeringAngleDegDEPRECATED = 0, - fuzzyFingerprint = false, - enableBsm = false, - hasStockCameraDEPRECATED = false, - longitudinalActuatorDelayUpperBound = 0.15, - vEgoStarting = 0.5, - stopAccel = -2, - longitudinalActuatorDelayLowerBound = 0.15, - safetyConfigs = [ - ( safetyModel = hondaNidec, - safetyParamDEPRECATED = 0, - safetyParam2DEPRECATED = 0, - safetyParam = 48 ) ], - wheelSpeedFactor = 1, - flags = 0, - alternativeExperience = 41, - notCar = false, - maxLateralAccel = 0.40185186, - autoResumeSng = true, - startingState = false, - experimentalLongitudinalAvailable = false, - tireStiffnessFactor = 1, - passive = false ) not recognized in planner cost set diff --git a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-29-18.log b/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-29-18.log deleted file mode 100644 index 87febf29d4dda4..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-29-18.log +++ /dev/null @@ -1,9 +0,0 @@ -Traceback (most recent call last): - File "/data/openpilot/selfdrive/manager/./manager.py", line 405, in main - manager_thread() - File "/data/openpilot/selfdrive/manager/./manager.py", line 379, in manager_thread - pm.send('managerState', msg) - File "/data/openpilot/cereal/messaging/__init__.py", line 293, in send - self.sock[s].send(dat) - File "cereal/messaging/messaging_pyx.pyx", line 242, in cereal.messaging.messaging_pyx.PubSocket.send -cereal.messaging.messaging_pyx.MultiplePublishersError: Messaging failure : Address already in use \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-30-20.log b/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-30-20.log deleted file mode 100644 index 87febf29d4dda4..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-23--21-30-20.log +++ /dev/null @@ -1,9 +0,0 @@ -Traceback (most recent call last): - File "/data/openpilot/selfdrive/manager/./manager.py", line 405, in main - manager_thread() - File "/data/openpilot/selfdrive/manager/./manager.py", line 379, in manager_thread - pm.send('managerState', msg) - File "/data/openpilot/cereal/messaging/__init__.py", line 293, in send - self.sock[s].send(dat) - File "cereal/messaging/messaging_pyx.pyx", line 242, in cereal.messaging.messaging_pyx.PubSocket.send -cereal.messaging.messaging_pyx.MultiplePublishersError: Messaging failure : Address already in use \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-29--16-10-18.log b/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-29--16-10-18.log deleted file mode 100644 index 87febf29d4dda4..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/data/community/crashes/2024-02-29--16-10-18.log +++ /dev/null @@ -1,9 +0,0 @@ -Traceback (most recent call last): - File "/data/openpilot/selfdrive/manager/./manager.py", line 405, in main - manager_thread() - File "/data/openpilot/selfdrive/manager/./manager.py", line 379, in manager_thread - pm.send('managerState', msg) - File "/data/openpilot/cereal/messaging/__init__.py", line 293, in send - self.sock[s].send(dat) - File "cereal/messaging/messaging_pyx.pyx", line 242, in cereal.messaging.messaging_pyx.PubSocket.send -cereal.messaging.messaging_pyx.MultiplePublishersError: Messaging failure : Address already in use \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--13-07-23--0/qcamera.ts b/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--13-07-23--0/qcamera.ts deleted file mode 100644 index e27983b5432fc0..00000000000000 Binary files a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--13-07-23--0/qcamera.ts and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--17-48-52--0/qcamera.ts b/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--17-48-52--0/qcamera.ts deleted file mode 100644 index 7a65041dab7190..00000000000000 Binary files a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-11--17-48-52--0/qcamera.ts and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--14-13-02--0/qcamera.ts b/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--14-13-02--0/qcamera.ts deleted file mode 100644 index c2274d269999c4..00000000000000 Binary files a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--14-13-02--0/qcamera.ts and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--17-39-18--0/qcamera.ts b/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--17-39-18--0/qcamera.ts deleted file mode 100644 index f8e1ff8e660a47..00000000000000 Binary files a/selfdrive/frogpilot/thepond/fixtures/data/media/0/realdata/2024-02-12--17-39-18--0/qcamera.ts and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/fixtures/data/media/0/videos/20240218-134851.mp4 b/selfdrive/frogpilot/thepond/fixtures/data/media/0/videos/20240218-134851.mp4 deleted file mode 100644 index 7cce867658ad90..00000000000000 Binary files a/selfdrive/frogpilot/thepond/fixtures/data/media/0/videos/20240218-134851.mp4 and /dev/null differ diff --git a/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.py b/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.py deleted file mode 100644 index 76a3c7b29d959a..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.py +++ /dev/null @@ -1,41 +0,0 @@ -import pathlib - -DIR_OF_THIS_FILE = pathlib.Path(__file__).parent.absolute() -PARAMS_FILE = f"{DIR_OF_THIS_FILE}/params.txt" # This is where param values are stored when running in debug - -class Params: - def __init__(self, path="") -> None: - pass - - def get(self, requestedKey): - with open(PARAMS_FILE) as f: - lines = f.readlines() - for line in lines: - if line == "\n": - continue - try: - key, value = line.strip().split(":", 1) # Split the line into key and value - if requestedKey == key: - return str.encode(value) - except: - print(f"Error parsing line from params.txt:{lines.index(line)}: ", line) - - def put(self, key, value): - with open(PARAMS_FILE, "r") as f: - lines = f.readlines() - with open(PARAMS_FILE, "w") as f: - hasWritten = False - for line in lines: - if line.startswith(f"{key}:"): - f.write(f"{key}: {value}\n") - hasWritten = True - else: - f.write(line) - if not hasWritten: - f.write(f"\n{key}: {value}\n") - - def put_bool(self, key, value): - """ - Don't care about this, it's only used to update "FrogPilotTogglesUpdated" - """ - pass diff --git a/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.txt b/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.txt deleted file mode 100644 index 94f14d01700bc0..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/fake_modules/params.txt +++ /dev/null @@ -1,206 +0,0 @@ -AccelerationPath: 1 -AccelerationProfile: 2 -AdjacentPath: 0 -AdjacentPathMetrics: 0 -AdjustablePersonalities: 1 -AggressiveAcceleration: 1 -AggressiveFollow: 1.25 -AggressiveJerk: 0.5 -AlertVolumeControl: 1 -AlwaysOnLateral: 1 -AlwaysOnLateralMain: 1 -ApiCache_DriveStats: {"all":{"distance":833.4716346324421,"minutes":2001,"routes":140},"week":{"distance":82.44520304882826,"minutes":157,"routes":11}} -ApiCache_NavDestinations: [{"latitude":60.14724428600704,"longitude":15.189865813164436,"place_name":"lekplats","save_type":"recent"}] -AthenadPid: 38342 -BlindSpotPath: 1 -CameraView: 1 -CarBatteryCapacity: 26418418 -CarMake: Ford -CarModel: FORD F-150 14TH GEN -CECurves: 1 -CENavigation: 1 -CENavigationIntersections: 1 -CENavigationLead: 1 -CENavigationTurns: 1 -CESignal: 1 -CESlowerLead: 0 -CESpeed: 0 -CESpeedLead: 0 -CEStopLights: 1 -CEStopLightsLead: 1 -Compass: 1 -CompletedTrainingVersion: 0.2.0 -ConditionalExperimental: 0 -CrosstrekTorque: 0 -CurveSensitivity: 100 -CustomAlerts: 1 -CustomColors: 2 -CustomIcons: 2 -CustomPersonalities: 1 -CustomSignals: 0 -CustomSounds: 0 -CustomTheme: 1 -CustomUI: 1 -DeviceShutdown: 2 -DisableOnroadUploads: 1 -DisableUpdates: 1 -DisengageOnAccelerator: 0 -DisengageVolume: 0.000000 -DongleId: a0234ef2807d2dc8 -DragonPilotTune: 0 -DriverCamera: 0 -DriveStats: 1 -EngageVolume: 0.000000 -EVTable: 1 -ExperimentalLongitudinalEnabled: 1 -ExperimentalMode: 1 -ExperimentalModeActivation: 1 -ExperimentalModeConfirmed: 1 -ExperimentalModeViaLKAS: 1 -ExperimentalModeViaScreen: 1 -Fahrenheit: 0 -FireTheBabysitter: 1 -ForceAutoTune: 0 -ForceFingerprint: 1 -FPSCounter: 0 -FullMap: 0 -GasRegenCmd: 0 -GitBranch: FrogPilot -GitCommit: 3e189672935f8b51184292b3581c8ae25d316d24 -GithubSshKeys: FOOBAR -GithubUsername: aidenir -GitRemote: https://github.com/frogai/openpilot.git -GoatScream: 1 -GreenLightAlert: 1 -GsmMetered: 1 -GsmRoaming: 0 -HardwareSerial: 0x00000000 -HasAcceptedTerms: 2 -HideSpeed: 1 -HideSpeedUI: 0 -HigherBitrate: 1 -IMEI: 000000000000000 -InstallDate: February 23, 2024 - 01:23AM -IsEngaged: 0 -IsMetric: 1 -IsOffroad: 1 -IsOnroad: 0 -IsReleaseBranch: 0 -IsRhdDetected: 0 -IsTestedBranch: 0 -LaneChangeTime: 2.000000 -LaneDetection: 1 -LaneDetectionWidth: 27 -LaneLinesWidth: 10 -LanguageSetting: main_en -LastAthenaPingTime: 5790754423591 -LastGPSPosition: {"latitude":60.14724428600704,"longitude":15.189865813164436,"altitude":50.656951246003345} -LastMapsUpdate: February 23rd, 2024 -LastUpdateTime: 2024-02-23T20:19:00.504822 -LateralTune: 1 -LeadDepartingAlert: 0 -LeadInfo: 0 -LiveParameters: {"carFingerprint":"VOLKSWAGEN PASSAT 8TH GEN","steerRatio":15.996020317077637,"stiffnessFactor":1.020769476890564,"angleOffsetAverageDeg":-0.19372670352458954} -LockDoors: 0 -LongitudinalPersonality: 0 -LongitudinalTune: 1 -LongPitch: 1 -LoudBlindspotAlert: 0 -LowerVolt: 1 -MapboxPublicKey: pk.key -MapboxSecretKey: foobar -Model: los-angeles -ModelUI: 1 -MTSCCurvatureCheck: 0 -MTSCEnabled: 1 -MTSCLimit: 159 -MuteOverheated: 0 -NavChill: 1 -NavPastDestinations:'[{"latitude":60.14724428600704,"longitude":15.189865813164436,"modified":null,"place_name":"lekplats","save_type":"recent","time":1708767861}]' -NavSettingTime24h: 1 -NetworkMetered: 0 -NNFF: 1 -NoLogging: 0 -NudgelessLaneChange: 1 -NumericalTemp: 0 -OfflineMode: 1 -Offset1: 3.000000 -Offset2: 4.000000 -Offset3: 5.000000 -Offset4: 5.000000 -OneLaneChange: 1 -OpenpilotEnabledToggle: 1 -OSMDownloadProgress: -PathEdgeWidth: 20 -PathWidth: 19 -PauseLateralOnSignal: 0 -PersonalitiesViaScreen: 1 -PersonalitiesViaWheel: 1 -PreferredSchedule: 0 -PreviousSpeedLimit: 11.111120 -PrimeType: 0 -PromptDistractedVolume: 50.000000 -PromptVolume: 50.000000 -QOLControls: 1 -QOLVisuals: 1 -RandomEvents: 0 -RecordFront: 1 -RefuseVolume: 100 -RelaxedFollow: 1.75 -RelaxedJerk: 1.0 -ReverseCruise: 0 -RoadEdgesWidth: 5 -RoadNameUI: 1 -RotatingWheel: 1 -ScreenBrightness: 101 -SearchInput: 0 -SetSpeedLimit: 0 -SetSpeedOffset: 0 -ShowCPU: 0 -ShowGPU: 0 -ShowIP: 0 -ShowMemoryUsage: 0 -ShowStorageLeft: 0 -ShowStorageUsed: 0 -Sidebar: 0 -SLCFallback: 1 -SLCOverride: 1 -SLCPriority1: 3 -SLCPriority2: 2 -SLCPriority3: 0 -SmoothBraking: 1 -SNGHack: 1 -SpeedLimitChangedAlert: 1 -SpeedLimitController: 1 -SshEnabled: 1 -StandardFollow: 1.45 -StandardJerk: 1.0 -SteerRatio: 15.600000 -SteerRatioStock: 15.600000 -StoppingDistance: 0.000000 -TermsVersion: 2 -Timezone: Europe/Stockholm -TrainingVersion: 0.2.0 -TurnAggressiveness: 100 -TurnDesires: 1 -UnlimitedLength: 0 -Updated: February 23, 2024 - 01:19PM -UpdaterAvailableBranches: FrogPilot,FrogPilot-Development,FrogPilot-Nissan,FrogPilot-Nissan-Test,FrogPilot-Old,FrogPilot-Previous,FrogPilot-Staging,FrogPilot-Testing,MAKE-PRS-HERE,optimize-lexus-es -UpdaterLastFetchTime: 2024-02-23T20:19:00.439281 -UseLateralJerk: 0 -UseSI: 0 -UseVienna: 1 -Version: 0.9.6 -VisionTurnControl: 1 -WarningImmediateVolume: 100 -WarningSoftVolume: 100 -WheelIcon: 0 -WheelSpeed: 0 -ModelSelector: 2 -NavDestination: 0 -FrogPilotKilometers: 10 -FrogPilotMinutes: 100 -FrogPilotDrives: 5 -ShowSLCOffset: 1 -ShowSLCOffsetUI: 1 -IsLdwEnabled: 0 diff --git a/selfdrive/frogpilot/thepond/fixtures/fake_modules/route.py b/selfdrive/frogpilot/thepond/fixtures/fake_modules/route.py deleted file mode 100644 index c640f2fccb13ee..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/fake_modules/route.py +++ /dev/null @@ -1,269 +0,0 @@ -import os -import re -from functools import cache -from urllib.parse import urlparse -from collections import defaultdict -from itertools import chain -from typing import Optional - -QLOG_FILENAMES = ['qlog', 'qlog.bz2'] -QCAMERA_FILENAMES = ['qcamera.ts'] -LOG_FILENAMES = ['rlog', 'rlog.bz2', 'raw_log.bz2'] -CAMERA_FILENAMES = ['fcamera.hevc', 'video.hevc'] -DCAMERA_FILENAMES = ['dcamera.hevc'] -ECAMERA_FILENAMES = ['ecamera.hevc'] - -class Route: - def __init__(self, name, data_dir=None): - self._name = RouteName(name) - self.files = None - if data_dir is not None: - self._segments = self._get_segments_local(data_dir) - else: - self._segments = self._get_segments_remote() - self.max_seg_number = self._segments[-1].name.segment_num - - @property - def name(self): - return self._name - - @property - def segments(self): - return self._segments - - def log_paths(self): - log_path_by_seg_num = {s.name.segment_num: s.log_path for s in self._segments} - return [log_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - def qlog_paths(self): - qlog_path_by_seg_num = {s.name.segment_num: s.qlog_path for s in self._segments} - return [qlog_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - def camera_paths(self): - camera_path_by_seg_num = {s.name.segment_num: s.camera_path for s in self._segments} - return [camera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - def dcamera_paths(self): - dcamera_path_by_seg_num = {s.name.segment_num: s.dcamera_path for s in self._segments} - return [dcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - def ecamera_paths(self): - ecamera_path_by_seg_num = {s.name.segment_num: s.ecamera_path for s in self._segments} - return [ecamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - def qcamera_paths(self): - qcamera_path_by_seg_num = {s.name.segment_num: s.qcamera_path for s in self._segments} - return [qcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] - - # TODO: refactor this, it's super repetitive - def _get_segments_remote(self): - api = CommaApi(get_token()) - route_files = api.get('v1/route/' + self.name.canonical_name + '/files') - self.files = list(chain.from_iterable(route_files.values())) - - segments = {} - for url in self.files: - _, dongle_id, time_str, segment_num, fn = urlparse(url).path.rsplit('/', maxsplit=4) - segment_name = f'{dongle_id}|{time_str}--{segment_num}' - if segments.get(segment_name): - segments[segment_name] = Segment( - segment_name, - url if fn in LOG_FILENAMES else segments[segment_name].log_path, - url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path, - url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path, - url if fn in DCAMERA_FILENAMES else segments[segment_name].dcamera_path, - url if fn in ECAMERA_FILENAMES else segments[segment_name].ecamera_path, - url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path, - ) - else: - segments[segment_name] = Segment( - segment_name, - url if fn in LOG_FILENAMES else None, - url if fn in QLOG_FILENAMES else None, - url if fn in CAMERA_FILENAMES else None, - url if fn in DCAMERA_FILENAMES else None, - url if fn in ECAMERA_FILENAMES else None, - url if fn in QCAMERA_FILENAMES else None, - ) - - return sorted(segments.values(), key=lambda seg: seg.name.segment_num) - - def _get_segments_local(self, data_dir): - files = os.listdir(data_dir) - segment_files = defaultdict(list) - - for f in files: - fullpath = os.path.join(data_dir, f) - explorer_match = re.match(RE.EXPLORER_FILE, f) - op_match = re.match(RE.OP_SEGMENT_DIR, f) - - if explorer_match: - segment_name = explorer_match.group('segment_name') - fn = explorer_match.group('file_name') - if segment_name.replace('_', '|').startswith(self.name.canonical_name): - segment_files[segment_name].append((fullpath, fn)) - elif op_match and os.path.isdir(fullpath): - segment_name = op_match.group('segment_name') - if segment_name.startswith(self.name.canonical_name): - for seg_f in os.listdir(fullpath): - segment_files[segment_name].append((os.path.join(fullpath, seg_f), seg_f)) - elif f == self.name.canonical_name: - for seg_num in os.listdir(fullpath): - if not seg_num.isdigit(): - continue - - segment_name = f'{self.name.canonical_name}--{seg_num}' - for seg_f in os.listdir(os.path.join(fullpath, seg_num)): - segment_files[segment_name].append((os.path.join(fullpath, seg_num, seg_f), seg_f)) - - segments = [] - for segment, files in segment_files.items(): - - try: - log_path = next(path for path, filename in files if filename in LOG_FILENAMES) - except StopIteration: - log_path = None - - try: - qlog_path = next(path for path, filename in files if filename in QLOG_FILENAMES) - except StopIteration: - qlog_path = None - - try: - camera_path = next(path for path, filename in files if filename in CAMERA_FILENAMES) - except StopIteration: - camera_path = None - - try: - dcamera_path = next(path for path, filename in files if filename in DCAMERA_FILENAMES) - except StopIteration: - dcamera_path = None - - try: - ecamera_path = next(path for path, filename in files if filename in ECAMERA_FILENAMES) - except StopIteration: - ecamera_path = None - - try: - qcamera_path = next(path for path, filename in files if filename in QCAMERA_FILENAMES) - except StopIteration: - qcamera_path = None - - segments.append(Segment(segment, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path)) - - if len(segments) == 0: - raise ValueError(f'Could not find segments for route {self.name.canonical_name} in data directory {data_dir}') - return sorted(segments, key=lambda seg: seg.name.segment_num) - -class Segment: - def __init__(self, name, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path): - self._name = SegmentName(name) - self.log_path = log_path - self.qlog_path = qlog_path - self.camera_path = camera_path - self.dcamera_path = dcamera_path - self.ecamera_path = ecamera_path - self.qcamera_path = qcamera_path - - @property - def name(self): - return self._name - -class RouteName: - def __init__(self, name_str: str): - self._name_str = name_str - delim = next(c for c in self._name_str if c in ("|", "/")) - self._dongle_id, self._time_str = self._name_str.split(delim) - - assert len(self._dongle_id) == 16, self._name_str - assert len(self._time_str) == 20, self._name_str - self._canonical_name = f"{self._dongle_id}|{self._time_str}" - - @property - def canonical_name(self) -> str: return self._canonical_name - - @property - def dongle_id(self) -> str: return self._dongle_id - - @property - def time_str(self) -> str: return self._time_str - - def __str__(self) -> str: return self._canonical_name - -class SegmentName: - # TODO: add constructor that takes dongle_id, time_str, segment_num and then create instances - # of this class instead of manually constructing a segment name (use canonical_name prop instead) - def __init__(self, name_str: str, allow_route_name=False): - data_dir_path_separator_index = name_str.rsplit("|", 1)[0].rfind("/") - use_data_dir = (data_dir_path_separator_index != -1) and ("|" in name_str) - self._name_str = name_str[data_dir_path_separator_index + 1:] if use_data_dir else name_str - self._data_dir = name_str[:data_dir_path_separator_index] if use_data_dir else None - - seg_num_delim = "--" if self._name_str.count("--") == 2 else "/" - name_parts = self._name_str.rsplit(seg_num_delim, 1) - if allow_route_name and len(name_parts) == 1: - name_parts.append("-1") # no segment number - self._route_name = RouteName(name_parts[0]) - self._num = int(name_parts[1]) - self._canonical_name = f"{self._route_name._dongle_id}|{self._route_name._time_str}--{self._num}" - - @property - def canonical_name(self) -> str: return self._canonical_name - - @property - def dongle_id(self) -> str: return self._route_name.dongle_id - - @property - def time_str(self) -> str: return self._route_name.time_str - - @property - def segment_num(self) -> int: return self._num - - @property - def route_name(self) -> RouteName: return self._route_name - - @property - def data_dir(self) -> Optional[str]: return self._data_dir - - def __str__(self) -> str: return self._canonical_name - - -@cache -def get_max_seg_number_cached(sr: 'SegmentRange'): - try: - api = CommaApi(get_token()) - return api.get("/v1/route/" + sr.route_name.replace("/", "|"))["segment_numbers"][-1] - except Exception as e: - raise Exception("unable to get max_segment_number. ensure you have access to this route or the route is public.") from e - - -class SegmentRange: - def __init__(self, segment_range: str): - self.m = re.fullmatch(RE.SEGMENT_RANGE, segment_range) - assert self.m, f"Segment range is not valid {segment_range}" - - def get_max_seg_number(self): - return get_max_seg_number_cached(self) - - @property - def route_name(self): - return self.m.group("route_name") - - @property - def dongle_id(self): - return self.m.group("dongle_id") - - @property - def timestamp(self): - return self.m.group("timestamp") - - @property - def _slice(self): - return self.m.group("slice") - - @property - def selector(self): - return self.m.group("selector") - - def __str__(self): - return f"{self.dongle_id}/{self.timestamp}" + (f"/{self._slice}" if self._slice else "") + (f"/{self.selector}" if self.selector else "") diff --git a/selfdrive/frogpilot/thepond/fixtures/fake_modules/xattr_cache.py b/selfdrive/frogpilot/thepond/fixtures/fake_modules/xattr_cache.py deleted file mode 100644 index 5feeff34d2a464..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/fake_modules/xattr_cache.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import errno -from typing import Dict, Optional, Tuple - -_cached_attributes: Dict[Tuple, Optional[bytes]] = {} - -def getxattr(path: str, attr_name: str) -> Optional[bytes]: - key = (path, attr_name) - if key not in _cached_attributes: - try: - response = os.getxattr(path, attr_name) - except OSError as e: - # ENODATA means attribute hasn't been set - if e.errno == errno.ENODATA: - response = None - else: - raise - _cached_attributes[key] = response - return _cached_attributes[key] - -def setxattr(path: str, attr_name: str, attr_value: bytes) -> None: - _cached_attributes.pop((path, attr_name), None) - return os.setxattr(path, attr_name, attr_value) diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/__init__.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/ford/__init__.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/ford/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/ford/values.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/ford/values.py deleted file mode 100644 index 5bdc6316e2aa6c..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/ford/values.py +++ /dev/null @@ -1,11 +0,0 @@ -from enum import StrEnum - -class CAR(StrEnum): - BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN" - ESCAPE_MK4 = "FORD ESCAPE 4TH GEN" - EXPLORER_MK6 = "FORD EXPLORER 6TH GEN" - F_150_MK14 = "FORD F-150 14TH GEN" - FOCUS_MK4 = "FORD FOCUS 4TH GEN" - MAVERICK_MK1 = "FORD MAVERICK 1ST GEN" - F_150_LIGHTNING_MK1 = "FORD F-150 LIGHTNING 1ST GEN" - MUSTANG_MACH_E_MK1 = "FORD MUSTANG MACH-E 1ST GEN" \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/tesla/__init__.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/tesla/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/tesla/values.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/tesla/values.py deleted file mode 100644 index 871506e7561c31..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/tesla/values.py +++ /dev/null @@ -1,5 +0,0 @@ -from enum import StrEnum - -class CAR(StrEnum): - AP1_MODELS = 'TESLA AP1 MODEL S' - AP2_MODELS = 'TESLA AP2 MODEL S' diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/volkswagen/__init__.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/volkswagen/__init__.py deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/volkswagen/values.py b/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/volkswagen/values.py deleted file mode 100644 index f5e26bd4d8cbca..00000000000000 --- a/selfdrive/frogpilot/thepond/fixtures/selfdrive_car/volkswagen/values.py +++ /dev/null @@ -1,30 +0,0 @@ -from enum import StrEnum - -class CAR(StrEnum): - ARTEON_MK1 = "VOLKSWAGEN ARTEON 1ST GEN" # Chassis AN, Mk1 VW Arteon and variants - ATLAS_MK1 = "VOLKSWAGEN ATLAS 1ST GEN" # Chassis CA, Mk1 VW Atlas and Atlas Cross Sport - CRAFTER_MK2 = "VOLKSWAGEN CRAFTER 2ND GEN" # Chassis SY/SZ, Mk2 VW Crafter, VW Grand California, MAN TGE - GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants - JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 VW Jetta - PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants - PASSAT_NMS = "VOLKSWAGEN PASSAT NMS" # Chassis A3, North America/China/Mideast NMS Passat, incl. facelift - POLO_MK6 = "VOLKSWAGEN POLO 6TH GEN" # Chassis AW, Mk6 VW Polo - SHARAN_MK2 = "VOLKSWAGEN SHARAN 2ND GEN" # Chassis 7N, Mk2 Volkswagen Sharan and SEAT Alhambra - TAOS_MK1 = "VOLKSWAGEN TAOS 1ST GEN" # Chassis B2, Mk1 VW Taos and Tharu - TCROSS_MK1 = "VOLKSWAGEN T-CROSS 1ST GEN" # Chassis C1, Mk1 VW T-Cross SWB and LWB variants - TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants - TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants - TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California - TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW T-Roc and variants - AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants - AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only) - AUDI_Q3_MK2 = "AUDI Q3 2ND GEN" # Chassis 8U/F3/FS, Mk2 Audi Q3 and variants - SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca - SEAT_LEON_MK3 = "SEAT LEON 3RD GEN" # Chassis 5F, Mk3 SEAT Leon and variants - SKODA_FABIA_MK4 = "SKODA FABIA 4TH GEN" # Chassis PJ, Mk4 Skoda Fabia - SKODA_KAMIQ_MK1 = "SKODA KAMIQ 1ST GEN" # Chassis NW, Mk1 Skoda Kamiq - SKODA_KAROQ_MK1 = "SKODA KAROQ 1ST GEN" # Chassis NU, Mk1 Skoda Karoq - SKODA_KODIAQ_MK1 = "SKODA KODIAQ 1ST GEN" # Chassis NS, Mk1 Skoda Kodiaq - SKODA_SCALA_MK1 = "SKODA SCALA 1ST GEN" # Chassis NW, Mk1 Skoda Scala and Skoda Kamiq - SKODA_SUPERB_MK3 = "SKODA SUPERB 3RD GEN" # Chassis 3V/NP, Mk3 Skoda Superb and variants - SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/fleet_manager_helpers.py b/selfdrive/frogpilot/thepond/fleet_manager_helpers.py deleted file mode 100644 index 13b1a500a43253..00000000000000 --- a/selfdrive/frogpilot/thepond/fleet_manager_helpers.py +++ /dev/null @@ -1,117 +0,0 @@ -# otisserv - Copyright (c) 2019-, Rick Lan, dragonpilot community, and a number of other of contributors. -# Fleet Manager - [actuallylemoncurd](https://github.com/actuallylemoncurd), [AlexandreSato](https://github.com/alexandreSato), [ntegan1](https://github.com/ntegan1), [royjr](https://github.com/royjr), and [sunnyhaibin] (https://github.com/sunnypilot) -# Almost everything else - ChatGPT -# dirty PR pusher - mike8643 -# Thepond author - Aidenir -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -# This file contains all the functionality that I (Aidenir) hasn't updated from -# fleet_manager. Everything in here though I lifted straight out of that project. - -from typing import List -import os -import subprocess - -# If running on comma device -if os.path.exists("/data/persist"): - from openpilot.common.swaglog import cloudlog - from tools.lib.route import SegmentName -else: - from .fixtures.fake_modules.route import SegmentName - -def segment_to_segment_name(data_dir, segment) -> SegmentName: - fake_dongle = "ffffffffffffffff" - return SegmentName(str(os.path.join(data_dir, fake_dongle + "|" + segment))) - -def get_directory_sort(d: str) -> List[str]: - return [s.rjust(10, '0') for s in d.rsplit('--', 1)] - -def get_all_segment_names(footage_path) -> list: - segments = [] - for segment in listdir_by_creation(footage_path): - try: - segments.append(segment_to_segment_name(footage_path, segment)) - except AssertionError: - pass - return segments - -def listdir_by_creation(d: str) -> List[str]: - if not os.path.isdir(d): - print("no such dir" + d) - return [] - - try: - paths = [f for f in os.listdir(d) if os.path.isdir(os.path.join(d, f))] - print(paths) - paths = sorted(paths, key=get_directory_sort) - return paths - except OSError: - cloudlog.exception("listdir_by_creation failed") - return [] - -def get_segments_in_route(route, footage_path) -> list: - segment_names = [segment_name for segment_name in get_all_segment_names(footage_path) if segment_name.time_str == route] - segments = [segment_name.time_str + "--" + str(segment_name.segment_num) for segment_name in segment_names] - return segments - -def get_routes_names(footage_path) -> list: - """ - Returns a list of all the routes in the footage directory, - without the segment number. - """ - segment_names = get_all_segment_names(footage_path) - route_names = [segment_name.route_name for segment_name in segment_names] - route_times = [route_name.time_str for route_name in route_names] - unique_routes = list(dict.fromkeys(route_times)) - return sorted(unique_routes, reverse=True) - -def list_file(path)-> list: - files = os.listdir(path) - sorted_files = sorted(files, reverse=True) - return sorted_files - -def ffmpeg_mp4_wrap_process_builder(filename): - """Returns a process that will wrap the given filename - inside a mp4 container, for easier playback by browsers - and other devices. Primary use case is streaming segment videos - to the vidserver tool. - filename is expected to be a pathname to one of the following - /path/to/a/qcamera.ts - /path/to/a/dcamera.hevc - /path/to/a/ecamera.hevc - /path/to/a/fcamera.hevc - """ - basename = filename.rsplit("/")[-1] - extension = basename.rsplit(".")[-1] - command_line = ["ffmpeg"] - if extension == "hevc": - command_line += ["-f", "hevc"] - command_line += ["-r", "20"] - command_line += ["-i", filename] - command_line += ["-c", "copy"] - command_line += ["-map", "0"] - if extension == "hevc": - command_line += ["-vtag", "hvc1"] - command_line += ["-f", "mp4"] - command_line += ["-movflags", "empty_moov"] - command_line += ["-"] - return subprocess.Popen( - command_line, stdout=subprocess.PIPE - ) \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/params.json b/selfdrive/frogpilot/thepond/params.json deleted file mode 100644 index ed711185775732..00000000000000 --- a/selfdrive/frogpilot/thepond/params.json +++ /dev/null @@ -1,998 +0,0 @@ -{ - "ApiCache_DriveStats": { - "value": { - "all": { - "distance": 0, - "hours": 0, - "drives": 0 - }, - "week": { - "distance": 0, - "hours": 0, - "drives": 0 - } - } - }, - "LastGPSPosition": {}, - "NavDestination": {}, - "ApiCache_NavDestinations": {}, - "OpenpilotEnabledToggle": { - "title": "Enable openpilot", - "description": "Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.", - "section": "toggles", - "type": "toggle" - }, - "ExperimentalLongitudinalEnabled": { - "title": "openpilot Longitudinal Control (Alpha)", - "description": "WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).

On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.", - "section": "toggles", - "type": "toggle" - }, - "ExperimentalMode": { - "title": "Experimental Mode", - "description": "Openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. Experimental features are listed below:

End-to-End Longitudinal Control
Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected.

Navigate on openpilot
When navigation has a destination, openpilot will input the map information into the model. This provides useful context for the model and allows openpilot to keep left or right appropriately at forks/exits. Lane change behavior is unchanged and still activated by the driver. This is an alpha quality feature; mistakes should be expected, particularly around exits and forks. These mistakes can include unintended lane line crossings, late exit taking, driving towards dividing barriers in the gore areas, etc.

New Driving Visualization
The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green.
", - "section": "toggles", - "type": "toggle" - }, - "DisengageOnAccelerator": { - "title": "Disengage on Accelerator Pedal", - "description": "When enabled, pressing the accelerator pedal will disengage openpilot.", - "section": "toggles", - "type": "toggle" - }, - "IsLdwEnabled": { - "title": "Enable Lane Departure Warnings", - "description": "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h).", - "section": "toggles", - "type": "toggle" - }, - "RecordFront": { - "title": "Record and Upload Driver Camera", - "description": "Upload data from the driver-facing camera and help improve the driver monitoring algorithm.", - "section": "toggles", - "type": "toggle" - }, - "IsMetric": { - "title": "Use Metric System", - "description": "Display speed in km/h instead of mph.", - "section": "toggles", - "type": "toggle" - }, - "NavSettingTime24h": { - "title": "Show ETA in 24h Format", - "description": "Use 24h format instead of am/pm", - "section": "toggles", - "type": "toggle" - }, - "NavSettingLeftSide": { - "title": "Show Map on Left Side of UI", - "description": "Show map on left side when in split-screen view.", - "section": "toggles", - "type": "toggle" - }, - "AutomaticUpdates": { - "title": "Automatically Update FrogPilot", - "description": "FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi.", - "section": "software", - "type": "toggle" - }, - "AlwaysOnLateral": { - "title": "Always on Lateral", - "description": "Maintain openpilot lateral control when the brake or gas pedals are used. Deactivation occurs only through the 'Cruise Control' button.", - "section": "controls", - "type": "toggle", - "subsettings": { - "AlwaysOnLateralLKAS": { - "title": "Control Via LKAS Button", - "description": "Enable or disable 'Always On Lateral' by clicking your 'LKAS' button.", - "section": "controls", - "type": "toggle" - }, - "AlwaysOnLateralMain": { - "title": "Enable On Cruise Main", - "description": "Enable 'Always On Lateral' by clicking your 'Cruise Control' button without requiring openpilot to be enabled first.", - "section": "controls", - "type": "toggle" - }, - "PauseAOLOnBrake": { - "title": "Pause On Brake Below", - "description": "Pause 'Always On Lateral' when the brake pedal is being pressed below the set speed.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "HideAOLStatusBar": { - "title": "Hide the Status Bar", - "description": "Don't use the status bar for 'Always On Lateral'.", - "section": "controls", - "type": "toggle" - } - } - }, - "ConditionalExperimental": { - "title": "Conditional Experimental Mode", - "description": "Automatically switches to 'Experimental Mode' under predefined conditions.", - "section": "controls", - "type": "toggle", - "subsettings": { - "CESpeed": { - "title": "Below", - "description": "Switch to 'Experimental Mode' below this speed when not following a lead vehicle.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "CESpeedLead": { - "title": "Below With Lead", - "description": "Switch to 'Experimental Mode' below this speed when following a lead vehicle.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "CECurves": { - "title": "Curve Detected Ahead", - "description": "Switch to 'Experimental Mode' when a curve is detected.", - "section": "controls", - "type": "toggle", - "toggles": { - "CECurvesLead": { - "title": "With Lead", - "section": "controls", - "type": "toggle" - } - } - }, - "CELead": { - "title": "Lead Detected Ahead", - "description": "Switch to 'Experimental Mode' when a slower or stopped lead vehicle is detected ahead.", - "section": "controls", - "type": "toggle", - "toggles": { - "CESlowerLead": { - "title": "Slower Lead", - "section": "controls", - "type": "toggle" - }, - "CEStoppedLead": { - "title": "Stopped Lead", - "section": "controls", - "type": "toggle" - } - } - }, - "CEModelStopTime": { - "title": "Model Wants To Stop In The Next", - "description": "Switch to 'Experimental Mode' when the model wants to stop like when it detects a stop light or stop sign.", - "section": "controls", - "type": "number", - "min": 1, - "max": 10, - "unit": "seconds" - }, - "CENavigation": { - "title": "Navigation Based", - "description": "Switch to 'Experimental Mode' based on navigation data. (i.e. Intersections, stop signs, upcoming turns, etc.)", - "section": "controls", - "type": "toggle", - "toggles": { - "CENavigationIntersections": { - "title": "Intersections", - "section": "controls", - "type": "toggle" - }, - "CENavigationTurns": { - "title": "Turns", - "section": "controls", - "type": "toggle" - }, - "CENavigationLead": { - "title": "With Lead", - "section": "controls", - "type": "toggle" - } - } - }, - "CESignal": { - "title": "Turn Signal When Below Highway Speeds", - "description": "Switch to 'Experimental Mode' when using turn signals below highway speeds to help assist with turns.", - "section": "controls", - "type": "toggle" - }, - "HideCEMStatusBar": { - "title": "Hide the Status Bar", - "description": "Don't use the status bar for 'Conditional Experimental Mode'.", - "section": "controls", - "type": "toggle" - } - } - }, - "DeviceManagement": { - "title": "Device Management", - "description": "Tweak your device's behaviors to your personal preferences.", - "section": "controls", - "type": "toggle", - "subsettings": { - "DeviceShutdown": { - "title": "Device Shutdown Timer", - "description": "Configure how quickly the device shuts down after going offroad.", - "section": "controls", - "type": "select", - "options": [ - "Instant", - "15 mins", - "30 mins", - "45 mins", - "1 hour", "2 hours", "3 hours", "4 hours", "5 hours", "6 hours", "7 hours", "8 hours", "9 hours", "10 hours", - "11 hours", "12 hours", "13 hours", "14 hours", "15 hours", "16 hours", "17 hours", "18 hours", "19 hours", "20 hours", - "21 hours", "22 hours", "23 hours", "24 hours", "25 hours", "26 hours", "27 hours", "28 hours", "29 hours", "30 hours" - ] - }, - "NoLogging": { - "title": "Disable Logging", - "description": "Turn off all data tracking to enhance privacy or reduce thermal load.", - "section": "controls", - "type": "toggle" - }, - "NoUploads": { - "title": "Disable Uploads", - "description": "Turn off all data uploads to comma's servers.", - "section": "controls", - "type": "toggle", - "toggles": { - "DisableOnroadUploads": { - "title": "Only Onroad", - "section": "controls", - "type": "toggle" - } - } - }, - "IncreaseThermalLimits": { - "title": "Increase Thermal Safety Limit", - "description": "Allow the device to run at a temperature above comma's recommended thermal limits.", - "section": "controls", - "type": "toggle" - }, - "LowVoltageShutdown": { - "title": "Low Voltage Shutdown Threshold", - "description": "Automatically shut the device down when your battery reaches a specific voltage level to prevent killing your battery.", - "section": "controls", - "type": "number", - "min": 1, - "max": 10 - }, - "OfflineMode": { - "title": "Offline Mode", - "description": "Allow the device to be offline indefinitely.", - "section": "controls", - "type": "toggle" - } - } - }, - "DrivingPersonalities": { - "title": "Driving Personalities", - "description": "Manage the driving behaviors of comma's 'Personality Profiles'.", - "section": "controls", - "type": "toggle", - "subsettings": { - "CustomPersonalities": { - "title": "Customize Personalities", - "description": "Customize the driving personality profiles to your driving style.", - "section": "controls", - "type": "toggle", - "toggles": { - "TrafficPersonalityProfile": { - "title": "Traffic Personality", - "description": "Customize the 'Traffic' personality profile.", - "section": "controls", - "type": "toggle", - "toggles": { - "TrafficFollow": { - "title": "Following Distance", - "description": "Set the minimum following distance when using 'Traffic Mode'. Your following distance will dynamically adjust between this distance and the following distance from the 'Aggressive' profile.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5, - "unit": "seconds" - }, - "TrafficJerkAcceleration": { - "title": "Acceleration Jerk", - "description": "Customize the acceleration jerk when using 'Traffic Mode'.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "TrafficJerkDanger": { - "title": "Danger Zone Jerk", - "description": "Customize the danger zone jerk when using the 'Traffic' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "TrafficJerkSpeed": { - "title": "Speed Control Jerk", - "description": "Customize the speed control jerk when using 'Traffic Mode'.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - } - } - }, - "AggressivePersonalityProfile": { - "title": "Aggressive Personality", - "description": "Customize the 'Aggressive' personality profile.", - "section": "controls", - "type": "toggle", - "toggles": { - "AggressiveFollow": { - "title": "Following Distance", - "description": "Set the 'Aggressive' personality following distance. Represents seconds to follow behind the lead vehicle. Stock: 1.25 seconds.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5, - "unit": "seconds" - }, - "AggressiveJerkAcceleration": { - "title": "Acceleration Jerk", - "description": "Customize the acceleration jerk when using the 'Aggressive' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "AggressiveJerkDanger": { - "title": "Danger Zone Jerk", - "description": "Customize the danger zone jerk when using the 'Aggressive' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "AggressiveJerkSpeed": { - "title": "Speed Control Jerk", - "description": "Customize the speed control jerk when using the 'Aggressive' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - } - } - }, - "StandardPersonalityProfile": { - "title": "Standard Personality", - "description": "Customize the 'Standard' personality profile.", - "section": "controls", - "type": "toggle", - "toggles": { - "StandardFollow": { - "title": "Following Distance", - "description": "Set the 'Standard' personality following distance. Represents seconds to follow behind the lead vehicle. Stock: 1.45 seconds.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5, - "unit": "seconds" - }, - "StandardJerkAcceleration": { - "title": "Acceleration Jerk", - "description": "Customize the acceleration jerk when using the 'Standard' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "StandardJerkDanger": { - "title": "Danger Zone Jerk", - "description": "Customize the danger zone jerk when using the 'Standard' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "StandardJerkSpeed": { - "title": "Speed Control Jerk", - "description": "Customize the speed control jerk when using the 'Standard' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - } - } - }, - "RelaxedPersonalityProfile": { - "title": "Relaxed Personality", - "description": "Customize the 'Relaxed' personality profile.", - "section": "controls", - "type": "toggle", - "toggles": { - "RelaxedFollow": { - "title": "Following Distance", - "description": "Set the 'Relaxed' personality following distance. Represents seconds to follow behind the lead vehicle. Stock: 1.75 seconds.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5, - "unit": "seconds" - }, - "RelaxedJerkAcceleration": { - "title": "Acceleration Jerk", - "description": "Customize the acceleration jerk when using the 'Relaxed' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "RelaxedJerkDanger": { - "title": "Danger Zone Jerk", - "description": "Customize the danger zone jerk when using the 'Relaxed' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - }, - "RelaxedJerkSpeed": { - "title": "Speed Control Jerk", - "description": "Customize the speed control jerk when using the 'Relaxed' personality.", - "section": "controls", - "type": "number", - "min": 1, - "max": 5 - } - } - } - } - }, - "OnroadDistanceButton": { - "title": "Onroad Distance Button", - "description": "Simulate a distance button via the onroad UI to control personalities, 'Experimental Mode', and 'Traffic Mode'.", - "section": "controls", - "type": "toggle" - } - } - }, - "ExperimentalModeActivation": { - "title": "Experimental Mode Activation", - "description": "Toggle Experimental Mode with either buttons on the steering wheel or the screen. Overrides 'Conditional Experimental Mode'.", - "section": "controls", - "type": "toggle", - "subsettings": { - "ExperimentalModeViaLKAS": { - "title": "Click LKAS Button", - "description": "Enable/disable 'Experimental Mode' by clicking the 'LKAS' button on your steering wheel.", - "section": "controls", - "type": "toggle" - }, - "ExperimentalModeViaTap": { - "title": "Double Tap the UI", - "description": "Enable/disable 'Experimental Mode' by double tapping the onroad UI within a 0.5 second time frame.", - "section": "controls", - "type": "toggle" - }, - "ExperimentalModeViaDistance": { - "title": "Long Press Distance", - "description": "Enable/disable 'Experimental Mode' by holding down the 'distance' button on your steering wheel for 0.5 seconds.", - "section": "controls", - "type": "toggle" - } - } - }, - "LaneChangeCustomizations": { - "title": "Lane Change Customizations", - "description": "Customize the lane change behaviors in openpilot.", - "section": "controls", - "type": "toggle", - "subsettings": { - "LaneChangeTime": { - "title": "Lane Change Timer", - "description": "Set a delay before executing a lane change.", - "section": "controls", - "type": "number", - "min": 0, - "max": 5, - "unit": "seconds" - }, - "LaneDetectionWidth": { - "title": "Lane Detection Threshold", - "description": "Set the required lane width to be qualified as a lane.", - "section": "controls", - "type": "number", - "min": 0, - "max": 10, - "unit": "meters" - }, - "MinimumLaneChangeSpeed": { - "title": "Minimum Lane Change Speed", - "description": "Customize the minimum driving speed to allow openpilot to change lanes.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "NudgelessLaneChange": { - "title": "Nudgeless Lane Change", - "description": "Enable lane changes without requiring manual steering input.", - "section": "controls", - "type": "toggle" - }, - "OneLaneChange": { - "title": "One Lane Change Per Signal", - "description": "Only allow one lane change per turn signal activation.", - "section": "controls", - "type": "toggle" - } - } - }, - "LateralTune": { - "title": "Lateral Tuning", - "description": "Modify openpilot's steering behavior.", - "section": "controls", - "type": "toggle", - "subsettings": { - "ForceAutoTune": { - "title": "Force Auto Tune", - "description": "Forces comma's auto lateral tuning for unsupported vehicles.", - "section": "controls", - "type": "toggle" - }, - "NNFF": { - "title": "NNFF", - "description": "Use Twilsonco's Neural Network Feedforward for enhanced precision in lateral control.", - "section": "controls", - "type": "toggle" - }, - "NNFFLite": { - "title": "NNFF-Lite", - "description": "Use Twilsonco's Neural Network Feedforward for enhanced precision in lateral control for cars without available NNFF logs.", - "section": "controls", - "type": "toggle" - }, - "SteerRatio": { - "title": "Steer Ratio", - "description": "Use a custom steer ratio as opposed to comma's auto tune value.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99 - }, - "TacoTune": { - "title": "Taco Tune", - "description": "Use comma's 'Taco Tune' designed for handling left and right turns.", - "section": "controls", - "type": "toggle" - }, - "TurnDesires": { - "title": "Use Turn Desires", - "description": "Use turn desires for greater precision in turns below the minimum lane change speed.", - "section": "controls", - "type": "toggle" - } - } - }, - "LongitudinalTune": { - "title": "Longitudinal Tuning", - "description": "Modify openpilot's acceleration and braking behavior.", - "section": "controls", - "type": "toggle", - "subsettings": { - "AccelerationProfile": { - "title": "Acceleration Profile", - "description": "Change the acceleration rate to be either sporty or eco-friendly.", - "section": "controls", - "type": "select", - "options": [ - "Standard", - "Eco", - "Sport", - "Sport+" - ] - }, - "DecelerationProfile": { - "title": "Deceleration Profile", - "description": "Change the deceleration rate to be either sporty or eco-friendly.", - "section": "controls", - "type": "select", - "options": [ - "Standard", - "Eco", - "Sport" - ] - }, - "HumanAcceleration": { - "title": "Human-Like Acceleration", - "description": "Tweaks the acceleration behavior to be more 'human-like'.", - "section": "controls", - "type": "toggle" - }, - "HumanFollowing": { - "title": "Human-Like Following Distance", - "description": "Tweaks the following distance dynamically to be more 'human-like' when coming up behind slower/stopped leads or following faster leads.", - "section": "controls", - "type": "toggle" - }, - "StoppingDistance": { - "title": "Increase Stop Distance Behind Lead", - "description": "Increase the stopping distance for a more comfortable stop from lead vehicles.", - "section": "controls", - "type": "number", - "min": 0, - "max": 10, - "unit": "meters" - }, - "LeadDetectionThreshold": { - "title": "Lead Detection Threshold", - "description": "Increase or decrease the lead detection threshold to either detect leads sooner, or increase model confidence.", - "section": "controls", - "type": "number", - "min": 1, - "max": 100, - "unit": "%" - }, - } - }, - "MTSCEnabled": { - "title": "Map Turn Speed Control", - "description": "Slow down for anticipated curves detected by the downloaded maps.", - "section": "controls", - "type": "toggle", - "subsettings": { - "MTSCCurvatureCheck": { - "title": "Model Curvature Detection Failsafe", - "description": "Only trigger MTSC when the model detects a curve in the road. Purely used as a failsafe to prevent false positives. Leave this off if you never experience false positives.", - "section": "controls", - "type": "toggle" - }, - } - }, - "ModelManagement": { - "title": "Model Management", - "description": "Manage openpilot's driving models.", - "section": "controls", - "type": "toggle", - "subsettings": { - "AutomaticallyUpdateModels": { - "title": "Automatically Update and Download Models", - "description": "Automatically download models as they're updated or added to the model list.", - "section": "controls", - "type": "toggle" - }, - "ModelRandomizer": { - "title": "Model Randomizer", - "description": "Have a random model be selected each drive that can be reviewed at the end of each drive to find your preferred model.", - "section": "controls", - "type": "toggle" - } - } - }, - "QOLControls": { - "title": "Quality of Life", - "description": "Miscellaneous quality of life changes to improve your overall openpilot experience.", - "section": "controls", - "type": "toggle", - "subsettings": { - "CustomCruise": { - "title": "Cruise Increase Interval", - "description": "Set a custom interval to increase the max set speed by.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "CustomCruiseLong": { - "title": "Cruise Increase Interval (Long Press)", - "description": "Set a custom interval to increase the max set speed by when holding down the cruise increase button.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - }, - "ForceStandstill": { - "title": "Force Standstill State", - "description": "Keeps openpilot in the 'standstill' state until the gas pedal is pressed.", - "section": "controls", - "type": "toggle" - }, - "MapGears": { - "title": "Map Accel/Decel To Gears", - "description": "Map your acceleration/deceleration profile to your 'Eco' and/or 'Sport' gears.", - "section": "controls", - "type": "toggle", - "toggles": { - "MapAcceleration": { - "title": "Acceleration", - "section": "controls", - "type": "toggle" - }, - "MapDeceleration": { - "title": "Deceleration", - "section": "controls", - "type": "toggle" - } - } - }, - "PauseLateralSpeed": { - "title": "Pause Lateral Below", - "description": "Pause lateral control on all speeds below the set speed.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph", - "toggles": { - "PauseLateralOnSignal": { - "title": "Turn Signal Only", - "section": "controls", - "type": "toggle" - } - } - }, - "ReverseCruise": { - "title": "Reverse Cruise Increase", - "description": "Reverses the 'long press' functionality logic to increase the max set speed by 5 instead of 1. Useful to increase the max speed quickly.", - "section": "controls", - "type": "toggle" - }, - "SetSpeedOffset": { - "title": "Set Speed Offset", - "description": "Set an offset for your desired set speed.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "kph" - } - } - }, - "SpeedLimitController": { - "title": "Speed Limit Controller", - "description": "Automatically adjust the max speed to match the current speed limit using 'Open Street Maps', 'Navigate On openpilot', or your car's dashboard (Toyotas/Lexus/HKG only).", - "section": "controls", - "type": "toggle", - "subsettings": { - "Offset1": { - "title": "Speed Limit Offset (0-34 mph)", - "description": "Speed limit offset for speed limits between 0-34 mph.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "mph" - }, - "Offset2": { - "title": "Speed Limit Offset (35-54 mph)", - "description": "Speed limit offset for speed limits between 35-54 mph.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "mph" - }, - "Offset3": { - "title": "Speed Limit Offset (55-64 mph)", - "description": "Speed limit offset for speed limits between 55-64 mph.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "mph" - }, - "Offset4": { - "title": "Speed Limit Offset (65-99 mph)", - "description": "Speed limit offset for speed limits between 65-99 mph.", - "section": "controls", - "type": "number", - "min": 0, - "max": 99, - "unit": "mph" - }, - "SLCFallback": { - "title": "Fallback Method", - "description": "Choose your fallback method when there is no speed limit available.", - "section": "controls", - "type": "toggle", - "type": "select", - "options": [ - "Set Speed", - "Experimental Mode", - "Previous Limit" - ] - }, - "SLCOverride": { - "title": "Override Method", - "description": "Choose your preferred method to override the current speed limit.", - "section": "controls", - "type": "toggle", - "type": "select", - "options": [ - "None", - "Manual Set Speed", - "Set Speed" - ] - }, - "SLCPriority1": { - "title": "Priority Order - Primary", - "description": "Self your primary speed limit priority.", - "section": "controls", - "type": "toggle", - "type": "select", - "options": [ - "None", - "Dashboard", - "Navigation", - "Offline Maps", - "Highest", - "Lowest" - ] - }, - "SLCPriority2": { - "title": "Priority Order - Secondary", - "description": "Self your secondary speed limit priority.", - "section": "controls", - "type": "toggle", - "type": "select", - "options": [ - "None", - "Dashboard", - "Navigation", - "Offline Maps", - "Highest", - "Lowest" - ] - }, - "SLCPriority3": { - "title": "Priority Order - Tertiary", - "description": "Self your tertiary speed limit priority.", - "section": "controls", - "type": "toggle", - "type": "select", - "options": [ - "None", - "Dashboard", - "Navigation", - "Offline Maps", - "Highest", - "Lowest" - ] - }, - "SLCConfirmation": { - "title": "Confirm New Speed Limits", - "description": "Don't automatically start using the new speed limit until it's been manually confirmed.", - "section": "controls", - "type": "toggle", - "toggles": { - "SLCConfirmationLower": { - "title": "Lower Limits", - "section": "controls", - "type": "toggle" - }, - "SLCConfirmationHigher": { - "title": "Higher Limits", - "section": "controls", - "type": "toggle" - } - } - }, - "ForceMPHDashboard": { - "title": "Force MPH From Dashboard Readings", - "description": "Force MPH readings from the dashboard. Only use this if you live in an area where the speed limits from your dashboard are in KPH, but you use MPH.", - "section": "controls", - "type": "toggle" - }, - "SLCLookaheadHigher": { - "title": "Prepare For Higher Speed Limits", - "description": "Set a 'lookahead' value to prepare for upcoming speed limits higher than your current speed limit using the data stored in 'Open Street Maps'.", - "section": "controls", - "type": "number", - "min": 0, - "max": 60, - "unit": "seconds" - }, - "SLCLookaheadLower": { - "title": "Prepare For Lower Speed Limits", - "description": "Set a 'lookahead' value to prepare for upcoming speed limits lower than your current speed limit using the data stored in 'Open Street Maps'.", - "section": "controls", - "type": "number", - "min": 0, - "max": 60, - "unit": "seconds" - }, - "SetSpeedLimit": { - "title": "Use Current Speed Limit As Set Speed", - "description": "Sets your max speed to the current speed limit if one is populated when you initially enable openpilot.", - "section": "controls", - "type": "toggle" - }, - "ShowSLCOffset": { - "title": "Show Speed Limit Offset", - "description": "Show the speed limit offset separated from the speed limit in the onroad UI when using 'Speed Limit Controller'.", - "section": "controls", - "type": "toggle", - "toggles": { - "ShowSLCOffsetUI": { - "title": "Control Via UI", - "section": "controls", - "type": "toggle" - } - } - }, - "SpeedLimitChangedAlert": { - "title": "Speed Limit Changed Alert", - "description": "Trigger an alert whenever the speed limit changes.", - "section": "controls", - "type": "toggle" - }, - "UseVienna": { - "title": "Use Vienna Speed Limit Signs", - "description": "Use the Vienna (EU) speed limit style signs as opposed to MUTCD (US).", - "section": "controls", - "type": "toggle" - } - } - }, - "VisionTurnControl": { - "title": "Vision Turn Speed Controller", - "description": "Slow down for detected curves in the road.", - "section": "controls", - "type": "toggle", - "subsettings": { - "CurveSensitivity": { - "title": "Curve Detection Sensitivity", - "description": "Set curve detection sensitivity. Higher values prompt earlier responses, lower values lead to smoother but later reactions.", - "section": "controls", - "type": "number", - "min": 1, - "max": 100, - "unit": "%" - }, - "TurnAggressiveness": { - "title": "Turn Speed Aggressiveness", - "description": "Set turn speed aggressiveness. Higher values result in faster turns, lower values yield gentler turns.", - "section": "controls", - "type": "number", - "min": 1, - "max": 100, - "unit": "%" - } - } - }, - "MapboxPublicKey": { - "title": "Public Mapbox Key", - "section": "navigation", - "type": "text" - }, - "MapboxSecretKey": { - "title": "Secret Mapbox Key", - "section": "navigation", - "type": "text" - }, - "CarMake": { - "title": "Car Make", - "section": "vehicles", - "type": "select", - "options": [ - "Acura", "Audi", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "Genesis", - "GMC", "Holden", "Honda", "Hyundai", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda", - "Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen" - ] - }, - "CarModel": { - "title": "Car Model", - "section": "vehicles", - "type": "select", - "options": [ - "" - ] - } -} diff --git a/selfdrive/frogpilot/thepond/start.sh b/selfdrive/frogpilot/thepond/start.sh deleted file mode 100644 index 79c555e238448c..00000000000000 --- a/selfdrive/frogpilot/thepond/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd .. -python3 -m thepond.thepond \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/templates/index.html b/selfdrive/frogpilot/thepond/templates/index.html deleted file mode 100644 index d9f89e5bfdd32b..00000000000000 --- a/selfdrive/frogpilot/thepond/templates/index.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ThePond - - - -
- - - -
- - - \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/templates/playground.html b/selfdrive/frogpilot/thepond/templates/playground.html deleted file mode 100644 index 06e09f7f561735..00000000000000 --- a/selfdrive/frogpilot/thepond/templates/playground.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - ThePond - - - -
- - - -
- - - \ No newline at end of file diff --git a/selfdrive/frogpilot/thepond/thepond.py b/selfdrive/frogpilot/thepond/thepond.py deleted file mode 100644 index d126aa803cd59a..00000000000000 --- a/selfdrive/frogpilot/thepond/thepond.py +++ /dev/null @@ -1,228 +0,0 @@ -from flask import Flask, render_template, Response, request, send_from_directory -from datetime import datetime - -import json -import re -import secrets - -from . import utils -from . import fleet_manager_helpers - -RUNNING_ON_COMMA = utils.is_running_on_comma() - -# Import comma code if running on comma device -if RUNNING_ON_COMMA: - from openpilot.common.realtime import set_core_affinity - from openpilot.common.swaglog import cloudlog - -app = Flask(__name__) - -def setup(app): - app = app - - @app.errorhandler(404) - def not_found(e): - return render_template("index.html") - - @app.route("/") - def index(): - return render_template("index.html") - - @app.route("/playground") - def playground(): - return render_template("playground.html") - - @app.route('/assets/') - def send_assets(path): - """ - Not the best strategy for serving static files, but it works for now. - """ - return send_from_directory('assets', path) - - @app.route("/api/settings") - def get_settings(): - return utils.load_settings() - - @app.route("/api/settings/", methods=["POST"]) - def save_settings(key): - body = request.json - params = utils.load_settings() - param = utils.find_setting(key, params) - if param == None: - return {"message": "Invalid key"}, 400 - - newValue = body.get("value") - if isinstance(param.get("value"), int) and not isinstance(newValue, int): - return {"message": "Invalid value, a number is required"}, 400 - - utils.save_setting(key, newValue) - return {"message": "Settings saved"} - - - @app.route("/api/stats") - def get_stats(): - drives, drive_errors = utils.get_drive_stats() - disk, disk_error = utils.get_disk_usage() - result = { - "driveStats": drives, - "diskUsage": disk, - } - if drive_errors is not None: - result["driveErrors"] = drive_errors - if disk_error is not None: - result["diskError"] = disk_error - return result - - - # Error logs start here - @app.route("/api/error-logs") - def get_error_logs(): - if request.accept_mimetypes['text/html']: - return render_template("v2/error-logs.jinja", active="error_logs") - elif request.accept_mimetypes['application/json']: - errorLogs=fleet_manager_helpers.list_file(utils.ERROR_LOGS_PATH) - return errorLogs, 200 - - - @app.route("/api/error-logs/") - def get_error_log(filename): - with open(f"{utils.ERROR_LOGS_PATH}{filename}") as f: - content = f.read() - return content, 200 - # Error logs end here - - @app.route("/api/navigation", methods=["GET"]) - def navigation(): - params = utils.load_settings() - return { - "mapboxToken": utils.find_setting("MapboxPublicKey", params).get("value", None), - "lastPosition": utils.find_setting("LastGPSPosition", params).get("value", None), - "destination": utils.find_setting("NavDestination", params).get("value", None), - "previousDestinations": utils.find_setting("ApiCache_NavDestinations", params).get("value", None), - } - - @app.route("/api/navigation", methods=["POST"]) - def set_nav_destination(): - body = request.json - utils.save_setting("NavDestination", json.dumps(body)) - return {"message": "Destination set"} - - @app.route("/api/navigation", methods=["DELETE"]) - def clear_nav_destination(): - utils.save_setting("NavDestination", 0) # undefined/None is not valid json - return {"message": "Destination cleared"} - - ## Routes/Dashcam endpoints start here - @app.route("/api/routes") - def v2_routes(): - route_names = fleet_manager_helpers.get_routes_names(utils.FOOTAGE_PATH) - routes = [] - # Regex to match route names in the format "00000009--53e66ae16d" - valid_format_regex = re.compile(r'^[0-9a-fA-F]{8}--[0-9a-fA-F]{10}$') - - for route_name in route_names: - # Since the format is not a date, we skip date parsing and just use the route name - first_segment_path = f"{utils.FOOTAGE_PATH}{route_name}--0" - qcamera_path = f"{first_segment_path}/qcamera.ts" - gif_path = f"{first_segment_path}/preview.gif" - png_path = f"{first_segment_path}/preview.png" - - # Ensure thumbnails are created if they don't exist - try: - utils.video_to_png(qcamera_path, png_path) - utils.video_to_gif(qcamera_path, gif_path) - except Exception as e: - print(f"Failed to generate thumbnails for {route_name}") - print(e) - - routes.append({ - "name": route_name, - "gif": f"/thumbnails/{route_name}--0/preview.gif", - "png": f"/thumbnails/{route_name}--0/preview.png" - }) - - return routes, 200 - - @app.route("/api/routes/") - def v2_route(name): - route_date = datetime.strptime(name, '%Y-%m-%d--%H-%M-%S') - segment_urls = [] - for segment in fleet_manager_helpers.get_segments_in_route(name, utils.FOOTAGE_PATH): - segment_urls.append(f"/video/{segment}") - lastSegmentDuration = utils.get_video_duration(f"{utils.FOOTAGE_PATH}{name}--{len(segment_urls)-1}/qcamera.ts") - total_duration = round(lastSegmentDuration + ((len(segment_urls) - 1) * 60)) - available_cameras = utils.get_available_cameras(f"{utils.FOOTAGE_PATH}{name}--0") - route_data = { - "name": name, - "segment_urls": segment_urls, - "total_duration": total_duration, - "date": route_date, - "available_cameras": available_cameras - } - return route_data, 200 - - @app.route("/thumbnails/", methods=['GET']) - def find_previewgif(file_path): - directory = utils.FOOTAGE_PATH - return send_from_directory(directory, file_path, as_attachment=True) - - @app.route("/video/") - def v2_video_file(path): - video_file = "qcamera.ts" # default to qcamera (same as fcamera, just lower quality) - if request.args.get("camera") == "driver": - video_file = "dcamera.hevc" - elif request.args.get("camera") == "wide": - video_file = "ecamera.hevc" - filepath = f"{utils.FOOTAGE_PATH}{path}/{video_file}" - return Response(fleet_manager_helpers.ffmpeg_mp4_wrap_process_builder(filepath).stdout.read(), status=200, mimetype='video/mp4') - - return - - ## Settings endpoints start here - @app.route("/6settings/
") - def settings(section): - params = utils.load_settings() - # Special case for vehicle settings - brands_and_models = None - if section == "vehicles": - brands_and_models = utils.get_all_car_models() - selected_model = params["CarModel"]["value"] - brand = selected_model.split(" ")[0] - params["CarModel"]["options"] = brands_and_models[brand] - params["CarModel"]["value"] = " ".join(selected_model.split(" ")[1:]) - - # Convert the params dict to a list of dicts and filter out the ones that don't belong to the current section - relevant_params = [param for param in params.values() if param.get("section", None) == section] - return render_template("v2/settings.jinja", active=f"settings_{section}", settings=relevant_params, section=section, brands_and_models=brands_and_models) - - - - - - # Navigation ends here - - - - -setup(app) - -def main(): - if RUNNING_ON_COMMA: - try: - set_core_affinity([0, 1, 2, 3]) - except Exception: - cloudlog.exception("the_pond: failed to set core affinity") - - debug = False - port = 8083 - if not RUNNING_ON_COMMA or __package__ == "thepond": - debug = True - port = 8084 - - print(f"The Pond is{' not' if not RUNNING_ON_COMMA else ''} running on comma device, in debug mode {debug}") - app.secret_key = secrets.token_hex(32) - app.run(host="0.0.0.0", port=port, debug=debug) - - -if __name__ == '__main__': - main() diff --git a/selfdrive/frogpilot/thepond/utils.py b/selfdrive/frogpilot/thepond/utils.py deleted file mode 100644 index c8718a21caa98e..00000000000000 --- a/selfdrive/frogpilot/thepond/utils.py +++ /dev/null @@ -1,295 +0,0 @@ -import subprocess -import os -import json -import importlib -import pathlib -import sys -import time -import shutil -import traceback - -from openpilot.common.conversions import Conversions as CV -from openpilot.system.hardware import HARDWARE -from openpilot.system.loggerd.config import get_available_bytes, get_used_bytes - -from openpilot.selfdrive.frogpilot.frogpilot_functions import update_frogpilot_toggles - -def is_running_on_comma(): - return os.path.exists("/data/persist") - -RUNNING_ON_COMMA = is_running_on_comma() - -DIR_OF_THIS_FILE = pathlib.Path(__file__).parent.absolute() -DATA_PATH = "" # On device /data is from the root -CAR_VALUES_PATH = f"{DIR_OF_THIS_FILE}/../../car" -""" Used to find models of all car brands """ - -if RUNNING_ON_COMMA: - # Do this to import the car modules. Let me know if there is a better way. - sys.path.append(f"{DIR_OF_THIS_FILE}/../..") # Adds higher directory to python modules path. - from common.params import Params - params = Params() - params_memory = Params("/dev/shm/params") - params_tracking = Params("/persist/tracking") -else: - # If running this on a computer, there is a bunch of fake stuff inside fixtures that - # will substitute openpilot stuff - DATA_PATH = f"{DIR_OF_THIS_FILE}/fixtures" - CAR_VALUES_PATH = f"{DIR_OF_THIS_FILE}/fixtures/selfdrive_car" - from .fixtures.fake_modules.params import Params - params = Params() - params_memory = params - params_tracking = params - -SCREENRECORD_PATH = f"{DATA_PATH}/data/media/0/videos/" -ERROR_LOGS_PATH = f"{DATA_PATH}/data/crashes/" -FOOTAGE_PATH = f"{DATA_PATH}/data/media/0/realdata/" - -def video_to_gif(input_path, output_path) -> None: - """ - Creates a looping gif from the input_path sped - up by a factor of 35. - """ - if os.path.exists(output_path): - return - - print(f"Create gif from video: {input_path}") - # First create a sped up mp4 - sped_up_path = output_path.replace(".gif", ".mp4") - print(f"Create temporary sped up mp4: {sped_up_path}") - run_ffmpeg(['-i', input_path, '-an', '-vf', 'setpts=PTS/35', sped_up_path]) - - # Next create a gif from the sped up mp4 - print(f"Create gif from sped up mp4: {output_path}") - run_ffmpeg(["-i", sped_up_path, "-loop", "0", output_path]) - # Finally remove the sped up mp4 - os.remove(sped_up_path) - -def video_to_png(input_path, output_path) -> None: - """ - Creates a single frame png from the input_path - """ - if os.path.exists(output_path): - return - run_ffmpeg(['-i', input_path, '-ss', '2', '-vframes', '1', output_path]) - print(f"PNG file created at: {output_path}") - -def get_video_duration(input_path) -> float: - """ - Returns the duration of the video in seconds - """ - result = subprocess.run(['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', input_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - return float(result.stdout) - -def run_ffmpeg(args) -> None: - """ - Just a wrapper around the ffmpeg command, that hides - all the verbose output and passss the -y flag to - overwrite the output file if it already exists. - """ - subprocess.run(['ffmpeg', '-hide_banner', '-loglevel', 'error', '-y'] + args) - -def get_available_cameras(segment_path) -> list: - """ - Checks the segment_path for camera files and returns a list with - "forward", "wide", "driver" depending on if qcamera.ts, ecamera.hevc and dcamera.ts - are present. - """ - available_cameras = [] - if os.path.exists(os.path.join(segment_path, "qcamera.ts")): - available_cameras.append("forward") - if os.path.exists(os.path.join(segment_path, "ecamera.hevc")): - available_cameras.append("wide") - if os.path.exists(os.path.join(segment_path, "dcamera.hevc")): - available_cameras.append("driver") - return available_cameras - -def get_all_car_models() -> dict: - models = {} - # List all directories in the car folder into a variable - makes = os.listdir(CAR_VALUES_PATH) - for make in makes: - # Check if the values.py file exists, and if it does import it using importlib - if os.path.exists(f"{CAR_VALUES_PATH}/{make}/values.py"): - package = f"car.{make}" - if not RUNNING_ON_COMMA: - package = f"thepond.fixtures.selfdrive_car.{make}" - values = importlib.import_module(f".values", package) - # Get all the car models from the values.py file - for model in values.CAR: - model = model.value - brand = model.split(" ")[0] - model = " ".join(model.split(" ")[1:]) - if brand not in models: - models[brand] = [] - models[brand].append(model) - return models - - -def convert_param_type(value: bytes | None): - """ - Small util to convert the value of a param - to int if that is the type, otherwise the original - input is returned - """ - if value == None: - return None - output = value.decode("utf-8") - try: - output = int(value) - except Exception as e: - pass - return output - - -def convert_settings_dict_to_array(settings: dict) -> list: - """ - Takes a dict where the keys are the setting keys and the values are the setting values - and converts it to an array of Setting objects where the key is inserted into the object - """ - for key, setting in settings.items(): - setting["key"] = key - if len(setting.get("subsettings", [])) > 0: - setting["subsettings"] = convert_settings_dict_to_array(setting["subsettings"]) - if len(setting.get("toggles", [])) > 0: - setting["toggles"] = convert_settings_dict_to_array(setting["toggles"]) - return list(settings.values()) - - -def get_settings_value(setting: dict) -> None: - """ - Reads the value for the setting, and loads all - values for subsettings and toggles as well recursivly - """ - value = params.get(setting["key"]) - setting["value"] = convert_param_type(value) - if len(setting.get("subsettings", [])) > 0: - for subsetting in setting["subsettings"]: - get_settings_value(subsetting) - if len(setting.get("toggles", [])) > 0: - for toggle in setting["toggles"]: - get_settings_value(toggle) - - -def find_setting(key, params): - """ - Find a setting in the params array by key. Searches subsettings and toggles as - well, recursivly - """ - for param in params: - if param["key"] == key: - return param - if len(param.get("subsettings", [])) > 0: - found = find_setting(key, param["subsettings"]) - if found: - return found - if len(param.get("toggles", [])) > 0: - found = find_setting(key, param["toggles"]) - if found: - return found - return None - - -def load_settings(): - """ - Load the params.json file, then load the actual values - from the filesystem - """ - with open(f"{DIR_OF_THIS_FILE}/params.json") as f: - usedParams = json.load(f) - - usedParams = convert_settings_dict_to_array(usedParams) - - # Read the values from the module - for param in usedParams: - get_settings_value(param) - return usedParams - - -def save_setting(key, value) -> None: - """ - Save the value to the filesystem. If running - on a comma it saves it into the file at - /data/params/d/{key} - If running on computer it saves it to params.txt - """ - value = str(value) - params.put(key, value) - print(f"Saved param: {key} with value: {value}") - - update_frogpilot_toggles() - - -def get_disk_usage(): - """ - Get the disk usage of the comma device - """ - try: - free = get_available_bytes() - used = get_used_bytes() - total = used + free - - results = [{ - "mount": HARDWARE.get_device_type(), - "size": f"{total // (2**30)} GB", - "used": f"{used // (2**30)} GB", - "free": f"{free // (2**30)} GB", - "usedPercentage": f"{(used / total) * 100:.2f}%" - }] - - return results, None - - except Exception as e: - error_message = f"Failed getting disk usage: {e}" - print(traceback.format_exc()) - print(error_message) - return [], [error_message] - -def get_drive_stats(): - """ - Get the drive stats of the comma device - """ - errors = [] - - try: - stats_value = params.get("ApiCache_DriveStats", encoding='utf-8') or "{}" - print(f"ApiCache_DriveStats: {stats_value}") - stats = json.loads(stats_value) - - except Exception: - error_message = f"Failed to retrieve or parse ApiCache_DriveStats: {traceback.format_exc()}" - print(error_message) - errors.append(error_message) - return None, errors - - try: - is_metric = params.get_bool("IsMetric") - conversion_factor = CV.KPH_TO_MPH if not is_metric else 1 - unit = "km" if is_metric else "miles" - - def process_stats(timeframe): - return { - "distance": stats.get(timeframe, {}).get("distance", 0) * conversion_factor, - "drives": stats.get(timeframe, {}).get("routes", 0), - "hours": stats.get(timeframe, {}).get("minutes", 0) / 60, - "unit": unit - } - - stats["all"] = process_stats("all") - stats["week"] = process_stats("week") - - stats["frogpilot"] = { - "distance": params_tracking.get_int("FrogPilotKilometers") * conversion_factor, - "hours": params_tracking.get_int("FrogPilotMinutes") / 60, - "drives": params_tracking.get_int("FrogPilotDrives"), - "unit": unit - } - - print(stats) - return stats, None - - except Exception: - error_message = f"Failed processing drive stats: {traceback.format_exc()}" - print(error_message) - errors.append(error_message) - return None, errors diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc index 4af8f4b960660c..012beb34925406 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc @@ -3,12 +3,12 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> advancedToggles { {"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced settings that control how openpilot manages steering."), "../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"}, - {"SteerFriction", steerFrictionStock != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)) : tr("Friction"), tr("Adjust the resistance in steering. Higher values provide more stable steering but can make it feel heavy, while lower values allow lighter steering but may feel too sensitive."), ""}, - {"SteerKP", steerKPStock != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)) : tr("Kp Factor"), tr("Control how aggressively the car corrects its steering. Higher values offer quicker corrections but may feel jerky, while lower values make steering smoother but slower to respond."), ""}, + {"SteerFriction", steerFrictionStock != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)) : tr("Friction"), tr("The resistance in steering. Higher values provide more stable steering but can make it feel heavy, while lower values allow lighter steering but may feel too sensitive."), ""}, + {"SteerKP", steerKPStock != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)) : tr("Kp Factor"), tr("How aggressively the car corrects its steering. Higher values offer quicker corrections but may feel jerky, while lower values make steering smoother but slower to respond."), ""}, {"SteerLatAccel", steerLatAccelStock != 0 ? QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2)) : tr("Lateral Accel"), tr("Adjust how fast the car can steer from side to side. Higher values allow quicker lane changes but can feel unstable, while lower values provide smoother steering but may feel sluggish."), ""}, - {"SteerRatio", steerRatioStock != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)) : tr("Steer Ratio"), tr("Adjust how much openpilot needs to turn the wheel to steer. Higher values feel like driving a truck, more stable at high speeds, but harder to steer quickly at low speeds while lower values feel like a go-kart, easier to steer in tight spots, but more sensitive and less stable at high speeds."), ""}, + {"SteerRatio", steerRatioStock != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)) : tr("Steer Ratio"), tr("Adjust how much openpilot needs to turn the wheel to steer. Higher values feel like driving a truck, more stable at high speeds, but harder to steer quickly at low speeds, while lower values feel like a go-kart, easier to steer in tight spots but more sensitive and less stable at high speeds."), ""}, {"TacoTune", tr("comma's 2022 Taco Bell Turn Hack"), tr("Use comma's hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive."), ""}, - {"ForceAutoTune", tr("Force Auto Tune"), tr("Forces comma's auto lateral tuning for unsupported vehicles."), ""}, + {"ForceAutoTune", tr("Force Auto Tune On"), tr("Forces comma's auto lateral tuning for unsupported vehicles."), ""}, {"ForceAutoTuneOff", tr("Force Auto Tune Off"), tr("Forces comma's auto lateral tuning off for supported vehicles."), ""}, {"TurnDesires", tr("Force Turn Desires Below Lane Change Speed"), tr("Force the model to use turn desires when driving below the minimum lane change speed to help make left and right turns more precisely."), ""}, @@ -16,56 +16,56 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi {"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("How sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but may occasionally mistake other objects for vehicles."), ""}, {"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Set a cap on how fast openpilot can accelerate to prevent high acceleration at low speeds."), ""}, - {"AdvancedQOLDriving", tr("Advanced Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot driving experience."), "../frogpilot/assets/toggle_icons/advanced_quality_of_life.png"}, - {"ForceStandstill", tr("Force Standstill State"), tr("Keeps openpilot in the 'standstill' state until the gas pedal or 'resume' button is pressed."), ""}, - {"ForceStops", tr("Force Stop For Detected Stop Lights/Signs"), tr("Whenever openpilot 'detects' a potential stop light/stop sign, force a stop where it originally detected it to prevent running the potential red light/stop sign."), ""}, - {"SetSpeedOffset", tr("Set Speed Offset"), tr("Adjust how much higher or lower the set speed should be compared to your current set speed. For example, if you prefer to drive 5 mph above the speed limit, this setting will automatically add that difference when you engage cruise control."), ""}, + {"AdvancedQOLDriving", tr("Advanced Quality of Life"), tr("Miscellaneous advanced features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/advanced_quality_of_life.png"}, + {"ForceStandstill", tr("Force Keep openpilot in the Standstill State"), tr("Keep openpilot in the 'standstill' state until the gas pedal or 'resume' button is pressed."), ""}, + {"ForceStops", tr("Force Stop for 'Detected' Stop Lights/Signs"), tr("Whenever openpilot 'detects' a potential stop light/stop sign, force a stop where it originally detected it to prevent running the potential red light/stop sign."), ""}, + {"SetSpeedOffset", tr("Set Speed Offset"), tr("How much higher or lower the set speed should be compared to your current set speed. For example, if you prefer to drive 5 mph above the speed limit, this setting will automatically add that difference when you adjust your set speed."), ""}, - {"CustomPersonalities", tr("Customize Driving Personalities"), tr("Create and customize your own driving personality profiles to suit your preferences."), "../frogpilot/assets/toggle_icons/icon_advanced_personality.png"}, + {"CustomPersonalities", tr("Customize Driving Personalities"), tr("Customize the personality profiles to suit your preferences."), "../frogpilot/assets/toggle_icons/icon_advanced_personality.png"}, {"TrafficPersonalityProfile", tr("Traffic Personality"), tr("Customize the 'Traffic' personality profile, tailored for navigating through traffic."), "../frogpilot/assets/stock_theme/distance_icons/traffic.png"}, - {"TrafficFollow", tr("Following Distance"), tr("Set the minimum following distance in 'Traffic Mode.' The system will adjust dynamically between this value and the 'Aggressive' profile distance based on your speed."), ""}, - {"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("Sets how sensitive the system is to changes in acceleration in 'Traffic Mode.' Higher values result in smoother, more gradual acceleration and deceleration, while lower values allow for faster changes that may feel more abrupt."), ""}, - {"TrafficJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive the system is to changes in deceleration in 'Traffic Mode.' Higher values result in smoother, more gradual braking, while lower values allow for quicker, more responsive braking that may feel abrupt."), ""}, - {"TrafficJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious the system is around other vehicles or obstacles in 'Traffic Mode.' Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time."), ""}, - {"TrafficJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly the system adjusts speed in 'Traffic Mode.' Higher values ensure smoother, more gradual speed changes, while lower values enable quicker adjustments that might feel sharper or less smooth."), ""}, - {"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly the system adjusts to decreasing speeds in 'Traffic Mode.' Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed reductions that might feel sharper."), ""}, + {"TrafficFollow", tr("Following Distance"), tr("The minimum following distance in 'Traffic Mode.' openpilot will adjust dynamically between this value and the 'Aggressive' profile distance based on your speed."), ""}, + {"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("How sensitive openpilot is to changes in acceleration in 'Traffic Mode.' Higher values result in smoother, more gradual acceleration and deceleration, while lower values allow for faster changes that may feel more abrupt."), ""}, + {"TrafficJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Traffic Mode.' Higher values result in smoother, more gradual braking, while lower values allow for quicker, more responsive braking that may feel abrupt."), ""}, + {"TrafficJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Traffic Mode.' Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time."), ""}, + {"TrafficJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Traffic Mode.' Higher values ensure smoother, more gradual speed changes, while lower values enable quicker adjustments that might feel sharper or less smooth."), ""}, + {"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Traffic Mode.' Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed reductions that might feel sharper."), ""}, {"ResetTrafficPersonality", tr("Reset Settings"), tr("Restore the 'Traffic Mode' settings to their default values."), ""}, {"AggressivePersonalityProfile", tr("Aggressive Personality"), tr("Customize the 'Aggressive' personality profile, designed for a more assertive driving style."), "../frogpilot/assets/stock_theme/distance_icons/aggressive.png"}, - {"AggressiveFollow", tr("Following Distance"), tr("Set the following distance for 'Aggressive' mode. This determines how many seconds you'll follow behind the car ahead.\n\nDefault: 1.25 seconds."), ""}, - {"AggressiveJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive the system is to acceleration changes in 'Aggressive' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive the system is to deceleration in 'Aggressive' mode. Higher values result in smoother braking, while lower values allow for more immediate braking that may feel abrupt.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious the system is around vehicles or obstacles in 'Aggressive' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly the system adjusts speed in 'Aggressive' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly the system adjusts to speed reductions in 'Aggressive' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 0.5."), ""}, + {"AggressiveFollow", tr("Following Distance"), tr("Set the following distance for 'Aggressive' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.25 seconds."), ""}, + {"AggressiveJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Aggressive' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Aggressive' mode. Higher values result in smoother braking, while lower values allow for more immediate braking that may feel abrupt.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Aggressive' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, + {"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Aggressive' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Aggressive' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 0.5."), ""}, {"ResetAggressivePersonality", tr("Reset Settings"), tr("Restore the 'Aggressive' settings to their default values."), ""}, {"StandardPersonalityProfile", tr("Standard Personality"), tr("Customize the 'Standard' personality profile, optimized for balanced driving."), "../frogpilot/assets/stock_theme/distance_icons/standard.png"}, - {"StandardFollow", tr("Following Distance"), tr("Set the following distance for 'Standard' mode. This determines how many seconds you'll follow behind the car ahead.\n\nDefault: 1.45 seconds."), ""}, - {"StandardJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive the system is to acceleration changes in 'Standard' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, - {"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive the system is to deceleration in 'Standard' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, - {"StandardJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious the system is around vehicles or obstacles in 'Standard' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly the system adjusts speed in 'Standard' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, - {"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly the system adjusts to speed reductions in 'Standard' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, + {"StandardFollow", tr("Following Distance"), tr("Set the following distance for 'Standard' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.45 seconds."), ""}, + {"StandardJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Standard' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, + {"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Standard' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"StandardJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Standard' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, + {"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Standard' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, + {"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Standard' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, {"ResetStandardPersonality", tr("Reset Settings"), tr("Restore the 'Standard' settings to their default values."), ""}, {"RelaxedPersonalityProfile", tr("Relaxed Personality"), tr("Customize the 'Relaxed' personality profile, ideal for a more laid-back driving style."), "../frogpilot/assets/stock_theme/distance_icons/relaxed.png"}, - {"RelaxedFollow", tr("Following Distance"), tr("Set the following distance for 'Relaxed' mode. This determines how many seconds you'll follow behind the car ahead.\n\nDefault: 1.75 seconds."), ""}, - {"RelaxedJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive the system is to acceleration changes in 'Relaxed' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive the system is to deceleration in 'Relaxed' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious the system is around vehicles or obstacles in 'Relaxed' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly the system adjusts speed in 'Relaxed' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly the system adjusts to speed reductions in 'Relaxed' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, + {"RelaxedFollow", tr("Following Distance"), tr("Set the following distance for 'Relaxed' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.75 seconds."), ""}, + {"RelaxedJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Relaxed' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Relaxed' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Relaxed' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Relaxed' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Relaxed' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, {"ResetRelaxedPersonality", tr("Reset Settings"), tr("Restore the 'Relaxed' settings to their default values."), ""}, {"ModelManagement", tr("Model Management"), tr("Manage the driving models used by openpilot."), "../frogpilot/assets/toggle_icons/icon_advanced_model.png"}, {"AutomaticallyUpdateModels", tr("Automatically Update and Download Models"), tr("Automatically download new or updated driving models."), ""}, - {"ModelRandomizer", tr("Model Randomizer"), tr("A random model is selected and can be reviewed at the end of each drive to help find your preferred model."), ""}, + {"ModelRandomizer", tr("Model Randomizer"), tr("A random model is selected and can be reviewed at the end of each drive if it's longer than 15 minutes to help find your preferred model."), ""}, {"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Control which models are blacklisted and won't be used for future drives."), ""}, {"ResetScores", tr("Reset Model Scores"), tr("Clear the ratings you've given to the driving models."), ""}, {"ReviewScores", tr("Review Model Scores"), tr("View the ratings you've assigned to the driving models."), ""}, {"DeleteModel", tr("Delete Model"), tr("Remove the selected driving model from your device."), ""}, - {"DownloadModel", tr("Download Model"), tr("Download a specific undownloaded driving model."), ""}, + {"DownloadModel", tr("Download Model"), tr("Download undownloaded driving models."), ""}, {"DownloadAllModels", tr("Download All Models"), tr("Download all undownloaded driving models."), ""}, {"SelectModel", tr("Select Model"), tr("Select which model openpilot uses to drive."), ""}, {"ResetCalibrations", tr("Reset Model Calibrations"), tr("Reset calibration settings for the driving models."), ""}, @@ -101,7 +101,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi advancedDrivingToggle = advancedLateralTuneToggle; } else if (param == "SteerFriction") { std::vector steerFrictionToggleNames{"Reset"}; - advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.25, QString(), std::map(), 0.01, {}, steerFrictionToggleNames, false); + advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map(), 0.01, {}, steerFrictionToggleNames, false); } else if (param == "SteerKP") { std::vector steerKPToggleNames{"Reset"}; advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKPStock * 0.50, steerKPStock * 1.50, QString(), std::map(), 0.01, {}, steerKPToggleNames, false); @@ -232,7 +232,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi QMap labelToModelMap; QStringList selectableModels, deletableModels; - for (int i = 0; i < availableModels.size(); i++) { + for (int i = 0; i < availableModels.size(); ++i) { QString model = availableModels[i]; if (blacklistedModels.contains(model)) { deletableModels.append(availableModelNames[i]); @@ -308,7 +308,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi QMap labelToFileMap; QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed"; - for (int i = 0; i < availableModels.size(); i++) { + for (int i = 0; i < availableModels.size(); ++i) { QString modelFile = availableModels[i] + ".thneed"; if (existingModels.contains(modelFile) && modelFile != currentModel && !availableModelNames[i].contains("(Default)")) { deletableModels.append(availableModelNames[i]); @@ -358,7 +358,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi QStringList existingModels = modelDir.entryList({"*.thneed"}, QDir::Files); QStringList downloadableModels; - for (int i = 0; i < availableModels.size(); i++) { + for (int i = 0; i < availableModels.size(); ++i) { QString modelFile = availableModels[i] + ".thneed"; if (!existingModels.contains(modelFile) && !availableModelNames[i].contains("(Default)")) { downloadableModels.append(availableModelNames[i]); @@ -457,7 +457,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi QSet modelFilesBaseNames = QSet::fromList(modelDir.entryList({"*.thneed"}, QDir::Files).replaceInStrings(QRegExp("\\.thneed$"), "")); QStringList selectableModels; - for (int i = 0; i < availableModels.size(); i++) { + for (int i = 0; i < availableModels.size(); ++i) { if (modelFilesBaseNames.contains(availableModels[i]) || availableModelNames[i].contains("(Default)")) { selectableModels.append(availableModelNames[i]); } @@ -510,7 +510,7 @@ FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWi } } else if (id == 1) { QStringList selectableModelLabels; - for (int i = 0; i < availableModels.size(); i++) { + for (int i = 0; i < availableModels.size(); ++i) { selectableModelLabels.append(availableModelNames[i]); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h index cf3d336486e85e..dd8320f56f2591 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h @@ -15,21 +15,12 @@ class FrogPilotAdvancedDrivingPanel : public FrogPilotListWidget { void openSubParentToggle(); void openSubSubParentToggle(); +protected: + void showEvent(QShowEvent *event) override; + private: FrogPilotSettingsWindow *parent; - void hideSubToggles(); - void hideSubSubToggles(); - void hideToggles(); - void showEvent(QShowEvent *event) override; - void showToggles(const std::set &keys); - void startDownloadAllModels(); - void updateCalibrationDescription(); - void updateCarToggles(); - void updateMetric(); - void updateModelLabels(); - void updateState(const UIState &s); - ButtonControl *deleteModelBtn; ButtonControl *downloadAllModelsBtn; ButtonControl *downloadModelBtn; @@ -40,23 +31,67 @@ class FrogPilotAdvancedDrivingPanel : public FrogPilotListWidget { FrogPilotParamValueButtonControl *steerKPToggle; FrogPilotParamValueButtonControl *steerRatioToggle; - std::set aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"}; - std::set customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"}; - std::set lateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio", "TacoTune", "TurnDesires"}; - std::set longitudinalTuneKeys = {"LeadDetectionThreshold", "MaxDesiredAcceleration"}; - std::set modelManagementKeys = {"AutomaticallyUpdateModels", "DeleteModel", "DownloadModel", "DownloadAllModels", "ModelRandomizer", "ResetCalibrations", "SelectModel"}; - std::set modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"}; - std::set qolKeys = {"ForceStandstill", "ForceStops", "SetSpeedOffset"}; - std::set relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"}; - std::set standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"}; - std::set trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"}; + std::set aggressivePersonalityKeys = { + "AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", + "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", + "ResetAggressivePersonality" + }; + + std::set customDrivingPersonalityKeys = { + "AggressivePersonalityProfile", "RelaxedPersonalityProfile", + "StandardPersonalityProfile", "TrafficPersonalityProfile" + }; + + std::set lateralTuneKeys = { + "ForceAutoTune", "ForceAutoTuneOff", "SteerFriction", "SteerLatAccel", + "SteerKP", "SteerRatio", "TacoTune", "TurnDesires" + }; + + std::set longitudinalTuneKeys = { + "LeadDetectionThreshold", "MaxDesiredAcceleration" + }; + + std::set modelManagementKeys = { + "AutomaticallyUpdateModels", "DeleteModel", "DownloadModel", "DownloadAllModels", + "ModelRandomizer", "ResetCalibrations", "SelectModel" + }; + + std::set modelRandomizerKeys = { + "ManageBlacklistedModels", "ResetScores", "ReviewScores" + }; + + std::set qolKeys = { + "ForceStandstill", "ForceStops", "SetSpeedOffset" + }; + + std::set relaxedPersonalityKeys = { + "RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", + "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", + "ResetRelaxedPersonality" + }; + + std::set standardPersonalityKeys = { + "StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", + "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", + "ResetStandardPersonality" + }; + + std::set trafficPersonalityKeys = { + "TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", + "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", + "ResetTrafficPersonality" + }; std::map toggles; - QDir modelDir{"/data/models/"}; + QStringList availableModelNames; + QStringList availableModels; + QStringList experimentalModels; QList labelControls; + QDir modelDir{"/data/models/"}; + Params params; Params paramsMemory{"/dev/shm/params"}; Params paramsStorage{"/persist/params"}; @@ -85,7 +120,15 @@ class FrogPilotAdvancedDrivingPanel : public FrogPilotListWidget { float steerKPStock; float steerRatioStock; - QStringList availableModelNames; - QStringList availableModels; - QStringList experimentalModels; + void hideSubToggles(); + void hideSubSubToggles(); + void hideToggles(); + void showToggles(const std::set &keys); + + void startDownloadAllModels(); + void updateCalibrationDescription(); + void updateCarToggles(); + void updateMetric(); + void updateModelLabels(); + void updateState(const UIState &s); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc index 0100da7841ad61..cf8fe71634bb40 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc @@ -2,12 +2,12 @@ FrogPilotAdvancedVisualsPanel::FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> advancedToggles { - {"AdvancedCustomUI", tr("Advanced Custom Onroad UI"), tr("Advanced user customizations for the Onroad UI."), "../frogpilot/assets/toggle_icons/icon_advanced_road.png"}, - {"CameraView", tr("Camera View"), tr("Choose your preferred camera view for the onroad UI. This is purely a visual change and doesn't impact how openpilot drives."), ""}, + {"AdvancedCustomUI", tr("Advanced Onroad UI Widgets"), tr("Advanced user customizations for the Onroad UI."), "../frogpilot/assets/toggle_icons/icon_advanced_road.png"}, + {"CameraView", tr("Camera View"), tr("Camera view for the onroad UI. This is purely a visual change and doesn't impact how openpilot drives."), ""}, + {"ShowStoppingPoint", tr("Display Stopping Points"), tr("Display an image on the screen where openpilot is detecting a potential red light/stop sign."), ""}, {"HideLeadMarker", tr("Hide Lead Marker"), tr("Hide the marker for the vehicle ahead on the screen."), ""}, {"HideSpeed", tr("Hide Speed"), tr("Hide the speed indicator in the onroad UI. Additional toggle allows it to be hidden/shown via tapping the speed itself."), ""}, {"HideUIElements", tr("Hide UI Elements"), tr("Hide the selected UI elements from the onroad screen."), ""}, - {"ShowStoppingPoint", tr("Stopping Points"), tr("Display an image on the screen where openpilot is detecting a potential red light/stop sign."), ""}, {"WheelSpeed", tr("Use Wheel Speed"), tr("Use the wheel speed instead of the cluster speed in the onroad UI."), ""}, {"DeveloperUI", tr("Developer UI"), tr("Show detailed information about openpilot's internal operations."), "../frogpilot/assets/toggle_icons/icon_advanced_device.png"}, @@ -20,10 +20,10 @@ FrogPilotAdvancedVisualsPanel::FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWi {"UseSI", tr("Use International System of Units"), tr("Display measurements using the 'International System of Units' (SI)."), ""}, {"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_advanced_calibration.png"}, - {"LaneLinesWidth", tr("Lane Lines Width"), tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""}, - {"PathEdgeWidth", tr("Path Edges Width"), tr("Adjust the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, - {"PathWidth", tr("Path Width"), tr("Set how wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""}, - {"RoadEdgesWidth", tr("Road Edges Width"), tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""}, + {"LaneLinesWidth", tr("Lane Lines Width"), tr("How thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""}, + {"PathEdgeWidth", tr("Path Edges Width"), tr("The width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, + {"PathWidth", tr("Path Width"), tr("How wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""}, + {"RoadEdgesWidth", tr("Road Edges Width"), tr("How thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""}, {"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extend the display of the path, lane lines, and road edges as far as the model can see."), ""}, }; @@ -56,7 +56,7 @@ FrogPilotAdvancedVisualsPanel::FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWi } else if (param == "DeveloperUI") { FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - borderMetricsBtn->setEnabledButtons(0, hasBSM); + borderMetricsBtn->setVisibleButton(0, hasBSM); std::set modifiedDeveloperUIKeys = developerUIKeys; diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h index e271ad23bdc709..c72a8f080164bb 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h @@ -16,16 +16,23 @@ class FrogPilotAdvancedVisualsPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showToggles(const std::set &keys); - void updateCarToggles(); - void updateMetric(); - FrogPilotButtonToggleControl *borderMetricsBtn; - std::set advancedCustomOnroadUIKeys = {"CameraView", "HideLeadMarker", "HideSpeed", "HideUIElements", "ShowStoppingPoint", "WheelSpeed"}; - std::set developerUIKeys = {"BorderMetrics", "FPSCounter", "LateralMetrics", "LongitudinalMetrics", "NumericalTemp", "SidebarMetrics", "UseSI"}; - std::set modelUIKeys = {"LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"}; + std::set advancedCustomOnroadUIKeys = { + "CameraView", "HideLeadMarker", "HideSpeed", + "HideUIElements", "ShowStoppingPoint", "WheelSpeed" + }; + + std::set developerUIKeys = { + "BorderMetrics", "FPSCounter", "LateralMetrics", + "LongitudinalMetrics", "NumericalTemp", + "SidebarMetrics", "UseSI" + }; + + std::set modelUIKeys = { + "LaneLinesWidth", "PathEdgeWidth", "PathWidth", + "RoadEdgesWidth", "UnlimitedLength" + }; std::map toggles; @@ -36,4 +43,9 @@ class FrogPilotAdvancedVisualsPanel : public FrogPilotListWidget { bool hasBSM; bool hasOpenpilotLongitudinal; bool isMetric = params.getBool("IsMetric"); + + void hideToggles(); + void showToggles(const std::set &keys); + void updateCarToggles(); + void updateMetric(); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc index 926c2cf6d0bbc3..727fa5de2250c7 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc @@ -4,7 +4,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { ButtonControl *deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Footage and Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")); - connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() { + QObject::connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) { std::thread([=] { deleteDrivingDataBtn->setEnabled(false); @@ -23,7 +23,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi addItem(deleteDrivingDataBtn); FrogPilotButtonsControl *screenRecordingsBtn = new FrogPilotButtonsControl(tr("Screen Recordings"), tr("Manage your screen recordings."), {tr("DELETE"), tr("RENAME")}); - connect(screenRecordingsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + QObject::connect(screenRecordingsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { QDir recordingsDir("/data/media/0/videos"); QStringList recordingsNames = recordingsDir.entryList(QDir::Files | QDir::NoDotAndDotDot); @@ -77,7 +77,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi addItem(screenRecordingsBtn); FrogPilotButtonsControl *frogpilotBackupBtn = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Manage your FrogPilot backups."), {tr("BACKUP"), tr("DELETE"), tr("RESTORE")}); - connect(frogpilotBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + QObject::connect(frogpilotBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { QDir backupDir("/data/backups"); QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name).filter(QRegularExpression("^(?!.*_in_progress$).*$")); @@ -216,7 +216,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi addItem(frogpilotBackupBtn); FrogPilotButtonsControl *toggleBackupBtn = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Manage your toggle backups."), {tr("BACKUP"), tr("DELETE"), tr("RESTORE")}); - connect(toggleBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + QObject::connect(toggleBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { QDir backupDir("/data/toggle_backups"); QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); diff --git a/selfdrive/frogpilot/ui/qt/offroad/data_settings.h b/selfdrive/frogpilot/ui/qt/offroad/data_settings.h index 6017de4b94d333..8d491738be5705 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/data_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/data_settings.h @@ -4,6 +4,7 @@ class FrogPilotDataPanel : public FrogPilotListWidget { Q_OBJECT + public: explicit FrogPilotDataPanel(FrogPilotSettingsWindow *parent); diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc index 749062e90c6752..c27674433b77f3 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc @@ -2,20 +2,20 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> deviceToggles { - {"DeviceManagement", tr("Device Management"), tr("Tweak your device's behaviors to your personal preferences."), "../frogpilot/assets/toggle_icons/icon_device.png"}, - {"DeviceShutdown", tr("Device Shutdown Timer"), tr("Configure how quickly the device shuts down after going offroad."), ""}, - {"NoLogging", tr("Disable Logging"), tr("Turn off all data tracking to enhance privacy or reduce thermal load."), ""}, - {"NoUploads", tr("Disable Uploads"), tr("Turn off all data uploads to comma's servers."), ""}, - {"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), tr("Allow the device to run at a temperature above comma's recommended thermal limits."), ""}, - {"LowVoltageShutdown", tr("Low Voltage Shutdown Threshold"), tr("Automatically shut the device down when your battery reaches a specific voltage level to prevent killing your battery."), ""}, - {"OfflineMode", tr("Offline Mode"), tr("Allow the device to be offline indefinitely."), ""}, - - {"ScreenManagement", tr("Screen Management"), tr("Manage your screen's brightness, timeout settings, and hide onroad UI elements."), "../frogpilot/assets/toggle_icons/icon_light.png"}, - {"ScreenBrightness", tr("Screen Brightness"), tr("Customize your screen brightness when offroad."), ""}, - {"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("Customize your screen brightness when onroad."), ""}, - {"ScreenRecorder", tr("Screen Recorder"), tr("Enable the ability to record the screen while onroad."), ""}, - {"ScreenTimeout", tr("Screen Timeout"), tr("Customize how long it takes for your screen to turn off."), ""}, - {"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("Customize how long it takes for your screen to turn off when onroad."), ""}, + {"DeviceManagement", tr("Device Settings"), tr("Device behavior settings."), "../frogpilot/assets/toggle_icons/icon_device.png"}, + {"DeviceShutdown", tr("Device Shutdown Timer"), tr("How long the device stays on after you stop driving."), ""}, + {"OfflineMode", tr("Disable Internet Requirement"), tr("The device can work without an internet connection for as long as you need."), ""}, + {"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), tr("The device can run at higher temperatures than recommended."), ""}, + {"LowVoltageShutdown", tr("Low Battery Shutdown Threshold"), tr("Shut down the device when the car's battery gets too low to prevent damage to the 12V battery."), ""}, + {"NoLogging", tr("Turn Off Data Tracking"), tr("Disable all tracking to improve privacy."), ""}, + {"NoUploads", tr("Turn Off Data Uploads"), tr("Stop the device from sending any data to the servers."), ""}, + + {"ScreenManagement", tr("Screen Settings"), tr("Screen behavior settings."), "../frogpilot/assets/toggle_icons/icon_light.png"}, + {"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("The screen brightness when you're not driving."), ""}, + {"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("The screen brightness while you're driving."), ""}, + {"ScreenRecorder", tr("Screen Recorder"), tr("Display a button in the onroad UI to record the screen."), ""}, + {"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("How long it takes for the screen to turn off when you're not driving."), ""}, + {"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("How long it takes for the screen to turn off while you're driving."), ""} }; for (const auto &[param, title, desc, icon] : deviceToggles) { @@ -29,7 +29,7 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr deviceToggle = deviceManagementToggle; } else if (param == "DeviceShutdown") { std::map shutdownLabels; - for (int i = 0; i <= 33; i++) { + for (int i = 0; i <= 33; ++i) { shutdownLabels[i] = i == 0 ? tr("5 mins") : i <= 3 ? QString::number(i * 15) + tr(" mins") : QString::number(i - 3) + (i == 4 ? tr(" hour") : tr(" hours")); } deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 33, QString(), shutdownLabels); @@ -49,7 +49,7 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr } else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") { std::map brightnessLabels; int minBrightness = (param == "ScreenBrightnessOnroad") ? 0 : 1; - for (int i = 1; i <= 101; i++) { + for (int i = 1; i <= 101; ++i) { brightnessLabels[i] = (i == 101) ? tr("Auto") : QString::number(i) + "%"; } deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, minBrightness, 101, QString(), brightnessLabels, 1, false, true); diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h index 48fd755f05f0dd..bc5192a7bd5a51 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h @@ -16,16 +16,23 @@ class FrogPilotDevicePanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showToggles(const std::set &keys); - void updateState(const UIState &s); + std::set deviceManagementKeys = { + "DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", + "NoLogging", "NoUploads", "OfflineMode" + }; - std::set deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"}; - std::set screenKeys = { "ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad"}; + std::set screenKeys = { + "ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", + "ScreenTimeout", "ScreenTimeoutOnroad" + }; std::map toggles; Params params; bool started; + + void hideToggles(); + void showToggles(const std::set &keys); + void updateState(const UIState &s); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc index fda53f8646fe2d..2732e0c95155f7 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc @@ -16,19 +16,21 @@ #include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h" bool checkNNFFLogFileExists(const std::string &carFingerprint) { - std::filesystem::path latModelsPath("../car/torque_data/lat_models"); + const std::filesystem::path latModelsPath("../car/torque_data/lat_models"); + if (!std::filesystem::exists(latModelsPath)) { std::cerr << "Lat models directory does not exist." << std::endl; return false; } for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(latModelsPath)) { - std::string filename = entry.path().filename().string(); - if (filename.find(carFingerprint) == 0) { + const std::string filename = entry.path().filename().string(); + if (filename.rfind(carFingerprint, 0) == 0) { std::cout << "NNFF supports fingerprint: " << filename << std::endl; return true; } } + return false; } @@ -90,12 +92,12 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram std::vector descriptions = { tr("Advanced FrogPilot features for more experienced users."), - tr("Control FrogPilot's sounds and alerts."), - tr("Control how FrogPilot handles your vehicle's acceleration, braking, and steering."), - tr("Download offline maps and manage 'Navigate On openpilot (NOO)' settings."), - tr("Tools and system utilities for maintaining and troubleshooting FrogPilot."), - tr("Customize the theme in FrogPilot and enable some unique onroad UI widgets."), - tr("Configure settings specific to your vehicle's make and model.") + tr("Options to customize FrogPilot's sound alerts and notifications."), + tr("FrogPilot features than impact acceleration, braking, and steering."), + tr("Offline maps downloader and 'Navigate On openpilot (NOO)' settings."), + tr("Tools and system utilities used to maintain and troubleshoot FrogPilot."), + tr("Options for customizing FrogPilot's themes, UI appearance, and onroad widgets."), + tr("Vehicle-specific settings and configurations for supported makes and models.") }; std::vector> buttonLabels = { @@ -109,7 +111,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram }; for (size_t i = 0; i < panels.size(); ++i) { - addPanelControl(list, panels[i].first, descriptions[i], buttonLabels[i], icons[i], panels[i].second); + bool isDrivingPanel = (panels[i].first == tr("Driving Controls")); + addPanelControl(list, panels[i].first, descriptions[i], buttonLabels[i], icons[i], panels[i].second, isDrivingPanel); } frogpilotSettingsLayout->addWidget(new ScrollView(list, frogpilotSettingsWidget)); @@ -118,7 +121,7 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram mainLayout->addWidget(frogpilotSettingsWidget); mainLayout->setCurrentWidget(frogpilotSettingsWidget); - QObject::connect(parent, &SettingsWindow::closePanel, [this]() {mainLayout->setCurrentWidget(frogpilotSettingsWidget);}); + QObject::connect(parent, &SettingsWindow::closePanel, this, &FrogPilotSettingsWindow::closePanel); QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotSettingsWindow::closeParentToggle); QObject::connect(parent, &SettingsWindow::closeSubParentToggle, this, &FrogPilotSettingsWindow::closeSubParentToggle); QObject::connect(parent, &SettingsWindow::closeSubSubParentToggle, this, &FrogPilotSettingsWindow::closeSubSubParentToggle); @@ -129,7 +132,22 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram } void FrogPilotSettingsWindow::showEvent(QShowEvent *event) { + updatePanelVisibility(); +} + +void FrogPilotSettingsWindow::closePanel() { + QWidget *currentWidget = mainLayout->currentWidget(); + if (currentWidget != frogpilotSettingsWidget) { + mainLayout->removeWidget(currentWidget); + } +} + +void FrogPilotSettingsWindow::updatePanelVisibility() { disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal"); + + drivingButton->setVisibleButton(0, hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal); + + mainLayout->setCurrentWidget(frogpilotSettingsWidget); } void FrogPilotSettingsWindow::updateCarVariables() { @@ -169,28 +187,28 @@ void FrogPilotSettingsWindow::updateCarVariables() { steerLatAccelStock = CP.getLateralTuning().getTorque().getLatAccelFactor(); steerRatioStock = CP.getSteerRatio(); - if (currentFrictionStock != steerFrictionStock) { + if (currentFrictionStock != steerFrictionStock && steerFrictionStock != 0) { if (params.getFloat("SteerFriction") == currentFrictionStock) { params.putFloatNonBlocking("SteerFriction", steerFrictionStock); } params.putFloatNonBlocking("SteerFrictionStock", steerFrictionStock); } - if (currentKPStock != steerKPStock) { + if (currentKPStock != steerKPStock && currentKPStock != 0) { if (params.getFloat("SteerKP") == currentKPStock) { params.putFloatNonBlocking("SteerKP", steerKPStock); } params.putFloatNonBlocking("SteerKPStock", steerKPStock); } - if (currentLatAccelStock != steerLatAccelStock) { + if (currentLatAccelStock != steerLatAccelStock && steerLatAccelStock != 0) { if (params.getFloat("SteerLatAccel") == steerLatAccelStock) { params.putFloatNonBlocking("SteerLatAccel", steerLatAccelStock); } params.putFloatNonBlocking("SteerLatAccelStock", steerLatAccelStock); } - if (currentRatioStock != steerRatioStock) { + if (currentRatioStock != steerRatioStock && steerRatioStock != 0) { if (params.getFloat("SteerRatio") == steerRatioStock) { params.putFloatNonBlocking("SteerRatio", steerRatioStock); } @@ -244,7 +262,7 @@ void FrogPilotSettingsWindow::updateCarVariables() { emit updateCarToggles(); } -void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, const QString &title, const QString &desc, const std::vector &button_labels, const QString &icon, const std::vector &panels) { +void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, const QString &title, const QString &desc, const std::vector &button_labels, const QString &icon, const std::vector &panels, const bool isDrivingPanel) { std::vector panelContainers; panelContainers.reserve(panels.size()); @@ -256,7 +274,14 @@ void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, const Q panelContainers.push_back(panelContainer); } - FrogPilotButtonsControl *button = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); + FrogPilotButtonsControl *button; + if (isDrivingPanel) { + drivingButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); + button = drivingButton; + } else { + button = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); + } + QObject::connect(button, &FrogPilotButtonsControl::buttonClicked, [this, panelContainers](int buttonId) { if (buttonId < panelContainers.size()) { QWidget *selectedPanel = panelContainers[buttonId]; diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h index 6c7e8dba52991b..7940c77744e274 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h @@ -4,6 +4,7 @@ class FrogPilotSettingsWindow : public QFrame { Q_OBJECT + public: explicit FrogPilotSettingsWindow(SettingsWindow *parent); @@ -43,13 +44,17 @@ class FrogPilotSettingsWindow : public QFrame { void updateMetric(); private: - void addPanelControl(FrogPilotListWidget *list, const QString &title, const QString &desc, const std::vector &button_labels, const QString &icon, const std::vector &panels); - void showEvent(QShowEvent *event) override; - void updateCarVariables(); + FrogPilotButtonsControl *drivingButton; + + Params params; QStackedLayout *mainLayout; QWidget *frogpilotSettingsWidget; - Params params; + void addPanelControl(FrogPilotListWidget *list, const QString &title, const QString &desc, const std::vector &button_labels, const QString &icon, const std::vector &panels, const bool isDrivingPanel = false); + void closePanel(); + void showEvent(QShowEvent *event) override; + void updateCarVariables(); + void updatePanelVisibility(); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc index 9a951c7b266f83..bac16a01852452 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc @@ -2,25 +2,25 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> lateralToggles { - {"AlwaysOnLateral", tr("Always on Lateral"), tr("Maintain openpilot lateral control when the brake or gas pedals are used.\n\nDeactivation occurs only through the 'Cruise Control' button."), "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"}, - {"AlwaysOnLateralLKAS", tr("Control Via LKAS Button"), tr("Enable or disable 'Always On Lateral' by clicking your 'LKAS' button."), ""}, - {"AlwaysOnLateralMain", tr("Enable On Cruise Main"), tr("Enable 'Always On Lateral' by clicking your 'Cruise Control' button without requiring openpilot to be enabled first."), ""}, - {"PauseAOLOnBrake", tr("Pause On Brake Below"), tr("Pause 'Always On Lateral' when the brake pedal is being pressed below the set speed."), ""}, - {"HideAOLStatusBar", tr("Hide the Status Bar"), tr("Don't use the status bar for 'Always On Lateral'."), ""}, - - {"LaneChangeCustomizations", tr("Lane Changes"), tr("Customize the lane change behaviors in openpilot."), "../frogpilot/assets/toggle_icons/icon_lane.png"}, - {"LaneChangeTime", tr("Lane Change Timer"), tr("Set a delay before executing a lane change."), ""}, - {"LaneDetectionWidth", tr("Lane Detection Threshold"), tr("Set the required lane width to be qualified as a lane."), ""}, - {"MinimumLaneChangeSpeed", tr("Minimum Lane Change Speed"), tr("Customize the minimum driving speed to allow openpilot to change lanes."), ""}, - {"NudgelessLaneChange", tr("Nudgeless Lane Change"), tr("Enable lane changes without requiring manual steering input."), ""}, - {"OneLaneChange", tr("One Lane Change Per Signal"), tr("Only allow one lane change per turn signal activation."), ""}, - - {"LateralTune", tr("Lateral Tuning"), tr("Modify openpilot's steering behavior."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"}, - {"NNFF", tr("NNFF"), tr("Use Twilsonco's Neural Network Feedforward for enhanced precision in lateral control."), ""}, - {"NNFFLite", tr("Smoother Entry and Exit for Curves"), tr("Uses Twilsonco's steering torque tweak to provide smoother handling when entering and exiting curves."), ""}, - - {"QOLLateral", tr("Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, - {"PauseLateralSpeed", tr("Pause Lateral Below"), tr("Pause lateral control on all speeds below the set speed."), ""}, + {"AlwaysOnLateral", tr("Always on Lateral"), tr("openpilot's steering control stays active even when the brake or gas pedals are pressed.\n\nDeactivate only occurs with the 'Cruise Control' button."), "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"}, + {"AlwaysOnLateralLKAS", tr("Control with LKAS Button"), tr("'Always on Lateral' gets turned on or off using the 'LKAS' button."), ""}, + {"AlwaysOnLateralMain", tr("Enable with Cruise Control"), tr("'Always on Lateral' gets turned on by pressing the 'Cruise Control' button bypassing the requirement to enable openpilot first."), ""}, + {"PauseAOLOnBrake", tr("Pause on Brake Below"), tr("'Always on Lateral' pauses when the brake pedal is pressed below the set speed."), ""}, + {"HideAOLStatusBar", tr("Hide the Status Bar"), tr("The status bar for 'Always on Lateral' is hidden."), ""}, + + {"LaneChangeCustomizations", tr("Lane Change Settings"), tr("How openpilot handles lane changes."), "../frogpilot/assets/toggle_icons/icon_lane.png"}, + {"NudgelessLaneChange", tr("Hands-Free Lane Change"), tr("Lane changes are conducted without needing to touch the steering wheel upon turn signal activation."), ""}, + {"LaneChangeTime", tr("Lane Change Delay"), tr("How long openpilot waits before changing lanes."), ""}, + {"LaneDetectionWidth", tr("Lane Width Requirement"), tr("The minimum lane width for openpilot to detect a lane as a lane."), ""}, + {"MinimumLaneChangeSpeed", tr("Minimum Speed for Lane Change"), tr("The minimum speed required for openpilot to perform a lane change."), ""}, + {"OneLaneChange", tr("Single Lane Change Per Signal"), tr("Lane changes are limited to one per turn signal activation."), ""}, + + {"LateralTune", tr("Lateral Tuning"), tr("Settings that control how openpilot manages steering."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"}, + {"NNFF", tr("Neural Network Feedforward (NNFF)"), tr("Twilsonco's 'Neural Network FeedForward' for more precise steering control."), ""}, + {"NNFFLite", tr("Smooth Curve Handling"), tr("Smoother steering when entering and exiting curves with Twilsonco's torque adjustments."), ""}, + + {"QOLLateral", tr("Quality of Life Improvements"), tr("Miscellaneous lateral focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, + {"PauseLateralSpeed", tr("Pause Steering Below"), tr("Pauses steering control when driving below the set speed."), ""} }; for (const auto &[param, title, desc, icon] : lateralToggles) { @@ -78,7 +78,7 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : lateralToggle = laneChangeToggle; } else if (param == "LaneChangeTime") { std::map laneChangeTimeLabels; - for (int i = 0; i <= 10; i++) { + for (int i = 0; i <= 10; ++i) { laneChangeTimeLabels[i] = i == 0 ? "Instant" : QString::number(i / 2.0) + " seconds"; } lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, QString(), laneChangeTimeLabels); diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h index 8d96ab31ac2add..2f5cd4e04fa203 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h @@ -16,16 +16,24 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showToggles(const std::set &keys); - void updateMetric(); - void updateCarToggles(); - void updateState(const UIState &s); + std::set aolKeys = { + "AlwaysOnLateralLKAS", "AlwaysOnLateralMain", + "HideAOLStatusBar", "PauseAOLOnBrake" + }; + + std::set laneChangeKeys = { + "LaneChangeTime", "LaneDetectionWidth", + "MinimumLaneChangeSpeed", "NudgelessLaneChange", + "OneLaneChange" + }; + + std::set lateralTuneKeys = { + "NNFF", "NNFFLite" + }; - std::set aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "HideAOLStatusBar", "PauseAOLOnBrake"}; - std::set laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"}; - std::set lateralTuneKeys = {"NNFF", "NNFFLite"}; - std::set qolKeys = {"PauseLateralSpeed"}; + std::set qolKeys = { + "PauseLateralSpeed" + }; std::map toggles; @@ -36,4 +44,10 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { bool isMetric = params.getBool("IsMetric"); bool isSubaru; bool started; + + void hideToggles(); + void showToggles(const std::set &keys); + void updateMetric(); + void updateCarToggles(); + void updateState(const UIState &s); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc index d42ceb8288d9b5..2f85dd0d2d67df 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc @@ -2,60 +2,59 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> longitudinalToggles { - {"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switches to 'Experimental Mode' under predefined conditions."), "../frogpilot/assets/toggle_icons/icon_conditional.png"}, - {"CESpeed", tr("Below"), tr("Switch to 'Experimental Mode' below this speed when not following a lead vehicle."), ""}, - {"CECurves", tr("Curve Detected Ahead"), tr("Switch to 'Experimental Mode' when a curve is detected."), ""}, - {"CELead", tr("Lead Detected Ahead"), tr("Switch to 'Experimental Mode' when a slower or stopped lead vehicle is detected ahead."), ""}, - {"CEModelStopTime", tr("Model Wants To Stop In The Next"), tr("Switch to 'Experimental Mode' when the model wants to stop like when it detects a stop light or stop sign."), ""}, - {"CENavigation", tr("Navigation Based"), tr("Switch to 'Experimental Mode' based on navigation data. (i.e. Intersections, stop signs, upcoming turns, etc.)"), ""}, - {"CESignal", tr("Turn Signal When Below Highway Speeds"), tr("Switch to 'Experimental Mode' when using turn signals below highway speeds to help assist with turns."), ""}, - {"HideCEMStatusBar", tr("Hide the Status Bar"), tr("Don't use the status bar for 'Conditional Experimental Mode'."), ""}, - - {"CurveSpeedControl", tr("Curve Speed Control"), tr("Slow down for anticipated curves detected by the downloaded maps."), "../frogpilot/assets/toggle_icons/icon_speed_map.png"}, - {"CurveDetectionMethod", tr("Curve Detection Method"), tr("Choose your preferred curve detection method."), ""}, - {"DisableCurveSpeedSmoothing", tr("Disable UI Smoothing"), tr("Disables the smoothing for the requested speed in the onroad UI to show exactly what speed MTSC/VTSC is currently requesting."), ""}, - {"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Only trigger MTSC when the model detects a curve in the road. Purely used as a failsafe to prevent false positives. Leave this off if you never experience false positives."), ""}, - {"CurveSensitivity", tr("Curve Detection Sensitivity"), tr("Set curve detection sensitivity. Higher values prompt earlier responses, lower values lead to smoother but later reactions."), ""}, - {"TurnAggressiveness", tr("Turn Speed Aggressiveness"), tr("Set turn speed aggressiveness. Higher values result in faster turns, lower values yield gentler turns."), ""}, - - {"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("Toggle Experimental Mode with either buttons on the steering wheel or the screen. \n\nOverrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"}, - {"ExperimentalModeViaLKAS", tr("Click LKAS Button"), tr("Enable/disable 'Experimental Mode' by clicking the 'LKAS' button on your steering wheel."), ""}, - {"ExperimentalModeViaTap", tr("Double Tap the UI"), tr("Enable/disable 'Experimental Mode' by double tapping the onroad UI within a 0.5 second time frame."), ""}, - {"ExperimentalModeViaDistance", tr("Long Press Distance"), tr("Enable/disable 'Experimental Mode' by holding down the 'distance' button on your steering wheel for 0.5 seconds."), ""}, - - {"LongitudinalTune", tr("Longitudinal Tuning"), tr("Modify openpilot's acceleration and braking behavior."), "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"}, - {"AccelerationProfile", tr("Acceleration Profile"), tr("Change the acceleration rate to be either sporty or eco-friendly."), ""}, - {"DecelerationProfile", tr("Deceleration Profile"), tr("Change the deceleration rate to be either sporty or eco-friendly."), ""}, - {"HumanAcceleration", tr("Human-Like Acceleration"), tr("Tweaks the acceleration behavior to be more 'human-like'."), ""}, - {"HumanFollowing", tr("Human-Like Following Distance"), tr("Tweaks the following distance dynamically to be more 'human-like' when coming up behind slower/stopped leads or following faster leads."), ""}, - {"StoppingDistance", tr("Stopped Distance From Lead"), tr("Control the stopped distance behind lead vehicles."), ""}, - - {"QOLLongitudinal", tr("Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, - {"CustomCruise", tr("Cruise Increase Interval"), tr("Set a custom interval to increase the max set speed by."), ""}, - {"CustomCruiseLong", tr("Cruise Increase Interval (Long Press)"), tr("Set a custom interval to increase the max set speed by when holding down the cruise increase button."), ""}, - {"MapGears", tr("Map Accel/Decel To Gears"), tr("Map your acceleration/deceleration profile to your 'Eco' and/or 'Sport' gears."), ""}, - {"OnroadDistanceButton", tr("Onroad Personality Button"), tr("Displays the current driving personality on the onroad screen. Tap to switch between personalities, or long press for 2.5 seconds to activate 'Traffic Mode'."), ""}, - {"ReverseCruise", tr("Reverse Cruise Increase"), tr("Reverses the 'long press' functionality logic to increase the max set speed by 5 instead of 1. Useful to increase the max speed quickly."), ""}, - - {"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust the max speed to match the current speed limit using 'Open Street Maps', 'Navigate On openpilot', or your car's dashboard (Toyotas/Lexus/HKG only)."), "../assets/offroad/icon_speed_limit.png"}, - {"SLCControls", tr("Controls Settings"), tr("Manage toggles related to 'Speed Limit Controller's controls."), ""}, - {"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Speed limit offset for speed limits between 0-34 mph."), ""}, - {"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Speed limit offset for speed limits between 35-54 mph."), ""}, - {"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Speed limit offset for speed limits between 55-64 mph."), ""}, - {"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Speed limit offset for speed limits between 65-99 mph."), ""}, - {"SLCFallback", tr("Fallback Method"), tr("Choose your fallback method when there is no speed limit available."), ""}, - {"SLCOverride", tr("Override Method"), tr("Choose your preferred method to override the current speed limit."), ""}, - {"SLCPriority", tr("Priority Order"), tr("Configure the speed limit priority order."), ""}, - {"SLCQOL", tr("Quality of Life"), tr("Manage toggles related to 'Speed Limit Controller's quality of life features."), ""}, - {"SLCConfirmation", tr("Confirm New Speed Limits"), tr("Don't automatically start using the new speed limit until it's been manually confirmed."), ""}, - {"ForceMPHDashboard", tr("Force MPH From Dashboard Readings"), tr("Force MPH readings from the dashboard. Only use this if you live in an area where the speed limits from your dashboard are in KPH, but you use MPH."), ""}, - {"SLCLookaheadHigher", tr("Prepare For Higher Speed Limits"), tr("Set a 'lookahead' value to prepare for upcoming speed limits higher than your current speed limit using the data stored in 'Open Street Maps'."), ""}, - {"SLCLookaheadLower", tr("Prepare For Lower Speed Limits"), tr("Set a 'lookahead' value to prepare for upcoming speed limits lower than your current speed limit using the data stored in 'Open Street Maps'."), ""}, - {"SetSpeedLimit", tr("Use Current Speed Limit As Set Speed"), tr("Sets your max speed to the current speed limit if one is populated when you initially enable openpilot."), ""}, - {"SLCVisuals", tr("Visuals Settings"), tr("Manage toggles related to 'Speed Limit Controller's visuals."), ""}, - {"ShowSLCOffset", tr("Show Speed Limit Offset"), tr("Show the speed limit offset separated from the speed limit in the onroad UI when using 'Speed Limit Controller'."), ""}, - {"SpeedLimitChangedAlert", tr("Speed Limit Changed Alert"), tr("Trigger an alert whenever the speed limit changes."), ""}, - {"UseVienna", tr("Use Vienna Speed Limit Signs"), tr("Use the Vienna (EU) speed limit style signs as opposed to MUTCD (US)."), ""}, + {"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switches to 'Experimental Mode' when specific conditions are met."), "../frogpilot/assets/toggle_icons/icon_conditional.png"}, + {"CESpeed", tr("Below"), tr("'Experimental Mode' is active when driving below the set speed without a lead vehicle."), ""}, + {"CECurves", tr("Curve Detected Ahead"), tr("'Experimental Mode' is active when a curve is detected in the road ahead."), ""}, + {"CELead", tr("Lead Detected Ahead"), tr("'Experimental Mode' is active when a slower or stopped vehicle is detected ahead."), ""}, + {"CENavigation", tr("Navigation Data"), tr("'Experimental Mode' is active based on navigation data, such as upcoming intersections or turns."), ""}, + {"CEModelStopTime", tr("openpilot Wants to Stop In"), tr("'Experimental Mode' is active when openpilot wants to stop such as for a stop sign or red light."), ""}, + {"CESignalSpeed", tr("Turn Signal Below"), tr("'Experimental Mode' is active when using turn signals below the set speed."), ""}, + {"HideCEMStatusBar", tr("Hide the Status Bar"), tr("The status bar for 'Conditional Experimental Mode' is hidden."), ""}, + + {"CurveSpeedControl", tr("Curve Speed Control"), tr("Automatically slow down for curves detected ahead or through the downloaded maps."), "../frogpilot/assets/toggle_icons/icon_speed_map.png"}, + {"CurveDetectionMethod", tr("Curve Detection Method"), tr("The method used to detect curves."), ""}, + {"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Curve control is triggered only when a curve is detected ahead. Use this as a failsafe to prevent false positives when using the 'Map Based' method."), ""}, + {"CurveSensitivity", tr("Curve Sensitivity"), tr("How sensitive openpilot is to detecting curves. Higher values trigger earlier responses at the risk of triggering too often, while lower values increase confidence at the risk of triggering too infrequently."), ""}, + {"TurnAggressiveness", tr("Turn Speed Aggressiveness"), tr("How aggressive openpilot takes turns. Higher values result in quicker turns, while lower values provide gentler turns."), ""}, + {"DisableCurveSpeedSmoothing", tr("Disable Speed Value Smoothing In the UI"), tr("Speed value smoothing is disabled in the UI to instead display the exact speed requested by the curve control."), ""}, + + {"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("'Experimental Mode' is toggled off/on using the steering wheel buttons or the on-screen controls.\n\nThis overrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"}, + {"ExperimentalModeViaLKAS", tr("Click the LKAS Button"), tr("'Experimental Mode' is toggled by pressing the 'LKAS' button on the steering wheel."), ""}, + {"ExperimentalModeViaTap", tr("Double-Tap the Screen"), tr("'Experimental Mode' is toggled by double-tapping the onroad UI within 0.5 seconds."), ""}, + {"ExperimentalModeViaDistance", tr("Long Press the Distance Button"), tr("'Experimental Mode' is toggled by holding the 'distance' button on the steering wheel for 0.5+ seconds."), ""}, + + {"LongitudinalTune", tr("Longitudinal Tuning"), tr("Settings that control how openpilot manages speed and acceleration."), "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"}, + {"AccelerationProfile", tr("Acceleration Profile"), tr("Choose between a sporty or eco-friendly acceleration rate."), ""}, + {"DecelerationProfile", tr("Deceleration Profile"), tr("Choose between a sporty or eco-friendly deceleration rate."), ""}, + {"HumanAcceleration", tr("Human-Like Acceleration"), tr("Uses the lead's acceleration rate when at a takeoff and ramps off the acceleration rate when approaching the maximum set speed for a smoother max speed approach."), ""}, + {"HumanFollowing", tr("Human-Like Following Distance"), tr("Dynamically adjusts the following distance to feel more natural when approaching slower or stopped vehicles."), ""}, + {"IncreasedStoppedDistance", tr("Increase Stopped Distance"), tr("Increases the distance to stop behind vehicles."), ""}, + + {"QOLLongitudinal", tr("Quality of Life Improvements"), tr("Miscellaneous longitudinal focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, + {"CustomCruise", tr("Cruise Increase Interval"), tr("Interval used when increasing the cruise control speed."), ""}, + {"CustomCruiseLong", tr("Custom Cruise Interval (Long Press)"), tr("Interval used when increasing the cruise control speed when holding down the button for 0.5+ seconds."), ""}, + {"MapGears", tr("Map Accel/Decel to Gears"), tr("Map the acceleration and deceleration profiles to the 'Eco' or 'Sport' gear modes."), ""}, + {"OnroadDistanceButton", tr("Onroad Personality Button"), tr("The current driving personality is displayed on the screen. Tap to switch personalities, or long press for 2.5 seconds to activate 'Traffic Mode'."), ""}, + {"ReverseCruise", tr("Reverse Cruise Increase"), tr("The long press feature is reversed in order to increase speed by 5 mph instead of 1."), ""}, + + {"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust your max speed to match the speed limit using 'Open Street Maps', 'Navigate on openpilot', or your car's dashboard (Toyota/Lexus/HKG only)."), "../assets/offroad/icon_speed_limit.png"}, + {"SLCConfirmation", tr("Confirm New Speed Limits"), tr("Require manual confirmation before using a new speed limit."), ""}, + {"SLCFallback", tr("Fallback Method"), tr("Choose what happens when no speed limit data is available."), ""}, + {"SLCOverride", tr("Override Method"), tr("Choose how you want to override the current speed limit.\n\n"), ""}, + {"SLCPriority", tr("Speed Limit Source Priority"), tr("Set the order of priority for speed limit data sources."), ""}, + {"SLCOffsets", tr("Speed Limit Offsets"), tr("Manage toggles related to 'Speed Limit Controller's controls."), ""}, + {"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Set the speed limit offset for speeds between 0 and 34 mph."), ""}, + {"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Set the speed limit offset for speeds between 35 and 54 mph."), ""}, + {"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Set the speed limit offset for speeds between 55 and 64 mph."), ""}, + {"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Set the speed limit offset for speeds between 65 and 99 mph."), ""}, + {"SLCQOL", tr("Quality of Life Improvements"), tr("Miscellaneous 'Speed Limit Controller' focused features to improve your overall openpilot experience."), ""}, + {"ForceMPHDashboard", tr("Force MPH Readings from Dashboard"), tr("Force speed limit readings in MPH from the dashboard if it normally displays in KPH."), ""}, + {"SLCLookaheadHigher", tr("Prepare for Higher Speed Limits"), tr("Set a lookahead value to prepare for upcoming higher speed limits based on map data."), ""}, + {"SLCLookaheadLower", tr("Prepare for Lower Speed Limits"), tr("Set a lookahead value to prepare for upcoming lower speed limits based on map data."), ""}, + {"SetSpeedLimit", tr("Set Speed to Current Limit"), tr("Set your max speed to match the current speed limit when enabling openpilot."), ""}, + {"SLCVisuals", tr("Visual Settings"), tr("Manage visual settings for the 'Speed Limit Controller'."), ""}, + {"UseVienna", tr("Use Vienna-Style Speed Signs"), tr("Switch to Vienna-style (EU) speed limit signs instead of MUTCD (US)."), ""}, + {"ShowSLCOffset", tr("Show Speed Limit Offset"), tr("Display the speed limit offset separately in the onroad UI when using the Speed Limit Controller."), ""}, }; for (const auto &[param, title, desc, icon] : longitudinalToggles) { @@ -69,7 +68,7 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * longitudinalToggle = conditionalExperimentalToggle; } else if (param == "CESpeed") { FrogPilotParamValueControl *CESpeed = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1.0, true); - FrogPilotParamValueControl *CESpeedLead = new FrogPilotParamValueControl("CESpeedLead", tr(" With Lead"), tr("Switch to 'Experimental Mode' below this speed when following a lead vehicle."), icon, 0, 99, tr("mph"), std::map(), 1.0, true); + FrogPilotParamValueControl *CESpeedLead = new FrogPilotParamValueControl("CESpeedLead", tr(" With Lead"), tr("Switches to 'Experimental Mode' when driving below the set speed with a lead vehicle."), icon, 0, 99, tr("mph"), std::map(), 1.0, true); FrogPilotDualParamControl *conditionalSpeeds = new FrogPilotDualParamControl(CESpeed, CESpeedLead); longitudinalToggle = reinterpret_cast(conditionalSpeeds); } else if (param == "CECurves") { @@ -86,10 +85,14 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, navigationToggles, navigationToggleNames); } else if (param == "CEModelStopTime") { std::map modelStopTimeLabels; - for (int i = 0; i <= 10; i++) { + for (int i = 0; i <= 10; ++i) { modelStopTimeLabels[i] = (i == 0) ? tr("Off") : QString::number(i) + " seconds"; } longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, QString(), modelStopTimeLabels); + } else if (param == "CESignalSpeed") { + std::vector ceSignalToggles{"CESignalLaneDetection"}; + std::vector ceSignalToggleNames{"Lane Detection"}; + longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1.0, ceSignalToggles, ceSignalToggleNames); } else if (param == "CurveSpeedControl") { FrogPilotParamManageControl *curveControlToggle = new FrogPilotParamManageControl(param, title, desc, icon); @@ -98,20 +101,45 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * curveDetectionBtn->setCheckedButton(0, params.getBool("MTSCEnabled")); curveDetectionBtn->setCheckedButton(1, params.getBool("VisionTurnControl")); - showToggles(curveSpeedKeys); + std::set modifiedCurveSpeedKeys = curveSpeedKeys; + + if (!params.getBool("MTSCEnabled")) { + modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck"); + } + + showToggles(modifiedCurveSpeedKeys); }); longitudinalToggle = curveControlToggle; } else if (param == "CurveDetectionMethod") { curveDetectionBtn = new FrogPilotButtonsControl(title, desc, {tr("Map Based"), tr("Vision")}, true, false); - connect(curveDetectionBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + QObject::connect(curveDetectionBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + bool mtscEnabled = params.getBool("MTSCEnabled"); + bool vtscEnabled = params.getBool("VisionTurnControl"); + if (id == 0) { - bool currentMTSCEnabled = params.getBool("MTSCEnabled"); - params.putBool("MTSCEnabled", !currentMTSCEnabled); - curveDetectionBtn->setCheckedButton(0, !currentMTSCEnabled); + if (mtscEnabled && !vtscEnabled) { + curveDetectionBtn->setCheckedButton(0, true); + return; + } + + params.putBool("MTSCEnabled", !mtscEnabled); + curveDetectionBtn->setCheckedButton(0, !mtscEnabled); + + std::set modifiedCurveSpeedKeys = curveSpeedKeys; + + if (mtscEnabled) { + modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck"); + } + + showToggles(modifiedCurveSpeedKeys); } else if (id == 1) { - bool currentVisionTurnControl = params.getBool("VisionTurnControl"); - params.putBool("VisionTurnControl", !currentVisionTurnControl); - curveDetectionBtn->setCheckedButton(1, !currentVisionTurnControl); + if (vtscEnabled && !mtscEnabled) { + curveDetectionBtn->setCheckedButton(1, true); + return; + } + + params.putBool("VisionTurnControl", !vtscEnabled); + curveDetectionBtn->setCheckedButton(1, !vtscEnabled); } }); longitudinalToggle = curveDetectionBtn; @@ -145,8 +173,8 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * std::vector profileOptions{tr("Standard"), tr("Eco"), tr("Sport")}; ButtonParamControl *profileSelection = new ButtonParamControl(param, title, desc, icon, profileOptions); longitudinalToggle = profileSelection; - } else if (param == "StoppingDistance") { - longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 15, 30, tr(" feet")); + } else if (param == "IncreasedStoppedDistance") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet")); } else if (param == "QOLLongitudinal") { FrogPilotParamManageControl *qolLongitudinalToggle = new FrogPilotParamManageControl(param, title, desc, icon); @@ -179,59 +207,54 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * } else if (param == "SpeedLimitController") { FrogPilotParamManageControl *speedLimitControllerToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(speedLimitControllerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + bool slcLower = params.getBool("SLCConfirmationLower"); + bool slcHigher = params.getBool("SLCConfirmationHigher"); + + slcConfirmationBtn->setCheckedButton(0, slcLower); + slcConfirmationBtn->setCheckedButton(1, slcHigher); + slcConfirmationBtn->setCheckedButton(2, !(slcLower || slcHigher)); + slcOpen = true; showToggles(speedLimitControllerKeys); }); longitudinalToggle = speedLimitControllerToggle; - } else if (param == "SLCControls") { - ButtonControl *manageSLCControlsBtn = new ButtonControl(title, tr("MANAGE"), desc); - QObject::connect(manageSLCControlsBtn, &ButtonControl::clicked, [this]() { - openSubParentToggle(); - showToggles(speedLimitControllerControlsKeys); - }); - longitudinalToggle = reinterpret_cast(manageSLCControlsBtn); - } else if (param == "SLCQOL") { - ButtonControl *manageSLCQOLBtn = new ButtonControl(title, tr("MANAGE"), desc); - QObject::connect(manageSLCQOLBtn, &ButtonControl::clicked, [this]() { - openSubParentToggle(); - std::set modifiedSpeedLimitControllerQOLKeys = speedLimitControllerQOLKeys; + } else if (param == "SLCConfirmation") { + slcConfirmationBtn = new FrogPilotButtonsControl(title, desc, {tr("Lower Limits"), tr("Higher Limits"), tr("None")}, true, false); + QObject::connect(slcConfirmationBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + bool lowerEnabled = params.getBool("SLCConfirmationLower"); + bool higherEnabled = params.getBool("SLCConfirmationHigher"); - if (hasPCMCruise) { - modifiedSpeedLimitControllerQOLKeys.erase("SetSpeedLimit"); - } + if (id == 0) { + params.putBool("SLCConfirmationLower", !lowerEnabled); + slcConfirmationBtn->setCheckedButton(0, !lowerEnabled); + slcConfirmationBtn->setCheckedButton(2, false); - if (!isToyota) { - modifiedSpeedLimitControllerQOLKeys.erase("ForceMPHDashboard"); - } + if (lowerEnabled & !higherEnabled) { + slcConfirmationBtn->setCheckedButton(2, true); + } + } else if (id == 1) { + params.putBool("SLCConfirmationHigher", !higherEnabled); + slcConfirmationBtn->setCheckedButton(1, !higherEnabled); + slcConfirmationBtn->setCheckedButton(2, false); - showToggles(modifiedSpeedLimitControllerQOLKeys); - }); - longitudinalToggle = reinterpret_cast(manageSLCQOLBtn); - } else if (param == "SLCConfirmation") { - std::vector slcConfirmationToggles{"SLCConfirmationLower", "SLCConfirmationHigher"}; - std::vector slcConfirmationNames{tr("Lower Limits"), tr("Higher Limits")}; - longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, slcConfirmationToggles, slcConfirmationNames); - } else if (param == "SLCLookaheadHigher" || param == "SLCLookaheadLower") { - longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 60, tr(" seconds")); - } else if (param == "SLCVisuals") { - ButtonControl *manageSLCVisualsBtn = new ButtonControl(title, tr("MANAGE"), desc); - QObject::connect(manageSLCVisualsBtn, &ButtonControl::clicked, [this]() { - openSubParentToggle(); - showToggles(speedLimitControllerVisualsKeys); + if (higherEnabled & !lowerEnabled) { + slcConfirmationBtn->setCheckedButton(2, true); + } + } else { + params.putBool("SLCConfirmationLower", false); + params.putBool("SLCConfirmationHigher", false); + slcConfirmationBtn->setCheckedButton(0, false); + slcConfirmationBtn->setCheckedButton(1, false); + slcConfirmationBtn->setCheckedButton(2, true); + } }); - longitudinalToggle = reinterpret_cast(manageSLCVisualsBtn); - } else if (param == "Offset1" || param == "Offset2" || param == "Offset3" || param == "Offset4") { - longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, -99, 99, tr("mph")); - } else if (param == "ShowSLCOffset") { - std::vector slcOffsetToggles{"ShowSLCOffsetUI"}; - std::vector slcOffsetToggleNames{tr("Control Via UI")}; - longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, slcOffsetToggles, slcOffsetToggleNames); + longitudinalToggle = slcConfirmationBtn; } else if (param == "SLCFallback") { std::vector fallbackOptions{tr("Set Speed"), tr("Experimental Mode"), tr("Previous Limit")}; ButtonParamControl *fallbackSelection = new ButtonParamControl(param, title, desc, icon, fallbackOptions); longitudinalToggle = fallbackSelection; } else if (param == "SLCOverride") { - std::vector overrideOptions{tr("None"), tr("Manual Set Speed"), tr("Set Speed")}; + std::vector overrideOptions{tr("None"), tr("Gas Pedal Press"), tr("Cruise Set Speed")}; ButtonParamControl *overrideSelection = new ButtonParamControl(param, title, desc, icon, overrideOptions); longitudinalToggle = overrideSelection; } else if (param == "SLCPriority") { @@ -243,7 +266,7 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * QObject::connect(slcPriorityButton, &ButtonControl::clicked, [=]() { QStringList selectedPriorities; - for (int i = 1; i <= 3; i++) { + for (int i = 1; i <= 3; ++i) { QStringList currentPriorities = (i == 1) ? primaryPriorities : secondaryTertiaryPriorities; QStringList prioritiesToDisplay = currentPriorities; for (const auto &selectedPriority : qAsConst(selectedPriorities)) { @@ -276,7 +299,7 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * }); QStringList initialPriorities; - for (int i = 1; i <= 3; i++) { + for (int i = 1; i <= 3; ++i) { QString priorityKey = QString("SLCPriority%1").arg(i); QString priority = QString::fromStdString(params.get(priorityKey.toStdString())); @@ -286,6 +309,45 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * } slcPriorityButton->setValue(initialPriorities.join(", ")); longitudinalToggle = slcPriorityButton; + } else if (param == "SLCOffsets") { + ButtonControl *manageSLCOffsetsBtn = new ButtonControl(title, tr("MANAGE"), desc); + QObject::connect(manageSLCOffsetsBtn, &ButtonControl::clicked, [this]() { + openSubParentToggle(); + showToggles(speedLimitControllerOffsetsKeys); + }); + longitudinalToggle = reinterpret_cast(manageSLCOffsetsBtn); + } else if (param == "SLCQOL") { + ButtonControl *manageSLCQOLBtn = new ButtonControl(title, tr("MANAGE"), desc); + QObject::connect(manageSLCQOLBtn, &ButtonControl::clicked, [this]() { + openSubParentToggle(); + std::set modifiedSpeedLimitControllerQOLKeys = speedLimitControllerQOLKeys; + + if (hasPCMCruise) { + modifiedSpeedLimitControllerQOLKeys.erase("SetSpeedLimit"); + } + + if (!isToyota) { + modifiedSpeedLimitControllerQOLKeys.erase("ForceMPHDashboard"); + } + + showToggles(modifiedSpeedLimitControllerQOLKeys); + }); + longitudinalToggle = reinterpret_cast(manageSLCQOLBtn); + } else if (param == "SLCLookaheadHigher" || param == "SLCLookaheadLower") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 60, tr(" seconds")); + } else if (param == "SLCVisuals") { + ButtonControl *manageSLCVisualsBtn = new ButtonControl(title, tr("MANAGE"), desc); + QObject::connect(manageSLCVisualsBtn, &ButtonControl::clicked, [this]() { + openSubParentToggle(); + showToggles(speedLimitControllerVisualsKeys); + }); + longitudinalToggle = reinterpret_cast(manageSLCVisualsBtn); + } else if (param == "Offset1" || param == "Offset2" || param == "Offset3" || param == "Offset4") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, -99, 99, tr("mph")); + } else if (param == "ShowSLCOffset") { + std::vector slcOffsetToggles{"ShowSLCOffsetUI"}; + std::vector slcOffsetToggleNames{tr("Control Via UI")}; + longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, slcOffsetToggles, slcOffsetToggleNames); } else { longitudinalToggle = new ParamControl(param, title, desc, icon); @@ -338,8 +400,9 @@ void FrogPilotLongitudinalPanel::updateMetric() { double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE; - params.putFloatNonBlocking("StoppingDistance", params.getFloat("StoppingDistance") * distanceConversion); + params.putFloatNonBlocking("IncreasedStoppedDistance", params.getFloat("IncreasedStoppedDistance") * distanceConversion); + params.putFloatNonBlocking("CESignalSpeed", params.getFloat("CESignalSpeed") * speedConversion); params.putFloatNonBlocking("CESpeed", params.getFloat("CESpeed") * speedConversion); params.putFloatNonBlocking("CESpeedLead", params.getFloat("CESpeedLead") * speedConversion); params.putFloatNonBlocking("CustomCruise", params.getFloat("CustomCruise") * speedConversion); @@ -351,13 +414,14 @@ void FrogPilotLongitudinalPanel::updateMetric() { } FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast(toggles["CESpeed"]); + FrogPilotParamValueButtonControl *ceSignal = reinterpret_cast(toggles["CESignalSpeed"]); FrogPilotParamValueControl *customCruiseToggle = static_cast(toggles["CustomCruise"]); FrogPilotParamValueControl *customCruiseLongToggle = static_cast(toggles["CustomCruiseLong"]); FrogPilotParamValueControl *offset1Toggle = static_cast(toggles["Offset1"]); FrogPilotParamValueControl *offset2Toggle = static_cast(toggles["Offset2"]); FrogPilotParamValueControl *offset3Toggle = static_cast(toggles["Offset3"]); FrogPilotParamValueControl *offset4Toggle = static_cast(toggles["Offset4"]); - FrogPilotParamValueControl *stoppingDistanceToggle = static_cast(toggles["StoppingDistance"]); + FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast(toggles["IncreasedStoppedDistance"]); if (isMetric) { offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)")); @@ -370,6 +434,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset3Toggle->setDescription(tr("Set speed limit offset for limits between 55-64 kph.")); offset4Toggle->setDescription(tr("Set speed limit offset for limits between 65-99 kph.")); + ceSignal->updateControl(0, 150, tr("kph")); ceSpeedToggle->updateControl(0, 150, tr("kph")); customCruiseToggle->updateControl(1, 150, tr("kph")); customCruiseLongToggle->updateControl(1, 150, tr("kph")); @@ -378,7 +443,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset3Toggle->updateControl(-99, 99, tr("kph")); offset4Toggle->updateControl(-99, 99, tr("kph")); - stoppingDistanceToggle->updateControl(5, 10, tr(" meters")); + increasedStoppedDistanceToggle->updateControl(0, 5, tr(" meters")); } else { offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 mph)")); offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 mph)")); @@ -390,6 +455,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset3Toggle->setDescription(tr("Set speed limit offset for limits between 55-64 mph.")); offset4Toggle->setDescription(tr("Set speed limit offset for limits between 65-99 mph.")); + ceSignal->updateControl(0, 99, tr("mph")); ceSpeedToggle->updateControl(0, 99, tr("mph")); customCruiseToggle->updateControl(1, 99, tr("mph")); customCruiseLongToggle->updateControl(1, 99, tr("mph")); @@ -398,7 +464,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset3Toggle->updateControl(-99, 99, tr("mph")); offset4Toggle->updateControl(-99, 99, tr("mph")); - stoppingDistanceToggle->updateControl(15, 30, tr(" feet")); + increasedStoppedDistanceToggle->updateControl(0, 15, tr(" feet")); } } @@ -425,7 +491,7 @@ void FrogPilotLongitudinalPanel::hideToggles() { longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() || qolKeys.find(key) != qolKeys.end() || speedLimitControllerKeys.find(key) != speedLimitControllerKeys.end() || - speedLimitControllerControlsKeys.find(key) != speedLimitControllerControlsKeys.end() || + speedLimitControllerOffsetsKeys.find(key) != speedLimitControllerOffsetsKeys.end() || speedLimitControllerQOLKeys.find(key) != speedLimitControllerQOLKeys.end() || speedLimitControllerVisualsKeys.find(key) != speedLimitControllerVisualsKeys.end(); diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h index 6d544a99633971..2b96dabaee1eec 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h @@ -17,23 +17,53 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideSubToggles(); - void hideToggles(); - void showToggles(const std::set &keys); - void updateCarToggles(); - void updateMetric(); - FrogPilotButtonsControl *curveDetectionBtn; + FrogPilotButtonsControl *slcConfirmationBtn; + + std::set conditionalExperimentalKeys = { + "CESpeed", "CESpeedLead", "CECurves", "CELead", + "CEModelStopTime", "CENavigation", "CESignalSpeed", + "HideCEMStatusBar" + }; + + std::set curveSpeedKeys = { + "CurveDetectionMethod", "CurveSensitivity", + "DisableCurveSpeedSmoothing", "MTSCCurvatureCheck", + "TurnAggressiveness" + }; + + std::set experimentalModeActivationKeys = { + "ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", + "ExperimentalModeViaTap" + }; + + std::set longitudinalTuneKeys = { + "AccelerationProfile", "DecelerationProfile", + "HumanAcceleration", "HumanFollowing", "IncreasedStoppedDistance" + }; - std::set conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignal", "HideCEMStatusBar"}; - std::set curveSpeedKeys = {"CurveDetectionMethod", "CurveSensitivity", "DisableCurveSpeedSmoothing", "MTSCCurvatureCheck", "TurnAggressiveness"}; - std::set experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"}; - std::set longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "StoppingDistance"}; - std::set qolKeys = {"CustomCruise", "CustomCruiseLong", "MapGears", "OnroadDistanceButton", "ReverseCruise"}; - std::set speedLimitControllerKeys = {"SLCControls", "SLCQOL", "SLCVisuals"}; - std::set speedLimitControllerControlsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "SLCFallback", "SLCOverride", "SLCPriority"}; - std::set speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower"}; - std::set speedLimitControllerVisualsKeys = {"ShowSLCOffset", "SpeedLimitChangedAlert", "UseVienna"}; + std::set qolKeys = { + "CustomCruise", "CustomCruiseLong", "MapGears", + "OnroadDistanceButton", "ReverseCruise" + }; + + std::set speedLimitControllerKeys = { + "SLCConfirmation", "SLCOffsets", "SLCFallback", "SLCOverride", + "SLCPriority", "SLCQOL", "SLCVisuals" + }; + + std::set speedLimitControllerOffsetsKeys = { + "Offset1", "Offset2", "Offset3", "Offset4" + }; + + std::set speedLimitControllerQOLKeys = { + "ForceMPHDashboard", "SetSpeedLimit", "SLCLookaheadHigher", + "SLCLookaheadLower" + }; + + std::set speedLimitControllerVisualsKeys = { + "ShowSLCOffset", "UseVienna" + }; std::map toggles; @@ -47,4 +77,10 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { bool isSubaru; bool isToyota; bool slcOpen; + + void hideSubToggles(); + void hideToggles(); + void showToggles(const std::set &keys); + void updateCarToggles(); + void updateMetric(); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc index f50edcdcbf7ff2..4aaf032cd40de2 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc @@ -16,6 +16,7 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr {"GreenLightAlert", tr("Green Light Alert"), tr("Get an alert when a traffic light changes from red to green."), ""}, {"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Get an alert when the lead vehicle starts departing when at a standstill."), ""}, {"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Enable a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""}, + {"SpeedLimitChangedAlert", tr("Speed Limit Change Alert"), tr("Trigger an alert when the speed limit changes."), ""}, }; for (const auto &[param, title, desc, icon] : soundsToggles) { @@ -43,6 +44,10 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr modifiedCustomAlertsKeys.erase("LoudBlindspotAlert"); } + if (!(hasOpenpilotLongitudinal && params.getBool("SpeedLimitController"))) { + modifiedCustomAlertsKeys.erase("SpeedLimitChangedAlert"); + } + showToggles(modifiedCustomAlertsKeys); }); soundsToggle = customAlertsToggle; @@ -71,6 +76,7 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr void FrogPilotSoundsPanel::updateCarToggles() { hasBSM = parent->hasBSM; + hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; hideToggles(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h index 0691bc0b153e18..3993da135d5bc6 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h @@ -16,16 +16,25 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showToggles(const std::set &keys); - void updateCarToggles(); + std::set alertVolumeControlKeys = { + "DisengageVolume", "EngageVolume", "PromptDistractedVolume", + "PromptVolume", "RefuseVolume", "WarningImmediateVolume", + "WarningSoftVolume" + }; - std::set alertVolumeControlKeys = {"DisengageVolume", "EngageVolume", "PromptDistractedVolume", "PromptVolume", "RefuseVolume", "WarningImmediateVolume", "WarningSoftVolume"}; - std::set customAlertsKeys = {"GoatScream", "GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert"}; + std::set customAlertsKeys = { + "GoatScream", "GreenLightAlert", "LeadDepartingAlert", + "LoudBlindspotAlert", "SpeedLimitChangedAlert" + }; std::map toggles; Params params; bool hasBSM; + bool hasOpenpilotLongitudinal; + + void hideToggles(); + void showToggles(const std::set &keys); + void updateCarToggles(); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc index ef289f9407d534..6c9aa3f47dc5dc 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc @@ -2,20 +2,20 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> themeToggles { - {"HolidayThemes", tr("Holiday Themes"), tr("The openpilot theme changes according to the current/upcoming holiday. Minor holidays last a day, while major holidays (Easter, Christmas, Halloween, etc.) last a week."), "../frogpilot/assets/toggle_icons/icon_calendar.png"}, - - {"PersonalizeOpenpilot", tr("Custom Theme"), tr("Customize openpilot to your personal tastes!"), "../frogpilot/assets/toggle_icons/frog.png"}, - {"CustomColors", tr("Color Theme"), tr("Switch out the standard openpilot color scheme with themed colors.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""}, - {"CustomDistanceIcon", "Distance Button", "Switch out the standard distance button icons with a set of themed icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""}, - {"CustomIcons", tr("Icon Pack"), tr("Switch out the standard openpilot icons with a set of themed icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""}, - {"CustomSounds", tr("Sound Pack"), tr("Switch out the standard openpilot sounds with a set of themed sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""}, - {"WheelIcon", tr("Steering Wheel"), tr("Replace the default steering wheel icon with a custom icon."), ""}, - {"CustomSignals", tr("Turn Signals"), tr("Add themed animation for your turn signals.\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!"), ""}, + {"PersonalizeOpenpilot", tr("Custom Theme"), tr("Custom openpilot themes."), "../frogpilot/assets/toggle_icons/frog.png"}, + {"CustomColors", tr("Color Scheme"), tr("Themed color schemes.\n\nWant to submit your own color scheme? Share it in the 'feature-request' channel on the FrogPilot Discord!"), ""}, + {"CustomDistanceIcon", "Distance Button Icons", "Themed distance button icons.\n\nWant to submit your own icon pack? Share it in the 'feature-request' channel on the FrogPilot Discord!", ""}, + {"CustomIcons", tr("Icon Pack"), tr("Themed icon packs.\n\nWant to submit your own icons? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, + {"CustomSounds", tr("Sound Pack"), tr("Themed sound effects.\n\nWant to submit your own sounds? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, + {"WheelIcon", tr("Steering Wheel"), tr("Custom steering wheel icons."), ""}, + {"CustomSignals", tr("Turn Signal Animation"), tr("Themed turn signal animations.\n\nWant to submit your own animations? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, {"DownloadStatusLabel", tr("Download Status"), "", ""}, - {"RandomEvents", tr("Random Events"), tr("Enjoy a bit of unpredictability with random events that can occur during certain driving conditions. This is purely cosmetic and has no impact on driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"}, + {"HolidayThemes", tr("Holiday Themes"), tr("Change the openpilot theme based on the current holiday. Minor holidays last one day, while major holidays (Easter, Christmas, Halloween, etc.) last a week."), "../frogpilot/assets/toggle_icons/icon_calendar.png"}, - {"StartupAlert", tr("Startup Alert"), tr("Customize the 'Startup' alert message that is shown when you go onroad."), "../frogpilot/assets/toggle_icons/icon_message.png"}, + {"RandomEvents", tr("Random Events"), tr("Random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"}, + + {"StartupAlert", tr("Startup Alert"), tr("Custom 'Startup' alert message that appears when you start driving."), "../frogpilot/assets/toggle_icons/icon_message.png"} }; for (const auto &[param, title, desc, icon] : themeToggles) { @@ -60,7 +60,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentColor = QString::fromStdString(params.get("CustomColors")).replace('_', ' ').replace('-', " (").toLower(); currentColor[0] = currentColor[0].toUpper(); - for (int i = 1; i < currentColor.length(); i++) { + for (int i = 1; i < currentColor.length(); ++i) { if (currentColor[i - 1] == ' ' || currentColor[i - 1] == '(') { currentColor[i] = currentColor[i].toUpper(); } @@ -153,7 +153,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentColor = QString::fromStdString(params.get("CustomColors")).replace('_', ' ').replace('-', " (").toLower(); currentColor[0] = currentColor[0].toUpper(); - for (int i = 1; i < currentColor.length(); i++) { + for (int i = 1; i < currentColor.length(); ++i) { if (currentColor[i - 1] == ' ' || currentColor[i - 1] == '(') { currentColor[i] = currentColor[i].toUpper(); } @@ -195,7 +195,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentDistanceIcon = QString::fromStdString(params.get("CustomDistanceIcons")).replace('_', ' ').replace('-', " (").toLower(); currentDistanceIcon[0] = currentDistanceIcon[0].toUpper(); - for (int i = 1; i < currentDistanceIcon.length(); i++) { + for (int i = 1; i < currentDistanceIcon.length(); ++i) { if (currentDistanceIcon[i - 1] == ' ' || currentDistanceIcon[i - 1] == '(') { currentDistanceIcon[i] = currentDistanceIcon[i].toUpper(); } @@ -281,7 +281,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentDistanceIcon = QString::fromStdString(params.get("CustomDistanceIcons")).replace('_', ' ').replace('-', " (").toLower(); currentDistanceIcon[0] = currentDistanceIcon[0].toUpper(); - for (int i = 1; i < currentDistanceIcon.length(); i++) { + for (int i = 1; i < currentDistanceIcon.length(); ++i) { if (currentDistanceIcon[i - 1] == ' ' || currentDistanceIcon[i - 1] == '(') { currentDistanceIcon[i] = currentDistanceIcon[i].toUpper(); } @@ -323,7 +323,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentIcon = QString::fromStdString(params.get("CustomIcons")).replace('_', ' ').replace('-', " (").toLower(); currentIcon[0] = currentIcon[0].toUpper(); - for (int i = 1; i < currentIcon.length(); i++) { + for (int i = 1; i < currentIcon.length(); ++i) { if (currentIcon[i - 1] == ' ' || currentIcon[i - 1] == '(') { currentIcon[i] = currentIcon[i].toUpper(); } @@ -414,7 +414,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentIcon = QString::fromStdString(params.get("CustomIcons")).replace('_', ' ').replace('-', " (").toLower(); currentIcon[0] = currentIcon[0].toUpper(); - for (int i = 1; i < currentIcon.length(); i++) { + for (int i = 1; i < currentIcon.length(); ++i) { if (currentIcon[i - 1] == ' ' || currentIcon[i - 1] == '(') { currentIcon[i] = currentIcon[i].toUpper(); } @@ -456,7 +456,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentSignal = QString::fromStdString(params.get("CustomSignals")).replace('_', ' ').replace('-', " (").toLower(); currentSignal[0] = currentSignal[0].toUpper(); - for (int i = 1; i < currentSignal.length(); i++) { + for (int i = 1; i < currentSignal.length(); ++i) { if (currentSignal[i - 1] == ' ' || currentSignal[i - 1] == '(') { currentSignal[i] = currentSignal[i].toUpper(); } @@ -547,7 +547,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentSignal = QString::fromStdString(params.get("CustomSignals")).replace('_', ' ').replace('-', " (").toLower(); currentSignal[0] = currentSignal[0].toUpper(); - for (int i = 1; i < currentSignal.length(); i++) { + for (int i = 1; i < currentSignal.length(); ++i) { if (currentSignal[i - 1] == ' ' || currentSignal[i - 1] == '(') { currentSignal[i] = currentSignal[i].toUpper(); } @@ -589,7 +589,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentSound = QString::fromStdString(params.get("CustomSounds")).replace('_', ' ').replace('-', " (").toLower(); currentSound[0] = currentSound[0].toUpper(); - for (int i = 1; i < currentSound.length(); i++) { + for (int i = 1; i < currentSound.length(); ++i) { if (currentSound[i - 1] == ' ' || currentSound[i - 1] == '(') { currentSound[i] = currentSound[i].toUpper(); } @@ -680,7 +680,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentSound = QString::fromStdString(params.get("CustomSounds")).replace('_', ' ').replace('-', " (").toLower(); currentSound[0] = currentSound[0].toUpper(); - for (int i = 1; i < currentSound.length(); i++) { + for (int i = 1; i < currentSound.length(); ++i) { if (currentSound[i - 1] == ' ' || currentSound[i - 1] == '(') { currentSound[i] = currentSound[i].toUpper(); } @@ -722,7 +722,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentWheel = QString::fromStdString(params.get("WheelIcon")).replace('_', ' ').replace('-', " (").toLower(); currentWheel[0] = currentWheel[0].toUpper(); - for (int i = 1; i < currentWheel.length(); i++) { + for (int i = 1; i < currentWheel.length(); ++i) { if (currentWheel[i - 1] == ' ' || currentWheel[i - 1] == '(') { currentWheel[i] = currentWheel[i].toUpper(); } @@ -813,7 +813,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr QString currentWheel = QString::fromStdString(params.get("WheelIcon")).replace('_', ' ').replace('-', " (").toLower(); currentWheel[0] = currentWheel[0].toUpper(); - for (int i = 1; i < currentWheel.length(); i++) { + for (int i = 1; i < currentWheel.length(); ++i) { if (currentWheel[i - 1] == ' ' || currentWheel[i - 1] == '(') { currentWheel[i] = currentWheel[i].toUpper(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h index c18cbaf4fa8428..dc1e8f364a11f2 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h @@ -10,18 +10,15 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { public: explicit FrogPilotThemesPanel(FrogPilotSettingsWindow *parent); +protected: + void showEvent(QShowEvent *event) override; + signals: void openParentToggle(); private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showEvent(QShowEvent *event) override; - void showToggles(const std::set &keys); - void updateCarToggles(); - void updateState(const UIState &s); - FrogPilotButtonsControl *manageCustomColorsBtn; FrogPilotButtonsControl *manageCustomIconsBtn; FrogPilotButtonsControl *manageCustomSignalsBtn; @@ -31,7 +28,11 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { LabelControl *downloadStatusLabel; - std::set customThemeKeys = {"CustomColors", "CustomDistanceIcon", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"}; + std::set customThemeKeys = { + "CustomColors", "CustomDistanceIcon", "CustomIcons", + "CustomSignals", "CustomSounds", "DownloadStatusLabel", + "WheelIcon" + }; std::map toggles; @@ -56,4 +57,9 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { bool themeDownloading; bool wheelDownloading; bool wheelsDownloaded; + + void hideToggles(); + void showToggles(const std::set &keys); + void updateCarToggles(); + void updateState(const UIState &s); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc index 79b529150f310c..1af7af85fc0996 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc @@ -4,7 +4,7 @@ UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { ButtonControl *flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to flash the Panda device's firmware if you're running into issues.")); - connect(flashPandaBtn, &ButtonControl::clicked, [=]() { + QObject::connect(flashPandaBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) { std::thread([=]() { device()->resetInteractiveTimeout(300); @@ -27,7 +27,7 @@ UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListW addItem(flashPandaBtn); forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Force openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); - connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { if (id == 0) { paramsMemory.putBool("ForceOffroad", true); paramsMemory.putBool("ForceOnroad", false); @@ -43,8 +43,8 @@ UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListW forceStartedBtn->setCheckedButton(2); addItem(forceStartedBtn); - ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles To Default"), tr("RESET"), tr("Reset your toggle settings back to their default settings.")); - connect(resetTogglesBtn, &ButtonControl::clicked, [=]() { + ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Reset your toggle settings back to their default settings.")); + QObject::connect(resetTogglesBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to completely reset all of your toggle settings?"), tr("Reset"), this)) { std::thread([=] { resetTogglesBtn->setEnabled(false); diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.h b/selfdrive/frogpilot/ui/qt/offroad/utilities.h index 5e5762c99683ec..a5b78f6c0de22c 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/utilities.h +++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.h @@ -4,14 +4,15 @@ class UtilitiesPanel : public FrogPilotListWidget { Q_OBJECT + public: explicit UtilitiesPanel(FrogPilotSettingsWindow *parent); private: FrogPilotSettingsWindow *parent; + FrogPilotButtonsControl *forceStartedBtn; + Params params; Params paramsMemory{"/dev/shm/params"}; - - FrogPilotButtonsControl *forceStartedBtn; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc index d9f1477791e518..3b49c754e5acd7 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc @@ -5,52 +5,53 @@ #include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h" QStringList getCarNames(const QString &carMake, QMap &carModels) { - QMap makeMap; - makeMap["acura"] = "honda"; - makeMap["audi"] = "volkswagen"; - makeMap["buick"] = "gm"; - makeMap["cadillac"] = "gm"; - makeMap["chevrolet"] = "gm"; - makeMap["chrysler"] = "chrysler"; - makeMap["dodge"] = "chrysler"; - makeMap["ford"] = "ford"; - makeMap["genesis"] = "hyundai"; - makeMap["gmc"] = "gm"; - makeMap["holden"] = "gm"; - makeMap["honda"] = "honda"; - makeMap["hyundai"] = "hyundai"; - makeMap["jeep"] = "chrysler"; - makeMap["kia"] = "hyundai"; - makeMap["lexus"] = "toyota"; - makeMap["lincoln"] = "ford"; - makeMap["man"] = "volkswagen"; - makeMap["mazda"] = "mazda"; - makeMap["nissan"] = "nissan"; - makeMap["ram"] = "chrysler"; - makeMap["seat"] = "volkswagen"; - makeMap["škoda"] = "volkswagen"; - makeMap["subaru"] = "subaru"; - makeMap["tesla"] = "tesla"; - makeMap["toyota"] = "toyota"; - makeMap["volkswagen"] = "volkswagen"; - - QString targetFolder = makeMap.value(carMake, carMake); + static const QMap makeMap = { + {"acura", "honda"}, + {"audi", "volkswagen"}, + {"buick", "gm"}, + {"cadillac", "gm"}, + {"chevrolet", "gm"}, + {"chrysler", "chrysler"}, + {"dodge", "chrysler"}, + {"ford", "ford"}, + {"genesis", "hyundai"}, + {"gmc", "gm"}, + {"holden", "gm"}, + {"honda", "honda"}, + {"hyundai", "hyundai"}, + {"jeep", "chrysler"}, + {"kia", "hyundai"}, + {"lexus", "toyota"}, + {"lincoln", "ford"}, + {"man", "volkswagen"}, + {"mazda", "mazda"}, + {"nissan", "nissan"}, + {"ram", "chrysler"}, + {"seat", "volkswagen"}, + {"škoda", "volkswagen"}, + {"subaru", "subaru"}, + {"tesla", "tesla"}, + {"toyota", "toyota"}, + {"volkswagen", "volkswagen"} + }; + + QString targetFolder = makeMap.value(carMake.toLower(), carMake); QFile file(QString("../car/%1/values.py").arg(targetFolder)); QStringList names; QSet uniqueNames; - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return names; + } - QTextStream in(&file); - QString fileContent = in.readAll(); + QString fileContent = QTextStream(&file).readAll(); file.close(); fileContent.remove(QRegularExpression("#[^\n]*")); fileContent.remove(QRegularExpression("footnotes=\\[[^\\]]*\\],\\s*")); - QRegularExpression carModelRegex(R"delimiter((\w+)\s*=\s*\w+\s*\(\s*\[([\s\S]*?)\]\s*,)delimiter"); - QRegularExpression carDocsRegex(R"delimiter(CarDocs\(\s*"([^"]+)"[^)]*\))delimiter"); + QRegularExpression carModelRegex(R"((\w+)\s*=\s*\w+\s*\(\s*\[([\s\S]*?)\]\s*,)"); + QRegularExpression carDocsRegex("CarDocs\\(\\s*\"([^\"]+)\"[^)]*\\)"); QRegularExpressionMatchIterator carModelIt = carModelRegex.globalMatch(fileContent); while (carModelIt.hasNext()) { @@ -60,17 +61,14 @@ QStringList getCarNames(const QString &carMake, QMap &carModel QRegularExpressionMatchIterator carDocsIt = carDocsRegex.globalMatch(platformSection); while (carDocsIt.hasNext()) { - QRegularExpressionMatch match = carDocsIt.next(); - QString carName = match.captured(1); + QString carName = carDocsIt.next().captured(1); if (carName.contains(QRegularExpression("^[A-Za-z0-9 Š.()-]+$")) && carName.count(" ") >= 1) { QStringList nameParts = carName.split(" "); - if (nameParts.contains(carMake, Qt::CaseInsensitive)) { - if (!uniqueNames.contains(carName)) { - names << carName; - carModels[carName] = platform; - uniqueNames.insert(carName); - } + if (nameParts.contains(carMake, Qt::CaseInsensitive) && !uniqueNames.contains(carName)) { + uniqueNames.insert(carName); + names << carName; + carModels[carName] = platform; } } } @@ -81,14 +79,13 @@ QStringList getCarNames(const QString &carMake, QMap &carModel } FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { + QStringList makes = { + "Acura", "Audi", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "Genesis", + "GMC", "Holden", "Honda", "Hyundai", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda", + "Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen" + }; selectMakeButton = new ButtonControl(tr("Select Make"), tr("SELECT")); - QObject::connect(selectMakeButton, &ButtonControl::clicked, [this]() { - QStringList makes = { - "Acura", "Audi", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "Genesis", - "GMC", "Holden", "Honda", "Hyundai", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda", - "Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen", - }; - + QObject::connect(selectMakeButton, &ButtonControl::clicked, [this, makes]() { QString newMakeSelection = MultiOptionDialog::getSelection(tr("Select a Make"), makes, "", this); if (!newMakeSelection.isEmpty()) { carMake = newMakeSelection; @@ -137,19 +134,19 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) addItem(disableOpenpilotLong); std::vector> vehicleToggles { - {"ExperimentalGMTune", tr("Experimental GM Tune"), tr("FrogsGoMoo's experimental GM tune that is based on nothing but guesswork. Use at your own risk."), ""}, - {"LongPitch", tr("Long Pitch Compensation"), tr("Smoothen out the gas and pedal controls."), ""}, - {"VoltSNG", tr("2017 Volt SNG"), tr("Enable the 'Stop and Go' hack for 2017 Chevy Volts."), ""}, - {"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Use comma's new longitudinal controls that have shown great improvement with acceleration and braking, but has a few issues on some GM vehicles."), ""}, + {"VoltSNG", tr("2017 Volt Stop and Go Hack"), tr("Force stop and go for the 2017 Chevy Volt."), ""}, + {"ExperimentalGMTune", tr("Experimental GM Tune"), tr("FrogsGoMoo's experimental GM tune that is based on nothing but guesswork. Use at your own risk!"), ""}, + {"LongPitch", tr("Uphill/Downhill Smoothing"), tr("Smoothen the car’s gas and brake response when driving on slopes."), ""}, + {"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has a few issues on some GM vehicles."), ""}, - {"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Use comma's new longitudinal controls that have shown great improvement with acceleration and braking, but has a few issues on Hyundai/Kia/Genesis."), ""}, + {"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Use comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has a few issues on Hyundai/Kia/Genesis."), ""}, {"CrosstrekTorque", tr("Subaru Crosstrek Torque Increase"), tr("Increases the maximum allowed torque for the Subaru Crosstrek."), ""}, {"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""}, - {"ClusterOffset", tr("Cluster Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""}, - {"NewToyotaTune", tr("comma's New Toyota Tune"), tr("Activate Comma's latest Toyota tuning, expertly crafted by Shane for enhanced vehicle performance."), ""}, - {"SNGHack", tr("Stop and Go Hack"), tr("Enable the 'Stop and Go' hack for vehicles without stock stop and go functionality."), ""}, + {"ClusterOffset", tr("Cluster Speed Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""}, + {"NewToyotaTune", tr("comma's New Toyota/Lexus Tune"), tr("Activate comma's latest Toyota tuning, expertly crafted by Shane for enhanced vehicle performance."), ""}, + {"SNGHack", tr("Stop and Go Hack"), tr("Force stop and go for vehicles without stock stop and go functionality."), ""}, }; for (const auto &[param, title, desc, icon] : vehicleToggles) { @@ -189,9 +186,9 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) updateFrogPilotToggles(); }); - std::set rebootKeys = {"CrosstrekTorque"}; + std::set rebootKeys = {"CrosstrekTorque", "ExperimentalGMTune", "NewLongAPI", "NewLongAPIGM", "NewToyotaTune"}; for (const QString &key : rebootKeys) { - QObject::connect(static_cast(toggles[key.toStdString().c_str()]), &ToggleControl::toggleFlipped, [this]() { + QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, [this]() { if (started) { if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) { Hardware::reboot(); @@ -211,8 +208,6 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) }).detach(); }); - QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState); - carMake = QString::fromStdString(params.get("CarMake")); carModel = QString::fromStdString(params.get(params.get("CarModelName").empty() ? "CarModel" : "CarModelName")); @@ -221,9 +216,11 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) } QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotVehiclesPanel::updateCarToggles); + QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState); } void FrogPilotVehiclesPanel::updateCarToggles() { + disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; hasExperimentalOpenpilotLongitudinal = parent->hasExperimentalOpenpilotLongitudinal; hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; hasSNG = parent->hasSNG; @@ -231,7 +228,7 @@ void FrogPilotVehiclesPanel::updateCarToggles() { isImpreza = parent->isImpreza; isVolt = parent->isVolt; - hideToggles(); + updateToggles(); } void FrogPilotVehiclesPanel::updateState(const UIState &s) { @@ -242,13 +239,13 @@ void FrogPilotVehiclesPanel::updateState(const UIState &s) { void FrogPilotVehiclesPanel::setModels() { models = getCarNames(carMake.toLower(), carModels); - hideToggles(); + updateToggles(); } -void FrogPilotVehiclesPanel::hideToggles() { +void FrogPilotVehiclesPanel::updateToggles() { setUpdatesEnabled(false); - disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise) || params.getBool("DisableOpenpilotLongitudinal")); + disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise) || disableOpenpilotLongitudinal); selectMakeButton->setValue(carMake); selectModelButton->setValue(carModel); @@ -259,39 +256,42 @@ void FrogPilotVehiclesPanel::hideToggles() { bool subaru = carMake == "Subaru"; bool toyota = carMake == "Lexus" || carMake == "Toyota"; - std::set imprezaKeys = {"CrosstrekTorque"}; - std::set longitudinalKeys = {"LongPitch", "SNGHack"}; - std::set sngKeys = {"SNGHack"}; - std::set voltKeys = {"VoltSNG"}; - for (auto &[key, toggle] : toggles) { - toggle->setVisible(false); + bool setVisible = false; - if ((!hasOpenpilotLongitudinal || params.getBool("DisableOpenpilotLongitudinal")) && longitudinalKeys.find(key) != longitudinalKeys.end()) { - continue; - } - - if (hasSNG && sngKeys.find(key) != sngKeys.end()) { - continue; - } - - if (!isImpreza && imprezaKeys.find(key) != imprezaKeys.end()) { - continue; - } - - if (!isVolt && voltKeys.find(key) != voltKeys.end()) { - continue; + if (gm && gmKeys.find(key) != gmKeys.end()) { + if (voltKeys.find(key) != voltKeys.end()) { + setVisible = isVolt && hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else if (longitudinalKeys.find(key) != longitudinalKeys.end()) { + setVisible = hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else { + setVisible = true; + } + } else if (hyundai && hyundaiKeys.find(key) != hyundaiKeys.end()) { + if (longitudinalKeys.find(key) != longitudinalKeys.end()) { + setVisible = hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else { + setVisible = true; + } + } else if (subaru && subaruKeys.find(key) != subaruKeys.end()) { + if (imprezaKeys.find(key) != imprezaKeys.end()) { + setVisible = isImpreza; + } else if (longitudinalKeys.find(key) != longitudinalKeys.end()) { + setVisible = hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else { + setVisible = true; + } + } else if (toyota && toyotaKeys.find(key) != toyotaKeys.end()) { + if (sngKeys.find(key) != sngKeys.end()) { + setVisible = !hasSNG && hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else if (longitudinalKeys.find(key) != longitudinalKeys.end()) { + setVisible = hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal; + } else { + setVisible = true; + } } - if (hyundai) { - toggle->setVisible(hyundaiKeys.find(key) != hyundaiKeys.end()); - } else if (gm) { - toggle->setVisible(gmKeys.find(key) != gmKeys.end()); - } else if (subaru) { - toggle->setVisible(subaruKeys.find(key) != subaruKeys.end()); - } else if (toyota) { - toggle->setVisible(toyotaKeys.find(key) != toyotaKeys.end()); - } + toggle->setVisible(setVisible); } setUpdatesEnabled(true); diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h index 49763ce5a35f70..856cb1632ec132 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h @@ -2,11 +2,6 @@ #include -#include - -#include "selfdrive/ui/qt/offroad/settings.h" -#include "selfdrive/ui/ui.h" - #include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h" class FrogPilotVehiclesPanel : public FrogPilotListWidget { @@ -18,16 +13,46 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void setModels(); - void updateCarToggles(); - void updateState(const UIState &s); - ButtonControl *selectMakeButton; ButtonControl *selectModelButton; ToggleControl *disableOpenpilotLong; + std::set gmKeys = { + "ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG" + }; + + std::set hyundaiKeys = { + "NewLongAPI" + }; + + std::set imprezaKeys = { + "CrosstrekTorque" + }; + + std::set longitudinalKeys = { + "ExperimentalGMTune", "LongPitch", "NewLongAPI", "NewLongAPIGM", + "NewToyotaTune", "SNGHack", "VoltSNG" + }; + + std::set sngKeys = { + "SNGHack" + }; + + std::set subaruKeys = { + "CrosstrekTorque" + }; + + std::set toyotaKeys = { + "ClusterOffset", "NewToyotaTune", "SNGHack", "ToyotaDoors" + }; + + std::set voltKeys = { + "VoltSNG" + }; + + std::map toggles; + QString carMake; QString carModel; @@ -35,15 +60,9 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { QMap carModels; - std::set gmKeys = {"ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG"}; - std::set hyundaiKeys = {"NewLongAPI"}; - std::set subaruKeys = {"CrosstrekTorque"}; - std::set toyotaKeys = {"ClusterOffset", "NewToyotaTune", "SNGHack", "ToyotaDoors"}; - - std::map toggles; - Params params; + bool disableOpenpilotLongitudinal; bool hasExperimentalOpenpilotLongitudinal; bool hasOpenpilotLongitudinal; bool hasSNG; @@ -51,4 +70,9 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { bool isImpreza; bool isVolt; bool started; + + void setModels(); + void updateCarToggles(); + void updateState(const UIState &s); + void updateToggles(); }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc index 00e04d95d98318..bfa9b392b1aa35 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc @@ -2,20 +2,20 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> visualToggles { - {"CustomUI", tr("Custom Onroad UI"), tr("Customize the Onroad UI."), "../assets/offroad/icon_road.png"}, - {"Compass", tr("Compass"), tr("Add a compass to the onroad UI."), ""}, - {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjust the width of the driving path display based on openpilot's current engagement state.\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%\n"), ""}, - {"PedalsOnUI", tr("Gas/Brake Pedal Indicators"), tr("Display the gas and brake pedals on the onroad UI that change opacity in accordance to how much gas/brake pressure is applied."), ""}, - {"CustomPaths", tr("Paths"), tr("Show your projected acceleration on the driving path, detected adjacent lanes, or when a vehicle is detected in your blindspot."), ""}, - {"RoadNameUI", tr("Road Name"), tr("Display the current road's name at the bottom of the screen using data from 'OpenStreetMap'."), ""}, - {"RotatingWheel", tr("Rotating Steering Wheel"), tr("Rotate the steering wheel in the onroad UI alongside your physical steering wheel."), ""}, - - {"QOLVisuals", tr("Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, - {"BigMap", tr("Big Map"), tr("Increase the size of the map in the onroad UI."), ""}, - {"MapStyle", tr("Map Style"), tr("Select a map style to use with navigation."), ""}, - {"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("Show the driver camera feed when in reverse."), ""}, - {"StandbyMode", tr("Standby Mode"), tr("Turn the screen off after your screen times out when onroad, but wake it back up when engagement state changes or important alerts are triggered."), ""}, - {"StoppedTimer", tr("Stopped Timer"), tr("Display a timer in the onroad UI that indicates how long you've been stopped for."), ""}, + {"CustomUI", tr("Onroad UI Widgets"), tr("Custom FrogPilot widgets used in the onroad user interface."), "../assets/offroad/icon_road.png"}, + {"Compass", tr("Compass"), tr("A compass in the onroad UI to show the current driving direction."), ""}, + {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjust the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""}, + {"PedalsOnUI", tr("Gas/Brake Pedal Indicators"), tr("Pedal indicators in the onroad UI that change opacity based on the pressure applied."), ""}, + {"CustomPaths", tr("Paths"), tr("Projected acceleration path, detected lanes, and vehicles in the blind spot."), ""}, + {"RoadNameUI", tr("Road Name"), tr("The current road name is displayed at the bottom of the screen using data from 'OpenStreetMap'."), ""}, + {"RotatingWheel", tr("Rotating Steering Wheel"), tr("The steering wheel in the onroad UI rotates along with your steering wheel movements."), ""}, + + {"QOLVisuals", tr("Quality of Life Improvements"), tr("Miscellaneous visual focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, + {"BigMap", tr("Larger Map Display"), tr("A larger size of the map in the onroad UI for easier navigation readings."), ""}, + {"MapStyle", tr("Map Style"), tr("Custom map styles for the map used during navigation."), ""}, + {"StandbyMode", tr("Screen Standby Mode"), tr("The screen is turned off after it times out when driving, but it automatically wakes up if engagement state changes or important alerts occur."), ""}, + {"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("The driver camera feed is displayed when the vehicle is in reverse."), ""}, + {"StoppedTimer", tr("Stopped Timer"), tr("A timer on the onroad UI to indicate how long the vehicle has been stopped."), ""} }; for (const auto &[param, title, desc, icon] : visualToggles) { @@ -24,13 +24,18 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : if (param == "CustomUI") { FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(customOnroadUIKeys); + customPathsBtn->setVisibleButton(0, hasBSM); + + std::set modifiedCustomOnroadUIKeys = customOnroadUIKeys; + + showToggles(modifiedCustomOnroadUIKeys); }); visualToggle = customUIToggle; } else if (param == "CustomPaths") { std::vector pathToggles{"AccelerationPath", "AdjacentPath", "BlindSpotPath"}; std::vector pathToggleNames{tr("Acceleration"), tr("Adjacent"), tr("Blind Spot")}; - visualToggle = new FrogPilotButtonToggleControl(param, title, desc, pathToggles, pathToggleNames); + customPathsBtn = new FrogPilotButtonToggleControl(param, title, desc, pathToggles, pathToggleNames); + visualToggle = customPathsBtn; } else if (param == "PedalsOnUI") { std::vector pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"}; std::vector pedalsToggleNames{tr("Dynamic"), tr("Static")}; @@ -106,6 +111,11 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : } QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideToggles); + QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotVisualsPanel::updateCarToggles); +} + +void FrogPilotVisualsPanel::updateCarToggles() { + hasBSM = parent->hasBSM; hideToggles(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h index 5b237082b1da1f..055c61dd337157 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h @@ -16,13 +16,25 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget { private: FrogPilotSettingsWindow *parent; - void hideToggles(); - void showToggles(const std::set &keys); + FrogPilotButtonToggleControl *customPathsBtn; - std::set customOnroadUIKeys = {"Compass", "CustomPaths", "DynamicPathWidth", "PedalsOnUI", "RoadNameUI", "RotatingWheel"}; - std::set qolKeys = {"BigMap", "DriverCamera", "MapStyle", "StandbyMode", "StoppedTimer"}; + std::set customOnroadUIKeys = { + "Compass", "CustomPaths", "DynamicPathWidth", + "PedalsOnUI", "RoadNameUI", "RotatingWheel" + }; + + std::set qolKeys = { + "BigMap", "DriverCamera", "MapStyle", + "StandbyMode", "StoppedTimer" + }; std::map toggles; Params params; + + bool hasBSM; + + void hideToggles(); + void showToggles(const std::set &keys); + void updateCarToggles(); }; diff --git a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h index 51ff52a42fdaac..d456abbea55699 100644 --- a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h +++ b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h @@ -137,6 +137,12 @@ class FrogPilotButtonsControl : public AbstractControl { } } + void setVisibleButton(int id, bool visible) { + if (QAbstractButton *button = buttonGroup->button(id)) { + button->setVisible(visible); + } + } + void setText(int id, const QString &text) { if (QAbstractButton *button = buttonGroup->button(id)) { button->setText(text); @@ -203,6 +209,12 @@ class FrogPilotButtonToggleControl : public ParamControl { } } + void setVisibleButton(int id, bool visible) { + if (QAbstractButton *button = buttonGroup->button(id)) { + button->setVisible(visible); + } + } + signals: void buttonClicked(int id); diff --git a/selfdrive/frogpilot/ui/qt/widgets/model_reviewer.cc b/selfdrive/frogpilot/ui/qt/widgets/model_reviewer.cc index ee8f13e7f097af..cd7ba342f44e9c 100644 --- a/selfdrive/frogpilot/ui/qt/widgets/model_reviewer.cc +++ b/selfdrive/frogpilot/ui/qt/widgets/model_reviewer.cc @@ -62,7 +62,7 @@ void ModelReview::setupRatingLayout() { for (int i = 0; i < emojis.size(); ++i) { QPushButton *ratingButton = createButton(emojis[i], "rating_button", scores[i], 150, 150); - connect(ratingButton, &QPushButton::clicked, this, &ModelReview::onRatingButtonClicked); + QObject::connect(ratingButton, &QPushButton::clicked, this, &ModelReview::onRatingButtonClicked); ratingButtons.append(ratingButton); ratingButtonsLayout->addWidget(ratingButton); } @@ -70,7 +70,7 @@ void ModelReview::setupRatingLayout() { ratingLayout->addLayout(ratingButtonsLayout); blacklistButton = createButton("Blacklist this model", "blacklist_button", 0, 600, 100); - connect(blacklistButton, &QPushButton::clicked, this, &ModelReview::onBlacklistButtonClicked); + QObject::connect(blacklistButton, &QPushButton::clicked, this, &ModelReview::onBlacklistButtonClicked); ratingLayout->addWidget(blacklistButton, 0, Qt::AlignCenter); QWidget *ratingWidget = new QWidget(this); diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 36b98f05cd18b8..539c6bb7e48979 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -50,7 +50,7 @@ 'secret-good-openpilot_metadata.pkl' if SECRET_GOOD_OPENPILOT else 'gas-brake_metadata.pkl' if GAS_BRAKE else 'poseless_metadata.pkl' if DISABLE_POSE else - 'supercombo_metadata.pkl' + 'classic_metadata.pkl' ) METADATA_PATH = Path(__file__).parent / f'models/{metadata_file}' diff --git a/selfdrive/modeld/models/classic_metadata.pkl b/selfdrive/modeld/models/classic_metadata.pkl new file mode 100644 index 00000000000000..6a8b5729b42cce Binary files /dev/null and b/selfdrive/modeld/models/classic_metadata.pkl differ diff --git a/selfdrive/modeld/models/commonmodel.cc b/selfdrive/modeld/models/commonmodel.cc index e99adefd6034d1..7a46144483093d 100644 --- a/selfdrive/modeld/models/commonmodel.cc +++ b/selfdrive/modeld/models/commonmodel.cc @@ -7,8 +7,8 @@ #include "common/clutil.h" ModelFrame::ModelFrame(cl_device_id device_id, cl_context context) { - frame = std::make_unique(MODEL_FRAME_SIZE); - input_frames = std::make_unique(buf_size); + frame = std::make_unique(MODEL_FRAME_SIZE); + input_frames = std::make_unique(buf_size); q = CL_CHECK_ERR(clCreateCommandQueue(context, device_id, 0, &err)); y_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, MODEL_WIDTH * MODEL_HEIGHT, NULL, &err)); @@ -40,12 +40,12 @@ uint8_t* ModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, i } } -uint8_t* ModelFrame::prepareSecret(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3 &projection, cl_mem *output) { +float* ModelFrame::prepareSecret(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3 &projection, cl_mem *output) { transform_queue(&this->transform, q, yuv_cl, frame_width, frame_height, frame_stride, frame_uv_offset, y_cl, u_cl, v_cl, MODEL_WIDTH, MODEL_HEIGHT, projection); loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, net_input_cl); - CL_CHECK(clEnqueueReadBuffer(q, net_input_cl, CL_TRUE, 0, MODEL_FRAME_SIZE * sizeof(uint8_t), &frame[0], 0, nullptr, nullptr)); + CL_CHECK(clEnqueueReadBuffer(q, net_input_cl, CL_TRUE, 0, MODEL_FRAME_SIZE * sizeof(float), &frame[0], 0, nullptr, nullptr)); clFinish(q); return &frame[0]; } @@ -58,4 +58,4 @@ ModelFrame::~ModelFrame() { CL_CHECK(clReleaseMemObject(u_cl)); CL_CHECK(clReleaseMemObject(y_cl)); CL_CHECK(clReleaseCommandQueue(q)); -} +} \ No newline at end of file diff --git a/selfdrive/modeld/models/commonmodel.h b/selfdrive/modeld/models/commonmodel.h index e2dfe78b1a0087..8aeef4101fddce 100644 --- a/selfdrive/modeld/models/commonmodel.h +++ b/selfdrive/modeld/models/commonmodel.h @@ -20,8 +20,8 @@ class ModelFrame { public: ModelFrame(cl_device_id device_id, cl_context context); ~ModelFrame(); - uint8_t* prepare(cl_mem yuv_cl, int width, int height, int frame_stride, int frame_uv_offset, const mat3& transform, cl_mem *output); - uint8_t* prepareSecret(cl_mem yuv_cl, int width, int height, int frame_stride, int frame_uv_offset, const mat3& transform, cl_mem *output); + float* prepare(cl_mem yuv_cl, int width, int height, int frame_stride, int frame_uv_offset, const mat3& transform, cl_mem *output); + float* prepareSecret(cl_mem yuv_cl, int width, int height, int frame_stride, int frame_uv_offset, const mat3& transform, cl_mem *output); const int MODEL_WIDTH = 512; const int MODEL_HEIGHT = 256; @@ -33,6 +33,6 @@ class ModelFrame { LoadYUVState loadyuv; cl_command_queue q; cl_mem y_cl, u_cl, v_cl, net_input_cl; - std::unique_ptr frame; - std::unique_ptr input_frames; + std::unique_ptr frame; + std::unique_ptr input_frames; }; diff --git a/selfdrive/modeld/models/commonmodel.pxd b/selfdrive/modeld/models/commonmodel.pxd index 68d77aa48ba907..9d3460f7429030 100644 --- a/selfdrive/modeld/models/commonmodel.pxd +++ b/selfdrive/modeld/models/commonmodel.pxd @@ -16,5 +16,5 @@ cdef extern from "selfdrive/modeld/models/commonmodel.h": int buf_size int MODEL_FRAME_SIZE ModelFrame(cl_device_id, cl_context) - unsigned char * prepare(cl_mem, int, int, int, int, mat3, cl_mem*) - unsigned char * prepareSecret(cl_mem, int, int, int, int, mat3, cl_mem*) + float * prepare(cl_mem, int, int, int, int, mat3, cl_mem*) + float * prepareSecret(cl_mem, int, int, int, int, mat3, cl_mem*) diff --git a/selfdrive/modeld/models/commonmodel_pyx.pyx b/selfdrive/modeld/models/commonmodel_pyx.pyx index 7210bac3d536bd..71876b682dd3f2 100644 --- a/selfdrive/modeld/models/commonmodel_pyx.pyx +++ b/selfdrive/modeld/models/commonmodel_pyx.pyx @@ -42,16 +42,16 @@ cdef class ModelFrame: data = self.frame.prepare(buf.buf.buf_cl, buf.width, buf.height, buf.stride, buf.uv_offset, cprojection, output.mem) if not data: return None - return np.asarray( data) + return np.asarray( data) def prepareSecret(self, VisionBuf buf, float[:] projection, CLMem output): cdef mat3 cprojection memcpy(cprojection.v, &projection[0], 9*sizeof(float)) - cdef unsigned char * data + cdef float * data if output is None: data = self.frame.prepareSecret(buf.buf.buf_cl, buf.width, buf.height, buf.stride, buf.uv_offset, cprojection, NULL) else: data = self.frame.prepareSecret(buf.buf.buf_cl, buf.width, buf.height, buf.stride, buf.uv_offset, cprojection, output.mem) if not data: return None - return np.asarray( data) + return np.asarray( data) diff --git a/selfdrive/test/longitudinal_maneuvers/maneuver.py b/selfdrive/test/longitudinal_maneuvers/maneuver.py index 6c8495cc3b5567..301f99dd568afe 100644 --- a/selfdrive/test/longitudinal_maneuvers/maneuver.py +++ b/selfdrive/test/longitudinal_maneuvers/maneuver.py @@ -12,11 +12,14 @@ def __init__(self, title, duration, **kwargs): self.breakpoints = kwargs.get("breakpoints", [0.0, duration]) self.speed_lead_values = kwargs.get("speed_lead_values", [0.0 for i in range(len(self.breakpoints))]) self.prob_lead_values = kwargs.get("prob_lead_values", [1.0 for i in range(len(self.breakpoints))]) + self.prob_throttle_values = kwargs.get("prob_throttle_values", [1.0 for i in range(len(self.breakpoints))]) self.cruise_values = kwargs.get("cruise_values", [50.0 for i in range(len(self.breakpoints))]) + self.pitch_values = kwargs.get("pitch_values", [0.0 for i in range(len(self.breakpoints))]) self.only_lead2 = kwargs.get("only_lead2", False) self.only_radar = kwargs.get("only_radar", False) self.ensure_start = kwargs.get("ensure_start", False) + self.ensure_slowdown = kwargs.get("ensure_slowdown", False) self.enabled = kwargs.get("enabled", True) self.e2e = kwargs.get("e2e", False) self.personality = kwargs.get("personality", 0) @@ -42,9 +45,11 @@ def evaluate(self): logs = [] while plant.current_time < self.duration: speed_lead = np.interp(plant.current_time, self.breakpoints, self.speed_lead_values) - prob = np.interp(plant.current_time, self.breakpoints, self.prob_lead_values) + prob_lead = np.interp(plant.current_time, self.breakpoints, self.prob_lead_values) cruise = np.interp(plant.current_time, self.breakpoints, self.cruise_values) - log = plant.step(speed_lead, prob, cruise) + pitch = np.interp(plant.current_time, self.breakpoints, self.pitch_values) + prob_throttle = np.interp(plant.current_time, self.breakpoints, self.prob_throttle_values) + log = plant.step(speed_lead, prob_lead, cruise, pitch, prob_throttle) d_rel = log['distance_lead'] - log['distance'] if self.lead_relevancy else 200. v_rel = speed_lead - log['speed'] if self.lead_relevancy else 0. @@ -57,13 +62,18 @@ def evaluate(self): speed_lead, log['acceleration']])) - if d_rel < .4 and (self.only_radar or prob > 0.5): + if d_rel < .4 and (self.only_radar or prob_lead > 0.5): print("Crashed!!!!") valid = False if self.ensure_start and log['v_rel'] > 0 and log['speeds'][-1] <= 0.1: print('LongitudinalPlanner not starting!') valid = False + + if self.ensure_slowdown and log['speed'] > 5.5: + print('LongitudinalPlanner not slowing down!') + valid = False + if self.force_decel and log['speed'] > 1e-1 and log['acceleration'] > -0.04: print('Not stopping with force decel') valid = False diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index daf7cec32b8e75..7596e194794592 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -56,12 +56,13 @@ def __init__(self, lead_relevancy=False, speed=0.0, distance_lead=2.0, def current_time(self): return float(self.rk.frame) / self.rate - def step(self, v_lead=0.0, prob=1.0, v_cruise=50.): + def step(self, v_lead=0.0, prob_lead=1.0, v_cruise=50., pitch=0.0, prob_throttle=1.0): # ******** publish a fake model going straight and fake calibration ******** # note that this is worst case for MPC, since model will delay long mpc by one time step radar = messaging.new_message('radarState') control = messaging.new_message('controlsState') car_state = messaging.new_message('carState') + car_control = messaging.new_message('carControl') model = messaging.new_message('modelV2') a_lead = (v_lead - self.v_lead_prev)/self.ts self.v_lead_prev = v_lead @@ -71,14 +72,14 @@ def step(self, v_lead=0.0, prob=1.0, v_cruise=50.): v_rel = v_lead - self.speed if self.only_radar: status = True - elif prob > .5: + elif prob_lead > .5: status = True else: status = False else: d_rel = 200. v_rel = 0. - prob = 0.0 + prob_lead = 0.0 status = False lead = log.RadarState.LeadData.new_message() @@ -92,7 +93,7 @@ def step(self, v_lead=0.0, prob=1.0, v_cruise=50.): # TODO use real radard logic for this lead.aLeadTau = float(_LEAD_ACCEL_TAU) lead.status = status - lead.modelProb = float(prob) + lead.modelProb = float(prob_lead) if not self.only_lead2: radar.radarState.leadOne = lead radar.radarState.leadTwo = lead @@ -109,6 +110,7 @@ def step(self, v_lead=0.0, prob=1.0, v_cruise=50.): acceleration = log.XYZTData.new_message() acceleration.x = [float(x) for x in np.zeros_like(ModelConstants.T_IDXS)] model.modelV2.acceleration = acceleration + model.modelV2.meta.disengagePredictions.gasPressProbs = [float(prob_throttle) for _ in range(6)] control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off control.controlsState.vCruise = float(v_cruise * 3.6) @@ -117,10 +119,13 @@ def step(self, v_lead=0.0, prob=1.0, v_cruise=50.): control.controlsState.forceDecel = self.force_decel car_state.carState.vEgo = float(self.speed) car_state.carState.standstill = self.speed < 0.01 + car_state.carState.vCruise = float(v_cruise * 3.6) + car_control.carControl.orientationNED = [0., float(pitch), 0.] # ******** get controlsState messages for plotting *** sm = {'radarState': radar.radarState, 'carState': car_state.carState, + 'carControl': car_control.carControl, 'controlsState': control.controlsState, 'modelV2': model.modelV2} self.planner.update(sm) diff --git a/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py b/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py index 62a95babeb3c05..7eea44aae6279e 100644 --- a/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py +++ b/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py @@ -82,6 +82,44 @@ def create_maneuvers(kwargs): breakpoints=[0.0, 2., 2.01], **kwargs, ), + Maneuver( + "approach stopped car at 20m/s, with prob_throttle_values and pitch = -0.1", + duration=30., + initial_speed=20., + lead_relevancy=True, + initial_distance_lead=120., + speed_lead_values=[0.0, 0., 0.], + prob_throttle_values=[1., 0., 0.], + cruise_values=[20., 20., 20.], + pitch_values=[0., -0.1, -0.1], + breakpoints=[0.0, 2., 2.01], + **kwargs, + ), + Maneuver( + "approach stopped car at 20m/s, with prob_throttle_values and pitch = +0.1", + duration=30., + initial_speed=20., + lead_relevancy=True, + initial_distance_lead=120., + speed_lead_values=[0.0, 0., 0.], + prob_throttle_values=[1., 0., 0.], + cruise_values=[20., 20., 20.], + pitch_values=[0., 0.1, 0.1], + breakpoints=[0.0, 2., 2.01], + **kwargs, + ), + Maneuver( + "slow to 5m/s with allow_throttle = False and pitch = +0.1", + duration=25., + initial_speed=20., + lead_relevancy=False, + prob_throttle_values=[1., 0., 0.], + cruise_values=[20., 20., 20.], + pitch_values=[0., 0.1, 0.1], + breakpoints=[0.0, 2., 2.01], + ensure_slowdown=True, + **kwargs, + ), Maneuver( "approach slower cut-in car at 20m/s", duration=20., diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e12a70cd1e3947..d48aa616e13887 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -39,15 +39,6 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] -widgets_src += ["../frogpilot/ui/qt/widgets/drive_stats.cc", "../frogpilot/ui/qt/widgets/frogpilot_controls.cc", - "../frogpilot/ui/qt/widgets/model_reviewer.cc", "../frogpilot/navigation/ui/navigation_settings.cc", - "../frogpilot/ui/qt/offroad/advanced_driving_settings.cc", "../frogpilot/ui/qt/offroad/advanced_visual_settings.cc", - "../frogpilot/ui/qt/offroad/data_settings.cc", "../frogpilot/ui/qt/offroad/device_settings.cc", - "../frogpilot/ui/qt/offroad/frogpilot_settings.cc", "../frogpilot/ui/qt/offroad/longitudinal_settings.cc", - "../frogpilot/ui/qt/offroad/lateral_settings.cc", "../frogpilot/ui/qt/offroad/sounds_settings.cc", - "../frogpilot/ui/qt/offroad/utilities.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc", - "../frogpilot/ui/qt/offroad/visual_settings.cc"] - qt_env['CPPDEFINES'] = [] if maps: base_libs += ['QMapLibre'] @@ -64,12 +55,7 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc", "qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", - "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", - "../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"] - -qt_src += ["../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"] - -qt_src += ["../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"] + "qt/onroad/buttons.cc", "qt/onroad/alerts.cc"] qt_src += ["../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"] diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index dd607defc8b6eb..b8ef352353632e 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -5,7 +5,6 @@ #include #include -#include #include "common/watchdog.h" #include "common/util.h" @@ -363,6 +362,7 @@ void DevicePanel::showEvent(QShowEvent *event) { pair_device->setVisible(uiState()->primeType() == PrimeType::UNPAIRED); ListWidget::showEvent(event); + // Frogpilot variables resetCalibBtn->setVisible(!params.getBool("ModelManagement")); } @@ -374,8 +374,6 @@ void SettingsWindow::hideEvent(QHideEvent *event) { parentToggleOpen = false; subParentToggleOpen = false; subSubParentToggleOpen = false; - - previousScrollPosition = 0; } void SettingsWindow::showEvent(QShowEvent *event) { @@ -438,9 +436,10 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { TogglesPanel *toggles = new TogglesPanel(this); QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); // FrogPilot panels + QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); + FrogPilotSettingsWindow *frogpilotSettingsWindow = new FrogPilotSettingsWindow(this); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openPanel, [this]() {panelOpen=true;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openParentToggle, [this]() {parentToggleOpen=true;}); @@ -494,7 +493,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { closeParentToggle(); parentToggleOpen = false; } - previousScrollPosition = 0; btn->setChecked(true); panel_widget->setCurrentWidget(w); }); diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 3b896a7f07e02e..2e7cb22bcad471 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -52,8 +52,6 @@ class SettingsWindow : public QFrame { bool parentToggleOpen; bool subParentToggleOpen; bool subSubParentToggleOpen; - - int previousScrollPosition; }; class DevicePanel : public ListWidget { @@ -74,6 +72,8 @@ private slots: private: Params params; ButtonControl *pair_device; + + // FrogPilot variables ButtonControl *resetCalibBtn; }; diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index 2735a66ea1a097..c9220c6dbf2978 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -352,7 +352,7 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { // Copy of the acceleration vector std::vector acceleration; acceleration.reserve(acceleration_const.size()); - for (size_t i = 0; i < acceleration_const.size(); i++) { + for (size_t i = 0; i < acceleration_const.size(); ++i) { acceleration.push_back(acceleration_const[i]); } diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc index c45feed5b63fa2..2621612f67f432 100644 --- a/selfdrive/ui/qt/widgets/prime.cc +++ b/selfdrive/ui/qt/widgets/prime.cc @@ -27,9 +27,6 @@ void PairingQRWidget::showEvent(QShowEvent *event) { refresh(); timer->start(5 * 60 * 1000); device()->setOffroadBrightness(100); - - // FrogPilot variables - useFrogPilotServer = Params().getBool("UseFrogServer"); } void PairingQRWidget::hideEvent(QHideEvent *event) { @@ -39,13 +36,8 @@ void PairingQRWidget::hideEvent(QHideEvent *event) { void PairingQRWidget::refresh() { QString pairToken = CommaApi::create_jwt({{"pair", true}}); - if (useFrogPilotServer) { - QString qrString = "https://portal.springerelectronics.com/?pair=" + pairToken; - this->updateQrCode(qrString); - } else { - QString qrString = "https://connect.comma.ai/?pair=" + pairToken; - this->updateQrCode(qrString); - } + QString qrString = "https://connect.comma.ai/?pair=" + pairToken; + this->updateQrCode(qrString); update(); } @@ -244,7 +236,7 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) { mainLayout->addWidget(content); primeUser->setVisible(uiState()->hasPrime()); - mainLayout->setCurrentIndex(0); + mainLayout->setCurrentIndex(1); setStyleSheet(R"( #primeWidget { @@ -281,7 +273,7 @@ void SetupWidget::replyFinished(const QString &response, bool success) { PrimeType prime_type = static_cast(json["prime_type"].toInt()); uiState()->setPrimeType(is_paired ? prime_type : PrimeType::UNPAIRED); - if (true) { + if (!is_paired) { mainLayout->setCurrentIndex(0); } else { popup->reject(); diff --git a/selfdrive/ui/qt/widgets/prime.h b/selfdrive/ui/qt/widgets/prime.h index f302788701419b..63341c4ceae8d9 100644 --- a/selfdrive/ui/qt/widgets/prime.h +++ b/selfdrive/ui/qt/widgets/prime.h @@ -22,9 +22,6 @@ class PairingQRWidget : public QWidget { void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; - // FrogPilot variables - bool useFrogPilotServer; - private slots: void refresh(); }; diff --git a/system/athena/registration.py b/system/athena/registration.py index 7fa10540da7017..5710c3c07823fe 100644 --- a/system/athena/registration.py +++ b/system/athena/registration.py @@ -96,12 +96,8 @@ def register(show_spinner=False) -> str | None: if dongle_id: params.put("DongleId", dongle_id) set_offroad_alert("Offroad_UnofficialHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC) - - if params.get_bool("UseFrogServer"): - os.environ['API_HOST'] = 'https://api.springerelectronics.com' - os.environ['ATHENA_HOST'] = 'wss://athena.springerelectronics.com' - return dongle_id + if __name__ == "__main__": print(register()) diff --git a/system/manager/manager.py b/system/manager/manager.py index 1dcdf50e4e8845..67278e6ede1e05 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -114,7 +114,8 @@ def manager_init() -> None: ("CertifiedHerbalistLiveTorqueParameters", ""), ("CertifiedHerbalistScore", "0"), ("CEModelStopTime", "8"), - ("CESignal", "1"), + ("CESignalSpeed", "55"), + ("CESignalLaneDetection", "1"), ("CESlowerLead", "1"), ("CESpeed", "0"), ("CESpeedLead", "0"), @@ -191,6 +192,7 @@ def manager_init() -> None: ("HolidayThemes", "1"), ("HumanAcceleration", "1"), ("HumanFollowing", "1"), + ("IncreasedStoppedDistance", "3"), ("IncreaseThermalLimits", "0"), ("JerkInfo", "1"), ("LaneChangeCustomizations", "1"), @@ -232,7 +234,7 @@ def manager_init() -> None: ("NavigationModels", ""), ("NewLongAPI", "0"), ("NewLongAPIGM", "1"), - ("NewToyotaTune", "1"), + ("NewToyotaTune", "0"), ("NNFF", "1"), ("NNFFLite", "1"), ("NoLogging", "0"), @@ -345,16 +347,15 @@ def manager_init() -> None: ("StandardPersonalityProfile", "1"), ("StandbyMode", "0"), ("StaticPedalsOnUI", "0"), - ("SteerFriction", "0"), - ("SteerFrictionStock", "0"), - ("SteerLatAccel", "0"), - ("SteerLatAccelStock", "0"), - ("SteerKP", "0"), - ("SteerKPStock", "0"), - ("SteerRatio", "0"), - ("SteerRatioStock", "0"), + ("SteerFriction", "0.1"), + ("SteerFrictionStock", "0.1"), + ("SteerLatAccel", "2.5"), + ("SteerLatAccelStock", "2.5"), + ("SteerKP", "1"), + ("SteerKPStock", "1"), + ("SteerRatio", "15"), + ("SteerRatioStock", "15"), ("StoppedTimer", "0"), - ("StoppingDistance", "15"), ("TacoTune", "0"), ("TombRaiderCalibrationParams", ""), ("TombRaiderDrives", "0"),