mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
140 lines
5.8 KiB
Python
140 lines
5.8 KiB
Python
"""Controller-timed automatic pulses and the stationary manual confirmation rhythm."""
|
|
from dataclasses import replace
|
|
|
|
import pyray as rl
|
|
import pytest
|
|
|
|
from openpilot.starpilot.ui.onroad_state import ObservationKind, OnroadState, SpeedLimitObservation
|
|
from openpilot.starpilot.ui.speed_limit_pulse import SpeedLimitPulse
|
|
|
|
|
|
def rgba(color):
|
|
return tuple(color) if isinstance(color, tuple) else (color.r, color.g, color.b, color.a)
|
|
|
|
|
|
def state(automatic=True, **changes):
|
|
obs = SpeedLimitObservation(ObservationKind.VALID, 'dashboard', 20., pending_speed_limit_mps=20.,
|
|
automatic_adoption_pending=automatic, pending_since_ns=10_000_000_000)
|
|
return replace(OnroadState(True, False, 10., 80., obs, drive_frame=1), **changes)
|
|
|
|
|
|
def test_three_slow_flashes_stop_at_the_controller_deadline():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(state(), 10.25)
|
|
assert pulse.start_time == 10.
|
|
for now in (10., 11., 12., 13., 14.):
|
|
assert rgba(pulse.color(rl.WHITE, now)) == rgba(rl.WHITE)
|
|
for now in (10.5, 11.5, 12.5):
|
|
assert rgba(pulse.color(rl.WHITE, now)) == (188, 132, 255, 255)
|
|
|
|
|
|
def test_manual_confirmation_repeats_two_gentle_pulses_for_ten_seconds_without_tinting_content():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(state(False), 10.)
|
|
base = rl.Color(244, 247, 245, 96)
|
|
for cycle in (0., 3.2, 6.4):
|
|
for elapsed in (.35, 1.25):
|
|
now = 10. + cycle + elapsed
|
|
pulse.update(state(False), now)
|
|
assert pulse.confirmation_strength(now) == pytest.approx(1.)
|
|
assert rgba(pulse.color(base, now)) == rgba(base)
|
|
for elapsed in (0., .8, 1.6, 2.1, 2.59, 3., 3.19):
|
|
assert rgba(pulse.color(base, 10. + cycle + elapsed)) == rgba(base)
|
|
assert pulse.confirmation_strength(9.9) == 0.
|
|
assert pulse.confirmation_strength(19.95) > .9
|
|
assert pulse.confirmation_strength(20.) == 0.
|
|
assert pulse.confirmation_strength(22.75) == 0.
|
|
|
|
|
|
@pytest.mark.parametrize('kind', list(ObservationKind))
|
|
def test_finished_or_unavailable_manual_decision_stops_pulsing(kind):
|
|
pulse = SpeedLimitPulse()
|
|
current = state(False)
|
|
pulse.update(current, 10.)
|
|
observation = replace(current.speed_limit, kind=kind, speed_limit_mps=20. if kind == ObservationKind.VALID else None,
|
|
pending_speed_limit_mps=None, last_received_speed_limit_mps=20.)
|
|
pulse.update(replace(current, speed_limit=observation), 10.1)
|
|
assert rgba(pulse.color(rl.WHITE, 10.35)) == rgba(rl.WHITE)
|
|
assert pulse.start_time == -float('inf')
|
|
|
|
|
|
def test_manual_widget_reappearance_and_new_decision_use_controller_time():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(state(False), 10., visible=False)
|
|
pulse.update(state(False), 13.2)
|
|
assert pulse.confirmation_strength(13.55) == pytest.approx(1.)
|
|
current = state(False)
|
|
current = replace(current, speed_limit=replace(current.speed_limit, pending_since_ns=13_000_000_000,
|
|
speed_limit_mps=25., pending_speed_limit_mps=25.))
|
|
pulse.update(current, 13.)
|
|
assert rgba(pulse.color(rl.WHITE, 13.)) == rgba(rl.WHITE)
|
|
assert pulse.confirmation_strength(13.35) == pytest.approx(1.)
|
|
|
|
|
|
def test_missing_pending_timestamp_starts_once_and_resets_for_a_new_drive():
|
|
pulse = SpeedLimitPulse()
|
|
current = state(False)
|
|
current = replace(current, speed_limit=replace(current.speed_limit, pending_since_ns=0))
|
|
pulse.update(current, 10.)
|
|
pulse.update(current, 10.35)
|
|
assert pulse.confirmation_strength(10.35) == pytest.approx(1.)
|
|
pulse.update(replace(current, drive_frame=2), 20.)
|
|
assert pulse.start_time == 20.
|
|
|
|
|
|
def test_switching_between_manual_and_automatic_uses_the_current_rhythm():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(state(), 10.)
|
|
pulse.update(state(False), 10.)
|
|
assert pulse.confirmation_strength(10.35) == pytest.approx(1.)
|
|
pulse.update(state(), 10.)
|
|
assert rgba(pulse.color(rl.WHITE, 10.5)) == (188, 132, 255, 255)
|
|
assert rgba(pulse.color(rl.WHITE, 13.)) == rgba(rl.WHITE)
|
|
|
|
|
|
def test_reappearing_widget_uses_remaining_controller_time():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(state(), 10., visible=False)
|
|
pulse.update(state(), 12.)
|
|
assert rgba(pulse.color(rl.WHITE, 12.5)) == (188, 132, 255, 255)
|
|
assert rgba(pulse.color(rl.WHITE, 13.)) == rgba(rl.WHITE)
|
|
|
|
|
|
def display_state(limit=20., **changes):
|
|
current = state()
|
|
return replace(current, speed_limit=replace(current.speed_limit, speed_limit_mps=limit,
|
|
pending_speed_limit_mps=None, automatic_adoption_pending=False, action_enabled=False, pending_since_ns=0), **changes)
|
|
|
|
|
|
def test_display_only_new_limit_pulses_without_restarting_on_repeated_messages():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(display_state(), 10.)
|
|
pulse.update(display_state(), 10.5)
|
|
assert rgba(pulse.color(rl.WHITE, 10.5)) == (188, 132, 255, 255)
|
|
pulse.update(display_state(), 13.5)
|
|
assert rgba(pulse.color(rl.WHITE, 13.5)) == rgba(rl.WHITE)
|
|
pulse.update(display_state(25.), 14.)
|
|
pulse.update(display_state(25.), 14.5)
|
|
assert rgba(pulse.color(rl.WHITE, 14.5)) == (188, 132, 255, 255)
|
|
|
|
|
|
def test_display_only_hidden_stale_and_reappearance_do_not_invent_new_limit():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(display_state(), 10., visible=False)
|
|
pulse.update(display_state(), 10.5)
|
|
assert rgba(pulse.color(rl.WHITE, 10.5)) == rgba(rl.WHITE)
|
|
current = display_state()
|
|
stale = replace(current, speed_limit=replace(current.speed_limit, kind=ObservationKind.STALE,
|
|
speed_limit_mps=None, last_received_speed_limit_mps=25.))
|
|
pulse.update(stale, 11.)
|
|
assert rgba(pulse.color(rl.WHITE, 11.5)) == rgba(rl.WHITE)
|
|
pulse.update(display_state(25.), 12.)
|
|
assert rgba(pulse.color(rl.WHITE, 12.5)) == rgba(rl.WHITE)
|
|
|
|
|
|
def test_display_only_new_drive_can_highlight_its_first_limit():
|
|
pulse = SpeedLimitPulse()
|
|
pulse.update(display_state(), 10.)
|
|
pulse.update(display_state(drive_frame=2), 20.)
|
|
assert rgba(pulse.color(rl.WHITE, 20.5)) == (188, 132, 255, 255)
|