November 2024 Update

This commit is contained in:
FrogAi 2024-10-21 10:27:33 -07:00
parent d9aaed3c64
commit 5145bdbd8d
132 changed files with 4092 additions and 3425 deletions

View File

@ -21,7 +21,7 @@ FrogPilot is a fully open-sourced fork of openpilot, featuring clear and concise
------
FrogPilot was last updated on:
**October 21st, 2024**
**November 1st, 2024**
Features
------

View File

@ -137,14 +137,15 @@ struct CarEvent @0x9b1657f34caf3ad3 {
openpilotCrashedRandomEvent @137;
pedalInterceptorNoBrake @138;
speedLimitChanged @139;
torqueNNLoad @140;
trafficModeActive @141;
trafficModeInactive @142;
turningLeft @143;
turningRight @144;
vCruise69 @145;
yourFrogTriedToKillMe @146;
youveGotMail @147;
thisIsFineSteerSaturated @140;
torqueNNLoad @141;
trafficModeActive @142;
trafficModeInactive @143;
turningLeft @144;
turningRight @145;
vCruise69 @146;
yourFrogTriedToKillMe @147;
youveGotMail @148;
radarCanErrorDEPRECATED @15;
communityFeatureDisallowedDEPRECATED @62;
@ -453,7 +454,8 @@ struct CarControl {
mail @16;
nessie @17;
noice @18;
uwu @19;
thisIsFine @19;
uwu @20;
}
}

View File

@ -54,27 +54,29 @@ struct FrogPilotPlan @0x80ae746ee2596b11 {
desiredFollowDistance @4 :Float32;
experimentalMode @5 :Bool;
forcingStop @6 :Bool;
frogpilotEvents @7 :List(Car.CarEvent);
lateralCheck @8 :Bool;
laneWidthLeft @9 :Float32;
laneWidthRight @10 :Float32;
maxAcceleration @11 :Float32;
minAcceleration @12 :Float32;
redLight @13 :Bool;
safeObstacleDistance @14 :Int32;
safeObstacleDistanceStock @15 :Int32;
slcOverridden @16 :Bool;
slcOverriddenSpeed @17 :Float32;
slcSpeedLimit @18 :Float32;
slcSpeedLimitOffset @19 :Float32;
speedJerk @20 :Float32;
speedJerkStock @21 :Float32;
speedLimitChanged @22 :Bool;
stoppedEquivalenceFactor @23 :Int32;
tFollow @24 :Float32;
unconfirmedSlcSpeedLimit @25 :Float32;
vCruise @26 :Float32;
vtscControllingCurve @27 :Bool;
forcingStopLength @7 :Float32;
frogpilotEvents @8 :List(Car.CarEvent);
lateralCheck @9 :Bool;
laneWidthLeft @10 :Float32;
laneWidthRight @11 :Float32;
maxAcceleration @12 :Float32;
minAcceleration @13 :Float32;
redLight @14 :Bool;
safeObstacleDistance @15 :Int64;
safeObstacleDistanceStock @16 :Int64;
slcOverridden @17 :Bool;
slcOverriddenSpeed @18 :Float32;
slcSpeedLimit @19 :Float32;
slcSpeedLimitOffset @20 :Float32;
speedJerk @21 :Float32;
speedJerkStock @22 :Float32;
speedLimitChanged @23 :Bool;
stoppedEquivalenceFactor @24 :Int64;
tFollow @25 :Float32;
togglesUpdated @26 :Bool;
unconfirmedSlcSpeedLimit @27 :Float32;
vCruise @28 :Float32;
vtscControllingCurve @29 :Bool;
}
struct CustomReserved5 @0xa5cd762cd951a455 {

View File

@ -618,6 +618,10 @@ struct RadarState @0x9a185389d6fdd05f {
leadOne @3 :LeadData;
leadTwo @4 :LeadData;
leadLeft @13 :LeadData;
leadRight @14 :LeadData;
leadLeftFar @15 :LeadData;
leadRightFar @16 :LeadData;
cumLagMs @5 :Float32;
struct LeadData {

View File

@ -74,13 +74,6 @@ _services: dict[str, tuple] = {
"userFlag": (True, 0., 1),
"microphone": (True, 10., 10),
# FrogPilot
"frogpilotCarControl": (True, 100., 10),
"frogpilotCarState": (True, 100., 10),
"frogpilotDeviceState": (True, 2., 1),
"frogpilotNavigation": (True, 1., 10),
"frogpilotPlan": (True, 20., 5),
# debug
"uiDebug": (True, 0., 1),
"testJoystick": (True, 0.),
@ -97,6 +90,13 @@ _services: dict[str, tuple] = {
"customReservedRawData0": (True, 0.),
"customReservedRawData1": (True, 0.),
"customReservedRawData2": (True, 0.),
# FrogPilot
"frogpilotCarControl": (True, 100., 10),
"frogpilotCarState": (True, 100., 10),
"frogpilotDeviceState": (True, 2., 1),
"frogpilotNavigation": (True, 1., 10),
"frogpilotPlan": (True, 20., 5),
}
SERVICE_LIST = {name: Service(*vals) for
idx, (name, vals) in enumerate(_services.items())}

View File

@ -12,8 +12,6 @@ class Conversions:
KNOTS_TO_MS = 1. / MS_TO_KNOTS
# Distance
KM_TO_MILES = KPH_TO_MPH
MILES_TO_KM = MPH_TO_KPH
METER_TO_FOOT = 3.28084
FOOT_TO_METER = 1. / METER_TO_FOOT
CM_TO_INCH = 1. / 2.54

View File

@ -211,12 +211,11 @@ std::unordered_map<std::string, uint32_t> keys = {
// FrogPilot parameters
{"AccelerationPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"AccelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AdjacentLeadsUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AdjacentPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"AdjacentPathMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"AdvancedCustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"AdvancedLateralTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AdvancedLongitudinalTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AdvancedQOLDriving", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AggressiveFollow", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AggressiveJerkAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"AggressiveJerkDanger", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
@ -240,7 +239,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"BlindSpotMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"BlindSpotPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"BorderMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CameraFPS", PERSISTENT},
{"CameraView", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CancelModelDownload", PERSISTENT},
{"CancelThemeDownload", PERSISTENT},
@ -266,6 +264,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CESpeedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"CEStatus", CLEAR_ON_OFFROAD_TRANSITION},
{"CEStoppedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ClassicModels", PERSISTENT},
{"ClusterOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
{"ColorToDownload", PERSISTENT},
{"Compass", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
@ -281,18 +280,24 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CustomCruiseLong", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"CustomDistanceIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"CustomIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CustomPaths", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CustomizationLevel", PERSISTENT},
{"CustomizationLevelConfirmed", PERSISTENT},
{"CustomPersonalities", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"CustomSignals", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CustomSounds", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"CustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"DecelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DefaultModelName", CLEAR_ON_MANAGER_START},
{"DeveloperUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"DeviceManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DeviceShutdown", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DisableCurveSpeedSmoothing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DisableOnroadUploads", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DisableOpenpilotLongitudinal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
{"DissolvedOxygenCalibrationParams", PERSISTENT},
{"DissolvedOxygenDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DissolvedOxygenLiveTorqueParameters", PERSISTENT},
{"DissolvedOxygenScore", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DistanceIconToDownload", PERSISTENT},
{"DisengageVolume", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"DoToggleReset", PERSISTENT},
@ -303,10 +308,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DownloadableSounds", PERSISTENT},
{"DownloadableWheels", PERSISTENT},
{"DownloadAllModels", PERSISTENT},
{"DragonPilotTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
{"DriveRated", CLEAR_ON_ONROAD_TRANSITION},
{"DriverCamera", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"DrivingPersonalities", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DuckAmigoCalibrationParams", PERSISTENT},
{"DuckAmigoDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"DuckAmigoLiveTorqueParameters", PERSISTENT},
@ -334,6 +337,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"FrogPilotDrives", PERSISTENT | FROGPILOT_TRACKING},
{"FrogPilotKilometers", PERSISTENT | FROGPILOT_TRACKING},
{"FrogPilotMinutes", PERSISTENT | FROGPILOT_TRACKING},
{"FrogPilotToggles", PERSISTENT},
{"FrogPilotTogglesUpdated", PERSISTENT},
{"FrogsGoMoo", PERSISTENT},
{"FrogsGoMoosTweak", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
@ -353,8 +357,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"HideMapIcon", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HideMaxSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HideSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HideSpeedUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HideUIElements", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HolidayThemes", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"HumanAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"HumanFollowing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
@ -397,16 +399,15 @@ std::unordered_map<std::string, uint32_t> keys = {
{"MinimumLaneChangeSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"Model", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelDownloadProgress", PERSISTENT},
{"ModelManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelName", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelRandomizer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelsDownloaded", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelSelector", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ModelToDownload", PERSISTENT},
{"ModelUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"MTSCCurvatureCheck", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"MTSCEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"NavigationModels", PERSISTENT},
{"NavigationUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"NextMapSpeedLimit", PERSISTENT},
{"NewLongAPI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
{"NewLongAPIGM", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
@ -434,6 +435,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"OneLaneChange", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"OnroadDistanceButton", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"OnroadDistanceButtonPressed", PERSISTENT},
{"openpilotMinutes", PERSISTENT},
{"OSMDownloadBounds", PERSISTENT},
{"OSMDownloadLocations", PERSISTENT},
{"OSMDownloadProgress", CLEAR_ON_MANAGER_START},
@ -493,7 +495,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"ShowIP", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"ShowMemoryUsage", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"ShowSLCOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ShowSLCOffsetUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"ShowSteering", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"ShowStoppingPoint", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"ShowStoppingPointMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
@ -503,6 +504,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"SidebarMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"SignalMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"SignalToDownload", PERSISTENT},
{"SLCConfirmation", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"SLCConfirmationHigher", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"SLCConfirmationLower", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"SLCConfirmed", PERSISTENT},
@ -541,6 +543,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"TacoTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"TetheringEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER},
{"ThemeDownloadProgress", PERSISTENT},
{"ThemeUpdated", PERSISTENT},
{"TombRaiderCalibrationParams", PERSISTENT},
{"TombRaiderDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"TombRaiderLiveTorqueParameters", PERSISTENT},
@ -564,7 +567,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UseSI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},
{"UseStockColors", CLEAR_ON_MANAGER_START},
{"UseVienna", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"VelocityModels", PERSISTENT},
{"VisionTurnControl", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS},
{"VoltSNG", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES},
{"WarningImmediateVolume", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS},

View File

@ -16,7 +16,8 @@ from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.controls.lib.events import Events
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles
from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles
REPLAY = "REPLAY" in os.environ
@ -28,7 +29,7 @@ class Car:
def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents', 'frogpilotPlan'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'frogpilotCarState'])
self.can_rcv_cum_timeout_counter = 0
@ -74,10 +75,7 @@ class Car:
self.rk = Ratekeeper(100, print_delay_threshold=None)
# FrogPilot variables
self.frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
self.update_toggles = False
self.frogpilot_toggles = get_frogpilot_toggles(True)
# set alternative experiences from parameters
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
@ -97,6 +95,8 @@ class Car:
self.params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
update_frogpilot_toggles()
def state_update(self) -> car.CarState:
"""carState update loop, driven by can"""
@ -199,11 +199,8 @@ class Car:
self.rk.monitor_time()
# Update FrogPilot parameters
if FrogPilotVariables.toggles_updated:
self.update_toggles = True
elif self.update_toggles:
FrogPilotVariables.update_frogpilot_params()
self.update_toggles = False
if self.sm['frogpilotPlan'].togglesUpdated:
self.frogpilot_toggles = get_frogpilot_toggles()
def main():
config_realtime_process(4, Priority.CTRL_HIGH)

View File

@ -97,9 +97,9 @@ class CarController(CarControllerBase):
if self.CP.openpilotLongitudinalControl and (self.frame % CarControllerParams.ACC_CONTROL_STEP) == 0:
# Both gas and accel are in m/s^2, accel is used solely for braking
if frogpilot_toggles.sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo)))
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo)))
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, CarControllerParams.ACCEL_MAX))
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX))
gas = accel
if not CC.longActive or gas < CarControllerParams.MIN_GAS:
gas = CarControllerParams.INACTIVE_GAS

View File

@ -219,9 +219,9 @@ class CarController(CarControllerBase):
if self.CP.carFingerprint in HONDA_BOSCH:
if frogpilot_toggles.sport_plus:
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo)))
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo)))
else:
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.params.BOSCH_ACCEL_MAX))
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.params.BOSCH_ACCEL_MAX))
self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
stopping = actuators.longControlState == LongCtrlState.stopping

View File

@ -82,9 +82,9 @@ class CarController(CarControllerBase):
# accel + longitudinal
if frogpilot_toggles.sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo)))
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo)))
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, CarControllerParams.ACCEL_MAX))
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX))
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)

View File

@ -0,0 +1 @@
{"input_std":[[9.281861],[1.8477924],[0.7977224],[0.047467366],[1.7969038],[1.8168812],[1.8351218],[1.785757],[1.7335743],[1.6658221],[1.5893887],[0.047346078],[0.0473731],[0.047383286],[0.047291175],[0.047300573],[0.04712479],[0.046799928]],"model_test_loss":0.027355343103408813,"input_size":18,"current_date_and_time":"2023-08-05_05-11-44","input_mean":[[21.655252],[-0.07694559],[-0.006081294],[-0.007598456],[-0.07309746],[-0.075890236],[-0.07804942],[-0.076106496],[-0.07184982],[-0.06528668],[-0.060404416],[-0.0077262702],[-0.0077046235],[-0.0076850485],[-0.007687444],[-0.007708868],[-0.0077959923],[-0.008017422]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.35776415],[-0.126288],[0.007988413],[0.03850029],[-0.056796823],[0.0072075897],[0.17376427]],"dense_1_W":[[0.010538558,6.7293744,-0.007391298,-1.035045,-0.47446147,1.0940393,0.08225446,-0.5795131,0.573115,1.82923,-0.16999252,0.8818023,0.03560901,-0.9470653,-1.042151,1.1532398,1.9009485,-1.2265956],[0.009010456,-1.3618345,0.039026346,0.22943486,-0.6669799,-0.768271,-0.16828564,-0.13406865,0.19760236,-0.18953702,-0.019137766,0.1468623,0.26367718,0.4732375,0.73679143,0.62450147,0.30198866,-0.93340015],[-0.044318866,2.2138615,9.620889,-0.66480356,-0.06849887,-0.038568456,-0.30580127,-0.088089585,0.31187677,0.5968372,-1.1462088,0.13234706,0.29166338,-0.69978577,-0.01790655,-0.097928315,-0.56950736,0.7560642],[1.3758256,0.8700429,-0.24295154,0.20832618,-0.7735097,1.0379355,-0.60753095,-0.5457418,-1.0892137,-0.6708325,0.24982774,0.09251328,0.08596103,-0.58034134,-0.3046114,-0.19754426,0.006461508,0.5989185],[0.06314328,-2.492993,-0.28200585,0.49902886,0.8513198,-0.5704965,1.0960953,-0.08426956,-0.76074624,0.10669376,0.02775254,-0.42194095,-0.34657434,-0.10863868,0.17019278,0.0963825,-0.10169116,0.21243806],[0.007984091,-1.9276509,-0.0013926353,0.04057924,1.4438432,1.3440262,-2.506722,1.700801,0.72410774,0.13471895,-0.18753268,0.7303607,0.018133093,-0.71643597,0.07050904,-0.30161944,0.085108586,0.061202843],[0.9413167,-0.9954305,-0.19510143,-0.4711845,-0.12398665,-0.45175523,1.0616771,0.28550953,-0.55543137,-0.21576759,-0.2364377,-0.012782618,-0.050565187,0.257694,-0.20975965,0.00022543047,-0.08760746,0.4983852]],"activation":"σ"},{"dense_2_W":[[-0.71804935,0.017023304,-0.099854074,-0.3262651,-0.402829,-0.12073083,0.13537839],[-0.44002575,-0.1071093,-0.58886194,-0.17319413,-0.21134079,0.23135148,0.0006880979],[-0.55711204,-0.8693639,-0.6443761,-0.7382846,0.18980889,-0.6082511,-0.63103676],[0.11338112,-0.5327033,0.2856197,0.32239884,-0.72682667,0.5302305,-0.45094746],[-0.35197002,-0.14440043,-0.025249843,-0.48697743,0.3340018,-0.25992322,0.0050561456],[0.34757507,0.15670523,-0.14263277,0.50704384,-0.020171141,0.6408679,-0.3074123],[0.40258837,-0.59363264,0.35948923,-0.060853776,-0.0072541097,0.89743376,-0.49789146],[-0.63476,0.29580453,0.1689314,-0.5061521,0.24901053,-0.23218995,0.57968193],[-0.66173035,0.50861925,-0.5845035,-0.6602214,0.8341883,-0.31437424,0.8046359],[0.038493533,0.15464582,-0.04848341,-0.57820857,-0.25891733,-0.47527292,0.21441916],[-0.15464531,-0.07454202,-0.8215851,-0.12614948,-0.5924209,0.00017916794,0.24154592],[0.6091424,-0.112086505,0.1144111,-0.31035227,-0.9237534,0.041003596,-0.3542808],[0.2989792,-0.23780549,0.116059326,-0.6056522,0.5499526,-0.9001413,0.5200723]],"activation":"σ","dense_2_b":[[-0.2638964],[-0.24607328],[-0.15493082],[-0.0071187904],[-0.23515384],[-0.09098133],[-0.034066215],[0.03609036],[-0.03842933],[-0.042302527],[-0.2439947],[-0.16342077],[-0.01030317]]},{"dense_3_W":[[0.45771673,-0.2084603,0.3570187,0.35835707,0.13684571,-0.582958,0.29889587,0.43543494,0.11841301,-0.26185623,-0.4988437,0.5752996,-0.28057483],[-0.19594137,0.050280698,-0.29057446,-0.2829161,-0.16987291,-0.21278952,-0.59946907,0.21295123,0.7040468,0.53549695,-0.52553934,-0.19560973,0.6233473],[-0.19091003,0.08669418,-0.5792192,0.57799137,-0.36263424,0.6037143,0.27898273,-0.30951327,-0.3572644,0.46720102,-0.5403428,0.32415462,-0.60570025]],"activation":"identity","dense_3_b":[[-0.0047377176],[0.023170695],[-0.021546118]]},{"dense_4_W":[[-0.12453941,-1.006034,0.96776205]],"dense_4_b":[[-0.02351544]],"activation":"identity"}]}

View File

@ -54,7 +54,7 @@ class CarController(CarControllerBase):
self.distance_button = 0
self.pcm_accel_compensation = 0.0
self.permit_braking = 0.0
self.permit_braking = True
self.packer = CANPacker(dbc_name)
self.accel = 0
@ -141,10 +141,13 @@ class CarController(CarControllerBase):
interceptor_gas_cmd = 0.
# For cars where we allow a higher max acceleration of 2.0 m/s^2, compensate for PCM request overshoot and imprecise braking
# TODO: sometimes when switching from brake to gas quickly, CLUTCH->ACCEL_NET shows a slow unwind. make it go to 0 immediately
if (self.CP.flags & ToyotaFlags.RAISED_ACCEL_LIMIT or self.CP.flags & ToyotaFlags.NEW_TOYOTA_TUNE) and CC.longActive and not CS.out.cruiseState.standstill:
# calculate amount of acceleration PCM should apply to reach target, given pitch
accel_due_to_pitch = math.sin(CS.slope_angle) * ACCELERATION_DUE_TO_GRAVITY
if len(CC.orientationNED) == 3:
accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
else:
accel_due_to_pitch = 0.0
net_acceleration_request = actuators.accel + accel_due_to_pitch
# let PCM handle stopping for now
@ -171,9 +174,9 @@ class CarController(CarControllerBase):
self.permit_braking = True
if frogpilot_toggles.sport_plus:
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo)))
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo)))
else:
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.params.ACCEL_MAX))
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.params.ACCEL_MAX))
# on entering standstill, send standstill request
if CS.out.standstill and not self.last_standstill and (self.CP.carFingerprint not in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor):

View File

@ -64,7 +64,6 @@ class CarState(CarStateBase):
self.acc_type = 1
self.lkas_hud = {}
self.pcm_accel_net = 0.0
self.slope_angle = 0.0
# FrogPilot variables
self.latActive_previous = False
@ -273,7 +272,6 @@ class CarState(CarStateBase):
("BODY_CONTROL_STATE", 3),
("BODY_CONTROL_STATE_2", 2),
("ESP_CONTROL", 3),
("VSC1S07", 20),
("EPS_STATUS", 25),
("BRAKE_MODULE", 40),
("WHEEL_SPEEDS", 80),

View File

@ -127,6 +127,7 @@ FW_VERSIONS = {
b'\x018966333X0000\x00\x00\x00\x00',
b'\x018966333X4000\x00\x00\x00\x00',
b'\x01896633T16000\x00\x00\x00\x00',
b'\x01896633TA2000\x00\x00\x00\x00',
b'\x028966306B2100\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
b'\x028966306B2300\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
b'\x028966306B2500\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00',
@ -321,6 +322,7 @@ FW_VERSIONS = {
b'F1526F4073\x00\x00\x00\x00\x00\x00',
b'F1526F4121\x00\x00\x00\x00\x00\x00',
b'F1526F4122\x00\x00\x00\x00\x00\x00',
b'F1526F4190\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B10011\x00\x00\x00\x00\x00\x00',
@ -337,6 +339,7 @@ FW_VERSIONS = {
b'\x033F401200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00',
b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00',
b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00',
b'\x033F435000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'8821F0W01000 ',
@ -431,6 +434,7 @@ FW_VERSIONS = {
CAR.TOYOTA_COROLLA_TSS2: {
(Ecu.engine, 0x700, None): [
b'\x01896630A22000\x00\x00\x00\x00',
b'\x01896630A42000\x00\x00\x00\x00',
b'\x01896630ZG2000\x00\x00\x00\x00',
b'\x01896630ZG5000\x00\x00\x00\x00',
b'\x01896630ZG5100\x00\x00\x00\x00',
@ -525,6 +529,7 @@ FW_VERSIONS = {
b'8965B16011\x00\x00\x00\x00\x00\x00',
b'8965B16101\x00\x00\x00\x00\x00\x00',
b'8965B16170\x00\x00\x00\x00\x00\x00',
b'8965B16260\x00\x00\x00\x00\x00\x00',
b'8965B76012\x00\x00\x00\x00\x00\x00',
b'8965B76050\x00\x00\x00\x00\x00\x00',
b'8965B76091\x00\x00\x00\x00\x00\x00',
@ -539,6 +544,7 @@ FW_VERSIONS = {
b'\x01F15260A010\x00\x00\x00\x00\x00\x00',
b'\x01F15260A050\x00\x00\x00\x00\x00\x00',
b'\x01F15260A070\x00\x00\x00\x00\x00\x00',
b'\x01F15260A33000\x00\x00\x00\x00',
b'\x01F152612641\x00\x00\x00\x00\x00\x00',
b'\x01F152612651\x00\x00\x00\x00\x00\x00',
b'\x01F152612862\x00\x00\x00\x00\x00\x00',
@ -598,6 +604,7 @@ FW_VERSIONS = {
b'\x028646F1601100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F1601200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F1601300\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F1601500\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F4203400\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
b'\x028646F76020C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F7603100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
@ -1005,6 +1012,33 @@ FW_VERSIONS = {
b'8646F4204000\x00\x00\x00\x00',
],
},
CAR.TOYOTA_RAV4_PRIME: {
(Ecu.engine, 0x700, None): [
b'\x018966342S7000\x00\x00\x00\x00',
b'\x018966342Z1000\x00\x00\x00\x00',
b'\x018966342Z1100\x00\x00\x00\x00',
b'\x01896634AJ7000\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F15264228300\x00\x00\x00\x00',
b'\x01F15264228500\x00\x00\x00\x00',
b'\x01F15264284100\x00\x00\x00\x00',
b'\x01F152642F3000\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x018965B4209000\x00\x00\x00\x00',
b'\x018965B4233100\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00',
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F4205200\x00\x00\x00\x008646G4202000\x00\x00\x00\x00',
b'\x028646F4205300\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
b'\x028646F4210100\x00\x00\x00\x008646G3305000\x00\x00\x00\x00',
],
},
CAR.TOYOTA_RAV4_TSS2: {
(Ecu.engine, 0x700, None): [
b'\x01896630R58000\x00\x00\x00\x00',
@ -1226,6 +1260,28 @@ FW_VERSIONS = {
b'8646F0801100\x00\x00\x00\x00',
],
},
CAR.TOYOTA_SIENNA_4TH_GEN: {
(Ecu.engine, 0x700, None): [
b'\x01896630841000\x00\x00\x00\x00',
b'\x01896630857101\x00\x00\x00\x00',
b'\x01896630864000\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F15260815100\x00\x00\x00\x00',
b'\x01F15260815300\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x018965B4509100\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301500\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F0802200\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
b'\x028646F0802300\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
b'\x028646F0802400\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
],
},
CAR.LEXUS_CTH: {
(Ecu.dsu, 0x791, None): [
b'881517601100\x00\x00\x00\x00',
@ -1644,6 +1700,7 @@ FW_VERSIONS = {
b'8965B47070\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301300\x00\x00\x00\x00',
b'\x018821F3301400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [

View File

@ -156,9 +156,6 @@ class CarInterface(CarInterfaceBase):
tune.kiV = [3.6, 2.4, 1.5]
if params.get_bool("FrogsGoMoosTweak"):
if ret.flags & ToyotaFlags.NEW_TOYOTA_TUNE or ret.flags & ToyotaFlags.RAISED_ACCEL_LIMIT:
tune.kiV = [0.3]
ret.stoppingDecelRate = 0.1 # reach stopping target smoothly
ret.vEgoStopping = 0.15
ret.vEgoStarting = 0.15

View File

@ -47,9 +47,7 @@ class CarControllerParams:
class ToyotaFlags(IntFlag):
# Detected flags
HYBRID = 1
SMART_DSU = 2
DISABLE_RADAR = 4
RADAR_CAN_FILTER = 2048
# Static flags
TSS2 = 8
@ -59,14 +57,17 @@ class ToyotaFlags(IntFlag):
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL = 128
NO_STOP_TIMER = 256
# these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU
# these cars are speculated to allow stop and go when the DSU is unplugged
SNG_WITHOUT_DSU = 512
# these cars can utilize 2.0 m/s^2
RAISED_ACCEL_LIMIT = 1024
SECOC = 2048
# FrogPilot Toyota flags
NEW_TOYOTA_TUNE = 4096
ZSS = 8192
RADAR_CAN_FILTER = 8192
SMART_DSU = 16384
ZSS = 32768
class Footnote(Enum):
CAMRY = CarFootnote(
@ -254,6 +255,14 @@ class CAR(Platforms):
TOYOTA_RAV4_TSS2.specs,
flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL,
)
TOYOTA_RAV4_PRIME = PlatformConfig(
# TODO: Enable this docs entry when it can be suppressed from openpilot CARS.md
# [ToyotaCarDocs("Toyota RAV4 Prime 2021-23", min_enable_speed=MIN_ACC_SPEED)],
[],
CarSpecs(mass=4372. * CV.LB_TO_KG, wheelbase=2.68, steerRatio=16.88, tireStiffnessFactor=0.5533),
dbc_dict('toyota_rav4_prime_generated', 'toyota_tss2_adas'),
flags=ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC,
)
TOYOTA_MIRAI = ToyotaTSS2PlatformConfig( # TSS 2.5
[ToyotaCarDocs("Toyota Mirai 2021")],
CarSpecs(mass=4300. * CV.LB_TO_KG, wheelbase=2.91, steerRatio=14.8, tireStiffnessFactor=0.8),
@ -264,6 +273,14 @@ class CAR(Platforms):
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER,
)
TOYOTA_SIENNA_4TH_GEN = PlatformConfig(
# TODO: Enable this docs entry when it can be suppressed from openpilot CARS.md
# [ToyotaCarDocs("Toyota Sienna 2021-23", min_enable_speed=MIN_ACC_SPEED)],
[],
CarSpecs(mass=4625. * CV.LB_TO_KG, wheelbase=3.06, steerRatio=17.8, tireStiffnessFactor=0.444),
dbc_dict('toyota_rav4_prime_generated', 'toyota_tss2_adas'),
flags=ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC,
)
# Lexus
LEXUS_CTH = PlatformConfig(
@ -547,6 +564,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
# Hybrid control computer can be on 0x7e2 (KWP) or 0x7d2 (UDS) depending on platform
(Ecu.hybrid, 0x7e2, None), # Hybrid Control Assembly & Computer
(Ecu.hybrid, 0x7d2, None), # Hybrid Control Assembly & Computer
(Ecu.srs, 0x780, None), # SRS Airbag
# Transmission is combined with engine on some platforms, such as TSS-P RAV4
(Ecu.transmission, 0x701, None),
@ -577,6 +595,8 @@ RADAR_ACC_CAR = CAR.with_flags(ToyotaFlags.RADAR_ACC)
ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL)
SECOC_CAR = CAR.with_flags(ToyotaFlags.SECOC)
# no resume button press required
NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER)

View File

@ -83,9 +83,9 @@ class CarController(CarControllerBase):
if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl:
acc_control = self.CCS.acc_control_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive)
if frogpilot_toggles.sport_plus:
accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) if CC.longActive else 0
accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) if CC.longActive else 0
else:
accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.CCP.ACCEL_MAX)) if CC.longActive else 0
accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.CCP.ACCEL_MAX)) if CC.longActive else 0
stopping = actuators.longControlState == LongCtrlState.stopping
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping)
can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, CS.acc_type, CC.longActive, accel,

View File

@ -27,7 +27,7 @@ from openpilot.selfdrive.classic_modeld.models.commonmodel_pyx import ModelFrame
from openpilot.selfdrive.frogpilot.assets.model_manager import DEFAULT_MODEL
from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles
PROCESS_NAME = "selfdrive.classic_modeld.modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
@ -136,14 +136,11 @@ class ModelState:
def main(demo=False):
# FrogPilot variables
frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
frogpilot_toggles = get_frogpilot_toggles(True)
enable_navigation = not frogpilot_toggles.navigationless_model
radarless = frogpilot_toggles.radarless_model
update_toggles = False
cloudlog.warning("classic_modeld init")
sentry.set_tag("daemon", PROCESS_NAME)
@ -347,11 +344,8 @@ def main(demo=False):
last_vipc_frame_id = meta_main.frame_id
# Update FrogPilot parameters
if FrogPilotVariables.toggles_updated:
update_toggles = True
elif update_toggles:
FrogPilotVariables.update_frogpilot_params()
update_toggles = False
if sm['frogpilotPlan'].togglesUpdated:
frogpilot_toggles = get_frogpilot_toggles()
if __name__ == "__main__":
try:

View File

@ -32,7 +32,7 @@ from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.system.hardware import HARDWARE
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_acceleration import get_max_allowed_accel
from openpilot.selfdrive.frogpilot.frogpilot_variables import NON_DRIVING_GEARS, FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_variables import CRUISING_SPEED, NON_DRIVING_GEARS, get_frogpilot_toggles
SOFT_DISABLE_TIME = 3 # seconds
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
@ -99,7 +99,7 @@ class Controls:
if REPLAY:
# no vipc in replay will make them ignored anyways
ignore += ['roadCameraState', 'wideRoadCameraState']
if FrogPilotVariables.toggles.radarless_model:
if get_frogpilot_toggles(True).radarless_model:
ignore += ['radarState']
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman',
@ -164,7 +164,7 @@ class Controls:
self.can_log_mono_time = 0
self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0, self.block_user, FrogPilotVariables.toggles)
self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0, self.block_user, get_frogpilot_toggles(True))
if not sounds_available:
self.events.add(EventName.soundsUnavailable, static=True)
@ -181,8 +181,7 @@ class Controls:
self.rk = Ratekeeper(100, print_delay_threshold=None)
# FrogPilot variables
self.frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
self.frogpilot_toggles = get_frogpilot_toggles(True)
self.params_memory = Params("/dev/shm/params")
@ -191,11 +190,12 @@ class Controls:
self.fcw_event_triggered = False
self.no_entry_alert_triggered = False
self.onroad_distance_pressed = False
self.radarless_model = self.frogpilot_toggles.radarless_model
self.resume_pressed = False
self.resume_previously_pressed = False
self.steer_saturated_event_triggered = False
self.update_toggles = False
self.radarless_model = self.frogpilot_toggles.radarless_model
self.use_old_long = self.CP.carName == "hyundai" and not self.params.get_bool("NewLongAPI")
self.use_old_long |= self.CP.carName == "gm" and not self.params.get_bool("NewLongAPIGM")
@ -626,7 +626,7 @@ class Controls:
t_since_plan = (self.sm.frame - self.sm.recv_frame['longitudinalPlan']) * DT_CTRL
actuators.accel = self.LoC.update_old_long(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan)
else:
actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits)
actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop or self.sm['frogpilotPlan'].forcingStopLength <= 0, pid_accel_limits)
if len(long_plan.speeds):
actuators.speed = long_plan.speeds[-1]
@ -673,7 +673,7 @@ class Controls:
good_speed = CS.vEgo > 5
max_torque = abs(self.sm['carOutput'].actuatorsOutput.steer) > 0.99
if undershooting and turning and good_speed and max_torque:
lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream else EventName.steerSaturated)
lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream_alert else EventName.steerSaturated)
self.steer_saturated_event_triggered = True
else:
self.steer_saturated_event_triggered = False
@ -730,7 +730,7 @@ class Controls:
self.always_on_lateral_active &= not (CS.brakePressed and CS.vEgo < self.frogpilot_toggles.always_on_lateral_pause_speed) or CS.standstill
self.always_on_lateral_active = bool(self.always_on_lateral_active)
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental:
if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode:
self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode
if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
@ -814,7 +814,7 @@ class Controls:
if self.enabled:
clear_event_types.add(ET.NO_ENTRY)
alerts = self.events.create_alerts(self.current_alert_types, [self.CP, CS, self.sm, self.is_metric, self.soft_disable_timer])
alerts = self.events.create_alerts(self.current_alert_types, [self.CP, CS, self.sm, self.is_metric, self.soft_disable_timer, self.frogpilot_toggles])
self.AM.add_many(self.sm.frame, alerts)
current_alert = self.AM.process_alerts(self.sm.frame, clear_event_types)
if current_alert:
@ -941,11 +941,8 @@ class Controls:
time.sleep(0.1)
# Update FrogPilot parameters
if FrogPilotVariables.toggles_updated:
self.update_toggles = True
elif self.update_toggles:
FrogPilotVariables.update_frogpilot_params()
self.update_toggles = False
if self.sm['frogpilotPlan'].togglesUpdated:
self.frogpilot_toggles = get_frogpilot_toggles()
def controlsd_thread(self):
e = threading.Event()

