Files
StarPilot/openpilot/starpilot/ui/gain_feature.py
T
firestar5683 a82935f326 User interfaces and Android Auto
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: Shane Smiskol <shane@smiskol.com>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: pharmacomaniac <blittle65@gmail.com>

Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

104 lines
6.2 KiB
Python

"""Explicit controller-bound steering response edits in the shared torque document."""
import json
from openpilot.starpilot.lateral.torque_runtime import gm_manual_supported_cp as supported_cp
from openpilot.starpilot.lateral.torque_supported import BOLT_VEHICLES
from openpilot.starpilot.lateral.controller_selection import DOCUMENT_KEY as SELECTION_KEY, ControllerMode, selection_from_bytes
from openpilot.starpilot.lateral.torque_settings import (DOCUMENT_KEY, GainBasis, FieldChoice, gain_bounds, parse_document, replace_gain, serialize_document)
from openpilot.starpilot.ui.feature_settings_state import FeatureRow
class GainFeature:
def __init__(self, owner):
self.owner = owner
def _basis(self, capability):
cp = self.owner.vehicle_params()
if cp is None or not supported_cp(cp) or capability != self.owner._capability("torque"):
raise ValueError("Qualified vehicle unavailable")
if not self.owner._readable(SELECTION_KEY):
raise ValueError("Controller selection unavailable")
selection = selection_from_bytes(cp, self.owner._raw(SELECTION_KEY))
if selection.source == "invalid":
raise ValueError("Controller selection needs review")
if selection.mode == ControllerMode.STARPILOT:
table = ((0,), (0.6,))
else:
from openpilot.selfdrive.controls.lib.latcontrol_torque import INTERP_SPEEDS, KP_INTERP
table = (tuple(INTERP_SPEEDS), tuple(KP_INTERP))
return GainBasis(selection.mode.value, table, (capability[5], capability[6], capability[7]))
def rows(self, capability, profiles, raw, allowed, repair_allowed):
try:
basis = self._basis(capability)
except (AttributeError, ValueError, TypeError, OverflowError):
return (FeatureRow("", "Steering response", "Controller selection unavailable"),)
profile = profiles.get(capability[0])
choice = profile.proportional_gain if profile is not None else FieldChoice()
dependencies = self.owner._dependents(*(("AdvancedLateralTune",) if capability[0] in BOLT_VEHICLES else ()), "ForceAutoTuneOff", SELECTION_KEY)
if choice.mode == "custom" and profile.gain_basis != basis:
return (FeatureRow("torque_gain_rebase", "Review steering response",
"Controller or vehicle tune changed. Confirm this response for the selected controller.", raw,
available=repair_allowed, capability=capability, dependencies=dependencies),
FeatureRow("torque:gain:mode", "Steering response source", "Custom", raw,
choices=("Selected controller",), available=allowed, capability=capability, dependencies=dependencies,
default_value="Selected controller"))
rows = [FeatureRow("", "Manual steering response",
"Higher values respond more strongly; lower values respond more gently. First manual activation " +
"requires a driving-process restart; an active tuning owner refreshes changes during the drive.")]
rows.append(FeatureRow("torque:gain:mode", "Steering response source",
"Custom" if choice.mode == "custom" else "Selected controller", raw,
choices=("Selected controller", "Custom"), available=allowed, capability=capability, dependencies=dependencies,
default_value="Selected controller"))
if choice.mode == "custom":
low, high = gain_bounds(capability[0], basis)
rows.append(FeatureRow("torque:gain:value", "Steering response", f"{choice.custom_value:.2f}", raw,
minimum=low, maximum=high, step=0.05, available=allowed,
default_value="Selected controller", default_key="torque:gain:mode",
capability=capability, dependencies=dependencies))
return tuple(rows)
def apply(self, request):
owner = self.owner
def current():
required = {"ForceAutoTuneOff", SELECTION_KEY}
if request.capability is not None and request.capability[0] in BOLT_VEHICLES:
required.add("AdvancedLateralTune")
return (owner.authority("torque") and request.vehicle_fingerprint == owner.vehicle_fingerprint() and
request.capability is not None and request.capability == owner._capability("torque") and
required.issubset(key for key, _ in request.dependencies) and
owner._readable(DOCUMENT_KEY) and owner._raw(DOCUMENT_KEY) == request.expected and
all(owner._readable(key) and owner._raw(key) == value for key, value in request.dependencies))
try:
if not current() or request.expected is None and request.capability[0] in BOLT_VEHICLES:
return False
basis = self._basis(request.capability)
profiles = {} if request.expected is None else parse_document(request.expected)
prior = profiles.get(request.capability[0])
choice = prior.proportional_gain if prior is not None else FieldChoice()
review = request.key == "torque_gain_rebase"
if review:
if not request.confirmation or not owner.authority("parked_preferences") or choice.mode != "custom":
return False
mode, value = choice.mode, choice.custom_value
elif request.key == "torque:gain:mode":
if request.value == "Selected controller":
mode, value = "source", None
elif request.value == "Custom":
initial = 0.6 if request.capability[0] in BOLT_VEHICLES else basis.source_table[1][-1]
mode, value = "custom", choice.custom_value if choice.custom_value is not None else initial
else:
return False
elif request.key == "torque:gain:value" and choice.mode == "custom":
mode, value = "custom", float(request.value)
else:
return False
updated = replace_gain(profiles, request.capability[0], basis, mode, value, review=review)
if not current() or self._basis(request.capability) != basis or review and not owner.authority("parked_preferences"):
return False
owner.params.put(DOCUMENT_KEY, json.loads(serialize_document(updated)), block=True)
return parse_document(owner._raw(DOCUMENT_KEY)) == updated
except (OSError, ValueError, TypeError, AttributeError, OverflowError, UnicodeError):
return False