ADS-B Decoding Guide

This is a small research project conducted by Junzi Sun at TuDelft. While we were trying to work with ADS-B data collected from our receiver, we notice that there are very few documents available which can explain the ADS-B data comprehensively. So, we created this guide, along with a decoder written in python (https://github.com/junzis/pyModeS). Have Fun!

The main focus of the guide is on reading different types of messages, understanding the information in the message, and decoding/computing aircraft status.

Introduction

ADS-B

ADS-B is short for Automatic Dependent Surveillance–Broadcast. it is a satellite based survillance system. Aircraft position, velocity, together with identification are transmitted through Mode-S Extended Squitter (1090 MHz).

Majority of the aircraft nowadays are broadcasting ADS-B messages constantly. There are many ways you can set up you own receiver and antenna to start tapping into those signals (DVB-T usb stick, ModeSBeast, Raspberry Pi, RadarScape, etc).

An ADS-B message is 112 bits long, following is an example:

BIN format:
10001101010010000100000011010110001000000010110011000011
01110001110000110010110011100000010101110110000010011000

HEX format:
8D4840D6202CC371C32CE0576098

This table lists the key bits of a message:

Bit from Bit to Abbr. Name
1 5 DF Downlink Format
6 8 CA Message Subtype
9 32 ICAO24 ICAO aircraft address
33 88 DATA Data frame
89 112 PC Parity check

The type of the message can be identified by checking its Downlink Format (DF), bit 1 to 5. For ADS-B message, we need: DF = 17 (in decimal), or 10001 (in binary),

Within the data frame, another import value is the Type Code. it tells what is inside of the data frame, it is located from bit 33 to 37 (5 bits)

Bit from Bit to Abbr. Name
33 37 TC Type Code

ADS-B message types

By looking at the DF and TC we can quickly understand what kind of information is contained in the data frame. The relations are listed as following:

DF TC Content
17 1 to 4 Aircraft identification
17 5 to 8 Surface position
17 9 to 18 Airborne position (Baro Alt)
17 19 Airborne velocities
17 20 to 22 Airborne position (GNSS Height)
17 23 Test message
17 24 Surface system status
17 25 to 27 Reserved
17 28 Extended squitter AC status
17 29 Target state and status (V.2)
17 30 Reserved
17 31 Aircraft Operation status

Note that within different type of the messages, the configurations of the bits in data frame are different. In next chapter, those will be explained in detail.

ADS-B Checksum

ADS-B uses cyclic redundancy check to validate the correctness of received message. The parity check bits are the last 6 bytes (bit 89 to 112)

Aircraft Identification

An aircraft identification message has DF: 17, and TC: 1 to 4.

For example:

8D4840D6202CC371C32CE0576098

The structure of the message is:

|    | ICAO24 |      DATA      |        |
|----|--------|----------------|--------|
| 8D | 4840D6 | 202CC371C32CE0 | 576098 |

| DF    | CA  | ICAO24 ADDRESS           | TC    |     | Data                                             |
|-------|-----|--------------------------|-------|-----|--------------------------------------------------|--------------------------|
| 10001 | 101 | 010010000100000011010110 | 00100 | 000 | 001011001100001101110001110000110010110011100000 | 010101110110000010011000 |

Note that TC is inside of the DATA frame. DF and TC can be easily calculated:

DF: 10001 -> 17
TC: 0010 -> 4

Those two values confirm that the message is good for decoding aircraft identification.

Next, we are decoding the data frame containing the aircraft callsign (identification). In order to get the callsign, a look-up table is needed for mapping index numbers to letters:

'#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######'

In our message data frame, it is easy to decode following:

HEX: 202CC371C32CE0
BIN: 00100 000 | 001011 001100 001101 110001 110000 110010 110011 100000
DEC:           |   11     12     13     49     48     50     51     32
LTR:           |   K      L      M      1      0      2      3      _

So now we have the aircraft ID here: KLM1023

Following is the calculation implemented in Python:

from math import log

#convert input hex into bin and fill zero in front of the str
def hex2bin(hexstr):
  scale = 16
  num_of_bits = len(hexstr)*log(scale, 2)
  binstr = bin(int(hexstr, scale))[2:].zfill(int(num_of_bits))
  return binstr

def bin2int(binstr):
  return int(binstr, 2)

charset = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######'

msg = "8D4840D6202CC371C32CE0576098"
msgbin = hex2bin(msg)
databin = msgbin[32:88]   # python start from 0

# get the callsign part
csbin = databin[8:]

# convert callsign by charset
callsign = ''
callsign += charset[ bin2int(csbin[0:6]) ]
callsign += charset[ bin2int(csbin[6:12]) ]
callsign += charset[ bin2int(csbin[12:18]) ]
callsign += charset[ bin2int(csbin[18:24]) ]
callsign += charset[ bin2int(csbin[24:30]) ]
callsign += charset[ bin2int(csbin[30:36]) ]
callsign += charset[ bin2int(csbin[36:42]) ]
callsign += charset[ bin2int(csbin[42:48]) ]

# clean string, remove spaces and marks, if any.
chars_to_remove = ['_', '#']
cs = callsign.translate(None, ''.join(chars_to_remove))

#print callsign
print cs

Airborne Positions

An aircraft position message has DownlinkFormat: 17 and TypeCode: from 9 to 18.

Messages are composed as following:

MSG bits # bits Abbr Content
1-5 5 DF Downlink format
33-37 5 TC Type code
38-39 2 SS Surveillance status
40 1 NICsb NIC supplement-B
41-52 12 ALT Altitude
53 1 T Time
54 1 F CPR odd/even frame flag
55-71 17 LAT-CPR Latitude in CPR format
72-88 17 LON-CPR Longitude in CPR format

Decoding the positions of the aircraft is a bit complicated. Naturally, we would expect to read latitude and longitude directly from the data frame. Unfortunately, it’s not that simple...

Two different types of the position messages (odd and even frames) are needed to find out the LAT and LON of the aircraft. The position is described in the Compact Position Reporting (CPR) format. The advantage of CPR is that we can use fewer bits to encode position with higher resolution. However this results the complexity of decoding process.

First, “odd” or “even” message?

For each frame, bit 54 determines whether it is an “odd” or “even” frame:

0 -> Even frame
1 -> Odd frame

For example, the two following messages are received:

8D40621D58C382D690C8AC2863A7
8D40621D58C386435CC412692AD6

|    | ICAO24 |      DATA      |        |
|----|--------|----------------|--------|
| 8D | 40621D | 58C382D690C8AC | 2863A7 |
| 8D | 40621D | 58C386435CC412 | 692AD6 |

Convert both messages to binary strings:

| DF    | CA  | ICAO24 ADDRESS           | DATA                                                                              | CRC                      |
|                                        | TC    | SS | NICsb | ALT          | T | F | LAT-CPR           | LON-CPR           |                          |
|-------|-----|--------------------------|-------|----|-------|--------------|---|---|-------------------|-------------------|--------------------------|
| 10001 | 101 | 010000000110001000011101 | 01011 | 00 | 0     | 110000111000 | 0 | 0 | 10110101101001000 | 01100100010101100 | 001010000110001110100111 |
| 10001 | 101 | 010000000110001000011101 | 01011 | 00 | 0     | 110000111000 | 0 | 1 | 10010000110101110 | 01100010000010010 | 011010010010101011010110 |

In both messages we can find DF=17 and TC=11, with the same ICAO24 address 40621D. So, those two frames are valid for decoding the positions of this aircraft.

CPR parameters and functions

First, we denotes some of the parameters and common functions used in the decoding process here.

NZ

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

floor(x)

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) return:

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

