Tuesday, June 17, 2025

Posting ACARS Messages Online



After all the work done to set up a radio monitor to scan the ACARS frequenices, figuring out what kind of messages I wanted to see, and finally building a way to easily view the messages, the final  part of the project is to now post these messages up somewhere where I can view them almost anywhere.

 As mentioned in the previous post, I had looked into building a feed to the usual social media applications, but usually these types of feeds usually required the use of API connections to make things work. While this is pretty common within the IT world where I spend my working hours, I also realize that a good part of the world may not be familiar with the voodoo that define electronic data interchange standards, so I wanted to develop a solution that would be pretty simple to implement by someone who wasn't a tech guru. 

After some experiementing, I settled on a solution that would post my daily ACARS feed up to a fairly simple Blogger web site. The main reason for using Blogger is that it's a platform that is built to allow people to post to a personal webpage via a relativley simple user interface.  Blogger also has the ability to customize the look and feel of your webpage, so you can be creative and have your webpage suit your personal taste.  The third (and probably the most important) reason I chose Blogger was that it was free. 

Since Blogger utilizes a graphical interface for creating posts, I thought it might be interesting to try and create a solution that minics a live person creating a post. Granted, this would probably be seen as sacraligous by my IT colleagues to consider this sort of solution, but like I said earlier, I wanted to build something that a "non-techy" person might be able to build with a minimal amount of fuss.    

 With those goals in mind, I started work by creating a Blogger site called "ACARS Radio Log" <Click To Visit Site> and after doing some tinkering on my site's look and feel, I had somethng that allowed me to easily see the current messages.

With the website set up, I next looked into how to build the process of loading up ACARS messages to the site.

Obviously I wanted to look at some sort of programmed solution and since I've had pretty good luck with Python so far I wanted to still some how be able to use it for the solution's foundation. 

In sketching out a logic flow, I can up with this high level process:

  • Open a browser (I chose Chrome as the browser for my solution)
  • Log onto the Google account (since Blogger is owned by Google)
  • Open the blogger session for my ACARS Log site and click on the New Post button
  • Set the Edit window that opens up to "HTML" mode
  • Copy the contents of the "blog_ready.html" file that was created in the previous process into the Blogger Edit window
  • Save and Post the Blog Post.    

An Interactive Session

To make this work, I needed to program something that was going to work in interactive mode, which as it suggests, means that the program needs to "interact" with the computer's GUI environment, which is a bit different for Python program, which usually likes to work behnd the scenes. 

The one thing that I like the most about Python is that there seems to be a really extensive library of companion tools that seems to give it the ability do almost anything. After some research, I came across a plug in called Playwright, which is a general purpose browser automation tool. 

Doing some experimenting with Playwright, I found that it would do a great job of opening up a browser and logging onto my Blogger page. However it was having troubles navigating the editor on Blogger. Doing some investigation showed that Playwright was looking for specific html code snippets within the Blogger page itself that didn't exist in the current Blogger site. What this told me was that Blogger would occasionally make some internal changes to their site, which even if I could get it to evetually work with Playwright, it was likely a matter of time before it broke again. 

To get around that I needed to find a method that avoided the need for looking at the internal programming of the site.  After some poking around, I decided I needed to resort to a good old fashion key logger. 

Key Logging

Again, looking at the Python libraries, I did find a plugin that would play back recorded key stokes. I ended up using a plugin called Pynput. This tool will allow you to record and playback any keystrokes on a computer, which is then stored as a json file, making editting of your keystrokes pretty easy if you need to modify anything. 

The first step was to get my Blogger session to the point where I was starting to have problems with Playwright. To create the keylogger file, I needed to create a "throw away" Python to create the log:

# keylogger.py
from pynput import keyboard
import json

keystrokes = []

def on_press(key):
    try:
        keystrokes.append(key.char)
    except AttributeError:
        keystrokes.append(str(key))

def on_release(key):
    if key == keyboard.Key.esc:
        # Save to file on ESC key press
        with open("keystrokes+2.json", "w") as f:
            json.dump(keystrokes, f)
        print("Keystrokes saved. Exiting...")
        return False  # Stop listener

with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    print("Recording... Press ESC to stop.")
    listener.join()