View File

@ -68,7 +68,7 @@ class DesireHelper:
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
self.turn_direction = TurnDirection.none
elif one_blinker and below_lane_change_speed and not carstate.standstill and frogpilot_toggles.turn_desires:
elif one_blinker and below_lane_change_speed and not carstate.standstill and frogpilot_toggles.use_turn_desires:
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
self.turn_direction = TurnDirection.turnLeft if carstate.leftBlinker else TurnDirection.turnRight

View File

@ -4,6 +4,7 @@ import math
import os
from enum import IntEnum
from collections.abc import Callable
from types import SimpleNamespace
from cereal import log, car
import cereal.messaging as messaging
@ -212,31 +213,31 @@ AlertCallbackType = Callable[[car.CarParams, car.CarState, messaging.SubMaster,
def soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
if soft_disable_time < int(0.5 / DT_CTRL):
return ImmediateDisableAlert(alert_text_2)
return SoftDisableAlert(alert_text_2)
return func
def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
if soft_disable_time < int(0.5 / DT_CTRL):
return ImmediateDisableAlert(alert_text_2)
return UserSoftDisableAlert(alert_text_2)
return func
def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
branch = get_short_branch() # Ensure get_short_branch is cached to avoid lags on startup
if "REPLAY" in os.environ:
branch = "replay"
return StartupAlert("WARNING: This branch is not tested", branch, alert_status=AlertStatus.userPrompt)
def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage")
def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
return Alert(
f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
"",
@ -244,7 +245,7 @@ def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 0.4)
def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
first_word = 'Recalibration' if sm['liveCalibration'].calStatus == log.LiveCalibrationData.Status.recalibrating else 'Calibration'
return Alert(
f"{first_word} in Progress: {sm['liveCalibration'].calPerc:.0f}%",
@ -253,54 +254,39 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2)
def torque_nn_load_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
model_name = Params().get("NNFFModelName", encoding='utf-8')
if model_name == "":
return Alert(
"NNFF Torque Controller not available",
"Donate logs to Twilsonco to get your car supported!",
AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 10.0)
else:
return Alert(
"NNFF Torque Controller loaded",
model_name,
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.engage, 5.0)
# *** debug alerts ***
def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
full_perc = round(100. - sm['deviceState'].freeSpacePercent)
return NormalPermanentAlert("Out of Storage", f"{full_perc}% full")
def posenet_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def posenet_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
mdl = sm['modelV2'].velocity.x[0] if len(sm['modelV2'].velocity.x) else math.nan
err = CS.vEgo - mdl
msg = f"Speed Error: {err:.1f} m/s"
return NoEntryAlert(msg, alert_text_1="Posenet Speed Invalid")
def process_not_running_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def process_not_running_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
not_running = [p.name for p in sm['managerState'].processes if not p.running and p.shouldBeRunning]
msg = ', '.join(not_running)
return NoEntryAlert(msg, alert_text_1="Process Not Running")
def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
bs = [s for s in sm.data.keys() if not sm.all_checks([s, ])]
msg = ', '.join(bs[:4]) # can't fit too many on one line
return NoEntryAlert(msg, alert_text_1="Communication Issue Between Processes")
def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
all_cams = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState')
bad_cams = [s.replace('State', '') for s in all_cams if s in sm.data.keys() and not sm.all_checks([s, ])]
return NormalPermanentAlert("Camera Malfunction", ', '.join(bad_cams))
def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
rpy = sm['liveCalibration'].rpyCalib
yaw = math.degrees(rpy[2] if len(rpy) == 3 else math.nan)
pitch = math.degrees(rpy[1] if len(rpy) == 3 else math.nan)
@ -308,34 +294,34 @@ def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging
return NormalPermanentAlert("Calibration Invalid", angles)
def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
cpu = max(sm['deviceState'].cpuTempC, default=0.)
gpu = max(sm['deviceState'].gpuTempC, default=0.)
temp = max((cpu, gpu, sm['deviceState'].memoryTempC))
return NormalPermanentAlert("System Overheated", f"{temp:.0f} °C")
def low_memory_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def low_memory_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
return NormalPermanentAlert("Low Memory", f"{sm['deviceState'].memoryUsagePercent}% used")
def high_cpu_usage_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def high_cpu_usage_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
x = max(sm['deviceState'].cpuUsagePercent, default=0.)
return NormalPermanentAlert("High CPU Usage", f"{x}% used")
def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
return NormalPermanentAlert("Driving Model Lagging", f"{sm['modelV2'].frameDropPerc:.1f}% frames dropped")
def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
text = "Enable Adaptive Cruise to Engage"
if CP.carName == "honda":
text = "Enable Main Switch to Engage"
return NoEntryAlert(text)
def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
axes = sm['testJoystick'].axes
gb, steer = list(axes)[:2] if len(axes) else (0., 0.)
vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%"
@ -343,11 +329,22 @@ def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
# FrogPilot Alerts
def custom_startup_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
params = Params()
return StartupAlert(params.get("StartupMessageTop", encoding='utf-8') or "", params.get("StartupMessageBottom", encoding='utf-8') or "", alert_status=AlertStatus.frogpilot)
def custom_startup_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
return StartupAlert(frogpilot_toggles.startup_alert_top, frogpilot_toggles.startup_alert_bottom, alert_status=AlertStatus.frogpilot)
def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def forcing_stop_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
model_length = sm['frogpilotPlan'].forcingStopLength
model_length_msg = f"{model_length:.1f} meters" if metric else f"{model_length * CV.METER_TO_FOOT:.1f} feet"
return Alert(
f"Forcing the car to stop in {model_length_msg}",
"Press the gas pedal or 'Resume' button to override",
AlertStatus.frogpilot, AlertSize.mid,
Priority.MID, VisualAlert.none, AudibleAlert.prompt, 1.)
def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
holiday_messages = {
"new_years": ("Happy New Year! 🎉", "newYearsDayAlert"),
"valentines": ("Happy Valentine's Day! ❤️", "valentinesDayAlert"),
@ -362,7 +359,7 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
"christmas_week": ("Merry Christmas! 🎄", "christmasAlert")
}
holiday_name = Params().get("CurrentHolidayTheme", encoding='utf-8')
holiday_name = frogpilot_toggles.current_holiday_theme
message, alert_type = holiday_messages.get(holiday_name, ("", ""))
return Alert(
@ -371,7 +368,8 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
AlertStatus.normal, AlertSize.small,
Priority.LOWEST, VisualAlert.none, AudibleAlert.engage, 5.)
def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
lane_width = sm['frogpilotPlan'].laneWidthLeft if CS.leftBlinker else sm['frogpilotPlan'].laneWidthRight
lane_width_msg = f"{lane_width:.1f} meters" if metric else f"{lane_width * CV.METER_TO_FOOT:.1f} feet"
@ -381,6 +379,23 @@ def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S
AlertStatus.normal, AlertSize.mid,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2)
def torque_nn_load_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
model_name = Params().get("NNFFModelName", encoding='utf-8')
if model_name == "":
return Alert(
"NNFF Torque Controller not available",
"Donate logs to Twilsonco to get your car supported!",
AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 10.0)
else:
return Alert(
"NNFF Torque Controller loaded",
model_name,
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 5.0)
EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
# ********** events with no alerts **********
@ -1020,11 +1035,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
},
EventName.forcingStop: {
ET.WARNING: Alert(
"Forcing the car to stop",
"Press the gas pedal or 'Resume' button to override",
AlertStatus.frogpilot, AlertSize.mid,
Priority.MID, VisualAlert.none, AudibleAlert.prompt, 1.),
ET.WARNING: forcing_stop_alert,
},
EventName.goatSteerSaturated: {
@ -1072,7 +1083,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
"openpilot crashed",
"Please post the 'Error Log' in the FrogPilot Discord!",
AlertStatus.normal, AlertSize.mid,
Priority.HIGH, VisualAlert.none, AudibleAlert.none, 10.),
Priority.HIGHEST, VisualAlert.none, AudibleAlert.prompt, 10.),
},
EventName.pedalInterceptorNoBrake: {
@ -1091,6 +1102,14 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 3.),
},
EventName.thisIsFineSteerSaturated: {
ET.WARNING: Alert(
"This is fine",
"",
AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.thisIsFine, 2.),
},
EventName.torqueNNLoad: {
ET.PERMANENT: torque_nn_load_alert,
},

View File

@ -326,10 +326,10 @@ class LongitudinalMpc:
lead_xv = np.column_stack((x_lead_traj, v_lead_traj))
return lead_xv
def process_lead(self, lead, increased_distance=0):
def process_lead(self, lead):
v_ego = self.x0[1]
if lead is not None and lead.status:
x_lead = lead.dRel - increased_distance
x_lead = lead.dRel
v_lead = lead.vLead
a_lead = lead.aLeadK
a_lead_tau = lead.aLeadTau
@ -355,12 +355,11 @@ class LongitudinalMpc:
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, trafficModeActive, frogpilot_toggles, personality=log.LongitudinalPersonality.standard):
def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_follow, trafficModeActive, 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, increased_distance)
lead_xv_0 = self.process_lead(lead_one)
lead_xv_1 = self.process_lead(lead_two)
# To estimate a safe distance from a moving lead, we calculate how much stopping
@ -370,7 +369,8 @@ class LongitudinalMpc:
lead_1_obstacle = lead_xv_1[:,0] + get_stopped_equivalence_factor(lead_xv_1[:,1])
self.params[:,0] = ACCEL_MIN
self.params[:,1] = self.max_a
# negative accel constraint causes problems because negative speed is not allowed
self.params[:,1] = max(0.0, self.max_a)
# Update in ACC mode or ACC/e2e blend
if self.mode == 'acc':
@ -379,6 +379,7 @@ class LongitudinalMpc:
# Fake an obstacle for cruise, this ensures smooth acceleration to set speed
# when the leads are no factor.
v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05)
# TODO does this make sense when max_a is negative?
v_upper = v_ego + (T_IDXS * self.max_a * 1.05)
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1),
v_lower,

View File

@ -22,7 +22,7 @@ A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
A_CRUISE_MAX_BP = [0., 10.0, 25., 40.]
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
ALLOW_THROTTLE_THRESHOLD = 0.5
ACCEL_LIMIT_MARGIN = 0.05
MIN_ALLOW_THROTTLE_SPEED = 2.5
# Lookup table for turns
_A_TOTAL_MAX_V = [1.7, 3.2]
@ -58,7 +58,10 @@ def get_accel_from_plan(CP, speeds, accels):
a_target_now = interp(DT_MDL, CONTROL_N_T_IDX, accels)
v_target = interp(CP.longitudinalActuatorDelay + DT_MDL, CONTROL_N_T_IDX, speeds)
a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now
if v_target != v_target_now:
a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now
else:
a_target = a_target_now
v_target_1sec = interp(CP.longitudinalActuatorDelay + DT_MDL + 1.0, CONTROL_N_T_IDX, speeds)
else:
@ -183,7 +186,7 @@ class LongitudinalPlanner:
throttle_prob = 1.0
return x, v, a, j, throttle_prob
def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggles):
def update(self, classic_model, radarless_model, sm, frogpilot_toggles):
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
if len(sm['carControl'].orientationNED) == 3:
@ -209,7 +212,8 @@ class LongitudinalPlanner:
accel_limits = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
if self.mpc.mode == 'acc':
accel_limits_turns = limit_accel_in_turns(v_ego, sm['carState'].steeringAngleDeg, accel_limits, self.CP)
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
accel_limits_turns = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_limits, self.CP)
else:
accel_limits_turns = [ACCEL_MIN, ACCEL_MAX]
@ -223,12 +227,13 @@ class LongitudinalPlanner:
# Compute model v_ego error
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error, v_ego, frogpilot_toggles.taco_tune)
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD
# Don't clip at low speeds since throttle_prob doesn't account for creep
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
if not self.allow_throttle and v_ego > 5.0 and secretgoodopenpilot_model: # Don't clip at low speeds since throttle_prob doesn't account for creep
# MPC breaks when accel limits would cause negative velocity within the MPC horizon, so we clip the max accel limit at vEgo/T_MAX plus a bit of margin
clipped_accel_coast = max(accel_coast, accel_limits_turns[0], -v_ego / T_IDXS_MPC[-1] + ACCEL_LIMIT_MARGIN)
accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast)
if not self.allow_throttle and not classic_model:
clipped_accel_coast = max(accel_coast, accel_limits_turns[0])
clipped_accel_coast_interp = interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_limits_turns[1], clipped_accel_coast])
accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast_interp)
if force_slow_decel:
v_cruise = 0.0
@ -242,8 +247,9 @@ class LongitudinalPlanner:
lead_states = [self.lead_one, self.lead_two]
for index in range(len(lead_states)):
if len(model_leads) > index:
distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not sm['frogpilotCarState'].trafficModeActive else 0
model_lead = model_leads[index]
lead_states[index].update(model_lead.x[0], model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob)
lead_states[index].update(model_lead.x[0] - distance_offset, model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob)
else:
lead_states[index].reset()
else:
@ -254,7 +260,7 @@ class LongitudinalPlanner:
self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1])
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(self.lead_one, self.lead_two, sm['frogpilotPlan'].vCruise, x, v, a, j, radarless_model, sm['frogpilotPlan'].tFollow,
sm['frogpilotCarState'].trafficModeActive, frogpilot_toggles, personality=sm['controlsState'].personality)
sm['frogpilotCarState'].trafficModeActive, 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)

View File

@ -6,7 +6,7 @@ from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
import cereal.messaging as messaging
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles
def publish_ui_plan(sm, pm, longitudinal_planner):
ui_send = messaging.new_message('uiPlan')
@ -30,31 +30,26 @@ def plannerd_thread():
longitudinal_planner = LongitudinalPlanner(CP)
pm = messaging.PubMaster(['longitudinalPlan', 'uiPlan'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2', 'frogpilotCarControl', 'frogpilotCarState', 'frogpilotPlan'],
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2',
'frogpilotCarControl', 'frogpilotCarState', 'frogpilotPlan'],
poll='modelV2', ignore_avg_freq=['radarState'])
# FrogPilot variables
frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
frogpilot_toggles = get_frogpilot_toggles(True)
classic_model = frogpilot_toggles.classic_model
radarless_model = frogpilot_toggles.radarless_model
secretgoodopenpilot_model = frogpilot_toggles.secretgoodopenpilot_model
update_toggles = False
while True:
sm.update()
if sm.updated['modelV2']:
longitudinal_planner.update(radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggles)
longitudinal_planner.update(classic_model, radarless_model, sm, frogpilot_toggles)
longitudinal_planner.publish(sm, pm)
publish_ui_plan(sm, pm, longitudinal_planner)
# Update FrogPilot parameters
if FrogPilotVariables.toggles_updated:
update_toggles = True
elif update_toggles:
FrogPilotVariables.update_frogpilot_params()
update_toggles = False
if sm['frogpilotPlan'].togglesUpdated:
frogpilot_toggles = get_frogpilot_toggles()
def main():
plannerd_thread()

View File

@ -2,6 +2,7 @@
import importlib
import math
from collections import deque
from types import SimpleNamespace
from typing import Any
import capnp
@ -13,7 +14,7 @@ from openpilot.common.swaglog import cloudlog
from openpilot.common.simple_kalman import KF1D
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles
# Default lead acceleration decay set to 50% at 1s
_LEAD_ACCEL_TAU = 1.5
@ -108,6 +109,19 @@ class Track:
"radarTrackId": self.identifier,
}
def potential_adjacent_lead(self, far: bool, lane_width: float, left: bool, model_data: capnp._DynamicStructReader):
adjacent_lane_max = float('inf') if far else lane_width * 1.5
adjacent_lane_min = max(lane_width * 1.5, 4.5) if far else max(lane_width * 0.5, 1.5)
y_delta = self.yRel + interp(self.dRel, model_data.position.x, model_data.position.y)
if left and adjacent_lane_min < y_delta < adjacent_lane_max:
return True
elif not left and adjacent_lane_min < -y_delta < adjacent_lane_max:
return True
else:
return False
def potential_low_speed_lead(self, v_ego: float):
# stop for stuff in front of you and low speed, even without model confirmation
# Radar points closer than 0.75, are almost always glitches on toyota radars
@ -168,9 +182,9 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa
def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader,
model_v_ego: float, lead_detection_threshold: float, low_speed_override: bool = True) -> dict[str, Any]:
model_v_ego: float, frogpilot_toggles: SimpleNamespace, frogpilotCarState: capnp._DynamicStructReader, low_speed_override: bool = True) -> dict[str, Any]:
# Determine leads, this is where the essential logic happens
if len(tracks) > 0 and ready and lead_msg.prob > lead_detection_threshold:
if len(tracks) > 0 and ready and lead_msg.prob > frogpilot_toggles.lead_detection_probability:
track = match_vision_to_track(v_ego, lead_msg, tracks)
else:
track = None
@ -178,7 +192,7 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn
lead_dict = {'status': False}
if track is not None:
lead_dict = track.get_RadarState(lead_msg.prob)
elif (track is None) and ready and (lead_msg.prob > lead_detection_threshold):
elif (track is None) and ready and (lead_msg.prob > frogpilot_toggles.lead_detection_probability):
lead_dict = get_RadarState_from_vision(lead_msg, v_ego, model_v_ego)
if low_speed_override:
@ -190,11 +204,25 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn
if (not lead_dict['status']) or (closest_track.dRel < lead_dict['dRel']):
lead_dict = closest_track.get_RadarState()
if 'dRel' in lead_dict:
lead_dict['dRel'] -= max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0
return lead_dict
def get_lead_adjacent(tracks: dict[int, Track], model_data: capnp._DynamicStructReader, lane_width: float, left: bool = True, far: bool = False) -> dict[str, Any]:
lead_dict = {'status': False}
adjacent_tracks = [c for c in tracks.values() if c.vLead > 1 and c.potential_adjacent_lead(far, lane_width, left, model_data)]
if len(adjacent_tracks) > 0:
closest_track = min(adjacent_tracks, key=lambda c: c.dRel)
lead_dict = closest_track.get_RadarState()
return lead_dict
class RadarD:
def __init__(self, radar_ts: float, delay: int = 0):
def __init__(self, frogpilot_toggles, radar_ts: float, delay: int = 0):
self.points: dict[int, tuple[float, float, float]] = {}
self.current_time = 0.0
@ -213,12 +241,9 @@ class RadarD:
self.ready = False
# FrogPilot variables
self.frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
self.frogpilot_toggles = frogpilot_toggles
self.velocity_model = self.frogpilot_toggles.velocity_model
self.update_toggles = False
self.classic_model = self.frogpilot_toggles.classic_model
def update(self, sm: messaging.SubMaster, rr):
self.ready = sm.seen['modelV2']
@ -263,23 +288,26 @@ class RadarD:
self.radar_state.radarErrors = list(radar_errors)
self.radar_state.carStateMonoTime = sm.logMonoTime['carState']
if self.velocity_model and len(sm['modelV2'].velocity.x):
model_v_ego = sm['modelV2'].velocity.x[0]
elif len(sm['modelV2'].temporalPose.trans):
if self.classic_model and len(sm['modelV2'].temporalPose.trans):
model_v_ego = sm['modelV2'].temporalPose.trans[0]
elif len(sm['modelV2'].velocity.x):
model_v_ego = sm['modelV2'].velocity.x[0]
else:
model_v_ego = self.v_ego
leads_v3 = sm['modelV2'].leadsV3
if len(leads_v3) > 1:
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, self.frogpilot_toggles.lead_detection_threshold, low_speed_override=True)
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, self.frogpilot_toggles.lead_detection_threshold, low_speed_override=False)
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, self.frogpilot_toggles, sm['frogpilotCarState'], low_speed_override=True)
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, self.frogpilot_toggles, sm['frogpilotCarState'], low_speed_override=False)
if self.frogpilot_toggles.adjacent_lead_tracking and self.ready:
self.radar_state.leadLeft = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthLeft, left=True)
self.radar_state.leadLeftFar = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthLeft, left=True, far=True)
self.radar_state.leadRight = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthRight, left=False)
self.radar_state.leadRightFar = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthRight, left=False, far=True)
# Update FrogPilot parameters
if FrogPilotVariables.toggles_updated:
self.update_toggles = True
elif self.update_toggles:
FrogPilotVariables.update_frogpilot_params()
self.update_toggles = False
if sm['frogpilotPlan'].togglesUpdated:
self.frogpilot_toggles = get_frogpilot_toggles()
def publish(self, pm: messaging.PubMaster, lag_ms: float):
assert self.radar_state is not None
@ -348,14 +376,14 @@ def main():
RI = RadarInterface(CP)
rk = Ratekeeper(1.0 / CP.radarTimeStep, print_delay_threshold=None)
RD = RadarD(CP.radarTimeStep, RI.delay)
# FrogPilot variables
frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
frogpilot_toggles = get_frogpilot_toggles(True)
RD = RadarD(frogpilot_toggles, CP.radarTimeStep, RI.delay)
if not frogpilot_toggles.radarless_model:
sm = messaging.SubMaster(['modelV2', 'carState'], frequency=int(1./DT_CTRL))
sm = messaging.SubMaster(['modelV2', 'carState', 'frogpilotCarState', 'frogpilotPlan'], frequency=int(1./DT_CTRL))
pm = messaging.PubMaster(['radarState', 'liveTracks'])
while 1:
can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)

View File