where y can not be zero

NL(lat)

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 equator or poles, following value is returned:

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

Latitude/Longitude calculation

There are a few technical documents that explain in detail the math behind the CPR. For example: a document from Eurocontrol.

Let’s first separate the CPR latitude and longitude bits in both messages. The steps after will guide you to calculate the LAT/LON of the aircraft.

| F | CPR Latitude      | CPR Longitude     |
|---|-------------------|-------------------|
| 0 | 10110101101001000 | 01100100010101100 |  -> newest frame received
| 1 | 10010000110101110 | 01100010000010010 |

Step 1: Convert the binary string to decimal value

LAT_CPR_EVEN: 93000 / 131072 -> 0.7095
LON_CPR_EVEN: 51372 / 131072 -> 0.3919
LAT_CPR_ODD:  74158 / 131072 -> 0.5658
LON_CPR_ODD:  50194 / 131072 -> 0.3829

Since CPR latitude and longitude are encoded in 17 bits, 131072 (2^17) is the maximum value. The resulting values from the calculations represent the percentages of that maximum value.

Step 2: Calculate the latitude index j

Use the following equation:

\[j = floor\left ( 59 \cdot Lat_{cprE} - 60 \cdot Lat_{cprO} + \frac{1}{2} \right )\]
j = 8

