Lab063: localize Lab062 RF quality degradation offline
This commit is contained in:
111
tests/test_lab063_tx_underrun_localization.py
Normal file
111
tests/test_lab063_tx_underrun_localization.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user