warming up your workspace

How GPS finds you from four satellites, and why three is not enough

Three satellites should be enough to find a point in 3D space. You know your distance to three known positions, that is three spheres, and three spheres intersect at a point. Every textbook says so. Yet GPS needs four satellites for a fix, and if you have only three your receiver refuses to give you a position. The reason is a subtle and beautiful one, and it is not about accuracy or redundancy. It is that your receiver does not actually know your distance to any satellite. Build the math and the fourth satellite reveals exactly what it is for.

The one idea

A GPS satellite broadcasts the time it sent each signal, using an atomic clock accurate to nanoseconds. Your receiver notes when the signal arrived and multiplies the travel time by the speed of light to get the distance. Simple, except your phone does not have an atomic clock. Its cheap clock is off by some unknown amount, and because signals travel at the speed of light, a clock error of a millionth of a second is a distance error of 300 meters. That single unknown clock offset corrupts every distance measurement by the same amount. So you do not have distances. You have "pseudoranges": true distance plus one shared, unknown error.

Now count unknowns. Position is three (x, y, z). The clock offset is a fourth. Four unknowns need four equations, and each satellite gives one. That is why you need four satellites. The fourth does not refine your position; it is what lets you solve for the clock error at all.

Build the solver

The equations are nonlinear, distance is a square root, so you solve them the way you solve most nonlinear systems: start with a guess and take Gauss-Newton steps, each one linearizing the problem and correcting toward the answer. The four unknowns are position and clock bias together.

import numpy as np
c = 299_792_458.0                          # speed of light, m/s

def solve(sats, ranges):
    x = np.zeros(3); b = 0.0               # guess: Earth's center, zero clock error
    for _ in range(10):
        d = np.linalg.norm(sats - x, axis=1)
        resid = (d + c*b) - ranges         # predicted pseudorange minus measured
        J = np.zeros((len(sats), 4))
        J[:, :3] = (x - sats) / d[:, None] # how pseudorange changes with position
        J[:, 3]  = c                       # ... and with clock bias
        step = np.linalg.lstsq(J, -resid, rcond=None)[0]
        x, b = x + step[:3], b + step[3]
    return x, b

Three details that matter:

  • The unknown vector is four-dimensional: x, y, z, and b, the clock bias. The Jacobian's last column is all c, because the clock error scales every pseudorange by the speed of light. That column is the mathematical presence of the fourth satellite's job.
  • Gauss-Newton converges in a handful of steps because, from any reasonable start, the distance functions are gently curved. GPS receivers do essentially this, warm-started from the last known position so it converges in one or two iterations.
  • Everything is in one consistent frame here for clarity. Real GPS works in an Earth-centered rotating coordinate system and corrects for relativity: the satellites' clocks tick measurably faster in weaker gravity and slower from their speed, and unmodeled that would add about 11 kilometers of error per day.

Proof: the fourth satellite is the whole game

Place four satellites, put a receiver at a known point, add a real clock bias, and solve:

true pos: [ 1200000. -4100000.  4500000.]
est  pos: [ 1200000. -4100000.  4500000.]
position error: 0.000000 m
true bias: 3.400000e-04 s, est bias: 3.400000e-04 s

Perfect recovery of both position and the clock offset. Now do what the textbook says is enough, use three satellites and assume the clock is correct:

no-clock-term error: 105631 m (clock bias unmodeled)

A hundred and five kilometers off. The clock error was under half a millisecond, invisible to a human, but at the speed of light it threw the position out by the width of a city and then some. The fourth satellite did not make a good answer better. It turned a useless answer into a correct one, by giving the solver enough equations to catch the clock in the act.

Where this shows up

This is the core of every satellite navigation system, GPS, Galileo, GLONASS, BeiDou, and modern receivers track dozens of satellites at once, feeding all of them into the same least-squares solve to average out noise and detect bad signals. The same "extra unknown for the sensor's own error" pattern runs through science: it is how you calibrate an instrument while you measure with it. And the Gauss-Newton engine underneath is the same one that fits models to data everywhere.

If you want to build the coordinate transforms, the error modeling, and the geometry that turns satellite ranges into a place on a map, that is what the geospatial track on IWTLP constructs from first principles.