Step 3: Latitude

First, two constants will be used:

\[\begin{split}DLat_{E} &= \frac{360}{4 \times NZ} = \frac{360}{60}\end{split}\]\[\begin{split}DLat_{O} &= \frac{360}{4 \times NZ - 1} = \frac{360}{59}\end{split}\]

Then we can use the following equations to compute the relative latitudes:

\[\begin{split}Lat_{E} &= DLat_{E} * (mod(j, 60) + Lat_{cprE})\end{split}\]\[\begin{split}Lat_{O} &= DLat_{O} * (mod(j, 59) + Lat_{cprO})\end{split}\]

For southern hemisphere, values will fall from 270 to 360 degrees. we need to make sure the latitude is within range [-90, +90]:

\[\begin{split}Lat_{E} &= Lat_{E} - 360 \quad \text{if } (Lat_{E} \geq 270)\end{split}\]\[\begin{split}Lat_{O} &= Lat_{O} - 360 \quad \text{if } (Lat_{O} \geq 270)\end{split}\]

Final latitude is chosen depending on the time stamp of the frames–the newest one is used:

\[\begin{split}Lat = \begin{cases} Lat_{E} & \text{if } (T_{E} \geq T_{O}) \\ Lat_{O} & \text{else} \end{cases}\end{split}\]

In the example:

Lat_EVEN = 52.25720214843750
Lat_ODD  = 52.26578017412606
Lat = Lat_EVEN = 52.25720

Step 4: Check

Compute NL(Lat_E) and NL(Lat_O). If not the same, two positions are located at different latitude zones. Computation of a global longitude is not possible. exit the calculation and wait for new messages.

If two values are the same, we proceed to longitude calculation.

Step 5: Longitude

If the even frame come latest T_EVEN > T_ODD:

\[\begin{split}ni &= max \left( NL(Lat_{E}), 1 \right)\end{split}\]\[\begin{split}DLon &= \frac{360}{ni}\end{split}\]\[\begin{split}m &= floor\left ( Lon_{cprE} \cdot [NL(Lat_{E})-1] - Lon_{cprO} \cdot NL(Lat_{E}) + \frac{1}{2} \right )\end{split}\]\[\begin{split}Lon &= DLon \cdot \left( mod(m, ni) + Lon_{cprE} \right)\end{split}\]

In case where the odd frame come latest T_EVEN < T_ODD:

\[\begin{split}ni &= max \left( NL(Lat_{O})-1, 1 \right)\end{split}\]\[\begin{split}DLon &= \frac{360}{ni}\end{split}\]\[\begin{split}m &= floor\left ( Lon_{cprE} \cdot [NL(Lat_{O})-1] - Lon_{cprO} \cdot NL(Lat_{O}) + \frac{1}{2} \right )\end{split}\]\[\begin{split}Lon &= DLon \cdot \left( mod(m, ni) + Lon_{cprO} \right)\end{split}\]

if the result is larger than 180 degrees:

\[Lon = Lon - 360 \quad \text{if } (Lon \geq 180)\]

In the example:

Lon:  3.91937

Here is a Python implemented: https://github.com/junzis/pyModeS/blob/faf4313/pyModeS/adsb.py#L166

Altitude Calculation

The altitude of the aircraft is much easier to compute from the data frame. The bits in the altitude field (either odd or even frame) are as following:

1100001 1 1000
        ^
       Q-bit

This Q-bit (bit 48) indicates whether the altitude is encoded in multiples of 25 or 100 ft (0: 100 ft, 1: 25 ft).

For Q = 1, we can calculate the altitude as following:

First, remove the Q-bit

N = 1100001 1000 => 1560 (in decimal)

The final altitude value will be:

\[Alt = N * 25 - 1000 \text { (ft.)}\]

In this example, the altitude at which aircraft is flying is:

1560 * 25 - 1000 = 38000 ft.

Note that the altitude has the accuracy of +/- 25 ft when the Q-bit is 1, and the value can represent altitude from -1000 to +50175 ft.

The final position

Finally, we have all three components (latitude/longitude/altitude) of the aircraft position:

LAT: 52.25720 (degrees N)
LON:  3.91937 (degrees E)
ALT:    38000 ft

Airborne Velocity

There are two different types of messages for velocities, determined by 3-bit subtype in the message. With subtype 1 and 2, surface velocity (ground speed) is reported. And in subtype 3 and 4, aircraft airspeed are reported.

In real world, very few of the subtype 3 or 4 messages are reported. In our setup, we only received 0.3% of these message with regard to subtype 1 (subtype 2 seems never used).

An aircraft velocity message has DF: 17, TC: 19. and the subtype code are represented in bits 38 to 40. Now, we can decode those messages.

Decoding message subtype 1 or 2

Subtype 1 (subsonic) or 2 (supersonic), are broadcast when ground velocity information are available. The aircraft velocity contains speed and heading information. The speed and heading are also decomposed into North-South, and East-West components.

Note: The following decoding only apply to Subtype 1 (subsonic).

For example, following message is received:

Message: 8D485020994409940838175B284F

|    | ICAO24 |      DATA      |  CRC   |
|----|--------|----------------|--------|
| 8D | 485020 | 99440994083817 | 5B284F |

Convert into binary:

| HEADER                                 |  DATA                                                                                                                         | CRC                      |
|-------|-----|--------------------------|-------|-----|----|--------|-----|------|------------|------|------------|-------|------|-----------|--------|-------|---------|--------------------------|
| DF    | CA  | ICAO24 ADDRESS           |  TC   | ST  | IC | RESV_A | NAC | S-EW | V-EW       | S-NS | V-NS       | VrSrc | S-Vr | Vr        | RESV_B | S-Dif | Dif     | CRC                      |
|-------|-----|--------------------------|-------|-----|----|--------|-----|------|------------|------|------------|-------|------|-----------|--------|-------|---------|--------------------------|
| 10001 | 101 | 010010000101000000100000 | 10011 | 001 | 0  | 1      | 000 | 1    | 0000001001 | 1    | 0010100000 | 0     | 1    | 000001110 | 00     | 0     | 0010111 | 010110110010100001001111 |

There are quite a few parameters in the the velocity message. From left to rights, the number of bits indicate the following contents:

MSG Bits N bits Abbr Content
33-37 5 TC Type code
38-40 3 ST Subtype
41 1 IC Intent change flag
42 1 RESV_A Reserved-A
43-45 3 NAC Velocity uncertainty (NAC)
46 1 S-WE East-West velocity sign
47-56 10 V-WE East-West velocity
57 1 S-NS North-South velocity sign
58-67 10 V-NS North-South velocity
68 1 VrSrc Vertical rate source
69 1 S-Vr Vertical rate sign
70-78 9 Vr Vertical rate
79-80 2 RESV_B Reserved-B
81 1 S-Dif Diff from baro alt, sign
82-88 7 Dif Diff from baro alt

Horizontal Velocity

For calculating the horizontal speed and heading we need four values, East-West Velocity V(ew), East-West Velocity Sign S(ew), North-South Velocity V(ns), North-South Velocity Sign S(ns). And pay attention on the directions (signs) in the calculation.

S-NS:
    1 -> flying North to South
    0 -> flying South to North
S-EW:
    1 -> flying East to West
    0 -> flying West to East

In mathematical representation, the Speed (v) and heading (h) can be computed as following:

\[\begin{split}V(we) = \begin{cases} -1 * [V(ew) - 1] & \text{if } (s(ew) = 1) \\ V(ew) - 1 & \text{if } (s(ew) = 0) \end{cases}\end{split}\]
\[\begin{split}V(sn) = \begin{cases} -1 * [V(ns) - 1] & \text{if } (s(ns) = 1) \\ V(ns) - 1 & \text{if } (s(ns) = 0) \end{cases}\end{split}\]
\[v = \sqrt{V_{we}^{2} + V_{sn}^{2}}\]
\[h = arctan(\frac{V_{we}}{V_{sn}}) * \frac{360}{2\pi} \quad \text{(deg)}\]

In case of an negative value here, we will simply add 360 degrees.

\[\begin{split}h = h + 360 \quad (\text{if } h < 0)\end{split}\]

So, now we have the speed and heading of our example:

V-EW: 0000001001 -> 9
S-EW: 1
V-NS: 0010100000 -> 160
S-NS: 1

V(we) = - (9 - 1) = -8
V(sn) = - (160 - 1) = -159

v = 159.20 (kn)
h = 182.88 (deg)

Vertical Rate

The direction of vertical movement of aircraft can be read from S(Vr) field, in message bit-69:

0 -> UP
1 -> Down

The actual vertical rate Vr is the binary representation of the decimal value in feet/minute (ft/min). In our example:

Vr: 000001110 => 14
S-Vr: 0 => Down / Descending

So we see a descending aircraft at 14 ft/min rate of descend.

The Vertical Rate Source (VrSrc) field determine whether if it is a measurement in barometric pressure altitude or geometric altitude:

0 ->  Baro-pressure altitude change rate
1 ->  Geometric altitude change rate

Decoding message subtype 3 or 4

Subtype 3 (subsonic) or 3 (supersonic), are broadcast when ground speed information are NOT available, while airspeed is available. Subtype 3 or 4 messages are rare. As stated previously, we only received about 0.3% of those messages from all the velocity reports. However, the information contains airspeed of aircraft, which can be an interesting parameter in some researches. The structure of the message is similar to previous one. Let’s take a close look at an example for decoding here.

Note: The following decoding only apply to Subtype 3 (subsonic).

Message: 8DA05F219B06B6AF189400CBC33F

|    | ICAO24 |      DATA      |  CRC   |
|----|--------|----------------|--------|
| 8D | A05F21 | 9B06B6AF189400 | CBC33F |

Convert into binary:

| HEADER                                 |  DATA                                                                                                                         | CRC                      |
|-------|-----|--------------------------|-------|-----|----|--------|-----|------|------------|------|------------|-------|------|-----------|--------|-------|---------|--------------------------|
| DF    | CA  | ICAO24 ADDRESS           |  TC   | ST  | IC | RESV_A | NAC | H-s  | Hdg        | AS-t | AS         | VrSrc | S-Vr | Vr        | RESV_B | S-Dif | Dif     | CRC                      |
|-------|-----|--------------------------|-------|-----|----|--------|-----|------|------------|------|------------|-------|------|-----------|--------|-------|---------|--------------------------|
| 10001 | 101 | 101000000101111100100001 | 10011 | 011 | 0  | 0      | 000 | 1    | 1010110110 | 1    | 0101111000 | 1     | 1    | 000100101 | 00     | 0     | 0000000 | 110010111100001100111111 |