@ -23,12 +23,6 @@
"blue": 54,
"alpha": 242
},
"RoadEdges": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"Sidebar1": {
"red": 23,
"green": 134,

View File

@ -1,7 +1,7 @@
import os
import requests
from openpilot.selfdrive.frogpilot.frogpilot_functions import delete_file, is_url_pingable
from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file, is_url_pingable
GITHUB_URL = "https://raw.githubusercontent.com/FrogAi/FrogPilot-Resources/"
GITLAB_URL = "https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/"
@ -44,10 +44,10 @@ def handle_error(destination, error_message, error, download_param, progress_par
def handle_request_error(error, destination, download_param, progress_param, params_memory):
error_map = {
requests.HTTPError: lambda e: f"Server error ({e.response.status_code})" if e.response else "Server error.",
requests.ConnectionError: "Connection dropped.",
requests.Timeout: "Download timed out.",
requests.RequestException: "Network request error. Check connection."
requests.HTTPError: lambda e: f"Server error ({e.response.status_code})" if e.response else "Server error.",
requests.RequestException: "Network request error. Check connection.",
requests.Timeout: "Download timed out."
}
error_message = error_map.get(type(error), "Unexpected error.")
@ -75,19 +75,6 @@ def get_repository_url():
return GITLAB_URL
return None
def link_valid(url):
try:
response = requests.head(url, allow_redirects=True, timeout=5)
response.raise_for_status()
return True
except requests.HTTPError as e:
if e.response.status_code != 404:
handle_request_error(e, None, None, None, None)
return False
except Exception as e:
handle_request_error(e, None, None, None, None)
return False
def verify_download(file_path, url, initial_download=True):
remote_file_size = get_remote_file_size(url)
if remote_file_size is None:

View File

@ -23,12 +23,6 @@
"blue": 255,
"alpha": 255
},
"RoadEdges": {
"red": 242,
"green": 117,
"blue": 3,
"alpha": 255
},
"Sidebar1": {
"red": 134,
"green": 47,

View File

@ -0,0 +1,44 @@
{
"LaneLines": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"LeadMarker": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"Path": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"PathEdge": {
"red": 18,
"green": 107,
"blue": 54,
"alpha": 242
},
"Sidebar1": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"Sidebar2": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
},
"Sidebar3": {
"red": 23,
"green": 134,
"blue": 68,
"alpha": 242
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,26 +1,31 @@
import json
import os
import re
import requests
import shutil
import time
import urllib.parse
import urllib.request
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params, UnknownKeyName
from openpilot.common.params import Params
from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_remote_file_size, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH, delete_file
from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH
from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file
VERSION = "v9"
VERSION = "v10"
DEFAULT_MODEL = "north-dakota"
DEFAULT_MODEL_NAME = "North Dakota (Default)"
def process_model_name(model_name):
cleaned_name = re.sub(r'[🗺️👀📡]', '', model_name)
cleaned_name = re.sub(r'[^a-zA-Z0-9()-]', '', cleaned_name)
return cleaned_name.replace(' ', '').replace('(Default)', '').replace('-', '')
class ModelManager:
def __init__(self):
self.params = Params()
@ -30,6 +35,63 @@ class ModelManager:
self.download_param = "ModelToDownload"
self.download_progress_param = "ModelDownloadProgress"
@staticmethod
def fetch_models(url):
try:
with urllib.request.urlopen(url, timeout=10) as response:
return json.loads(response.read().decode('utf-8'))['models']
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
@staticmethod
def fetch_all_model_sizes(repo_url):
project_path = "FrogAi/FrogPilot-Resources"
branch = "Models"
if "github" in repo_url:
api_url = f"https://api.github.com/repos/{project_path}/contents?ref={branch}"
elif "gitlab" in repo_url:
api_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/tree?ref={branch}"
else:
raise ValueError(f"Unsupported repository URL format: {repo_url}. Supported formats are GitHub and GitLab URLs.")
try:
response = requests.get(api_url)
response.raise_for_status()
thneed_files = [file for file in response.json() if file['name'].endswith('.thneed')]
if "gitlab" in repo_url:
model_sizes = {}
for file in thneed_files:
file_path = file['path']
metadata_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/files/{urllib.parse.quote_plus(file_path)}/raw?ref={branch}"
metadata_response = requests.head(metadata_url)
metadata_response.raise_for_status()
model_sizes[file['name'].replace('.thneed', '')] = int(metadata_response.headers.get('content-length', 0))
return model_sizes
else:
return {file['name'].replace('.thneed', ''): file['size'] for file in thneed_files if 'size' in file}
except requests.RequestException as e:
raise ConnectionError(f"Failed to fetch model sizes from {'GitHub' if 'github' in repo_url else 'GitLab'}: {e}")
@staticmethod
def copy_default_model():
classic_default_model_path = os.path.join(MODELS_PATH, f"{DEFAULT_MODEL}.thneed")
source_path = os.path.join(BASEDIR, "selfdrive", "classic_modeld", "models", "supercombo.thneed")
if os.path.isfile(source_path):
shutil.copyfile(source_path, classic_default_model_path)
print(f"Copied the classic default model from {source_path} to {classic_default_model_path}")
default_model_path = os.path.join(MODELS_PATH, "secret-good-openpilot.thneed")
source_path = os.path.join(BASEDIR, "selfdrive", "modeld", "models", "supercombo.thneed")
if os.path.isfile(source_path):
shutil.copyfile(source_path, default_model_path)
print(f"Copied the default model from {source_path} to {default_model_path}")
def handle_verification_failure(self, model, model_path):
if self.params_memory.get_bool(self.cancel_download_param):
return
@ -65,65 +127,53 @@ class ModelManager:
else:
self.handle_verification_failure(model_to_download, model_path)
def fetch_models(self, url):
try:
with urllib.request.urlopen(url, timeout=10) as response:
return json.loads(response.read().decode('utf-8'))['models']
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
def queue_model_download(self, model, model_name=None):
while self.params_memory.get(self.download_param, encoding='utf-8'):
time.sleep(1)
self.params_memory.put(self.download_param, model)
if model_name:
self.params_memory.put(self.download_progress_param, f"Downloading {model_name}...")
def update_model_params(self, model_info, repo_url):
available_models, available_model_names, experimental_models, navigation_models, radarless_models, velocity_models = [], [], [], [], [], []
available_models = []
for model in model_info:
available_models.append(model['id'])
available_model_names.append(model['name'])
if model.get("e2e_longitudinal", False):
e2e_longitudinal_models.append(model['id'])
if model.get("experimental", False):
experimental_models.append(model['id'])
if model.get("gas_brake", False):
gas_brake_models.append(model['id'])
if model.get("velocity", False):
velocity_models.append(model['id'])
if "🗺️" in model['name']:
navigation_models.append(model['id'])
if "📡" not in model['name']:
radarless_models.append(model['id'])
self.params.put_nonblocking("AvailableModels", ','.join(available_models))
self.params.put_nonblocking("AvailableModelsNames", ','.join(available_model_names))
self.params.put_nonblocking("ExperimentalModels", ','.join(experimental_models))
self.params.put_nonblocking("NavigationModels", ','.join(navigation_models))
self.params.put_nonblocking("RadarlessModels", ','.join(radarless_models))
self.params.put_nonblocking("VelocityModels", ','.join(velocity_models))
self.params.put_nonblocking("AvailableModelsNames", ','.join([model['name'] for model in model_info]))
self.params.put_nonblocking("ClassicModels", ','.join([model['id'] for model in model_info if model.get("classic_model", False)]))
self.params.put_nonblocking("ExperimentalModels", ','.join([model['id'] for model in model_info if model.get("experimental", False)]))
self.params.put_nonblocking("NavigationModels", ','.join([model['id'] for model in model_info if "🗺️" in model['name']]))
self.params.put_nonblocking("RadarlessModels", ','.join([model['id'] for model in model_info if "📡" not in model['name']]))
print("Models list updated successfully.")
if available_models:
models_downloaded = self.are_all_models_downloaded(available_models, available_model_names, repo_url)
models_downloaded = self.are_all_models_downloaded(available_models, repo_url)
self.params.put_bool_nonblocking("ModelsDownloaded", models_downloaded)
def are_all_models_downloaded(self, available_models, available_model_names, repo_url):
def are_all_models_downloaded(self, available_models, repo_url):
automatically_update_models = self.params.get_bool("AutomaticallyUpdateModels")
all_models_downloaded = True
model_sizes = self.fetch_all_model_sizes(repo_url)
download_queue = []
for model in available_models:
model_path = os.path.join(MODELS_PATH, f"{model}.thneed")
model_url = f"{repo_url}Models/{model}.thneed"
expected_size = model_sizes.get(model)
if expected_size is None:
print(f"Size data for {model} not available.")
continue
if os.path.isfile(model_path):
if automatically_update_models:
verify_result = verify_download(model_path, model_url, False)
if verify_result is None:
all_models_downloaded = False
elif not verify_result:
print(f"Model {model} is outdated. Re-downloading...")
delete_file(model_path)
download_queue.append(model)
all_models_downloaded = False
local_size = os.path.getsize(model_path)
if automatically_update_models and local_size != expected_size:
print(f"Model {model} is outdated. Re-downloading...")
delete_file(model_path)
download_queue.append(model)
all_models_downloaded = False
else:
if automatically_update_models:
print(f"Model {model} isn't downloaded. Downloading...")
@ -135,14 +185,6 @@ class ModelManager:
return all_models_downloaded
def queue_model_download(self, model, model_name=None):
while self.params_memory.get(self.download_param, encoding='utf-8'):
time.sleep(1)
self.params_memory.put(self.download_param, model)
if model_name:
self.params_memory.put(self.download_progress_param, f"Downloading {model_name}...")
def validate_models(self):
current_model = self.params.get("Model", encoding='utf-8')
current_model_name = self.params.get("ModelName", encoding='utf-8')
@ -162,31 +204,11 @@ class ModelManager:
for model_file in os.listdir(MODELS_PATH):
model_name = model_file.replace(".thneed", "")
if model_name not in available_models.split(','):
reason = "Model is not in the list of available models"
if model_name == current_model:
self.params.put_nonblocking("Model", DEFAULT_MODEL)
self.params.put_nonblocking("ModelName", DEFAULT_MODEL_NAME)
delete_file(os.path.join(MODELS_PATH, model_file))
print(f"Deleted model file: {model_file} - Reason: {reason}")
def copy_default_model(self):
default_model_path = os.path.join(MODELS_PATH, f"{DEFAULT_MODEL}.thneed")
source_path = os.path.join(BASEDIR, "selfdrive", "classic_modeld", "models", "supercombo.thneed")
if os.path.isfile(source_path) and not os.path.isfile(default_model_path):
shutil.copyfile(source_path, default_model_path)
print(f"Copied default model from {source_path} to {default_model_path}")
else:
print(f"Source default model not found at {source_path}. Exiting...")
sgo_model_path = os.path.join(MODELS_PATH, "secret-good-openpilot.thneed")
source_path = os.path.join(BASEDIR, "selfdrive", "modeld", "models", "supercombo.thneed")
if os.path.isfile(source_path) and not os.path.isfile(sgo_model_path):
shutil.copyfile(source_path, sgo_model_path)
print(f"Copied 'secret-good-openpilot' model from {source_path} to {sgo_model_path}")
else:
print(f"Source 'secret-good-openpilot' model not found at {source_path}. Exiting...")
print(f"Deleted model file: {model_file} - Reason: Model is not in the list of available models")
def update_models(self, boot_run=False):
if boot_run:
@ -197,12 +219,12 @@ class ModelManager:
print("GitHub and GitLab are offline...")
return
model_info = self.fetch_models(f"{repo_url}Versions/model_names_{VERSION}.json")
if model_info:
self.update_model_params(model_info, repo_url)
if boot_run:
self.validate_models()
else:
model_info = self.fetch_models(f"{repo_url}Versions/model_names_{VERSION}.json")
if model_info:
self.update_model_params(model_info, repo_url)
def download_all_models(self):
repo_url = get_repository_url()
@ -230,10 +252,8 @@ class ModelManager:
if not os.path.isfile(os.path.join(MODELS_PATH, f"{model}.thneed")):
model_index = available_models.index(model)
model_name = available_model_names[model_index]
cleaned_model_name = re.sub(r'[🗺️👀📡]', '', model_name).strip()
print(f"Downloading model: {cleaned_model_name}")
self.queue_model_download(model, cleaned_model_name)
while self.params_memory.get(self.download_param, encoding='utf-8'):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 KiB

View File

@ -1,4 +1,5 @@
import glob
import json
import os
import re
import requests
@ -11,12 +12,14 @@ from dateutil import easter
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, link_valid, verify_download
from openpilot.selfdrive.frogpilot.frogpilot_functions import ACTIVE_THEME_PATH, THEME_SAVE_PATH, update_frogpilot_toggles
from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.selfdrive.frogpilot.frogpilot_functions import ACTIVE_THEME_PATH, THEME_SAVE_PATH
from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles
CANCEL_DOWNLOAD_PARAM = "CancelThemeDownload"
DOWNLOAD_PROGRESS_PARAM = "ThemeDownloadProgress"
def update_theme_asset(asset_type, theme, holiday_theme, params):
save_location = os.path.join(ACTIVE_THEME_PATH, asset_type)
@ -49,6 +52,7 @@ def update_theme_asset(asset_type, theme, holiday_theme, params):
shutil.copytree(asset_location, save_location)
print(f"Copied {asset_location} to {save_location}")
def update_wheel_image(image, holiday_theme=None, random_event=True):
wheel_save_location = os.path.join(ACTIVE_THEME_PATH, "steering_wheel")
@ -74,6 +78,7 @@ def update_wheel_image(image, holiday_theme=None, random_event=True):
print(f"Copied {source_file} to {destination_file}")
break
class ThemeManager:
def __init__(self):
self.params = Params()
@ -90,8 +95,83 @@ class ThemeManager:
@staticmethod
def is_within_week_of(target_date, now):
start_of_week = target_date - timedelta(days=target_date.weekday())
end_of_week = start_of_week + timedelta(days=6)
return start_of_week <= now <= end_of_week
return start_of_week <= now < target_date
@staticmethod
def fetch_files(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return [name for name in re.findall(r'href="[^"]*\/blob\/[^"]*\/([^"]*)"', response.text) if name.lower() != "license"]
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
@staticmethod
def fetch_assets(repo_url):
repo = "FrogAi/FrogPilot-Resources"
branches = ["Themes", "Distance-Icons", "Steering-Wheels"]
assets = {
"themes": {},
"distance_icons": [],
"wheels": []
}
if "github" in repo_url:
base_api_url = "https://api.github.com/repos"
elif "gitlab" in repo_url:
base_api_url = "https://gitlab.com/api/v4/projects"
repo = repo.replace("/", "%2F")
else:
print(f"Unsupported repository URL: {repo_url}")
return assets
for branch in branches:
if "github" in repo_url:
api_url = f"{base_api_url}/{repo}/git/trees/{branch}?recursive=1"
elif "gitlab" in repo_url:
api_url = f"{base_api_url}/{repo}/repository/tree?ref={branch}&recursive=true"
try:
print(f"Fetching assets from branch '{branch}': {api_url}")
response = requests.get(api_url, timeout=10)
response.raise_for_status()
content = response.json()
if "github" in repo_url:
items = content.get('tree', [])
elif "gitlab" in repo_url:
items = content
for item in items:
if item["type"] != "blob":
continue
item_path = item["path"].lower()
if branch == "Themes":
theme_name = item["path"].split('/')[0]
assets["themes"].setdefault(theme_name, set())
if "icons" in item_path:
assets["themes"][theme_name].add("icons")
elif "signals" in item_path:
assets["themes"][theme_name].add("signals")
elif "sounds" in item_path:
assets["themes"][theme_name].add("sounds")
else:
assets["themes"][theme_name].add("colors")
elif branch == "Distance-Icons":
assets["distance_icons"].append(item["path"])
elif branch == "Steering-Wheels":
assets["wheels"].append(item["path"])
except requests.exceptions.RequestException as error:
print(f"Error occurred when fetching from branch '{branch}': {error}")
assets["themes"] = {k: list(v) for k, v in assets["themes"].items()}
return assets
def update_holiday(self):
now = date.today()
@ -150,6 +230,7 @@ class ThemeManager:
"wheel_image": ("wheel_image", "stock")
}
theme_changed = False
for asset, (asset_type, current_value) in asset_mappings.items():
if current_value != self.previous_assets.get(asset) or current_holiday_theme != self.previous_assets.get("holiday_theme"):
print(f"Updating {asset}: {asset_type} with value {current_holiday_theme if current_holiday_theme else current_value}")
@ -160,9 +241,13 @@ class ThemeManager:
update_theme_asset(asset_type, current_value, current_holiday_theme, self.params)
self.previous_assets[asset] = current_value
theme_changed = True
self.previous_assets["holiday_theme"] = current_holiday_theme
update_frogpilot_toggles()
if theme_changed:
if current_holiday_theme:
self.previous_assets["holiday_theme"] = current_holiday_theme
self.params_memory.put_bool("ThemeUpdated", True)
update_frogpilot_toggles()
def extract_zip(self, zip_file, extract_path):
print(f"Extracting {zip_file} to {extract_path}")
@ -242,26 +327,6 @@ class ThemeManager:
self.handle_verification_failure(extentions, theme_component, theme_name, theme_param, download_path)
@staticmethod
def fetch_files(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return [name for name in re.findall(r'href="[^"]*\/blob\/[^"]*\/([^"]*)"', response.text) if name.lower() != "license"]
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
@staticmethod
def fetch_folders(url):
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return re.findall(r'href="[^"]*\/tree\/[^"]*\/([^"]*)"', response.text)
except Exception as error:
handle_request_error(error, None, None, None, None)
return []
def update_theme_params(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels):
def filter_existing_assets(assets, subfolder):
existing_themes = {
@ -333,6 +398,7 @@ class ThemeManager:
def update_themes(self, boot_run=False):
if not os.path.exists(THEME_SAVE_PATH):
print(f"Theme save path does not exist: {THEME_SAVE_PATH}")
return
repo_url = get_repository_url()
@ -343,34 +409,34 @@ class ThemeManager:
if boot_run:
self.validate_themes()
if repo_url == GITHUB_URL:
base_url = "https://github.com/FrogAi/FrogPilot-Resources/blob/Themes/"
distance_icons_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Distance-Icons")
wheel_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Steering-Wheels")
else:
base_url = "https://gitlab.com/FrogAi/FrogPilot-Resources/-/blob/Themes/"
distance_icons_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Distance-Icons")
wheel_files = self.fetch_files("https://gitlab.com/FrogAi/FrogPilot-Resources/-/blob/Steering-Wheels")
assets = self.fetch_assets(repo_url)
theme_folders = self.fetch_folders(base_url)
downloadable_colors = []
downloadable_icons = []
downloadable_signals = []
downloadable_sounds = []
for theme in theme_folders:
for theme, available_assets in assets["themes"].items():
theme_name = theme.replace('_', ' ').split('.')[0].title()
print(f"Checking theme: {theme_name}")
if link_valid(f"{base_url}{theme}/colors.zip"):
if "colors" in available_assets:
downloadable_colors.append(theme_name)
if link_valid(f"{base_url}{theme}/icons.zip"):
if "icons" in available_assets:
downloadable_icons.append(theme_name)
if link_valid(f"{base_url}{theme}/signals.zip"):
if "signals" in available_assets:
downloadable_signals.append(theme_name)
if link_valid(f"{base_url}{theme}/sounds.zip"):
if "sounds" in available_assets:
downloadable_sounds.append(theme_name)
downloadable_distance_icons = [distance_icons.replace('_', ' ').split('.')[0].title() for distance_icons in distance_icons_files]
downloadable_wheels = [wheel.replace('_', ' ').split('.')[0].title() for wheel in wheel_files]
downloadable_distance_icons = [distance_icon.replace('_', ' ').split('.')[0].title() for distance_icon in assets["distance_icons"]]
downloadable_wheels = [wheel.replace('_', ' ').split('.')[0].title() for wheel in assets["wheels"]]
print(f"Downloadable Colors: {downloadable_colors}")
print(f"Downloadable Icons: {downloadable_icons}")
print(f"Downloadable Signals: {downloadable_signals}")
print(f"Downloadable Sounds: {downloadable_sounds}")
print(f"Downloadable Distance Icons: {downloadable_distance_icons}")
print(f"Downloadable Wheels: {downloadable_wheels}")
self.update_theme_params(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -12,7 +12,7 @@ from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_acceleration import Fr
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_events import FrogPilotEvents
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_following import FrogPilotFollowing
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_vcruise import FrogPilotVCruise
from openpilot.selfdrive.frogpilot.frogpilot_functions import MovingAverageCalculator, calculate_lane_width, calculate_road_curvature, update_frogpilot_toggles
from openpilot.selfdrive.frogpilot.frogpilot_utilities import MovingAverageCalculator, calculate_lane_width, calculate_road_curvature
from openpilot.selfdrive.frogpilot.frogpilot_variables import CRUISING_SPEED, MODEL_LENGTH, NON_DRIVING_GEARS, PLANNER_TIME, THRESHOLD
class FrogPilotPlanner:
@ -29,23 +29,21 @@ class FrogPilotPlanner:
self.tracking_lead_mac = MovingAverageCalculator()
self.lateral_check = False
self.lead_departing = False
self.model_stopped = False
self.slower_lead = False
self.taking_curve_quickly = False
self.tracking_lead = False
self.model_length = 0
self.road_curvature = 1
self.tracking_lead_distance = 0
self.v_cruise = 0
def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, radarless_model, radarState, frogpilot_toggles):
if radarless_model:
model_leads = list(modelData.leadsV3)
if len(model_leads) > 0:
distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0
model_lead = model_leads[0]
self.lead_one.update(model_lead.x[0], model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob)
self.lead_one.update(model_lead.x[0] - distance_offset, model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob)
else:
self.lead_one.reset()
else:
@ -55,41 +53,25 @@ class FrogPilotPlanner:
v_ego = max(carState.vEgo, 0)
v_lead = self.lead_one.vLead
driving_gear = carState.gearShifter not in NON_DRIVING_GEARS
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)
run_cem = frogpilot_toggles.conditional_experimental_mode or frogpilot_toggles.force_stops or frogpilot_toggles.green_light_alert or frogpilot_toggles.show_stopping_point
if run_cem and (controlsState.enabled or frogpilotCarControl.alwaysOnLateralActive) and driving_gear:
self.cem.update(carState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles)
if run_cem and (controlsState.enabled or frogpilotCarControl.alwaysOnLateralActive) and carState.gearShifter not in NON_DRIVING_GEARS:
self.cem.update(carState, frogpilotCarState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles)
else:
self.cem.stop_light_detected = False
self.frogpilot_events.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, modelData, frogpilot_toggles)
self.frogpilot_following.update(carState.aEgo, controlsState, frogpilotCarState, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles)
self.frogpilot_events.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, self.lead_one.dRel, modelData, v_lead, frogpilot_toggles)
self.frogpilot_following.update(carState.aEgo, controlsState, frogpilotCarState, self.lead_one.dRel, v_ego, v_lead, frogpilot_toggles)
check_lane_width = frogpilot_toggles.adjacent_lanes or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection
if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed:
check_lane_width = frogpilot_toggles.adjacent_paths or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection
if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed or frogpilot_toggles.adjacent_lead_tracking:
self.lane_width_left = calculate_lane_width(modelData.laneLines[0], modelData.laneLines[1], modelData.roadEdges[0])
self.lane_width_right = calculate_lane_width(modelData.laneLines[3], modelData.laneLines[2], modelData.roadEdges[1])
else:
self.lane_width_left = 0
self.lane_width_right = 0
if frogpilot_toggles.lead_departing_alert and self.tracking_lead and driving_gear and carState.standstill:
if self.tracking_lead_distance == 0:
self.tracking_lead_distance = lead_distance
self.lead_departing = lead_distance - self.tracking_lead_distance > 1
self.lead_departing &= v_lead > 1
else:
self.lead_departing = False
self.tracking_lead_distance = 0
self.lateral_check = v_ego >= frogpilot_toggles.pause_lateral_below_speed
self.lateral_check |= frogpilot_toggles.pause_lateral_below_signal and not (carState.leftBlinker or carState.rightBlinker)
self.lateral_check |= carState.standstill
@ -100,26 +82,20 @@ class FrogPilotPlanner:
self.road_curvature = calculate_road_curvature(modelData, v_ego) if not carState.standstill else 1
if frogpilot_toggles.random_events and v_ego > CRUISING_SPEED and driving_gear:
self.taking_curve_quickly = v_ego > (1 / self.road_curvature)**0.75 * 2 > CRUISING_SPEED * 2 and abs(carState.steeringAngleDeg) > 30
else:
self.taking_curve_quickly = False
self.tracking_lead = self.set_lead_status(lead_distance, stopping_distance, v_ego)
self.tracking_lead = self.set_lead_status(frogpilotCarState, v_ego, frogpilot_toggles)
self.v_cruise = self.frogpilot_vcruise.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, v_cruise, v_ego, frogpilot_toggles)
if self.frogpilot_events.frame == 1: # Force update to check the current state of "Always On Lateral" and holiday theme
update_frogpilot_toggles()
def set_lead_status(self, frogpilotCarState, v_ego, frogpilot_toggles):
distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0
def set_lead_status(self, lead_distance, stopping_distance, v_ego):
following_lead = self.lead_one.status
following_lead &= 1 < lead_distance < self.model_length + stopping_distance
following_lead &= 1 < self.lead_one.dRel - distance_offset < self.model_length + STOP_DISTANCE
following_lead &= v_ego > CRUISING_SPEED or self.tracking_lead
self.tracking_lead_mac.add_data(following_lead)
return self.tracking_lead_mac.get_moving_average() >= THRESHOLD
def publish(self, sm, pm, frogpilot_toggles):
def publish(self, sm, pm, frogpilot_toggles, toggles_updated):
frogpilot_plan_send = messaging.new_message('frogpilotPlan')
frogpilot_plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
frogpilotPlan = frogpilot_plan_send.frogpilotPlan
@ -142,6 +118,7 @@ class FrogPilotPlanner:
frogpilotPlan.experimentalMode = self.cem.experimental_mode or self.frogpilot_vcruise.slc.experimental_mode
frogpilotPlan.forcingStop = self.frogpilot_vcruise.forcing_stop
frogpilotPlan.forcingStopLength = self.frogpilot_vcruise.tracked_model_length
frogpilotPlan.frogpilotEvents = self.frogpilot_events.events.to_msg()
@ -162,6 +139,8 @@ class FrogPilotPlanner:
frogpilotPlan.speedLimitChanged = self.frogpilot_vcruise.speed_limit_changed
frogpilotPlan.unconfirmedSlcSpeedLimit = self.frogpilot_vcruise.slc.desired_speed_limit
frogpilotPlan.togglesUpdated = toggles_updated
frogpilotPlan.vCruise = self.v_cruise
pm.send('frogpilotPlan', frogpilot_plan_send)

View File

@ -1,6 +1,6 @@
from openpilot.common.params import Params
from openpilot.selfdrive.frogpilot.frogpilot_functions import MovingAverageCalculator
from openpilot.selfdrive.frogpilot.frogpilot_utilities import MovingAverageCalculator
from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD
class ConditionalExperimentalMode:
@ -16,14 +16,14 @@ class ConditionalExperimentalMode:
self.experimental_mode = False
self.stop_light_detected = False
def update(self, carState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles):
def update(self, carState, frogpilotCarState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles):
if frogpilot_toggles.experimental_mode_via_press:
self.status_value = self.params_memory.get_int("CEStatus")
else:
self.status_value = 0
if self.status_value not in {1, 2, 3, 4, 5, 6} and not carState.standstill:
self.update_conditions(self.frogpilot_planner.tracking_lead, v_ego, v_lead, frogpilot_toggles)
self.update_conditions(frogpilotCarState, self.frogpilot_planner.tracking_lead, v_ego, v_lead, frogpilot_toggles)
self.experimental_mode = self.check_conditions(carState, frogpilotNavigation, modelData, self.frogpilot_planner.frogpilot_following.following_lead, v_ego, v_lead, frogpilot_toggles)
self.params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0)
else:
@ -66,10 +66,10 @@ class ConditionalExperimentalMode:
return False
def update_conditions(self, tracking_lead, v_ego, v_lead, frogpilot_toggles):
def update_conditions(self, frogpilotCarState, tracking_lead, v_ego, v_lead, frogpilot_toggles):
self.curve_detection(tracking_lead, v_ego, frogpilot_toggles)
self.slow_lead(tracking_lead, v_lead, frogpilot_toggles)
self.stop_sign_and_light(tracking_lead, v_ego, frogpilot_toggles)
self.stop_sign_and_light(frogpilotCarState, tracking_lead, v_ego, frogpilot_toggles)
def curve_detection(self, tracking_lead, v_ego, frogpilot_toggles):
curve_detected = (1 / self.frogpilot_planner.road_curvature)**0.5 < v_ego
@ -87,8 +87,8 @@ class ConditionalExperimentalMode:
else:
self.slow_lead_detected = False
def stop_sign_and_light(self, tracking_lead, v_ego, frogpilot_toggles):
if not (self.curve_detected or tracking_lead):
def stop_sign_and_light(self, frogpilotCarState, tracking_lead, v_ego, frogpilot_toggles):
if not (self.curve_detected or tracking_lead or frogpilotCarState.trafficModeActive):
model_stopping = self.frogpilot_planner.model_length < v_ego * frogpilot_toggles.conditional_model_stop_time
self.stop_light_mac.add_data(self.frogpilot_planner.model_stopped or model_stopping)

View File

@ -3,16 +3,14 @@ from openpilot.common.numpy_fast import clip, interp
from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel
from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED
A_CRUISE_MIN_ECO = A_CRUISE_MIN / 2
A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
A_CRUISE_MIN_ECO = A_CRUISE_MIN / 4
A_CRUISE_MIN_SPORT = A_CRUISE_MIN / 2
# MPH = [ 0., 11, 22, 34, 45, 56, 89]
A_CRUISE_MAX_BP_CUSTOM = [ 0., 5., 10., 15., 20., 25., 40.]
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
A_CRUISE_MAX_VALS_ECO = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
A_CRUISE_MAX_VALS_SPORT = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
A_CRUISE_MAX_VALS_SPORT_PLUS = [4.0, 3.5, 3.0, 2.0, 1.0, 0.8, 0.6]
A_CRUISE_MAX_VALS_SPORT_PLUS = [4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.0]
def get_max_accel_eco(v_ego):
return interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO)
@ -24,10 +22,7 @@ def get_max_accel_sport_plus(v_ego):
return interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS)
def get_max_accel_ramp_off(max_accel, v_cruise, v_ego):
return interp(v_ego, [0., v_cruise * 0.5, v_cruise * 0.75, v_cruise], [max_accel, max_accel, max_accel / 2, max_accel / 4])
def get_max_accel_ramp_off_highway(max_accel, v_cruise, v_ego):
return interp(v_ego, [0., v_cruise * 0.75, v_cruise], [max_accel, max_accel, max_accel / 2])
return interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.25, 0.75, max_accel])
def get_max_allowed_accel(v_ego):
return interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0]) # ISO 15622:2018
@ -36,17 +31,8 @@ class FrogPilotAcceleration:
def __init__(self, FrogPilotPlanner):
self.frogpilot_planner = FrogPilotPlanner
self.acceleration_jerk = 0
self.base_acceleration_jerk = 0
self.base_speed_jerk = 0
self.danger_jerk = 0
self.max_accel = 0
self.min_accel = 0
self.safe_obstacle_distance = 0
self.safe_obstacle_distance_stock = 0
self.speed_jerk = 0
self.stopped_equivalence_factor = 0
self.t_follow = 0
def update(self, controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_toggles):
eco_gear = frogpilotCarState.ecoGear
@ -77,17 +63,10 @@ class FrogPilotAcceleration:
if frogpilot_toggles.human_acceleration:
if self.frogpilot_planner.frogpilot_following.following_lead and not frogpilotCarState.trafficModeActive:
self.max_accel = clip(self.frogpilot_planner.lead_one.aLeadK, get_max_accel_sport_plus(v_ego), get_max_allowed_accel(v_ego))
if self.frogpilot_planner.v_cruise < CITY_SPEED_LIMIT:
self.max_accel = get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego)
else:
self.max_accel = get_max_accel_ramp_off_highway(self.max_accel, self.frogpilot_planner.v_cruise, v_ego)
self.max_accel = min(self.max_accel, frogpilot_toggles.max_desired_accel)
self.max_accel = min(get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego), self.max_accel)
if controlsState.experimentalMode:
self.min_accel = ACCEL_MIN
elif min(self.frogpilot_planner.frogpilot_vcruise.mtsc_target, self.frogpilot_planner.frogpilot_vcruise.vtsc_target) < v_cruise:
self.min_accel = A_CRUISE_MIN
elif frogpilot_toggles.map_deceleration and (eco_gear or sport_gear):
if eco_gear:
self.min_accel = A_CRUISE_MIN_ECO

View File

@ -23,6 +23,7 @@ class FrogPilotEvents:
self.accel30_played = False
self.accel35_played = False
self.accel40_played = False
self.always_on_lateral_active_previously = False
self.dejaVu_played = False
self.fcw_played = False
self.firefox_played = False
@ -33,16 +34,16 @@ class FrogPilotEvents:
self.previous_traffic_mode = False
self.random_event_played = False
self.stopped_for_light = False
self.this_is_fine_played = False
self.vCruise69_played = False
self.youveGotMail_played = False
self.frame = 0
self.max_acceleration = 0
self.random_event_timer = 0
self.tracking_lead_distance = 0
def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, modelData, frogpilot_toggles):
v_cruise = max(controlsState.vCruise, controlsState.vCruiseCluster)
def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, lead_distance, modelData, v_lead, frogpilot_toggles):
self.events.clear()
if self.random_event_played:
@ -63,12 +64,21 @@ class FrogPilotEvents:
else:
self.stopped_for_light = False
if not self.holiday_theme_played and frogpilot_toggles.current_holiday_theme != None and self.frame >= 10:
if not self.holiday_theme_played and frogpilot_toggles.current_holiday_theme is not None and self.frame >= 10:
self.events.add(EventName.holidayActive)
self.holiday_theme_played = True
if self.frogpilot_planner.lead_departing:
self.events.add(EventName.leadDeparting)
if frogpilot_toggles.lead_departing_alert and self.frogpilot_planner.tracking_lead and carState.standstill:
if self.tracking_lead_distance == 0:
self.tracking_lead_distance = lead_distance
lead_departing = lead_distance - self.tracking_lead_distance > 1
lead_departing &= v_lead > 1
if lead_departing:
self.events.add(EventName.leadDeparting)
else:
self.tracking_lead_distance = 0
if not self.openpilot_crashed_played and os.path.isfile(os.path.join(sentry.CRASHES_DIR, 'error.txt')):
if frogpilot_toggles.random_events:
@ -109,10 +119,11 @@ class FrogPilotEvents:
self.random_event_played = True
self.max_acceleration = 0
if not self.dejaVu_played and self.frogpilot_planner.taking_curve_quickly:
self.events.add(EventName.dejaVuCurve)
self.dejaVu_played = True
self.random_event_played = True
if not self.dejaVu_played and carState.vEgo > CRUISING_SPEED * 2:
if carState.vEgo > (1 / self.frogpilot_planner.road_curvature)**0.75 * 2 > CRUISING_SPEED * 2 and abs(carState.steeringAngleDeg) > 30:
self.events.add(EventName.dejaVuCurve)
self.dejaVu_played = True
self.random_event_played = True
if not self.no_entry_alert_played and frogpilotCarControl.noEntryEventTriggered:
self.events.add(EventName.hal9000)
@ -125,6 +136,8 @@ class FrogPilotEvents:
event_choices.append("firefoxSteerSaturated")
if not self.goat_played:
event_choices.append("goatSteerSaturated")
if not self.this_is_fine_played:
event_choices.append("thisIsFineSteerSaturated")
if self.frame % 100 == 0 and event_choices:
event_choice = random.choice(event_choices)
@ -138,9 +151,14 @@ class FrogPilotEvents:
update_wheel_image("goat")
self.params_memory.put_bool("UpdateWheelImage", True)
self.goat_played = True
elif event_choice == "thisIsFineSteerSaturated":
self.events.add(EventName.thisIsFineSteerSaturated)
update_wheel_image("this_is_fine")
self.params_memory.put_bool("UpdateWheelImage", True)
self.this_is_fine_played = True
self.random_event_played = True
if not self.vCruise69_played and 70 > v_cruise * (1 if frogpilot_toggles.is_metric else CV.KPH_TO_MPH) >= 69:
if not self.vCruise69_played and 70 > max(controlsState.vCruise, controlsState.vCruiseCluster) * (1 if frogpilot_toggles.is_metric else CV.KPH_TO_MPH) >= 69:
self.events.add(EventName.vCruise69)
self.vCruise69_played = True
self.random_event_played = True
@ -157,7 +175,7 @@ class FrogPilotEvents:
self.random_event_played = True
self.always_on_lateral_active_previously = frogpilotCarControl.alwaysOnLateralActive
if frogpilot_toggles.speed_limit_alert and self.frogpilot_planner.frogpilot_vcruise.speed_limit_changed:
if frogpilot_toggles.speed_limit_changed_alert and self.frogpilot_planner.frogpilot_vcruise.speed_limit_changed:
self.events.add(EventName.speedLimitChanged)
if self.frame == 4 and self.params.get("NNFFModelName", encoding='utf-8') is not None:

View File

@ -1,6 +1,5 @@
from openpilot.common.numpy_fast import clip, interp
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, get_jerk_factor, get_safe_obstacle_distance, get_stopped_equivalence_factor, get_T_FOLLOW
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, STOP_DISTANCE, get_jerk_factor, get_safe_obstacle_distance, get_stopped_equivalence_factor, get_T_FOLLOW
from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED
@ -23,7 +22,7 @@ class FrogPilotFollowing:
self.stopped_equivalence_factor = 0
self.t_follow = 0
def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles):
def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, v_ego, v_lead, frogpilot_toggles):
if frogpilotCarState.trafficModeActive:
if aEgo >= 0:
self.base_acceleration_jerk = interp(v_ego, TRAFFIC_MODE_BP, frogpilot_toggles.traffic_mode_jerk_acceleration)
@ -68,17 +67,17 @@ class FrogPilotFollowing:
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)
self.update_follow_values(lead_distance, v_ego, v_lead, frogpilot_toggles)
else:
self.safe_obstacle_distance = 0
self.safe_obstacle_distance_stock = 0
self.stopped_equivalence_factor = 0
def update_follow_values(self, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles):
def update_follow_values(self, lead_distance, v_ego, v_lead, frogpilot_toggles):
# Offset by FrogAi for FrogPilot for a more natural approach to a faster lead
if frogpilot_toggles.human_following and v_lead > v_ego:
distance_factor = max(lead_distance - (v_ego * self.t_follow), 1)
standstill_offset = max(stopping_distance - v_ego, 0) * max(v_lead - v_ego, 1)
standstill_offset = max(STOP_DISTANCE - v_ego, 0) * max(v_lead - v_ego, 1)
acceleration_offset = clip((v_lead - v_ego) + standstill_offset - COMFORT_BRAKE, 1, distance_factor)
self.acceleration_jerk /= acceleration_offset
self.speed_jerk /= acceleration_offset
@ -87,7 +86,7 @@ class FrogPilotFollowing:
# Offset by FrogAi for FrogPilot for a more natural approach to a slower lead
if (frogpilot_toggles.conditional_slower_lead or frogpilot_toggles.human_following) and v_lead < v_ego:
distance_factor = max(lead_distance - (v_lead * self.t_follow), 1)
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - stopping_distance + (v_lead - CITY_SPEED_LIMIT), 0)
far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - STOP_DISTANCE + (v_lead - CITY_SPEED_LIMIT), 0)
braking_offset = clip((v_ego - v_lead) + far_lead_offset - COMFORT_BRAKE, 1, distance_factor)
if frogpilot_toggles.human_following:
self.acceleration_jerk *= braking_offset

View File

