Volt | Genesis | Ioniq | Kia | Model randomizer

This commit is contained in:
firestar5683
2026-04-24 00:41:11 -05:00
parent f20d22d62a
commit 1430c59da9
9 changed files with 220 additions and 52 deletions
@@ -2,17 +2,19 @@ from hypothesis import settings, given, strategies as st
import pytest
from opendbc.car import gen_empty_fingerprint
from opendbc.can import CANPacker
from opendbc.car import Bus, gen_empty_fingerprint
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.hyundai.carstate import CarState
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai import hyundaicanfd
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
HyundaiFlags, get_platform_codes, HyundaiSafetyFlags
CarControllerParams, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
Ecu = CarParams.Ecu
@@ -120,6 +122,39 @@ class TestHyundaiFingerprint:
assert CarState.get_canfd_blinker_sig_names(CAR.HYUNDAI_KONA_EV_2ND_GEN, False) == ("LEFT_LAMP_ALT", "RIGHT_LAMP_ALT")
assert CarState.get_canfd_blinker_sig_names(CAR.KIA_EV6, False) == ("LEFT_LAMP", "RIGHT_LAMP")
def test_sportage_angle_steering_uses_adas_cmd_with_send_lfa(self):
fingerprint = gen_empty_fingerprint()
cam_can = CanBus(None, fingerprint).CAM
fingerprint[cam_can][0xCB] = 24
CP = CarInterface.get_params(CAR.KIA_SPORTAGE_HEV_2026, fingerprint, [], False, False, False, None)
assert CP.flags & HyundaiFlags.SEND_LFA
assert CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
msgs = hyundaicanfd.create_steering_messages(packer, CP, CanBus(CP), True, True, 1.0, 12.3)
assert [(addr, bus) for addr, _, bus in msgs] == [(0xCB, CanBus(CP).ECAN)]
def test_sportage_angle_jerk_override_is_scoped(self):
sportage = CarParams.new_message()
sportage.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
sportage.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_ANGLE_STEERING)
comparison_angle = CarParams.new_message()
comparison_angle.carFingerprint = CAR.KIA_EV6
comparison_angle.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_ANGLE_STEERING)
ioniq6 = CarParams.new_message()
ioniq6.carFingerprint = CAR.HYUNDAI_IONIQ_6
ioniq6.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
sportage_params = CarControllerParams(sportage)
comparison_params = CarControllerParams(comparison_angle)
ioniq6_params = CarControllerParams(ioniq6)
assert sportage_params.ANGLE_LIMITS.MAX_LATERAL_JERK < comparison_params.ANGLE_LIMITS.MAX_LATERAL_JERK
assert comparison_params.ANGLE_LIMITS.MAX_LATERAL_JERK == ioniq6_params.ANGLE_LIMITS.MAX_LATERAL_JERK
def test_blacklisted_parts(self, subtests):
# Asserts no ECUs known to be shared across platforms exist in the database.
# Tucson having Santa Cruz camera and EPS for example
+9 -1
View File
@@ -1,5 +1,5 @@
import re
from dataclasses import dataclass, field
from dataclasses import dataclass, field, replace
from enum import IntFlag
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
@@ -29,6 +29,7 @@ class CarControllerParams:
ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN = 0.6
def __init__(self, CP, vEgoRaw=100.):
self.ANGLE_LIMITS = self.ANGLE_LIMITS
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 7
self.STEER_DRIVER_ALLOWANCE = 50
@@ -52,6 +53,13 @@ class CarControllerParams:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.STEER_THRESHOLD = 175
# The Sportage angle port is rough at low speed on the higher global jerk limit.
# Keep the branch-wide higher limit for other cars, but restore the older calmer
# jerk ceiling on this port only.
if CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026:
self.ANGLE_LIMITS = replace(self.ANGLE_LIMITS,
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL))
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_ELANTRA_GT_I30, CAR.HYUNDAI_IONIQ,
+1 -1
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-49c71e2e-DEBUG";
const uint8_t gitversion[19] = "DEV-f20d22d6-DEBUG";
+1 -1
View File
@@ -1 +1 @@
DEV-49c71e2e-DEBUG
DEV-f20d22d6-DEBUG
+34 -34
View File
@@ -155,81 +155,81 @@ VOLT_STANDARD_FF_CUTOFF = 1.30
VOLT_STANDARD_FF_CUTOFF_WIDTH = 0.24
VOLT_STANDARD_TRANSITION_SPEED = 10.0
VOLT_STANDARD_PHASE_SCALE = 0.10
VOLT_STANDARD_TURN_IN_BOOST_LEFT = 0.02
VOLT_STANDARD_TURN_IN_BOOST_LEFT = 0.06
VOLT_STANDARD_TURN_IN_BOOST_RIGHT = 0.30
VOLT_STANDARD_UNWIND_TAPER_LEFT = 0.08
VOLT_STANDARD_UNWIND_TAPER_RIGHT = 0.14
VOLT_STANDARD_UNWIND_TAPER_LEFT = 0.12
VOLT_STANDARD_UNWIND_TAPER_RIGHT = 0.22
VOLT_STANDARD_FRICTION_MULT = 1.04
VOLT_STANDARD_FRICTION_LAT_RISE = 0.20
VOLT_STANDARD_FRICTION_JERK_RISE = 0.24
VOLT_STANDARD_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.02
VOLT_STANDARD_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 0.18
VOLT_STANDARD_UNWIND_THRESHOLD_INCREASE_LEFT = 0.02
VOLT_STANDARD_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.10
VOLT_STANDARD_UNWIND_THRESHOLD_INCREASE_LEFT = 0.04
VOLT_STANDARD_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.18
VOLT_STANDARD_TURN_IN_FRICTION_BOOST_LEFT = 0.01
VOLT_STANDARD_TURN_IN_FRICTION_BOOST_RIGHT = 0.10
VOLT_STANDARD_UNWIND_FRICTION_REDUCTION_LEFT = 0.02
VOLT_STANDARD_UNWIND_FRICTION_REDUCTION_RIGHT = 0.08
VOLT_STANDARD_CENTER_TAPER_MAX = 0.11
VOLT_STANDARD_CENTER_TAPER_LAT = 0.12
VOLT_STANDARD_UNWIND_FRICTION_REDUCTION_LEFT = 0.04
VOLT_STANDARD_UNWIND_FRICTION_REDUCTION_RIGHT = 0.14
VOLT_STANDARD_CENTER_TAPER_MAX = 0.08
VOLT_STANDARD_CENTER_TAPER_LAT = 0.10
VOLT_STANDARD_CENTER_TAPER_LAT_WIDTH = 0.02
VOLT_STANDARD_CENTER_TAPER_SPEED = 17.0
VOLT_STANDARD_CENTER_TAPER_SPEED = 20.0
VOLT_STANDARD_CENTER_TAPER_SPEED_WIDTH = 2.5
GENESIS_G90_LATERAL_TESTING_GROUND_ID = testing_ground.id_4
GENESIS_G90_FF_GAIN_LEFT = 0.13
GENESIS_G90_FF_GAIN_RIGHT = 0.10
GENESIS_G90_FF_GAIN_RIGHT = 0.14
GENESIS_G90_FF_ONSET = 0.10
GENESIS_G90_FF_ONSET_WIDTH = 0.05
GENESIS_G90_FF_CUTOFF = 1.55
GENESIS_G90_FF_CUTOFF_WIDTH = 0.30
GENESIS_G90_TRANSITION_SPEED = 8.5
GENESIS_G90_PHASE_SCALE = 0.12
GENESIS_G90_TURN_IN_BOOST_LEFT = 0.14
GENESIS_G90_TURN_IN_BOOST_RIGHT = 0.22
GENESIS_G90_UNWIND_TAPER_LEFT = 0.12
GENESIS_G90_UNWIND_TAPER_RIGHT = 0.18
GENESIS_G90_TURN_IN_BOOST_LEFT = 0.22
GENESIS_G90_TURN_IN_BOOST_RIGHT = 0.34
GENESIS_G90_UNWIND_TAPER_LEFT = 0.18
GENESIS_G90_UNWIND_TAPER_RIGHT = 0.26
GENESIS_G90_FRICTION_MULT = 1.02
GENESIS_G90_FRICTION_LAT_RISE = 0.22
GENESIS_G90_FRICTION_JERK_RISE = 0.24
GENESIS_G90_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.10
GENESIS_G90_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 0.14
GENESIS_G90_UNWIND_THRESHOLD_INCREASE_LEFT = 0.08
GENESIS_G90_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.14
GENESIS_G90_TURN_IN_FRICTION_BOOST_LEFT = 0.05
GENESIS_G90_TURN_IN_FRICTION_BOOST_RIGHT = 0.08
GENESIS_G90_UNWIND_FRICTION_REDUCTION_LEFT = 0.07
GENESIS_G90_UNWIND_FRICTION_REDUCTION_RIGHT = 0.11
GENESIS_G90_UNWIND_THRESHOLD_INCREASE_LEFT = 0.12
GENESIS_G90_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.20
GENESIS_G90_TURN_IN_FRICTION_BOOST_LEFT = 0.06
GENESIS_G90_TURN_IN_FRICTION_BOOST_RIGHT = 0.10
GENESIS_G90_UNWIND_FRICTION_REDUCTION_LEFT = 0.10
GENESIS_G90_UNWIND_FRICTION_REDUCTION_RIGHT = 0.16
IONIQ_6_LATERAL_TESTING_GROUND_ID = testing_ground.id_5
IONIQ_6_LATERAL_TESTING_GROUND_VARIANT = "C"
IONIQ_6_FF_GAIN_LEFT = 0.04
IONIQ_6_FF_GAIN_RIGHT = 0.015
IONIQ_6_FF_GAIN_LEFT = 0.045
IONIQ_6_FF_GAIN_RIGHT = 0.008
IONIQ_6_FF_ONSET = 0.10
IONIQ_6_FF_ONSET_WIDTH = 0.04
IONIQ_6_FF_CUTOFF = 0.60
IONIQ_6_FF_CUTOFF_WIDTH = 0.14
IONIQ_6_TRANSITION_SPEED = 10.0
IONIQ_6_PHASE_SCALE = 0.10
IONIQ_6_TURN_IN_BOOST_LEFT = 0.38
IONIQ_6_TURN_IN_BOOST_RIGHT = 0.32
IONIQ_6_UNWIND_TAPER_LEFT = 0.34
IONIQ_6_UNWIND_TAPER_RIGHT = 0.54
IONIQ_6_TURN_IN_BOOST_LEFT = 0.42
IONIQ_6_TURN_IN_BOOST_RIGHT = 0.40
IONIQ_6_UNWIND_TAPER_LEFT = 0.40
IONIQ_6_UNWIND_TAPER_RIGHT = 0.66
IONIQ_6_FRICTION_MULT = 0.995
IONIQ_6_FRICTION_LAT_RISE = 0.20
IONIQ_6_FRICTION_JERK_RISE = 0.24
IONIQ_6_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.12
IONIQ_6_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 0.10
IONIQ_6_UNWIND_THRESHOLD_INCREASE_LEFT = 0.24
IONIQ_6_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.36
IONIQ_6_UNWIND_THRESHOLD_INCREASE_LEFT = 0.28
IONIQ_6_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.44
IONIQ_6_TURN_IN_FRICTION_BOOST_LEFT = 0.05
IONIQ_6_TURN_IN_FRICTION_BOOST_RIGHT = 0.04
IONIQ_6_UNWIND_FRICTION_REDUCTION_LEFT = 0.18
IONIQ_6_UNWIND_FRICTION_REDUCTION_RIGHT = 0.28
IONIQ_6_CENTER_TAPER_MAX = 0.05
IONIQ_6_CENTER_TAPER_LAT = 0.10
IONIQ_6_UNWIND_FRICTION_REDUCTION_LEFT = 0.22
IONIQ_6_UNWIND_FRICTION_REDUCTION_RIGHT = 0.34
IONIQ_6_CENTER_TAPER_MAX = 0.025
IONIQ_6_CENTER_TAPER_LAT = 0.08
IONIQ_6_CENTER_TAPER_LAT_WIDTH = 0.02
IONIQ_6_CENTER_TAPER_SPEED = 20.0
IONIQ_6_CENTER_TAPER_SPEED = 22.0
IONIQ_6_CENTER_TAPER_SPEED_WIDTH = 2.5
KIA_EV6_LATERAL_TESTING_GROUND_ID = testing_ground.id_6
+4 -2
View File
@@ -181,7 +181,7 @@ class TestLatControl:
def test_genesis_g90_ff_scale_curve(self):
assert get_genesis_g90_ff_scale(0.0, 0.0, 20.0) == 1.0
assert get_genesis_g90_ff_scale(0.5, 0.0, 20.0) > get_genesis_g90_ff_scale(-0.5, 0.0, 20.0)
assert get_genesis_g90_ff_scale(-0.5, 0.0, 20.0) > get_genesis_g90_ff_scale(0.5, 0.0, 20.0)
assert get_genesis_g90_ff_scale(0.6, 0.7, 8.0) > get_genesis_g90_ff_scale(0.6, 0.0, 8.0) > get_genesis_g90_ff_scale(0.6, -0.7, 8.0)
assert get_genesis_g90_ff_scale(-0.6, -0.7, 8.0) > get_genesis_g90_ff_scale(-0.6, 0.0, 8.0) > get_genesis_g90_ff_scale(-0.6, 0.7, 8.0)
assert get_genesis_g90_ff_scale(2.0, 0.0, 20.0) < get_genesis_g90_ff_scale(0.8, 0.0, 20.0)
@@ -219,7 +219,9 @@ class TestLatControl:
right_turn_in = get_ioniq_6_friction_threshold(6.0, -0.5, -0.8)
left_unwind = get_ioniq_6_friction_threshold(6.0, 0.5, -0.8)
right_unwind = get_ioniq_6_friction_threshold(6.0, -0.5, 0.8)
assert left_turn_in < right_turn_in < base < left_unwind < right_unwind
assert max(left_turn_in, right_turn_in) < base
assert left_unwind >= base
assert right_unwind >= base
def test_ioniq_6_friction_scale_curve(self):
base = get_ioniq_6_friction_scale(25.0, 0.5, 0.8)
+35 -6
View File
@@ -60,6 +60,39 @@ def _get_param_str(params: Params, key: str, default: str = "") -> str:
return str(val)
def _get_default_param_str(params: Params, key: str) -> str:
try:
val = params.get_default_value(key)
except Exception:
return ""
if val is None:
return ""
if isinstance(val, bytes):
try:
return val.decode("utf-8")
except Exception:
return ""
return str(val)
def _resolve_mirrored_param(params: Params, primary_key: str, secondary_key: str) -> str:
primary_val = _get_param_str(params, primary_key).strip()
secondary_val = _get_param_str(params, secondary_key).strip()
if primary_val == secondary_val:
return secondary_val or primary_val
primary_default = _get_default_param_str(params, primary_key).strip()
secondary_default = _get_default_param_str(params, secondary_key).strip()
primary_non_default = bool(primary_val) and primary_val != primary_default
secondary_non_default = bool(secondary_val) and secondary_val != secondary_default
if secondary_non_default:
return secondary_val
if primary_non_default:
return primary_val
return secondary_val or primary_val
def _canonical_model_id(model_id: str) -> str:
key = (model_id or "").strip().lower()
return BUILTIN_MODEL_KEY if key in BUILTIN_MODEL_ALIASES else key
@@ -143,14 +176,10 @@ class ModelState:
def __init__(self, context: CLContext):
# Dynamically build paths based on current model ID
params = Params()
model_id_raw = _get_param_str(params, "Model")
if not model_id_raw:
model_id_raw = _get_param_str(params, "DrivingModel", BUILTIN_MODEL_KEY)
model_id_raw = _resolve_mirrored_param(params, "Model", "DrivingModel") or BUILTIN_MODEL_KEY
model_id = _canonical_model_id(model_id_raw)
model_version = _get_param_str(params, "ModelVersion")
if not model_version:
model_version = _get_param_str(params, "DrivingModelVersion")
model_version = _resolve_mirrored_param(params, "ModelVersion", "DrivingModelVersion")
model_dir = MODELS_PATH
use_builtin_model = model_id == BUILTIN_MODEL_KEY
+89 -3
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import json
import random
import re
import urllib.request
@@ -89,6 +90,12 @@ class ModelManager:
return raw.decode("utf-8", errors="ignore").strip()
return str(raw).strip()
def _param_bool(self, key: str) -> bool:
try:
return bool(self.params.get_bool(key))
except Exception:
return self._param_text(key).lower() in {"1", "true", "yes", "on"}
def _default_param_text(self, key: str) -> str:
try:
default_value = self.params.get_default_value(key)
@@ -100,6 +107,23 @@ class ModelManager:
return default_value.decode("utf-8", errors="ignore").strip()
return str(default_value).strip()
def _resolve_mirrored_param(self, primary_key: str, secondary_key: str) -> str:
primary_val = self._param_text(primary_key)
secondary_val = self._param_text(secondary_key)
if primary_val == secondary_val:
return secondary_val or primary_val
primary_default = self._default_param_text(primary_key)
secondary_default = self._default_param_text(secondary_key)
primary_non_default = bool(primary_val) and primary_val != primary_default
secondary_non_default = bool(secondary_val) and secondary_val != secondary_default
if secondary_non_default:
return secondary_val
if primary_non_default:
return primary_val
return secondary_val or primary_val
def _load_catalog_from_params(self):
self.available_models = [entry for entry in self._param_text("AvailableModels").split(",") if entry]
self.model_versions = [entry for entry in self._param_text("ModelVersions").split(",") if entry]
@@ -119,7 +143,7 @@ class ModelManager:
def _ensure_model_params(self):
selected_model = self._selected_model()
current_version = self._param_text("ModelVersion") or self._param_text("DrivingModelVersion")
current_version = self._resolve_mirrored_param("ModelVersion", "DrivingModelVersion")
if not current_version:
current_version = self._default_param_text("ModelVersion") or self._default_param_text("DrivingModelVersion") or "v11"
@@ -141,8 +165,15 @@ class ModelManager:
if index < len(self.model_versions) and model_key
}
def _blacklisted_model_keys(self) -> set[str]:
return {
self._canonical_model_key(entry)
for entry in self._param_text("BlacklistedModels").split(",")
if entry.strip()
}
def _selected_model(self) -> str:
selected = self._param_text("Model") or self._param_text("DrivingModel")
selected = self._resolve_mirrored_param("Model", "DrivingModel")
if selected:
return self._canonical_model_key(selected)
default_value = self._default_param_text("Model") or self._default_param_text("DrivingModel")
@@ -178,6 +209,60 @@ class ModelManager:
return False
return all((MODELS_PATH / filename).is_file() for filename in required_files)
def _installed_model_choices(self) -> list[tuple[str, str, str]]:
self._load_catalog_from_params()
version_map = self._model_version_map()
blacklisted_keys = self._blacklisted_model_keys()
choices: list[tuple[str, str, str]] = []
seen_keys: set[str] = set()
for index, model_key in enumerate(self.available_models):
if not model_key:
continue
canonical_key = self._canonical_model_key(model_key)
if canonical_key in blacklisted_keys or canonical_key in seen_keys:
continue
model_version = version_map.get(model_key) or version_map.get(canonical_key) or ""
if not model_version and is_builtin_model_key(canonical_key):
model_version = self._default_param_text("ModelVersion") or self._default_param_text("DrivingModelVersion") or "v11"
if not self._is_model_downloaded(model_key, model_version):
continue
model_name = self.available_model_names[index] if index < len(self.available_model_names) else canonical_key
choices.append((canonical_key, model_name, model_version))
seen_keys.add(canonical_key)
return choices
def randomize_selected_model(self) -> str | None:
if not self._param_bool("ModelRandomizer"):
return None
choices = self._installed_model_choices()
if not choices:
print("Model Randomizer skipped: no installed, non-blacklisted models available.")
return None
selected = self._selected_model()
eligible_choices = [choice for choice in choices if self._canonical_model_key(choice[0]) != selected]
if not eligible_choices:
eligible_choices = choices
model_key, model_name, model_version = random.choice(eligible_choices)
if not model_version:
model_version = self._default_param_text("ModelVersion") or self._default_param_text("DrivingModelVersion") or "v11"
self._set_model_param_keys(model_key, model_name, model_version)
try:
self.params_memory.put_bool("StarPilotTogglesUpdated", True)
except Exception:
pass
print(f"Model Randomizer selected {model_name} ({model_key}, {model_version}).")
return model_key
def _sync_selected_model_version(self):
version_map = self._model_version_map()
name_map = {model_key: model_name for model_key, model_name in zip(self.available_models, self.available_model_names)}
@@ -194,7 +279,7 @@ class ModelManager:
self._set_model_param_keys(selected, selected_name, version)
return
fallback_version = self._param_text("ModelVersion") or self._param_text("DrivingModelVersion")
fallback_version = self._resolve_mirrored_param("ModelVersion", "DrivingModelVersion")
if not fallback_version:
fallback_version = self._default_param_text("ModelVersion") or self._default_param_text("DrivingModelVersion") or "v11"
self._set_model_param_keys(selected, name_map.get(selected, ""), fallback_version)
@@ -390,6 +475,7 @@ class ModelManager:
self.params_memory.put(DOWNLOAD_PROGRESS_PARAM, "All models downloaded!")
self.params_memory.remove(MODEL_DOWNLOAD_ALL_PARAM)
self.randomize_selected_model()
def update_tinygrad(self):
# This branch ships tinygrad runtime in-tree. "Update" here refreshes local model files.
+10 -2
View File
@@ -115,7 +115,7 @@ def sync_drive_stats(params, session):
except Exception as exception:
print(f"Failed to sync drive stats: {exception}")
def transition_offroad(starpilot_planner, theme_manager, thread_manager, time_validated, sm, params, starpilot_toggles):
def transition_offroad(starpilot_planner, model_manager, theme_manager, thread_manager, time_validated, sm, params, starpilot_toggles):
params.put("LastGPSPosition", json.dumps(starpilot_planner.gps_position))
if starpilot_toggles.lock_doors_timer != 0:
@@ -124,6 +124,8 @@ def transition_offroad(starpilot_planner, theme_manager, thread_manager, time_va
if starpilot_toggles.random_themes:
theme_manager.update_active_theme(time_validated, starpilot_toggles, randomize_theme=True)
model_manager.randomize_selected_model()
if time_validated:
thread_manager.run_with_lock(send_stats)
@@ -193,6 +195,7 @@ def starpilot_thread():
run_update_checks = False
safe_mode_active = safe_mode_enabled(params_raw)
started_previously = False
model_randomizer_previously = params.get_bool("ModelRandomizer")
time_validated = False
error_log = ERROR_LOGS_PATH / "error.txt"
@@ -216,7 +219,7 @@ def starpilot_thread():
starpilot_toggles = update_toggles(starpilot_variables, started, theme_manager, thread_manager, time_validated, params, starpilot_toggles)
serialized_starpilot_toggles = serialize_starpilot_toggles(starpilot_toggles)
toggle_broadcast_pending = True
transition_offroad(starpilot_planner, theme_manager, thread_manager, time_validated, sm, params, starpilot_toggles)
transition_offroad(starpilot_planner, model_manager, theme_manager, thread_manager, time_validated, sm, params, starpilot_toggles)
run_update_checks = True
elif started and not started_previously:
@@ -268,6 +271,11 @@ def starpilot_thread():
serialized_starpilot_toggles = serialize_starpilot_toggles(starpilot_toggles)
toggle_broadcast_pending = True
model_randomizer_enabled = params.get_bool("ModelRandomizer")
if model_randomizer_enabled and not model_randomizer_previously and not started:
model_manager.randomize_selected_model()
model_randomizer_previously = model_randomizer_enabled
periodic_update_due = monotonic_now >= next_periodic_update_check
if periodic_update_due:
next_periodic_update_check = get_next_periodic_update_check(monotonic_now, periodic_update_phase)