"""Full-band CBOC acquisition/tracking evidence at 32.736 Msps.""" from __future__ import annotations import numpy as np import pytest 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.e1_modulation import E1Modulation from galileo_rx.signals.fullband_e1_generator import FullBandE1SignalGenerator from galileo_rx.signals.signal_generator import ChannelModelConfig, SatelliteSignalConfig from galileo_rx.tracking.pilot_aided_e1 import synchronize_e1c_for_tracking from galileo_rx.tracking.tracking_channel import TrackingChannel FS_HZ = 32.736e6 IF_HZ = 2.25e6 DOPPLER_HZ = 1250.0 DELAY_CHIPS = 80.25 def _run_case(transmit_modulation: E1Modulation, receiver_modulation: E1Modulation): profile = galileo_e1_profile() provider = SyntheticCodeProvider() generator = FullBandE1SignalGenerator( profile, provider, FS_HZ, IF_HZ, satellites=[ SatelliteSignalConfig( 1, "E1C", code_delay_chips=DELAY_CHIPS, doppler_hz=DOPPLER_HZ, carrier_phase_rad=0.3, cn0_db_hz=55.0, modulation=transmit_modulation, ) ], channel=ChannelModelConfig(seed=2026), ) samples, truth = generator.generate(round(0.16 * FS_HZ)) acquisition_config = AcquisitionConfig( modulation=receiver_modulation, doppler_min_hz=1000.0, doppler_max_hz=1500.0, doppler_step_hz=250.0, coherent_integration_ms=4.0, noncoherent_accumulations=2, detection_threshold=2.0, zero_padding_mode="none", parabolic_interpolation=False, ) acquisition, _ = AcquisitionManager( profile, provider, FS_HZ, acquisition_config ).acquire_one(samples, "E1C", 1, IF_HZ) synchronization = synchronize_e1c_for_tracking( samples, profile, provider, FS_HZ, acquisition, round(0.008 * FS_HZ), search_primary_epochs=30, residual_frequency_step_hz=1.0, modulation=receiver_modulation, ) tracking = TrackingChannel( profile, provider, FS_HZ, TrackingConfig( modulation=receiver_modulation, pull_in_duration_ms=20.0, loss_of_lock_epochs=20, ), synchronization.acquisition, synchronization.tracking_start_sample, synchronization.sync.phase, ) measurements = tracking.process(samples[synchronization.tracking_start_sample :]) return acquisition, synchronization.sync, measurements, truth @pytest.mark.parametrize( ("transmit_modulation", "receiver_modulation"), [ (E1Modulation.BOC11, E1Modulation.BOC11), (E1Modulation.CBOC, E1Modulation.BOC11), (E1Modulation.CBOC, E1Modulation.CBOC), ], ) def test_fullband_modulation_matrix_acquires_and_tracks( transmit_modulation: E1Modulation, receiver_modulation: E1Modulation, ) -> None: acquisition, secondary, measurements, truth = _run_case( transmit_modulation, receiver_modulation ) expected = truth.satellites[0] assert acquisition.detected assert acquisition.raw_doppler_hz == DOPPLER_HZ assert abs(acquisition.code_phase_chips - expected.initial_code_delay_chips) <= 1.0 / 32.0 assert secondary.normalized_correlation > 0.98 assert secondary.sign_errors == 0 assert len(measurements) >= 35 assert measurements[-1].carrier_lock assert measurements[-1].code_lock assert not any(item.cycle_slip for item in measurements) assert not any(item.sample_discontinuity for item in measurements) tail = measurements[-10:] assert np.sqrt(np.mean([item.pll_error_rad**2 for item in tail])) < 0.12 assert np.sqrt(np.mean([item.dll_error_chips**2 for item in tail])) < 0.12 @pytest.mark.parametrize("intermediate_frequency_hz", [0.0, 2.25e6]) def test_cboc_pcps_carrier_wipeoff_at_zero_and_nonzero_if( intermediate_frequency_hz: float, ) -> None: profile = galileo_e1_profile() provider = SyntheticCodeProvider() samples, _ = FullBandE1SignalGenerator( profile, provider, FS_HZ, intermediate_frequency_hz, satellites=[ SatelliteSignalConfig( 5, "E1B", code_delay_chips=19.5, doppler_hz=-750.0, modulation=E1Modulation.CBOC, ) ], channel=ChannelModelConfig(add_awgn=False), ).generate(round(0.008 * FS_HZ)) result, _ = AcquisitionManager( profile, provider, FS_HZ, AcquisitionConfig( modulation=E1Modulation.CBOC, doppler_min_hz=-1000.0, doppler_max_hz=-500.0, doppler_step_hz=250.0, coherent_integration_ms=4.0, noncoherent_accumulations=2, detection_threshold=2.0, zero_padding_mode="none", parabolic_interpolation=False, ), ).acquire_one(samples, "E1B", 5, intermediate_frequency_hz) assert result.detected assert result.raw_doppler_hz == -750.0 assert abs(result.code_phase_chips - 20.5) <= 1.0 / 32.0