Files
SDR-Rover/tests/test_lab063_tx_underrun_localization.py
2026-09-14 15:11:46 +03:00

112 lines
4.7 KiB
Python

"""Lab063: автономные проверки локального сопоставления без SDR."""
from __future__ import annotations
import numpy as np
from experiments import lab063_tx_underrun_localization as lab
def test_underrun_scale_is_exact():
result = lab.underrun_scale(16_672)
assert result["complex_samples"] == 8_336
assert result["duration_seconds"] == 0.001042
assert result["symbols"] == 521
assert np.isclose(result["fraction_of_packet"], 8_336 / 92_256)
def test_normalized_match_recovers_offset_gain_and_low_residual():
random_generator = np.random.default_rng(6301)
transmitted = (random_generator.choice((-1.0, 1.0), 2048)
* np.exp(1j * np.arange(2048) * 0.01))
gain = 0.35 - 0.2j
search = np.concatenate((np.zeros(321, complex), gain * transmitted, np.zeros(400, complex)))
result = lab.normalized_match(transmitted, search)
assert result["offset"] == 321
assert result["normalized_correlation"] > 0.999999
assert abs(result["gain"] - gain) < 1e-12
assert result["residual_power"] < 1e-20
def test_alignment_localizes_inserted_delay_jump():
random_generator = np.random.default_rng(6302)
transmitted = random_generator.choice((-1.0, 1.0), 8192).astype(np.complex64)
split, inserted = 4096, 700
received = np.concatenate((np.zeros(1000), transmitted[:split], np.zeros(inserted), transmitted[split:], np.zeros(1000)))
rows = lab.track_alignment(
lab.ArrayReader(transmitted), lab.ArrayReader(received), 1000,
frame_samples=8192, window_samples=512, step_samples=256, search_margin=1000,
reliable_packet_start=0, reliable_packet_end=8192,
)
verdict = lab.alignment_verdict(rows, inserted)
assert verdict["local_delay_discontinuity"]
assert any(abs(row["delay_change_samples"]) >= inserted - 1 for row in verdict["jump_candidates"])
def test_alignment_compares_reliable_windows_across_packet_boundary():
rows = [
{
"window_index": 4, "tx_sample": 1000, "rx_sample": 2000,
"packet_index": 1, "position_inside_packet_samples": 1000,
"local_delay_samples": 0, "normalized_correlation": 0.9,
"inside_reliable_region": True, "correlation_above_threshold": True,
"reliable_for_delay": True,
},
{
"window_index": 9, "tx_sample": 9000, "rx_sample": 10700,
"packet_index": 2, "position_inside_packet_samples": 800,
"local_delay_samples": 700, "normalized_correlation": 0.9,
"inside_reliable_region": True, "correlation_above_threshold": True,
"reliable_for_delay": True,
},
]
verdict = lab.alignment_verdict(rows, 700)
assert verdict["local_delay_discontinuity"]
assert verdict["jump_candidates"][0]["packet_index"] == 2
def test_alignment_discloses_internal_low_correlation_window():
rows = [
{
"window_index": 0, "tx_sample": 1000, "rx_sample": 2000,
"packet_index": 1, "position_inside_packet_samples": 1000,
"local_delay_samples": 0, "normalized_correlation": 0.9,
"inside_reliable_region": True, "correlation_above_threshold": True,
"reliable_for_delay": True,
},
{
"window_index": 1, "tx_sample": 1256, "rx_sample": 2956,
"packet_index": 1, "position_inside_packet_samples": 1256,
"local_delay_samples": 700, "normalized_correlation": 0.1,
"inside_reliable_region": True, "correlation_above_threshold": False,
"reliable_for_delay": False,
},
]
verdict = lab.alignment_verdict(rows, 700)
assert verdict["internal_low_correlation_windows"]["count"] == 1
assert "UNDETERMINED_IN_1_INTERNAL_LOW_CORRELATION_WINDOWS" in verdict["local_delay_discontinuity_scope"]
assert verdict["local_delay_discontinuity"] is False
def test_classification_marks_global_degradation_without_jump():
packets = [{
"packet_index": index,
"symbol_error_rate": 0.1,
"symbol_rms_deviation": 1.2,
"matched_residual_db": -1.0,
} for index in range(1, 17)]
result = lab.classify(packets, {"local_delay_discontinuity": False})
assert result["quality_distribution"] == "GLOBAL"
assert result["primary_cause_status"] == "PARTIALLY_LOCALIZED"
def test_classification_marks_mixed_when_jump_and_global_damage_coexist():
packets = [{
"packet_index": index,
"symbol_error_rate": 0.1,
"symbol_rms_deviation": 1.2,
"matched_residual_db": -1.0,
} for index in range(1, 17)]
result = lab.classify(packets, {"local_delay_discontinuity": True})
assert result["quality_distribution"] == "MIXED"