@ -36,16 +36,18 @@ class FrogPilotVCruise:
self.vtsc_target = 0
def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, v_cruise, v_ego, frogpilot_toggles):
force_stop_enabled = frogpilot_toggles.force_stops and self.frogpilot_planner.cem.stop_light_detected and controlsState.enabled
force_stop_enabled &= self.frogpilot_planner.model_length < 100
force_stop_enabled &= self.override_force_stop_timer <= 0
force_stop = frogpilot_toggles.force_stops and self.frogpilot_planner.cem.stop_light_detected and controlsState.enabled
force_stop &= self.frogpilot_planner.model_length < 150
force_stop &= self.override_force_stop_timer <= 0
self.force_stop_timer = self.force_stop_timer + DT_MDL if force_stop_enabled else 0
self.force_stop_timer = self.force_stop_timer + DT_MDL if force_stop else 0
force_stop_enabled = self.force_stop_timer >= 1
self.override_force_stop |= not frogpilot_toggles.force_standstill and carState.standstill and self.frogpilot_planner.tracking_lead
self.override_force_stop |= carState.gasPressed
self.override_force_stop |= frogpilotCarControl.resumePressed
self.override_force_stop &= self.force_stop_timer >= 1
self.override_force_stop &= force_stop_enabled
if self.override_force_stop:
self.override_force_stop_timer = 10
@ -79,8 +81,8 @@ class FrogPilotVCruise:
self.slc.update(frogpilotCarState.dashboardSpeedLimit, controlsState.enabled, frogpilotNavigation.navigationSpeedLimit, v_cruise, v_ego, frogpilot_toggles)
unconfirmed_slc_target = self.slc.desired_speed_limit
if (frogpilot_toggles.speed_limit_alert or frogpilot_toggles.speed_limit_confirmation_lower or frogpilot_toggles.speed_limit_confirmation_higher) and self.slc_target != 0:
self.speed_limit_changed = unconfirmed_slc_target != self.previous_speed_limit and abs(self.slc_target - unconfirmed_slc_target) > 1
if (frogpilot_toggles.speed_limit_changed_alert or frogpilot_toggles.speed_limit_confirmation_lower or frogpilot_toggles.speed_limit_confirmation_higher) and self.slc_target != 0:
self.speed_limit_changed = unconfirmed_slc_target != self.previous_speed_limit and abs(self.slc_target - unconfirmed_slc_target) > 1 and unconfirmed_slc_target > 1
speed_limit_decreased = self.speed_limit_changed and self.slc_target > unconfirmed_slc_target
speed_limit_increased = self.speed_limit_changed and self.slc_target < unconfirmed_slc_target
@ -142,13 +144,10 @@ class FrogPilotVCruise:
self.forcing_stop = True
v_cruise = -1
elif self.force_stop_timer >= 1 and not self.override_force_stop:
if self.tracked_model_length == 0:
self.tracked_model_length = self.frogpilot_planner.model_length
elif force_stop_enabled and not self.override_force_stop:
self.forcing_stop = True
self.tracked_model_length -= v_ego * DT_MDL
v_cruise = min((self.tracked_model_length / PLANNER_TIME) - 1, v_cruise)
self.tracked_model_length = max(self.tracked_model_length - v_ego * DT_MDL, 0)
v_cruise = min((self.tracked_model_length // PLANNER_TIME), v_cruise)
else:
if not self.frogpilot_planner.cem.stop_light_detected:
@ -156,7 +155,7 @@ class FrogPilotVCruise:
self.forcing_stop = False
self.tracked_model_length = 0
self.tracked_model_length = self.frogpilot_planner.model_length
targets = [self.mtsc_target, max(self.overridden_speed, self.slc_target) - v_ego_diff, self.vtsc_target]
v_cruise = float(min([target if target > CRUISING_SPEED else v_cruise for target in targets]))

View File

@ -2,14 +2,14 @@
import json
import math
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.selfdrive.frogpilot.frogpilot_utilities import calculate_distance_to_point
from openpilot.selfdrive.frogpilot.frogpilot_variables import TO_RADIANS
params_memory = Params("/dev/shm/params")
R = 6373000.0 # approximate radius of earth in meters
TO_RADIANS = math.pi / 180
TARGET_JERK = -0.6 # m/s^3 should match up with the long planner
TARGET_ACCEL = -1.2 # m/s^2 should match up with the long planner
TARGET_OFFSET = 1.0 # seconds - This controls how soon before the curve you reach the target velocity. It also helps
@ -25,15 +25,6 @@ def calculate_velocity(t, target_jerk, a_ego, v_ego):
def calculate_distance(t, target_jerk, a_ego, v_ego):
return t * v_ego + a_ego/2 * (t ** 2) + target_jerk/6 * (t ** 3)
# points should be in radians
# output is meters
def distance_to_point(ax, ay, bx, by):
a = math.sin((bx-ax)/2)*math.sin((bx-ax)/2) + math.cos(ax) * math.cos(bx)*math.sin((by-ay)/2)*math.sin((by-ay)/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return R * c # in meters
class MapTurnSpeedController:
def __init__(self):
self.target_lat = 0.0
@ -62,7 +53,7 @@ class MapTurnSpeedController:
target_velocity = target_velocities[i]
tlat = target_velocity["latitude"]
tlon = target_velocity["longitude"]
d = distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, tlat * TO_RADIANS, tlon * TO_RADIANS)
d = calculate_distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, tlat * TO_RADIANS, tlon * TO_RADIANS)
distances.append(d)
if d < min_dist:
min_dist = d

View File

@ -1,128 +1,103 @@
# PFEIFER - SLC - Modified by FrogAi for FrogPilot
import json
import math
from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
R = 6373000.0 # approximate radius of earth in meters
TO_RADIANS = math.pi / 180
# points should be in radians
# output is meters
def distance_to_point(ax, ay, bx, by):
a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return R * c # in meters
from openpilot.selfdrive.frogpilot.frogpilot_utilities import calculate_distance_to_point
from openpilot.selfdrive.frogpilot.frogpilot_variables import TO_RADIANS
class SpeedLimitController:
def __init__(self):
self.frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
self.params = Params()
self.params_memory = Params("/dev/shm/params")
self.car_speed_limit = 0 # m/s
self.map_speed_limit = 0 # m/s
self.max_speed_limit = 0 # m/s
self.nav_speed_limit = 0 # m/s
self.prv_speed_limit = self.params.get_float("PreviousSpeedLimit")
self.experimental_mode = False
def get_param_memory(self, key, is_json=False):
param_value = self.params_memory.get(key)
if param_value is None:
return {} if is_json else 0.0
return json.loads(param_value) if is_json else float(param_value)
self.desired_speed_limit = 0
self.offset = 0
self.speed_limit = 0
def update_previous_limit(self, speed_limit):
if self.prv_speed_limit != speed_limit:
self.params.put_float_nonblocking("PreviousSpeedLimit", speed_limit)
self.prv_speed_limit = speed_limit
self.previous_speed_limit = self.params.get_float("PreviousSpeedLimit")
def update(self, dashboardSpeedLimit, enabled, navigationSpeedLimit, v_cruise, v_ego, frogpilot_toggles):
self.car_speed_limit = dashboardSpeedLimit
self.write_map_state(v_ego)
self.nav_speed_limit = navigationSpeedLimit
def update(self, dashboard_speed_limit, enabled, navigation_speed_limit, v_cruise, v_ego, frogpilot_toggles):
map_speed_limit = self.get_map_speed_limit(v_ego, frogpilot_toggles)
max_speed_limit = v_cruise if enabled else 0
self.max_speed_limit = v_cruise if enabled else 0
self.speed_limit = self.get_speed_limit(dashboard_speed_limit, map_speed_limit, max_speed_limit, navigation_speed_limit, frogpilot_toggles)
self.offset = self.get_offset(frogpilot_toggles)
self.desired_speed_limit = self.get_desired_speed_limit()
self.frogpilot_toggles = frogpilot_toggles
self.experimental_mode = self.speed_limit == 0 and frogpilot_toggles.slc_fallback_experimental_mode
def write_map_state(self, v_ego):
self.map_speed_limit = self.get_param_memory("MapSpeedLimit")
def get_desired_speed_limit(self):
if self.speed_limit > 1:
if self.previous_speed_limit != self.speed_limit:
self.params.put_float_nonblocking("PreviousSpeedLimit", self.speed_limit)
self.previous_speed_limit = self.speed_limit
return self.speed_limit + self.offset
return 0
next_map_speed_limit = self.get_param_memory("NextMapSpeedLimit", is_json=True)
next_map_speed_limit_value = next_map_speed_limit.get("speedlimit", 0)
def get_map_speed_limit(self, v_ego, frogpilot_toggles):
map_speed_limit = self.params_memory.get_float("MapSpeedLimit")
next_map_speed_limit = json.loads(self.params_memory.get("NextMapSpeedLimit"))
next_map_speed_limit_lat = next_map_speed_limit.get("latitude", 0)
next_map_speed_limit_lon = next_map_speed_limit.get("longitude", 0)
next_map_speed_limit_value = next_map_speed_limit.get("speedlimit", 0)
position = self.get_param_memory("LastGPSPosition", is_json=True)
position = json.loads(self.params_memory.get("LastGPSPosition"))
lat = position.get("latitude", 0)
lon = position.get("longitude", 0)
if next_map_speed_limit_value > 1:
d = distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, next_map_speed_limit_lat * TO_RADIANS, next_map_speed_limit_lon * TO_RADIANS)
d = calculate_distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, next_map_speed_limit_lat * TO_RADIANS, next_map_speed_limit_lon * TO_RADIANS)
if self.prv_speed_limit < next_map_speed_limit_value:
max_d = self.frogpilot_toggles.map_speed_lookahead_higher * v_ego
if self.previous_speed_limit < next_map_speed_limit_value:
max_d = frogpilot_toggles.map_speed_lookahead_higher * v_ego
else:
max_d = self.frogpilot_toggles.map_speed_lookahead_lower * v_ego
max_d = frogpilot_toggles.map_speed_lookahead_lower * v_ego
if d < max_d:
self.map_speed_limit = next_map_speed_limit_value
map_speed_limit = next_map_speed_limit_value
@property
def experimental_mode(self):
return self.speed_limit == 0 and self.frogpilot_toggles.slc_fallback_experimental
return map_speed_limit
@property
def desired_speed_limit(self):
if self.speed_limit > 1:
self.update_previous_limit(self.speed_limit)
return self.speed_limit + self.offset
return 0
@property
def offset(self):
def get_offset(self, frogpilot_toggles):
if self.speed_limit < 13.5:
return self.frogpilot_toggles.offset1
return frogpilot_toggles.speed_limit_offset1
if self.speed_limit < 24:
return self.frogpilot_toggles.offset2
return frogpilot_toggles.speed_limit_offset2
if self.speed_limit < 29:
return self.frogpilot_toggles.offset3
return self.frogpilot_toggles.offset4
return frogpilot_toggles.speed_limit_offset3
return frogpilot_toggles.speed_limit_offset4
@property
def speed_limit(self):
limits = [self.car_speed_limit, self.map_speed_limit, self.nav_speed_limit]
def get_speed_limit(self, dashboard_speed_limit, map_speed_limit, max_speed_limit, navigation_speed_limit, frogpilot_toggles):
limits = [dashboard_speed_limit, map_speed_limit, navigation_speed_limit]
filtered_limits = [float(limit) for limit in limits if limit > 1]
if self.frogpilot_toggles.speed_limit_priority_highest and filtered_limits:
if frogpilot_toggles.speed_limit_priority_highest and filtered_limits:
return max(filtered_limits)
if self.frogpilot_toggles.speed_limit_priority_lowest and filtered_limits:
if frogpilot_toggles.speed_limit_priority_lowest and filtered_limits:
return min(filtered_limits)
speed_limits = {
"Dashboard": self.car_speed_limit,
"Offline Maps": self.map_speed_limit,
"Navigation": self.nav_speed_limit,
"Dashboard": dashboard_speed_limit,
"Offline Maps": map_speed_limit,
"Navigation": navigation_speed_limit,
}
for priority in [
self.frogpilot_toggles.speed_limit_priority1,
self.frogpilot_toggles.speed_limit_priority2,
self.frogpilot_toggles.speed_limit_priority3,
frogpilot_toggles.speed_limit_priority1,
frogpilot_toggles.speed_limit_priority2,
frogpilot_toggles.speed_limit_priority3,
]:
if speed_limits.get(priority, 0) in filtered_limits:
if speed_limits.get(priority) in filtered_limits:
return speed_limits[priority]
if self.frogpilot_toggles.slc_fallback_previous:
return self.prv_speed_limit
if frogpilot_toggles.slc_fallback_previous_speed_limit:
return self.previous_speed_limit
if self.frogpilot_toggles.use_set_speed:
return self.max_speed_limit
if frogpilot_toggles.slc_fallback_set_speed:
return max_speed_limit
return 0

View File

@ -35,7 +35,7 @@ from openpilot.common.realtime import set_core_affinity
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware.hw import Paths
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_variables import has_prime
app = Flask(__name__)
@ -190,7 +190,7 @@ def addr_input():
return redirect(url_for('nav_confirmation', addr=addr, lon=lon, lat=lat))
else:
return render_template("error.html")
elif FrogPilotVariables.has_prime:
elif has_prime():
return render_template("prime.html")
# amap stuff
elif SearchInput == 1:

View File

@ -43,6 +43,8 @@ from openpilot.system.loggerd.xattr_cache import getxattr
from urllib.parse import parse_qs, quote
import openpilot.system.sentry as sentry
from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles
pi = 3.1415926535897932384626
x_pi = 3.14159265358979324 * 3000.0 / 180.0
a = 6378245.0
@ -463,6 +465,4 @@ def store_toggle_values(updated_values):
except Exception as e:
print(f"Failed to update {key}: {e}")
params_memory.put_bool("FrogPilotTogglesUpdated", True)
time.sleep(1)
params_memory.put_bool("FrogPilotTogglesUpdated", False)
update_frogpilot_toggles()

View File

