ADS-B Guide

Aircraft frequently pass overhead, often several within a short span. Services like Flightradar24 provide precise real-time information about these flights by utilizing data from the ADS-B protocol. In this guide, I will explain how to independently receive and decode this data.

ADS-B Guide

Everyone has certainly flown on an airplane or seen one at least once in their life. Modern aircraft operate globally, transporting vast numbers of passengers across various regions. However, air travel has only been possible for approximately a century, with reliable aircraft becoming available relatively recently.

Flightradar24

After the invention of airplanes and the start of commercial flights, it became clear that some way was needed to track all the vehicles in the air; otherwise, accidents would be unavoidable. Radars or even visual observation were not good enough for this task, so radio communication came into play. Now every airplane has an aviation transponder on board. This greatly eases the work of dispatchers and pilots and allows data from onboard sensors to be transmitted during flight and instructions from the ground to be received.

In simple terms, an aviation transponder is a two-way radio communication device that does two things:

  1. Responds to requests from ground stations: when the dispatcher asks for data, the transponder automatically responds. This request for data is also called an "interrogation".
  2. Acts as an aircraft's radio beacon: in this mode, the transponder periodically sends information about itself, such as its location or speed.

There are different generations, or modes, of transponders. Each was created for different purposes and has its own signal structure, although later modes support everything that earlier ones do (though their signals are incompatible). There are five main modes:

  • Mode A only transmits the identification number of the aircraft. This number can be hard-coded into the transponder or assigned by a dispatcher before takeoff. In practice, it was used to track which airport an airplane was at.
  • Mode C, created later, allowed tracking not just the aircraft's number but also its flight altitude. Its main advantage was that it could automatically get the plane’s altitude without asking the pilot.
  • Mode S is a modern mode currently used on 99% of all airplanes. It allows not only receiving data from onboard sensors but also sending data back to the airplane. In this mode, there is full two-way communication between the aircraft and ground stations. ADS-B, which we will discuss today, is part of this mode.
  • Mode 4 and Mode 5 are more advanced and used exclusively by military forces. Both are much better protected (unlike previous ones), so it's not possible to tamper with them.

Modes B and D are not included in this list, as their existence was brief and does not warrant detailed discussion.

ADS-B

A review of the description of Mode S reveals that messages transmitted via this protocol are typically sent by the transponder in response to a request from a ground dispatcher, with the exception of ADS-B (Automatic Dependent Surveillance — Broadcast). In this context, 'Broadcast' signifies that messages are transmitted universally rather than to a specific recipient, thereby enabling their reception by any equipped system.

It is a common misconception to regard ADS-B as an independent transponder mode equivalent to Modes A, C, or S. However, it is actually just part of the larger Mode S. An ADS-B message is simply a Mode S message with type 17.

Types of Mode S Messages

This article focuses only on ADS-B (type 17), but it is useful to know about other types of Mode S messages as well:

  • All-call reply (type 11) — this message, in response to an operator's request, transmits the unique 24-bit identifier of the transponder. This number is usually programmed at the factory and remains unchanged, but it may be replaced for military purposes.
  • ACAS short and long replies (type 0/16) — these messages are used to prevent collisions between aircraft. If a transponder detects another aircraft nearby, it sends an alert to other systems that can prevent mid-air collisions.
  • Altitude and identity replies (type 4/5) — this message contains data about the altitude and call sign (often referred to as the squawk code, which is manually entered by the pilot before takeoff).
  • Comm-B (type 20/21) — these messages include readings from onboard sensors, planned routes, and other data that can be useful for vessel management.

While the operation of the Airborne Collision Avoidance System (ACAS) is sophisticated, an in-depth discussion is beyond the scope of this article.

All Mode S messages are transmitted to the aircraft at 1030 MHz and to the ground at 1090 MHz.

The radio transmission is not encrypted, and it carries a lot of interesting information about the aircraft's location, altitude, speed, and other parameters. This led to the creation of services like Flightradar24, which make flight information freely available to everyone. These services gather data from numerous sensors installed by volunteers worldwide.

Physical Structure of the Signal