Executing this program turns the recorder on and I can start recording keystrokes (I noticed that it won't record any mouse activity). After I finished my keystrokes, I simply turned the recorder off by pressing the Escape key. 

After recording, I got this as a json file:

["Key.tab", "Key.tab", "Key.tab", "Key.tab", "Key.tab", "Key.enter", "Key.down", "Key.up", "Key.enter"]

After putting together some Pynput playback code, I gave it a quick test 


It certainly will do what I want it to do. 

Posting To The Blog

Now that I've sorted out the mechanics how things should work, I threw together a small Python program that follows the logic flow that I've highlighted earlier

import os
import time
from playwright.sync_api import sync_playwright
from pynput.keyboard import Controller, Key
import json
import pyperclip


# === CONFIGURATION ===
BLOGGER_URL = "https://www.blogger.com"
HTML_FILE_PATH = "blog_ready.html"
PASTE_DELAY = 1.0

# === Credentials ===
EMAIL = "<My Google Account>"
PASSWORD = "<My Password>"

if not EMAIL or not PASSWORD:
    raise ValueError("Missing EMAIL or PASSWORD")

# === Load HTML content ===
with open(HTML_FILE_PATH, 'r', encoding='utf-8') as file:
    html_content = file.read()

# === Copy to clipboard ===
pyperclip.copy(html_content)

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False, args=[
        '--disable-blink-features=AutomationControlled',
        '--incognito',
        '--disable-extensions',
        '--start-maximized'
    ])
    context = browser.new_context()
    page = context.new_page()

    # Stealth
    page.add_init_script("""
        Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
    """)

    # Step 1: Google login
    page.goto("https://accounts.google.com/")
    page.wait_for_selector("input[type='email']", timeout=10000)
    page.fill("input[type='email']", EMAIL)
    page.click("#identifierNext")

    page.wait_for_selector("input[type='password']", timeout=10000)
    page.fill("input[type='password']", PASSWORD)
    page.click("#passwordNext")

    page.wait_for_timeout(5000)  # Let login settle

    # Step 2: Go to Blogger dashboard
    page.goto(BLOGGER_URL)
    page.wait_for_load_state("load", timeout=15000)
    page.wait_for_timeout(5000)

    # Click New Post
    print("Clicking NEW POST button...")
    try:
        new_post_button = page.get_by_role("button", name="New Post").first
        new_post_button.wait_for(state="visible", timeout=10000)
        new_post_button.click()
    except Exception as e:
        print(f"Failed to click NEW POST button: {e}")
    # Do not close the browser; just wait for user input
        input("Press Enter to exit and close the browser...")
        exit(1)

    keyboard_controller = Controller()

    # Load keystrokes from file
    with open("keystrokes_1.json", "r") as f:
        keystrokes = json.load(f)
 
    print("Replaying keystrokes in 3 seconds...")
    time.sleep(3)

    for key in keystrokes:
        if key.startswith("Key."):
            # Convert string back to actual Key
            try:
                k = getattr(Key, key.split(".")[1])
                keyboard_controller.press(k)
                keyboard_controller.release(k)
            except AttributeError:
                pass  # Unknown key
        else:
            keyboard_controller.press(key)
            keyboard_controller.release(key)
        time.sleep(0.05)  # slight delay to mimic real typing

    # === Wait for user to focus editor ===
    print("📋 HTML content copied to clipboard.")
    print("Switch to the editor window in the next few seconds...")
    time.sleep(PASTE_DELAY)

    # === Paste using keyboard (Ctrl+V) ===
    keyboard = Controller()
    keyboard.press(Key.ctrl)
    keyboard.press('v')
    keyboard.release('v')
    keyboard.release(Key.ctrl)

    print("✅ Content pasted.")
  
     # === Publish post ===
    try:
        print("🚀 Attempting to publish post...")

    # Look for 'Publish' button
        publish_button = page.locator('div[role="button"] span:has-text("Publish")').first
        publish_button.wait_for(state="visible", timeout=10000)
        publish_button.click()
        print("🟡 Clicked Publish button...")
        
    except Exception as e:
        print("❌ Could not publish post:", e)
    

    # Confirm Publish
    
    with open("keystrokes_2.json", "r") as f:
        keystrokes = json.load(f)
 
    print("Replaying keystrokes in 3 seconds...")
    time.sleep(3)

    for key in keystrokes:
        if key.startswith("Key."):
            # Convert string back to actual Key
            try:
                k = getattr(Key, key.split(".")[1])
                keyboard_controller.press(k)
                keyboard_controller.release(k)
            except AttributeError:
                pass  # Unknown key
        else:
            keyboard_controller.press(key)
            keyboard_controller.release(key)
        time.sleep(0.05)  # slight delay to mimic real typing

    print("✅ Confirmed Publish.")

    time.sleep(3)
    browser.close()

The program follows this flow:
  • It first logs onto my Blogger page through the associated Google account. 
  • Next it navigates to the Blogger Editor by simulating the pressing of the New Post button.
  • Once the Editor screen is open, the Keylogger playback navigates the editor by first setting the editor to html mode, and tabbing over to the edit screen. 
  • The html code that has been created in the ACARS_to_htm program is pasted to the Editor
  • The post is then published 

