How it is built
Architecture overview
A GNSS receiver (GPS and its siblings Galileo, GLONASS, BeiDou) computes where it is from signals that arrive far below the noise floor. Each satellite broadcasts a known spreading code on a carrier, modulated by a slow navigation message that carries the satellite's orbit and clock. The receiver therefore does four jobs in a chain. It brings the antenna signal down to baseband and samples it. It acquires each visible satellite by searching for the code delay and the Doppler shift. It tracks the satellites it found, with feedback loops that hold alignment in code and carrier and so also measure the signal's travel time. It decodes the navigation message to learn where each satellite was and what its clock said. Finally a solver turns the measured ranges into position, velocity and receiver clock error, optionally refined with carrier phase for centimetre accuracy.
The one idea that makes it work: the receiver correlates the incoming samples with a local replica of the code, and the correlation peak is both the detection test and, once a loop keeps it centred, the measurement. Everything before the correlator is signal conditioning and everything after it is estimation. The open reference is GNSS-SDR, a software receiver whose flow graph is signal source, conditioner, channels (acquisition, tracking, telemetry decoder), observables and PVT (source: GNSS-SDR docs), and RTKLIB for the positioning side.
Parts:
- Front end and samples — antenna, RF chain and ADC, or a recorded sample file; outside the graph (hardware), but its sample rate and bit depth set every contract below
- Digital front end — mix to baseband, filter and decimate, produce I/Q —
digital-down-conversion(no page yet), SIMD programming, Deterministic fixed-point math - Acquisition — find code phase and Doppler for each satellite —
gnss-signal-acquisition(no page yet), Fast Fourier transform - Tracking — hold lock and measure code and carrier phase every millisecond or so —
gnss-tracking-loops(no page yet), Phase-locked loop (digital) - Navigation message decoder — bits, frames, error correction, ephemeris — Convolutional codes and the Viterbi decoder, CRC32C checksums
- Satellite state — where each satellite was at transmit time —
orbit-propagation(no page yet) - Position solver — pseudoranges to position and clock —
pseudorange-least-squares-positioning(no page yet), Extended Kalman filter - Precise positioning — carrier phase against a base station —
rtk-ambiguity-resolution(no page yet)
antenna ─► RF front end ─► ADC ─► samples (IF or I/Q, 2-16 Msps order of)
│
┌────────────▼──────────────┐
│ digital down-conversion │ NCO mix, decimate (SIMD / fixed-point)
└────────────┬──────────────┘
channel manager ◄────┤
│ assign sat ├──► ACQUISITION: FFT correlation over Doppler bins ──┐ code phase,
▼ │ │ Doppler
┌─ channel 1..N ─────────────────────────────────────────────────────────────┘
│ TRACKING: early/prompt/late correlators ─► DLL + PLL/FLL ─► code, carrier phase
│ │ prompt symbols │ measurements
│ ▼ ▼
│ TELEMETRY: bit sync ─► Viterbi ─► CRC ─► ephemeris OBSERVABLES: pseudorange, phase, Doppler
└───────┬───────────────────────────────────────────────────┘
▼
SATELLITE STATE (orbit from ephemeris) ─► SOLVER: least squares / EKF ─► position, velocity, time
▲
base-station corrections ─► RTK ambiguity resolution
The receiver is a pipeline whose stages run at very different rates: the front end at millions of samples per second, tracking at hundreds to thousands of updates per second per satellite, the decoder at tens of bits per second, and the solver at one to a few fixes per second.
How the parts fit together
Digital front end
digital-down-conversion (no page yet) mixes the sampled signal with a numerically controlled oscillator (NCO) to
move the satellite band to zero frequency, then filters and decimates (CIC and FIR stages are the usual choice) and
keeps I and Q. It is where the data rate collapses, so it is the first place to spend effort. SIMD programming
puts several samples through each instruction in a software receiver; GNSS-SDR builds on VOLK, the vector-optimized library of SIMD kernels, and ships its own VOLK_GNSSSDR module
(source: GNSS-SDR README). Deterministic fixed-point math is the
other face of the same stage: an FPGA or ASIC correlator uses integer phase accumulators and fixed-width samples, and
a software receiver that must give bit-identical results across compilers and machines (regression tests, replaying a
recorded dataset) needs the same discipline, since floating-point shortcuts differ between builds.
Contract: after decimation, one shared stream of complex samples at a sampling rate that divides evenly into the code period, so every channel can index into it by sample number instead of resampling.
Acquisition
The question is "is satellite PRN k here, and at what code delay and Doppler?" gnss-signal-acquisition (no page
yet) answers it by trying every Doppler bin and, for each, computing the circular correlation of the signal with the
local code over all code delays at once. That per-bin step is a Fast Fourier transform:
transform the signal, multiply by the conjugate of the transformed code, transform back, take the peak. This is the
parallel code phase search, the method used by GNSS-SDR's PCPS acquisition blocks (source: GNSS-SDR docs,
Acquisition). The FFT is chosen over a time-domain sliding correlator because it turns a search over thousands of
code delays into one O(n log n) operation per Doppler bin. Detection compares the peak to a threshold derived from the
target false-alarm probability, and non-coherent dwells (accumulating several code periods) trade time for sensitivity.
Contract: acquisition hands the channel a coarse code phase and Doppler, accurate enough that the tracking loops can pull in. It does not have to be precise, and it is allowed to be slow; it runs only when a channel starts or after loss of lock.
Tracking
Tracking turns the coarse estimate into a continuous measurement. Each channel correlates the samples with early,
prompt and late copies of the code (source: GNSS-SDR docs, Tracking). A delay-locked loop steers the code replica to
keep early and late equal; the carrier is held by a Phase-locked loop (digital), with a frequency-locked
loop to pull in large frequency errors before the phase loop can hold. gnss-tracking-loops (no page yet) is the
combination: discriminators, loop filters and bandwidth choices, plus lock detectors that decide when a channel has
failed. The PLL is the block of the graph; the loop filter order and bandwidth are the design decisions. A narrow loop
averages more noise but loses lock under dynamics; a wide one tracks motion but is noisy.
Contract: each integration period (one code period or a multiple) the channel outputs a prompt correlator value, whose sign carries the navigation data bit, plus the current code phase, carrier phase and Doppler. The latter three become the observables; the prompt stream goes to the decoder.
Navigation message decoder
The prompt values become bits through bit synchronisation (finding the data-bit edges inside the code periods) and frame synchronisation (a preamble). Newer signals protect the message with a convolutional code, so recovery is a Convolutional codes and the Viterbi decoder step followed by a cyclic redundancy check: GNSS-SDR's Galileo decoder describes pages of 240 coded symbols carrying 114 data bits and 6 zero tail bits that make Viterbi decoding possible, and its SBAS decoder performs Viterbi decoding of the convolutional code, preamble detection with polarity resolution, and CRC-24Q verification (source: GNSS-SDR docs, Telemetry Decoder). The legacy GPS message uses parity bits instead of a convolutional code. Viterbi decoding is used because it gives maximum-likelihood decoding on a noisy channel that cannot be retransmitted; a hard-decision decoder throws away information the demodulator has.
Note the checksum: the standard GNSS frame check is CRC-24Q. CRC32C belongs in this system only on the file side, for the integrity of recorded sample blocks and log files, where its hardware instructions are cheap. The graph is missing a generic CRC block for the 24-bit polynomial.
Contract: the decoder emits whole, CRC-verified frames only, never partial data; ephemeris is accepted when it is complete and consistent.
Satellite state
orbit-propagation (no page yet) converts broadcast ephemeris into the satellite's position and clock correction at the
transmit time of each measured signal. Broadcast ephemeris is a set of Keplerian elements plus harmonic corrections,
evaluated with a fixed algorithm in the interface spec, so a receiver does not integrate equations of motion; the
heavier propagators (SGP4, numerical integration) are for planning and prediction, as in assisted start, almanac-based
visibility prediction and ground-station work. The accuracy of the solution is limited by how well the receiver models
the satellite, so the order of evaluation, the reference frame, the time system and the relativistic clock correction are
all specified exactly.
Contract: input is ephemeris plus transmit time; output is an earth-fixed position and a clock offset for that epoch. Transmit time comes from the tracking loops, not the receiver clock, which is itself an unknown.
Position solver
pseudorange-least-squares-positioning (no page yet) is the heart: pseudorange equals geometric range plus receiver
clock error plus propagation delays, so with four or more satellites one solves for three coordinates and a clock
bias by iterated weighted least squares, correcting for ionosphere and troposphere with models. GNSS-SDR's PVT block
states this as an iterative weighted least squares estimator and offers a RAIM check for outlier detection (source:
GNSS-SDR docs, PVT). A one-epoch solver has no memory, so an Extended Kalman filter takes over where a
dynamic model and continuity help: GNSS-SDR's PVT docs use an EKF for precise point positioning, and the same filter
carries position, velocity and clock state across epochs, bridges short outages and weights measurements by their noise.
Least squares is the right start because it is stateless and gives a clean first fix after acquisition; the EKF is the
step you add when smoothness and outage handling matter.
Contract: observables with satellite state and a noise estimate per measurement go in; a fix with covariance goes out. A bad satellite is rejected by residual test, not removed from the channel.
Precise positioning
rtk-ambiguity-resolution (no page yet) works with carrier phase, which is millimetres of wavelength against metres of
code error, but has an unknown integer number of cycles per satellite. Real-time kinematic positioning forms
double-differenced carrier-phase and pseudorange measurements against a base station of known coordinates and estimates
the float ambiguities in an extended Kalman filter; integer ambiguity resolution (the LAMBDA search is the standard) then
fixes them, with a validation step and a fix-and-hold mode (source: GNSS-SDR docs, PVT). It needs a base station
stream and clean phase tracking: a cycle slip invalidates the ambiguity.
Data and control flow
Cold start to first fix:
- The sample source delivers I/Q; the front end downconverts and decimates.
- The channel manager assigns a satellite; acquisition runs the FFT search across Doppler bins and returns the best code phase and Doppler, or nothing.
- A tracking channel starts: pull-in, then the carrier and code loops lock.
- Prompt values give bits; bit and frame sync, Viterbi and the CRC produce ephemeris. This is where time goes on a cold start, because the message is slow and ephemeris repeats on a cycle of tens of seconds.
- Four satellites with ephemeris and pseudoranges: the solver computes the first fix.
One epoch in steady state:
- Each channel latches code phase, carrier phase and Doppler on a common receiver time tag.
- Observables are formed (pseudorange from the time difference to the reconstructed transmit time).
- Satellite positions are computed at transmit time; atmospheric corrections applied.
- The solver or EKF runs; the fix is emitted in a standard format.
RTK update:
- Base-station observations arrive over a network link.
- Double differences are formed on common satellites.
- The filter updates float ambiguities; the integer search proposes a fixed set; validation accepts or holds float.
Scaling
- Correlator cost dominates a software receiver: channels times correlators times samples per second. Levers in order: wider SIMD kernels, fewer samples (lower rate, smarter decimation), a multithreaded flow graph, then FPGA or ASIC correlators with fixed-point datapaths.
- Acquisition is bound by Doppler bins and dwell time. Levers: assisted start with known approximate time and position to narrow the Doppler span, FFT reuse across satellites, running acquisition on a spare core, longer coherent integration only when sensitivity requires it. GNSS-SDR documents a reduced Doppler search and fine-Doppler variants for this.
- Signal count: adding constellations and frequencies multiplies channels. Share the front end; per-signal tracking code is the part that grows.
- Solver is cheap; RTK ambiguity search grows with the number of ambiguities, so partial fixing (a subset) is the lever.
- Sensitivity and interference are limits of the physics: longer integration, better antenna, filtering, not more compute.
Failure modes
| Failure | Symptom | Cause | Fix |
|---|---|---|---|
| False acquisition | channel locks onto nothing, no bits | threshold too low, strong cross-correlation | confirm with a second dwell, pull-in test, lock detectors |
| Loss of lock | satellite drops under motion or shadowing | loop bandwidth too narrow for the dynamics, weak signal | wider or adaptive bandwidth, FLL assist, fast reacquisition |
| Half-cycle or data-bit ambiguity | position flips sign or carrier phase off by half a cycle | PLL tracks sign only | Costas loop, data-bit polarity from preamble |
| Corrupt ephemeris | position jumps by kilometres | bit errors passed the check, or frame mix-up | CRC/parity gate, consistency check on repeat, reject until two copies match |
| Multipath and non-line-of-sight | metres of bias in cities | reflected signal in the correlator | narrow correlator spacing, RAIM, weighting by C/N0 |
| Cycle slip | RTK fix lost or wrong fix | phase loop glitch | slip detector, reset ambiguity, fix-and-hold with validation |
| Timing drift | position error grows with receiver clock | clock modelled as constant | clock bias and drift in the EKF state |
| Float nondeterminism | tests pass on one machine only | compiler or fast-math differences | fixed-point datapath, pinned build flags |
| Jamming or spoofing | sudden loss, or position pulled away | strong interferer, counterfeit signals | front-end filtering, residual and C/N0 monitoring, authentication where available |
Real implementations
| name | what it is | repo | licence | notes |
|---|---|---|---|---|
| GNSS-SDR | software-defined GNSS receiver, many signals, PVT block | gnss-sdr/gnss-sdr, docs gnss-sdr.org | GPL-3.0 | the open reference for the chain above; uses VOLK for SIMD |
| VOLK | Vector-Optimized Library of Kernels: hand-written SIMD code | gnuradio/volk | LGPL-3.0 (COPYING) | the SIMD layer; sub-project of GNU Radio |
| RTKLIB | positioning library and tools: single point, PPP, RTK | tomojitakasu/RTKLIB; fork rtklibexplorer/RTKLIB | BSD-2-Clause with additional terms (see its readme) | the solver side; GNSS-SDR's RTKLIB_PVT uses its algorithms |
| PocketSDR | GNSS SDR toolkit with front-end and receiver | tomojitakasu/PocketSDR | BSD-2-Clause (see repo for terms) | open-source receiver for GPS, GLONASS, Galileo and more, with its own front-end hardware |
| gps-sdr-sim | generates GPS baseband signals for testing | osqzss/gps-sdr-sim | MIT | a test signal source for receivers; keep it conducted or shielded, it is not for broadcasting |
| laika | GNSS processing library (observations, ephemeris, positioning) | commaai/laika | MIT | the solver side, in Python; self-described as similar to RTKLIB |
The alternative: a hardware receiver chip with fixed correlators and a small CPU does the same chain in silicon with many parallel correlators; you give up flexibility and gain power and cost. A software receiver is the right tool for research, new signals and offline processing.
Sources
- GNSS-SDR documentation: fundamentals and architecture, Acquisition, Tracking, Telemetry Decoder, PVT, design forces.
- RTKLIB: T. Takasu, RTKLIB: An Open Source Program Package for GNSS Positioning (readme in the repo).
- IS-GPS-200, GPS Interface Specification (legacy navigation message, ephemeris algorithm).
- Galileo Open Service Signal-In-Space ICD, v2.1, GSC: PDF (opened, text not read).
- P. J. G. Teunissen, The least-squares ambiguity decorrelation adjustment: a method for fast GPS integer ambiguity estimation, J. Geodesy 70, 1995 (LAMBDA).
- K. Borre et al., A Software-Defined GPS and Galileo Receiver: A Single-Frequency Approach, Birkhauser 2007.