The ADS-B signals are transmitted by the aircraft at 1090 MHz, similar to all other Mode S signals. The second frequency, 1030 MHz (uplink), is not required because ADS-B signals are sent without being requested.

Pulse-Position Modulation (PPM) is used for encoding. Essentially, bits are transmitted into the air and can be read by sampling every N microseconds. This process is clearly illustrated in an image from Wikipedia.

PPM

In ADS-B, each bit lasts 0.5 microseconds, so samples can be taken every 0.5 microseconds to determine whether each bit has a high or low level. These levels are then recorded and converted into bytes to obtain the original message. However, this is theoretical; in practice, there are issues that we will address as we proceed.

Structure of the Packet

If you take the data as it is, you will get a bit of a mess from which useful information still needs to be extracted. The data itself has a very clear structure, so if you notice its constant parts in the data stream, you can obtain the entire packet.

ADS-B Signal

The packet consists of a preamble and actual data. As seen above, the preamble lasts for 8 microseconds, followed by data over 56 or 112 microseconds.

The preamble is particularly important when considering that all aircraft broadcast on the same frequency and signals from them can arrive simultaneously. The issue of overlapping signals being lost is resolved quite simply: if something is not caught by one receiver, another will catch it. There are many receivers covering most inhabited areas of Earth; thus, if a specific signal is too weak for one receiver, it will be strong enough to be picked up by another and will not be lost. Of course, this approach does not guarantee that all signals will be received, but it is unnecessary since the signals repeat regularly, and losing some will not cause any significant problems.

I have already mentioned that each bit is encoded with 0.5 microseconds, which is true. However, to simplify reception, a convention was introduced: one actual bit is coded as two bits of 0.5 microseconds each. A one is then coded as 1 and 0, while a zero is coded as 0 and 1 (thus, the data 1011 would appear as 10011010). This does not complicate the receiver but protects against interference and allows reliable signal capture. Without this modification, transmitting several zeros would look like silence in the air, but with this method, the receiver always clearly sees the signal, even when zeros are being transmitted.

Structure of Data

Assume we have decoded the signal and found a message within it. Now we need to decode the useful data block and filter out unnecessary messages (i.e., all Mode S messages except ADS-B).

ADS-B Fields

The length of the message we are interested in is 112 microseconds, which equals exactly 112 bits (thanks to two-bit encoding!), and it is divided into five main blocks:

  1. DF (Downlink Format) — transmission format code, 5 bits. For ADS-B, this value is always 17.
  2. CA (Transponder capability) — type of transmitter and its capability level, 3 bits. This field helps the dispatcher know what data can be requested from this transponder. The field can have values 0, 4, 5, or 6; values 1–3 and 7 are reserved for future use. Value 0 indicates a first-level transponder that typically does not have ACAS (collision avoidance system). Value 4 indicates a second-level or more advanced transponder without ACAS but capable of transmitting altitude (i.e., working with Mode C and Mode S). Values 5 and 6 are the same as 4 but support ACAS, where 6 means ACAS is enabled and 5 means it is disabled.
  3. ICAO — unique aircraft number, 24 bits. This number identifies the signal sender. It is programmed once at the factory and does not change during operation, although some individuals can alter them. There are also military transponders that do not follow any rules, so anything could be there.
  4. ME (Message) — actual payload with data about altitude, speed, or other information, 56 bits in length. We will examine this block in more detail below.
  5. PI (Parity/Interrogator ID) — checksum, 24 bits.

Field ME

The ME field is the most interesting part for us because it contains coordinates, flight speed, altitude, and other data from onboard sensors. 56 bits are not enough to transmit all this information at once, so each message has its own type indicated by the first five bits of this field. It’s like a nesting doll: there is a specific format for Mode S messages to indicate ADS-B, and another format within ADS-B to specify what data is inside.

ADS-B has 31 types of data in total, but we will focus on the most important ones.

Code 1–4: This message contains identification data. It includes the call sign and other registration-related properties (such as whether it is a light or heavy aircraft). These call signs are displayed on airport terminals and represent flight numbers. A decoded message looks like this:

ME : ADSB Msg Type  : (4) Aircraft Identification and Category
CAT: Aircraft Cat   : (0:0) No ADS-B Emitter Category Information
    flight Number   : SIA224  
Wake Type           : (TC:4 CAT:0) - No Information Provided

Code 5–8: This message contains the position on the ground. These data are usually used to know where and which runway an aircraft is at. The message can include latitude, longitude, speed, and direction of movement. An example of a decoded message:

ME : ADSB Msg Type  : (7) Surface Position
  Super Sonic?      : No
  velocity          : 0.00
  EW/NS VEL         : (East/west: 0) (North/South: 0)
  heading           : 253.12
Before Decoding : Half of vehicle location
  UTC Sync?     : false
  CPR Frame     : Odd
  CPR latitude  : 8675
  CPR longitude : 17674

Code 9–19: This message contains the position in the air (usually transmitted together with altitude). It is important to understand that you cannot find a familiar latitude and longitude here because a short notation of coordinates is used instead of normal ones. How to deal with such coordinates will be explained below.

ME : ADSB Msg Type  : (14) Airborne Position (with Barometric altitude)
  Containment Radius: 1852.00 metres
  Surveillance      : (status:0) No condition information
  NIC Supplement B  : 0
  Nav Integrity     : 5
AC: altitude        : 3100 feet (q bit: false, m bit: false)
Before Decoding : Half of vehicle location
  UTC Sync?     : true
  CPR Frame     : Odd
  CPR latitude  : 101906
  CPR longitude : 103292

Code 19: This message contains the speed of an aircraft.

ME : ADSB Msg Type  : (19) Airborne velocity
SUB:      Sub Type  : 3 
  Intent Change     : false
  IFR Capable       : true
  Nav Accuracy Cat  : 0
  heading           : 8.44
  Super Sonic?      : No
  velocity          : 136.00
  EW/NS VEL         : (East/west: 0) (North/South: 0)
  Vertical Rate     : -768
  HAE Delta         : Unavailable

Decoding each message bit by bit can take a long time, so if you are really interested, you can search for ready-made ADS-B parsers on GitHub and see their structure. For now, it doesn’t make sense to delve into the construction of these messages because we will not be transmitting anything yet, and we do not want to go too deep into the protocol details.

CPR: Simplifying the Complex

To accurately determine coordinates, we typically use two numbers — latitude and longitude. A 32-bit float provides precision up to seven decimal places, which translates into accuracy on the order of a few centimeters for coordinates. If we slightly reduce this precision (to tens of centimeters), then two such numbers can fit into the 56 bits of a message without needing complex compact coordinate systems. For an aircraft flying at over 100 m/s, accuracy in the range of tens of centimeters is enormous, so it's hard to understand what guided the creators of the protocol.

Nevertheless, for recording coordinates, the CPR (Compact Position Reporting) format is used, as its name suggests, which is designed for compact transmission of coordinates. We have already seen part of this data in an example signal with coordinates above. Since compressing a large amount of data into a small space is impossible, these data are simply split into parts and sent in two batches; the packets are called "even" and "odd." How do we get normal coordinates from this? Let me show you!

Imagine all aircraft flying in 2D-space. This space is divided into two grids with different parameters — an even grid and an odd one. The even grid will be a 4x4, while the odd grid will be a 5x5.

Let's say we want to transmit the location of an aircraft in a 16x16 grid — let these coordinates be (9,7). If there was just one grid, we would simply send 9 and 7, and operators would find us on their map. However, CPR actually uses two grids.

Grids

In such grids, our position (9,7) is indicated as (1,3) in the even grid (left) and (4,2) in the odd grid (right). When operators receive both messages, they need to overlay them on both grids.

Grids 2

By overlapping these two grids with the received coordinates, we find our object at the point of intersection.

Grids 3

I have described this algorithm without complex mathematical calculations to help you understand how coordinates are recovered from two parts. The real grid is different from the example and looks like the image below.

Real Grid

A Simple Way to Receive ADS-B

Now that we understand the main components of the protocol, let's try to receive a real signal. To capture any such signal, you will need three basic things: an antenna, a receiver, and a PC.