Putting It All Together


Now that I've got a full end to end process to collect, filter and post the ACARS messages, the last step is to put the whole thing together as a consolidated script that will run everything at once.  

To do this I created a script file called Create_post.sh on my ACARS computer:

# Run file_rename.py
python3 file_rename.py

# Check if the first script succeeded
if [ $? -ne 0 ]; then
    echo "file_rename.py failed. Aborting."
    exit 1
fi
# Run ACARS_to_html.py
python3 ACARS_to_html.py
# Check if the second script succeeded
if [ $? -ne 0 ]; then
    echo "ACARS_to_html.py failed."
    exit 1
fi
# Run ACARS_to_html.py
python3 Post_Blog.py
# Check if the second script succeeded
if [ $? -ne 0 ]; then
    echo "Post_Blog.py failed."
    exit 1
fi

echo "All scripts executed successfully."

To finish things up I then set up a crontab schedule to run this script at midnight every day. 

So far it's been running like clockwork everyday, and I love really being able to pop in anytime to check out the activity. 

So that completes the ACARS project. I'll admit that there was probably a bit more software with this one. I promise that the next project will be a bit more hands on! 

Thursday, May 22, 2025

Reading ACARS Messages



Continuing on from last month's post, now that I've set up my Linux computer and my Software Defined Radio to monitor ACARS frequencies and send the messages received every day to a log file that I can read and maybe do something a little more interesting with it.


When reviewing the daily log files, I can see that there can be literally hundreds of messages being received during a tpical 24 hour period, which is hardly surprising considering I am fairly close to a  large international airport. 

One immediate draw back is the shear volume of rather cryptic messages that I needed to wade through. 
  

A lot of it is really pretty mundane stuff like position reports, clearance approvals and system status updates with the odd free text messages.

What I would like to do is to come up with some way to filter through a day's worth of messages, filter out only the messages I care about, and post it up to a place where I can check in to see what\s been happening during the day, regardless where I may in the world - meaning I wanted to have the messages posted someplace where I can look at them online.  

To do this I initially looked at maybe posting things up to a social media site like X (i.e. Twitter). The issue with that idea is that I would need to hook into them with an API connection, which can be a bit of a hassle to set up, and depending on the service involved, may also require a paid subscription. After some playing around I determined that the best option was to set up a pretty simple blog site, which wuld allow me to use a non-API based interface, with the added bonus of being able to fully customize the look and feel of the messages.

I also didn't want to post up in "real time" just in case there may be some sort of security concerns that I might not be aware of. Based on that concern I wanted to delay any posts until at least 2 days had past since the transmission. 

So, with those ideas in mind, I needed to come up with an automated process that would:
  1. Look for the log file that my SDR process created 2 days ago
  2. Look at the file selected and extract only the messages I care about
  3. Create html code to format the messages in a format that makes them easy to read
  4. Post the html code up to a blog page.  
Since there is a fair bit of  logic needed here to make this all happen, I needed some sort of programmed solution here. In short I needed to write some code. 

Recently, I've started using Python as part of my day to day work. While I am nowhere near what I would consider proficient in it, I know enough to fully appreciate what it is able to do and it seemed to be the perfect tool to do what I want to do. 

To make it happen, I needed to break the process into steps.



Step 1 - Identifying and Selecting The Correct Message File


The first step in the process is to look for a Daily log file that was created by ACARSDEC two days ago for converting it into a temporary file that can be used for further processing. 

The basic logic for this is to look at the date tag in the Daily log file name. ACARSDEC creates the daily log files as a "Daily_YYYYMMDD.log" naming convention. For pulling the needed file, I needed my program to do the following:

  • Scan the directory that contains my log files and look for a file that has the date labelled in it's name that equals to 2 days previous than today's date. 
  • Once the file is found, create a copy of that file called "Daily.log" that we will use for further processing. 
In Python code, it looks like this:

import os
import shutil
from datetime import datetime, timedelta

def copy_log_file_if_two_days_old():
    # Get the date two days ago
    two_days_ago = datetime.now() - timedelta(days=2)
    date_str = two_days_ago.strftime('%Y%m%d')
    
    # Construct the expected filename
    source_filename = f"Daily_{date_str}.log"
    
    # Check if the file exists in the current directory
    if os.path.exists(source_filename):
        # Copy the file to 'daily.log'
        shutil.copyfile(source_filename, "Daily.log")
        print(f"Copied '{source_filename}' to 'Daily.log'.")
    else:
        print(f"File '{source_filename}' does not exist.")

if __name__ == "__main__":
    copy_log_file_if_two_days_old()


