• The cover of the 'Perl Hacks' book
  • The cover of the 'Beginning Perl' book
  • An image of Curtis Poe, holding some electronic equipment in front of his face.

Tracking Elon Musk's Plane with Perl

minute read



Find me on ... Tags

You may recall that one Jack Sweeney created ElonJet , a service to track Elon Musk’s jet in real time. This led to his account being suspended by Twitter, but you can find him on Mastodon .

He has a public repository for tracking planes , but it’s written in Python. I wanted to do the same for Perl, so I did. I wrote WebService::OpenSky (also available on GitHub ), which is a Perl wrapper around the OpenSky Network API . From OpenSky Network’s home page :

The OpenSky Network is a non-profit community-based receiver network which has been continuously collecting air traffic surveillance data since 2013. Unlike other networks, OpenSky keeps the complete unfiltered raw data and makes it accessible to academic and institutional researchers. With over 30 trillion ADS-B, Mode S, TCAS and FLARM messages collected from more than 5000 sensors around the world, the OpenSky Network exhibits the largest air traffic surveillance dataset of its kind. The mission of our non-profit association is to support open global air traffic research by universities and other not-for-profit institutions. You can find more information on our blog .

I won’t go into great detail about the API, but it’s pretty simple. You can read the docs to understand more, but here’s a quick example:

use WebService::OpenSky;

my $musks_jet = 'a835af';
my $openapi   = WebService::OpenSky->new;

my $days = shift @ARGV || 7;
my $now  = time;
my $then = $now - 86400 * $days;

my $flight_data = $openapi
  ->get_flights_by_aircraft( $musks_jet, $then, $now );
say "Jet $musks_jet has "
  . $flight_data->count . " flights";

As of this writing, that prints out: Jet a835af has 3 flights

The $flight_data object allows you to iterate over the flights, but it’s pretty limited. I wanted more. I wanted something like this:

Flight #1. Departed Monday, 29 May, 10:42 AM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #1. Arrived Monday, 29 May, 04:34 PM UTC
    at Ted Stevens Anchorage International Airport, Anchorage, Alaska, US

Flight #2. Departed Thursday, 01 Jun, 04:32 AM UTC
    from unknown airport, unknown city
Flight #2. Arrived Thursday, 01 Jun, 04:45 PM UTC
    at Austin-Bergstrom International Airport, Austin, Texas, US

Flight #3. Departed Thursday, 01 Jun, 04:32 AM UTC
    from unknown airport, unknown city
Flight #3. Arrived Thursday, 01 Jun, 04:45 PM UTC
    at unknown airport, unknown city

As you can see, the data itself is spotty, but the older the data, the more accurate it appears to be. So how did we get there?

First, we need to get the ICAO 24-bit hexadecimal code for Musk’s private jet. It turns out he has three of them, but I’m only tracking the one he’s using the most. A quick search for “icao 24 Elon Musk’s jet” revealed this Hacker News discussion of it . Various other sources confirmed this, with the N628TS registrations having the icao number of a835af.

Next, we need to get the flight data. The OpenSky API allows you to get flight data and that’s shown in the script above. However, it’s not very useful. I wanted to see the actual airport names and times (the times returned by the API are Unix epochs ).

Getting the ICAO codes for the airports was a bit harder. Since I’m doing this open source, I eventually tracked down a fairly active GitHub repository with airport data . I wanted to use the Geo::ICAO module to convert the ICAO data, but it’s out-of-date and missing a lot of airports. Thus, I wrote this code:

sub get_icao_data {
  my $url
    = 'https://raw.githubusercontent.com/mborsetti/airportsdata/main/airportsdata/airports.csv';
  my $ua = Mojo::UserAgent->new;
  return eval {
    my $response = $ua->get($url)->res;
    my %lookup;
    if ( $response->is_success ) {
      my ( $fh, $filename ) = tempfile();
      print {$fh} $response->body;
      close $fh;
      my $data = csv(
        in      => $filename,
        headers => "auto",
      );
      %lookup = map { $_->{icao} => $_ } @$data;
    }
    \%lookup;
  };
}

If any airports are missing from that (and it appears there may be some issues), I fall back to Geo::ICAO. The solution has been working well so far. Obviously, paid services are more accurate, but this is good enough for my needs.

I’ll skip explaining the rest of the code, but here’s a link to the full script . Running that as perl lights.pl --days 30 outputs:

Flight #1. Departed Sunday, 07 May, 08:39 PM UTC
    from Miami-Opa Locka Exec Airport, Miami, Florida, US
Flight #1. Arrived Sunday, 07 May, 11:01 PM UTC
    at Austin-Bergstrom International Airport, Austin, Texas, US

Flight #2. Departed Monday, 08 May, 06:40 PM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #2. Arrived Monday, 08 May, 07:13 PM UTC
    at Corpus Christi International Airport, Corpus Christi, Texas, US

Flight #3. Departed Monday, 08 May, 07:47 PM UTC
    from Corpus Christi International Airport, Corpus Christi, Texas, US
Flight #3. Arrived Monday, 08 May, 08:21 PM UTC
    at Austin-Bergstrom International Airport, Austin, Texas, US

Flight #4. Departed Monday, 08 May, 08:59 PM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #4. Arrived Monday, 08 May, 09:29 PM UTC
    at Corpus Christi International Airport, Corpus Christi, Texas, US

Flight #5. Departed Monday, 08 May, 09:55 PM UTC
    from Corpus Christi International Airport, Corpus Christi, Texas, US
