How to decode the position of the aircraft from an Odd and an Even CPR ADS-B Frame?

Say you receive the following two frames:


Bytes 2 to 4 give ICAO address 75804B which is
CEB [5J] Cebu Pacific Air
Registration RP-C3191
Airbus A319

The first 5 bits contain the Downlink Format (DF).
First byte 8D is 10001-101 so DF=17 and CA=5
DF 17 means we have an extended 112 bit squitter. Not all extended squitters have the position. We need to check the Type Code (TC).

Byte 5 is the first byte of the extended squitter as such, which is an extra 56 bits compared to a short squitter.
This makes up to the last 3 bytes not included. These last 3 bytes are an error check.
The Type Code is contained in the first 5 bits which is in Byte 5 of the whole frame: 58hex is 01011-000bin
Both TC are 01011bin which is 11dec.
This TC is Airborne Position with Barometric Altitude as follows:
Airborne position with Horizontal protection limit: (HPL) 25 m ≤ HPL < 185.2 m (0.1 NM)
95% Containment radius, μ and v, on horizontal and vertical position error: 10 m ≤ μ < 92.6 m (0.05 NM)
Navigational uncertainty category: 7

This is still not sufficient, as to start decoding positions we need an ODD and an EVEN frame.
These frames contain the position in CPR (Compact Position Reporting) format.
Whether a frame is odd or even is indicated in bit 22 of the extended squitter.
As we need more binary data lets change the extended squitters into binary.

The first frame:
[TC-] [-Altitude-] T F [—-Latitude—] [—Longitutde–]
01011 000 000011111111 0 0 10110011110111111 01001101110100110

The second frame:
[TC-] [-Altitude-] T F [—-Latitude—] [—Longitutde–]
01011 000 000011111111 0 1 10101100101000001 11110101101111010

In the first byte:
First 5 bits are the TC which is 11.
A TC 11 decodes to the following fields:

Bits 6 and 7 are the Surveillance Status.
Bit 8 indicates the antennas used.
Bits 9 (MSB) to 20 (LSB) contain the altitude.
Bit 21 contains the T (Time) bit. T in this case is 0 which means we are not synchronized to UTC.
Bit 22 contains the F flag which indicates which CPR format is used (odd or even).
Bits 23 (MSB) to 39 (LSB) contain the encoded latitude.
Bits 40 (MSB) to 56 (LSB) contain the encoded longitude.

So our first frame has F flag = 0 so is even and the second frame has F flag = 1 so odd.
So we finaly know that we can use our information to find the position of this aircraft.

CPR uses several functions which are good to know before we start:

Nb is the number of bits for encoding. Airborne positions use Nb = 17 as we can confirm from above. Note Nb = 19 for surface positions.

CPR decodes positions to Zones Nz. The number of possible zones for airborne positions is Nz = 15 giving an unambiguous airborne range for decoding of 360 NM.

The floor notation floor(x) denotes which is the greatest integer k such that k

Modulus MOD(x,y) is always positive. In VB6 I have written it as follows:
Function modulo(val, modval As Double) As Double
modulo = val Mod modval
If val < 0 Then modulo = modulo + modval
End Function

The NL(x) is a big one but only returns a number between 1 and 59. In my VB6 progam I use a lookup table as described in the PDF document 1090-WP-9-14.

So our starting point is:

Lat(0) = 10110011110111111 or 92095 dec
Lat(1) = 10101100101000001 or 88385 dec
Lon(0) = 01001101110100110 or 39846 dec
Lon(1) = 11110101101111010 or 125818 dec

1. Compute the latitude index j:
Under VB6 that is done as follows:
j = Int(((59 * Lat(0) – 60 * Lat(1)) / 131072) + 0.5)
j = 1

2. Compute the values of Rlat(0) and Rlat(1):
rlat(0) = AirDlat0 * (modulo(j, 60) + Lat(0) / 131072)
rlat(1) = AirDlat1 * (modulo(j, 59) + Lat(1) / 131072)
Const AirDlat0 As Double = 6
Const AirDlat1 As Double = 360 / 59
This gives
rlat(0) = 10.2157745361328
rlat(1) = 10.2162144547802
Note: Southern hemisphere values are 270° to 360°. Subtract 360°.

3.NL for Rlat(0) and Rlat(1) , NL(0) and NL(1) are both equal to 59. Do not proceed from here if NL(0) not equal to NL(1).
NL(0) = 59
NL(1) = 59
Both NL are equal so rlat(0) and rlat(1) are our latitudes.

4. i being the frame to decode, the last frame is odd, i = 1, compute n(i) which is the greater of 1 and NL(i) – i
ni = 58

5. Next Dlon(i) = 360 / n(i)
dlon(1) = 6.20689655172414

6.Find M, the longitude index. You need to know that in this case T = 1 (odd).
M = Int((((Lon(0) * (nl(T) – 1)) – (Lon(1) * nl(T))) / 131072) + 0.5)
M = -39

7. Compute the global longitude, Lon
Lon = dlon(T) * (modulo(M, ni) + Lon(T) / 131072)
Lon = 123.889128586342

So there you have it!

On the second frame our aircraft was at

Lon = 123.889128586342
Lat = 10.2162144547802

Also on 2nd frame for example: Altitude is 2175 feet but that is a different story …