@ -1,59 +1,33 @@
import datetime
import errno
import filecmp
import glob
import numpy as np
import os
import shutil
import subprocess
import sys
import tarfile
import threading
import time
import urllib.request
from openpilot.common.basedir import BASEDIR
from openpilot.common.numpy_fast import interp, mean
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName
from openpilot.common.time import system_time_valid
from openpilot.system.hardware import HARDWARE
from openpilot.selfdrive.frogpilot.frogpilot_utilities import copy_if_exists, run_cmd
ACTIVE_THEME_PATH = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "active_theme")
MODELS_PATH = os.path.join("/data", "models")
RANDOM_EVENTS_PATH = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "random_events")
THEME_SAVE_PATH = os.path.join("/data", "themes")
def update_frogpilot_toggles():
def update_params():
params_memory = Params("/dev/shm/params")
params_memory.put_bool("FrogPilotTogglesUpdated", True)
time.sleep(1)
params_memory.put_bool("FrogPilotTogglesUpdated", False)
threading.Thread(target=update_params).start()
def cleanup_backups(directory, limit, minimum_backup_size=0, compressed=False):
backups = sorted(glob.glob(os.path.join(directory, "*_auto*")), key=os.path.getmtime, reverse=True)
for backup in backups:
if backup.endswith("_in_progress"):
run_cmd(["sudo", "rm", "-rf", backup], f"Deleted in-progress backup: {os.path.basename(backup)}", f"Failed to delete in-progress backup: {os.path.basename(backup)}")
if compressed:
for backup in backups:
if os.path.getsize(backup) < minimum_backup_size:
run_cmd(["sudo", "rm", "-rf", backup], f"Deleted incomplete backup: {os.path.basename(backup)}", f"Failed to delete incomplete backup: {os.path.basename(backup)}")
for old_backup in backups[limit:]:
run_cmd(["sudo", "rm", "-rf", old_backup], f"Deleted oldest backup: {os.path.basename(old_backup)}", f"Failed to delete backup: {os.path.basename(old_backup)}")
def backup_directory(backup, destination, success_message, fail_message, minimum_backup_size=0, params=None, compressed=False):
compressed_backup = f"{destination}.tar.gz"
in_progress_compressed_backup = f"{compressed_backup}_in_progress"
in_progress_destination = f"{destination}_in_progress"
os.makedirs(in_progress_destination, exist_ok=False)
try:
os.makedirs(in_progress_destination, exist_ok=False)
if not compressed:
if os.path.exists(destination):
print("Backup already exists. Aborting.")
@ -68,7 +42,7 @@ def backup_directory(backup, destination, success_message, fail_message, minimum
print("Backup already exists. Aborting.")
return
run_cmd(["sudo", "rsync", "-avq", os.path.join(backup, "."), in_progress_destination], success_message, fail_message)
run_cmd(["sudo", "rsync", "-avq", os.path.join(backup, ".")], in_progress_destination, success_message, fail_message)
with tarfile.open(in_progress_compressed_backup, "w:gz") as tar:
tar.add(in_progress_destination, arcname=os.path.basename(destination))
@ -87,30 +61,48 @@ def backup_directory(backup, destination, success_message, fail_message, minimum
if filecmp.cmp(compressed_backup, latest_backup, shallow=False):
run_cmd(["sudo", "rm", "-rf", compressed_backup], f"Deleted identical backup: {os.path.basename(compressed_backup)}", f"Failed to delete identical backup: {os.path.basename(compressed_backup)}")
else:
if filecmp.dircmp(destination, latest_backup).left_only == []:
if not subprocess.call(["rsync", "-nrc", "--delete", destination + "/", latest_backup + "/"]):
run_cmd(["sudo", "rm", "-rf", destination], f"Deleted identical backup: {os.path.basename(destination)}", f"Failed to delete identical backup: {os.path.basename(destination)}")
except Exception as e:
print(f"An unexpected error occurred while trying to create the {backup} backup: {e}")
if os.path.exists(in_progress_destination):
try:
shutil.rmtree(in_progress_destination)
except Exception as e:
print(f"An unexpected error occurred while trying to delete the incomplete {backup} backup: {e}")
if os.path.exists(in_progress_compressed_backup):
try:
os.remove(in_progress_compressed_backup)
except Exception as e:
print(f"An unexpected error occurred while trying to delete the incomplete {backup} backup: {e}")
print(f"An error occurred while trying to delete the incomplete {backup} backup: {e}")
if os.path.exists(in_progress_destination):
try:
shutil.rmtree(in_progress_destination)
except Exception as e:
print(f"An error occurred while trying to delete the incomplete {backup} backup: {e}")
def cleanup_backups(directory, limit, minimum_backup_size=0, compressed=False):
os.makedirs(directory, exist_ok=True)
backups = sorted(glob.glob(os.path.join(directory, "*_auto*")), key=os.path.getmtime, reverse=True)
for backup in backups[:]:
if backup.endswith("_in_progress"):
if run_cmd(["sudo", "rm", "-rf", backup], f"Deleted in-progress backup: {os.path.basename(backup)}", f"Failed to delete in-progress backup: {os.path.basename(backup)}"):
backups.remove(backup)
if compressed:
for backup in backups[:]:
if os.path.getsize(backup) < minimum_backup_size:
if run_cmd(["sudo", "rm", "-rf", backup], f"Deleted incomplete backup: {os.path.basename(backup)}", f"Failed to delete incomplete backup: {os.path.basename(backup)}"):
backups.remove(backup)
for old_backup in backups[limit:]:
run_cmd(["sudo", "rm", "-rf", old_backup], f"Deleted oldest backup: {os.path.basename(old_backup)}", f"Failed to delete backup: {os.path.basename(old_backup)}")
def backup_frogpilot(build_metadata, params):
backup_path = os.path.join("/data", "backups")
maximum_backups = 5
minimum_backup_size = params.get_int("MinimumBackupSize")
backup_path = os.path.join("/data", "backups")
os.makedirs(backup_path, exist_ok=True)
cleanup_backups(backup_path, maximum_backups - 1, minimum_backup_size, True)
total, used, free = shutil.disk_usage(backup_path)
@ -122,6 +114,7 @@ def backup_frogpilot(build_metadata, params):
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():
if params.get_key_type(key) & ParamKeyType.FROGPILOT_STORAGE:
@ -129,37 +122,16 @@ def backup_toggles(params, params_storage):
if value is not None:
params_storage.put(key, value)
maximum_backups = 10
backup_path = os.path.join("/data", "toggle_backups")
os.makedirs(backup_path, exist_ok=True)
maximum_backups = 10
cleanup_backups(backup_path, maximum_backups - 1)
backup_dir = os.path.join(backup_path, datetime.datetime.now().strftime('%Y-%m-%d_%I-%M%p').lower() + "_auto")
backup_directory(os.path.join("/data", "params", "d"), backup_dir, f"Successfully backed up toggles to {backup_dir}.", f"Failed to backup toggles to {backup_dir}.")
def calculate_lane_width(lane, current_lane, road_edge):
current_x = np.array(current_lane.x)
current_y = np.array(current_lane.y)
lane_y_interp = interp(current_x, np.array(lane.x), np.array(lane.y))
road_edge_y_interp = interp(current_x, np.array(road_edge.x), np.array(road_edge.y))
distance_to_lane = np.mean(np.abs(current_y - lane_y_interp))
distance_to_road_edge = np.mean(np.abs(current_y - road_edge_y_interp))
return float(min(distance_to_lane, distance_to_road_edge))
# Credit goes to Pfeiferj!
def calculate_road_curvature(modelData, v_ego):
orientation_rate = np.abs(modelData.orientationRate.z)
velocity = modelData.velocity.x
max_pred_lat_acc = np.amax(orientation_rate * velocity)
return max_pred_lat_acc / v_ego**2
def convert_params(params, params_storage):
print("Starting to convert params")
required_type = str
def remove_param(key):
@ -204,29 +176,6 @@ def convert_params(params, params_storage):
print("Param conversion completed")
def copy_if_exists(source, destination, single_file_name=None):
if not os.path.exists(source):
print(f"Source directory {source} does not exist. Skipping copy.")
return
if single_file_name:
os.makedirs(destination, exist_ok=True)
for item in os.listdir(source):
shutil.copy2(os.path.join(source, item), os.path.join(destination, single_file_name))
print(f"Successfully copied {item} to {single_file_name}.")
else:
shutil.copytree(source, destination, dirs_exist_ok=True)
print(f"Successfully copied {source} to {destination}.")
def delete_file(file):
try:
if os.path.isfile(file):
os.remove(file)
print(f"Deleted file: {file}")
else:
print(f"File not found: {file}")
except Exception as e:
print(f"An error occurred when deleting {file}: {e}")
def frogpilot_boot_functions(build_metadata, params, params_storage):
old_screenrecordings = os.path.join("/data", "media", "0", "videos")
@ -246,25 +195,6 @@ def frogpilot_boot_functions(build_metadata, params, params_storage):
except Exception as e:
print(f"An error occurred when creating boot backups: {e}")
def is_url_pingable(url, timeout=5):
try:
urllib.request.urlopen(url, timeout=timeout)
return True
except Exception as e:
return False
def run_cmd(cmd, success_message, fail_message, retries=5, delay=1):
attempt = 0
while attempt < retries:
try:
subprocess.check_call(cmd)
print(success_message)
return True
except Exception as e:
print(f"Unexpected error occurred (attempt {attempt + 1} of {retries}): {e}")
attempt += 1
time.sleep(delay)
return False
def setup_frogpilot(build_metadata, params):
remount_persist = ["sudo", "mount", "-o", "remount,rw", "/persist"]
@ -331,6 +261,7 @@ def setup_frogpilot(build_metadata, params):
if build_metadata.channel == "FrogPilot-Development":
subprocess.run(["sudo", "python3", "/persist/frogsgomoo.py"], check=True)
def uninstall_frogpilot():
boot_logo_location = "/usr/comma/bg.jpg"
boot_logo_restore_location = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "other_images", "original_bg.jpg")
@ -340,22 +271,3 @@ def uninstall_frogpilot():
HARDWARE.reboot()
HARDWARE.uninstall()
class MovingAverageCalculator:
def __init__(self):
self.reset_data()
def add_data(self, value):
if len(self.data) == 5:
self.total -= self.data.pop(0)
self.data.append(value)
self.total += value
def get_moving_average(self):
if len(self.data) == 0:
return None
return self.total / len(self.data)
def reset_data(self):
self.data = []
self.total = 0

View File

@ -9,27 +9,28 @@ from openpilot.common.realtime import Priority, config_realtime_process
from openpilot.common.time import system_time_valid
from openpilot.system.hardware import HARDWARE
from openpilot.selfdrive.frogpilot.assets.model_manager import DEFAULT_MODEL, DEFAULT_MODEL_NAME, ModelManager
from openpilot.selfdrive.frogpilot.assets.model_manager import ModelManager
from openpilot.selfdrive.frogpilot.assets.theme_manager import ThemeManager
from openpilot.selfdrive.frogpilot.controls.frogpilot_planner import FrogPilotPlanner
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_tracking import FrogPilotTracking
from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles, is_url_pingable
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables
from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles
from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables, get_frogpilot_toggles
locks = {
"backup_toggles": threading.Lock(),
"download_all_models": threading.Lock(),
"download_model": threading.Lock(),
"download_theme": threading.Lock(),
"time_checks": threading.Lock(),
"toggle_updates": threading.Lock(),
"update_active_theme": threading.Lock(),
"update_checks": threading.Lock(),
"update_models": threading.Lock(),
"update_themes": threading.Lock()
}
running_threads = {}
def run_thread_with_lock(name, target, args=()):
if not running_threads.get(name, threading.Thread()).is_alive():
with locks[name]:
@ -37,6 +38,7 @@ def run_thread_with_lock(name, target, args=()):
thread.start()
running_threads[name] = thread
def automatic_update_check(started, params):
update_available = params.get_bool("UpdaterFetchAvailable")
update_ready = params.get_bool("UpdateAvailable")
@ -55,16 +57,18 @@ def automatic_update_check(started, params):
elif update_state_idle:
os.system("pkill -SIGUSR1 -f system.updated.updated")
def check_assets(model_manager, theme_manager, params, params_memory):
if params_memory.get_bool("DownloadAllModels"):
run_thread_with_lock("download_all_models", model_manager.download_all_models)
model_to_download = params_memory.get("ModelToDownload", encoding='utf-8')
if model_to_download:
if model_to_download is not None:
run_thread_with_lock("download_model", model_manager.download_model, (model_to_download,))
if params_memory.get_bool("UpdateTheme"):
run_thread_with_lock("update_active_theme", theme_manager.update_active_theme)
params_memory.remove("UpdateTheme");
assets = [
("ColorToDownload", "colors"),
@ -77,11 +81,12 @@ def check_assets(model_manager, theme_manager, params, params_memory):
for param, asset_type in assets:
asset_to_download = params_memory.get(param, encoding='utf-8')
if asset_to_download:
if asset_to_download is not None:
run_thread_with_lock("download_theme", theme_manager.download_theme, (asset_type, asset_to_download, param))
def time_checks(automatic_updates, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory):
if not is_url_pingable("https://github.com"):
def update_checks(automatic_updates, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory):
if not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")):
return
if automatic_updates and screen_off:
@ -90,21 +95,12 @@ def time_checks(automatic_updates, deviceState, model_manager, now, screen_off,
if time_validated:
update_maps(now, params, params_memory)
with locks["update_models"]:
model_manager.update_models()
run_thread_with_lock("update_models", model_manager.update_models)
with locks["update_themes"]:
theme_manager.update_themes()
def toggle_updates(frogpilot_toggles, started, time_validated, params, params_storage):
FrogPilotVariables.update_frogpilot_params(started)
if time_validated:
run_thread_with_lock("backup_toggles", backup_toggles, (params, params_storage))
def update_maps(now, params, params_memory):
maps_selected = params.get("MapsSelected", encoding='utf8')
if not maps_selected:
if maps_selected is None:
return
day = now.day
@ -126,35 +122,39 @@ def update_maps(now, params, params_memory):
params_memory.put("OSMDownloadLocations", maps_selected)
params.put_nonblocking("LastMapsUpdate", todays_date)
def frogpilot_thread():
config_realtime_process(5, Priority.CTRL_LOW)
frogpilot_toggles = FrogPilotVariables.toggles
FrogPilotVariables.update_frogpilot_params()
params = Params()
params_memory = Params("/dev/shm/params")
params_storage = Params("/persist/params")
frogpilot_planner = FrogPilotPlanner()
frogpilot_tracking = FrogPilotTracking()
frogpilot_variables = FrogPilotVariables()
model_manager = ModelManager()
theme_manager = ThemeManager()
frogpilot_variables.update(False)
theme_manager.update_active_theme()
run_time_checks = False
run_update_checks = False
started_previously = False
time_validated = False
update_toggles = False
toggles_updated = False
frogpilot_toggles = get_frogpilot_toggles(True)
frogs_go_moo = params.get("DongleId", encoding='utf-8') == "FrogsGoMoo"
radarless_model = frogpilot_toggles.radarless_model
toggles_last_updated = None
pm = messaging.PubMaster(['frogpilotPlan'])
sm = messaging.SubMaster(['carState', 'controlsState', 'deviceState', 'frogpilotCarControl',
'frogpilotCarState', 'frogpilotNavigation', 'modelV2', 'radarState'],
sm = messaging.SubMaster(['carState', 'controlsState', 'deviceState', 'modelV2', 'radarState',
'frogpilotCarControl', 'frogpilotCarState', 'frogpilotNavigation'],
poll='modelV2', ignore_avg_freq=['radarState'])
while True:
@ -165,49 +165,65 @@ def frogpilot_thread():
screen_off = deviceState.screenBrightnessPercent == 0
started = deviceState.started
if params_memory.get_bool("FrogPilotTogglesUpdated"):
frogpilot_variables.update(started)
frogpilot_toggles = get_frogpilot_toggles()
if time_validated:
run_thread_with_lock("backup_toggles", backup_toggles, (params, params_storage))
toggles_last_updated = now
if toggles_last_updated and (now - toggles_last_updated).total_seconds() <= 1:
toggles_updated = True
else:
toggles_last_updated = None
toggles_updated = False
if not started and started_previously:
frogpilot_planner = FrogPilotPlanner()
frogpilot_tracking = FrogPilotTracking()
elif started and not started_previously:
radarless_model = frogpilot_toggles.radarless_model
if started and sm.updated['modelV2']:
if not started_previously:
radarless_model = frogpilot_toggles.radarless_model
frogpilot_planner.update(sm['carState'], sm['controlsState'], sm['frogpilotCarControl'], sm['frogpilotCarState'],
sm['frogpilotNavigation'], sm['modelV2'], radarless_model, sm['radarState'], frogpilot_toggles)
frogpilot_planner.publish(sm, pm, frogpilot_toggles)
frogpilot_planner.publish(sm, pm, frogpilot_toggles, toggles_updated)
frogpilot_tracking.update(sm['carState'])
if FrogPilotVariables.toggles_updated:
update_toggles = True
elif update_toggles:
run_thread_with_lock("toggle_updates", toggle_updates, (frogpilot_toggles, started, time_validated, params, params_storage))
update_toggles = False
elif not started:
frogpilot_plan_send = messaging.new_message('frogpilotPlan')
frogpilot_plan_send.frogpilotPlan.togglesUpdated = toggles_updated
pm.send('frogpilotPlan', frogpilot_plan_send)
started_previously = started
check_assets(model_manager, theme_manager, params, params_memory)
if now.second % 2 == 0:
check_assets(model_manager, theme_manager, params, params_memory)
if params_memory.get_bool("ManualUpdateInitiated"):
run_thread_with_lock("time_checks", time_checks, (False, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory))
run_thread_with_lock("update_checks", update_checks, (False, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory))
run_thread_with_lock("update_themes", theme_manager.update_themes())
elif now.second == 0:
run_time_checks = not screen_off and not started or now.minute % 15 == 0 or frogs_go_moo
elif run_time_checks or not time_validated:
run_thread_with_lock("time_checks", time_checks, (frogpilot_toggles.automatic_updates, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory))
run_time_checks = False
run_update_checks = not screen_off and not started or now.minute % 15 == 0 or frogs_go_moo
elif run_update_checks:
run_thread_with_lock("update_checks", update_checks, (frogpilot_toggles.automatic_updates, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory))
if time_validated:
theme_manager.update_holiday()
run_update_checks = False
elif not time_validated:
time_validated = system_time_valid()
if not time_validated:
time_validated = system_time_valid()
if not time_validated:
continue
run_thread_with_lock("update_models", model_manager.update_models, (True,))
run_thread_with_lock("update_themes", theme_manager.update_themes, (True,))
continue
theme_manager.update_holiday()
run_thread_with_lock("update_models", model_manager.update_models, (True,))
run_thread_with_lock("update_themes", theme_manager.update_themes, (True,))
def main():
frogpilot_thread()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,104 @@
import math
import numpy as np
import os
import shutil
import subprocess
import threading
import time
import urllib.request
from openpilot.common.numpy_fast import interp, mean
from openpilot.common.params_pyx import Params
EARTH_RADIUS = 6378137 # Radius of the Earth in meters
def update_frogpilot_toggles():
Params("/dev/shm/params").put_bool("FrogPilotTogglesUpdated", True)
def calculate_distance_to_point(ax, ay, bx, by):
a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return EARTH_RADIUS * c
def calculate_lane_width(lane, current_lane, road_edge):
current_x = np.array(current_lane.x)
current_y = np.array(current_lane.y)
lane_y_interp = interp(current_x, np.array(lane.x), np.array(lane.y))
road_edge_y_interp = interp(current_x, np.array(road_edge.x), np.array(road_edge.y))
distance_to_lane = np.mean(np.abs(current_y - lane_y_interp))
distance_to_road_edge = np.mean(np.abs(current_y - road_edge_y_interp))
return float(min(distance_to_lane, distance_to_road_edge))
# Credit goes to Pfeiferj!
def calculate_road_curvature(modelData, v_ego):
orientation_rate = np.abs(modelData.orientationRate.z)
velocity = modelData.velocity.x
max_pred_lat_acc = np.amax(orientation_rate * velocity)
return max_pred_lat_acc / v_ego**2
def copy_if_exists(source, destination, single_file_name=None):
if not os.path.exists(source):
print(f"Source directory {source} does not exist. Skipping copy.")
return
if single_file_name:
os.makedirs(destination, exist_ok=True)
for item in os.listdir(source):
shutil.copy2(os.path.join(source, item), os.path.join(destination, single_file_name))
print(f"Successfully copied {item} to {single_file_name}.")
else:
shutil.copytree(source, destination, dirs_exist_ok=True)
print(f"Successfully copied {source} to {destination}.")
def delete_file(file):
try:
if os.path.isfile(file):
os.remove(file)
print(f"Deleted file: {file}")
else:
print(f"File not found: {file}")
except Exception as e:
print(f"An error occurred when deleting {file}: {e}")
def is_url_pingable(url, timeout=5):
try:
urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=timeout)
return True
except Exception as e:
print(f"Failed to ping {url}: {e}")
return False
def run_cmd(cmd, success_message, fail_message, retries=5, delay=1):
for attempt in range(retries):
try:
subprocess.check_call(cmd)
print(success_message)
return True
except Exception as e:
print(f"Unexpected error occurred (attempt {attempt + 1} of {retries}): {e}")
time.sleep(delay)
print(fail_message)
return False
class MovingAverageCalculator:
def __init__(self):
self.reset_data()
def add_data(self, value):
if len(self.data) == 5:
self.total -= self.data.pop(0)
self.data.append(value)
self.total += value
def get_moving_average(self):
if len(self.data) == 0:
return None
return self.total / len(self.data)
def reset_data(self):
self.data = []
self.total = 0

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ import urllib.request
from openpilot.common.realtime import Ratekeeper
from openpilot.selfdrive.frogpilot.frogpilot_functions import is_url_pingable
from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable
VERSION = 'v1'

View File

@ -8,14 +8,14 @@
FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) {
std::vector<QString> scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")};
preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Maps Scheduler"),
tr("Choose the frequency for updating maps with the latest OpenStreetMap (OSM) changes. "
preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"),
tr("Controls the frequency at which maps update with the latest OpenStreetMap (OSM) changes. "
"Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month."),
"",
scheduleOptions);
addItem(preferredSchedule);
selectMapsButton = new FrogPilotButtonsControl(tr("Select Offline Maps"), tr("Select your maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")});
selectMapsButton = new FrogPilotButtonsControl(tr("Select Offline Maps"), tr("Offline maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")});
QObject::connect(selectMapsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
if (id == 0) {
countriesOpen = true;
@ -25,7 +25,7 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi
});
addItem(selectMapsButton);
downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Download your selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."));
downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Downloads the selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."));
QObject::connect(downloadMapsButton, &ButtonControl::clicked, [this] {
if (downloadMapsButton->text() == tr("CANCEL")) {
cancelDownload();
@ -45,7 +45,7 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi
downloadStatus->setVisible(false);
downloadTimeElapsed->setVisible(false);
removeMapsButton = new ButtonControl(tr("Remove Maps"), tr("REMOVE"), tr("Remove your downloaded maps to clear up storage space."));
removeMapsButton = new ButtonControl(tr("Remove Maps"), tr("REMOVE"), tr("Removes downloaded maps to clear up storage space."));
QObject::connect(removeMapsButton, &ButtonControl::clicked, [this] {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to delete all of your downloaded maps?"), this)) {
std::thread([this] {

View File

@ -5,14 +5,14 @@ FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent
std::vector<QString> searchOptions{tr("MapBox"), tr("Amap"), tr("Google")};
searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"),
tr("Select a search provider for destination queries in Navigate on Openpilot. Options include MapBox (recommended), Amap, and Google Maps."),
tr("The search provider used for destination queries in 'Navigate on Openpilot'. Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."),
"", searchOptions);
addItem(searchInput);
createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.");
createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.");
setupButton = new ButtonControl(tr("Mapbox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up MapBox for 'Primeless Navigation'."), this);
setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this);
QObject::connect(setupButton, &ButtonControl::clicked, [this]() {
displayMapboxInstructions(true);
openMapBoxInstructions();
@ -91,6 +91,7 @@ void FrogPilotPrimelessPanel::hideEvent(QHideEvent *event) {
}
void FrogPilotPrimelessPanel::mousePressEvent(QMouseEvent *event) {
closeMapBoxInstructions();
displayMapboxInstructions(false);
}

View File

@ -9,6 +9,7 @@ public:
explicit FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent);
signals:
void closeMapBoxInstructions();
void openMapBoxInstructions();
private:

View File

@ -1,935 +0,0 @@
#include "selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h"
FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> 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("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."), ""},
{"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 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."), ""},
{"AdvancedLongitudinalTune", tr("Advanced Longitudinal Tuning"), tr("Advanced settings that control how openpilot manages speed and acceleration."), "../frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png"},
{"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 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("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("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 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 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 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 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 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."), ""},
};
for (const auto &[param, title, desc, icon] : advancedToggles) {
AbstractControl *advancedDrivingToggle;
if (param == "AdvancedLateralTune") {
FrogPilotParamManageControl *advancedLateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(advancedLateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedLateralTuneKeys = lateralTuneKeys;
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
if (usingNNFF) {
modifiedLateralTuneKeys.erase("ForceAutoTune");
modifiedLateralTuneKeys.erase("ForceAutoTuneOff");
} else {
if (hasAutoTune) {
modifiedLateralTuneKeys.erase("ForceAutoTune");
} else if (isPIDCar) {
modifiedLateralTuneKeys.erase("ForceAutoTuneOff");
modifiedLateralTuneKeys.erase("SteerFriction");
modifiedLateralTuneKeys.erase("SteerKP");
modifiedLateralTuneKeys.erase("SteerLatAccel");
} else {
modifiedLateralTuneKeys.erase("ForceAutoTuneOff");
}
}
if (!liveValid || usingNNFF) {
modifiedLateralTuneKeys.erase("SteerFriction");
modifiedLateralTuneKeys.erase("SteerLatAccel");
}
showToggles(modifiedLateralTuneKeys);
});
advancedDrivingToggle = advancedLateralTuneToggle;
} else if (param == "SteerFriction") {
std::vector<QString> steerFrictionToggleNames{"Reset"};
advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map<int, QString>(), 0.01, {}, steerFrictionToggleNames, false);
} else if (param == "SteerKP") {
std::vector<QString> steerKPToggleNames{"Reset"};
advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKPStock * 0.50, steerKPStock * 1.50, QString(), std::map<int, QString>(), 0.01, {}, steerKPToggleNames, false);
} else if (param == "SteerLatAccel") {
std::vector<QString> steerLatAccelToggleNames{"Reset"};
advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerLatAccelStock * 0.25, steerLatAccelStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerLatAccelToggleNames, false);
} else if (param == "SteerRatio") {
std::vector<QString> steerRatioToggleNames{"Reset"};
advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerRatioToggleNames, false);
} else if (param == "AdvancedLongitudinalTune") {
FrogPilotParamManageControl *advancedLongitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(advancedLongitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedLongitudinalTuneKeys = longitudinalTuneKeys;
bool radarlessModel = QString::fromStdString(params.get("RadarlessModels")).split(",").contains(QString::fromStdString(params.get("Model")));
if (radarlessModel) {
modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold");
}
showToggles(modifiedLongitudinalTuneKeys);
});
advancedDrivingToggle = advancedLongitudinalTuneToggle;
} else if (param == "LeadDetectionThreshold") {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, "%");
} else if (param == "MaxDesiredAcceleration") {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.1, 4.0, "m/s", std::map<int, QString>(), 0.1);
} else if (param == "AdvancedQOLDriving") {
FrogPilotParamManageControl *advancedQOLToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(advancedQOLToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedQolKeys = qolKeys;
if (hasPCMCruise) {
modifiedQolKeys.erase("SetSpeedOffset");
}
showToggles(modifiedQolKeys);
});
advancedDrivingToggle = advancedQOLToggle;
} else if (param == "SetSpeedOffset") {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} else if (param == "CustomPersonalities") {
FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(customDrivingPersonalityKeys);
});
advancedDrivingToggle = customPersonalitiesToggle;
} else if (param == "ResetTrafficPersonality" || param == "ResetAggressivePersonality" || param == "ResetStandardPersonality" || param == "ResetRelaxedPersonality") {
FrogPilotButtonsControl *profileBtn = new FrogPilotButtonsControl(title, desc, {tr("Reset")});
advancedDrivingToggle = profileBtn;
} else if (param == "TrafficPersonalityProfile") {
FrogPilotParamManageControl *trafficPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(trafficPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(trafficPersonalityKeys);
});
advancedDrivingToggle = trafficPersonalityToggle;
} else if (param == "AggressivePersonalityProfile") {
FrogPilotParamManageControl *aggressivePersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(aggressivePersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(aggressivePersonalityKeys);
});
advancedDrivingToggle = aggressivePersonalityToggle;
} else if (param == "StandardPersonalityProfile") {
FrogPilotParamManageControl *standardPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(standardPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(standardPersonalityKeys);
});
advancedDrivingToggle = standardPersonalityToggle;
} else if (param == "RelaxedPersonalityProfile") {
FrogPilotParamManageControl *relaxedPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(relaxedPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(relaxedPersonalityKeys);
});
advancedDrivingToggle = relaxedPersonalityToggle;
} else if (trafficPersonalityKeys.find(param) != trafficPersonalityKeys.end() ||
aggressivePersonalityKeys.find(param) != aggressivePersonalityKeys.end() ||
standardPersonalityKeys.find(param) != standardPersonalityKeys.end() ||
relaxedPersonalityKeys.find(param) != relaxedPersonalityKeys.end()) {
if (param == "TrafficFollow" || param == "AggressiveFollow" || param == "StandardFollow" || param == "RelaxedFollow") {
if (param == "TrafficFollow") {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.5, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
} else {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
}
} else {
advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 500, "%");
}
} else if (param == "ModelManagement") {
FrogPilotParamManageControl *modelManagementToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(modelManagementToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
availableModelNames = QString::fromStdString(params.get("AvailableModelsNames")).split(",");
availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
experimentalModels = QString::fromStdString(params.get("ExperimentalModels")).split(",");
modelManagementOpen = true;
QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed";
QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files);
modelFiles.removeAll(currentModel);
haveModelsDownloaded = modelFiles.size() > 1;
modelsDownloaded = params.getBool("ModelsDownloaded");
showToggles(modelManagementKeys);
});
advancedDrivingToggle = modelManagementToggle;
} else if (param == "ModelRandomizer") {
FrogPilotParamManageControl *modelRandomizerToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(modelRandomizerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
openSubParentToggle();
showToggles(modelRandomizerKeys);
updateModelLabels();
});
advancedDrivingToggle = modelRandomizerToggle;
} else if (param == "ManageBlacklistedModels") {
FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, {tr("ADD"), tr("REMOVE")});
QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",", QString::SkipEmptyParts);
QStringList selectableModels = availableModelNames;
for (QString &model : blacklistedModels) {
selectableModels.removeAll(model);
if (model.contains("(Default)")) {
blacklistedModels.move(blacklistedModels.indexOf(model), 0);
}
}
if (id == 0) {
if (selectableModels.size() == 1) {
FrogPilotConfirmationDialog::toggleAlert(tr("There's no more models to blacklist! The only available model is \"%1\"!").arg(selectableModels.first()), tr("OK"), this);
} else {
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), selectableModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to add the '%1' model to the blacklist?").arg(selectedModel), tr("Add"), this)) {
if (!blacklistedModels.contains(selectedModel)) {
blacklistedModels.append(selectedModel);
params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
}
}
}
}
} else if (id == 1) {
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), blacklistedModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the '%1' model from the blacklist?").arg(selectedModel), tr("Remove"), this)) {
if (blacklistedModels.contains(selectedModel)) {
blacklistedModels.removeAll(selectedModel);
params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
}
}
}
}
});
advancedDrivingToggle = blacklistBtn;
} else if (param == "ResetScores") {
ButtonControl *resetCalibrationsBtn = new ButtonControl(title, tr("RESET"), desc);
QObject::connect(resetCalibrationsBtn, &ButtonControl::clicked, [this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model scores?"), this)) {
for (const QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
params.remove(QString("%1Drives").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1Drives").arg(cleanedModel).toStdString());
params.remove(QString("%1Score").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1Score").arg(cleanedModel).toStdString());
}
updateModelLabels();
}
});
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(resetCalibrationsBtn);
} else if (param == "ReviewScores") {
ButtonControl *reviewScoresBtn = new ButtonControl(title, tr("VIEW"), desc);
QObject::connect(reviewScoresBtn, &ButtonControl::clicked, [this]() {
openSubSubParentToggle();
for (LabelControl *label : labelControls) {
label->setVisible(true);
}
for (auto &[key, toggle] : toggles) {
toggle->setVisible(false);
}
});
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(reviewScoresBtn);
} else if (param == "DeleteModel") {
deleteModelBtn = new ButtonControl(title, tr("DELETE"), desc);
QObject::connect(deleteModelBtn, &ButtonControl::clicked, [this]() {
QStringList deletableModels, existingModels = modelDir.entryList({"*.thneed"}, QDir::Files);
QMap<QString, QString> labelToFileMap;
QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed";
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]);
labelToFileMap[availableModelNames[i]] = modelFile;
}
}
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to delete"), deletableModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' model?").arg(selectedModel), tr("Delete"), this)) {
std::thread([=]() {
modelDeleting = true;
modelsDownloaded = false;
update();
params.putBoolNonBlocking("ModelsDownloaded", false);
deleteModelBtn->setValue(tr("Deleting..."));
QFile::remove(modelDir.absoluteFilePath(labelToFileMap[selectedModel]));
deleteModelBtn->setValue(tr("Deleted!"));
util::sleep_for(1000);
deleteModelBtn->setValue("");
modelDeleting = false;
QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files);
modelFiles.removeAll(currentModel);
haveModelsDownloaded = modelFiles.size() > 1;
update();
}).detach();
}
}
});
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(deleteModelBtn);
} else if (param == "DownloadModel") {
downloadModelBtn = new ButtonControl(title, tr("DOWNLOAD"), desc);
QObject::connect(downloadModelBtn, &ButtonControl::clicked, [this]() {
if (downloadModelBtn->text() == tr("CANCEL")) {
paramsMemory.remove("ModelToDownload");
paramsMemory.putBool("CancelModelDownload", true);
cancellingDownload = true;
device()->resetInteractiveTimeout(30);
} else {
QMap<QString, QString> labelToModelMap;
QStringList existingModels = modelDir.entryList({"*.thneed"}, QDir::Files);
QStringList downloadableModels;
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]);
labelToModelMap.insert(availableModelNames[i], availableModels[i]);
}
}
QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this);
if (!modelToDownload.isEmpty()) {
device()->resetInteractiveTimeout(300);
modelDownloading = true;
paramsMemory.put("ModelToDownload", labelToModelMap.value(modelToDownload).toStdString());
paramsMemory.put("ModelDownloadProgress", "0%");
downloadModelBtn->setValue(tr("Downloading %1...").arg(modelToDownload.remove(QRegularExpression("[🗺️👀📡]")).trimmed()));
QTimer *progressTimer = new QTimer(this);
progressTimer->setInterval(100);
QObject::connect(progressTimer, &QTimer::timeout, this, [=]() {
QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress"));
bool downloadComplete = progress.contains(QRegularExpression("downloaded", QRegularExpression::CaseInsensitiveOption));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (!progress.isEmpty() && progress != "0%") {
downloadModelBtn->setValue(progress);
}
if (downloadComplete || downloadFailed) {
bool lastModelDownloaded = downloadComplete;
if (downloadComplete) {
haveModelsDownloaded = true;
update();
}
if (downloadComplete) {
for (const QString &model : availableModels) {
if (!QFile::exists(modelDir.filePath(model + ".thneed"))) {
lastModelDownloaded = false;
break;
}
}
}
downloadModelBtn->setValue(progress);
paramsMemory.remove("CancelModelDownload");
paramsMemory.remove("ModelDownloadProgress");
progressTimer->stop();
progressTimer->deleteLater();
QTimer::singleShot(2000, this, [=]() {
cancellingDownload = false;
modelDownloading = false;
downloadModelBtn->setValue("");
if (lastModelDownloaded) {
modelsDownloaded = true;
update();
params.putBoolNonBlocking("ModelsDownloaded", modelsDownloaded);
}
device()->resetInteractiveTimeout(30);
});
}
});
progressTimer->start();
}
}
});
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(downloadModelBtn);
} else if (param == "DownloadAllModels") {
downloadAllModelsBtn = new ButtonControl(title, tr("DOWNLOAD"), desc);
QObject::connect(downloadAllModelsBtn, &ButtonControl::clicked, [this]() {
if (downloadAllModelsBtn->text() == tr("CANCEL")) {
paramsMemory.remove("DownloadAllModels");
paramsMemory.putBool("CancelModelDownload", true);
cancellingDownload = true;
device()->resetInteractiveTimeout(30);
} else {
device()->resetInteractiveTimeout(300);
startDownloadAllModels();
}
});
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(downloadAllModelsBtn);
} else if (param == "SelectModel") {
selectModelBtn = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(selectModelBtn, &ButtonControl::clicked, [this]() {
QSet<QString> modelFilesBaseNames = QSet<QString>::fromList(modelDir.entryList({"*.thneed"}, QDir::Files).replaceInStrings(QRegExp("\\.thneed$"), ""));
QStringList selectableModels;
for (int i = 0; i < availableModels.size(); ++i) {
if (modelFilesBaseNames.contains(availableModels[i]) || availableModelNames[i].contains("(Default)")) {
selectableModels.append(availableModelNames[i]);
}
}
QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, "", this);
if (!modelToSelect.isEmpty()) {
selectModelBtn->setValue(modelToSelect);
int modelIndex = availableModelNames.indexOf(modelToSelect);
params.putNonBlocking("Model", availableModels.at(modelIndex).toStdString());
params.putNonBlocking("ModelName", modelToSelect.toStdString());
if (experimentalModels.contains(availableModels.at(modelIndex))) {
FrogPilotConfirmationDialog::toggleAlert(tr("WARNING: This is a very experimental model and may drive dangerously!"), tr("I understand the risks."), this);
}
QString model = availableModelNames.at(modelIndex);
QString part_model_param = processModelName(model);
if (!params.checkKey(part_model_param.toStdString() + "CalibrationParams") || !params.checkKey(part_model_param.toStdString() + "LiveTorqueParameters")) {
if (FrogPilotConfirmationDialog::yesorno(tr("Start with a fresh calibration for the newly selected model?"), this)) {
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
}
}
if (started) {
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
Hardware::reboot();
}
}
}
});
selectModelBtn->setValue(QString::fromStdString(params.get("ModelName")));
advancedDrivingToggle = reinterpret_cast<AbstractControl*>(selectModelBtn);
} else if (param == "ResetCalibrations") {
FrogPilotButtonsControl *resetCalibrationsBtn = new FrogPilotButtonsControl(title, desc, {tr("RESET ALL"), tr("RESET ONE")});
QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::showDescriptionEvent, this, &FrogPilotAdvancedDrivingPanel::updateCalibrationDescription);
QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
if (id == 0) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset all of your model calibrations?"), this)) {
for (const QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
}
}
} else if (id == 1) {
QStringList selectableModelLabels;
for (int i = 0; i < availableModels.size(); ++i) {
selectableModelLabels.append(availableModelNames[i]);
}
QString modelToReset = MultiOptionDialog::getSelection(tr("Select a model to reset"), selectableModelLabels, "", this);
if (!modelToReset.isEmpty()) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset this model's calibrations?"), this)) {
QString cleanedModel = processModelName(modelToReset);
params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
}
}
}
});
advancedDrivingToggle = resetCalibrationsBtn;
} else {
advancedDrivingToggle = new ParamControl(param, title, desc, icon);
}
addItem(advancedDrivingToggle);
toggles[param] = advancedDrivingToggle;
makeConnections(advancedDrivingToggle);
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(advancedDrivingToggle)) {
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotAdvancedDrivingPanel::openParentToggle);
}
QObject::connect(advancedDrivingToggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
}
QObject::connect(static_cast<ToggleControl*>(toggles["ModelManagement"]), &ToggleControl::toggleFlipped, [this](bool state) {
modelManagement = state;
});
QObject::connect(static_cast<ToggleControl*>(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) {
modelRandomizer = state;
if (state && !modelsDownloaded) {
if (FrogPilotConfirmationDialog::yesorno(tr("The 'Model Randomizer' only works with downloaded models. Do you want to download all the driving models?"), this)) {
startDownloadAllModels();
}
}
});
steerFrictionToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerFriction"]);
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerFriction", steerFrictionStock);
steerFrictionToggle->refresh();
updateFrogPilotToggles();
});
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerKP", steerKPStock);
steerKPToggle->refresh();
updateFrogPilotToggles();
});
steerLatAccelToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerLatAccel"]);
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerLatAccel", steerLatAccelStock);
steerLatAccelToggle->refresh();
updateFrogPilotToggles();
});
steerRatioToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerRatio"]);
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerRatio", steerRatioStock);
steerRatioToggle->refresh();
updateFrogPilotToggles();
});
FrogPilotParamValueControl *trafficFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficFollow"]);
FrogPilotParamValueControl *trafficAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkAcceleration"]);
FrogPilotParamValueControl *trafficDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDeceleration"]);
FrogPilotParamValueControl *trafficDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDanger"]);
FrogPilotParamValueControl *trafficSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeed"]);
FrogPilotParamValueControl *trafficSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeedDecrease"]);
FrogPilotButtonsControl *trafficResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetTrafficPersonality"]);
QObject::connect(trafficResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Traffic Mode' personality?"), this)) {
params.putFloat("TrafficFollow", 0.5);
params.putFloat("TrafficJerkAcceleration", 50);
params.putFloat("TrafficJerkDeceleration", 50);
params.putFloat("TrafficJerkDanger", 100);
params.putFloat("TrafficJerkSpeed", 50);
params.putFloat("TrafficJerkSpeedDecrease", 50);
trafficFollowToggle->refresh();
trafficAccelerationToggle->refresh();
trafficDecelerationToggle->refresh();
trafficDangerToggle->refresh();
trafficSpeedToggle->refresh();
trafficSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *aggressiveFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveFollow"]);
FrogPilotParamValueControl *aggressiveAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkAcceleration"]);
FrogPilotParamValueControl *aggressiveDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDeceleration"]);
FrogPilotParamValueControl *aggressiveDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDanger"]);
FrogPilotParamValueControl *aggressiveSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeed"]);
FrogPilotParamValueControl *aggressiveSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeedDecrease"]);
FrogPilotButtonsControl *aggressiveResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetAggressivePersonality"]);
QObject::connect(aggressiveResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Aggressive' personality?"), this)) {
params.putFloat("AggressiveFollow", 1.25);
params.putFloat("AggressiveJerkAcceleration", 50);
params.putFloat("AggressiveJerkDeceleration", 50);
params.putFloat("AggressiveJerkDanger", 100);
params.putFloat("AggressiveJerkSpeed", 50);
params.putFloat("AggressiveJerkSpeedDecrease", 50);
aggressiveFollowToggle->refresh();
aggressiveAccelerationToggle->refresh();
aggressiveDecelerationToggle->refresh();
aggressiveDangerToggle->refresh();
aggressiveSpeedToggle->refresh();
aggressiveSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *standardFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardFollow"]);
FrogPilotParamValueControl *standardAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkAcceleration"]);
FrogPilotParamValueControl *standardDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDeceleration"]);
FrogPilotParamValueControl *standardDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDanger"]);
FrogPilotParamValueControl *standardSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeed"]);
FrogPilotParamValueControl *standardSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeedDecrease"]);
FrogPilotButtonsControl *standardResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetStandardPersonality"]);
QObject::connect(standardResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Standard' personality?"), this)) {
params.putFloat("StandardFollow", 1.45);
params.putFloat("StandardJerkAcceleration", 100);
params.putFloat("StandardJerkDeceleration", 100);
params.putFloat("StandardJerkDanger", 100);
params.putFloat("StandardJerkSpeed", 100);
params.putFloat("StandardJerkSpeedDecrease", 100);
standardFollowToggle->refresh();
standardAccelerationToggle->refresh();
standardDecelerationToggle->refresh();
standardDangerToggle->refresh();
standardSpeedToggle->refresh();
standardSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *relaxedFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedFollow"]);
FrogPilotParamValueControl *relaxedAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkAcceleration"]);
FrogPilotParamValueControl *relaxedDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDeceleration"]);
FrogPilotParamValueControl *relaxedDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDanger"]);
FrogPilotParamValueControl *relaxedSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeed"]);
FrogPilotParamValueControl *relaxedSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeedDecrease"]);
FrogPilotButtonsControl *relaxedResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetRelaxedPersonality"]);
QObject::connect(relaxedResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Relaxed' personality?"), this)) {
params.putFloat("RelaxedFollow", 1.75);
params.putFloat("RelaxedJerkAcceleration", 100);
params.putFloat("RelaxedJerkDeceleration", 100);
params.putFloat("RelaxedJerkDanger", 100);
params.putFloat("RelaxedJerkSpeed", 100);
params.putFloat("RelaxedJerkSpeedDecrease", 100);
relaxedFollowToggle->refresh();
relaxedAccelerationToggle->refresh();
relaxedDecelerationToggle->refresh();
relaxedDangerToggle->refresh();
relaxedSpeedToggle->refresh();
relaxedSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideSubToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideSubSubToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotAdvancedDrivingPanel::updateCarToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotAdvancedDrivingPanel::updateMetric);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotAdvancedDrivingPanel::updateState);
updateMetric();
}
void FrogPilotAdvancedDrivingPanel::updateMetric() {
bool previousIsMetric = isMetric;
isMetric = params.getBool("IsMetric");
if (isMetric != previousIsMetric) {
double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE;
params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion);
}
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast<FrogPilotParamValueControl*>(toggles["SetSpeedOffset"]);
if (isMetric) {
setSpeedOffsetToggle->updateControl(0, 150, tr("kph"));
} else {
setSpeedOffsetToggle->updateControl(0, 99, tr("mph"));
}
}
void FrogPilotAdvancedDrivingPanel::showEvent(QShowEvent *event) {
modelManagement = params.getBool("ModelManagement");
modelRandomizer = params.getBool("ModelRandomizer");
}
void FrogPilotAdvancedDrivingPanel::updateCarToggles() {
disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal;
hasAutoTune = parent->hasAutoTune;
hasNNFFLog = parent->hasNNFFLog;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
hasPCMCruise = parent->hasPCMCruise;
isPIDCar = parent->isPIDCar;
liveValid = parent->liveValid;
steerFrictionStock = parent->steerFrictionStock;
steerKPStock = parent->steerKPStock;
steerLatAccelStock = parent->steerLatAccelStock;
steerRatioStock = parent->steerRatioStock;
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)));
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)));
steerKPToggle->updateControl(steerKPStock * 0.50, steerKPStock * 1.50);
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2)));
steerLatAccelToggle->updateControl(steerLatAccelStock * 0.75, steerLatAccelStock * 1.25);
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)));
steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25);
hideToggles();
}
void FrogPilotAdvancedDrivingPanel::updateState(const UIState &s) {
if (!isVisible()) return;
if (modelManagementOpen) {
downloadAllModelsBtn->setText(modelDownloading && allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
downloadModelBtn->setText(modelDownloading && !allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
deleteModelBtn->setEnabled(!modelDeleting && !modelDownloading);
downloadAllModelsBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && (!modelDownloading || allModelsDownloading) && !modelsDownloaded);
downloadModelBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && !allModelsDownloading && !modelsDownloaded);
selectModelBtn->setEnabled(!modelDeleting && !modelDownloading && !modelRandomizer);
}
started = s.scene.started;
}
void FrogPilotAdvancedDrivingPanel::startDownloadAllModels() {
allModelsDownloading = true;
modelDownloading = true;
paramsMemory.putBool("DownloadAllModels", true);
downloadAllModelsBtn->setValue(tr("Downloading models..."));
QTimer *progressTimer = new QTimer(this);
progressTimer->setInterval(100);
QObject::connect(progressTimer, &QTimer::timeout, this, [=]() {
QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress"));
bool downloadComplete = progress.contains(QRegularExpression("All models downloaded!", QRegularExpression::CaseInsensitiveOption));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (!progress.isEmpty() && progress != "0%") {
downloadAllModelsBtn->setValue(progress);
}
if (downloadComplete || downloadFailed) {
if (downloadComplete) {
haveModelsDownloaded = true;
update();
}
downloadAllModelsBtn->setValue(progress);
paramsMemory.remove("CancelModelDownload");
paramsMemory.remove("ModelDownloadProgress");
progressTimer->stop();
progressTimer->deleteLater();
QTimer::singleShot(2000, this, [=]() {
cancellingDownload = false;
modelDownloading = false;
paramsMemory.remove("DownloadAllModels");
downloadAllModelsBtn->setValue("");
device()->resetInteractiveTimeout(30);
});
}
});
progressTimer->start();
}
void FrogPilotAdvancedDrivingPanel::updateCalibrationDescription() {
QString model = QString::fromStdString(params.get("ModelName"));
QString part_model_param = processModelName(model);
QString desc =
tr("openpilot requires the device to be mounted within 4° left or right and "
"within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required.");
std::string calib_bytes = params.get(part_model_param.toStdString() + "CalibrationParams");
if (!calib_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) {
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
desc += tr(" Your device is pointed %1° %2 and %3° %4.")
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"),
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right"));
}
} catch (kj::Exception) {
qInfo() << "invalid CalibrationParams";
}
}
qobject_cast<FrogPilotButtonsControl *>(sender())->setDescription(desc);
}
void FrogPilotAdvancedDrivingPanel::updateModelLabels() {
QVector<QPair<QString, int>> modelScores;
for (QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
int score = params.getInt((cleanedModel + "Score").toStdString());
if (model.contains("(Default)")) {
modelScores.prepend(qMakePair(model, score));
} else {
modelScores.append(qMakePair(model, score));
}
}
labelControls.clear();
for (QPair<QString, int> &pair : modelScores) {
QString scoreDisplay = pair.second == 0 ? "N/A" : QString::number(pair.second) + "%";
LabelControl *labelControl = new LabelControl(pair.first, scoreDisplay, "");
labelControls.append(labelControl);
addItem(labelControl);
}
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
}
void FrogPilotAdvancedDrivingPanel::showToggles(const std::set<QString> &keys) {
setUpdatesEnabled(false);
for (auto &[key, toggle] : toggles) {
toggle->setVisible(keys.find(key) != keys.end());
}
setUpdatesEnabled(true);
update();
}
void FrogPilotAdvancedDrivingPanel::hideToggles() {
setUpdatesEnabled(false);
customPersonalityOpen = false;
modelManagementOpen = false;
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
std::set<QString> longitudinalKeys = {"AdvancedLongitudinalTune", "CustomPersonalities"};
for (auto &[key, toggle] : toggles) {
bool subToggles = aggressivePersonalityKeys.find(key) != aggressivePersonalityKeys.end() ||
customDrivingPersonalityKeys.find(key) != customDrivingPersonalityKeys.end() ||
lateralTuneKeys.find(key) != lateralTuneKeys.end() ||
longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() ||
modelManagementKeys.find(key) != modelManagementKeys.end() ||
modelRandomizerKeys.find(key) != modelRandomizerKeys.end() ||
qolKeys.find(key) != qolKeys.end() ||
relaxedPersonalityKeys.find(key) != relaxedPersonalityKeys.end() ||
standardPersonalityKeys.find(key) != standardPersonalityKeys.end() ||
trafficPersonalityKeys.find(key) != trafficPersonalityKeys.end();
if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) {
if (longitudinalKeys.find(key) != longitudinalKeys.end()) {
toggle->setVisible(false);
continue;
}
}
toggle->setVisible(!subToggles);
}
setUpdatesEnabled(true);
update();
}
void FrogPilotAdvancedDrivingPanel::hideSubToggles() {
if (customPersonalityOpen) {
customPersonalityOpen = false;
showToggles(customDrivingPersonalityKeys);
} else if (modelManagementOpen) {
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
showToggles(modelManagementKeys);
}
}
void FrogPilotAdvancedDrivingPanel::hideSubSubToggles() {
if (modelManagementOpen) {
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
showToggles(modelRandomizerKeys);
}
}

View File

