Files
StarPilot/openpilot/starpilot/flm/source_plant_fit.py
T
2026-10-21 23:28:39 -05:00

527 lines
30 KiB
Python

from openpilot.starpilot.flm.normalized_trace_archive import guarded_archive
from openpilot.starpilot.flm.normalized_trace_archive import SOURCE_VERSION as NORMALIZED_SOURCE_VERSION,archive_of,record_body,stored_record
"""Child-owned independent source-pose plant experiment.
No legacy RawPoint or external delay enters this module. The controller owner
must first replay the complete original inventory, including excluded context.
Only actual healthy native/CAN endpoints become the arithmetic command tape.
"""
from bisect import bisect_right
from dataclasses import asdict, dataclass
import hashlib
import math
from openpilot.starpilot.flm import plant_lag as lag
PROTOCOL = 'chronological-source-physical-observation-fit-v1'
CONTEXT_VERSION = 'original-source-physical-observation-context-v1'
BLOCK_NS = 4_000_000_000
PURGE_NS = 600_000_000
FIRST_SLOT_NS = 2_600_000_000
@dataclass(frozen=True)
class TemporalPlantWindow:
"""Chronological physical validation block, not an independent maneuver."""
window_id: str
slot: int
checkpoint_ns: int
start_ns: int
end_ns: int # Inclusive integer convention for the pure helper.
crossing_ns: int
def _hash(value):
from openpilot.starpilot.flm.runtime_adapter_registry import digest
return digest(value)
@dataclass(frozen=True)
class PreparedPlant:
binding: lag.SourceBinding
identification: lag.IdentificationResult
context: tuple
model: object
source_poses: tuple
command_pairs: tuple
original_windows: tuple
def compact_receipt(result):
"""Bounded worker report; full original rows stay inside the existing child.
The original decision/model digests still bind the complete training IDs and
exact row inventory. This wire schema adds compact custody digests rather
than exceeding FitProcess's unchanged64KiB result limit.
"""
value = asdict(result)
rows = value.pop('rows')
value.update(receipt_schema='compact-source-plant-receipt-v1',row_count=len(rows),rows_sha256=lag.digest(rows))
if value['decision'] is not None:
decision = value['decision']
ids = decision.pop('train_pose_ids')
decision.update(train_pose_ids_sha256=lag.digest(ids),train_pose_count=len(ids))
decision['compact_decision_sha256'] = lag.digest(decision)
return value
def _pose(value, states, binding, controls,outputs,engine):
"""Recompute calibrated yaw/roll and interpolation from original primitives."""
from openpilot.starpilot.flm.original_source_capture import OriginalSourcePose, validate_pose
from openpilot.common.transformations.orientation import rot_from_euler, euler_from_rot
import numpy as np
def tuples(item):
return tuple(tuples(child) for child in item) if type(item) in (list,tuple) else item
row = dict(value)
for name in ('context', 'angular_velocity_device', 'orientation_ned', 'calibration_rpy', 'left_state', 'right_state',
'source_clock_contract', 'clock_equivalence', 'ordinary_safety'):
row[name] = tuples(row[name])
original = OriginalSourcePose(**row)
validate_pose(original, original.completed_ns)
native = controls.get(original.native_control_ns)
if (native is None or original.native_required is not binding.native_required or
(native.native_required,native.native_identity,native.native_observed_ns,native.native_expires_ns) !=
(original.native_required,original.native_identity,original.native_observed_ns,original.native_expires_ns)):
raise ValueError('plant original pose native owner receipt')
if not original.native_required and original.ordinary_safety[1] != binding.cp_sha256:
raise ValueError('plant ordinary native exact CarParams')
applied = outputs.get((original.native_control_ns,original.native_apply_ns))
if (applied is None or applied.apply_ns != original.native_apply_ns or
not native.enabled or not native.active or not native.valid or native.manual or native.override or native.fault or
not applied.valid or not applied.active or applied.override or applied.fault or not applied.native_allowed or
not engine.applied_matches(native,applied)):
raise ValueError('plant original pose receipt actual native/CAN')
if ((original.identity, original.source_epoch, original.baseline_revision, _hash(original.controller_revision),
original.native_identity, original.calibration, _hash((original.source_clock_contract,original.context[7]))) !=
(binding.identity, binding.source_epoch, binding.baseline_revision, binding.controller_revision,
binding.native_identity, binding.calibration_generation, binding.source_clock_contract_sha256)):
raise ValueError('plant original pose source/native/calibration')
# Both speed brackets must occur in independently reconstructed original
# ControlEvidence, rather than a second copy of supplied pose floats.
if (states.get(original.left_state_ns) != original.left_state or
states.get(original.right_state_ns) != original.right_state):
raise ValueError('plant original state bracket custody')
device_from_calib = rot_from_euler(np.asarray(original.calibration_rpy))
yaw = float((device_from_calib.T @ np.asarray(original.angular_velocity_device))[2])
roll = float(euler_from_rot(rot_from_euler(np.asarray(original.orientation_ned)) @ device_from_calib)[0])
if yaw != original.yaw_rate or roll != original.roll_rad or not math.isfinite(yaw + roll):
raise ValueError('plant original calibrated pose recomputation')
# The estimate identifies exact per-pose extrinsics; lifecycle remains stable.
if original.calibration_estimate != _hash(tuple(original.calibration_rpy)):
raise ValueError('plant original extrinsics estimate')
return lag.SourcePose(pose_id=_hash(asdict(original)),mono_ns=original.mono_ns,received_ns=original.received_ns,
source_timestamp_ns=original.source_timestamp_ns,source_clock_domain=original.source_clock_domain,
clock_offset_ns=original.clock_offset_ns,clock_uncertainty_ns=original.clock_uncertainty_ns,
source_lower_ns=original.source_lower_ns,source_upper_ns=original.source_upper_ns,
clock_generation=original.clock_generation,speed_mps=original.speed_mps,
calibrated_yaw_rate=yaw,calibrated_roll_rad=roll,binding_digest=lag.binding_digest(binding))
def _command(control, output, engine, binding):
from openpilot.starpilot.flm.torque_eligibility import exact_unclipped_pair, consumed_sources_current
from openpilot.starpilot.flm.torque_emission_replay import delivered
from opendbc.car.steering_projection import projection_to_dict
# Preserve the exact original guard order; label a bounded decline in this
# child-only phase instead of concealing which original endpoint failed.
rejected = (
('disabled',lambda:not control.enabled),('manual',lambda:control.manual),
('inactive',lambda:not control.active),('invalid_control',lambda:not control.valid),
('required_native_denied',lambda:control.native_required and not control.native_allowed),
('override',lambda:control.override),('fault',lambda:control.fault),
('saturated',lambda:control.saturated),('model_wobble',lambda:control.model_wobble),
('controller_chatter',lambda:control.controller_chatter),
('limited_control',lambda:control.limited and not exact_unclipped_pair(control,output)),
('legacy_coordinates',lambda:any(control.applied_coordinates)),
('command_clock',lambda:output.control_ns != control.control_ns),
('invalid_output',lambda:not output.valid),('inactive_output',lambda:not output.active),
('output_override',lambda:output.override),('output_fault',lambda:output.fault),
('limited_output',lambda:output.limited),('output_native_denied',lambda:not output.native_allowed),
('consumed_original_sources',lambda:not consumed_sources_current(control,output)),
('actual_native_CAN',lambda:not engine.applied_matches(control,output)),
)
for reason,predicate in rejected:
if predicate():
raise ValueError('plant actual endpoint '+reason+' control_ns='+str(control.control_ns)+' apply_ns='+str(output.apply_ns))
return lag.DeliveredCommand(control.control_ns, control.state_ns, output.apply_ns, -delivered(output),
control.source_receipt_ns, control.native_observed_ns, control.native_expires_ns,
output.carrier_frame, output.carrier_observed_ns, _hash(projection_to_dict(output.projection)),
_hash(record_body(control,engine)), _hash({**asdict(output), 'projection': projection_to_dict(output.projection)}),
control.identity, control.source_epoch, control.baseline_revision, control.native_identity,control.native_required)
def native_output(control, value):
"""Same evidence-only native reconstruction as FullrateCollector._join_emission.
Original raw output stays in the context packet and packet hash. A current
independently verified control lease supplies the native permission used by
the existing healthy EmissionHistory; a parent output flag alone cannot.
"""
from openpilot.starpilot.flm.torque_raw_fit import output_from_dict
return _native_output_from_original(control, output_from_dict(value))
def _native_output_from_original(control, output):
"""Same native lease reconstruction from an already parsed original body.
Private caller-local reuse only, no cached permission or packet authority.
"""
from dataclasses import replace
from openpilot.starpilot.flm.fullrate_collector import AppliedEvidence
if control.native_required:
if (not control.native_allowed or not control.native_identity or
not 0 < control.native_observed_ns <= control.control_ns <= output.apply_ns <= control.native_expires_ns or
control.native_expires_ns-control.native_observed_ns > 200_000_000):
return output # Context only; _command rejects if a pose selects it.
if type(output) is not AppliedEvidence or output.native_allowed is not True:
output = replace(output,native_allowed=True)
return output
def _physical_observation(control, output, engine, binding, original_output):
"""New physical-only type. Legacy _command/stage/current stay strict."""
from openpilot.starpilot.flm.physical_input_observation import from_original
return from_original(control, output, engine, binding, original_output=original_output)
def _required_controls(controls, poses, *, stamp_field="control_ns"):
"""Exact closed union of every all-lag lookback, in original control order."""
merged = []
for lower, upper in sorted((p.source_lower_ns-lag.LAGS_NS[-1]-2*lag.JOIN_NS,
p.source_upper_ns) for p in poses):
if merged and lower <= merged[-1][1]:
merged[-1] = (merged[-1][0], max(merged[-1][1], upper))
else:
merged.append((lower, upper))
starts = tuple(lower for lower, _ in merged)
def included(stamp):
index = bisect_right(starts, stamp)-1
return index >= 0 and stamp <= merged[index][1]
return tuple(control for control in controls.values() if included(getattr(control,stamp_field)))
BOOTSTRAP_SLOT_POLICY = 'interval-contained-bootstrap-slots-v1'
SOURCE_COMPLETE_SLOT_POLICY = 'source-complete-fixed-bootstrap-slots-v2'
def bootstrap_slot_rows(rows, start_ns, end_ns):
selected = []
for row in rows:
if type(row) is not dict or type(row.get('mono_ns')) is not int:
raise ValueError('exact original bootstrap pose clock')
if not start_ns <= row['mono_ns'] <= end_ns:
continue
low, high = row.get('source_lower_ns'), row.get('source_upper_ns')
if type(low) is not int or type(high) is not int or not 0 < low <= row['mono_ns'] <= high:
raise ValueError('known original bootstrap source interval required')
if start_ns <= low and high <= end_ns:
selected.append(row)
return tuple(selected)
@guarded_archive(position=1)
def prepare(packet, engine, binding, *, slot_policy=None, support_policy=lag.STRICT_SUPPORT):
"""Exactly one earliest octet; child verifies boundaries before reading y.
`engine` is the complete original inventory replay returned by the actual
registered-controller/native archive verifier. A caller assertion is not a
substitute for that source/native/controller replay.
"""
from openpilot.starpilot.flm.original_context_receipts import verify as verify_context, reuse_verified_context
from openpilot.starpilot.flm.torque_raw_fit import output_from_dict
from openpilot.starpilot.flm.physical_response import PhysicalModel
from openpilot.starpilot.flm.physical_input_observation import (
PROTOCOL as OBSERVATION_PROTOCOL, VerifiedPhysicalTape, physical_tape_digest,
)
from openpilot.starpilot.flm.runtime_adapter_registry import canonical_physical_context
from openpilot.starpilot.flm.physical_input_observation import _physical_asdict
if (packet.get('version') not in ('unpaired-original-source-v8',NORMALIZED_SOURCE_VERSION) or
packet.get('encoding') != 'primitive-snapshot-v1' or packet.get('qualified') is not False or
packet.get('plant_lag_ns') is not None or not isinstance(binding, lag.SourceBinding) or
len(engine.records) > 12000 or len(packet['source_poses']) > 3116):
raise ValueError('plant independent original packet/bounds')
if (packet.get('version') == NORMALIZED_SOURCE_VERSION) != (archive_of(engine) is not None):
raise ValueError('explicit normalized physical source owner')
if slot_policy not in (None, BOOTSTRAP_SLOT_POLICY, SOURCE_COMPLETE_SLOT_POLICY):
raise ValueError('explicit physical calibration slot policy')
frames = tuple(engine.records)
controls = reuse_verified_context(packet['context_receipts'],engine,frames)
if controls is None:
controls = verify_context(packet['context_receipts'],engine,frames)
q = frames[0].control_ns
if q not in dict(engine.checkpoints):
raise ValueError('plant original first checkpoint unavailable')
final_end = q+FIRST_SLOT_NS+7*(BLOCK_NS+PURGE_NS)+BLOCK_NS
crossings = tuple(frame for frame in frames if frame.control_ns >= final_end)
if not crossings:
return {'status':'collecting','reason':'chronological_plant_slots_not_sealed','profile':None,'validation':None}
# Seal each slot by its ORIGINAL crossing tick in the same continuous healthy
# prefix. No fault, buffer tail or source-window closure is called independence.
prefix = tuple(frame for frame in frames if frame.control_ns <= crossings[0].control_ns)
if any(not frame.healthy or frame.manual or
not controls[frame.control_ns].enabled or not controls[frame.control_ns].active or
not controls[frame.control_ns].valid or controls[frame.control_ns].manual or
controls[frame.control_ns].override or controls[frame.control_ns].fault for frame in prefix):
raise ValueError('plant chronological prefix contains invalid physical source context')
windows = []
for slot in range(8):
start = q+FIRST_SLOT_NS+slot*(BLOCK_NS+PURGE_NS)
end = start+BLOCK_NS
crossing = next(frame.control_ns for frame in frames if frame.control_ns >= end)
original = tuple(_physical_asdict(controls[frame.control_ns],_trace_owner=engine) for frame in prefix if start <= frame.control_ns < end)
window_id = _hash((PROTOCOL,lag.binding_digest(binding),q,slot,start,end,original))
if slot_policy is not None:
window_id = _hash((slot_policy,window_id))
windows.append(TemporalPlantWindow(window_id,slot,q,start,end-1,crossing))
outputs, original_outputs = {}, {}
for row in packet['context_receipts']['outputs']:
stamp = row['control_ns']
control = controls[stamp] # Preserve original lookup-before-parse order.
original = output_from_dict(row)
key=(stamp,original.apply_ns)
outputs[key] = _native_output_from_original(control,original)
original_outputs[key] = original
states = {}
for control in controls.values():
values = dict(control.replay_inputs)
state = (float(values['vEgo']), float(values['steeringAngleDeg']))
if control.state_ns in states and states[control.state_ns] != state:
raise ValueError('plant conflicting original state bracket')
states[control.state_ns] = state
poses, selected = [], []
# Predeclared exact eight slots; a short/long slot rejects this experiment.
# Never skip/top-up/slide a slot after command or response outcomes are known.
for window in windows:
raw = (bootstrap_slot_rows(packet['source_poses'], window.start_ns, window.end_ns) if slot_policy is not None else
tuple(row for row in packet['source_poses'] if window.start_ns <= row['mono_ns'] <= window.end_ns))
if not 40 <= len(raw) <= 240:
return {'status':'collecting','reason':'chronological_plant_slot_pose_count',
'profile':None,'validation':None,'slot':window.slot,'poseCount':len(raw)}
selected.append((window, raw))
if len(selected) != 8:
return {'status': 'collecting', 'reason': 'independent_original_source_window_support',
'profile': None, 'validation': None, 'temporalBlocks': len(selected)}
if slot_policy == SOURCE_COMPLETE_SLOT_POLICY:
# Preserve the FULL original packet, native reconstruction and fixed slots.
# Determine eligibility solely from original input metadata. The independent
# physical verifier below still has to admit EVERY command of EVERY
# retained all-lag lookback; parent-provided masks are never trusted here.
from types import SimpleNamespace
from openpilot.starpilot.flm.live_tracking_bootstrap_cohorts import source_complete_groups
from openpilot.starpilot.flm.torque_eligibility import consumed_sources_current
original_events = tuple(engine.emissions.events.values())
stamps = tuple(event.apply_ns for event in original_events)
source_known = []
for event in original_events:
control = controls.get(event.control_ns)
output = outputs.get((event.control_ns,event.apply_ns))
known = (control is not None and output is not None and event.valid and event.active and
not event.override and not event.fault and event.native_allowed and
control.valid and control.active and control.enabled and not control.manual and
not control.override and not control.fault and consumed_sources_current(control,output))
source_known.append(0. if known else None)
# Only source clock intervals are exposed to selection, never response
# yaw/roll/force, tracking error, residual or a proposed tuning coordinate.
clocks = tuple(tuple(SimpleNamespace(mono_ns=row['mono_ns'],source_lower_ns=row['source_lower_ns'],
source_upper_ns=row['source_upper_ns']) for row in raw) for _,raw in selected)
eligible = source_complete_groups(stamps,source_known,clocks)
replacement = []
for (window,raw), group in zip(selected,eligible,strict=True):
included = {row.mono_ns for row in group}
retained = tuple(row for row in raw if row['mono_ns'] in included)
if not 40 <= len(retained) <= 240:
return {'status':'collecting','reason':'source_complete_fixed_slot_count',
'profile':None,'validation':None,'slot':window.slot,'poseCount':len(retained)}
replacement.append((window,retained))
selected = replacement
declared = []
for window, raw in selected:
block_poses = tuple(_pose(row, states, binding,controls,outputs,engine) for row in raw)
if any(p.source_lower_ns < window.start_ns or p.source_upper_ns > window.end_ns for p in block_poses):
raise ValueError('plant source interval crosses immutable temporal slot')
poses.extend(block_poses)
declared.append(lag.CalibrationWindow(_hash((PROTOCOL, window.window_id)), window.start_ns,
window.end_ns, tuple(p.pose_id for p in block_poses), _hash(asdict(window))))
# Verify every original command in all-candidate lookbacks once. An invalid
# command is an unavailable experiment; never silently drop it from the tape.
# Controller ticks and Card applications are different clocks. Preserve
# every actual emission, including repeated CCs; a superseded CC that was
# never sent is controller context, not an invented physical plant input.
required = _required_controls(engine.emissions.events,poses,stamp_field="apply_ns")
if slot_policy == SOURCE_COMPLETE_SLOT_POLICY:
events = tuple(engine.emissions.events.values())
stamps = tuple(event.apply_ns for event in events)
covered = set()
for pose in poses:
first = bisect_right(stamps,pose.source_lower_ns-lag.LAGS_NS[-1])-1
last = bisect_right(stamps,pose.source_upper_ns)-1
if first < 0 or last < first:
return {'status':'collecting','reason':'source_complete_lookback_missing','profile':None,'validation':None}
covered.update(range(first,last+1))
required = tuple(event for index,event in enumerate(events) if index in covered)
if not required or any((event.control_ns,event.apply_ns) not in outputs for event in required):
return {'status':'collecting','reason':'plant_original_application_receipt_missing','profile':None,'validation':None}
pairs = tuple((controls[event.control_ns], outputs[event.control_ns,event.apply_ns]) for event in required)
try:
commands = tuple(_physical_observation(control, output, engine, binding,
original_outputs[control.control_ns,output.apply_ns]) for control, output in pairs)
except ValueError as error:
return {'status':'collecting','reason':'plant_common_original_command_unavailable',
'detail':str(error),'profile':None,'validation':None}
verifier_hash = hashlib.sha256(__loader__.get_data(__file__)).hexdigest()
tape = VerifiedPhysicalTape(OBSERVATION_PROTOCOL, commands, lag.binding_digest(binding),
physical_tape_digest(binding, commands), verifier_hash)
if support_policy != lag.STRICT_SUPPORT and slot_policy not in (BOOTSTRAP_SLOT_POLICY,SOURCE_COMPLETE_SLOT_POLICY):
raise ValueError('training-domain support is response-bootstrap only')
result = lag.identify_physical_plant_lag(binding, tape, tuple(poses), tuple(declared), support_policy=support_policy)
if result.status != 'physical_affine_test_passed':
return {'status': result.status, 'reason': result.reason, 'profile': None, 'validation': None,
'plant_identification': compact_receipt(result)}
# Freeze this experiment context before child fitting. The exact packet binds
# every decision input, and the model itself carries the chosen lag. A parent
# cannot promote an old external-seed report into this context/version.
context = experiment_context(packet, binding)
canonical = canonical_physical_context(context)
# The new typed observation protocol passed the SAME affine, direction,
# excitation and ALL-row per-block .05 arithmetic. Bootstrap may retain
# an explicitly training-domain-only model; its range never expands to cover
# unseen inputs and every heldout block still needs in-domain support.
# Do not manufacture legacy
# PhysicalRow(healthy=True,saturated=False) from tagged originals merely to
# get past the legacy unclipped-input policy. A numeric plant model is NOT
# permission; its context binds this exact packet, tagged tape and decision.
decision = result.decision
if (decision.protocol != lag.PHYSICAL_PROTOCOL or decision.slope <= 0 or
not lag.heldout_supported(result) or
decision.train_block_ids != tuple(window.block_id for window in declared[:4]) or
tuple(block for block, _, _ in result.heldout_blocks) != tuple(window.block_id for window in declared[4:])):
raise ValueError('physical observation original affine receipt mismatch')
model = PhysicalModel(engine.identity, canonical, 'normalized_torque', decision.lag_ns,
decision.slope, decision.intercept, decision.command_low, decision.command_high,
decision.train_block_ids, tuple(window.block_id for window in declared[4:]),
max(error for _, error, _ in result.heldout_blocks))
return PreparedPlant(binding, result, context, model, tuple(poses), pairs, tuple(window for window, _ in selected))
def experiment_context(packet, binding):
version = 'original-source-feedback-mean-context-v1' if 'feedback_observations' in packet else CONTEXT_VERSION
return (version, lag.binding_digest(binding), _hash(packet))
def source_binding(packet, engine):
"""Binding from exact selected factory/anchor and original observer context."""
from openpilot.starpilot.flm.runtime_adapter_registry import car_params_digest
first = packet['source_poses'][0]
context = tuple(first['context'])
if len(context) != 15 or any(tuple(row['context']) != context for row in packet['source_poses']):
raise ValueError('plant stable original source observer context')
session, identity, generation, epoch, baseline, revision, calibration, clock_context, context_clock, native, regime, *geometry = context
cp = engine.factory.cp
if (identity != engine.identity or epoch != engine.objective.context_key or baseline != engine.baseline_revision or
revision != engine.original_anchor['controller_revision'] or geometry !=
[float(cp.steerRatio), float(cp.wheelbase), float(cp.centerToFront), float(cp.mass)] or
first['identity'] != identity or first['source_epoch'] != epoch or first['native_identity'] != native):
raise ValueError('plant selected factory/source observer binding')
contract = tuple(first['source_clock_contract'])
if tuple(clock_context) != ('original_source_clock_generation', first['clock_generation']):
raise ValueError('plant original interval clock context mismatch')
if any(tuple(row['source_clock_contract']) != contract for row in packet['source_poses']):
raise ValueError('plant original source clock contract changed')
required = packet['context_receipts']['controls'][0]['native_required']
if type(required) is not bool or any(row['native_required'] is not required for row in packet['context_receipts']['controls']):
raise ValueError('plant original native-required contract changed')
return lag.SourceBinding(identity, car_params_digest(cp), _hash(engine.original_anchor), _hash(revision),
epoch, baseline, native, calibration, _hash((generation, regime, geometry)), session, _hash((contract,clock_context)),required)
STAGE_MEMBERSHIP_PROTOCOL = 'original-completed-whole-source-membership-v1'
def completed_stage_windows(engine, cutoff_ns):
"""Goal/source geometry only; no response, fit, clipping or loss selection.
Whole original windows come from the actual verified replay. Incomplete
source prefixes/tails are not completed maneuvers. Their frames, poses and
native packets remain archived and in every counterfactual rollout.
"""
from openpilot.starpilot.flm.controller_replay import ControllerReplay
from openpilot.starpilot.flm.event_controller_replay import EventControllerReplay
from openpilot.starpilot.flm.event_windows import whole_event_windows
if (type(engine) is not EventControllerReplay or type(engine.engine) is not ControllerReplay or
engine.failed or not engine.raw_mode or type(cutoff_ns) is not int or cutoff_ns <= 0 or
not 1 <= len(engine.records) <= 12000):
raise ValueError('stage membership requires actual original replay')
return tuple(window for window in whole_event_windows(engine) if window[1] > cutoff_ns)
def _stage_context_endpoint(control, output, engine):
"""Excluded goal prefix/tail stays HARD context, never a fitting input.
Original performance labels remain unchanged/archived. Manual, source,
clocks, native permission, override, fault and actual CAN are mandatory even
for an endpoint outside the completed goal windows.
"""
from openpilot.starpilot.flm.torque_eligibility import consumed_sources_current
if (not control.enabled or control.manual or not control.active or not control.valid or
control.native_required and not control.native_allowed or control.override or control.fault or
any(control.applied_coordinates) or output.control_ns != control.control_ns or
not output.valid or not output.active or output.override or output.fault or not output.native_allowed or
not consumed_sources_current(control,output) or not engine.applied_matches(control,output)):
raise ValueError('plant excluded whole-source prefix hard endpoint control_ns='+str(control.control_ns))
def stage_control_member(windows, control_ns):
"""Pure membership predicate; it confers no source/native/application proof."""
return any(start <= control_ns <= end for _,start,end,_ in windows)
def rejoin_stage(poses, commands, prepared, engine):
"""Associate NEW stage poses with original delivered commands at locked lag."""
from openpilot.starpilot.flm.torque_raw_fit import control_from_dict
from openpilot.starpilot.flm.torque_emission_replay import delivered
from opendbc.car.steering_projection import projection_to_dict
if not isinstance(prepared, PreparedPlant):
raise ValueError('plant frozen stage plan required')
cutoff, delay = prepared.identification.cutoff_ns, prepared.model.lag_ns
def parsed_pair(c,o):
control = control_from_dict(c,trace_archive=archive_of(engine))
return control,native_output(control,o)
ordered = tuple(parsed_pair(c,o) for c, o in commands)
stamps = tuple(output.apply_ns for _, output in ordered)
if any(right <= left for left, right in zip(stamps, stamps[1:], strict=False)):
raise ValueError('plant original stage command order')
windows = completed_stage_windows(engine,cutoff)
previous, previous_command, rows = cutoff, cutoff, []
for pose in poses:
index = bisect_right(stamps, pose.source_lower_ns-delay)-1
upper_index = bisect_right(stamps,pose.source_upper_ns-delay)-1
if index < 0 or index != upper_index or pose.source_upper_ns-delay-stamps[index] > lag.JOIN_NS:
raise ValueError('plant original stage lag support')
control, output = ordered[index]
if min(control.control_ns, output.apply_ns, pose.mono_ns) <= cutoff or pose.mono_ns <= previous:
raise ValueError('plant stage strictly after physical cutoff')
previous = pose.mono_ns # Preserve ordering of EVERY original stage pose.
if not stage_control_member(windows,control.control_ns):
_stage_context_endpoint(control,output,engine)
continue # Original goal geometry only; no performance/y/native selection.
if output.apply_ns <= previous_command:
raise ValueError('plant stage strictly after physical cutoff')
# Invoke exactly the same source/native/output guards again for NEW joins.
# The binding used by _command is only carried metadata; actual verification
# is engine.applied_matches, not the earlier calibration tape boolean.
_command(control, output, engine, prepared.binding)
previous_command = output.apply_ns
rows.append({'control_ns': control.control_ns, 'apply_ns': output.apply_ns, 'mono_ns': pose.mono_ns,
'control': stored_record(control), 'output': {**asdict(output), 'projection': projection_to_dict(output.projection)},
'physical_context': prepared.context, 'physical': {'applied_command': delivered(output),
'lateral_accel': pose.speed_mps*pose.calibrated_yaw_rate-9.81*math.sin(pose.calibrated_roll_rad),
'speed_mps': pose.speed_mps, 'response_ns': pose.mono_ns}})
return tuple(rows)