Lab049: define ELRS control and SDR video architecture

This commit is contained in:
LittleSam129
2026-09-08 13:26:06 +03:00
parent ac6cc792b8
commit cdf5396e80
12 changed files with 3139 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
"""Регрессии неизвестной границы, 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"]