"""Tests for the subcarrier extractor block.""" import numpy as np import pytest try: from gnuradio import blocks, gr HAS_GNURADIO = True except ImportError: HAS_GNURADIO = False from apollo.constants import PCM_SUBCARRIER_HZ, SAMPLE_RATE_BASEBAND pytestmark = pytest.mark.skipif(not HAS_GNURADIO, reason="GNU Radio not installed") class TestSubcarrierExtract: """Test subcarrier extraction and frequency translation.""" def test_passes_target_frequency(self): """A tone at the center frequency should pass through.""" from apollo.subcarrier_extract import subcarrier_extract tb = gr.top_block() sample_rate = SAMPLE_RATE_BASEBAND n_samples = 50000 # Generate a pure tone at 1.024 MHz t = np.arange(n_samples, dtype=np.float64) / sample_rate tone = np.cos(2 * np.pi * PCM_SUBCARRIER_HZ * t).astype(np.float32) src = blocks.vector_source_f(tone.tolist()) extract = subcarrier_extract( center_freq=PCM_SUBCARRIER_HZ, bandwidth=150_000, sample_rate=sample_rate, ) snk = blocks.vector_sink_c() tb.connect(src, extract, snk) tb.run() output = np.array(snk.data()) # Output should have significant energy (tone passed through) assert len(output) > 0 power = np.mean(np.abs(output[1000:]) ** 2) assert power > 0.01, f"Target frequency power too low: {power}" def test_rejects_distant_frequency(self): """A tone far from the passband should be strongly attenuated.""" from apollo.subcarrier_extract import subcarrier_extract tb = gr.top_block() sample_rate = SAMPLE_RATE_BASEBAND n_samples = 50000 # Generate a tone at 500 kHz (far from 1.024 MHz passband) t = np.arange(n_samples, dtype=np.float64) / sample_rate tone = np.cos(2 * np.pi * 500_000 * t).astype(np.float32) src = blocks.vector_source_f(tone.tolist()) extract = subcarrier_extract( center_freq=PCM_SUBCARRIER_HZ, bandwidth=150_000, sample_rate=sample_rate, ) snk = blocks.vector_sink_c() tb.connect(src, extract, snk) tb.run() output = np.array(snk.data()) if len(output) > 1000: power = np.mean(np.abs(output[1000:]) ** 2) assert power < 0.001, f"Out-of-band frequency not rejected: {power}" def test_output_sample_rate_property(self): """Output sample rate should account for decimation.""" from apollo.subcarrier_extract import subcarrier_extract ext = subcarrier_extract(sample_rate=5_120_000, decimation=4) assert ext.output_sample_rate == 1_280_000 def test_block_instantiation(self): from apollo.subcarrier_extract import subcarrier_extract ext = subcarrier_extract() assert ext is not None