With the execution of this program, I now had a working copy of the raw ACARS data from 2 days ago. What I needed to do now was to filter out the data that I didn't want to look at and create an html file that I then see the data that I was interested in a fairly easy to read format



Step 2 - Extracting The Information and Building the Webpage


Now comes the heavy lifting.

As I mentioned at the start of this post, there is a really large volume of data that's being transmitted on a daily basis. While all interesting stuff in of itself, I was really interested in looking at any messages that were likely to be human generated. 

To figure out what sorts of messages I wanted to look at, I first captured several days worth of transmissions and tried to find some common message labels that were most likely to have been maually created. 

From my analysis I determined that the following message labels were my best candidates:
  • 84 - labelled as "S.A.S. Free Text Message"
  • 87 - labelled as "Airline Defined" - Likely Air Canada based on the aircraft tail numbers
  • 85 - labelled as "Airline Defined" - Likely Air Canada based on the aircraft tail numbers
  • 5Z (with the words "FRM ENTRY " or "DISP MSG" in the message text.  - labelled as "Airline designated downlink" - Primarily United Airlines based on the aircraft tail numbers
Once I had defined the messages I wanted to look at, it should be fairly straightforward to parse the file to pull out the required messages. 

In order to increase the readability of the message, I decided that I really only wanted to see:
  • Message Label
  • Message Number
  • Tail Number
  • Message Text 
Once I had figured out what I wanted to see and how I wanted to see it, the final step was to put the information in a format that would make it readable as a webpage - in preparation for eventually posting it to a website - which meant converting the data to html code.  

While it looks like there's a lot going, it actually translated into a fairly compact Python program where the program reads in the Daily.log file that I crreated in the last program, pulls out the records that I mentioned above and threw some html code around it before spitting it all out as a html file. 

As a result I ended up with a Python program that looked like this:

import json
from datetime import datetime, timedelta

phrase = "FRM ENTRY"
phrase2 = "DISP MSG"

# Get the date two days ago
two_days_ago = datetime.now() - timedelta(days=2)
date_str = two_days_ago.strftime("%B %d, %Y")

def read_json_records(filename, fields):
    records = []
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            try:
                record = json.loads(line.strip())
                if (
                    "label" in record and
                    (
                        record["label"] in ["84", "87", "85"] or
                        (record["label"] == '5Z' and phrase in record["text"]) or
                        (record["label"] == '5Z' and phrase2 in record["text"])
                    )
                ):
                    records.append({field: record.get(field, "") for field in fields})
            except json.JSONDecodeError:
                continue
    return records

def records_to_html(records, fields):
    html = f"<html><head><meta charset='UTF-8'><title>Filtered Records</title></head><body>"
    html += f"<h1>ACARS Messages {date_str}</h1>"
    for record in records:
        html += "<div style='margin-bottom: 20px; padding: 10px; border-bottom: 1px solid #ccc;'>"
        for field in fields:
            html += f"<p><strong>{field}:</strong> {record.get(field, '')}</p>"
        html += "</div>"
    html += "</body></html>"
    return html

# Example usage
if __name__ == "__main__":
    filename = 'Daily.log'
    fields = ["label", "msgno", "tail", "text"]
    json_records = read_json_records(filename, fields)

    # Save to HTML
    html_output = records_to_html(json_records, fields)
    with open("blog_ready.html", "w", encoding="utf-8") as f:
        f.write(html_output)

    print("HTML file created: blog_ready.html")

Executing the program gives me something that looks like this:


Now I am able to convert the huge mass of cryptic messages from an airplanes ACARS terminal into something that is pretty easy to understand. 

The final step in the process is to now post the day's activity up to a website that I can view anytime I want.

As I mentioned at the start of this post, I wanted to post this up to a blog page, without the need to utilize any API connections. That proved to be more of a complex process than I had expected. So because of that, I think this may be a good time to wrap it up for this month and describe how I sorted that out in my next post.

Stay tuned next month for the finale of this project. 
 

Tuesday, April 15, 2025

Receiving ACARS Signals

 

ACARS Receiver

Last month I talked a little bit about the secret world of aviation communication that involved radio signals coming from the little screen that you typically find in the middle of the cockpit of commercial aircraft. 

Now that I learned a bit about what comes out of that box, it was only natural that I should try to listen in on the transmissions that are coming from the aircraft flying nearby.  

As I also mentioned last month, I had a few Software Defined Radio (SDR) dongles sitting around that I really wanted to find a use for.

For those who may not be familiar with them, a Software Defined Radio (SDR) is a radio communication system where the components that you would have traditionally equated to physical electronic circuits and interfaces  (like mixers, filters, amplifiers, modulators/demodulators, detectors, dials, switches, etc.) are instead implemented by software on a computer. 

The big thing is that traditional radios are usually built for a specific purpose with fixed hardware tuned for specific frequencies and modulation types - which is why for example, you can't listen to cell phone conversation through your bedroom clock radio. SDRs on the other hand, digitize the signal as early as possible (usually right after the antenna), and then do all the signal processing through software.

As a result, your typical SDR looks like a very small brick that plugs into the side of your computer

Software Defined Radio
Software Defined Radio



The beauty of this arrangement is that this really opens up what you can listen to over the airwaves, your only restriction is purely based on the software that you are running on your computer. 

A while back I "inherited" a Chromebook computer. While I have nothing personally against the Chrome operating system in of itself, I found that the amount of SDR tools for Chrome a bit lacking. 

Since the Chromebook was fairly new and had some pretty powerful specs, I was wondering about perhaps installing an operating system that had a bit more of a software selection available. Since installing a version of windows looked very much like a non-starter, it wasn't until I came across the  MrChromebox.tech website where I found out that I could install Linux on my Chromebook with relatively little effort. Walking through how I did the install will probably be covered in a future post, but for now, I successfully installed a copy of MX Linux on the Chromebook and I was now ready for setting up the radio,  

Once I had my computer all set up and ready to roll, my next area of focus was to figure out what sort of software I want to use to pull in the ACARS transmissions. 

The beauty of  Linux is that there is a large library of open source software out there, a lot of which is supported by a very active community of enthusiasts. 

So it wasn't much a surprise to find a decent number of applications out there that could work with my SDR to snoop on aircraft. 

With all the choices available, I had to winnow down the list to one that had the following criteria that I was looking for:

  • Can run as a command line script (the idea here is that I could schedule my Chromebook to automatically start scanning as soon as it's booted up). 
  • Easily customizable to scan for certain ACARS message labels (this way I can only pull in the messages that I am only interested in) 
  • Can export the results of the scan out to a text file, which I can use to feed in another application for further processing   

After reviewing the options, I settled on ACARSDEC which was written by Thierry Leconte and seems to have a fairly robust support community behind it. 

This application looks to be very powerful, with the ability to monitor and report on multiple SDR's at once (a bit overkill for me, but good to know that I have lots of room for future expansion), and of course more importantly, it ticked all the boxes on what I was looking for, 

The bulk of the information out there for installing ACARSDEC seems to be primarily tailored for installation on a Raspberry Pi. That would certainly make sense since I would fully expect this to be the main platform for doing this sort of scanning, however, since I had a bit of a different set up so I found that the published instructions on installing ACARSDEC weren't working very well on my MX Live installation. 

After some playing around, I ended up with a process that worked for my particular set-up.  For my particular case I used RTL_SDR as the background SDR tool (ACARSDEC can also support Airspy and SDRPlay too) 

To install ACARSDEC on my Chromebook, I opened up a terminal session and entered the following commands:

git clone GitHub - TLeconte/acarsdec 

cd arsdec

mkdir build

cd build

cmake .. -Drtl=ON

make

sudo make install

With that done, I plugged in my SDR dongle into the Chromebook and did a quick test of the dongle by executing the RTL_TEST function at the terminal command line. Once I got the message back that the dongle was on line and receiving, I was ready to listen to airplanes.  

The next step was to craft the command that I needed to execute in order to gather the specific messages that I wanted to capture.

To do this I needed to find what the most active VHF frequencies are in my area. Luckily I am fairly close to a few airport, with the closest major one being Pearson airport near Toronto, so I should have a healthy amount of traffic to listen too. 

To determine what frequencies to use, I plugged in the known ACARS frequencies (which are pretty easy to find online) into a radio scanner that had a nice feature of counting the number of transmissions that happen on a frequency. After running the scanner for 24 hours I got a very good sense what ACARS frequencies were the most active.

To do this I had to run the ACARSDEC command at the terminal window in MX Linux. 

In a broad term - ACARSDEC is activated using this command structure:

acarsdec  [-o lv] [-t time] [-A] [-b filter ] [-e] [-n|N|j ipaddr:port] [-i stationid] [-l logfile [-H|-D]] -r rtldevicenumber  f1 [f2] [... fN] | -s f1 [f2] [... fN]

Granted this looks pretty cryptic, but these are the mainly used to define the options you want to use so that that does give you a bit of an idea that is within the application. 

For my initial test, I wanted to focus on what I could actually hear on the frequencies that I've identified from my scanner as the most active. I wanted to store what was picked up in a Daily Log file in a JSON file format. 

Basically I need to set the appropriate parameter value for:

  • Setting my output to the JSON format (-o)
  • I also don't really care about empty messages (-e)  
  • I want to output the data to a file called Daily.log
  • I also want to create a new log file daily (-D) 
  • I also need to define the where to find my SDR to scan (-r)
  • Once I know what particular messages I wanted to gather, I can define them with a filter list (-b) 
  • And I can also list the frequencies that I've found the most activity to scan
With that I entered the following command to start monitoring:

acarsdec -o 4 -e -l Daily.log -D -r 0 130.450 131.550 131.725 130.025 131.125

Since I wanted to get all of the messages at the moment, I didn't define any filters. 

After executing the command, ACARSDEC started to monitor away with no issues and I was able to collect a significant amount of aircraft data. 

Sample Output

There was one small quirk that I did uncover which usually manifested itself after the application had been running a while. Every once in a while I found that the application stopped recognizing the SDR and errored out the process. 

I wasn't able to really get to the bottom of the problem, however I found that by adding the ACASDEC command as part of the start up processes when the Chromebook reboots along with a scheduled task on the Chromebook to have it reboot every hour seemed to be a pretty good workaround. 

Now that I have establish a way to monitor and log all the activity in my area, the next step is to collect some data for a while and take a look at what kind of interesting stuff could I use for my next phase of the project.  

Tuesday, March 11, 2025

"CARS In The Skies" - A Crash Course on ACARS

                  ACARS Primer

If you've been following along for a while, you may have noticed that I have some various strange combinations of interests, of which one seem to involve things that deal with radios and things that fly. 

On that subject, I've made posts about listening in on things like weather balloons, gliders and commercial aircraft. It's pretty amazing what sorts of rabbit holes you could go down when you are talking about all the various ways that stuff in the air communicates with stuff on the ground. 

Over Christmas, I came across a couple of SDR dongles in a desk drawer while doing a bit of a reorganization. 

Those who don't know, SDR (or Software Defined Radio) are neat little devices that are small radio receivers that can be plugged into the USB port of a computer. While by itself the SDR is just a small brick that doesn't do a lot by itself, but when plugged into a computer that's loaded up with the appropriate software that controls the SDR, you can literally receive any radio signal that's out there.  


Software Defined Radio
SDR Dongle

So, having found my lost stash of SDR dongles, I started to think of what sort of project I could do - after all there's no sense in just keeping them in the desk drawer. 

I wanted to do something that was a bit unique as a project, something that would take more advantage of the processing power of the computer to decipher what was coming down the wire. So obviously I wanted to focus on something was a bit obscure. 

After some snooping around the web,  I came across this image

Flight Management Computer
Flight Management Computer

If you ever seen any photos of the flight deck of any commercial airliner, you may see this little screen sitting between the pilots. This is the Flight Management Computer and is typically used as a navigation database. This database is used to store route information which the autopilot in turn will use to fly the aircraft to the proper destination. It also reads in data from the aircraft's systems to give a gross weight and determine the best speeds for climb, cruise, descent, holding, approach, etc.  It will also compute the aircrafts position based upon inputs from the IRSs, GPS and radio position updating. On top of all that, this little box also monitors the aircrafts systems to keep any eye out for any potential issues. 

So there is a lot going on with this little gizmo. However, the most important thing, at least from my perspective, is that the FMC also transmits data about what's going on with the airplane down to ground stations through either VHF or HF radio frequencies using the ACARS transmission standard

ACARS, which stands for Aircraft Communications Addressing and Reporting System, is a digital data link standard that allows aircraft to communicate with ground stations, enabling the exchange of information for operational efficiency and flight safety. 

The type of stuff that is communicated through ACARS include:

  • Air Traffic Control (ATC) requests for clearances and instructions from air traffic controllers. 
  • Communication between an aircraft and its base for operational matters. 
  • Communication between an aircraft and its base for administrative purposes. 
  • Weather reporting 
  • Transmit aircraft health and maintenance information. 
As you can see, there is a lot of stuff that could go into an ACARS transmission. Since I am relatively close to a number of large airports, I figure that I should be able to listen in some action. 

But first I needed to understand what exactly was being transmitted.

From what I can gather, ACARS transmit in a standardized message format that typically, among some other details, contains the following information in the message:
  • Date and Time
  • Label - which indicates the nature of the message 
  • Aircraft Registration Number
  • Flight Number
  • Message details 

So for example, a typical ACARS message could look something like this:

[#4 (F:130.025 L:-34.5 E:1) 18/02/2025 00:03:54.852 --------------------------------
Mode : 2 Label : H1 Id : 0 Nak
Aircraft reg: N27722 Flight id: UA0630
No: D26K
#DFB9 94.1316CLMBAIR -26.3  -9.50.584 94.59 94.0621CLMBAIR -26.8  -9.
80.584 94.59 94.0926CLMBAIR -27.0 -10.30.585 94.59 94.1331CLMBAIR -27
.5 -10.50.590 94.59 94.0936CLMBAIR -28.0 -10.50.597 94.59 94.13

Obviously it's rather cryptic, but sometimes you do see some stuff in plain text, usually of a pretty mundane nature:

 [#3 (F:131.725 L:-19.1 E:0) 17/02/2025 01:58:21.571 --------------------------------
Mode : 2 Label : 84 Id : 1 Nak
Aircraft reg: C-GDMP Flight id: WS1224
No: M24A
HI
ITS LOOKING CLOSE FOR MI
N REST FOR MY DH TMW.
ANY CHANCE OF CHANGING T
O SHERATON TO IMPROVE MY
TURN AROUND TIME.

The key to figuring out the meaning of the messages can be found in the Label value. It took a bit of digging around but I did manage to source out a list of what those labels meant - which once you know what the labels meant, you are able to roughly decipher the contents of the message.

Since I needed to spend a fair amount of time doing the research on what those labels actually meant, I figured I would be helpful and provide the definitions that I uncovered for the ACARS Message labels for anyone that's interested (a bit of a warning, it's a pretty long list!):

 Label         Message Type
    00 Emergency SitRep, Hijack Situation
    10 British Air ETA, American Airlines 
    11 Inrange Arrival Report
    13 CMU loadsheet uplink
    14 General Aviation Free Text
    15 General Aviation Position Report
    16 General Aviation Weather Request
    17 Position/Weather Report
    18 General aviation weather report
    19 ATIS report
    1C Flight plan request
    1L QF OFF message
    1M Estimated Time of Arrival report
    20 SAS Initialization
    21 SAS Takeoff Data calculation
    23 SAS Loadsheet Uplink
    24 Northwest Airlines Position/Weather
    25 SAS Special Load Notification
    26 SAS SIGMET message
    27 SA Weather Request
    28 Weather Request
    29 SAS Flow Message
    2S Weather Request
    2U Weather
    30 UPS Position Report
    31 Northwest Airlines Weather Report
    36 Northwest Airlines In Range Message
    39 Northwest Airlines free text message
    3F UPS ETA Downlink Message
    3G UPS Free Text Message Format
    3J UPS Downlink Message
    3S UPS Downlink Message
    3U UPS Uplink acknowledgement
    41 SAS Flow Message
    46 Pirep
    47 747-400 Airway Position Downlink
    48 Misc Msgs
    49 Air Canada Status Reports
    4A Air Canada latest new format
    4M Cargo Info
    4P Weather report/forecast
    4Q Air Canada departure format
    4R Air Canada-Specific Message
    4S Weather Request
    4T ETA Report
    4X Air Canada Frequency List
    50 Possible HFDL Msg router
    51 Ground GMT Request/Update
    52 Ground UTC Request/Update
    53 Reserved Spare
    54 Aircrew Voice Contact Request
    57 Alternate Aircrew Position Report
    58 Aircraft Tracking Position Report
    5D ATIS Request
    5P Temp Suspension of ACARS Service 
    5R Aircraft initiated position report
    5U Downlink Weather Request
    5V VDL Switch Advisory
    5Y ETA Revision, Diversion
    5Z Airline designated downlink
    7A Takeoff Thrust Report
    7B Misc Aircrew Initiated Msgs
    80 Position report format, Airline Defined
    81 Airline Defined
    82 Airline Defined
    83 Airline Defined
    84 S.A.S. Free Text Message
    85 Airline Defined
    86 Airline Defined
    87 Airline Defined
    88 Airline Defined
    89 Airline Defined
    8A SAS Out report
    8B SAS Off report
    8C SAS On report
    8D SAS In report
    8E SAS Out/return in report
    8G SAS Take off data calculation request
    8H SAS Loadsheet request/confirmation
    8I SAS Flightplan request
    8J SAS Crewlist request
    8X Uplink ATIS Information
    8Z Avionics unable to process data
    99 Emergency Situation Report
    A0 ATIS Facilities Notification
    A1 Deliver Oceanic Clearance
    A2 Unassigned
    A3 Deliver Departure Clearance
    A4 Acknowledge Departure Clearance
    A5 Request Position Report
    A6 Request ADS Report
    A7 Forward Free Text to Aircraft
    A8 Deliver Departure Slot
    A9 Deliver ATIS Information
    AA ATC Communications
    AB Weather Information for Pilots
    AC Pushback Clearance Uplink
    AD Expected Taxi Clearance
    AE Unassigned
    AF CPC Command Response
    AG Unassigned
    AW ATS Wind Data
    B0 ATS Facility Notification
    B1 Oceanic Clearance Request
    B2 Oceanic Clearance Readback
    B3 Request Departure Clearance
    B4 Acknowledge Departure Clearance
    B5 Provide Position Report
    B6 Provide ADS Report
    B7 Forward Free Text to ADS
    B8 Request Departure Slot
    B9 Request ATIS information
    BA ATC Communications
    BB Weather Information for Pilots
    BC Pushback Clearance Request
    BD Expected Taxi Clearance Request
    BE CPC Log-on/Log-off Request
    BF CPC Wilco
    BG Unassigned
    BW ATS Wind Response
    C# Message to Cockpit Printer #
    C0 Uplink message to all printers
    C1 Uplink message to printer 1
    C2 Uplink message to printer 2
    C3 Uplink message to printer 3
    CA Printer Error
    CB Printer Busy
    CC Printer Local, Printer in Test Mode
    CD Printer out of Paper, Printer No Paper
    CE Printer Buffer Overrun
    CF Printer Reserved Status
    DL Reserved for Data Loading
    D  De-Icing Downlinks
    DI De-Icing Uplinks
    EI Internet Email Message
    E1 Internet Email Message
    E2 Internet
    EL Engine Monitoring Unit Message
    ER Engine Monitoring Unit Message
    F3 Dedicated Transceiver Advisory
    H1 Messages from Avionic System
    H2 Meteorological Report
    H3 Icing Report
    H4 Meteorological Report Config
    HF HF Report
    HX Undelivered Uplink report
    KB Loopback Request/Response
    LB Cabin E-Logbook
    LC Cabin E-Logbook
    LS Technical (Cockpit) E-Logbook
    LT Technical (Cockpit) E-Logbook
    M1 IATA Departure Msg
    M2 IATA Arrival Msg
    M3 IATA Return to Ramp Message
    M4 IATA Return from Airborne Message
    P0 AMS-Protected Downlink Message
    P6 Unassigned
    P7 ATS Message
    P8 AMS-Specific Message
    P9 AOC Message
    PA ATS Message
    PB AMS-Specific Message
    PC AOC Message
    Q0 ACARS Link Test
    Q1 ETA Departure/Arrival Report
    Q2 ETA Report
    Q3 GMT Clock Update
    Q4 Voice Circuit Busy - Reply to 54
    Q5 Unable to process messages
    Q6 Voice-to-ACARS change over
    Q7 Delay message
    Q8 Reserved
    Q9 Reserved
    QA Out/fuel report, Departure Report
    QB Off report
    QC On Report
    QD Arrival Report / In Fuel
    QE Out/fuel destination report
    QF OFF (Off/destination report) / Fuel
    QG Returning to Gate Report
    QH Out Report
    QK Landing report
    QL Arrival Report
    QM Arrival Info Report
    QN Diversion Report
    QP OUT report
    QQ OFF report
    QR ON report
    QS IN report
    QT OUT/Return IN report
    QU Unassigned
    QV Autotune Reject
    QW Unassigned
    QX Airborne system unable to process
    QY Unassigned
    QZ Unassigned
    RA Command Acft terminal to TX data
    RB Response of aircraft terminal to RA
    RE Refuel
    S1 VHF Network Statistics Report
    S2 VHF Performance Report
    S3 LRU Configuration Report
    SA Media Advisory
    SQ Uplink Squitter
    UP Message Acknowledgement
    WP Potable Water Remote Pre-selection
    _D General Response
    _J No info to TX
 

Whew!

So, armed with this knowledge, looking at the 2 messages I used as an example:
  • I see that the first message has a label of "H1" which means that it's a message from the aircraft's avionic subsystem - likely a status report on aircraft position and activity - by looking at the message contents I can make out GPS co-ordinates and altitude. 
  • The second message, the one with the plain text, had a label of "84" - which indicated that it was a SAS Free Text Message - which makes sense when you look at the message contents
Now that I am armed with this new found knowledge, my next step is to step up my SDR dongle and computer to monitor ACARS frequencies and to capture the messages that are received. 

My ultimate plan is to only parse out the free text messages, since they seem to provide a bit of a behind the scenes peek of the working lives of aircrew, and probably far more interesting than position reports. 

Once I've got a process sorted out on how to capture these messages, I would like to perhaps post them as a live feed somewhere. Now since I believe there are some rules out there that prohibit posting "live" transmissions, I would need to create something that won't post until at least 48 hours after the transmission. 

In my next post I'll work on getting my SDR up and look at starting to gather my messages.