67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""Регрессии неизвестной границы, CFO и полной задержки доставки Lab049."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from experiments import lab049_bpsk_symbol_rate as lab
|
|
from experiments import lab049_independent_iq as independent
|
|
|
|
|
|
def test_fractional_symbol_rate_is_not_silently_rounded() -> None:
|
|
with pytest.raises(ValueError):
|
|
lab.phy_mode(20000.5)
|
|
|
|
|
|
@pytest.mark.parametrize("rate", (50_000, 100_000))
|
|
def test_unknown_frame_start_and_residual_cfo(rate: int) -> None:
|
|
mode = lab.phy_mode(rate)
|
|
payload = bytes(range(256))
|
|
plan = lab.lab045.build_transmission_plan(payload)
|
|
iq = lab.lab045.lab044.signed_iq_bytes_to_complex64(
|
|
lab.build_parameterized_tx_cs8(plan, mode))
|
|
iq *= np.exp(1j * (2.7 + 2 * np.pi * 10 * np.arange(iq.size)
|
|
/ mode.sample_rate_hz)).astype(np.complex64)
|
|
stream = np.pad(iq, (12007, 12003))
|
|
result = independent.receive_stream(stream, mode)
|
|
assert result.termination_reason == "END_OF_STREAM"
|
|
assert result.crc_pass_count == 1
|
|
assert result.accepted_frames[0].reception.payload_bytes == payload
|
|
assert result.accepted_frames[0].estimated_cfo_hz == pytest.approx(10, abs=1)
|
|
assert lab.lab045.SYMBOL_RATE == 20_000
|
|
assert lab.lab045.lab044.lab043.SAMPLES_PER_SYMBOL == 400
|
|
|
|
|
|
def test_receiver_mode_restores_constants_on_exception() -> None:
|
|
with pytest.raises(RuntimeError):
|
|
with independent.receiver_mode(lab.phy_mode(100_000)):
|
|
raise RuntimeError("контроль выхода")
|
|
assert lab.lab045.SAMPLES_PER_SYMBOL == 400
|
|
assert lab.lab045.lab044.SAMPLES_PER_SYMBOL == 400
|
|
|
|
|
|
def test_noise_only_does_not_produce_a_packet() -> None:
|
|
generator = np.random.default_rng(49)
|
|
noise = (generator.standard_normal(180000)
|
|
+ 1j * generator.standard_normal(180000)).astype(np.complex64)
|
|
result = independent.receive_stream(noise, lab.phy_mode(100_000))
|
|
assert result.crc_pass_count == 0
|
|
|
|
|
|
def test_emergency_delivery_includes_its_own_frame() -> None:
|
|
probe = lab.priority_probe(0.02883)
|
|
assert probe["emergency_max_video_blocking_ms"] == pytest.approx(28.83)
|
|
assert probe["emergency_worst_delivery_ms"] == pytest.approx(57.66)
|
|
assert not probe["emergency_delivery_deadline_met_in_worst_phase"]
|
|
|
|
|
|
def test_deadlines_use_complete_delivery_and_drain_is_separate() -> None:
|
|
result = lab.loaded_scheduler_scenario(0.05766)
|
|
assert result["deadline_misses"]["CONTROL"] == 6
|
|
assert result["deadline_misses"]["EMERGENCY"] == 1
|
|
assert result["active_window_delivered_packets"]["TELEMETRY"] == 0
|
|
assert result["active_window_delivery_latency"]["TELEMETRY"]["count"] == 0
|
|
assert result["delivery_latency_until_drain_end"]["CONTROL"]["max_ms"] == pytest.approx(105.64)
|
|
assert result["drain_end_seconds"] > result["source_duration_seconds"]
|