The 1090MHz Riddle

An open-access book about decoding Mode-S and ADS-B data

Compact Position Reporting

The position information in ADS-B messages is encoded in a compact position reporting (CPR) format. The general idea behind CPR is to be able to encode more coordinate decimals using less bits. It is achieved by trading global position ambiguity and time with local position accuracy.

An easy example to understand the principle behind CPR:

Imaging the world is constructed by 16 grid, which we have divided into two levels, each level is encoded with two bits. Higher levels in color are 00 (yellow), 01 (blue), 10 (red), 11 (green). And within each color grid, the lower levels are also encoded similarly.

Then each grid can be represented as 4 digits from 0000 to 1111. Now, we want to describe the movement indicated as the arrows in the green grids 1100 -> 1101, but we only have 3 bits to encode each position.

It is easy to see that the high 2 bits appeared in all positions, so we can define a structure to do the following:

  1. The last two bits shall represent the local position

  2. The combination of first digit from two messages defines the higher grid

Then the two messages can be sent as 1 00 -> 1 01.

From lower bits 00 -> 01, we have four different possibility of movement as shown in dashed arrows, and from the two first bits combination 11, we know that the arrow shall represent the movement in the green grids:

The CPR and functions

The actual CPR algorithm of course is more complicated, but the principle is very similar to the previous example. If only one message is given, it is possible to find multiple solutions that are spaced around the world. The combination of two (different types of) messages will yield the final result.

In CPR encoding, the Earth is divided in many zones (similar to the grid in the previous example). And the encoding algorithm is also more complicated (described in a later section). First, we will list some of the parameters and common functions used in the decoding process here.


Number of geographic latitude zones between equator and a pole. It is set to NZ = 15 for Mode-S CPR encoding.


the floor function floor(x) defines as the greatest integer value k, such that k<=x, for example:

floor(5.6) = 5
floor(-5.6) = -6

mod(x, y)

the modulus function mod(x, y) returns:

\[x - y \cdot floor(\frac{x}{y})\]

where y can not be zero


Denotes the “number of longitude zones” function, given the latitude angle lat. The returned integer value is constrained within [1, 59], calculated as:

\[\text{NL}(lat) = floor \left( \frac{2 \pi}{\arccos(1 - \frac{1-\cos(\frac{\pi}{2 \cdot \text{NZ}})}{\cos^2(\frac{\pi}{180} \cdot \text{lat})}) } \right)\]

For latitudes that are close to the equator or the poles, one of following values is returned:

lat = 0     ->    NL = 59
lat = +87   ->    NL = 2
lat = -87   ->    NL = 2
lat > +87   ->    NL = 1
lat < -87   ->    NL = 1

Site maintained by @junzis. Build with LaTeX, Pandoc, and GitHub