The detail bits representations are:

MSG Bits N bits Abbr Content
33-37 5 TC Type code
38-40 3 ST Subtype
41 1 IC Intent change flag
42 1 RESV_A Reserved-A
43-45 3 NAC Velocity uncertainty (NAC)
46 1 H-s Heading status
47-56 10 Hdg Heading (proportion)
57 1 AS-t Airspeed Type
58-67 10 AS Airspeed
68 1 VrSrc Vertical rate source
69 1 S-Vr Vertical rate sign
70-78 9 Vr Vertical rate
79-80 2 RESV_B Reserved-B
81 1 S-Dif Difference from baro alt, sign
82-88 7 Dif Difference from baro alt

Heading

H-s makes the status of heading data:

0 -> heading data not available
1 -> heading data available

10-bits Hdg is the represent the proportion of the degrees of a full circle, i.e. 360 degrees. (Note: 0000000000 - 1111111111 represents 0 - 1023 )

\[heading = Decimal(Hdg) / 1024 * 360^o\]

in our example

1010110110 -> 694
heading = 694 / 1024 * 360 = 243.98 (degree)

Velocity (Airspeed)

To find out which type of the airspeed (TAS or IAS), first we need to look at the AS-t field:

0 -> Indicated Airspeed (IAS)
1 -> True Airspeed (TAS)

And the the speed is simply a binary to decimal conversion of AS bits (in knot). In our example:

0101111000 -> 376 knot

Vertical Rate

The vertical rate decoding remains the same as subtype 1 or 2 messages.

NIC / NAC

NIC, NAC, NUC, and SIL, those acronyms do sound confusing. They are measurement for the integrity, accuracy, or uncertainties of the position measurement from GPS unit.

  • NIC - Navigation Integrity Category
  • NUC - Navigation Uncertainty Category
  • NAC - Navigation Accuracy Category
  • SIL - Surveillance/Source Integrity Level

Two of those values are more interesting for us, NICp and NACv, represent the position integrity and velocity accuracy respectively.

Before dive into decoding and interpolation, let’s introduce two parameters:

  • Rc: Horizontal Containment Radius Limit, interpolated from NICp number
  • HFOM: Horizontal Figure of Merit, interpolated from NACv number

NIC and Rc

Bring back the message from position decoding previously:

MSG: 8D40621D58C382D690C8AC2863A7

|    | ICAO24 |      DATA      |        |
|----|--------|----------------|--------|
| 8D | 40621D | 58C382D690C8AC | 2863A7 |

Convert both messages to binary strings:

| DF    | CA  | ICAO24 ADDRESS           | DATA                                                                                  | CRC                      |
|                                        | TC    | SS | SBnic | Altitude     | T | CPR-F | CPR Latitude      | CPR Longitude     |                          |
|-------|-----|--------------------------|-------|----|-------|--------------|---|-------|-------------------|-------------------|--------------------------|
| 10001 | 101 | 010000000110001000011101 | 01011 | 00 | 0     | 110000111000 | 0 | 0     | 10110101101001000 | 01100100010101100 | 001010000110001110100111 |

Not the *2 field (bit-40), where we have the NIC Supplement-B (S[B]) in combination with TC number, we are able to determine the NIC value.

The relation of TC, NIC, and Rc are as follow:

TC SBnic NIC Rc
9 0 11 < 7.5 m
10 0 10 < 25 m
11 1 9 < 74 m
0 8 < 0.1 NM (185 m)
12 0 7 < 0.2 NM (370 m)
13 1 * 6 < 0.3 NM (556 m)
0 < 0.5 NM (925 m)
1 ** < 0.6 NM (1111 m)
14 0 5 < 1.0 NM (1852 m)
15 0 4 < 2 NM (3704 m)
16 1 3 < 4 NM (7408 m)
0 2 < 8 NM (14.8 km)
17 0 1 < 20 NM (37.0 km)
18 0 0 > 20 NM or Unknown
  • * NIC Supplement-A = 0
  • ** NIC Supplement-A = 1

In our example:

TC -> 11
SBnic -> 0

We have:
  NIC -> 8

So, what happened to the NIC Supplement-A and C? Those two bits are broadcast in Aircraft Operational Status Message (TC=31, see Introduction page). For Surface Position Message, you will need the combination of A and C to determine the NIC number (note: Rc values are different from Airborne Position Messages). However, with Supplement-B bit we are already able to decode the NIC and Rc for airborne positions.

NAC and HFOM

NAC is reported in the Airborne Velocity Message.

Tips on ADS-B

If you have to write every line of code from scratch, reading and decoding ADS-B messages can be difficult at some point. Here we have some tips for those who might encounter the same challenges as we had.

The message structure

All the ADSB messages have a similar structure. Total of 112 bits, started 5 bits of Downlink Format, followed by 3 bits of the Message Type, then 24 bits of unique 24 bits of ICAO address, after that, is the 56 bits of data frame, and finished by 24 bits of parity check.

Type Code (bit 33 to 37) in located in inside, at the beginning of the data frame.

For computing the desired aircraft status, we need to have the right type of the data frame. The type is determined by Downlink Format and Type Code.

Then you can go ahead and decode each message different into correct values.

The CPR positions

The most crazy part is to compute the lat/lon from the Compact Position Reporting format data. Remember that we need to have two data frame (one odd, and one even) to calculate one position. Keep timestamps of those data frames, as at one point you will need to know the newest frame to have a better position calculation.

Withing the calculation process, an lookup table of so called Latitude Zone is used. check out our code too see how the earth latitudes are divided into 60 zones, of which are not equally distributed.

Aircraft Identification

After the aircraft ID (aka. Callsign) is decoded, there are sometimes spaces in the Callsign. Each time you decode on Callsign, you may want to strip the spaces before using it in your program. Sometimes a tailing space in a string can cause unexceptionable behaviors.

More than just ADS-B data

Usually the ADS-B data are presented live through a stream from a server (receiver). In order to have a good robust program, you will also need to do some low level networking programming to make sure the date are correctly received. Python - of course - has a great Socket library that can be used easily.

Mode S Enhanced Surveillance (EHS)

Let’s hack into the EHS messaged too! more information on aricraft air speeds. Guide to be added here soon...

For a complete Python implementation: https://github.com/junzis/pyModeS/blob/master/pyModeS/ehs.py

Apendix

Documents, code, and data

This guide document is shared on GitHub and ReadTheDoc. Please feel free to help us improving it.

Links to this guide document:

You can download from GitHub the python decoder, as well as some data samples we collected:

Contact

Feel free to drop me a messages at: j.sun-1[at]tudelft.nl

About us

We are a group at TuDelft working on aircraft operations and controls.

  • Junzi Sun, PhD Student
  • Jacco Hoekstra, Prof.dr.ir
  • Joost EllerBroek, Dr.ir

References

Some good source of documents:

  • RTCA/EUROCAE: Minimum Operational Performance Standards for 1090 MHz Extended Squitter Automatic Dependent Surveillance – Broadcast (ADS-B) and Traffic Information Services – Broadcast (TIS-B)
  • ICAO: Technical Provisions for Mode S Services and Extended Squitter
  • ICAO ADS-B Guide
  • Dump1090 Project
  • A Very Simple ADSB Receiver,