Files
onepilot/selfdrive/ui/lib/starpilot_state.py
T
firestar5683 d0e1db6766 StarPilot
2026-03-22 03:15:05 -05:00

196 lines
8.8 KiB
Python

import time
import json
from dataclasses import dataclass
from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE, PC
from openpilot.selfdrive.ui.ui_state import ui_state
from cereal import car, log, custom, messaging
@dataclass
class StarPilotCarState:
# ========== Car Type Detection ==========
isGM: bool = False
isHKG: bool = False
isToyota: bool = False
isSubaru: bool = False
isVolt: bool = False
isBolt: bool = False
isAngleCar: bool = False
isTorqueCar: bool = False
isTSK: bool = False
isHKGCanFd: bool = False
# ========== Car Capabilities ==========
hasBSM: bool = False
hasRadar: bool = True
hasPedal: bool = False
hasSNG: bool = False
hasNNFFLog: bool = True
hasAutoTune: bool = True
hasOpenpilotLongitudinal: bool = True
hasDashSpeedLimits: bool = True
hasZSS: bool = False
canUsePedal: bool = False
canUseSDSU: bool = False
# ========== Device/Car State ==========
isFrogsGoMoo: bool = False
hasPCMCruise: bool = False
lkasAllowedForAOL: bool = False
openpilotLongitudinalControlDisabled: bool = False
hasAlphaLongitudinal: bool = False
# ========== Car Values for Range Calculation ==========
steerActuatorDelay: float = 0.0
friction: float = 0.0
steerKp: float = 1.0
latAccelFactor: float = 0.0
steerRatio: float = 0.0
longitudinalActuatorDelay: float = 0.0
startAccel: float = 0.0
stopAccel: float = 0.0
stoppingDecelRate: float = 0.0
vEgoStarting: float = 0.0
vEgoStopping: float = 0.0
class StarPilotState:
_instance: 'StarPilotState | None' = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialize()
return cls._instance
def _initialize(self):
self.params = Params()
self.car_state = StarPilotCarState()
self.tuning_level: int = 0
self.toggle_levels: dict[str, int] = {}
self._param_update_time = 0.0
self.update()
def _apply_desktop_fallback(self, starpilot_toggles: dict):
fallback_make = starpilot_toggles.get("car_make", "")
fallback_model = starpilot_toggles.get("car_model", "")
if not fallback_make:
fallback_make = "gm"
if not fallback_model:
fallback_model = "CHEVROLET_BOLT_ACC_2022_2023"
self.car_state.hasPedal = starpilot_toggles.get("has_pedal", True)
self.car_state.hasSDSU = starpilot_toggles.get("has_sdsu", False)
self.car_state.hasZSS = starpilot_toggles.get("has_zss", False)
self.car_state.isBolt = fallback_model.startswith("CHEVROLET_BOLT")
self.car_state.isGM = fallback_make == "gm"
self.car_state.isHKG = fallback_make == "hyundai"
self.car_state.isSubaru = fallback_make == "subaru"
self.car_state.isToyota = fallback_make == "toyota"
self.car_state.isVolt = fallback_model.startswith("CHEVROLET_VOLT")
self.car_state.canUsePedal = self.car_state.hasPedal or self.car_state.isBolt
self.car_state.canUseSDSU = self.car_state.hasSDSU
def _safe_get(self, obj, field: str, default=None):
try:
return getattr(obj, field)
except Exception:
return default
def update(self) -> None:
# Throttle heavy param parsing to avoid slowing down UI
current_time = time.monotonic()
if current_time - self._param_update_time < 2.0:
return
self._param_update_time = current_time
self.tuning_level = self.params.get_int("TuningLevel")
# Read starpilot_toggles from params memory or scene
try:
starpilot_toggles = json.loads(self.params.get("FrogPilotToggles", encoding="utf-8") or "{}")
except:
starpilot_toggles = {}
# 1. Parse CarParamsPersistent
cp_bytes = self.params.get("CarParamsPersistent")
if cp_bytes is not None:
CP = messaging.log_from_bytes(cp_bytes, car.CarParams)
safety_configs = self._safe_get(CP, "safetyConfigs", [])
safety_model = safety_configs[0].safetyModel if len(safety_configs) > 0 else None
car_fingerprint = self._safe_get(CP, "carFingerprint", "")
car_make = self._safe_get(CP, "brand", self._safe_get(CP, "carName", ""))
# Map values
if self._safe_get(CP, "lateralTuning", None) is not None and CP.lateralTuning.which() == 'torque':
self.car_state.friction = self._safe_get(CP.lateralTuning.torque, "friction", self.car_state.friction)
self.car_state.latAccelFactor = self._safe_get(CP.lateralTuning.torque, "latAccelFactor", self.car_state.latAccelFactor)
self.car_state.isTorqueCar = True
else:
self.car_state.isTorqueCar = False
self.car_state.hasAlphaLongitudinal = bool(self._safe_get(CP, "alphaLongitudinalAvailable", False))
self.car_state.hasBSM = bool(self._safe_get(CP, "enableBsm", False))
self.car_state.hasDashSpeedLimits = car_make in ("ford", "hyundai", "toyota")
# hasNNFFLog is default True for now
self.car_state.hasOpenpilotLongitudinal = bool(self._safe_get(CP, "openpilotLongitudinalControl", False))
self.car_state.hasPCMCruise = bool(self._safe_get(CP, "pcmCruise", False))
self.car_state.hasPedal = bool(self._safe_get(CP, "enableGasInterceptorDEPRECATED", False))
self.car_state.hasRadar = not bool(self._safe_get(CP, "radarUnavailable", False))
self.car_state.hasSDSU = starpilot_toggles.get("has_sdsu", False)
self.car_state.hasSNG = bool(self._safe_get(CP, "autoResumeSng", False))
self.car_state.hasZSS = starpilot_toggles.get("has_zss", False)
self.car_state.isAngleCar = self._safe_get(CP, "steerControlType", None) == car.CarParams.SteerControlType.angle
self.car_state.isBolt = car_fingerprint.startswith("CHEVROLET_BOLT")
self.car_state.isGM = car_make == "gm"
self.car_state.isHKG = car_make == "hyundai"
self.car_state.isHKGCanFd = self.car_state.isHKG and safety_model == car.CarParams.SafetyModel.hyundaiCanfd
self.car_state.isSubaru = car_make == "subaru"
self.car_state.isToyota = car_make == "toyota"
self.car_state.isTSK = bool(self._safe_get(CP, "secOcRequired", False))
self.car_state.isVolt = car_fingerprint.startswith("CHEVROLET_VOLT")
self.car_state.lkasAllowedForAOL = starpilot_toggles.get("lkas_allowed_for_aol", False)
self.car_state.longitudinalActuatorDelay = float(self._safe_get(CP, "longitudinalActuatorDelay", self.car_state.longitudinalActuatorDelay))
self.car_state.startAccel = float(self._safe_get(CP, "startAccel", self.car_state.startAccel))
self.car_state.steerActuatorDelay = float(self._safe_get(CP, "steerActuatorDelay", self.car_state.steerActuatorDelay))
self.car_state.steerRatio = float(self._safe_get(CP, "steerRatio", self.car_state.steerRatio))
self.car_state.stopAccel = float(self._safe_get(CP, "stopAccel", self.car_state.stopAccel))
self.car_state.stoppingDecelRate = float(self._safe_get(CP, "stoppingDecelRate", self.car_state.stoppingDecelRate))
self.car_state.vEgoStarting = float(self._safe_get(CP, "vEgoStarting", self.car_state.vEgoStarting))
self.car_state.vEgoStopping = float(self._safe_get(CP, "vEgoStopping", self.car_state.vEgoStopping))
if PC and (car_make == "mock" or car_fingerprint == "MOCK"):
self._apply_desktop_fallback(starpilot_toggles)
elif PC:
self._apply_desktop_fallback(starpilot_toggles)
elif PC:
self._apply_desktop_fallback(starpilot_toggles)
# 2. Parse FrogPilotCarParamsPersistent
fpcp_bytes = self.params.get("FrogPilotCarParamsPersistent")
if fpcp_bytes is not None:
try:
FPCP = messaging.log_from_bytes(fpcp_bytes, custom.FrogPilotCarParams)
self.car_state.canUsePedal = FPCP.canUsePedal
self.car_state.canUseSDSU = FPCP.canUseSDSU
self.car_state.openpilotLongitudinalControlDisabled = FPCP.openpilotLongitudinalControlDisabled
except Exception:
pass
# 3. Parse LiveTorqueParameters
ltp_bytes = self.params.get("LiveTorqueParameters")
if ltp_bytes is not None:
try:
event = log.Event.from_bytes(ltp_bytes)
self.car_state.hasAutoTune = event.liveTorqueParameters.useParams
except Exception:
pass
# Global instance
starpilot_state = StarPilotState()