My first guess is that the correction from the Space Vehicle’s local clock to GPS time is wrong. I’ll have to look into it in great detail tonight.

Here is my amended version of Andrew Holme’s solution routine, which has been converted to C, and had a few minor interface tweeks. This “struct Location” are just ‘x’,’y’,’z’ and ‘time’ values, as doubles. You may also need to add a few definitions for constants (as this is snipped out of a bigger source file).

The GPS specs are pretty average on the how the parity calculation for each subframe is performed, with things that I found confusing.

The most confusing thing for me is that the bit numbering within the fields is most significant bit first, so if the Time of Week is from bits 1 to 17 in the subframe bit 1 holds the 2^16 bit, and bit 17 holds the 2^0 bit.

To add to the fun, the bits are numbered from 1 to 30, rather than the more programmer-friendly 0 to 29.

And even worse, it is documented to be viewed as a hardware operation with lots of XOR gates, and that is not to be efficiently be implemented in software.

The routine below takes the last 32 bits to be received in ‘d’ and returns 1 if the parity is correct, or zero if false.

It does this three ways – a either a mass of bit XOR operations (which is great for hardware), or a set of test-bit-then-XOR operations, either from a constants or from a lookup table (which is great for software) – you can select which one to use by changing the “#if” values.

The key maths behind receiving GPS is cross-correlation – the testing of two signals to see how alike they are, in frequency and phase. For a GPS receiver this technique is used to see if the pseudo-random bit pattern is being transmitted on a given frequency, and when required, measure the phase difference between the local sample clock and the down-converted signal.

The process can be quite easily described:

Stretch the 1023-bit Gold code of the Space Vehicle to match the number of samples per millisecond of the incoming data stream.

Multiply the sample stream with the Gold code you are hunting for, treating the 0s in the stretched Gold code as -1s. If the Gold code is in the data stream, and they are aligned in phase, then this should remove the Gold code leaving just the intermediate frequency signal and any other noise.

Multiply the resulting stream with the sin() and cos() functions of the intermediate frequency that you are probing, to give you an ‘I’ (in phase) and ‘Q’ (quadrature) stream.

Sum up the I and Q streams for a sensible period of time (usually a multiple of length of time it takes to transmit a Gold code. These two numbers will indicate how much signal is in phase with the sin() and cos() functions used in step three. This can be treated as a 2D vector to measure phase

Finally work out the magnitude of the vector – i^2+q^2 will give you the power level for that Gold code, with that alignment, at that frequency

This all sees like a lot of work – for each sample, there is a sin(), a cos(), three multiplies and two additions – tracking 5 space vehicles using a 5456kHz sample rate will require at least a hundred of megaflops.

Work avoidance scheme

A simple trick can save the day. For one-bit samples (where ‘1’ indicates a positive value, and ‘0’ indicates a negative value), a binary XOR is equivalent to multiplication by either 1 or -1 as it flips the bit.

This reduces the process to:

Stretch the Gold code from 1023 bits to match the sample rate (in bits per millisecond)

XOR the raw bitstream with the stretched Gold code.

XOR the bitstream from step 2 with a binary string, which is ‘1’ where sine of the frequency being tested is positive (and ‘0’ where it is negative) to give the I stream. Also XOR the bitstream from step 2 with a binary string, which is ‘1’ where cosine is positive (and ‘0’ where it is negative) to give the Q stream.

Count up the ‘1’s and ‘0’s in the I and Q streams for a sensible time period, then subtract the ‘1’s count from the ‘0’s count.

Finally work out the magnitude of the vector – i^2+q^2 will give you the power of that Gold code, with that alignment, at that frequency

It should also be pretty self-evident step 1 (stretching the Gold code) only needs to be performed once, and that steps 2 and 3 can be performed in any convenient order. A less obvious optimization is that in step 4, you only need to count the ‘1’s, as you can always calculate how many zeros there must have been.

So, with all that explanation out of the way, here is my code that teases out GPS signals from the bit stream from a single bit ADC:

To prove how little they match, here are graphs of Space Vehicles 1’s Gold code correlated against itself and that of the Gold code for Space Vehicle 2:

The single peak can clearly be seen standing out from the noise, SV1’s code XORed with SV1’s code, with an phase offset of zero.

This special property of Gold codes allows a GPS receiver to check if a Space Vehicle’s signal is being received, and the relative phase to with sub-microsecond microsecond accuracy. However, this comes at a price. You can’t just ‘tune in’ to a Space Vehicle, you have to do a lot of work, deliberately looking for the correct single, at the correct frequency and with the correct phase.

Here is the C program used to produce the data for the graphs. You might find it useful:

GPS is now a key technology for many diverse fields, from controlling autonomous vehicles, location aware games on cellphones to controlling continental power grids. I want to understand it.

One unique feature of GPS is the ability for it to provide a highly accurate time reference, and it was thinking about how this feature was implemented that started me wondering how GPS actually works. How can a small electronic device determine an accurate time, based on the transmission of satellites that are 20,200 km away?

After investigating on Google, I’ve reached the conclusion that one way to understand everything about it is to make my own functional GPS receiver.

In a act of serendipity I am just about to receive the hardware for this project – the KiwiSDR cape for the Beagle Bone. It handles the messy RF front-end, delivers the output of a Skyworks single chip GPS front-end to an Xilinx Spartan 6 FPGA for decoding. You can find the full details on the project’s web site – http://kiwisdr.com/kiwisdr/ including the full set of design files.

For this blog I will focus on the most primitive, understandable techniques I can find to decode this raw ADC output into usable navigation information. Rather than efficiency the focus will be on being understandable and testable with the minimal advanced maths skills I have.