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 chips

Each result contains:

  • carrier_doppler: Estimated Doppler frequency
  • code_phase: Code phase in chips
  • CN0: Carrier-to-noise density ratio (dB-Hz)
  • peak_to_noise_ratio: Peak correlation power divided by noise power
  • power_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)