The Antenna

Let's start with the most important item that is essential for any radio communication — an antenna. The choice of antenna depends on many factors, including frequency, signal directionality, and the environment through which it travels. Our signal is transmitted at 1090 MHz and will be received in open space.

The simplest option, though less efficient, is a whip antenna, which can be constructed using a piece of wire or metal. Its length must be calculated accurately to match the signal frequency. The length of the antenna depends on the wavelength we want it to receive. Wavelength is the distance between two adjacent "peaks" of the signal (see the image below).

Wavelength

Lambda (λ) is the wavelength, which can be obtained from frequency using the formula λ = C/f, where C is the speed of light and f is the signal frequency. For 1090 MHz, it is approximately equal to 27.5 cm.

If you use a metal rod of this length, you will get what is called a full-wave antenna. You can safely shorten it by half or even a quarter to obtain a half-wave or quarter-wave antenna respectively. There will be differences in sensitivity among these antennas, so I recommend using a half-wave antenna with a length of approximately 13.75 cm.

Constructing an antenna can be a complex task; therefore, I used a pre-assembled antenna for this demonstration. Antennas for walkie-talkies might be suitable for you if you are receiving signals in open areas with minimal interference. I use a standard coil-loaded whip antenna, which essentially functions like a whip antenna but is shorter due to the coil.

My antenna

You can measure the main characteristics of an antenna using a special vector analyzer, which generates different frequencies and checks how the antenna reacts to them.

SWR

The output of the analyzer may seem complicated at first glance, but it's actually quite simple. To check if an antenna is suitable for a specific frequency, you just need to look at the yellow SWR (Standing Wave Ratio) line. This coefficient shows how much of the signal the antenna has transmitted into the air and how much has returned. The less signal that returns, the better the antenna performs at a specific frequency.

The device shows that at marker 1 (I set it to 1090 MHz), the SWR is 1.73, which is quite good. Typically, a good antenna has an SWR around 1 (not more than 2).

Receiver

For the receiver, we will use an SDR (Software Defined Radio) dongle. It is essentially a regular radio that is controlled by special software instead of using knobs like in old-fashioned radios. Any SDR adapter can be used for ADS-B, starting from the cheapest RTL-SDR to more expensive ones like BladeRF and similar models. The price of cheaper options starts at around $30, so everyone can get started.

I will be using the BladeRF micro - it supports a wide frequency range and has a high sampling rate, which we will discuss later.

BladeRF

Putting It All Together

Now that you have the antenna and SDR, you need to find a place free from interference and obstacles - I just drove about ten kilometers outside the city. Signals around 1 GHz (which includes ADS-B) barely propagate beyond the horizon. So, if you don't live near an airport and there are obstacles around, you might not receive any signals at all.

Improving Reception

You can add a filter between the antenna and the SDR to reduce noise. There are special filters designed for ADS-B, which you can order from China for about $10–15.

To analyze the radio spectrum, GQRX is a recommended tool for Linux and macOS, while SDR# is suitable for Windows users. In Ubuntu, GQRX can be installed from the official repositories:

apt update
apt install -y gqrx 

Next, turn up the volume, select your SDR as the input source, and press the large "Start" button. If everything is set up correctly, you will hear a loud hissing noise. After that, mute the sound using the Mute button located in the bottom right corner.

To set the frequency, enter 1.090.000 (which equals 1090 MHz) at the top of the screen. Once you do this, you should see something similar to the screenshot below.

GQRX

The short bars in the center are the ADS-B signals, which stand out clearly against the background noise. If the signals are not visible, adjust the gain settings in the "Input Controls" tab. Alternatively, modify the "Plot" and "WF" parameters in the "FFT Settings" tab or reposition the antenna for improved reception.

Dump1090

When you achieve a stable signal reception in GQRX, you can move on to the next step.

In most cases, if someone wants to receive and decode Mode S signals, they use an existing program like dump1090. This tool with open-source code can demodulate and decode almost all Mode S signals and even display them in a neat table. To check if everything is working correctly, it's best to start with something that is known to work well, and we'll begin with dump1090.

