161 lines
5.3 KiB
Python
161 lines
5.3 KiB
Python
"""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
|