Flight #5. Arrived Monday, 08 May, 10:10 PM UTC
    at Farm Services Inc Airport, Rio Hondo, Texas, US

Flight #6. Departed Tuesday, 09 May, 02:07 PM UTC
    from unknown airport, unknown city
Flight #6. Arrived Tuesday, 09 May, 05:19 PM UTC
    at Napa County Airport, Napa, California, US

Flight #7. Departed Tuesday, 09 May, 06:37 PM UTC
    from Napa County Airport, Napa, California, US
Flight #7. Arrived Tuesday, 09 May, 07:06 PM UTC
    at Norman Y Mineta San Jose International Airport, San Jose, California, US

Flight #8. Departed Friday, 12 May, 06:25 AM UTC
    from Norman Y Mineta San Jose International Airport, San Jose, California, US
Flight #8. Arrived Friday, 12 May, 07:15 AM UTC
    at Jack Northrop Field/Hawthorne Municipal Airport, Hawthorne, California, US

Flight #9. Departed Saturday, 13 May, 04:18 AM UTC
    from Jack Northrop Field/Hawthorne Municipal Airport, Hawthorne, California, US
Flight #9. Arrived Saturday, 13 May, 05:27 AM UTC
    at unknown airport, unknown city

Flight #10. Departed Sunday, 14 May, 10:27 PM UTC
    from unknown airport, unknown city
Flight #10. Arrived Monday, 15 May, 07:38 AM UTC
    at Paris-Le Bourget Airport, Paris, Ile-de-France, FR

Flight #11. Departed Monday, 15 May, 06:19 PM UTC
    from Paris-Le Bourget Airport, Paris, Ile-de-France, FR
Flight #11. Arrived Tuesday, 16 May, 03:48 AM UTC
    at Seidel Ranch Airport, Elroy, Texas, US

Flight #12. Departed Friday, 19 May, 01:10 PM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #12. Arrived Friday, 19 May, 03:59 PM UTC
    at Norman Y Mineta San Jose International Airport, San Jose, California, US

Flight #13. Departed Saturday, 20 May, 06:35 AM UTC
    from Norman Y Mineta San Jose International Airport, San Jose, California, US
Flight #13. Arrived Saturday, 20 May, 11:12 AM UTC
    at Rhode Island Tf Green International Airport, Providence, Rhode Island, US

Flight #14. Departed Saturday, 20 May, 07:54 PM UTC
    from unknown airport, unknown city
Flight #14. Arrived Saturday, 20 May, 11:17 PM UTC
    at Seidel Ranch Airport, Elroy, Texas, US

Flight #15. Departed Sunday, 21 May, 12:12 AM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #15. Arrived Sunday, 21 May, 02:45 AM UTC
    at Los Angeles International Airport, Los Angeles, California, US

Flight #16. Departed Sunday, 21 May, 06:03 PM UTC
    from Los Angeles International Airport, Los Angeles, California, US
Flight #16. Arrived Sunday, 21 May, 08:32 PM UTC
    at Seidel Ranch Airport, Elroy, Texas, US

Flight #17. Departed Tuesday, 23 May, 02:41 PM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #17. Arrived Tuesday, 23 May, 05:38 PM UTC
    at Norman Y Mineta San Jose International Airport, San Jose, California, US

Flight #18. Departed Thursday, 25 May, 03:40 PM UTC
    from Norman Y Mineta San Jose International Airport, San Jose, California, US
Flight #18. Arrived Thursday, 25 May, 04:27 PM UTC
    at Jack Northrop Field/Hawthorne Municipal Airport, Hawthorne, California, US

Flight #19. Departed Friday, 26 May, 01:08 AM UTC
    from Jack Northrop Field/Hawthorne Municipal Airport, Hawthorne, California, US
Flight #19. Arrived Friday, 26 May, 01:55 AM UTC
    at Norman Y Mineta San Jose International Airport, San Jose, California, US

Flight #20. Departed Friday, 26 May, 07:00 AM UTC
    from Norman Y Mineta San Jose International Airport, San Jose, California, US
Flight #20. Arrived Friday, 26 May, 09:37 AM UTC
    at Seidel Ranch Airport, Elroy, Texas, US

Flight #21. Departed Monday, 29 May, 10:42 AM UTC
    from Austin-Bergstrom International Airport, Austin, Texas, US
Flight #21. Arrived Monday, 29 May, 04:34 PM UTC
    at Ted Stevens Anchorage International Airport, Anchorage, Alaska, US

Flight #22. Departed Thursday, 01 Jun, 04:32 AM UTC
    from unknown airport, unknown city
Flight #22. Arrived Thursday, 01 Jun, 04:45 PM UTC
    at Austin-Bergstrom International Airport, Austin, Texas, US

Flight #23. Departed Thursday, 01 Jun, 04:32 AM UTC
    from unknown airport, unknown city
Flight #23. Arrived Thursday, 01 Jun, 04:45 PM UTC
    at unknown airport, unknown city

As you can see, he arrived in Paris on Monday, 15 May, 2023 at 07:38 AM UTC. He spent a few hours there before flying back to the Seidel Ranch airport in Texas (that was the first airport which let me know that Geo::ICAO data was incomplete). A quick search revealed he was in Paris to meet with French president Emmanuel Macron.

Have fun!

Please leave a comment below!

Full-size image


If you'd like top-notch consulting or training, email me and let's discuss how I can help you. Read my hire me page to learn more about my background.


Copyright © 2018-2024 by Curtis “Ovid” Poe.