Files
galileo/tests/unit/test_frontend.py
T

33 lines
1.5 KiB
Python

import numpy as np
from galileo_rx.config import FrontendConfig, RfConfig
from galileo_rx.frontend.frontend_chain import FrontendChain
from galileo_rx.frontend.mixer import PhaseAccumulatorNco
def test_nco_frequency_word_accuracy_and_block_continuity() -> None:
full = PhaseAccumulatorNco(10.0e6, 1.234567e6, phase_width=32)
blocked = PhaseAccumulatorNco(10.0e6, 1.234567e6, phase_width=32)
expected = full.generate(100)
actual = np.concatenate([blocked.generate(37), blocked.generate(63)])
np.testing.assert_allclose(actual, expected, atol=1e-14)
assert abs(full.actual_frequency_hz - 1.234567e6) <= 10.0e6 / 2**33
def test_real_if_mixer_sign_and_phase_continuity() -> None:
fs = 8.0e6
rf = RfConfig(sample_format="float64", input_is_real_if=True, sampling_frequency_hz=fs, intermediate_frequency_hz=2.0e6)
chain = FrontendChain(rf, FrontendConfig())
time_s = np.arange(160) / fs
real_if = np.cos(2 * np.pi * 2.0e6 * time_s)
result = np.concatenate([chain.process(real_if[:61]), chain.process(real_if[61:])])
# A real mixer produces desired DC plus the unfiltered negative-frequency image.
assert abs(np.mean(result) - 0.5) < 0.02
def test_signed_sample_conversion() -> None:
rf = RfConfig(sample_format="int8", sampling_frequency_hz=4.0e6, intermediate_frequency_hz=0.0, sample_bit_width=8)
chain = FrontendChain(rf, FrontendConfig())
result = chain.process(np.array([-128, 0, 127], dtype=np.int8))
np.testing.assert_allclose(result.real, [-1.0, 0.0, 127 / 128])