To install it, you need to clone the project repository from GitHub and build the binary. This is quite simple:

git clone https://github.com/antirez/dump1090
cd dump1090
make

After this, you should have the dump1090 binary. If you are using an RTL-SDR, you can use dump1090 directly with it, but I am using a BladeRF, which requires some additional setup.

First, install the driver for your SDR, they are available in the repositories of most distributions; you can search online to find them.

Second, you need to flash a special firmware onto your SDR. For BladeRF, these firmwares are available on the Nuand website, where you should choose the appropriate file for your version of BladeRF.

Next, download the program for decoding ADS-B messages and build it:

git clone https://github.com/Nuand/bladeRF-adsb
cd bladeRF-adsb/bladeRF_adsb
make

Now, flash the firmware to the BladeRF using the bladerf-cli package:

bladeRF-cli -l ~/Downloads/adsbxA4.rbf

Next, run dump1090 in one window and bladeRF-adsb, which you built a few steps earlier, in another window:

~/Soft/dump1090/dump1090 --raw --device-type bladerf --bladerf-fpga ''
~/Soft/Blade/bladeRF-adsb

If everything is set up correctly, you will see a lot of hexadecimal lines in the dump1090 window. These are Mode S messages that still need to be decoded and filtered.

Dump1090 output

If you remove --raw from the dump1090 startup arguments, the signals will be automatically decoded and displayed in a table, as shown in the screenshot below.

Dump1090 table

In the table, you can find already decoded latitude and longitude from CPR, the aircraft call sign, its speed, altitude, and other data. To deepen understanding, we will manually decode the data.

A More Complex Way to Receive ADS-B

To receive the signal manually, you first need to demodulate it, and then decode what you get.

Demodulation

Demodulation (signal detection) is the process opposite to signal modulation, where the information (modulating) signal is extracted from a high-frequency modulated wave. In simpler terms, demodulation means extracting the signal from high-frequency oscillations.

ADS-B uses PPM (Pulse Position Modulation). To extract the useful signal, you need to determine the state of the signal every 0.5 microseconds (0 or 1). Here’s our plan:

  1. Record the signal.
  2. Take samples every 0.5 microseconds.
  3. Convert them into bytes.
  4. Decode the ADS-B messages.

For convenience, we will use either Python or a pre-made set of tools from GNU Radio. On Ubuntu, GNU Radio can be installed as follows:

sudo add-apt-repository ppa:gnuradio/gnuradio-releases -y
sudo apt update
sudo apt install gnuradio -y

Of course, for GNU Radio to work, you will need the drivers for your SDR. However, we won't use the SDR as the primary signal source so that we can run different algorithms on the same data and compare results.

Recording the Signal

The first thing to do after launching GNU Radio is to build a basic pipeline. It should look like the screenshot below.

Basic pipeline

There are five blocks here, two of which are options and a variable block. At the very beginning is the osmocom Source, which handles receiving raw data from the SDR. There are many settings, but only five are necessary (example below for BladeRF):

  1. Device Argument — arguments passed to the SDR. Enter bladerf=0 here.
  2. Ch0 Freq — frequency of channel 0. BladeRF has two channels, but we need only one. Enter the ADS-B frequency, which is 1090 MHz or 1.09 GHz.
  3. RF Gain — sets the receiver sensitivity. I set it to 15 dB. Do not set too high a sensitivity; strong signals can damage the radio chain!
  4. Bandwidth — reception channel bandwidth, for ADS-B this is 2 MHz.

The fifth parameter is Sample Rate or sampling rate. In simple terms, this is the number of samples per second that the SDR takes to digitize the signal. What is it and why do we need it? The SDR receives an analog signal at its input, which needs to be digitized for processing. To digitize the signal, samples are taken at regular intervals. The more samples per second, the better, but this requires a more powerful device. The process of digitization is illustrated in the image below.

ADC

The number of bars in the picture represents the number of samples, and SPS stands for Samples per Second, which is the number of samples taken per second. In the samp_rate parameter at the top, I set the sampling rate to 4 MS/s (MegaSamples per Second). This value is not random; it is four times higher than the input signal frequency. According to the Nyquist theorem, the sampling rate must be at least twice as high as the signal frequency. For ADS-B, you can use sampling rates of 2 and 2.5 MS/s, but a higher sampling rate results in better digitization quality.

