mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
141 lines
7.0 KiB
Python
141 lines
7.0 KiB
Python
"""Real encoder and child-worker tests; no claim of route or physical fit."""
|
|
from dataclasses import replace
|
|
|
|
import pytest
|
|
from opendbc.car.toyota.values import CAR
|
|
from opendbc.car.steering_projection import inspect_projection
|
|
from openpilot.starpilot.flm.fullrate_collector import AppliedEvidence
|
|
from openpilot.starpilot.flm.torque_emission_replay import EmissionHistory, EncoderBranch
|
|
from openpilot.starpilot.flm.torque_eligibility import bounded_context_output, exact_unclipped_pair
|
|
from openpilot.starpilot.flm.tests.test_torque_emission_join import BASE, actual_public_health, cc, interface, settle, source
|
|
from openpilot.starpilot.flm.tests import test_actual_raw_worker as raw_worker
|
|
|
|
|
|
def clipped_context(tmp_path):
|
|
ci = interface(CAR.TOYOTA_COROLLA_TSS2)
|
|
settle(ci, cc(0.0))
|
|
limits = ci.CC.params
|
|
command = cc((limits.STEER_DELTA_UP + 1) / limits.STEER_MAX)
|
|
for _ in range(5):
|
|
stamp = BASE + ci.CC.frame * 10_000_000
|
|
output, packets = ci.apply(command, stamp)
|
|
witness = ci.CC.learning_projection
|
|
if witness is not None and witness.unlimited_raw != witness.final_raw:
|
|
break
|
|
else:
|
|
raise AssertionError('actual registered encoder did not clip')
|
|
assert (witness.can_address, witness.can_data, witness.can_bus) in packets
|
|
assert inspect_projection(ci, command, output, witness) is False
|
|
control = source(ci, tmp_path, float(command.actuators.torque), stamp)
|
|
health = actual_public_health(ci.CP, stamp)
|
|
evidence = AppliedEvidence(stamp, stamp - 1, stamp, 'normalized_torque', float(output.torque),
|
|
True, True, False, False, True, health, witness, ci.CC.frame, stamp)
|
|
return ci, control, evidence
|
|
|
|
|
|
def test_actual_clipped_emission_is_context_not_estimation(tmp_path):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
assert bounded_context_output(output)
|
|
assert not exact_unclipped_pair(control, output)
|
|
history = EmissionHistory(ci.CP, identity=control.identity, baseline_revision=control.baseline_revision)
|
|
assert history.record_carrier(output) and history.record(control, output)
|
|
event = history.event(control.control_ns)
|
|
history.verify(control, output)
|
|
actual = EncoderBranch()
|
|
actual.step(event, control.requested, actual=True)
|
|
assert actual.previous == output.projection.final_raw
|
|
assert event.limited and output.limited # original observations stay immutable
|
|
|
|
|
|
def test_candidate_cannot_increase_clipping_in_context(tmp_path):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
history = EmissionHistory(ci.CP, identity=control.identity, baseline_revision=control.baseline_revision)
|
|
assert history.record_carrier(output) and history.record(control, output)
|
|
event = history.event(control.control_ns)
|
|
branch = EncoderBranch()
|
|
with pytest.raises(ValueError, match='clipping regression'):
|
|
branch.step(event, control.requested + 5 / output.projection.effective_max)
|
|
|
|
|
|
@pytest.mark.parametrize('field', ('fault', 'override'))
|
|
def test_clipped_context_never_relaxes_fault_or_override(tmp_path, field):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
changed = replace(output, **{field: True})
|
|
assert not bounded_context_output(changed)
|
|
history = EmissionHistory(ci.CP)
|
|
assert history.record_carrier(changed)
|
|
assert not history.record(control, changed)
|
|
|
|
|
|
def test_actual_raw_child_accepts_exact_false_hint_without_rewriting_inputs(monkeypatch):
|
|
original = raw_worker.ControlEvidence
|
|
observed = []
|
|
|
|
def asynchronous_hint(**kwargs):
|
|
evidence = original(**kwargs)
|
|
assert not dict(evidence.replay_inputs)['safety_limited']
|
|
changed = replace(evidence, limited=True)
|
|
assert changed.replay_inputs == evidence.replay_inputs
|
|
observed.append(changed)
|
|
return changed
|
|
|
|
monkeypatch.setattr(raw_worker, 'ControlEvidence', asynchronous_hint)
|
|
raw_worker.test_actual_raw_registered_worker_report_reaches_controls()
|
|
assert len(observed) == 3200 and all(row.limited for row in observed)
|
|
|
|
|
|
def test_exact_unclipped_hint_keeps_original_source_native_deadlines(tmp_path):
|
|
from types import SimpleNamespace
|
|
from openpilot.starpilot.flm.tests.test_torque_emission_join import emitted
|
|
|
|
ci = interface(CAR.TOYOTA_COROLLA_TSS2)
|
|
settle(ci, cc(0.01))
|
|
control, output = emitted(ci, cc(0.01), tmp_path)
|
|
control = SimpleNamespace(**vars(control), limited=True, saturated=False, domain='normalized_torque',
|
|
stage_schema_revision='torque-selected-stage-snapshot-v1',
|
|
replay_inputs=(('curvature_limited', 0.0),), state_ns=output.state_ns,
|
|
source_receipt_ns=control.control_ns - 1)
|
|
assert exact_unclipped_pair(control, output)
|
|
assert control.limited and not output.limited
|
|
old_source = SimpleNamespace(**{**vars(control), 'source_receipt_ns': control.control_ns - 500_000_001})
|
|
assert not exact_unclipped_pair(old_source, output)
|
|
assert not exact_unclipped_pair(control, replace(output, state_ns=control.state_ns + 30_000_001,
|
|
apply_ns=control.control_ns + 30_000_002))
|
|
native = SimpleNamespace(**{**vars(control), 'native_required': True, 'native_allowed': True,
|
|
'native_identity': 'a' * 64, 'native_observed_ns': control.control_ns - 1,
|
|
'native_expires_ns': output.apply_ns})
|
|
assert exact_unclipped_pair(native, output)
|
|
assert not exact_unclipped_pair(native, replace(output, apply_ns=output.apply_ns + 1))
|
|
assert not exact_unclipped_pair(SimpleNamespace(**{**vars(control), 'replay_inputs': (('curvature_limited', 1.0),)}), output)
|
|
|
|
|
|
def test_candidate_cannot_reverse_context_clipping_sign(tmp_path):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
history = EmissionHistory(ci.CP)
|
|
assert history.record_carrier(output) and history.record(control, output)
|
|
with pytest.raises(ValueError, match='clipping regression'):
|
|
EncoderBranch().step(history.event(control.control_ns), -0.1)
|
|
|
|
|
|
def test_candidate_uses_own_previous_state_for_context_demand(tmp_path):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
history = EmissionHistory(ci.CP)
|
|
assert history.record_carrier(output) and history.record(control, output)
|
|
branch = EncoderBranch()
|
|
branch.previous = output.projection.previous_torque - 20
|
|
with pytest.raises(ValueError, match='clipping regression'):
|
|
branch.step(history.event(control.control_ns), control.requested)
|
|
|
|
|
|
def test_clipping_guard_runs_in_unscored_warmup_context(tmp_path):
|
|
ci, control, output = clipped_context(tmp_path)
|
|
history = EmissionHistory(ci.CP)
|
|
assert history.record_carrier(output) and history.record(control, output)
|
|
history.verify(control, output)
|
|
# The only emission precedes the scored window; it still advances encoder
|
|
# state and must enforce candidate demand rather than disappearing as warmup.
|
|
window = ((control.control_ns + 1, control.control_ns + 10_000_000),)
|
|
with pytest.raises(ValueError, match='clipping regression'):
|
|
history.rollout({control.control_ns: control.requested + 5 / output.projection.effective_max},
|
|
control.control_ns, control.control_ns, window)
|