mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-06-28 01:52:06 +08:00
Annie Are You Okay
This commit is contained in:
@@ -54,6 +54,10 @@ VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN = 0.8 * DT_CTRL * 4
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP = [4.0, 8.0]
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V = [0.4, 1.0]
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V = [0.2, 1.0]
|
||||
TRUCK_LONG_SMOOTH_CARS = {
|
||||
CAR.CHEVROLET_SILVERADO,
|
||||
CAR.CHEVROLET_SILVERADO_CC,
|
||||
}
|
||||
|
||||
|
||||
def get_stock_cc_active_for_cancel(CP, CS):
|
||||
@@ -149,6 +153,22 @@ def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int:
|
||||
return int(round(np.interp(v_ego, [0.0, 6.0, 15.0, 30.0], [40.0, 85.0, 130.0, 170.0])))
|
||||
|
||||
|
||||
def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> float:
|
||||
if not enabled or accel <= 0.0 or v_ego < 8.0:
|
||||
return accel
|
||||
|
||||
low_scale = float(np.interp(v_ego, [8.0, 15.0, 25.0, 35.0], [0.60, 0.45, 0.32, 0.25]))
|
||||
mid_scale = float(np.interp(v_ego, [8.0, 15.0, 25.0, 35.0], [0.82, 0.72, 0.60, 0.52]))
|
||||
|
||||
if accel <= 0.18:
|
||||
return accel * low_scale
|
||||
if accel <= 0.45:
|
||||
return float(np.interp(accel, [0.18, 0.45], [0.18 * low_scale, 0.45 * mid_scale]))
|
||||
if accel <= 0.8:
|
||||
return float(np.interp(accel, [0.45, 0.8], [0.45 * mid_scale, 0.8]))
|
||||
return accel
|
||||
|
||||
|
||||
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
|
||||
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
|
||||
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
|
||||
@@ -197,17 +217,18 @@ def get_volt_one_pedal_target_decel(v_ego: float) -> float:
|
||||
|
||||
def should_activate_volt_one_pedal(one_pedal_ready: bool, cruise_main: bool, long_active: bool,
|
||||
gas_pressed: bool, brake_pressed: bool, regen_braking: bool,
|
||||
single_pedal_mode: bool, gear_shifter, moving_backward: bool) -> bool:
|
||||
single_pedal_mode: bool, gear_shifter, drive_time_s: float) -> bool:
|
||||
# Volt rear wheel direction bits can falsely report reverse while stopping in L.
|
||||
return (
|
||||
one_pedal_ready and
|
||||
cruise_main and
|
||||
single_pedal_mode and
|
||||
gear_shifter in AUTO_HOLD_DRIVE_GEARS and
|
||||
drive_time_s >= AUTO_HOLD_MIN_DRIVE_TIME_S and
|
||||
not long_active and
|
||||
not gas_pressed and
|
||||
not brake_pressed and
|
||||
not regen_braking and
|
||||
not moving_backward
|
||||
not regen_braking
|
||||
)
|
||||
|
||||
|
||||
@@ -505,7 +526,7 @@ class CarController(CarControllerBase):
|
||||
CS.out.regenBraking,
|
||||
bool(getattr(CS, "single_pedal_mode", False)),
|
||||
CS.out.gearShifter,
|
||||
bool(getattr(CS, "moving_backward", False)),
|
||||
float(getattr(CS, "one_pedal_drive_time", 0.0)),
|
||||
)
|
||||
|
||||
if self.frame % 4 == 0:
|
||||
@@ -638,7 +659,7 @@ class CarController(CarControllerBase):
|
||||
volt_one_pedal_hold_active = (
|
||||
volt_one_pedal_braking and
|
||||
not auto_hold_active and
|
||||
CS.auto_hold_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
|
||||
CS.one_pedal_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
|
||||
(CS.out.standstill or CS.out.vEgo < 0.02)
|
||||
)
|
||||
|
||||
@@ -745,7 +766,16 @@ class CarController(CarControllerBase):
|
||||
if testing_ground.use_1:
|
||||
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
|
||||
|
||||
accel_cmd = float(np.clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max))
|
||||
accel_input = actuators.accel + accel_due_to_pitch
|
||||
if (
|
||||
getattr(starpilot_toggles, "truck_tuning", False) and
|
||||
self.CP.carFingerprint in TRUCK_LONG_SMOOTH_CARS and
|
||||
getattr(self.CP, "transmissionType", None) == TransmissionType.automatic and
|
||||
not self.CP.enableGasInterceptorDEPRECATED
|
||||
):
|
||||
accel_input = shape_truck_positive_accel(accel_input, CS.out.vEgo, True)
|
||||
|
||||
accel_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
|
||||
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
|
||||
scaled_torque = torque + self.params.ZERO_GAS
|
||||
apply_gas_torque = np.clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
|
||||
|
||||
@@ -78,6 +78,7 @@ class CarState(CarStateBase):
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.one_pedal_drive_time = 0.0
|
||||
self.auto_hold_fault_suppression_timer = 0.0
|
||||
self.regen_release_timer = 0.0
|
||||
self.user_regen_paddle_pressed = False
|
||||
@@ -215,8 +216,10 @@ class CarState(CarStateBase):
|
||||
self.auto_hold_drive_time = AUTO_HOLD_MIN_DRIVE_TIME_S
|
||||
else:
|
||||
self.auto_hold_drive_time = min(self.auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
self.one_pedal_drive_time = min(self.one_pedal_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
else:
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.one_pedal_drive_time = 0.0
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
|
||||
|
||||
@@ -217,6 +217,10 @@ class CarInterface(CarInterfaceBase):
|
||||
gm_auto_hold = params.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
gm_auto_hold = False
|
||||
try:
|
||||
volt_one_pedal_mode = params.get_bool("VoltOnePedalMode")
|
||||
except UnknownKeyName:
|
||||
volt_one_pedal_mode = False
|
||||
|
||||
ret.brand = "gm"
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
|
||||
@@ -671,8 +675,8 @@ class CarInterface(CarInterfaceBase):
|
||||
if remote_start_boots_comma:
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
|
||||
|
||||
volt_stock_auto_hold_safety = (
|
||||
gm_auto_hold and
|
||||
volt_stock_friction_brake_safety = (
|
||||
(gm_auto_hold or volt_one_pedal_mode) and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
@@ -680,11 +684,11 @@ class CarInterface(CarInterfaceBase):
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
)
|
||||
if volt_stock_auto_hold_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a Volt auto-hold marker on
|
||||
# non-pedal paths. Hold can run while OP longitudinal is configured but
|
||||
# not currently active, so the bit must be present regardless of the
|
||||
# current long-control mode.
|
||||
if volt_stock_friction_brake_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
|
||||
# marker on non-pedal paths. Both auto hold and one-pedal can run while
|
||||
# OP longitudinal is configured but not currently active, so the bit must
|
||||
# be present regardless of the current long-control mode.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
use_panda_3d1_sched = (
|
||||
|
||||
@@ -43,6 +43,7 @@ from opendbc.car.gm.carcontroller import (
|
||||
get_volt_one_pedal_target_decel,
|
||||
get_testing_ground_1_brake_switch_bias,
|
||||
get_stock_cc_active_for_cancel,
|
||||
shape_truck_positive_accel,
|
||||
should_activate_auto_hold,
|
||||
should_activate_volt_one_pedal,
|
||||
should_send_stock_long_cancel,
|
||||
@@ -109,7 +110,7 @@ def test_stock_cancel_is_suppressed_when_acc_is_faulted():
|
||||
cs = _cs(True, AccState.FAULTED)
|
||||
cs.out.accFaulted = True
|
||||
|
||||
assert not get_stock_cc_active_for_cancel(CP, cs)
|
||||
assert get_stock_cc_active_for_cancel(CP, cs)
|
||||
assert not should_send_stock_long_cancel(11, cs)
|
||||
|
||||
|
||||
@@ -328,6 +329,7 @@ def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_thre
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
0.03,
|
||||
)
|
||||
|
||||
@@ -370,7 +372,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
@@ -381,7 +383,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
@@ -392,7 +394,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
@@ -403,7 +405,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
@@ -414,7 +416,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
True,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
@@ -425,7 +427,7 @@ def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
False,
|
||||
False,
|
||||
structs.CarState.GearShifter.drive,
|
||||
False,
|
||||
3.0,
|
||||
)
|
||||
|
||||
|
||||
@@ -435,6 +437,34 @@ def test_volt_one_pedal_target_decel_stays_active_above_low_speed_band():
|
||||
assert get_volt_one_pedal_target_decel(20.0 * CV.MPH_TO_MS) == -1.1
|
||||
|
||||
|
||||
def test_volt_one_pedal_regression_ignores_noisy_wheel_direction_bits():
|
||||
assert should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
|
||||
|
||||
def test_volt_one_pedal_requires_time_in_drive_before_arming():
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
2.5,
|
||||
)
|
||||
|
||||
|
||||
def test_friction_brake_mode_keeps_near_stop_disabled_for_regular_long_braking():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
|
||||
|
||||
@@ -494,6 +524,27 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
|
||||
assert pedal_gas - 0.18 > 0.04
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_softens_small_highway_requests():
|
||||
shaped = shape_truck_positive_accel(0.12, 26.0, True)
|
||||
|
||||
assert shaped < 0.05
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
|
||||
shaped = shape_truck_positive_accel(0.45, 13.5, True)
|
||||
|
||||
assert 0.30 < shaped < 0.35
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_leaves_large_requests_alone():
|
||||
assert shape_truck_positive_accel(1.0, 26.0, True) == 1.0
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
|
||||
assert shape_truck_positive_accel(0.12, 26.0, False) == 0.12
|
||||
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
|
||||
|
||||
|
||||
def test_use_interceptor_sng_launch_requires_actual_near_stop():
|
||||
CP = SimpleNamespace(vEgoStarting=0.25)
|
||||
|
||||
|
||||
@@ -186,6 +186,24 @@ class TestGMInterface:
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", False)
|
||||
params.put_bool("VoltOnePedalMode", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
params.remove("VoltOnePedalMode")
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
@parameterized.expand(VOLT_CARS)
|
||||
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
|
||||
@@ -28,15 +28,6 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
#searchSuggestions p {
|
||||
font-size: var(--font-size-sm);
|
||||
margin: 0;
|
||||
max-width: calc(100% - 8rem);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-item {
|
||||
align-items: center;
|
||||
background-color: var(--input-bg);
|
||||
@@ -50,6 +41,32 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-copy {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-primary,
|
||||
#searchSuggestions .suggestion-secondary {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-primary {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-demi-bold);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-secondary {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
margin-top: 0.25rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#searchSuggestions .suggestion-item:first-child {
|
||||
border-top-left-radius: var(--border-radius-xl);
|
||||
border-top-right-radius: var(--border-radius-xl);
|
||||
@@ -533,4 +550,4 @@
|
||||
min-width: 0;
|
||||
width: calc(100% - var(--padding-xl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,76 @@ function parseCoordinatePair(value) {
|
||||
return { latitude, longitude };
|
||||
}
|
||||
|
||||
function cleanSuggestionText(value) {
|
||||
if (typeof value !== "string") return "";
|
||||
return value.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function joinSuggestionParts(parts) {
|
||||
const seen = new Set();
|
||||
const unique = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const text = cleanSuggestionText(part);
|
||||
if (!text) continue;
|
||||
|
||||
const key = text.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
unique.push(text);
|
||||
}
|
||||
|
||||
return unique.join(", ");
|
||||
}
|
||||
|
||||
function splitSuggestionLabel(value) {
|
||||
const cleaned = cleanSuggestionText(value);
|
||||
if (!cleaned) {
|
||||
return { primary: "", secondary: "" };
|
||||
}
|
||||
|
||||
const [primary, ...rest] = cleaned.split(",").map(part => part.trim()).filter(Boolean);
|
||||
return {
|
||||
primary: primary || cleaned,
|
||||
secondary: rest.join(", "),
|
||||
};
|
||||
}
|
||||
|
||||
function getSuggestionText(suggestion) {
|
||||
const rawName = cleanSuggestionText(suggestion.name || suggestion.title || "");
|
||||
const rawPlaceName = cleanSuggestionText(suggestion.place_name || "");
|
||||
const explicitAddress = joinSuggestionParts([
|
||||
suggestion.full_address,
|
||||
suggestion.address,
|
||||
suggestion.district,
|
||||
suggestion.city,
|
||||
suggestion.cityname,
|
||||
suggestion.adcode && suggestion.address ? "" : suggestion.adname,
|
||||
suggestion.pname,
|
||||
]);
|
||||
|
||||
if (rawName) {
|
||||
const splitPlaceName = rawPlaceName && rawPlaceName.toLowerCase().startsWith(`${rawName.toLowerCase()},`)
|
||||
? splitSuggestionLabel(rawPlaceName)
|
||||
: { primary: rawName, secondary: "" };
|
||||
const secondary = explicitAddress || splitPlaceName.secondary;
|
||||
return {
|
||||
primary: splitPlaceName.primary || rawName,
|
||||
secondary: secondary && secondary.toLowerCase() !== rawName.toLowerCase() ? secondary : "",
|
||||
};
|
||||
}
|
||||
|
||||
if (rawPlaceName) {
|
||||
return splitSuggestionLabel(rawPlaceName);
|
||||
}
|
||||
|
||||
if (explicitAddress) {
|
||||
return splitSuggestionLabel(explicitAddress);
|
||||
}
|
||||
|
||||
return { primary: "Unnamed Location", secondary: "" };
|
||||
}
|
||||
|
||||
let map;
|
||||
let destinationMarker;
|
||||
let favoriteMarkers = [];
|
||||
@@ -711,7 +781,7 @@ export function NavDestination() {
|
||||
${() => (state.favoritesCount > 0 ? html`<button class="favorites-toggle-button" @click="${handleFavoritesClick}">❤️ Favorites</button>` : "")}
|
||||
${() => (state.canToggleProvider ? html`
|
||||
<div class="search-provider-toggle">
|
||||
<button class="${() => (state.searchProvider === "amap" ? "active" : "")}" @click="${() => { state.searchProvider = "amap"; state.suggestions = "[]"; }}">AMap</button>
|
||||
<button class="${() => (state.searchProvider === "amap" ? "active" : "")}" title="AMap / Gaode search provider" @click="${() => { state.searchProvider = "amap"; state.suggestions = "[]"; }}">AMap</button>
|
||||
<button class="${() => (state.searchProvider === "mapbox" ? "active" : "")}" @click="${() => { state.searchProvider = "mapbox"; state.suggestions = "[]"; }}">Mapbox</button>
|
||||
</div>
|
||||
` : "")}
|
||||
@@ -789,11 +859,19 @@ function SearchSuggestions({ suggestions, selectSuggestion, removeFavorite, rena
|
||||
const isFavorite = s => s.name && s.latitude != null && s.longitude != null && s.routeId;
|
||||
const item = s => html`
|
||||
<div class="suggestion-item" @click="${() => selectSuggestion(s)}">
|
||||
<p>
|
||||
${s.is_home ? "🏠 " : ""}
|
||||
${s.is_work ? "💼 " : ""}
|
||||
${s.name || s.address}
|
||||
</p>
|
||||
${(() => {
|
||||
const text = getSuggestionText(s);
|
||||
return html`
|
||||
<div class="suggestion-copy">
|
||||
<p class="suggestion-primary">
|
||||
${s.is_home ? "🏠 " : ""}
|
||||
${s.is_work ? "💼 " : ""}
|
||||
${text.primary}
|
||||
</p>
|
||||
${text.secondary ? html`<p class="suggestion-secondary">${text.secondary}</p>` : ""}
|
||||
</div>
|
||||
`;
|
||||
})()}
|
||||
${isFavorite(s) ? html`
|
||||
<div class="favorite-actions">
|
||||
<button class="home-favorite-button ${s.is_home ? "active" : ""}" title="Set as Home" @click="${e => { e.stopPropagation(); setHome(s); }}">🏠</button>
|
||||
|
||||
@@ -132,6 +132,13 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navkeys-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
margin: calc(var(--margin-base) * -1) 0 var(--margin-base);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navkeys-wrapper {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
@@ -153,4 +160,4 @@
|
||||
.navkeys-input {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ export function NavKeys() {
|
||||
|
||||
const getDeleteLabel = (kind) => {
|
||||
switch (kind) {
|
||||
case "amap1": return "Amap 1"
|
||||
case "amap2": return "Amap 2"
|
||||
case "amap1": return "AMap / Gaode 1"
|
||||
case "amap2": return "AMap / Gaode 2"
|
||||
case "public": return "Public Mapbox"
|
||||
case "secret": return "Secret Mapbox"
|
||||
default: return kind
|
||||
@@ -203,20 +203,22 @@ export function NavKeys() {
|
||||
queueMicrotask(api.load)
|
||||
|
||||
function renderGroup(title, kinds) {
|
||||
const isMapbox = title === "Mapbox Keys"
|
||||
const isMapbox = title === "Mapbox Keys"
|
||||
const isAMap = title === "AMap / Gaode Keys"
|
||||
|
||||
return html`
|
||||
<div class="navkeys-group">
|
||||
<div class="navkeys-title">
|
||||
${title}
|
||||
${isMapbox ? html`
|
||||
<span class="navkeys-help-icon" @click="${() => state.showMapboxHelp = !state.showMapboxHelp}">
|
||||
<i class="bi bi-question-circle-fill"></i>
|
||||
</span>
|
||||
` : ""}
|
||||
</div>
|
||||
|
||||
${kinds.map(kind => {
|
||||
<div class="navkeys-title">
|
||||
${title}
|
||||
${isMapbox ? html`
|
||||
<span class="navkeys-help-icon" @click="${() => state.showMapboxHelp = !state.showMapboxHelp}">
|
||||
<i class="bi bi-question-circle-fill"></i>
|
||||
</span>
|
||||
` : ""}
|
||||
</div>
|
||||
${isAMap ? html`<div class="navkeys-subtitle">AMap is the Gaode provider, not Google Maps.</div>` : ""}
|
||||
|
||||
${kinds.map(kind => {
|
||||
const keyMeta = meta[kind]
|
||||
const label = kind[0].toUpperCase() + kind.slice(1).replace(/[0-9]/, d => " " + d)
|
||||
|
||||
@@ -296,7 +298,7 @@ export function NavKeys() {
|
||||
return html`
|
||||
<div class="navkeys-wrapper navkeys-offset-top">
|
||||
<div class="navkeys-container">
|
||||
${renderGroup("AMap Keys", ["amap1", "amap2"])}
|
||||
${renderGroup("AMap / Gaode Keys", ["amap1", "amap2"])}
|
||||
${renderStatus("amap")}
|
||||
</div>
|
||||
<div class="navkeys-container">
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Home } from "/assets/components/home/home.js"
|
||||
import { LateralManeuvers } from "/assets/components/tools/lateral_maneuvers.js"
|
||||
import { LongitudinalManeuvers } from "/assets/components/tools/longitudinal_maneuvers.js"
|
||||
import { MapsManager } from "/assets/components/tools/maps.js"
|
||||
import { NavDestination } from "/assets/components/navigation/navigation_destination.js?v=nav-favorites-2"
|
||||
import { NavKeys } from "/assets/components/navigation/navigation_keys.js"
|
||||
import { NavDestination } from "/assets/components/navigation/navigation_destination.js?v=nav-search-context-1"
|
||||
import { NavKeys } from "/assets/components/navigation/navigation_keys.js?v=nav-search-context-1"
|
||||
import { RouteRecordings } from "/assets/components/recordings/dashcam_routes.js"
|
||||
import { SettingsView } from "/assets/components/settings.js"
|
||||
import { ScreenRecordings } from "/assets/components/recordings/screen_recordings.js"
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<link rel="stylesheet" href="/assets/components/home/home.css">
|
||||
<link rel="stylesheet" href="/assets/components/main.css">
|
||||
<link rel="stylesheet" href="/assets/components/modal.css">
|
||||
<link rel="stylesheet" href="/assets/components/navigation/navigation_destination.css?v=nav-favorites-2">
|
||||
<link rel="stylesheet" href="/assets/components/navigation/navigation_keys.css">
|
||||
<link rel="stylesheet" href="/assets/components/navigation/navigation_destination.css?v=nav-search-context-1">
|
||||
<link rel="stylesheet" href="/assets/components/navigation/navigation_keys.css?v=nav-search-context-1">
|
||||
<link rel="stylesheet" href="/assets/components/recordings/dashcam_routes.css">
|
||||
<link rel="stylesheet" href="/assets/components/recordings/screen_recordings.css">
|
||||
<link rel="stylesheet" href="/assets/components/settings.css">
|
||||
|
||||
@@ -461,6 +461,7 @@ def test_persistent_personal_records_and_model_usage_survive_empty_routes():
|
||||
"distractedMoments": 0,
|
||||
"unresponsiveMoments": 0,
|
||||
"routeModifiedAt": 100,
|
||||
"analysisComplete": True,
|
||||
},
|
||||
{
|
||||
"name": "route-2",
|
||||
@@ -472,6 +473,7 @@ def test_persistent_personal_records_and_model_usage_survive_empty_routes():
|
||||
"distractedMoments": 1,
|
||||
"unresponsiveMoments": 0,
|
||||
"routeModifiedAt": 100,
|
||||
"analysisComplete": True,
|
||||
},
|
||||
{
|
||||
"name": "route-3",
|
||||
@@ -483,6 +485,7 @@ def test_persistent_personal_records_and_model_usage_survive_empty_routes():
|
||||
"distractedMoments": 0,
|
||||
"unresponsiveMoments": 0,
|
||||
"routeModifiedAt": 100,
|
||||
"analysisComplete": True,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -509,6 +512,67 @@ def test_persistent_personal_records_and_model_usage_survive_empty_routes():
|
||||
assert restored_records["cleanDriveStreak"]["value"] == "3 drives"
|
||||
|
||||
|
||||
def test_model_usage_ignores_pending_route_shells():
|
||||
params = FakeParams({"AvailableModels": "orion,vega", "AvailableModelNames": "Orion,Vega"})
|
||||
drives = [
|
||||
{
|
||||
"name": "route-1",
|
||||
"date": "2026-06-15T08:00:00",
|
||||
"distanceMeters": 10000.0,
|
||||
"duration": 1200,
|
||||
"engagedSeconds": 900.0,
|
||||
"model": "Orion",
|
||||
"routeModifiedAt": 100,
|
||||
"analysisComplete": True,
|
||||
},
|
||||
{
|
||||
"name": "route-2",
|
||||
"date": "2026-06-16T08:00:00",
|
||||
"distanceMeters": 0.0,
|
||||
"duration": 600,
|
||||
"engagedSeconds": 0.0,
|
||||
"model": "Vega",
|
||||
"routeModifiedAt": 100,
|
||||
"analysisComplete": False,
|
||||
"attentionKnown": False,
|
||||
},
|
||||
]
|
||||
|
||||
persistent = utilities._update_dashboard_persistent_stats(params, drives, wall_now=1000)
|
||||
favorites = utilities._build_favorite_models(params, persistent)
|
||||
|
||||
assert [model["name"] for model in favorites] == ["Orion"]
|
||||
assert favorites[0]["drives"] == 1
|
||||
|
||||
|
||||
def test_cpu_temp_reader_uses_hardware_cpu_values(monkeypatch):
|
||||
hardware_module = _simple_module(
|
||||
"openpilot.system.hardware",
|
||||
HARDWARE=SimpleNamespace(
|
||||
get_thermal_config=lambda: SimpleNamespace(
|
||||
get_msg=lambda: {"cpuTempC": [55.1, 56.6], "memoryTempC": 75.0}
|
||||
)
|
||||
),
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "openpilot.system.hardware", hardware_module)
|
||||
|
||||
assert utilities._read_cpu_temp_c() == 57
|
||||
|
||||
|
||||
def test_cpu_temp_reader_ignores_non_cpu_thermal_zones(tmp_path):
|
||||
cpu_zone = tmp_path / "thermal_zone0"
|
||||
cpu_zone.mkdir()
|
||||
(cpu_zone / "type").write_text("cpu0-silver-usr", encoding="utf-8")
|
||||
(cpu_zone / "temp").write_text("61000", encoding="utf-8")
|
||||
|
||||
pmic_zone = tmp_path / "thermal_zone1"
|
||||
pmic_zone.mkdir()
|
||||
(pmic_zone / "type").write_text("pm8998_tz", encoding="utf-8")
|
||||
(pmic_zone / "temp").write_text("75000", encoding="utf-8")
|
||||
|
||||
assert utilities._read_cpu_temp_c(tmp_path) == 61
|
||||
|
||||
|
||||
def test_persistent_loader_accepts_decoded_param_dict():
|
||||
params = FakeParams({
|
||||
utilities.DASHBOARD_PERSISTENT_STATS_PARAM: {
|
||||
@@ -587,8 +651,7 @@ def test_lightweight_routes_surface_recent_drives_without_log_analysis(monkeypat
|
||||
assert [drive["name"] for drive in dashboard["recentDrives"]] == ["route-new", "route-old"]
|
||||
assert dashboard["lastDrive"]["model"] == "Orion"
|
||||
assert dashboard["week"]["drives"] == 2
|
||||
assert dashboard["favoriteModels"][0]["name"] == "Orion"
|
||||
assert dashboard["favoriteModels"][0]["drives"] == 2
|
||||
assert dashboard["favoriteModels"] == []
|
||||
assert dashboard["analysis"]["pendingRoutes"] == 2
|
||||
assert dashboard["analysis"]["batchSize"] == 2
|
||||
utilities._invalidate_dashboard_cache()
|
||||
@@ -948,4 +1011,5 @@ def test_stats_endpoint_keeps_existing_keys_and_adds_dashboard(monkeypatch):
|
||||
assert "diskUsage" in payload
|
||||
assert "driveStats" in payload
|
||||
assert "softwareInfo" in payload
|
||||
assert payload["softwareInfo"]["buildEnvironment"] == "Experimental"
|
||||
assert payload["dashboard"]["recentDrives"] == []
|
||||
|
||||
@@ -478,8 +478,8 @@ except TypeError:
|
||||
]
|
||||
|
||||
KEYS = {
|
||||
"amap1": ("amap1", "", "AMapKey1", "Amap key #1", 39),
|
||||
"amap2": ("amap2", "", "AMapKey2", "Amap key #2", 39),
|
||||
"amap1": ("amap1", "", "AMapKey1", "AMap / Gaode key #1", 39),
|
||||
"amap2": ("amap2", "", "AMapKey2", "AMap / Gaode key #2", 39),
|
||||
"public": ("public", "pk.", "MapboxPublicKey", "Public key", 80),
|
||||
"secret": ("secret", "sk.", "MapboxSecretKey", "Secret key", 80),
|
||||
}
|
||||
@@ -5176,18 +5176,16 @@ def setup(app):
|
||||
build_metadata = get_build_metadata()
|
||||
|
||||
short_branch = build_metadata.channel
|
||||
if build_metadata.release_channel:
|
||||
env = "Release"
|
||||
elif short_branch in ("StarPilot-Development", "StarPilot-Testing"):
|
||||
env = "Testing"
|
||||
elif build_metadata.tested_channel:
|
||||
env = "Staging"
|
||||
if short_branch == "StarPilot":
|
||||
galaxy_label = "Stable"
|
||||
elif short_branch == "Dom":
|
||||
galaxy_label = "Testing"
|
||||
else:
|
||||
env = short_branch
|
||||
galaxy_label = "Experimental"
|
||||
|
||||
software_info = {
|
||||
"branchName": build_metadata.channel,
|
||||
"buildEnvironment": env,
|
||||
"buildEnvironment": galaxy_label,
|
||||
"changelogUrl": utilities.get_github_changelog_url(build_metadata.openpilot.git_normalized_origin, build_metadata.channel),
|
||||
"commitHash": build_metadata.openpilot.git_commit,
|
||||
"commitUrl": utilities.get_github_commit_url(build_metadata.openpilot.git_normalized_origin, build_metadata.openpilot.git_commit),
|
||||
|
||||
@@ -1993,6 +1993,8 @@ def _recalculate_persistent_stats(stats):
|
||||
model_usage = {}
|
||||
|
||||
for _, entry in ordered_routes:
|
||||
if not bool(entry.get("analysisComplete", False)):
|
||||
continue
|
||||
model_name = _clean_model_label(entry.get("model", ""))
|
||||
model_key = canonical_model_key(entry.get("modelKey", "")) or _model_usage_key(model_name)
|
||||
if model_key:
|
||||
@@ -2140,8 +2142,40 @@ def _read_uptime_seconds():
|
||||
return None
|
||||
|
||||
|
||||
def _read_cpu_temp_c():
|
||||
thermal_root = Path("/sys/class/thermal")
|
||||
def _normalize_temp_c(value):
|
||||
try:
|
||||
raw = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if raw > 1000:
|
||||
raw /= 1000.0
|
||||
return raw if 0 < raw < 150 else None
|
||||
|
||||
|
||||
def _read_hardware_cpu_temps():
|
||||
try:
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
thermal_config = HARDWARE.get_thermal_config()
|
||||
thermal_msg = thermal_config.get_msg()
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
cpu_temps = thermal_msg.get("cpuTempC", [])
|
||||
if not isinstance(cpu_temps, (list, tuple)):
|
||||
cpu_temps = [cpu_temps]
|
||||
return [
|
||||
temp for temp in (_normalize_temp_c(value) for value in cpu_temps)
|
||||
if temp is not None
|
||||
]
|
||||
|
||||
|
||||
def _read_cpu_temp_c(thermal_root=None):
|
||||
if thermal_root is None:
|
||||
hardware_temps = _read_hardware_cpu_temps()
|
||||
if hardware_temps:
|
||||
return round(max(hardware_temps))
|
||||
thermal_root = Path("/sys/class/thermal")
|
||||
|
||||
try:
|
||||
zones = sorted(thermal_root.glob("thermal_zone*/temp"))
|
||||
except Exception:
|
||||
@@ -2150,13 +2184,18 @@ def _read_cpu_temp_c():
|
||||
values = []
|
||||
for temp_path in zones:
|
||||
try:
|
||||
raw = float(temp_path.read_text().strip())
|
||||
zone_type = temp_path.with_name("type").read_text(encoding="utf-8").strip().lower()
|
||||
except Exception:
|
||||
zone_type = ""
|
||||
if "cpu" not in zone_type:
|
||||
continue
|
||||
try:
|
||||
raw = temp_path.read_text().strip()
|
||||
except Exception:
|
||||
continue
|
||||
if raw > 1000:
|
||||
raw /= 1000.0
|
||||
if 0 < raw < 150:
|
||||
values.append(raw)
|
||||
temp = _normalize_temp_c(raw)
|
||||
if temp is not None:
|
||||
values.append(temp)
|
||||
|
||||
return round(max(values)) if values else None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user