import numpy as np from galileo_rx.acquisition.acquisition_manager import AcquisitionManager from galileo_rx.config import AcquisitionConfig, TrackingConfig from galileo_rx.signal_profiles import galileo_e1_profile from galileo_rx.signals.code_provider import SyntheticCodeProvider from galileo_rx.signals.signal_generator import ChannelModelConfig, SatelliteSignalConfig, SyntheticSignalGenerator from galileo_rx.tracking.tracking_channel import TrackingChannel from galileo_rx.tracking.tracking_manager import TrackingManager from galileo_rx.types import AcquisitionResult, TrackingState def test_acquisition_to_tracking_and_multichannel_manager() -> None: fs = 4.092e6 profile = galileo_e1_profile() provider = SyntheticCodeProvider() satellites = [ SatelliteSignalConfig(2, "E1C", code_delay_chips=81.0, doppler_hz=1000.0, carrier_phase_rad=0.1, cn0_db_hz=52.0), ] generator = SyntheticSignalGenerator(profile, provider, fs, satellites=satellites, channel=ChannelModelConfig(seed=9)) samples, _ = generator.generate(int(0.12 * fs)) acquisition_config = AcquisitionConfig( doppler_min_hz=0, doppler_max_hz=2000, doppler_step_hz=250, coherent_integration_ms=4, noncoherent_accumulations=2, detection_threshold=2.0, zero_padding_mode="none", parabolic_interpolation=False, ) acquisition, _ = AcquisitionManager(profile, provider, fs, acquisition_config).acquire_one(samples, "E1C", 2) assert acquisition.detected tracking_config = TrackingConfig( integration_ms=4.0, use_fll_assist=True, pull_in_duration_ms=20.0, secondary_code_mode="wipeoff", loss_of_lock_epochs=20, ) manager = TrackingManager(profile, provider, fs, tracking_config) channel = manager.add_channel(acquisition) measurements = channel.process(samples[: 25 * channel.integration_samples]) assert len(measurements) == 25 assert not measurements[-1].reacquisition_requested assert measurements[-1].state in {TrackingState.PLL_TRACK, TrackingState.WEAK_SIGNAL} assert abs(measurements[-1].carrier_frequency_hz - 1000.0) < 100.0 assert np.median([abs(item.dll_error_chips) for item in measurements[-10:]]) < 0.1 def test_tracking_under_doppler_rate() -> None: fs = 4.092e6 profile = galileo_e1_profile() provider = SyntheticCodeProvider() generator = SyntheticSignalGenerator( profile, provider, fs, satellites=[SatelliteSignalConfig(4, "E1B", code_delay_chips=11.0, doppler_hz=-500.0, doppler_rate_hz_s=100.0)], channel=ChannelModelConfig(add_awgn=False), ) samples, _ = generator.generate(int(0.08 * fs)) acquisition_config = AcquisitionConfig( doppler_min_hz=-1000, doppler_max_hz=0, doppler_step_hz=250, coherent_integration_ms=4, noncoherent_accumulations=1, detection_threshold=1.5, zero_padding_mode="none", ) acquisition, _ = AcquisitionManager(profile, provider, fs, acquisition_config).acquire_one(samples, "E1B", 4) channel = TrackingChannel(profile, provider, fs, TrackingConfig(pull_in_duration_ms=40.0), acquisition) measurements = channel.process(samples) expected_hz = -500.0 + 100.0 * measurements[-1].time_s assert abs(measurements[-1].carrier_frequency_hz - expected_hz) < 100.0 def test_code_aiding_uses_doppler_not_nonzero_if_center() -> None: fs = 8.184e6 profile = galileo_e1_profile() provider = SyntheticCodeProvider() if_hz = 2.046e6 doppler_hz = 1800.0 acquisition = AcquisitionResult( detected=True, prn=1, component="E1B", code_phase_samples=0.0, code_phase_chips=0.0, doppler_hz=doppler_hz, carrier_frequency_hz=if_hz + doppler_hz, peak_power=1.0, second_peak_power=0.1, detection_metric=10.0, estimated_cn0_db_hz=None, coherent_integration_ms=4.0, noncoherent_accumulations=1, ) channel = TrackingChannel(profile, provider, fs, TrackingConfig(use_fll_assist=False), acquisition) expected_hz = profile.code_rate_hz * (1.0 + doppler_hz / profile.carrier_frequency_hz) assert abs(channel.code_nco.frequency_hz - expected_hz) < 1e-9 assert channel.carrier_center_hz == if_hz