@ -1,137 +0,0 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
class FrogPilotAdvancedDrivingPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWindow *parent);
signals:
void openParentToggle();
void openSubParentToggle();
void openSubSubParentToggle();
protected:
void showEvent(QShowEvent *event) override;
private:
void hideSubToggles();
void hideSubSubToggles();
void hideToggles();
void showToggles(const std::set<QString> &keys);
void startDownloadAllModels();
void updateCalibrationDescription();
void updateCarToggles();
void updateMetric();
void updateModelLabels();
void updateState(const UIState &s);
std::set<QString> aggressivePersonalityKeys = {
"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration",
"AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease",
"ResetAggressivePersonality"
};
std::set<QString> customDrivingPersonalityKeys = {
"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile",
"TrafficPersonalityProfile"
};
std::set<QString> lateralTuneKeys = {
"ForceAutoTune", "ForceAutoTuneOff", "SteerFriction",
"SteerLatAccel", "SteerKP", "SteerRatio", "TacoTune",
"TurnDesires"
};
std::set<QString> longitudinalTuneKeys = {
"LeadDetectionThreshold", "MaxDesiredAcceleration"
};
std::set<QString> modelManagementKeys = {
"AutomaticallyUpdateModels", "DeleteModel", "DownloadModel",
"DownloadAllModels", "ModelRandomizer", "ResetCalibrations",
"SelectModel"
};
std::set<QString> modelRandomizerKeys = {
"ManageBlacklistedModels", "ResetScores", "ReviewScores"
};
std::set<QString> qolKeys = {
"ForceStandstill", "ForceStops", "SetSpeedOffset"
};
std::set<QString> relaxedPersonalityKeys = {
"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration",
"RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease",
"ResetRelaxedPersonality"
};
std::set<QString> standardPersonalityKeys = {
"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration",
"StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease",
"ResetStandardPersonality"
};
std::set<QString> trafficPersonalityKeys = {
"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration",
"TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease",
"ResetTrafficPersonality"
};
ButtonControl *deleteModelBtn;
ButtonControl *downloadAllModelsBtn;
ButtonControl *downloadModelBtn;
ButtonControl *selectModelBtn;
FrogPilotParamValueButtonControl *steerFrictionToggle;
FrogPilotParamValueButtonControl *steerLatAccelToggle;
FrogPilotParamValueButtonControl *steerKPToggle;
FrogPilotParamValueButtonControl *steerRatioToggle;
FrogPilotSettingsWindow *parent;
Params params;
Params paramsMemory{"/dev/shm/params"};
Params paramsStorage{"/persist/params"};
QDir modelDir{"/data/models/"};
QList<LabelControl*> labelControls;
QStringList availableModelNames;
QStringList availableModels;
QStringList experimentalModels;
bool allModelsDownloading;
bool cancellingDownload;
bool customPersonalityOpen;
bool disableOpenpilotLongitudinal;
bool hasAutoTune;
bool hasNNFFLog;
bool hasOpenpilotLongitudinal;
bool hasPCMCruise;
bool haveModelsDownloaded;
bool isMetric = params.getBool("IsMetric");
bool isPIDCar;
bool liveValid;
bool modelDeleting;
bool modelDownloading;
bool modelManagement;
bool modelManagementOpen;
bool modelRandomizer;
bool modelsDownloaded;
bool started;
float steerFrictionStock;
float steerLatAccelStock;
float steerKPStock;
float steerRatioStock;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -1,226 +0,0 @@
#include "selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h"
FrogPilotAdvancedVisualsPanel::FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> advancedToggles {
{"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."), ""},
{"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"},
{"BorderMetrics", tr("Border Metrics"), tr("Display performance metrics around the edge of the screen while driving."), ""},
{"FPSCounter", tr("FPS Display"), tr("Display the 'Frames Per Second' (FPS) at the bottom of the screen while driving."), ""},
{"LateralMetrics", tr("Lateral Metrics"), tr("Display metrics related to steering control at the top of the screen while driving."), ""},
{"LongitudinalMetrics", tr("Longitudinal Metrics"), tr("Display metrics related to acceleration, speed, and desired following distance at the top of the screen while driving."), ""},
{"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Show exact temperature readings instead of general status labels like 'GOOD', 'OK', or 'HIGH' in the sidebar."), ""},
{"SidebarMetrics", tr("Sidebar"), tr("Display system information like CPU, GPU, RAM usage, IP address, and storage space in the sidebar."), ""},
{"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("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."), ""},
};
for (const auto &[param, title, desc, icon] : advancedToggles) {
AbstractControl *advancedVisualToggle;
if (param == "AdvancedCustomUI") {
FrogPilotParamManageControl *advancedCustomUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(advancedCustomUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(advancedCustomOnroadUIKeys);
});
advancedVisualToggle = advancedCustomUIToggle;
} else if (param == "CameraView") {
std::vector<QString> cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")};
ButtonParamControl *preferredCamera = new ButtonParamControl(param, title, desc, icon, cameraOptions);
advancedVisualToggle = preferredCamera;
} else if (param == "HideSpeed") {
std::vector<QString> hideSpeedToggles{"HideSpeedUI"};
std::vector<QString> hideSpeedToggleNames{tr("Control Via UI")};
advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, hideSpeedToggles, hideSpeedToggleNames);
} else if (param == "HideUIElements") {
std::vector<QString> uiElementsToggles{"HideAlerts", "HideMapIcon", "HideMaxSpeed"};
std::vector<QString> uiElementsToggleNames{tr("Alerts"), tr("Map Icon"), tr("Max Speed")};
advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, uiElementsToggles, uiElementsToggleNames);
} else if (param == "ShowStoppingPoint") {
std::vector<QString> stoppingPointToggles{"ShowStoppingPointMetrics"};
std::vector<QString> stoppingPointToggleNames{tr("Show Distance")};
advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, stoppingPointToggles, stoppingPointToggleNames);
} else if (param == "DeveloperUI") {
FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
borderMetricsBtn->setVisibleButton(0, hasBSM);
lateralMetricsBtn->setVisibleButton(1, hasAutoTune);
std::set<QString> modifiedDeveloperUIKeys = developerUIKeys;
if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) {
modifiedDeveloperUIKeys.erase("LongitudinalMetrics");
}
showToggles(modifiedDeveloperUIKeys);
});
advancedVisualToggle = developerUIToggle;
} else if (param == "BorderMetrics") {
std::vector<QString> borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"};
std::vector<QString> borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")};
borderMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, borderToggles, borderToggleNames);
advancedVisualToggle = borderMetricsBtn;
} else if (param == "LateralMetrics") {
std::vector<QString> lateralToggles{"AdjacentPathMetrics", "TuningInfo"};
std::vector<QString> lateralToggleNames{tr("Adjacent Path Metrics"), tr("Auto Tune")};
lateralMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, lateralToggles, lateralToggleNames);
advancedVisualToggle = lateralMetricsBtn;
} else if (param == "LongitudinalMetrics") {
std::vector<QString> longitudinalToggles{"LeadInfo", "JerkInfo"};
std::vector<QString> longitudinalToggleNames{tr("Lead Info"), tr("Jerk Values")};
longitudinalMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, longitudinalToggles, longitudinalToggleNames);
advancedVisualToggle = longitudinalMetricsBtn;
} else if (param == "NumericalTemp") {
std::vector<QString> temperatureToggles{"Fahrenheit"};
std::vector<QString> temperatureToggleNames{tr("Fahrenheit")};
advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, temperatureToggles, temperatureToggleNames);
} else if (param == "SidebarMetrics") {
std::vector<QString> sidebarMetricsToggles{"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"};
std::vector<QString> sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")};
FrogPilotButtonToggleControl *sidebarMetricsToggle = new FrogPilotButtonToggleControl(param, title, desc, sidebarMetricsToggles, sidebarMetricsToggleNames, false, 150);
QObject::connect(sidebarMetricsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) {
if (index == 0) {
params.putBool("ShowGPU", false);
} else if (index == 1) {
params.putBool("ShowCPU", false);
} else if (index == 3) {
params.putBool("ShowStorageLeft", false);
params.putBool("ShowStorageUsed", false);
} else if (index == 4) {
params.putBool("ShowMemoryUsage", false);
params.putBool("ShowStorageUsed", false);
} else if (index == 5) {
params.putBool("ShowMemoryUsage", false);
params.putBool("ShowStorageLeft", false);
}
});
advancedVisualToggle = sidebarMetricsToggle;
} else if (param == "ModelUI") {
FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedModelUIKeysKeys = modelUIKeys;
if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) {
modifiedModelUIKeysKeys.erase("HideLeadMarker");
}
showToggles(modifiedModelUIKeysKeys);
});
advancedVisualToggle = modelUIToggle;
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, tr(" inches"));
} else if (param == "PathEdgeWidth") {
advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, tr("%"));
} else if (param == "PathWidth") {
advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"), std::map<int, QString>(), 0.1);
} else {
advancedVisualToggle = new ParamControl(param, title, desc, icon);
}
addItem(advancedVisualToggle);
toggles[param] = advancedVisualToggle;
makeConnections(advancedVisualToggle);
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(advancedVisualToggle)) {
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotAdvancedVisualsPanel::openParentToggle);
}
QObject::connect(advancedVisualToggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
}
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotAdvancedVisualsPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotAdvancedVisualsPanel::updateCarToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotAdvancedVisualsPanel::updateMetric);
updateMetric();
}
void FrogPilotAdvancedVisualsPanel::updateCarToggles() {
disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal;
hasAutoTune = parent->hasAutoTune;
hasBSM = parent->hasBSM;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
hideToggles();
}
void FrogPilotAdvancedVisualsPanel::updateMetric() {
bool previousIsMetric = isMetric;
isMetric = params.getBool("IsMetric");
if (isMetric != previousIsMetric) {
double smallDistanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion);
params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion);
params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion);
}
FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
if (isMetric) {
laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters."));
roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters."));
laneLinesWidthToggle->updateControl(0, 60, tr(" centimeters"));
roadEdgesWidthToggle->updateControl(0, 60, tr(" centimeters"));
pathWidthToggle->updateControl(0, 3, tr(" meters"));
} else {
laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."));
roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard of 4 inches."));
laneLinesWidthToggle->updateControl(0, 24, tr(" inches"));
roadEdgesWidthToggle->updateControl(0, 24, tr(" inches"));
pathWidthToggle->updateControl(0, 10, tr(" feet"));
}
}
void FrogPilotAdvancedVisualsPanel::showToggles(const std::set<QString> &keys) {
setUpdatesEnabled(false);
for (auto &[key, toggle] : toggles) {
toggle->setVisible(keys.find(key) != keys.end());
}
setUpdatesEnabled(true);
update();
}
void FrogPilotAdvancedVisualsPanel::hideToggles() {
setUpdatesEnabled(false);
for (auto &[key, toggle] : toggles) {
bool subToggles = advancedCustomOnroadUIKeys.find(key) != advancedCustomOnroadUIKeys.end() ||
developerUIKeys.find(key) != developerUIKeys.end() ||
modelUIKeys.find(key) != modelUIKeys.end();
toggle->setVisible(!subToggles);
}
setUpdatesEnabled(true);
update();
}

View File

@ -1,53 +0,0 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
class FrogPilotAdvancedVisualsPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWindow *parent);
signals:
void openParentToggle();
private:
void hideToggles();
void showToggles(const std::set<QString> &keys);
void updateCarToggles();
void updateMetric();
std::set<QString> advancedCustomOnroadUIKeys = {
"CameraView", "HideLeadMarker", "HideSpeed",
"HideUIElements", "ShowStoppingPoint", "WheelSpeed"
};
std::set<QString> developerUIKeys = {
"BorderMetrics", "FPSCounter", "LateralMetrics",
"LongitudinalMetrics", "NumericalTemp",
"SidebarMetrics", "UseSI"
};
std::set<QString> modelUIKeys = {
"LaneLinesWidth", "PathEdgeWidth", "PathWidth",
"RoadEdgesWidth", "UnlimitedLength"
};
FrogPilotButtonToggleControl *borderMetricsBtn;
FrogPilotButtonToggleControl *lateralMetricsBtn;
FrogPilotButtonToggleControl *longitudinalMetricsBtn;
FrogPilotSettingsWindow *parent;
Params params;
bool disableOpenpilotLongitudinal;
bool hasAutoTune;
bool hasBSM;
bool hasOpenpilotLongitudinal;
bool isMetric = params.getBool("IsMetric");
std::map<QString, AbstractControl*> toggles;
};

View File

@ -3,7 +3,7 @@
#include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h"
FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(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."));
ButtonControl *deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Footage and Data"), tr("DELETE"), tr("Permanently deletes all stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space."));
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([=] {

View File

@ -1,21 +1,21 @@
#include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h"
FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) {
FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> deviceToggles {
{"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."), ""},
{"DeviceShutdown", tr("Device Shutdown Timer"), tr("Controls how long the device stays on after you stop driving."), ""},
{"OfflineMode", tr("Disable Internet Requirement"), tr("Allows the device to work without an internet connection."), ""},
{"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), tr("Allows the device to run at higher temperatures than recommended."), ""},
{"LowVoltageShutdown", tr("Low Battery Shutdown Threshold"), tr("Manages the threshold for shutting down the device to protect the car's battery from excessive drain and potential damage."), ""},
{"NoLogging", tr("Turn Off Data Tracking"), tr("Disables all data tracking to improve privacy."), ""},
{"NoUploads", tr("Turn Off Data Uploads"), tr("Stops 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."), ""}
{"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("Controls the screen brightness when you're not driving."), ""},
{"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("Controls the screen brightness while you're driving."), ""},
{"ScreenRecorder", tr("Screen Recorder"), tr("Enables a button in the onroad UI to record the screen."), ""},
{"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("Controls how long it takes for the screen to turn off when you're not driving."), ""},
{"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("Controls how long it takes for the screen to turn off while you're driving."), ""}
};
for (const auto &[param, title, desc, icon] : deviceToggles) {
@ -24,7 +24,17 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr
if (param == "DeviceManagement") {
FrogPilotParamManageControl *deviceManagementToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(deviceManagementToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(deviceManagementKeys);
std::set<QString> modifiedDeviceManagementKeys = deviceManagementKeys;
if (customizationLevel != 2) {
modifiedDeviceManagementKeys.erase("IncreaseThermalLimits");
modifiedDeviceManagementKeys.erase("LowVoltageShutdown");
modifiedDeviceManagementKeys.erase("NoLogging");
modifiedDeviceManagementKeys.erase("NoUploads");
modifiedDeviceManagementKeys.erase("OfflineMode");
}
showToggles(modifiedDeviceManagementKeys);
});
deviceToggle = deviceManagementToggle;
} else if (param == "DeviceShutdown") {
@ -43,14 +53,16 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr
} else if (param == "ScreenManagement") {
FrogPilotParamManageControl *screenToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(screenToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(screenKeys);
std::set<QString> modifiedScreenKeys = screenKeys;
showToggles(modifiedScreenKeys);
});
deviceToggle = screenToggle;
} else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") {
std::map<int, QString> brightnessLabels;
int minBrightness = (param == "ScreenBrightnessOnroad") ? 0 : 1;
for (int i = 1; i <= 101; ++i) {
brightnessLabels[i] = (i == 101) ? tr("Auto") : QString::number(i) + "%";
for (int i = 0; i <= 101; ++i) {
brightnessLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Screen Off") : QString::number(i) + "%";
}
deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, minBrightness, 101, QString(), brightnessLabels, 1, false, true);
} else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") {
@ -118,6 +130,12 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr
hideToggles();
}
void FrogPilotDevicePanel::showEvent(QShowEvent *event) {
customizationLevel = parent->customizationLevel;
toggles["ScreenManagement"]->setVisible(customizationLevel == 2);
}
void FrogPilotDevicePanel::updateState(const UIState &s) {
started = s.scene.started;
}
@ -142,6 +160,8 @@ void FrogPilotDevicePanel::hideToggles() {
toggle->setVisible(!subToggles);
}
toggles["ScreenManagement"]->setVisible(customizationLevel == 2);
setUpdatesEnabled(true);
update();
}

View File

@ -15,6 +15,7 @@ signals:
private:
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set<QString> &keys);
void updateState(const UIState &s);
@ -28,9 +29,13 @@ private:
"ScreenTimeout", "ScreenTimeoutOnroad"
};
FrogPilotSettingsWindow *parent;
Params params;
bool started;
int customizationLevel;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -5,13 +5,12 @@
#include "selfdrive/frogpilot/navigation/ui/maps_settings.h"
#include "selfdrive/frogpilot/navigation/ui/primeless_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/theme_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/utilities.h"
@ -45,13 +44,15 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
FrogPilotListWidget *list = new FrogPilotListWidget(frogpilotSettingsWidget);
FrogPilotAdvancedDrivingPanel *frogpilotAdvancedDrivingPanel = new FrogPilotAdvancedDrivingPanel(this);
QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openSubSubParentToggle, this, &FrogPilotSettingsWindow::openSubSubParentToggle);
FrogPilotAdvancedVisualsPanel *frogpilotAdvancedVisualsPanel = new FrogPilotAdvancedVisualsPanel(this);
QObject::connect(frogpilotAdvancedVisualsPanel, &FrogPilotAdvancedVisualsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
std::vector<QString> toggle_presets{tr("Basic"), tr("Standard"), tr("Advanced")};
ButtonParamControl *toggle_preset = new ButtonParamControl("CustomizationLevel", tr("Customization Level"),
tr("Choose your preferred customization level. 'Standard' is recommended for most users, offering a balanced experience and automatically managing more 'Advanced' features,"
" while 'Basic' is designed for those new to customization or seeking simplicity."),
"../frogpilot/assets/toggle_icons/icon_customization.png",
toggle_presets);
QObject::connect(toggle_preset, &ButtonParamControl::buttonClicked, this, &FrogPilotSettingsWindow::updatePanelVisibility);
QObject::connect(toggle_preset, &ButtonParamControl::buttonClicked, this, &updateFrogPilotToggles);
list->addItem(toggle_preset);
FrogPilotDevicePanel *frogpilotDevicePanel = new FrogPilotDevicePanel(this);
QObject::connect(frogpilotDevicePanel, &FrogPilotDevicePanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
@ -63,10 +64,15 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
FrogPilotModelPanel *frogpilotModelPanel = new FrogPilotModelPanel(this);
QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
FrogPilotMapsPanel *frogpilotMapsPanel = new FrogPilotMapsPanel(this);
QObject::connect(frogpilotMapsPanel, &FrogPilotMapsPanel::openMapSelection, this, &FrogPilotSettingsWindow::openMapSelection);
FrogPilotPrimelessPanel *frogpilotPrimelessPanel = new FrogPilotPrimelessPanel(this);
QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::closeMapBoxInstructions, this, &FrogPilotSettingsWindow::closeMapBoxInstructions);
QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::openMapBoxInstructions, this, &FrogPilotSettingsWindow::openMapBoxInstructions);
FrogPilotSoundsPanel *frogpilotSoundsPanel = new FrogPilotSoundsPanel(this);
@ -79,9 +85,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
QObject::connect(frogpilotVisualsPanel, &FrogPilotVisualsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
std::vector<std::pair<QString, std::vector<QWidget*>>> panels = {
{tr("Advanced Settings"), {frogpilotAdvancedDrivingPanel, frogpilotAdvancedVisualsPanel}},
{tr("Alerts and Sounds"), {frogpilotSoundsPanel}},
{tr("Driving Controls"), {frogpilotLongitudinalPanel, frogpilotLateralPanel}},
{tr("Driving Controls"), {frogpilotModelPanel, frogpilotLongitudinalPanel, frogpilotLateralPanel}},
{tr("Navigation"), {frogpilotMapsPanel, frogpilotPrimelessPanel}},
{tr("System Management"), {new FrogPilotDataPanel(this), frogpilotDevicePanel, new UtilitiesPanel(this)}},
{tr("Theme and Appearance"), {frogpilotVisualsPanel, frogpilotThemesPanel}},
@ -89,7 +94,6 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
};
std::vector<QString> icons = {
"../frogpilot/assets/toggle_icons/icon_advanced.png",
"../frogpilot/assets/toggle_icons/icon_sound.png",
"../frogpilot/assets/toggle_icons/icon_steering.png",
"../frogpilot/assets/toggle_icons/icon_map.png",
@ -99,7 +103,6 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
};
std::vector<QString> descriptions = {
tr("Advanced FrogPilot features for more experienced users."),
tr("Options to customize FrogPilot's sound alerts and notifications."),
tr("FrogPilot features that impact acceleration, braking, and steering."),
tr("Offline maps downloader and 'Navigate On openpilot (NOO)' settings."),
@ -109,9 +112,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
};
std::vector<std::vector<QString>> buttonLabels = {
{tr("DRIVING"), tr("VISUALS")},
{tr("MANAGE")},
{tr("GAS / BRAKE"), tr("STEERING")},
{tr("DRIVING MODEL"), tr("GAS / BRAKE"), tr("STEERING")},
{tr("OFFLINE MAPS"), tr("PRIMELESS NAVIGATION")},
{tr("DATA"), tr("DEVICE"), tr("UTILITIES")},
{tr("APPEARANCE"), tr("THEME")},
@ -119,7 +121,7 @@ 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, panels[i].first == tr("Driving Controls"), panels[i].first == tr("Navigation"));
addPanelControl(list, panels[i].first, descriptions[i], buttonLabels[i], icons[i], panels[i].second, panels[i].first);
}
frogpilotSettingsLayout->addWidget(new ScrollView(list, frogpilotSettingsWidget));
@ -133,11 +135,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
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);
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotSettingsWindow::updateMetric);
QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateCarVariables);
updateCarVariables();
}
void FrogPilotSettingsWindow::showEvent(QShowEvent *event) {
@ -152,10 +151,18 @@ void FrogPilotSettingsWindow::closePanel() {
}
void FrogPilotSettingsWindow::updatePanelVisibility() {
customizationLevel = params.getInt("CustomizationLevel");
disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal");
drivingButton->setVisibleButton(0, hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal);
if ((hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal) || customizationLevel != 0) {
drivingButton->setVisibleButton(0, customizationLevel == 2);
drivingButton->setVisibleButton(1, hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal);
drivingButton->setVisibleButton(2, customizationLevel != 0);
} else {
drivingButton->setVisible(false);
}
navigationButton->setVisibleButton(1, !uiState()->hasPrime());
systemButton->setVisibleButton(1, customizationLevel != 0);
mainLayout->setCurrentWidget(frogpilotSettingsWidget);
}
@ -183,7 +190,9 @@ void FrogPilotSettingsWindow::updateCarVariables() {
hasNNFFLog = checkNNFFLogFileExists(carFingerprint);
hasOpenpilotLongitudinal = hasLongitudinalControl(CP);
hasPCMCruise = CP.getPcmCruise();
hasRadar = !CP.getRadarUnavailable();
hasSNG = CP.getMinEnableSpeed() <= 0;
isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV";
isGM = carModel == "gm";
isGMPCMCruise = CP.getCarName() == "gm" && CP.getPcmCruise();
isHKGCanFd = carModel == "hyundai" && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
@ -236,17 +245,21 @@ void FrogPilotSettingsWindow::updateCarVariables() {
hasNNFFLog = true;
hasOpenpilotLongitudinal = true;
hasPCMCruise = true;
hasRadar = true;
hasSNG = false;
isGM = true;
isGMPCMCruise = false;
isHKGCanFd = true;
isImpreza = true;
isSubaru = false;
isPIDCar = false;
isSubaru = true;
isToyota = true;
isToyotaTuneSupported = true;
isVolt = true;
}
std::string liveTorqueParamsKey;
if (params.getBool("ModelManagement")) {
if (customizationLevel == 2) {
QString model = QString::fromStdString(params.get("ModelName"));
QString part_model_param = processModelName(model);
liveTorqueParamsKey = part_model_param.toStdString() + "LiveTorqueParameters";
@ -255,13 +268,13 @@ void FrogPilotSettingsWindow::updateCarVariables() {
}
if (params.checkKey(liveTorqueParamsKey)) {
auto torqueParams = params.get(liveTorqueParamsKey);
std::string torqueParams = params.get(liveTorqueParamsKey);
if (!torqueParams.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(torqueParams.data(), torqueParams.size()));
cereal::Event::Reader LTP = cmsg.getRoot<cereal::Event>();
auto liveTorqueParams = LTP.getLiveTorqueParameters();
cereal::LiveTorqueParametersData::Reader liveTorqueParams = LTP.getLiveTorqueParameters();
liveValid = liveTorqueParams.getLiveValid();
} else {
@ -274,7 +287,7 @@ void FrogPilotSettingsWindow::updateCarVariables() {
emit updateCarToggles();
}
void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector<QString> &button_labels, QString &icon, std::vector<QWidget*> &panels, bool isDrivingPanel, bool isNavigationPanel) {
void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector<QString> &button_labels, QString &icon, std::vector<QWidget*> &panels, QString &currentPanel) {
std::vector<QWidget*> panelContainers;
panelContainers.reserve(panels.size());
@ -287,12 +300,15 @@ void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString
}
FrogPilotButtonsControl *button;
if (isDrivingPanel) {
if (currentPanel == tr("Driving Controls")) {
drivingButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon);
button = drivingButton;
} else if (isNavigationPanel) {
} else if (currentPanel == tr("Navigation")) {
navigationButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon);
button = navigationButton;
} else if (currentPanel == tr("System Management")) {
systemButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon);
button = systemButton;
} else {
button = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon);
}

View File

@ -16,7 +16,9 @@ public:
bool hasNNFFLog;
bool hasOpenpilotLongitudinal;
bool hasPCMCruise;
bool hasRadar;
bool hasSNG;
bool isBolt;
bool isGM;
bool isGMPCMCruise;
bool isHKGCanFd;
@ -34,23 +36,23 @@ public:
float steerLatAccelStock;
float steerRatioStock;
int customizationLevel;
signals:
void closeMapBoxInstructions();
void closeMapSelection();
void closeParentToggle();
void closeSubParentToggle();
void closeSubSubParentToggle();
void openMapBoxInstructions();
void openMapSelection();
void openPanel();
void openParentToggle();
void openSubParentToggle();
void openSubSubParentToggle();
void updateCarToggles();
void updateMetric();
private:
void addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector<QString> &button_labels, QString &icon, std::vector<QWidget*> &panels, bool isDrivingPanel, bool isNavigationPanel);
void addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector<QString> &button_labels, QString &icon, std::vector<QWidget*> &panels, QString &currentPanel);
void closePanel();
void showEvent(QShowEvent *event) override;
void updateCarVariables();
@ -58,6 +60,7 @@ private:
FrogPilotButtonsControl *drivingButton;
FrogPilotButtonsControl *navigationButton;
FrogPilotButtonsControl *systemButton;
Params params;

View File