Nyquist theorem

The Nyquist Theorem (also known as the Sampling Theorem or Kotelnikov's theorem) is a fundamental principle in digital signal processing that connects continuous and discrete signals. It states that "any function F(t), consisting of frequencies from 0 to f1, can be continuously transmitted with any accuracy using numbers following each other at intervals less than 1 / (2 * f1) seconds."

Now that everything is set up, you can press the "Start" button on the top panel in GNU Radio. If everything is configured correctly, you will see a spectrum similar to what I captured.

GNU radio

The ADS-B signals are visible as stripes at the top of the screenshot. Although the BladeRF used here is not calibrated, the distortions do not significantly impact the experiment.

Demodulation

For demodulation, I set up a more complex and interesting scheme in GNU Radio.

Demodulation GNU Radio

Let's go through each block of the scheme so you understand what is happening. On the diagram, some inputs and outputs have different colors; these colors indicate different data types:

  • Blue — complex numbers.
  • Orange — floating-point numbers.
  • Purple — bytes.

Now let’s move on to the blocks themselves.

  1. File Source — this is a signal source from a file. In this block, you need to select the file we recorded when receiving the signal. This block replaces the real SDR for our experiments.
  2. Throttle — an element that slows down the flow of signals into our scheme. Without this block, all processing would happen too quickly and nothing would be visible.
  3. Waterfall — a visualization of the signal. The yellow-blue pixel mush on the graph is what it represents.
  4. QT GUI Time Sink draws a plot of the signal over time.
  5. Complex To Mag — a conversion block from complex numbers to floating-point numbers. It has blue input values and orange output values.
  6. Threshold — the most important block in our demodulation scheme, which helps separate signals from noise. I set the values low 13m and high 13m, these are the amplitudes of the signals that need to be separated.
  7. Symbol Sync — another important synchronization block for symbols. As mentioned earlier, I chose a sampling rate of 4 MSPS, which is necessary for this sync block because its minimum parameter Samples per Symbol is 2, and this is possible only if the input signal's sampling rate is at least 4 MSPS. If you have a less powerful receiver (like RTL-SDR), it’s fine to disable Symbol Sync and use the Keep 1 in N block (drawn gray on the scheme above). In my test, with synchronization from raw recordings, I got 18 out of 31 good packets, while without it, only 11 out of 29. So using proper synchronization gives significantly more valid data.
  8. Float to Char — this block converts floating-point numbers to characters and sends them to the File Sink.
  9. File Sink saves bytes into a file for further analysis.

How to Determine Sensitivity Threshold

To find the threshold for your SDR, you will have to build another additional scheme. In the search on the right, find blocks Peak Detector and Time Sink. In the Time Sink block, set three inputs, and if the Peak Detector lacks a Debug port, then only two. After building the scheme, you will see at what amplitude useful signal peaks appear.

We’ve covered the scheme; you can now activate it. After starting, you will see something like this picture.

Demodulation scheme

First, we have the Waterfall, where it's easy to visually search for signals. Next is the Time Sink after synchronization: here you see two signals — red (error signal) and blue (time-corrected signal). These signals already look somewhat digital, but the true digital signal appears on the third graph (processed by the Threshold block). The fourth graph shows the same original signal as the first graph, but processed by the Threshold block.

File Analysis

After demodulation, the file will look like this.

000717e0: 0000 0100 0100 0000 0000 0001 0000 0000  ................
000717f0: 0101 0000 0100 0000 0000 0101 0000 0100  ................
00071800: 0101 0001 0000 0100 0101 0000 0101 0001  ................
00071810: 0000 0101 0001 0001 0100 0101 0000 0100  ................
00071820: 0100 0100 0100 0101 0001 0001 0001 0000  ................
00071830: 0100 0100 0100 0100 0101 0000 0100 0100  ................
00071840: 0100 0001 0101 0000 0100 0100 0100 0100  ................
00071850: 0100 0100 0100 0100 0100 0100 0100 0100  ................
00071860: 0100 0100 0100 0100 0100 0100 0100 0100  ................
00071870: 0100 0100 0100 0100 0100 0100 0100 0101  ................

Next, the file is analyzed to identify Mode S packet preambles within the data stream. I have written a few functions in Python to do that.

import requests

def read_binary_file(filename):
 with open(filename, 'rb') as file:
  return [int(byte) for byte in file.read()]

def find_pattern(data, pattern):
 pattern_length = len(pattern)
 matches = []
 for i in range(len(data) - pattern_length + 1):
  if data[i:i+pattern_length] == pattern:
   matches.append(i)
 return matches

The first function simply reads the file, while the second searches for a specified pattern (our preamble). However, before searching, we need to convert double bits into single ones (those repetitions that make each bit last 1 microsecond instead of 0.5 microseconds). The next function does exactly this:

def decode_adsb(bits):
  decoded = []
  for i in range(0, len(bits), 2):
   if bits[i:i+2] == [1, 0]:
     decoded.append(1)
   elif bits[i:i+2] == [0, 1]:
     decoded.append(0)
  return decoded

Read the file and find all preambles:

data = read_binary_file("decoded2.bin")
pattern = [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
matches = find_pattern(data, pattern)
print("Total matches: ", len(matches))
good = 0
bad = 0

Here pattern is the desired preamble. You can even scroll up in the article to compare. Since the preamble indicates any Mode S signal, not just ADS-B, there might be signals that are 56 bits long. Support for these is also included.

for match_index in matches:
 try:
  start_index = match_index + len(pattern)

  end_index = start_index + 224
  end_index2 = start_index + 112

  extracted_bits = data[start_index:end_index]
  extracted_bits2 = data[start_index:end_index2] 

  decoded_bits = decode_adsb(extracted_bits)
  decoded_bits2 = decode_adsb(extracted_bits2)

  hex_value = hex(int(''.join(map(str, decoded_bits)), 2)).replace('0x','')
  hex_value2 = hex(int(''.join(map(str, decoded_bits2)), 2)).replace('0x','')

  if len(hex_value) == 28:
   response = requests.get(f"http://jasonplayne.com:8080/decode?packet={hex_value}&refLat=&refLon=")
   if "Failed to decode." in response.text:
    bad += 1
   else:
    good += 1
    print(response.text)

  if len(hex_value2) == 14:
   response = requests.get(f"http://jasonplayne.com:8080/decode?packet={hex_value}&refLat=&refLon=")
   if "Failed to decode." in response.text:
    bad += 1
   else:
    good += 1
    print(response.text)

 except:
  pass
print("Total good/bad: ", good, "/", bad)

After reading the code, you'll notice I extract ADS-B packets and send them to an external service for decoding instead of creating a custom parser for all packet types. You can open this same site in your browser to browse sample packets and dive deeper into the world of Mode S and ADS-B.

If everything goes well, you'll see output similar to this:

8d89617999086e99b8480ab9e174
MODE S Packet:
Length              : 56 bits
Frame               : 8d89617999086e99b8480ab9e174
DF: Downlink Format : (17) ADS-B
CA: Plane Mode S Cap: (5) Level 2,3 or 4. can set code 7. is airborne
VS: Vertical status : Airborne
AA: ICAO            : 896179

ME : ADSB Msg Type  : (19) Airborne velocity
SUB:      Sub Type  : 1 
  Intent Change     : false
  IFR Capable       : false
  Nav Accuracy Cat  : 1
  heading           : 151.88
  Super Sonic?      : No
  velocity          : 232.65
  EW/NS VEL         : (East/west: 109) (North/South: -204)
  Vertical Rate     : -1088
  HAE Delta         : 225 (Height Above Ellipsoid)

I received signals with aircraft speed information, but you might get many more. My DF is 17 (ADS-B), and the transponder's ICAO code is 896179.

This exploration provides an introduction to the technical aspects of ADS-B and radio signal decoding. Further experimentation is encouraged for those interested.

Subscribe to exploit.org

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe