Pictured above is the front-end, first mixer and IF amplifier of an experimental GPS receiver. The leftmost SMA is connected to a commercial antenna with integral LNA and SAW filter. A synthesized first local oscillator drives the bottom SMA. Pin headers to the right are power input and IF output. The latter is connected to a Xilinx FPGA which not only performs DSP, but also hosts a fractional-N synthesizer. More on this later.
I was motivated to design this receiver after reading the work  of Matjaž Vidmar, S53MV, who developed a GPS receiver from scratch, using mainly discrete components, over 20 years ago. His use of DSP following a hard-limiting IF and 1-bit ADC interested me. The receiver described here works on the same principle. Its 1-bit ADC is the 6-pin IC near the pin headers, an LVDS-output comparator. Hidden under noise but not obliterated in the bi-level quantised mush that emerges are signals from every satellite in view.
All GPS satellites transmit on the same frequency, 1575.42 MHz, using direct sequence spread spectrum (DSSS). The L1 carrier is spread over a 2 MHz bandwidth and its strength at the Earth's surface is -130 dBm. Thermal noise power in the same bandwidth is -111 dBm, so a GPS signal at the receiving antenna is ~ 20 dB below the noise floor. That any of the signals present, superimposed on one another and buried in noise, are recoverable after bi-level quantisation seems counter-intuitive! But the magic of DSSS makes it possible, of course.
GPS relies on the auto- and cross-correlation properties of pseudo-random sequences called Gold Codes to separate signal from signal and signal from noise. Each satellite has a unique sequence. Detection is possible above a minimum signal-to-noise ratio (SNR). All uncorrelated signals are noise, including those of other satellites and quantisation errors. Hard-limiting (1-bit ADC) actually only degrades SNR by less than 3 dB, a price worth paying to avoid hardware AGC.
My homemade GPS receiver has two printed circuit boards: the front-end depicted above and an FPGA board from an earlier frequency synthesizer project which was not designed to be reused in this way. The FPGA is controlled by "C" software running on a Windows PC connected to the FPGA via a Xilinx Platform USB JTAG cable. JTAG is slow but then so are GPS data rates. The FPGA performs real-time signal processing autonomously. Its principle roles are: synthesizing the first local oscillator and IF DSP. Computational "heavy-lifting" such as FFT-based search and solving for user position are performed by the PC.
This is a four-channel GPS receiver, which means it can track four satellites simultaneously. A minimum of four are required to solve for user position and receiver clock bias. Inside the FPGA, the tracking module is instantiated four times. In principle, more channels could easily be added simply by changing the size of a Verilog instance array; however, the Spartan 3 XC3S400 is almost 100% full. It might be possible to track more satellites by multiplexing channels between satellites; but this has not been explored.
The 1575.42 MHz L1 carrier is down-converted to a first IF (intermediate frequency) of 22.5 MHz and amplified by analogue means. Subsequently, all IF and baseband signal processing is DSP in the FPGA. Each of the four channels has digital PI controllers to track carrier and code phase. The 50 bps NAV data is collected in four FIFO memories. These are polled via JTAG by "C" software on the PC which performs parity checks, identification and decoding of subframes. Once the necessary ephemeris is collected, a snapshot is taken of certain internal counters in the FPGA. From this, four times of transmission are computed to a precision of ± 0.1 µs.
Signal processing up to and including the hard-limiter:
The LMH7220 comparator has a maximum input offset voltage of 9.5mV. Amplified thermal noise must comfortably exceed this to keep it toggling. Weak GPS signals only influence the comparator near zero crossings! They are "sampled" by the noise! To estimate noise level at the comparator input we tabulate gains, insertion losses and noise figures:
LNA SAW Coax RF Mixer IF Overall system noise figure
Gain +28.5 -1.5 -3.9 +19.0 -6
NF 0.8 1.5 3.9 3.3 6 7 1 dB
In-band noise at the mixer output is -174+1+28.5-1.5-3.9+19-6+10*log10(2.5e6) = -73 dBm or 51µV RMS. The mixer is resistively terminated in 50-ohms and the stages thereafter work at higher impedance. The discrete IF strip has an overall voltage gain of 800 so the comparator input level is 40mV RMS.
The LMH7220 adds 59 dB of gain making a total of 116 dB for the whole IF. Deploying so much gain at one frequency was a risk. To minimise it, balanced circuitry over a solid ground plane was used and screened twisted-pair carries the output to the FPGA. The motivation was simplicity, avoiding a second conversion. In practice, the circuit is stable, so the gamble paid-off.
Active decoupler Q1 supplies 5V for the remote LNA. MMIC amplifier U2 provides 19 dB gain (not at IF!) and ensures low overall system noise figure, even if long antenna cables are used. L1 and L2 are hand-wound microwave chokes with very high self-resonant frequency, mounted perpendicular to one another and clear of the ground plane. Wind 14 turns, air-cored, 1mm inside diameter from 7cm lengths of 32swg enameled copper wire. Checked with the tracking generator on a Marconi 2383 SA, these were good to 4 GHz.
The Mini-Circuits MBA-15L DBM was chosen for its low 6 dB conversion loss at 1.5 GHz and low 4 dBm LO drive requirement. R9 terminates the IF port.
Three fully-differential IF amplifier stages follow the mixer. Low-Q parallel tuned circuits strung between collectors set the -3 dB bandwidth around 2.5 MHz and prevent build-up of DC offsets. L4, L5 and L6 are screened Toko 7mm coils. The BFS17 was chosen for its high (but not too high) 1 GHz fT. Ie is 2mA for lowest noise and reasonable βre.
The 1st IF is digitally down-converted to 2.5 MHz by under-sampling at 10 MHz in the FPGA. 2.5 MHz lies at the centre of the 5 MHz Nyquist bandwidth. Alternative 1st IF frequencies are 12.5, 17.5 and 27.5 MHz. There is a trade-off between image problems at lower and available BFS17 gain at higher frequencies. Both 22.5 MHz and 27.5 MHz have been tried successfully. 17.5 and 27.5 MHz produce spectrum inversion at the 2nd IF.
Signal detection entails resolving three unknowns: what satellites are in view, their Doppler shifts and code phases. A sequential search of this three-dimensional space from a so-called "cold start" could take many minutes. A "warm start" using almanac data to predict positions and velocities still requires a code search. All 1023 code phases must be tested to find the maximum correlation peak. Calculating 1023 correlation integrals in the time-domain is very expensive and redundant. This GPS receiver uses an FFT-based algorithm that tests all code phases in parallel. From cold, it takes 2.5 seconds on a 1.7 GHz Pentium to measure signal strength, Doppler shift and code phase of every visible satellite.
With over bar denoting conjugation, the cross-correlation function y(Τ) of complex signal s(t) and code c(t) shifted by offset Τ is:
The Correlation Theorem states that the Fourier transform of a correlation integral is equal to the product of the complex conjugate of the Fourier transform of the first function and the Fourier transform of the second function:
FFT(y) = CONJUGATE(FFT(s)) * FFT©
Correlation is performed at baseband. The 1.023 Mbps C/A code is 1023 chips or 1ms long. FFT length must be a multiple of this. The sampling rate and FFT length chosen give a 500 Hz bin size. 21 Doppler shifts are tested by rotating the frequency domain data, one bin at a time, up to ±10 bins = ±5 KHz. Rotation can be applied to either function.
The 22.5 MHz 1st IF from the 1-bit ADC is under-sampled by a 10 MHz clock in the FPGA, down-converting it to a 2nd IF of 2.5 MHz. Although not shown above, the samples are held in a FIFO memory before JTAG transfer. In the PC, the 2nd IF is converted to complex baseband (IQ) using quadrature local oscillators. The mixers are simple XOR gates, since the signals are all bi-level. Complex baseband is transformed to the frequency domain by a forward FFT which need only be computed once.
A forward FFT is also required for each satellite's C/A code. Memory permitting, these could be pre-calculated; but generation-on-demand is not that expensive. The 1.023 Mbps code rate is generated by a numerically-controlled-oscillator (NCO) phase accumulator. Unlike 2.5, which is precisely a quarter, 1.023 MHz is not a sub-harmonic of the sampling rate. Consequently, the NCO output has fractional spurs. The number of samples per code chip dithers between 9 and 10.
Processing time is dominated by the inner-most loop which performs shifting, conjugation, complex multiplication and one inverse-FFT per satellite-Doppler test. There is significant scope for optimisation in the current code. At 10 MHz sampling rate, code phase is resolved to ~100ns, which seems a little extravagant. Typical CCF output is illustrated below:
Calculating peak to average power over this data gives a good estimate of SNR and is used to find the strongest signal(s). The following were received at 20:14 GMT on 4 March 2011 in Cambridge, UK with the antenna on an outside North-facing window ledge:
PRN NAVSTAR Doppler (Hz) Code Phase SNR
9 33 1500 2.4 95.3
17 57 500 364.5 98.4
22 53 1000 844.7 54.1
27 27 0 770.0 53.8
28 48 -3000 103.9 99.1
From northern latitudes, more GPS satellites will generally be found in the southern sky i.e. towards the equator.
Having scanned the sky and selected the strongest signal, the next step is locking on, tracking it and demodulating the 50 bps NAV data. This requires two inter-dependent phase locked loops (PLLs) to track code and carrier phase. These PLLs must operate in real-time and are implemented as DSP functions in the FPGA. PC software has a supervisory role: deciding which satellites to track, monitoring the lock status and processing the received NAV data.
The tracking PLLs are good at maintaining lock, because they have very narrow loop bandwidths; however, this same characteristic makes them poor at acquiring lock without help. A PLL cannot "see" beyond loop bandwidth to capture anything further away. Initial phases and frequencies must be preset to the measured code phase and Doppler shift of the target satellite. This is orchestrated under PC control. The loops should be in-lock from the outset and remain so. NCO dither is seemingly not a problem.
PC processing time varies and there is latency in JTAG communications; however, code phase is measured relative to the 2ms sample. To mark this reference point, the code NCO in the FPGA is reset at the start of sampling and accumulates phase at a fixed 1.023 MHz. I assumed the NCO could later be aligned with the received code simply by subtracting the measured code phase; but it didn't work. Doppler shift on the 1575.42 MHz carrier is ±5 KHz or ±3 ppm. It also affects the 1.023 Mbps code rate by ±3 chips per second. Fortunately, code Doppler is proportional to carrier Doppler, which is known; and a code phase Doppler correction can be calculated for the elapsed time.
Thin traces are 1-bit, notionally representing ±1. The 2.5 MHz carrier is first despread by mixing with early, late and punctual code. I and Q complex baseband products from the second rank of XOR gate mixers are summed over 10000 samples or 1ms. This low-pass filtering raises SNR 34 dB by reducing noise bandwidth from 2.5 MHz to 1 KHz. Downsampling to 1 KHz necessitates wider onward data paths.
The code tracking loop uses a conventional early-late gate. Power in the early and late channels is calculated using P = I2 + Q2 which is insensitive to carrier phase θ. Early and late codes are one chip apart i.e. ½ chip ahead-of and behind punctual. This diagram helps get the error sense correct:
A Costas Loop is used for carrier tracking and NAV data recovery in the punctual channel. θ is phase error between the received carrier (sans modulation) and local NCO. The I*Q product is actually ½.k2.d2(t).sin(2θ) which is approximately k2θ for small θ, since d(t) is ±1. NAV data, d(t), with the customary 180° phase uncertainty, is taken from the I-arm sign bit. Loop filter F(z) is a discrete-time proportional integral (PI) controller. The same KI and KP are currently used for both loops. Below is a Bode plot of open-loop gain and the step response:
Loop bandwidth is ~ 3 Hz. Noise power in this bandwidth is tiny and the loops can track very weak signals. As there is no software AGC, loop gain and bandwidth vary with signal strength. Because search uses 500 Hz FFT bins, the initial carrier NCO can be up to 250 Hz off-frequency, potentially placing the carrier beyond the loop's grasp. Some GPS receivers have a "pull-in" mode where loop bandwidth only narrows after lock is detected.
The code loop always locks; but the carrier loop occasionally needs help. I noticed carriers close to the original IF centre frequency of 2.5 MHz were most problematic and realised this was due to fractional divider spurs on the NCO. A huge improvement was obtained by shifting the IF frequency up 100 KHz. Being a fractional-N synthesizer, The first local oscillator was easily changed to 1552.82 MHz moving the first and second IF frequencies to 22.6 MHz and 2.6 MHz respectively.
These spectra show the carrier NCO set 50 Hz above IF centres of 2.5 and 2.6 MHz. The original centre frequency was one quarter of the sampling rate. The spurs are safely further away when the frequencies are not in a simple ratio. Despite this improvement, there are still times when the Costas Loop fails to lock, or rather, as a "C" code simulation revealed, acquires a false lock. With the main goal of solving for user position in sight and not wishing to get bogged-down with this issue, I opted for a semi-automatic (or semi-manual if your glass is half empty) procedure in which the NCO frequency can be "nudged" by key-presses if the NAV data stream is not recovered right away.
The above are 2 consecutive frames of 5 subframes each. Subframes are 300-bits long and take 6 seconds to transmit. Column 1 is the preamble 10001011. This appears at the start of every subframe; but can occur anywhere in the data. The 17-bit counter in column 5 is time-of-week (TOW) and resets to zero at midnight Sunday. The 3-bit counter in column 7 is the subframe ID 1 through 5. Subframes 4 and 5 are subcommutated into 25 pages each and a complete data message comprising 25 full frames takes 12.5 minutes to transmit. I am only using data in subframes 1, 2 and 3 at present.
Solving for user position
Every GPS satellite transmits its position and the time. Subtracting time sent from time received and multiplying by the speed of light is how a receiver measures distance between itself and the satellites. Doing so with three satellites would yield three simultaneous equations in three unknowns (user position: x, y, z) if the precise time was available. In practice, receiver clocks are not accurate enough, the exact time is a fourth unknown, four satellites are therefore required and four simultaneous equations must be solved:
An iterative method is used because the equations are non-linear. Using earth's centre (0, 0, 0) and the approximate time as a starting point, the algorithm converges in only five or six iterations. The solution is found even if user clock error is large. The satellites carry atomic clocks; but these too have errors and correction coefficients in subframe 1 must be applied to the time of transmission. Typical adjustments can be hundreds of microseconds.
The uncorrected time of transmission is formed by scaling and adding several counters. Time-of-week (TOW) in seconds since midnight Sunday is sent every subframe. Data edges mark out 20ms intervals within 300-bit subframes. The code repeats 20 times per data bit. Code length is 1023 chips and chip rate is 1.023 Mbps. The code NCO is phase-locked to the signal and accumulated at 10 MHz. All this fixes time of transmission to ± 0.1 µs. Faster NCO clocking could improve on this.
Satellite positions at the corrected transmission time are calculated using ephemeris in subframes 2 and 3. Orbital position at a reference time t_oe (time of ephemeris) is provided along with parameters allowing (x,y,z) position to be calculated up to a few hours before or after. Ephemerides are regularly updated and satellites only transmit their own. Long term orbits of the entire constellation can be predicted less accurately using Almanac data in subframes 4 and 5; however, this is not essential if a fast FFT-based search is used.
Solutions are computed in earth-centred, earth-fixed (ECEF) coordinates. User location is converted to latitude, longitude and altitude with a correction for eccentricity of the earth, which bulges at the equator. The scatter diagrams below illustrate repeatability, the benefit of averaging and the effect of poor satellite choices. Grid squares are 0.001° or ~111 metres on each side. Blue dots mark 1000 fixes. Yellow circles mark the average of each 1000:
(i) North-facing window ledge
(ii) Rooftop antenna
(iii) East-facing window ledge
The tight cluster (ii) was obtained using satellites in four different quarters of the sky. Only the rooftop antenna had a clear view in all directions. But equally good fixes were obtained by averaging, even when half the sky was obscured. Rooftop fixes also exhibit spreading like (i) and (iii) if the wrong satellites are chosen.
The above solutions were generated without compensating for ionospheric propagation delays using parameters in page 18 of subframe 4 which should be applied because this is a single frequency receiver. Ionospheric refraction increases path lengths between users and satellites.
I'm grateful to Dan Doberstein for sending me an early draft of his GPS book  from which I obtained the solution algorithm. The official US government GPS Interface Specification  is an essential reference.
The above circuit arrangement, mostly implemented in FPGA, de-spreads by taking the product of the 1-bit IF and punctual code, leaving 50 bps data modulation. A small notch due to BPSK carrier suppression can just be seen:
These spectra show the same de-spread transmission at different spans and resolution bandwidths (RBW). Doppler shift was -1.2 KHz. The noise floor is antenna thermal noise amplified and filtered by the IF strip. -3 dB bandwidth looks around 3 MHz, slightly wider than planned. The de-spread carrier is 5 dB above noise at 30 KHz RBW and 25 dB above at 300 Hz RBW. Received signal strength at the antenna can be estimated as -174+1+10*log10(30e3)+5 = -123 dBm.
It still amazes me how well frequency domain information is preserved through hard-limiting! The LVDS transmitter has a constant output current of ~3mA which is ~1mW in 100 ohms. Peak power seen at the SA cannot exceed 0 dBm. Here, we see this available power spread across a range of frequencies. Wideband integrated power spectral density must be ~1mW.
First local oscillator
I've been building experimental fractional-N synthesizers using general-purpose programmable logic for several years:
Project Date Technology Frequency (MHz)
FracN 2004 Altera MAX 7000 CPLD 4.3
Frac2 2005 Altera MAX 7000 CPLD 15 - 25
Frac3 2009 Xilinx Spartan 3 FPGA 38 - 76
Frac4 2009 Xilinx Spartan 3 FPGA 38 - 76
Frac5 2010 Xilinx Spartan 3 FPGA 800 - 1600
The Frac5 board not only synthesizes the 1552.92 MHz first local oscillator but also hosts the GPS receiver's DSP functions in its FPGA. I had no idea Frac5 would be used in a GPS receiver when I originally designed it. The photo below shows how the ROS-1455 VCO output is resistively split between the output SMA and a Hittite HMC363 divide-by-8 prescaler. The 200 MHz divider output is routed (differentially) into the FPGA which phase locks it to a master reference using methods documented in my earlier projects.
High stability and low phase noise are achieved, as can be seen in the VCO output spectra shown below. When this board was originally developed as a dedicated frequency synthesizer, simultaneous toggling of logic on frequencies not harmonically related was avoided to minimise intermodulation spurs. The FPGA was static when clock pulses that toggled phase detector outputs crossed the fabric. No such luxury is practical now the FPGA is almost 100% utilised by a GPS receiver; however, the local oscillator output is still more than good enough:
The Marconi 2383 spectrum analyser's 50 MHz STD OUTPUT is used as the master reference source for Frac5 and all internal GPS receiver clocks. GPS receivers need accuracies better than 1 ppm (parts per million) to measure ±5 KHz Doppler shifts on the 1575.42 MHz L1 carrier. Any frequency uncertainty would necessitate a wider search range.