@ -2,22 +2,32 @@
FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> lateralToggles {
{"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced settings for fine tuning openpilot's lateral controls."), "../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("Adjusts 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("Adjusts 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("Adjusts 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("Adjusts 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."), ""},
{"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."), ""},
{"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."), ""},
{"AlwaysOnLateralLKAS", tr("Control with LKAS Button"), tr("Controls the current state of 'Always on Lateral' with the 'LKAS' button."), ""},
{"AlwaysOnLateralMain", tr("Enable with Cruise Control"), tr("Activates 'Always on Lateral' whenever 'Cruise Control' is active bypassing the requirement to enable openpilot first."), ""},
{"PauseAOLOnBrake", tr("Pause on Brake Below"), tr("Pauses 'Always on Lateral' when the brake pedal is pressed below the set speed."), ""},
{"HideAOLStatusBar", tr("Hide the Status Bar"), tr("Hides status bar for 'Always On Lateral'."), ""},
{"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."), ""},
{"NudgelessLaneChange", tr("Automatic Lane Changes"), tr("Conducts lane changes without needing to touch the steering wheel upon turn signal activation."), ""},
{"LaneChangeTime", tr("Lane Change Delay"), tr("Delays lane changes by the set time to prevent sudden changes."), ""},
{"LaneDetectionWidth", tr("Lane Width Requirement"), tr("Sets the minimum lane width for openpilot to detect a lane as a lane."), ""},
{"MinimumLaneChangeSpeed", tr("Minimum Speed for Lane Change"), tr("Sets the minimum speed required for openpilot to perform a lane change."), ""},
{"OneLaneChange", tr("Only One Lane Change Per Signal"), tr("Limits lane changes 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."), ""},
{"LateralTune", tr("Lateral Tuning"), tr("Settings for fine tuning openpilot's lateral controls."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"},
{"TacoTune", tr("comma's 2022 Taco Bell Turn Hack"), tr("Uses comma's hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive."), ""},
{"TurnDesires", tr("Force Turn Desires Below Lane Change Speed"), tr("Forces the model to use turn desires when driving below the minimum lane change speed to help make left and right turns more precisely."), ""},
{"NNFF", tr("Neural Network Feedforward (NNFF)"), tr("Uses Twilsonco's 'Neural Network FeedForward' for more precise steering control."), ""},
{"NNFFLite", tr("Smooth Curve Handling"), tr("Smoothens the steering control when entering and exiting curves by using 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."), ""}
@ -26,7 +36,50 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
for (const auto &[param, title, desc, icon] : lateralToggles) {
AbstractControl *lateralToggle;
if (param == "AlwaysOnLateral") {
if (param == "AdvancedLateralTune") {
FrogPilotParamManageControl *advancedLateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(advancedLateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedAdvancedLateralTuneKeys = advancedLateralTuneKeys;
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
if (usingNNFF) {
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune");
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
} else {
if (hasAutoTune) {
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune");
} else if (isPIDCar) {
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
} else {
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
}
}
if (!liveValid || usingNNFF) {
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
}
showToggles(modifiedAdvancedLateralTuneKeys);
});
lateralToggle = advancedLateralTuneToggle;
} else if (param == "SteerFriction") {
std::vector<QString> steerFrictionToggleNames{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map<int, QString>(), 0.01, {}, steerFrictionToggleNames, false, false);
} else if (param == "SteerKP") {
std::vector<QString> steerKPToggleNames{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKPStock * 0.50, steerKPStock * 1.50, QString(), std::map<int, QString>(), 0.01, {}, steerKPToggleNames, false, false);
} else if (param == "SteerLatAccel") {
std::vector<QString> steerLatAccelToggleNames{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerLatAccelStock * 0.25, steerLatAccelStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerLatAccelToggleNames, false, false);
} else if (param == "SteerRatio") {
std::vector<QString> steerRatioToggleNames{"Reset"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerRatioToggleNames, false, false);
} else if (param == "AlwaysOnLateral") {
FrogPilotParamManageControl *aolToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(aolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedAOLKeys = aolKeys;
@ -35,12 +88,37 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
modifiedAOLKeys.erase("AlwaysOnLateralLKAS");
}
if (customizationLevel != 2) {
modifiedAOLKeys.erase("AlwaysOnLateralMain");
modifiedAOLKeys.erase("HideAOLStatusBar");
}
showToggles(modifiedAOLKeys);
});
lateralToggle = aolToggle;
} else if (param == "PauseAOLOnBrake") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} else if (param == "LaneChangeCustomizations") {
FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
std::set<QString> modifiedLaneChangeKeys = laneChangeKeys;
if (customizationLevel != 2) {
modifiedLaneChangeKeys.erase("LaneDetectionWidth");
modifiedLaneChangeKeys.erase("MinimumLaneChangeSpeed");
}
showToggles(modifiedLaneChangeKeys);
});
lateralToggle = laneChangeToggle;
} else if (param == "LaneChangeTime") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 5, tr(" seconds"), {{0, "Instant"}, {10, "1.0 second"}}, 0.1);
} else if (param == "LaneDetectionWidth") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"), std::map<int, QString>(), 0.1);
} else if (param == "MinimumLaneChangeSpeed") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} else if (param == "LateralTune") {
FrogPilotParamManageControl *lateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(lateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
@ -53,6 +131,11 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
modifiedLateralTuneKeys.erase("NNFFLite");
}
if (customizationLevel != 2) {
modifiedLateralTuneKeys.erase("TacoTune");
modifiedLateralTuneKeys.erase("TurnDesires");
}
showToggles(modifiedLateralTuneKeys);
});
lateralToggle = lateralTuneToggle;
@ -66,27 +149,10 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
} else if (param == "PauseLateralSpeed") {
std::vector<QString> pauseLateralToggles{"PauseLateralOnSignal"};
std::vector<QString> pauseLateralToggleNames{"Turn Signal Only"};
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1, pauseLateralToggles, pauseLateralToggleNames);
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1, pauseLateralToggles, pauseLateralToggleNames, true);
} else if (param == "PauseLateralOnSignal") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} else if (param == "LaneChangeCustomizations") {
FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(laneChangeKeys);
});
lateralToggle = laneChangeToggle;
} else if (param == "LaneChangeTime") {
std::map<int, QString> laneChangeTimeLabels;
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);
} else if (param == "LaneDetectionWidth") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"), std::map<int, QString>(), 0.1);
} else if (param == "MinimumLaneChangeSpeed") {
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} else {
lateralToggle = new ParamControl(param, title, desc, icon);
}
@ -141,6 +207,34 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
});
}
steerFrictionToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerFriction"]);
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerFriction", steerFrictionStock);
steerFrictionToggle->refresh();
updateFrogPilotToggles();
});
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerKP", steerKPStock);
steerKPToggle->refresh();
updateFrogPilotToggles();
});
steerLatAccelToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerLatAccel"]);
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerLatAccel", steerLatAccelStock);
steerLatAccelToggle->refresh();
updateFrogPilotToggles();
});
steerRatioToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerRatio"]);
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
params.putFloat("SteerRatio", steerRatioStock);
steerRatioToggle->refresh();
updateFrogPilotToggles();
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLateralPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotLateralPanel::updateCarToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLateralPanel::updateMetric);
@ -149,10 +243,30 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
updateMetric();
}
void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
customizationLevel = parent->customizationLevel;
toggles["AdvancedLateralTune"]->setVisible(customizationLevel == 2);
}
void FrogPilotLateralPanel::updateCarToggles() {
hasAutoTune = parent->hasAutoTune;
hasNNFFLog = parent->hasNNFFLog;
isPIDCar = parent->isPIDCar;
isSubaru = parent->isSubaru;
liveValid = parent->liveValid;
steerFrictionStock = parent->steerFrictionStock;
steerKPStock = parent->steerKPStock;
steerLatAccelStock = parent->steerLatAccelStock;
steerRatioStock = parent->steerRatioStock;
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)));
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)));
steerKPToggle->updateControl(steerKPStock * 0.50, steerKPStock * 1.50);
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2)));
steerLatAccelToggle->updateControl(steerLatAccelStock * 0.75, steerLatAccelStock * 1.25);
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)));
steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25);
hideToggles();
}
@ -214,7 +328,8 @@ void FrogPilotLateralPanel::hideToggles() {
setUpdatesEnabled(false);
for (auto &[key, toggle] : toggles) {
bool subToggles = aolKeys.find(key) != aolKeys.end() ||
bool subToggles = advancedLateralTuneKeys.find(key) != advancedLateralTuneKeys.end() ||
aolKeys.find(key) != aolKeys.end() ||
laneChangeKeys.find(key) != laneChangeKeys.end() ||
lateralTuneKeys.find(key) != lateralTuneKeys.end() ||
qolKeys.find(key) != qolKeys.end();
@ -222,6 +337,8 @@ void FrogPilotLateralPanel::hideToggles() {
toggle->setVisible(!subToggles);
}
toggles["AdvancedLateralTune"]->setVisible(customizationLevel == 2);
setUpdatesEnabled(true);
update();
}

View File

@ -15,30 +15,41 @@ signals:
private:
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set<QString> &keys);
void updateMetric();
void updateCarToggles();
void updateState(const UIState &s);
std::set<QString> advancedLateralTuneKeys = {
"ForceAutoTune", "ForceAutoTuneOff", "SteerFriction",
"SteerLatAccel", "SteerKP", "SteerRatio"
};
std::set<QString> aolKeys = {
"AlwaysOnLateralLKAS", "AlwaysOnLateralMain",
"HideAOLStatusBar", "PauseAOLOnBrake"
"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "HideAOLStatusBar",
"PauseAOLOnBrake"
};
std::set<QString> laneChangeKeys = {
"LaneChangeTime", "LaneDetectionWidth",
"MinimumLaneChangeSpeed", "NudgelessLaneChange",
"OneLaneChange"
"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed",
"NudgelessLaneChange", "OneLaneChange"
};
std::set<QString> lateralTuneKeys = {
"NNFF", "NNFFLite"
"NNFF", "NNFFLite", "TacoTune",
"TurnDesires"
};
std::set<QString> qolKeys = {
"PauseLateralSpeed"
};
FrogPilotParamValueButtonControl *steerFrictionToggle;
FrogPilotParamValueButtonControl *steerLatAccelToggle;
FrogPilotParamValueButtonControl *steerKPToggle;
FrogPilotParamValueButtonControl *steerRatioToggle;
FrogPilotSettingsWindow *parent;
Params params;
@ -46,8 +57,17 @@ private:
bool hasAutoTune;
bool hasNNFFLog;
bool isMetric = params.getBool("IsMetric");
bool isPIDCar;
bool isSubaru;
bool liveValid;
bool started;
float steerFrictionStock;
float steerLatAccelStock;
float steerKPStock;
float steerRatioStock;
int customizationLevel;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -2,68 +2,169 @@
FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> longitudinalToggles {
{"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."), ""},
{"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switch to 'Experimental Mode' when specific conditions are met."), "../frogpilot/assets/toggle_icons/icon_conditional.png"},
{"CESpeed", tr("Below"), tr("Triggers 'Experimental Mode' when driving below the set speed without a lead vehicle."), ""},
{"CECurves", tr("Curve Detected Ahead"), tr("Triggers 'Experimental Mode' when a curve is detected in the road ahead."), ""},
{"CELead", tr("Lead Detected Ahead"), tr("Triggers 'Experimental Mode' when a slower or stopped vehicle is detected ahead."), ""},
{"CENavigation", tr("Navigation Data"), tr("Triggers 'Experimental Mode' based on navigation data, such as upcoming intersections or turns."), ""},
{"CEModelStopTime", tr("openpilot Wants to Stop In"), tr("Triggers 'Experimental Mode' when openpilot wants to stop such as for a stop sign or red light."), ""},
{"CESignalSpeed", tr("Turn Signal Below"), tr("Triggers 'Experimental Mode' when using turn signals below the set speed."), ""},
{"HideCEMStatusBar", tr("Hide the Status Bar"), tr("Hides status bar for 'Conditional Experimental Mode'."), ""},
{"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."), ""},
{"CurveDetectionMethod", tr("Curve Detection Method"), tr("Uses data from either the downloaded maps or the model to determine where curves are."), ""},
{"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Triggers 'Curve Speed Control' only when a curve is detected with the model as well when using the 'Map Based' method."), ""},
{"CurveSensitivity", tr("Curve Detection Sensitivity"), tr("Controls 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("Speed Aggressiveness"), tr("Controls how aggressive openpilot takes turns. Higher values result in faster turns, while lower values result in slower 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."), ""},
{"CustomPersonalities", tr("Customize Driving Personalities"), tr("Customize the personality profiles to suit your driving style."), "../frogpilot/assets/toggle_icons/icon_personality.png"},
{"TrafficPersonalityProfile", tr("Traffic Personality"), tr("Customizes the 'Traffic' personality profile, tailored for navigating through traffic."), "../frogpilot/assets/stock_theme/distance_icons/traffic.png"},
{"TrafficFollow", tr("Following Distance"), tr("Controls the minimum following distance in 'Traffic' mode. openpilot will automatically dynamically between this value and the 'Aggressive' profile distance based on your current speed."), ""},
{"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Traffic' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel 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 deceleration, while lower values allow for quicker, more responsive changes 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 increases speed in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
{"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
{"ResetTrafficPersonality", tr("Reset Settings"), tr("Restores 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("Sets 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 changes in acceleration in 'Aggressive' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
{"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Aggressive' mode. Higher values result in smoother, more gradual deceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
{"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Aggressive' 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.\n\nDefault: 1.0."), ""},
{"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
{"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
{"ResetAggressivePersonality", tr("Reset Settings"), tr("Restores 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 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 changes in acceleration in 'Standard' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in 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 other vehicles or obstacles in 'Standard' 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.\n\nDefault: 1.0."), ""},
{"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"ResetStandardPersonality", tr("Reset Settings"), tr("Restores 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 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 changes in acceleration in 'Relaxed' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in 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 other vehicles or obstacles in 'Relaxed' 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.\n\nDefault: 1.0."), ""},
{"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
{"ResetRelaxedPersonality", tr("Reset Settings"), tr("Restores the 'Relaxed' settings to their default values."), ""},
{"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("Toggle 'Experimental Mode' on/off using either the steering wheel buttons or screen.\n\nThis overrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"},
{"ExperimentalModeViaLKAS", tr("Click the LKAS Button"), tr("Toggles 'Experimental Mode' by pressing the 'LKAS' button on the steering wheel."), ""},
{"ExperimentalModeViaTap", tr("Double-Tap the Screen"), tr("Toggles 'Experimental Mode' by double-tapping the onroad UI within a 0.5 second period."), ""},
{"ExperimentalModeViaDistance", tr("Long Press the Distance Button"), tr("Toggles 'Experimental Mode' by holding down 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."), ""},
{"AccelerationProfile", tr("Acceleration Profile"), tr("Enables either a sporty or eco-friendly acceleration rate. 'Sport+' aims to make openpilot accelerate as fast as possible."), ""},
{"DecelerationProfile", tr("Deceleration Profile"), tr("Enables either 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 more 'human-like' driving experience."), ""},
{"HumanFollowing", tr("Human-Like Following Distance"), tr("Dynamically adjusts the following distance when approaching slower or stopped vehicles for a more 'human-like' driving experience."), ""},
{"IncreasedStoppedDistance", tr("Increase Stopped Distance"), tr("Increases the distance to stop behind vehicles."), ""},
{"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("Controls how sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but increases the chance openpilot mistakes other objects for vehicles."), ""},
{"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Sets a cap on how fast openpilot can accelerate."), ""},
{"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."), ""},
{"CustomCruise", tr("Cruise Increase Interval"), tr("Controls the interval used when increasing the cruise control speed."), ""},
{"CustomCruiseLong", tr("Custom Cruise Interval (Long Press)"), tr("Controls the interval used when increasing the cruise control speed while holding down the button for 0.5+ seconds."), ""},
{"ForceStandstill", tr("Force Keep openpilot in the 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("Forces a stop whenever openpilot 'detects' a potential red light/stop sign to prevent it from running the red light/stop sign."), ""},
{"SetSpeedOffset", tr("Set Speed Offset"), tr("Controls 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."), ""},
{"MapGears", tr("Map Accel/Decel to Gears"), tr("Maps the acceleration and deceleration profiles to your car's 'Eco' or 'Sport' gear modes."), ""},
{"ReverseCruise", tr("Reverse Cruise Increase"), tr("Reverses the long press cruise increase feature to increase the max speed by 5 mph instead of 1 on short presses."), ""},
{"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."), ""},
{"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust your max speed to match the speed limit using downloaded 'Open Street Maps' data, '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("Enables manual confirmations before using a new speed limit."), ""},
{"SLCFallback", tr("Fallback Method"), tr("Controls what happens when no speed limit data is available."), ""},
{"SLCOverride", tr("Override Method"), tr("Controls how the current speed limit is overriden.\n\n"), ""},
{"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."), ""},
{"ForceMPHDashboard", tr("Force MPH Readings from Dashboard"), tr("Forces speed limit readings from the dashboard to MPH if it normally displays them in KPH."), ""},
{"SLCLookaheadHigher", tr("Prepare for Higher Speed Limits"), tr("Sets a lookahead value to prepare for upcoming higher speed limits when using downloaded map data."), ""},
{"SLCLookaheadLower", tr("Prepare for Lower Speed Limits"), tr("Sets a lookahead value to prepare for upcoming lower speed limits when using downloaded map data."), ""},
{"SetSpeedLimit", tr("Set Speed to Current Limit"), tr("Sets your max speed to match the current speed limit when enabling openpilot."), ""},
{"SLCPriority", tr("Speed Limit Source Priority"), tr("Sets the order of priority for speed limit data sources."), ""},
{"SLCOffsets", tr("Speed Limit Offsets"), tr("Set speed limit offsets to drive over the posted speed limit."), ""},
{"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Sets the speed limit offset for speeds between 0 and 34 mph."), ""},
{"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Sets the speed limit offset for speeds between 35 and 54 mph."), ""},
{"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Sets the speed limit offset for speeds between 55 and 64 mph."), ""},
{"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Sets the speed limit offset for speeds between 65 and 99 mph."), ""},
};
for (const auto &[param, title, desc, icon] : longitudinalToggles) {
AbstractControl *longitudinalToggle;
if (param == "ConditionalExperimental") {
if (param == "CustomPersonalities") {
FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(customDrivingPersonalityKeys);
});
longitudinalToggle = customPersonalitiesToggle;
} else if (param == "ResetTrafficPersonality" || param == "ResetAggressivePersonality" || param == "ResetStandardPersonality" || param == "ResetRelaxedPersonality") {
FrogPilotButtonsControl *profileBtn = new FrogPilotButtonsControl(title, desc, {tr("Reset")});
longitudinalToggle = profileBtn;
} else if (param == "TrafficPersonalityProfile") {
FrogPilotParamManageControl *trafficPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(trafficPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(trafficPersonalityKeys);
});
longitudinalToggle = trafficPersonalityToggle;
} else if (param == "AggressivePersonalityProfile") {
FrogPilotParamManageControl *aggressivePersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(aggressivePersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(aggressivePersonalityKeys);
});
longitudinalToggle = aggressivePersonalityToggle;
} else if (param == "StandardPersonalityProfile") {
FrogPilotParamManageControl *standardPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(standardPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(standardPersonalityKeys);
});
longitudinalToggle = standardPersonalityToggle;
} else if (param == "RelaxedPersonalityProfile") {
FrogPilotParamManageControl *relaxedPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(relaxedPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
customPersonalityOpen = true;
openSubParentToggle();
showToggles(relaxedPersonalityKeys);
});
longitudinalToggle = relaxedPersonalityToggle;
} else if (trafficPersonalityKeys.find(param) != trafficPersonalityKeys.end() ||
aggressivePersonalityKeys.find(param) != aggressivePersonalityKeys.end() ||
standardPersonalityKeys.find(param) != standardPersonalityKeys.end() ||
relaxedPersonalityKeys.find(param) != relaxedPersonalityKeys.end()) {
if (param == "TrafficFollow" || param == "AggressiveFollow" || param == "StandardFollow" || param == "RelaxedFollow") {
if (param == "TrafficFollow") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.5, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
} else {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
}
} else {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 500, "%");
}
} else if (param == "ConditionalExperimental") {
FrogPilotParamManageControl *conditionalExperimentalToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(conditionalExperimentalToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(conditionalExperimentalKeys);
std::set<QString> modifiedConditionalExperimentalKeys = conditionalExperimentalKeys;
if (customizationLevel != 2) {
modifiedConditionalExperimentalKeys.erase("CENavigation");
modifiedConditionalExperimentalKeys.erase("CESignalSpeed");
modifiedConditionalExperimentalKeys.erase("HideCEMStatusBar");
}
showToggles(modifiedConditionalExperimentalKeys);
});
longitudinalToggle = conditionalExperimentalToggle;
} else if (param == "CESpeed") {
@ -84,15 +185,11 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
std::vector<QString> navigationToggleNames{tr("Intersections"), tr("Turns"), tr("With Lead")};
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, navigationToggles, navigationToggleNames);
} else if (param == "CEModelStopTime") {
std::map<int, QString> modelStopTimeLabels;
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);
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" seconds"), {{0, "Off"}});
} else if (param == "CESignalSpeed") {
std::vector<QString> ceSignalToggles{"CESignalLaneDetection"};
std::vector<QString> ceSignalToggleNames{"Lane Detection"};
longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1.0, ceSignalToggles, ceSignalToggleNames);
std::vector<QString> ceSignalToggleNames{"Only For Detected Lanes"};
longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1.0, ceSignalToggles, ceSignalToggleNames, true);
} else if (param == "CurveSpeedControl") {
FrogPilotParamManageControl *curveControlToggle = new FrogPilotParamManageControl(param, title, desc, icon);
@ -107,6 +204,12 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck");
}
if (customizationLevel != 2) {
modifiedCurveSpeedKeys.erase("CurveSensitivity");
modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck");
modifiedCurveSpeedKeys.erase("TurnAggressiveness");
}
showToggles(modifiedCurveSpeedKeys);
});
longitudinalToggle = curveControlToggle;
@ -162,19 +265,38 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
} else if (param == "LongitudinalTune") {
FrogPilotParamManageControl *longitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(longitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(longitudinalTuneKeys);
std::set<QString> modifiedLongitudinalTuneKeys = longitudinalTuneKeys;
if (customizationLevel == 0) {
modifiedLongitudinalTuneKeys.erase("HumanAcceleration");
modifiedLongitudinalTuneKeys.erase("HumanFollowing");
modifiedLongitudinalTuneKeys.erase("IncreasedStoppedDistance");
modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold");
modifiedLongitudinalTuneKeys.erase("MaxDesiredAcceleration");
} else if (customizationLevel == 1) {
modifiedLongitudinalTuneKeys.erase("HumanAcceleration");
modifiedLongitudinalTuneKeys.erase("HumanFollowing");
modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold");
modifiedLongitudinalTuneKeys.erase("MaxDesiredAcceleration");
}
showToggles(modifiedLongitudinalTuneKeys);
});
longitudinalToggle = longitudinalTuneToggle;
} else if (param == "AccelerationProfile") {
std::vector<QString> profileOptions{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")};
ButtonParamControl *profileSelection = new ButtonParamControl(param, title, desc, icon, profileOptions);
longitudinalToggle = profileSelection;
std::vector<QString> accelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")};
ButtonParamControl *accelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, accelerationProfiles);
longitudinalToggle = accelerationProfileToggle;
} else if (param == "DecelerationProfile") {
std::vector<QString> profileOptions{tr("Standard"), tr("Eco"), tr("Sport")};
ButtonParamControl *profileSelection = new ButtonParamControl(param, title, desc, icon, profileOptions);
longitudinalToggle = profileSelection;
std::vector<QString> decelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport")};
ButtonParamControl *decelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, decelerationProfiles);
longitudinalToggle = decelerationProfileToggle;
} else if (param == "IncreasedStoppedDistance") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"));
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"));
} else if (param == "LeadDetectionThreshold") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, "%");
} else if (param == "MaxDesiredAcceleration") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.1, 4.0, "m/s", std::map<int, QString>(), 0.1);
} else if (param == "QOLLongitudinal") {
FrogPilotParamManageControl *qolLongitudinalToggle = new FrogPilotParamManageControl(param, title, desc, icon);
@ -186,12 +308,20 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
} else {
modifiedQolKeys.erase("CustomCruise");
modifiedQolKeys.erase("CustomCruiseLong");
modifiedQolKeys.erase("SetSpeedOffset");
}
if (!isToyota && !isGM && !isHKGCanFd) {
if (!(isGM || isHKGCanFd || isToyota)) {
modifiedQolKeys.erase("MapGears");
}
if (customizationLevel != 2) {
modifiedQolKeys.erase("ForceStandstill");
modifiedQolKeys.erase("ForceStops");
modifiedQolKeys.erase("ReverseCruise");
modifiedQolKeys.erase("SetSpeedOffset");
}
showToggles(modifiedQolKeys);
});
longitudinalToggle = qolLongitudinalToggle;
@ -203,58 +333,39 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
std::vector<QString> mapGearsToggles{"MapAcceleration", "MapDeceleration"};
std::vector<QString> mapGearsToggleNames{tr("Acceleration"), tr("Deceleration")};
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, mapGearsToggles, mapGearsToggleNames);
} else if (param == "SetSpeedOffset") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
} 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");
std::set<QString> modifiedSpeedLimitControllerKeys = speedLimitControllerKeys;
slcConfirmationBtn->setCheckedButton(0, slcLower);
slcConfirmationBtn->setCheckedButton(1, slcHigher);
slcConfirmationBtn->setCheckedButton(2, !(slcLower || slcHigher));
if (customizationLevel == 0) {
modifiedSpeedLimitControllerKeys.erase("SLCFallback");
modifiedSpeedLimitControllerKeys.erase("SLCOverride");
modifiedSpeedLimitControllerKeys.erase("SLCPriority");
modifiedSpeedLimitControllerKeys.erase("SLCQOL");
} else if (customizationLevel != 2) {
modifiedSpeedLimitControllerKeys.erase("SLCPriority");
modifiedSpeedLimitControllerKeys.erase("SLCQOL");
}
showToggles(modifiedSpeedLimitControllerKeys);
slcOpen = true;
showToggles(speedLimitControllerKeys);
});
longitudinalToggle = speedLimitControllerToggle;
} 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 (id == 0) {
params.putBool("SLCConfirmationLower", !lowerEnabled);
slcConfirmationBtn->setCheckedButton(0, !lowerEnabled);
slcConfirmationBtn->setCheckedButton(2, false);
if (lowerEnabled & !higherEnabled) {
slcConfirmationBtn->setCheckedButton(2, true);
}
} else if (id == 1) {
params.putBool("SLCConfirmationHigher", !higherEnabled);
slcConfirmationBtn->setCheckedButton(1, !higherEnabled);
slcConfirmationBtn->setCheckedButton(2, false);
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 = slcConfirmationBtn;
std::vector<QString> confirmationToggles{"SLCConfirmationLower", "SLCConfirmationHigher"};
std::vector<QString> confirmationToggleNames{tr("Lower Limits"), tr("Higher Limits")};
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, confirmationToggles, confirmationToggleNames);
} else if (param == "SLCFallback") {
std::vector<QString> 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<QString> overrideOptions{tr("None"), tr("Gas Pedal Press"), tr("Cruise Set Speed")};
std::vector<QString> overrideOptions{tr("None"), tr("Set With Gas Pedal"), tr("Max Set Speed")};
ButtonParamControl *overrideSelection = new ButtonParamControl(param, title, desc, icon, overrideOptions);
longitudinalToggle = overrideSelection;
} else if (param == "SLCPriority") {
@ -269,7 +380,8 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
for (int i = 1; i <= 3; ++i) {
QStringList currentPriorities = (i == 1) ? primaryPriorities : secondaryTertiaryPriorities;
QStringList prioritiesToDisplay = currentPriorities;
for (const auto &selectedPriority : qAsConst(selectedPriorities)) {
for (QString selectedPriority : qAsConst(selectedPriorities)) {
prioritiesToDisplay.removeAll(selectedPriority);
}
@ -284,12 +396,24 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
QString priorityKey = QString("SLCPriority%1").arg(i);
QString selection = MultiOptionDialog::getSelection(priorityPrompts[i - 1], prioritiesToDisplay, "", this);
if (selection.isEmpty()) break;
if (selection.isEmpty()) {
break;
}
params.putNonBlocking(priorityKey.toStdString(), selection.toStdString());
selectedPriorities.append(selection);
if (selection == tr("Lowest") || selection == tr("Highest") || selection == tr("None")) break;
if (selection == tr("None")) {
for (int j = i + 1; j <= 3; ++j) {
QString priorityKeyNext = QString("SLCPriority%1").arg(j);
params.putNonBlocking(priorityKeyNext.toStdString(), "None");
}
break;
}
if (selection == tr("Lowest") || selection == tr("Highest")) {
break;
}
updateFrogPilotToggles();
}
@ -335,19 +459,8 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
longitudinalToggle = reinterpret_cast<AbstractControl*>(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<AbstractControl*>(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<QString> slcOffsetToggles{"ShowSLCOffsetUI"};
std::vector<QString> slcOffsetToggleNames{tr("Control Via UI")};
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, slcOffsetToggles, slcOffsetToggleNames);
} else {
longitudinalToggle = new ParamControl(param, title, desc, icon);
@ -373,6 +486,106 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
}
});
FrogPilotParamValueControl *trafficFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficFollow"]);
FrogPilotParamValueControl *trafficAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkAcceleration"]);
FrogPilotParamValueControl *trafficDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDeceleration"]);
FrogPilotParamValueControl *trafficDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDanger"]);
FrogPilotParamValueControl *trafficSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeed"]);
FrogPilotParamValueControl *trafficSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeedDecrease"]);
FrogPilotButtonsControl *trafficResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetTrafficPersonality"]);
QObject::connect(trafficResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for 'Traffic Mode'?"), this)) {
params.putFloat("TrafficFollow", 0.5);
params.putFloat("TrafficJerkAcceleration", 50);
params.putFloat("TrafficJerkDeceleration", 50);
params.putFloat("TrafficJerkDanger", 100);
params.putFloat("TrafficJerkSpeed", 50);
params.putFloat("TrafficJerkSpeedDecrease", 50);
trafficFollowToggle->refresh();
trafficAccelerationToggle->refresh();
trafficDecelerationToggle->refresh();
trafficDangerToggle->refresh();
trafficSpeedToggle->refresh();
trafficSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *aggressiveFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveFollow"]);
FrogPilotParamValueControl *aggressiveAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkAcceleration"]);
FrogPilotParamValueControl *aggressiveDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDeceleration"]);
FrogPilotParamValueControl *aggressiveDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDanger"]);
FrogPilotParamValueControl *aggressiveSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeed"]);
FrogPilotParamValueControl *aggressiveSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeedDecrease"]);
FrogPilotButtonsControl *aggressiveResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetAggressivePersonality"]);
QObject::connect(aggressiveResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Aggressive' personality?"), this)) {
params.putFloat("AggressiveFollow", 1.25);
params.putFloat("AggressiveJerkAcceleration", 50);
params.putFloat("AggressiveJerkDeceleration", 50);
params.putFloat("AggressiveJerkDanger", 100);
params.putFloat("AggressiveJerkSpeed", 50);
params.putFloat("AggressiveJerkSpeedDecrease", 50);
aggressiveFollowToggle->refresh();
aggressiveAccelerationToggle->refresh();
aggressiveDecelerationToggle->refresh();
aggressiveDangerToggle->refresh();
aggressiveSpeedToggle->refresh();
aggressiveSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *standardFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardFollow"]);
FrogPilotParamValueControl *standardAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkAcceleration"]);
FrogPilotParamValueControl *standardDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDeceleration"]);
FrogPilotParamValueControl *standardDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDanger"]);
FrogPilotParamValueControl *standardSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeed"]);
FrogPilotParamValueControl *standardSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeedDecrease"]);
FrogPilotButtonsControl *standardResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetStandardPersonality"]);
QObject::connect(standardResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Standard' personality?"), this)) {
params.putFloat("StandardFollow", 1.45);
params.putFloat("StandardJerkAcceleration", 100);
params.putFloat("StandardJerkDeceleration", 100);
params.putFloat("StandardJerkDanger", 100);
params.putFloat("StandardJerkSpeed", 100);
params.putFloat("StandardJerkSpeedDecrease", 100);
standardFollowToggle->refresh();
standardAccelerationToggle->refresh();
standardDecelerationToggle->refresh();
standardDangerToggle->refresh();
standardSpeedToggle->refresh();
standardSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
FrogPilotParamValueControl *relaxedFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedFollow"]);
FrogPilotParamValueControl *relaxedAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkAcceleration"]);
FrogPilotParamValueControl *relaxedDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDeceleration"]);
FrogPilotParamValueControl *relaxedDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDanger"]);
FrogPilotParamValueControl *relaxedSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeed"]);
FrogPilotParamValueControl *relaxedSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeedDecrease"]);
FrogPilotButtonsControl *relaxedResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetRelaxedPersonality"]);
QObject::connect(relaxedResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Relaxed' personality?"), this)) {
params.putFloat("RelaxedFollow", 1.75);
params.putFloat("RelaxedJerkAcceleration", 100);
params.putFloat("RelaxedJerkDeceleration", 100);
params.putFloat("RelaxedJerkDanger", 100);
params.putFloat("RelaxedJerkSpeed", 100);
params.putFloat("RelaxedJerkSpeedDecrease", 100);
relaxedFollowToggle->refresh();
relaxedAccelerationToggle->refresh();
relaxedDecelerationToggle->refresh();
relaxedDangerToggle->refresh();
relaxedSpeedToggle->refresh();
relaxedSpeedDecreaseToggle->refresh();
updateFrogPilotToggles();
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLongitudinalPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotLongitudinalPanel::hideSubToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotLongitudinalPanel::updateCarToggles);
@ -381,6 +594,16 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
updateMetric();
}
void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
customizationLevel = parent->customizationLevel;
toggles["ConditionalExperimental"]->setVisible(customizationLevel != 0);
toggles["CurveSpeedControl"]->setVisible(customizationLevel != 0);
toggles["CustomPersonalities"]->setVisible(customizationLevel == 2);
toggles["ExperimentalModeActivation"]->setVisible(customizationLevel != 0);
toggles["QOLLongitudinal"]->setVisible(customizationLevel != 0);
}
void FrogPilotLongitudinalPanel::updateCarToggles() {
hasDashSpeedLimits = parent->hasDashSpeedLimits;
hasPCMCruise = parent->hasPCMCruise;
@ -411,6 +634,7 @@ void FrogPilotLongitudinalPanel::updateMetric() {
params.putFloatNonBlocking("Offset2", params.getFloat("Offset2") * speedConversion);
params.putFloatNonBlocking("Offset3", params.getFloat("Offset3") * speedConversion);
params.putFloatNonBlocking("Offset4", params.getFloat("Offset4") * speedConversion);
params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion);
}
FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast<FrogPilotDualParamControl*>(toggles["CESpeed"]);
@ -422,6 +646,7 @@ void FrogPilotLongitudinalPanel::updateMetric() {
FrogPilotParamValueControl *offset3Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset3"]);
FrogPilotParamValueControl *offset4Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset4"]);
FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistance"]);
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast<FrogPilotParamValueControl*>(toggles["SetSpeedOffset"]);
if (isMetric) {
offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)"));
@ -429,10 +654,10 @@ void FrogPilotLongitudinalPanel::updateMetric() {
offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 kph)"));
offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 kph)"));
offset1Toggle->setDescription(tr("Set speed limit offset for limits between 0-34 kph."));
offset2Toggle->setDescription(tr("Set speed limit offset for limits between 35-54 kph."));
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."));
offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 kph."));
offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 kph."));
offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 kph."));
offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 kph."));
ceSignal->updateControl(0, 150, tr("kph"));
ceSpeedToggle->updateControl(0, 150, tr("kph"));
@ -442,18 +667,19 @@ void FrogPilotLongitudinalPanel::updateMetric() {
offset2Toggle->updateControl(-99, 99, tr("kph"));
offset3Toggle->updateControl(-99, 99, tr("kph"));
offset4Toggle->updateControl(-99, 99, tr("kph"));
setSpeedOffsetToggle->updateControl(0, 150, tr("kph"));
increasedStoppedDistanceToggle->updateControl(0, 5, tr(" meters"));
increasedStoppedDistanceToggle->updateControl(0, 3, tr(" meters"));
} else {
offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 mph)"));
offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 mph)"));
offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 mph)"));
offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 mph)"));
offset1Toggle->setDescription(tr("Set speed limit offset for limits between 0-34 mph."));
offset2Toggle->setDescription(tr("Set speed limit offset for limits between 35-54 mph."));
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."));
offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 mph."));
offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 mph."));
offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 mph."));
offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 mph."));
ceSignal->updateControl(0, 99, tr("mph"));
ceSpeedToggle->updateControl(0, 99, tr("mph"));
@ -463,8 +689,9 @@ void FrogPilotLongitudinalPanel::updateMetric() {
offset2Toggle->updateControl(-99, 99, tr("mph"));
offset3Toggle->updateControl(-99, 99, tr("mph"));
offset4Toggle->updateControl(-99, 99, tr("mph"));
setSpeedOffsetToggle->updateControl(0, 99, tr("mph"));
increasedStoppedDistanceToggle->updateControl(0, 15, tr(" feet"));
increasedStoppedDistanceToggle->updateControl(0, 10, tr(" feet"));
}
}
@ -482,28 +709,51 @@ void FrogPilotLongitudinalPanel::showToggles(const std::set<QString> &keys) {
void FrogPilotLongitudinalPanel::hideToggles() {
setUpdatesEnabled(false);
customPersonalityOpen = false;
slcOpen = false;
for (auto &[key, toggle] : toggles) {
bool subToggles = conditionalExperimentalKeys.find(key) != conditionalExperimentalKeys.end() ||
bool subToggles = aggressivePersonalityKeys.find(key) != aggressivePersonalityKeys.end() ||
conditionalExperimentalKeys.find(key) != conditionalExperimentalKeys.end() ||
curveSpeedKeys.find(key) != curveSpeedKeys.end() ||
customDrivingPersonalityKeys.find(key) != customDrivingPersonalityKeys.end() ||
experimentalModeActivationKeys.find(key) != experimentalModeActivationKeys.end() ||
longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() ||
qolKeys.find(key) != qolKeys.end() ||
relaxedPersonalityKeys.find(key) != relaxedPersonalityKeys.end() ||
speedLimitControllerKeys.find(key) != speedLimitControllerKeys.end() ||
speedLimitControllerOffsetsKeys.find(key) != speedLimitControllerOffsetsKeys.end() ||
speedLimitControllerQOLKeys.find(key) != speedLimitControllerQOLKeys.end() ||
speedLimitControllerVisualsKeys.find(key) != speedLimitControllerVisualsKeys.end();
standardPersonalityKeys.find(key) != standardPersonalityKeys.end() ||
trafficPersonalityKeys.find(key) != trafficPersonalityKeys.end();
toggle->setVisible(!subToggles);
}
toggles["ConditionalExperimental"]->setVisible(customizationLevel != 0);
toggles["CurveSpeedControl"]->setVisible(customizationLevel != 0);
toggles["CustomPersonalities"]->setVisible(customizationLevel == 2);
toggles["ExperimentalModeActivation"]->setVisible(customizationLevel != 0);
toggles["QOLLongitudinal"]->setVisible(customizationLevel != 0);
setUpdatesEnabled(true);
update();
}
void FrogPilotLongitudinalPanel::hideSubToggles() {
if (slcOpen) {
showToggles(speedLimitControllerKeys);
if (customPersonalityOpen) {
customPersonalityOpen = false;
showToggles(customDrivingPersonalityKeys);
} else if (slcOpen) {
std::set<QString> modifiedSpeedLimitControllerKeys = speedLimitControllerKeys;
if (customizationLevel == 0) {
modifiedSpeedLimitControllerKeys.erase("SLCFallback");
modifiedSpeedLimitControllerKeys.erase("SLCOverride");
modifiedSpeedLimitControllerKeys.erase("SLCPriority");
modifiedSpeedLimitControllerKeys.erase("SLCQOL");
}
showToggles(modifiedSpeedLimitControllerKeys);
}
}

View File

@ -17,40 +17,58 @@ signals:
private:
void hideSubToggles();
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set<QString> &keys);
void updateCarToggles();
void updateMetric();
std::set<QString> aggressivePersonalityKeys = {
"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration",
"AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease",
"ResetAggressivePersonality"
};
std::set<QString> conditionalExperimentalKeys = {
"CESpeed", "CESpeedLead", "CECurves", "CELead",
"CEModelStopTime", "CENavigation", "CESignalSpeed",
"HideCEMStatusBar"
"CESpeed", "CESpeedLead", "CECurves",
"CELead", "CEModelStopTime", "CENavigation",
"CESignalSpeed", "HideCEMStatusBar"
};
std::set<QString> curveSpeedKeys = {
"CurveDetectionMethod", "CurveSensitivity",
"DisableCurveSpeedSmoothing", "MTSCCurvatureCheck",
"TurnAggressiveness"
"CurveDetectionMethod", "CurveSensitivity", "DisableCurveSpeedSmoothing",
"MTSCCurvatureCheck", "TurnAggressiveness"
};
std::set<QString> customDrivingPersonalityKeys = {
"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile",
"TrafficPersonalityProfile"
};
std::set<QString> experimentalModeActivationKeys = {
"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS",
"ExperimentalModeViaTap"
"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"
};
std::set<QString> longitudinalTuneKeys = {
"AccelerationProfile", "DecelerationProfile",
"HumanAcceleration", "HumanFollowing", "IncreasedStoppedDistance"
"AccelerationProfile", "DecelerationProfile", "HumanAcceleration",
"HumanFollowing", "IncreasedStoppedDistance", "LeadDetectionThreshold",
"MaxDesiredAcceleration"
};
std::set<QString> qolKeys = {
"CustomCruise", "CustomCruiseLong", "MapGears",
"OnroadDistanceButton", "ReverseCruise"
"CustomCruise", "CustomCruiseLong", "ForceStandstill",
"ForceStops", "MapGears", "ReverseCruise",
"SetSpeedOffset"
};
std::set<QString> relaxedPersonalityKeys = {
"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration",
"RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease",
"ResetRelaxedPersonality"
};
std::set<QString> speedLimitControllerKeys = {
"SLCConfirmation", "SLCOffsets", "SLCFallback", "SLCOverride",
"SLCPriority", "SLCQOL", "SLCVisuals"
"SLCConfirmation", "SLCOffsets", "SLCFallback",
"SLCOverride", "SLCPriority", "SLCQOL"
};
std::set<QString> speedLimitControllerOffsetsKeys = {
@ -62,17 +80,26 @@ private:
"SLCLookaheadLower"
};
std::set<QString> speedLimitControllerVisualsKeys = {
"ShowSLCOffset", "UseVienna"
std::set<QString> standardPersonalityKeys = {
"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration",
"StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease",
"ResetStandardPersonality"
};
std::set<QString> trafficPersonalityKeys = {
"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration",
"TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease",
"ResetTrafficPersonality"
};
FrogPilotSettingsWindow *parent;
FrogPilotButtonsControl *curveDetectionBtn;
FrogPilotButtonsControl *slcConfirmationBtn;
Params params;
bool customPersonalityOpen;
bool disableOpenpilotLongitudinal;
bool hasPCMCruise;
bool hasDashSpeedLimits;
bool isGM;
@ -82,5 +109,7 @@ private:
bool isToyota;
bool slcOpen;
int customizationLevel;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -0,0 +1,527 @@
#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h"
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> modelToggles {
{"AutomaticallyUpdateModels", tr("Automatically Update and Download Models"), tr("Automatically downloads new models and updates them if needed."), ""},
{"ModelRandomizer", tr("Model Randomizer"), tr("Randomly selects a model each drive and brings up a prompt at the end of the drive to review the model if it's longer than 15 minutes to help find your preferred model."), ""},
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Controls which models are blacklisted and won't be used for future drives."), ""},
{"ResetScores", tr("Reset Model Scores"), tr("Clears the ratings you've given to the driving models."), ""},
{"ReviewScores", tr("Review Model Scores"), tr("Displays the ratings you've assigned to the driving models."), ""},
{"DeleteModel", tr("Delete Model"), tr("Removes the selected driving model from your device."), ""},
{"DownloadModel", tr("Download Model"), tr("Downloads the selected driving model."), ""},
{"DownloadAllModels", tr("Download All Models"), tr("Downloads all undownloaded driving models."), ""},
{"SelectModel", tr("Select Model"), tr("Selects which model openpilot uses to drive."), ""},
{"ResetCalibrations", tr("Reset Model Calibrations"), tr("Resets calibration settings for the driving models."), ""},
};
for (const auto &[param, title, desc, icon] : modelToggles) {
AbstractControl *modelToggle;
if (param == "ModelRandomizer") {
FrogPilotParamManageControl *modelRandomizerToggle = new FrogPilotParamManageControl(param, title, desc, icon);
QObject::connect(modelRandomizerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
showToggles(modelRandomizerKeys);
updateModelLabels();
});
modelToggle = modelRandomizerToggle;
} else if (param == "ManageBlacklistedModels") {
FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, {tr("ADD"), tr("REMOVE")});
QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",", QString::SkipEmptyParts);
QStringList selectableModels = availableModelNames;
for (QString &model : blacklistedModels) {
selectableModels.removeAll(model);
if (model.contains("(Default)")) {
blacklistedModels.move(blacklistedModels.indexOf(model), 0);
}
}
if (id == 0) {
if (selectableModels.size() == 1) {
FrogPilotConfirmationDialog::toggleAlert(tr("There's no more models to blacklist! The only available model is \"%1\"!").arg(selectableModels.first()), tr("OK"), this);
} else {
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), selectableModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to add the '%1' model to the blacklist?").arg(selectedModel), tr("Add"), this)) {
if (!blacklistedModels.contains(selectedModel)) {
blacklistedModels.append(selectedModel);
params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
}
}
}
}
} else if (id == 1) {
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), blacklistedModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the '%1' model from the blacklist?").arg(selectedModel), tr("Remove"), this)) {
if (blacklistedModels.contains(selectedModel)) {
blacklistedModels.removeAll(selectedModel);
params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString());
}
}
}
}
});
modelToggle = blacklistBtn;
} else if (param == "ResetScores") {
ButtonControl *resetCalibrationsBtn = new ButtonControl(title, tr("RESET"), desc);
QObject::connect(resetCalibrationsBtn, &ButtonControl::clicked, [this]() {
if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model scores?"), this)) {
for (const QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
params.remove(QString("%1Drives").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1Drives").arg(cleanedModel).toStdString());
params.remove(QString("%1Score").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1Score").arg(cleanedModel).toStdString());
}
updateModelLabels();
}
});
modelToggle = reinterpret_cast<AbstractControl*>(resetCalibrationsBtn);
} else if (param == "ReviewScores") {
ButtonControl *reviewScoresBtn = new ButtonControl(title, tr("VIEW"), desc);
QObject::connect(reviewScoresBtn, &ButtonControl::clicked, [this]() {
openSubParentToggle();
for (LabelControl *label : labelControls) {
label->setVisible(true);
}
for (auto &[key, toggle] : toggles) {
toggle->setVisible(false);
}
});
modelToggle = reinterpret_cast<AbstractControl*>(reviewScoresBtn);
} else if (param == "DeleteModel") {
deleteModelBtn = new ButtonControl(title, tr("DELETE"), desc);
QObject::connect(deleteModelBtn, &ButtonControl::clicked, [this]() {
QStringList deletableModels, existingModels = modelDir.entryList({"*.thneed"}, QDir::Files);
QMap<QString, QString> labelToFileMap;
QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed";
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]);
labelToFileMap[availableModelNames[i]] = modelFile;
}
}
QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to delete"), deletableModels, "", this);
if (!selectedModel.isEmpty()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' model?").arg(selectedModel), tr("Delete"), this)) {
std::thread([=]() {
modelDeleting = true;
modelsDownloaded = false;
update();
params.putBoolNonBlocking("ModelsDownloaded", false);
deleteModelBtn->setValue(tr("Deleting..."));
QFile::remove(modelDir.absoluteFilePath(labelToFileMap[selectedModel]));
deleteModelBtn->setValue(tr("Deleted!"));
util::sleep_for(1000);
deleteModelBtn->setValue("");
modelDeleting = false;
QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files);
modelFiles.removeAll(currentModel);
haveModelsDownloaded = modelFiles.size() > 1;
update();
}).detach();
}
}
});
modelToggle = reinterpret_cast<AbstractControl*>(deleteModelBtn);
} else if (param == "DownloadModel") {
downloadModelBtn = new ButtonControl(title, tr("DOWNLOAD"), desc);
QObject::connect(downloadModelBtn, &ButtonControl::clicked, [this]() {
if (downloadModelBtn->text() == tr("CANCEL")) {
paramsMemory.remove("ModelToDownload");
paramsMemory.putBool("CancelModelDownload", true);
cancellingDownload = true;
device()->resetInteractiveTimeout(30);
} else {
QMap<QString, QString> labelToModelMap;
QStringList existingModels = modelDir.entryList({"*.thneed"}, QDir::Files);
QStringList downloadableModels;
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]);
labelToModelMap.insert(availableModelNames[i], availableModels[i]);
}
}
QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this);
if (!modelToDownload.isEmpty()) {
device()->resetInteractiveTimeout(300);
modelDownloading = true;
paramsMemory.put("ModelToDownload", labelToModelMap.value(modelToDownload).toStdString());
paramsMemory.put("ModelDownloadProgress", "0%");
downloadModelBtn->setValue(tr("Downloading %1...").arg(modelToDownload.remove(QRegularExpression("[🗺️👀📡]")).trimmed()));
QTimer *progressTimer = new QTimer(this);
progressTimer->setInterval(100);
QObject::connect(progressTimer, &QTimer::timeout, this, [=]() {
QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress"));
bool downloadComplete = progress.contains(QRegularExpression("downloaded", QRegularExpression::CaseInsensitiveOption));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (!progress.isEmpty() && progress != "0%") {
downloadModelBtn->setValue(progress);
}
if (downloadComplete || downloadFailed) {
bool lastModelDownloaded = downloadComplete;
if (downloadComplete) {
haveModelsDownloaded = true;
update();
}
if (downloadComplete) {
for (const QString &model : availableModels) {
if (!QFile::exists(modelDir.filePath(model + ".thneed"))) {
lastModelDownloaded = false;
break;
}
}
}
downloadModelBtn->setValue(progress);
paramsMemory.remove("CancelModelDownload");
paramsMemory.remove("ModelDownloadProgress");
progressTimer->stop();
progressTimer->deleteLater();
QTimer::singleShot(2000, this, [=]() {
cancellingDownload = false;
modelDownloading = false;
downloadModelBtn->setValue("");
if (lastModelDownloaded) {
modelsDownloaded = true;
update();
params.putBoolNonBlocking("ModelsDownloaded", modelsDownloaded);
}
device()->resetInteractiveTimeout(30);
});
}
});
progressTimer->start();
}
}
});
modelToggle = reinterpret_cast<AbstractControl*>(downloadModelBtn);
} else if (param == "DownloadAllModels") {
downloadAllModelsBtn = new ButtonControl(title, tr("DOWNLOAD"), desc);
QObject::connect(downloadAllModelsBtn, &ButtonControl::clicked, [this]() {
if (downloadAllModelsBtn->text() == tr("CANCEL")) {
paramsMemory.remove("DownloadAllModels");
paramsMemory.putBool("CancelModelDownload", true);
cancellingDownload = true;
device()->resetInteractiveTimeout(30);
} else {
device()->resetInteractiveTimeout(300);
startDownloadAllModels();
}
});
modelToggle = reinterpret_cast<AbstractControl*>(downloadAllModelsBtn);
} else if (param == "SelectModel") {
selectModelBtn = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(selectModelBtn, &ButtonControl::clicked, [this]() {
QSet<QString> modelFilesBaseNames = QSet<QString>::fromList(modelDir.entryList({"*.thneed"}, QDir::Files).replaceInStrings(QRegExp("\\.thneed$"), ""));
QStringList selectableModels;
for (int i = 0; i < availableModels.size(); ++i) {
if (modelFilesBaseNames.contains(availableModels[i]) || availableModelNames[i].contains("(Default)")) {
selectableModels.append(availableModelNames[i]);
}
}
QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, "", this);
if (!modelToSelect.isEmpty()) {
selectModelBtn->setValue(modelToSelect);
int modelIndex = availableModelNames.indexOf(modelToSelect);
params.put("Model", availableModels.at(modelIndex).toStdString());
params.put("ModelName", modelToSelect.toStdString());
if (experimentalModels.contains(availableModels.at(modelIndex))) {
FrogPilotConfirmationDialog::toggleAlert(tr("WARNING: This is a very experimental model and may drive dangerously!"), tr("I understand the risks."), this);
}
QString model = availableModelNames.at(modelIndex);
QString part_model_param = processModelName(model);
if (!params.checkKey(part_model_param.toStdString() + "CalibrationParams") || !params.checkKey(part_model_param.toStdString() + "LiveTorqueParameters")) {
if (FrogPilotConfirmationDialog::yesorno(tr("Start with a fresh calibration for the newly selected model?"), this)) {
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
}
}
if (started) {
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) {
Hardware::reboot();
}
}
updateFrogPilotToggles();
}
});
selectModelBtn->setValue(QString::fromStdString(params.get("ModelName")));
modelToggle = reinterpret_cast<AbstractControl*>(selectModelBtn);
} else if (param == "ResetCalibrations") {
FrogPilotButtonsControl *resetCalibrationsBtn = new FrogPilotButtonsControl(title, desc, {tr("RESET ALL"), tr("RESET ONE")});
QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::showDescriptionEvent, this, &FrogPilotModelPanel::updateCalibrationDescription);
QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
if (id == 0) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset all of your model calibrations?"), this)) {
for (const QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
}
}
} else if (id == 1) {
QStringList selectableModelLabels;
for (int i = 0; i < availableModels.size(); ++i) {
selectableModelLabels.append(availableModelNames[i]);
}
QString modelToReset = MultiOptionDialog::getSelection(tr("Select a model to reset"), selectableModelLabels, "", this);
if (!modelToReset.isEmpty()) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset this model's calibrations?"), this)) {
QString cleanedModel = processModelName(modelToReset);
params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString());
params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString());
}
}
}
});
modelToggle = resetCalibrationsBtn;
} else {
modelToggle = new ParamControl(param, title, desc, icon);
}
addItem(modelToggle);
toggles[param] = modelToggle;
makeConnections(modelToggle);
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(modelToggle)) {
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotModelPanel::openParentToggle);
}
QObject::connect(modelToggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
}
QObject::connect(static_cast<ToggleControl*>(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) {
modelRandomizer = state;
if (state && !modelsDownloaded) {
if (FrogPilotConfirmationDialog::yesorno(tr("The 'Model Randomizer' only works with downloaded models. Do you want to download all the driving models?"), this)) {
startDownloadAllModels();
}
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotModelPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotModelPanel::hideSubToggles);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState);
hideToggles();
}
void FrogPilotModelPanel::showEvent(QShowEvent *event) {
QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed";
availableModelNames = QString::fromStdString(params.get("AvailableModelsNames")).split(",");
availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
experimentalModels = QString::fromStdString(params.get("ExperimentalModels")).split(",");
modelRandomizer = params.getBool("ModelRandomizer");
modelsDownloaded = params.getBool("ModelsDownloaded");
QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files);
modelFiles.removeAll(currentModel);
haveModelsDownloaded = modelFiles.size() > 1;
}
void FrogPilotModelPanel::updateState(const UIState &s) {
if (!isVisible()) return;
downloadAllModelsBtn->setText(modelDownloading && allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
downloadModelBtn->setText(modelDownloading && !allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
deleteModelBtn->setEnabled(!modelDeleting && !modelDownloading);
downloadAllModelsBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && (!modelDownloading || allModelsDownloading) && !modelsDownloaded);
downloadModelBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && !allModelsDownloading && !modelsDownloaded);
selectModelBtn->setEnabled(!modelDeleting && !modelDownloading && !modelRandomizer);
started = s.scene.started;
}
void FrogPilotModelPanel::startDownloadAllModels() {
allModelsDownloading = true;
modelDownloading = true;
paramsMemory.putBool("DownloadAllModels", true);
downloadAllModelsBtn->setValue(tr("Downloading models..."));
QTimer *progressTimer = new QTimer(this);
progressTimer->setInterval(100);
QObject::connect(progressTimer, &QTimer::timeout, this, [=]() {
QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress"));
bool downloadComplete = progress.contains(QRegularExpression("All models downloaded!", QRegularExpression::CaseInsensitiveOption));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (!progress.isEmpty() && progress != "0%") {
downloadAllModelsBtn->setValue(progress);
}
if (downloadComplete || downloadFailed) {
if (downloadComplete) {
haveModelsDownloaded = true;
update();
}
downloadAllModelsBtn->setValue(progress);
paramsMemory.remove("CancelModelDownload");
paramsMemory.remove("ModelDownloadProgress");
progressTimer->stop();
progressTimer->deleteLater();
QTimer::singleShot(2000, this, [=]() {
cancellingDownload = false;
modelDownloading = false;
paramsMemory.remove("DownloadAllModels");
downloadAllModelsBtn->setValue("");
device()->resetInteractiveTimeout(30);
});
}
});
progressTimer->start();
}
void FrogPilotModelPanel::updateCalibrationDescription() {
QString model = QString::fromStdString(params.get("ModelName"));
QString part_model_param = processModelName(model);
QString desc =
tr("openpilot requires the device to be mounted within 4° left or right and "
"within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required.");
std::string calib_bytes = params.get(part_model_param.toStdString() + "CalibrationParams");
if (!calib_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) {
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
desc += tr(" Your device is pointed %1° %2 and %3° %4.")
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"),
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right"));
}
} catch (kj::Exception) {
qInfo() << "invalid CalibrationParams";
}
}
qobject_cast<FrogPilotButtonsControl *>(sender())->setDescription(desc);
}
void FrogPilotModelPanel::updateModelLabels() {
QVector<QPair<QString, int>> modelScores;
for (QString &model : availableModelNames) {
QString cleanedModel = processModelName(model);
int score = params.getInt((cleanedModel + "Score").toStdString());
if (model.contains("(Default)")) {
modelScores.prepend(qMakePair(model, score));
} else {
modelScores.append(qMakePair(model, score));
}
}
labelControls.clear();
for (QPair<QString, int> &pair : modelScores) {
QString scoreDisplay = pair.second == 0 ? "N/A" : QString::number(pair.second) + "%";
LabelControl *labelControl = new LabelControl(pair.first, scoreDisplay, "");
labelControls.append(labelControl);
addItem(labelControl);
}
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
}
void FrogPilotModelPanel::showToggles(const std::set<QString> &keys) {
setUpdatesEnabled(false);
for (auto &[key, toggle] : toggles) {
toggle->setVisible(keys.find(key) != keys.end());
}
setUpdatesEnabled(true);
update();
}
void FrogPilotModelPanel::hideToggles() {
setUpdatesEnabled(false);
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
for (auto &[key, toggle] : toggles) {
bool subToggles = modelRandomizerKeys.find(key) != modelRandomizerKeys.end();
toggle->setVisible(!subToggles);
}
setUpdatesEnabled(true);
update();
}
void FrogPilotModelPanel::hideSubToggles() {
for (LabelControl *label : labelControls) {
label->setVisible(false);
}
}

