mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
a82935f326
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>
141 lines
5.8 KiB
Python
141 lines
5.8 KiB
Python
"""GM live FLM source binding; numerical overlay only, never axis authority."""
|
|
import math
|
|
import time
|
|
|
|
from openpilot.starpilot.flm.torque_surface import GmSurface as GmSurface, SurfaceInput
|
|
from openpilot.starpilot.saved_source import read_saved
|
|
|
|
|
|
def gm_capability(cp, controller):
|
|
from openpilot.starpilot.lateral.controller_selection import policy_for
|
|
from openpilot.starpilot.lateral.torque_runtime import gm_manual_supported_cp
|
|
from openpilot.starpilot.lateral.bolt_policy import BOLT_GENERATIONS
|
|
if controller not in ('standard', 'starpilot') or not gm_manual_supported_cp(cp):
|
|
return None
|
|
policy = policy_for(cp)
|
|
if policy is None:
|
|
return None
|
|
tune = cp.lateralTuning.torque
|
|
# Rich Bolt stages belong to its selected custom law. STANDARD retains its
|
|
# standard law plus the neutral universal surface rather than importing a
|
|
# rich default law from a different selected controller.
|
|
profile = ('gm_bolt_2022_2023' if controller == 'starpilot' and policy == 'bolt' and
|
|
BOLT_GENERATIONS[cp.carFingerprint] == 2022 else 'torque_universal')
|
|
return {'fingerprint': str(cp.carFingerprint), 'controller': str(controller), 'policy': policy,
|
|
'basis': (float(tune.latAccelFactor), float(tune.latAccelOffset), float(tune.friction)), 'profile': profile}
|
|
|
|
|
|
def binding_matches(profile, capability):
|
|
return (profile is not None and capability is not None and profile.basis == capability['basis'] and
|
|
profile.flm is not None and profile.flm.controller == capability['controller'] and
|
|
profile.flm.policy == capability['policy'] and profile.flm.basis == capability['basis'] and
|
|
all(surface.profile == capability['profile'] for _, _, surface in profile.flm.saved))
|
|
|
|
|
|
class GmLiveSource:
|
|
def __init__(self, cp, controller, params=None):
|
|
self.cp = cp
|
|
self.capability = gm_capability(cp, controller)
|
|
if params is None and self.capability is not None:
|
|
from openpilot.common.params import Params
|
|
params = Params()
|
|
self.params = params
|
|
self.surface = None
|
|
self.last_ns = None
|
|
|
|
def sample(self, *, active, speed, now_ns=None):
|
|
from openpilot.starpilot.lateral.controller_selection import DOCUMENT_KEY as SELECTION_KEY, selection_from_bytes
|
|
from openpilot.starpilot.lateral.torque_settings import DOCUMENT_KEY, MAX_DOCUMENT_BYTES, parse_document
|
|
now_ns = time.monotonic_ns() if now_ns is None else now_ns
|
|
if not active or self.capability is None:
|
|
self.surface = None
|
|
self.last_ns = None
|
|
return None
|
|
if type(now_ns) is not int or now_ns <= 0 or self.last_ns is not None and now_ns < self.last_ns:
|
|
self.surface = None
|
|
self.last_ns = None
|
|
return None
|
|
if self.last_ns is not None and now_ns - self.last_ns < 1_000_000_000:
|
|
return self._frame_surface(speed)
|
|
self.last_ns = now_ns
|
|
self.surface = None
|
|
# Complete reader, no prefix, global legacy keys, bootstrap or writer.
|
|
raw, readable = read_saved(self.params, DOCUMENT_KEY, MAX_DOCUMENT_BYTES)
|
|
if not readable or raw is None:
|
|
return None
|
|
try:
|
|
selected_raw, selected_readable = read_saved(self.params, SELECTION_KEY, 4096)
|
|
selected = selection_from_bytes(self.cp, selected_raw) if selected_readable else None
|
|
if selected is None or selected.source == 'invalid' or str(selected.mode) != self.capability['controller']:
|
|
return None
|
|
profiles = parse_document(raw)
|
|
profile = profiles.get(self.capability['fingerprint'])
|
|
if binding_matches(profile, self.capability):
|
|
self.surface = profile.flm.selected()
|
|
except (ValueError, TypeError, UnicodeError, OverflowError, RecursionError):
|
|
pass
|
|
return self._frame_surface(speed)
|
|
|
|
def _frame_surface(self, speed):
|
|
if self.surface is None or not math.isfinite(speed) or not 0. <= speed <= 90.:
|
|
return None
|
|
try:
|
|
self.surface.base_threshold(speed)
|
|
except (ValueError, OverflowError):
|
|
return None
|
|
return self.surface
|
|
|
|
|
|
def frame(surface, cs, setpoint, jerk, output=0., desired_angle=0., actual_angle=0.):
|
|
if surface is None:
|
|
return None
|
|
try:
|
|
return SurfaceInput(float(cs.vEgo), float(setpoint), float(jerk), float(desired_angle), float(actual_angle),
|
|
float(output), bool(cs.steeringPressed), 1.)
|
|
except (ValueError, OverflowError, TypeError):
|
|
return None
|
|
|
|
|
|
def deadband(surface, speed):
|
|
return 0. if surface is None or not math.isfinite(speed) or not 0. <= speed <= 90. else surface.deadband(speed)
|
|
|
|
|
|
def base_threshold(surface, speed, fallback):
|
|
if surface is None or surface.base_values is None:
|
|
return fallback
|
|
try:
|
|
return surface.base_threshold(speed)
|
|
except (ValueError, OverflowError):
|
|
return fallback
|
|
|
|
|
|
def scaled_threshold(surface, speed, threshold, source_base):
|
|
if surface is None or surface.base_values is None:
|
|
return threshold
|
|
candidate = (threshold / source_base) * base_threshold(surface, speed, source_base)
|
|
return candidate if math.isfinite(candidate) and candidate > 0. else threshold
|
|
|
|
|
|
def stages(surface, cs, setpoint, jerk, ff, threshold, *, rich=False, rich_input=None):
|
|
sample = frame(surface, cs, setpoint, jerk)
|
|
if sample is None:
|
|
return ff, threshold
|
|
try:
|
|
return surface.stages(sample, ff if rich_input is None else rich_input, threshold, rich=rich)
|
|
except (ValueError, OverflowError):
|
|
return ff, threshold
|
|
|
|
|
|
def angle_assist(surface, cs, vm, params, curvature, setpoint, jerk, output):
|
|
if surface is None or cs.steeringPressed:
|
|
return output
|
|
try:
|
|
desired = math.degrees(vm.get_steer_from_curvature(-curvature, cs.vEgo, params.roll))
|
|
actual = cs.steeringAngleDeg - params.angleOffsetDeg
|
|
except (ValueError, OverflowError, TypeError):
|
|
return output
|
|
sample = frame(surface, cs, setpoint, jerk, output, desired, actual)
|
|
if sample is None:
|
|
return output
|
|
return surface.angle_assist(sample)
|