Usage Guide
Basic Acquisition
The simplest way to acquire GNSS signals is with the acquire function:
using Acquisition, GNSSSignals
import Unitful: Hz
system = GPSL1()
# Generate a synthetic GPS L1 signal for PRN 1
(; signal, sampling_freq, interm_freq) = generate_test_signal(system, 1)Acquire multiple PRNs at once:
results = acquire(system, signal, sampling_freq, 1:3; interm_freq)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0712 │ 1250.0 Hz │ 110.62 │
│ 2.0 │ 41.2728 │ 2000.0 Hz │ 231.608 │
│ 3.0 │ 40.84 │ 6750.0 Hz │ 678.593 │
└─────┴────────────┴──────────────────────┴────────────────────┘
Acquire a single PRN:
result = acquire(system, signal, sampling_freq, 1; interm_freq)AcquisitionResults: PRN 1, CN0 = 51.07 dB-Hz, Doppler = 1249.9994499999993 Hz, Code phase = 110.62 chipsEach result contains:
carrier_doppler: Estimated Doppler frequencycode_phase: Code phase in chipsCN0: Carrier-to-noise density ratio (dB-Hz)peak_to_noise_ratio: Peak correlation power divided by noise powerpower_bins: Correlation power matrix
Detecting Satellites
Use is_detected to decide whether a satellite signal is present:
# Filter to detected satellites (1% false alarm probability)
detected = filter(is_detected, results)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0712 │ 1250.0 Hz │ 110.62 │
└─────┴────────────┴──────────────────────┴────────────────────┘
# Custom false alarm probability
detected = filter(r -> is_detected(r; pfa = 0.001), results)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0712 │ 1250.0 Hz │ 110.62 │
└─────┴────────────┴──────────────────────┴────────────────────┘
Under the hood, this compares each result's peak_to_noise_ratio against a CFAR (Constant False Alarm Rate) threshold computed by cfar_threshold.
Coarse-Fine Acquisition
For better Doppler resolution without the computational cost of a high-resolution search, use coarse_fine_acquire:
results = coarse_fine_acquire(system, signal, sampling_freq, 1:3;
interm_freq,
coarse_step = 250Hz,
fine_step = 25Hz
)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0668 │ 1250.0 Hz │ 110.62 │
│ 2.0 │ 41.2712 │ 2000.0 Hz │ 231.608 │
│ 3.0 │ 41.2219 │ 6725.0 Hz │ 678.593 │
└─────┴────────────┴──────────────────────┴────────────────────┘
This performs an initial coarse search, then refines the Doppler estimate around detected peaks.
Custom Doppler Range
Specify custom Doppler search bounds or a custom range:
# Custom bounds
results = acquire(system, signal, sampling_freq, 1:3;
interm_freq,
min_doppler = -5000Hz,
max_doppler = 5000Hz
)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0738 │ 1250.0 Hz │ 110.62 │
│ 2.0 │ 41.2503 │ 2000.0 Hz │ 231.608 │
│ 3.0 │ 40.8054 │ 2083.33 Hz │ 580.997 │
└─────┴────────────┴──────────────────────┴────────────────────┘
# Custom range with specific step
results = acquire(system, signal, sampling_freq, 1:3;
interm_freq,
dopplers = -7000Hz:100Hz:7000Hz
)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 50.5791 │ 1200.0 Hz │ 110.62 │
│ 2.0 │ 41.2721 │ 2000.0 Hz │ 231.608 │
│ 3.0 │ 41.6054 │ 3300.0 Hz │ 746.178 │
└─────┴────────────┴──────────────────────┴────────────────────┘
Using Acquisition Plans
For repeated acquisitions with the same parameters, pre-compute an acquisition plan to avoid redundant FFT planning and memory allocation:
(; signal, num_samples, sampling_freq, interm_freq) = generate_test_signal(system, 1)
# Create plan once (coherent integration length = half the signal for DBZP)
plan = AcquisitionPlan(system, num_samples ÷ 2, sampling_freq; prns=1:3)
# Reuse for multiple signals
results = acquire!(plan, signal, 1:3; interm_freq)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0712 │ 1250.0 Hz │ 110.62 │
│ 2.0 │ 41.2728 │ 2000.0 Hz │ 231.608 │
│ 3.0 │ 40.84 │ 6750.0 Hz │ 678.593 │
└─────┴────────────┴──────────────────────┴────────────────────┘
Similarly, for coarse-fine acquisition:
plan = CoarseFineAcquisitionPlan(system, num_samples ÷ 2, sampling_freq; prns=1:3)
results = acquire!(plan, signal, 1:3; interm_freq)┌─────┬────────────┬──────────────────────┬────────────────────┐
│ PRN │ CN0 (dBHz) │ Carrier Doppler (Hz) │ Code phase (chips) │
├─────┼────────────┼──────────────────────┼────────────────────┤
│ 1.0 │ 51.0712 │ 1250.0 Hz │ 110.62 │
│ 2.0 │ 41.2971 │ 1991.67 Hz │ 231.608 │
│ 3.0 │ 41.2536 │ 6733.33 Hz │ 678.593 │
└─────┴────────────┴──────────────────────┴────────────────────┘
Plotting Results
Acquisition results can be plotted directly with Plots.jl:
using Plots
plotlyjs()
# Use a smaller signal for plotting (fewer samples = smaller power_bins matrix)
plot_data = generate_test_signal(system, 1; num_samples = 5000, sampling_freq = 2e6Hz)
plot_result = acquire(system, plot_data.signal, plot_data.sampling_freq, 1; interm_freq = plot_data.interm_freq, dopplers = 0Hz:100Hz:2000Hz)
# 3D surface plot of correlation power
plot(plot_result)
# Log scale (dB)
plot(plot_result, true)