View File

@ -0,0 +1,60 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
class FrogPilotModelPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotModelPanel(FrogPilotSettingsWindow *parent);
signals:
void openParentToggle();
void openSubParentToggle();
protected:
void showEvent(QShowEvent *event) override;
private:
void hideSubToggles();
void hideToggles();
void showToggles(const std::set<QString> &keys);
void startDownloadAllModels();
void updateCalibrationDescription();
void updateModelLabels();
void updateState(const UIState &s);
std::set<QString> modelRandomizerKeys = {
"ManageBlacklistedModels", "ResetScores", "ReviewScores"
};
ButtonControl *deleteModelBtn;
ButtonControl *downloadAllModelsBtn;
ButtonControl *downloadModelBtn;
ButtonControl *selectModelBtn;
Params params;
Params paramsMemory{"/dev/shm/params"};
Params paramsStorage{"/persist/params"};
QDir modelDir{"/data/models/"};
QList<LabelControl*> labelControls;
QStringList availableModelNames;
QStringList availableModels;
QStringList experimentalModels;
bool allModelsDownloading;
bool cancellingDownload;
bool haveModelsDownloaded;
bool modelDeleting;
bool modelDownloading;
bool modelRandomizer;
bool modelsDownloaded;
bool started;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -11,12 +11,12 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr
{"WarningSoftVolume", tr("Warning Soft Volume"), tr("Related alerts:\n\nBRAKE!, Risk of Collision\nTAKE CONTROL IMMEDIATELY"), ""},
{"WarningImmediateVolume", tr("Warning Immediate Volume"), tr("Related alerts:\n\nDISENGAGE IMMEDIATELY, Driver Distracted\nDISENGAGE IMMEDIATELY, Driver Unresponsive"), ""},
{"CustomAlerts", tr("Custom Alerts"), tr("Enable custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"},
{"GoatScream", tr("Goat Scream Steering Saturated Alert"), tr("Enable the famed 'Goat Scream' that has brought both joy and anger to FrogPilot users all around the world!"), ""},
{"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."), ""},
{"CustomAlerts", tr("Custom Alerts"), tr("Custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"},
{"GoatScream", tr("Goat Scream Steering Saturated Alert"), tr("Enables the famed 'Goat Scream' that has brought both joy and anger to FrogPilot users all around the world!"), ""},
{"GreenLightAlert", tr("Green Light Alert"), tr("Plays an alert when a traffic light changes from red to green."), ""},
{"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Plays an alert when the lead vehicle starts departing when at a standstill."), ""},
{"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Plays a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""},
{"SpeedLimitChangedAlert", tr("Speed Limit Change Alert"), tr("Plays an alert when the speed limit changes."), ""},
};
for (const auto &[param, title, desc, icon] : soundsToggles) {
@ -29,10 +29,14 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr
});
soundsToggle = alertVolumeControlToggle;
} else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) {
std::map<int, QString> volumeLabels;
for (int i = 0; i <= 101; ++i) {
volumeLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Muted") : QString::number(i) + "%";
}
if (param == "WarningImmediateVolume") {
soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 100, "%");
soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 101, QString(), volumeLabels);
} else {
soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, "%");
soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, QString(), volumeLabels);
}
} else if (param == "CustomAlerts") {
@ -74,6 +78,12 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotSoundsPanel::updateCarToggles);
}
void FrogPilotSoundsPanel::showEvent(QShowEvent *event) {
customizationLevel = parent->customizationLevel;
toggles["AlertVolumeControl"]->setVisible(customizationLevel == 2);
}
void FrogPilotSoundsPanel::updateCarToggles() {
hasBSM = parent->hasBSM;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
@ -101,6 +111,8 @@ void FrogPilotSoundsPanel::hideToggles() {
toggle->setVisible(!subToggles);
}
toggles["AlertVolumeControl"]->setVisible(customizationLevel == 2);
setUpdatesEnabled(true);
update();
}

View File

@ -15,6 +15,7 @@ signals:
private:
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set<QString> &keys);
void updateCarToggles();
@ -36,5 +37,7 @@ private:
bool hasBSM;
bool hasOpenpilotLongitudinal;
int customizationLevel;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -3,19 +3,19 @@
FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> themeToggles {
{"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", "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!"), ""},
{"CustomColors", tr("Color Scheme"), tr("Changes out openpilot's color scheme.\n\nWant to submit your own color scheme? Share it in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
{"CustomDistanceIcons", "Distance Button", "Changes out openpilot's distance button icons.\n\nWant to submit your own icon pack? Share it in the 'custom-themes' channel on the FrogPilot Discord!", ""},
{"CustomIcons", tr("Icon Pack"), tr("Changes out openpilot's icon pack.\n\nWant to submit your own icons? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
{"CustomSounds", tr("Sound Pack"), tr("Changes out openpilot's sound effects.\n\nWant to submit your own sounds? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
{"WheelIcon", tr("Steering Wheel"), tr("Enables a custom steering wheel icon in the top right of the screen."), ""},
{"CustomSignals", tr("Turn Signal Animation"), tr("Enables themed turn signal animations.\n\nWant to submit your own animations? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
{"DownloadStatusLabel", tr("Download Status"), "", ""},
{"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"},
{"HolidayThemes", tr("Holiday Themes"), tr("Changes the openpilot theme based on the current holiday. Minor holidays last one day, while major holidays (Easter, Christmas, Halloween, etc.) last the entire week."), "../frogpilot/assets/toggle_icons/icon_calendar.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"},
{"RandomEvents", tr("Random Events"), tr("Enables 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"}
{"StartupAlert", tr("Startup Alert"), tr("Controls the text of the 'Startup' alert message that appears when you start the drive."), "../frogpilot/assets/toggle_icons/icon_message.png"}
};
for (const auto &[param, title, desc, icon] : themeToggles) {
@ -162,7 +162,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
}
manageCustomColorsBtn->setValue(currentColor);
themeToggle = manageCustomColorsBtn;
} else if (param == "CustomDistanceIcon") {
} else if (param == "CustomDistanceIcons") {
manageDistanceIconsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
std::function<QString(QString)> formatIconName = [](QString name) -> QString {
@ -847,10 +847,10 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
params.put("StartupMessageTop", frogpilotTop.toStdString());
params.put("StartupMessageBottom", frogpilotBottom.toStdString());
} else if (id == 2) {
QString newTop = InputDialog::getText(tr("Enter your text for the top half"), this, tr("Characters: 0/%1").arg(maxLengthTop), false, -1, currentTop, maxLengthTop).trimmed();
QString newTop = InputDialog::getText(tr("Enter the text for the top half"), this, tr("Characters: 0/%1").arg(maxLengthTop), false, -1, currentTop, maxLengthTop).trimmed();
if (newTop.length() > 0) {
params.putNonBlocking("StartupMessageTop", newTop.toStdString());
QString newBottom = InputDialog::getText(tr("Enter your text for the bottom half"), this, tr("Characters: 0/%1").arg(maxLengthBottom), false, -1, currentBottom, maxLengthBottom).trimmed();
QString newBottom = InputDialog::getText(tr("Enter the text for the bottom half"), this, tr("Characters: 0/%1").arg(maxLengthBottom), false, -1, currentBottom, maxLengthBottom).trimmed();
if (newBottom.length() > 0) {
params.putNonBlocking("StartupMessageBottom", newBottom.toStdString());
}
@ -859,6 +859,8 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
params.remove("StartupMessageTop");
params.remove("StartupMessageBottom");
}
updateFrogPilotToggles();
});
themeToggle = startupAlertButton;
@ -889,7 +891,6 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotThemesPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotThemesPanel::updateCarToggles);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotThemesPanel::updateState);
}
@ -900,17 +901,17 @@ void FrogPilotThemesPanel::showEvent(QShowEvent *event) {
signalsDownloaded = params.get("DownloadableSignals").empty();
soundsDownloaded = params.get("DownloadableSounds").empty();
wheelsDownloaded = params.get("DownloadableWheels").empty();
}
void FrogPilotThemesPanel::updateCarToggles() {
disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
customizationLevel = parent->customizationLevel;
hideToggles();
toggles["RandomEvents"]->setVisible(customizationLevel != 0);
toggles["StartupAlert"]->setVisible(customizationLevel == 2);
}
void FrogPilotThemesPanel::updateState(const UIState &s) {
if (!isVisible()) return;
if (!isVisible()) {
return;
}
if (personalizeOpenpilotOpen) {
if (themeDownloading) {
@ -999,6 +1000,9 @@ void FrogPilotThemesPanel::hideToggles() {
toggle->setVisible(!subToggles);
}
toggles["RandomEvents"]->setVisible(customizationLevel != 0);
toggles["StartupAlert"]->setVisible(customizationLevel == 2);
setUpdatesEnabled(true);
update();
}

View File

@ -19,11 +19,10 @@ signals:
private:
void hideToggles();
void showToggles(const std::set<QString> &keys);
void updateCarToggles();
void updateState(const UIState &s);
std::set<QString> customThemeKeys = {
"CustomColors", "CustomDistanceIcon", "CustomIcons",
"CustomColors", "CustomDistanceIcons", "CustomIcons",
"CustomSignals", "CustomSounds", "DownloadStatusLabel",
"WheelIcon"
};
@ -45,10 +44,8 @@ private:
bool cancellingDownload;
bool colorDownloading;
bool colorsDownloaded;
bool disableOpenpilotLongitudinal;
bool distanceIconDownloading;
bool distanceIconsDownloaded;
bool hasOpenpilotLongitudinal;
bool iconDownloading;
bool iconsDownloaded;
bool personalizeOpenpilotOpen;
@ -61,5 +58,7 @@ private:
bool wheelDownloading;
bool wheelsDownloaded;
int customizationLevel;
std::map<QString, AbstractControl*> toggles;
};

View File

@ -3,7 +3,7 @@
#include "selfdrive/frogpilot/ui/qt/offroad/utilities.h"
UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(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."));
ButtonControl *flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Flashes the Panda device's firmware if you're running into issues."));
QObject::connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
std::thread([=]() {
@ -26,7 +26,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);
forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true);
QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
if (id == 0) {
paramsMemory.putBool("ForceOffroad", true);
@ -43,7 +43,7 @@ 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."));
ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Resets 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([=] {

View File

@ -110,11 +110,11 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
addItem(selectModelButton);
selectModelButton->setVisible(false);
ParamControl *forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), "");
forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), "");
addItem(forceFingerprint);
bool disableOpenpilotLongState = params.getBool("DisableOpenpilotLongitudinal");
disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disable openpilot longitudinal control and use stock ACC instead."), "", disableOpenpilotLongState);
disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongState);
QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [this](bool state) {
if (state) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) {
@ -134,20 +134,20 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
addItem(disableOpenpilotLong);
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
{"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 cars 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."), ""},
{"VoltSNG", tr("2017 Volt Stop and Go Hack"), tr("Forces stop and go for the 2017 Chevy Volt."), ""},
{"ExperimentalGMTune", tr("Experimental GM Tune"), tr("Enables FrogsGoMoo's experimental GM tune that is based on nothing but guesswork. Use at your own risk!"), ""},
{"LongPitch", tr("Uphill/Downhill Smoothing"), tr("Smoothens the gas and brake response when driving on slopes."), ""},
{"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Enables comma's new 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 control system that has 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("Enables comma's new 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 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."), ""},
{"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("Use FrogsGoMoo's personal tweaks to the Toyota tune focused around his 2019 Lexus ES 350 to take off a bit quicker and stop a bit smoother."), ""},
{"SNGHack", tr("Stop and Go Hack"), tr("Force stop and go for vehicles without stock stop and go functionality."), ""},
{"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically locks the doors when in drive and unlocks when in park."), ""},
{"ClusterOffset", tr("Cluster Speed Offset"), tr("Sets the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""},
{"NewToyotaTune", tr("comma's New Toyota/Lexus Tune"), tr("Enables comma's latest Toyota tuning, expertly crafted by Shane for enhanced vehicle performance."), ""},
{"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("Enables FrogsGoMoo's personal tweaks to the Toyota tune focused around his 2019 Lexus ES 350 to take off a bit quicker and stop a bit smoother."), ""},
{"SNGHack", tr("Stop and Go Hack"), tr("Forces stop and go for vehicles without stock stop and go functionality."), ""},
};
for (const auto &[param, title, desc, icon] : vehicleToggles) {
@ -159,7 +159,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
vehicleToggle = new FrogPilotButtonToggleControl(param, title, desc, lockToggles, lockToggleNames);
} else if (param == "ClusterOffset") {
std::vector<QString> clusterOffsetToggleNames{"Reset"};
vehicleToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 1.000, 1.050, "x", std::map<int, QString>(), 0.001, {}, clusterOffsetToggleNames, false);
vehicleToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 1.000, 1.050, "x", std::map<int, QString>(), 0.001, {}, clusterOffsetToggleNames, false, false);
} else {
vehicleToggle = new ParamControl(param, title, desc, icon);
@ -216,11 +216,18 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState);
}
void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
customizationLevel = parent->customizationLevel;
updateToggles();
}
void FrogPilotVehiclesPanel::updateCarToggles() {
disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal;
hasExperimentalOpenpilotLongitudinal = parent->hasExperimentalOpenpilotLongitudinal;
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
hasSNG = parent->hasSNG;
isBolt = parent->isBolt;
isGMPCMCruise = parent->isGMPCMCruise;
isImpreza = parent->isImpreza;
isToyotaTuneSupported = parent->isToyotaTuneSupported;
@ -230,7 +237,9 @@ void FrogPilotVehiclesPanel::updateCarToggles() {
}
void FrogPilotVehiclesPanel::updateState(const UIState &s) {
if (!isVisible()) return;
if (!isVisible()) {
return;
}
started = s.scene.started;
}
@ -243,7 +252,8 @@ void FrogPilotVehiclesPanel::setModels() {
void FrogPilotVehiclesPanel::updateToggles() {
setUpdatesEnabled(false);
disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise) || disableOpenpilotLongitudinal);
disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise && customizationLevel == 2) || disableOpenpilotLongitudinal);
forceFingerprint ->setVisible(customizationLevel == 2 || isBolt);
selectMakeButton->setValue(carMake);
selectModelButton->setValue(carModel);
@ -294,6 +304,14 @@ void FrogPilotVehiclesPanel::updateToggles() {
toggle->setVisible(setVisible);
}
toggles["ClusterOffset"]->setVisible(toggles["ClusterOffset"]->isVisible() && customizationLevel == 2);
toggles["CrosstrekTorque"]->setVisible(toggles["CrosstrekTorque"]->isVisible() && customizationLevel == 2);
toggles["ExperimentalGMTune"]->setVisible(toggles["ExperimentalGMTune"]->isVisible() && customizationLevel == 2);
toggles["FrogsGoMoosTweak"]->setVisible(toggles["FrogsGoMoosTweak"]->isVisible() && customizationLevel == 2);
toggles["LongPitch"]->setVisible(toggles["LongPitch"]->isVisible() && customizationLevel == 2);
toggles["NewLongAPI"]->setVisible(toggles["NewLongAPI"]->isVisible() && customizationLevel == 2);
toggles["NewToyotaTune"]->setVisible(toggles["NewToyotaTune"]->isVisible() && customizationLevel == 2);
setUpdatesEnabled(true);
update();
}

Some files were not shown because too many files have changed in this diff Show More