Files
StarPilot/openpilot/starpilot/flm/event_windows.py
T
2026-10-18 18:30:31 -05:00

129 lines
5.4 KiB
Python

"""Whole physical event lineage; storage packet IDs never imply independence."""
import hashlib
import math
from openpilot.starpilot.flm.physical_response import kinematic_accel
from openpilot.starpilot.flm.controller_replay import MAX_FRAME_GAP_NS
GROUPING_REVISION = 'whole-source-window-bundles-v3'
def whole_event_windows(engine):
events = []
start = last = neutral = 0
prior_sign = first_sign = reversals = 0
mode = None
previous_window = None
for frame in engine.records:
window = next((index for index, (a, b) in enumerate(engine.replay_windows) if a <= frame.control_ns <= b), None)
if (window is None or window != previous_window or not engine._frame_context(frame) or
last and not 0 < frame.control_ns - last <= MAX_FRAME_GAP_NS):
start = neutral = prior_sign = first_sign = reversals = 0
mode = None
previous_window, last = window, frame.control_ns
if window is None or not engine._frame_context(frame):
continue
goal = kinematic_accel(frame)
if goal is None or not math.isfinite(goal):
raise ValueError('whole episode actual goal missing')
sign = 1 if goal > .03 else -1 if goal < -.03 else 0
if abs(goal) >= .25:
neutral = 0
if mode != 'large':
start, mode, first_sign, neutral = frame.control_ns, 'large', sign, 0
elif sign != first_sign:
if frame.control_ns - start >= 200_000_000:
events.append((window, start, frame.control_ns - 1, 'large'))
start, first_sign = frame.control_ns, sign
prior_sign = sign
continue
if mode == 'large':
if not sign:
neutral = neutral or frame.control_ns
if frame.control_ns - neutral >= 500_000_000:
if frame.control_ns - start <= 30_000_000_000:
events.append((window, start, frame.control_ns, 'large'))
start = neutral = prior_sign = first_sign = reversals = 0
mode = None
elif sign != first_sign:
if 200_000_000 <= frame.control_ns - start <= 30_000_000_000:
events.append((window, start, frame.control_ns - 1, 'large'))
start = neutral = prior_sign = first_sign = reversals = 0
mode = None
else:
neutral = 0
continue
if sign and prior_sign and sign != prior_sign:
if mode is None:
start, mode, first_sign, reversals = frame.control_ns, 'subtle', sign, 0
else:
reversals += 1
if reversals == 2 and sign == first_sign:
if 200_000_000 <= frame.control_ns - start <= 10_000_000_000:
events.append((window, start, frame.control_ns - 1, 'subtle'))
start, first_sign, reversals = frame.control_ns, sign, 0
if mode == 'subtle' and frame.control_ns - start > 10_000_000_000:
start = first_sign = reversals = 0
mode = None
if sign:
prior_sign = sign
return tuple(events)
def regroup_whole_events(engine, raw_packets, objective):
"""Accumulate entire cycles to sample support; never cut a physical event.
Packet boundaries are intentionally ignored. Bundles are disjoint in source
time and cannot cross an omitted/rejected original source window. Episode
kinds remain bound metadata; they do not define independent plant identities.
Every original source, applied witness and physical timestamp is retained.
"""
points = {}
for packet in raw_packets:
if packet['identity'] != engine.identity:
raise ValueError('event storage identity')
for point in packet['samples']:
key = point['control_ns'], point['apply_ns'], point['mono_ns']
if key in points:
raise ValueError('event storage duplicated endpoint')
points[key] = point
ordered = sorted(points.values(), key=lambda point: point['mono_ns'])
blocks = []
pending, boundaries = [], []
previous_context = None
consumed = set()
for window, start, end, kind in whole_event_windows(engine):
context = window
if context != previous_context:
pending, boundaries = [], []
previous_context = context
rows = [point for point in ordered if start <= point['control_ns'] <= end]
if not rows:
# Zero measured rows confer zero support or boundary/ID membership.
# Keep existing support: complete replay from its first through final
# original boundary still includes every intervening source/native tick.
continue
if len(rows) > 240:
pending, boundaries = [], []
continue
if len(pending) + len(rows) > 240:
# Discard insufficient prior support, rather than manufacture a split.
pending, boundaries = [], []
pending.extend(rows)
boundaries.append((start, end, kind))
if len(pending) < 40:
continue
for point in pending:
key = point['control_ns'], point['apply_ns'], point['mono_ns']
if key in consumed:
raise ValueError('event physical endpoint reused')
consumed.add(key)
digest = hashlib.sha256(repr((GROUPING_REVISION, engine.identity, engine.baseline_revision, objective.context_key,
tuple(boundaries))).encode()).hexdigest()
blocks.append({'identity': engine.identity, 'block_id': 'event-v3:' + digest, 'grouping_revision': GROUPING_REVISION,
'cohort': objective.required_cohorts[0], 'stage_revision': 'torque-selected-effect-v2',
'response_revision': 'event-independent-physical-v2', 'event_windows': tuple(boundaries), 'samples': tuple(pending)})
pending, boundaries = [], []
return tuple(blocks)