mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
172 lines
6.6 KiB
Python
172 lines
6.6 KiB
Python
"""Actual effect owner lifecycle; profile fixture is not physical fit proof."""
|
|
|
|
from dataclasses import replace
|
|
|
|
import pytest
|
|
|
|
from openpilot.starpilot.flm.adaptive_profile import Profile, ReplayValidation
|
|
from openpilot.starpilot.flm.canonical_surface_adapter import (
|
|
MANIFEST_SHA256,
|
|
NAMES,
|
|
full_original_anchor,
|
|
full_profile_sha256,
|
|
original_anchor,
|
|
parameters_for,
|
|
)
|
|
from openpilot.starpilot.flm.event_objective import EffectObjective
|
|
from openpilot.starpilot.flm.full_surface_profile import FullSurfaceEnvelope
|
|
from openpilot.starpilot.flm.selected_effects import EffectOwner, EffectTrial
|
|
from openpilot.starpilot.flm.torque_adapter import Admission
|
|
|
|
NOW = 1_000_000_000
|
|
BASE = 'b' * 64
|
|
SOURCE = 'e' * 64
|
|
|
|
|
|
def fixture(*, accumulated=False):
|
|
names = ('ff_gain_left',)
|
|
prior = dict.fromkeys(NAMES, 0.0)
|
|
if accumulated:
|
|
prior['center_taper_max'] = 0.1
|
|
proposed = dict(prior, ff_gain_left=0.004)
|
|
envelope = FullSurfaceEnvelope(
|
|
1,
|
|
MANIFEST_SHA256,
|
|
full_original_anchor(BASE),
|
|
original_anchor(BASE, names),
|
|
SOURCE,
|
|
'c' * 64,
|
|
'selected-controller',
|
|
SOURCE,
|
|
'0',
|
|
BASE,
|
|
full_profile_sha256(BASE, prior),
|
|
full_profile_sha256(BASE, proposed),
|
|
tuple(prior.items()),
|
|
tuple(proposed.items()),
|
|
names,
|
|
'f' * 64,
|
|
)
|
|
profile = Profile(
|
|
'a' * 64,
|
|
1,
|
|
NOW,
|
|
NOW + 500_000_000,
|
|
parameters_for(names),
|
|
(0.004,),
|
|
tuple(f'train{i}' for i in range(4)),
|
|
tuple(f'held{i}' for i in range(4)),
|
|
1.0,
|
|
0.8,
|
|
(('held', 0.1),),
|
|
policy='event_effect_coordinate_v2',
|
|
effect_envelope=envelope,
|
|
)
|
|
validation = ReplayValidation(profile, BASE, 'physical-model', True, True, 0.1)
|
|
objective = EffectObjective('ff.left', names, envelope.trial_anchor, SOURCE)
|
|
context = {
|
|
'cp_sha256': envelope.cp_sha256,
|
|
'controller_revision': envelope.controller_revision,
|
|
'source_epoch': SOURCE,
|
|
'reset_generation': '0',
|
|
'selected_baseline_revision': BASE,
|
|
'context_key': SOURCE,
|
|
'persistent_source_epoch': 'f' * 64,
|
|
'current_full_profile_sha256': envelope.prior_full_profile_sha256,
|
|
}
|
|
owner = EffectOwner(
|
|
profile.identity, objective, selected_baseline_revision=BASE, physical_model_revision='physical-model', original_anchor=envelope.replay_anchor()
|
|
)
|
|
return owner, profile, validation, context
|
|
|
|
|
|
def test_expired_proof_withdraws_all30_not_just_fitted_group():
|
|
owner, p, v, context = fixture(accumulated=True)
|
|
owner.prepare(Admission(p, NOW, True, False, True, v), active=True, current_context=context)
|
|
assert owner.source is not None
|
|
assert 0 < owner.applied_full['center_taper_max'] <= 0.005 * 0.01
|
|
owner.prepare(Admission(p, NOW + 500_000_001, True, False, True, v), active=True, current_context=context)
|
|
assert owner.source is None
|
|
assert all(value == 0 for value in owner.applied_full.values())
|
|
|
|
|
|
@pytest.mark.parametrize('reason', ['manual', 'off', 'inactive', 'unhealthy'])
|
|
def test_withdrawal_and_recovery_slew_all30(reason):
|
|
owner, p, v, context = fixture(accumulated=True)
|
|
owner.prepare(Admission(p, NOW, True, False, True, v), active=True, current_context=context)
|
|
owner.prepare(
|
|
Admission(p, NOW + 10_000_000, reason != 'off', reason == 'manual', reason != 'unhealthy', v), active=reason != 'inactive', current_context=context
|
|
)
|
|
assert owner.source is None and not any(owner.applied_full.values())
|
|
owner.prepare(Admission(p, NOW + 20_000_000, True, False, True, v), active=True, current_context=context)
|
|
assert owner.source is not None
|
|
assert max(abs(value) for value in owner.applied_full.values()) <= 0.005 * 0.01 + 1e-12
|
|
|
|
|
|
def test_changed_current_context_never_leaves_previous_source_attached():
|
|
owner, p, v, context = fixture()
|
|
owner.prepare(Admission(p, NOW, True, False, True, v), active=True, current_context=context)
|
|
with pytest.raises(ValueError):
|
|
owner.prepare(Admission(p, NOW + 10_000_000, True, False, True, v), active=True, current_context=dict(context, source_epoch='d' * 64))
|
|
assert owner.source is None
|
|
|
|
|
|
def test_replay_trial_preserves_nonzero_group_and_outside_group_original_prior():
|
|
owner, p, _, _ = fixture(accumulated=True)
|
|
prior = dict(p.effect_envelope.prior_coordinates)
|
|
prior['ff_gain_left'] = 0.1
|
|
envelope = replace(
|
|
p.effect_envelope,
|
|
prior_coordinates=tuple(prior.items()),
|
|
coordinates=tuple(prior.items()),
|
|
prior_full_profile_sha256=full_profile_sha256(BASE, prior),
|
|
full_profile_sha256=full_profile_sha256(BASE, prior),
|
|
)
|
|
trial = EffectTrial(owner.objective, (0.104,), BASE, envelope.replay_anchor())
|
|
source = trial.source()
|
|
assert source.values['ff_gain_left'] == 0.104
|
|
assert source.values['center_taper_max'] == 0.1
|
|
assert source.values['ff_gain_right'] == 0
|
|
|
|
|
|
def test_trial_group_switch_preserves_full30_state_and_cas_without_step_jump():
|
|
owner, p, v, context = fixture()
|
|
owner.prepare(Admission(p, NOW, True, False, True, v), active=True, current_context=context)
|
|
before = dict(owner.applied_full)
|
|
old = p.effect_envelope
|
|
names = ('turn_in_threshold_reduction_left',)
|
|
prior = dict(old.coordinates)
|
|
proposed = dict(prior, turn_in_threshold_reduction_left=0.004)
|
|
envelope = replace(
|
|
old,
|
|
trial_names=names,
|
|
trial_anchor=original_anchor(BASE, names),
|
|
prior_coordinates=tuple(prior.items()),
|
|
coordinates=tuple(proposed.items()),
|
|
prior_full_profile_sha256=old.full_profile_sha256,
|
|
full_profile_sha256=full_profile_sha256(BASE, proposed),
|
|
)
|
|
p2 = replace(p, sequence=2, parameters=parameters_for(names), coefficients=(0.004,), effect_envelope=envelope)
|
|
v2 = replace(v, profile=p2)
|
|
objective = EffectObjective('friction.left', names, envelope.trial_anchor, SOURCE)
|
|
next_owner = EffectOwner(
|
|
p.identity,
|
|
objective,
|
|
selected_baseline_revision=BASE,
|
|
physical_model_revision='physical-model',
|
|
original_anchor=envelope.replay_anchor(),
|
|
applied_full=before,
|
|
)
|
|
context2 = dict(context, current_full_profile_sha256=old.full_profile_sha256)
|
|
next_owner.prepare(Admission(p2, NOW + 10_000_000, True, False, True, v2), active=True, current_context=context2)
|
|
assert next_owner.trace.before == tuple(before.items())
|
|
assert next_owner.applied_full['ff_gain_left'] >= before['ff_gain_left']
|
|
assert 0 < next_owner.applied_full['turn_in_threshold_reduction_left'] <= 0.005 * 0.01
|
|
assert all(abs(next_owner.applied_full[n] - before[n]) <= 0.005 * 0.01 + 1e-12 for n in NAMES)
|
|
stale = EffectOwner(
|
|
p.identity, objective, selected_baseline_revision=BASE, physical_model_revision='physical-model', original_anchor=envelope.replay_anchor()
|
|
)
|
|
with pytest.raises(ValueError):
|
|
stale.prepare(Admission(p2, NOW + 10_000_000, True, False, True, v2), active=True, current_context=context)
|
|
assert stale.source is None
|