Friday 16 February 2024

ADS-B and Tracking Flights

 After 2 and a bit years working for Optus Sport constructing a CDN ( Content Delivery Network ) my contract finished up. With the Australian job market slowing down for Christmas I took the opportunity to learn some new skills and what better way than a project I always wanted to do 😊

For many years I had been watching aircraft using a software called Dump1090 to receive ADS-B transponder data. This was OK to start with as it had a very simplistic web interface that overlays the location and direction for the aircraft. 

But I always found it lacking, no track data. I wanted more and so it was time to go on a bit of a software adventure.

Now I am no software dev ( yes I still say that even thought I write more code than most, just ask some of my old managers ).

The Radio

First was to setup an old laptop with the latest version of dump1090 and as I don't like installing lots of things I went with setting up a docker container.

FROM rockylinux:9 as builder
RUN sed 's/https/http/g' -i /etc/yum.repos.d/* \
  && dnf install epel-release -y \
  && sed 's/https/http/g' -i /etc/yum.repos.d/* \
  && dnf install rtl-sdr-devel git make gcc -y \
  && git clone https://github.com/antirez/dump1090.git \
  && cd dump1090  \
  && make

FROM rockylinux:9
COPY --from=builder /dump1090 /dump1090
RUN sed 's/https/http/g' -i /etc/yum.repos.d/* \
  && dnf install epel-release -y \
  && sed 's/https/http/g' -i /etc/yum.repos.d/* \
  && dnf install rtl-sdr -y
EXPOSE 8081 30003
WORKDIR /dump1090   

You can see I like using multi stage container builds so that I can get consistent builds and make them a lot smaller 

This image was then pushed up to docker hub as  dump1090
When this is installed with a volume to /dev/bus/usb it will have the ability to receive the ADS-B signals

Database connector

Next on the list was how to store the data...
I settled on a Postgres database backend and a single table as Dump1090 output a format called "Base Station Mode". I this mode all the data is formatted in a comma separated string with 22 fields. Nice and easy for a little golang container ( code ) ( container )

API's

This good but has a major down side, the second field is the message type and not all fields have values  on every row. This make it so that to the only way to find a flight callsign for a location is to cross reference the hex_ident (ICAO id)  from message where the message type is 3 and include the  timestamp , the match that to message type 1 from the same table.... this all gets messy after a while

After working all this out I decided to move all the the data joins and as much as possible everything I could to database views. I mean the DB is good at data manipulations and it can cache better, sort better etc. So why not just use it.
This has lead to a bit of explosion in the views I went thru and tested different things, but its fast with over 11G of data in 5.3 million rows

Now to the real API's, there are on 3 and that could be shrunk more.
  1. api/adsb/position returns a list of aircraft seen in the last 10 seconds with latitude, longitude and altitude and is used for both the marker icon on the map and the table data for flights

  2. api/adsb/points/{flight}/{hex_ident} returns all the locations the aircraft was seen at over the last 2 hours used for displaying the track when an aircraft is selected

  3. api/adsb/track/{hex_ident} returns the current track of the aircraft use to set the rotation of the aircraft marker icon

Web UI

This was the most interesting to me as I am not a visual person... I just don't understand how to make things look nice but I can make it functional ( I hope )
I the past I have used VueJS and it just kinda clicked for me and this was when I was working with a ReactJS dev team, still don't understand React
Since my last adventure in UI, VueJS has moved onto Vue3 but this gave me no end of issues with the leaflet component and making it reactive to change. So I went back to old faithful v2 and it all started to work.
Getting aircraft markers to show up was easy thanks to the api call that just gave them to me. Then the table was also easy. Making it auto-resize based on the number of rows, not to difficult until I found that window resize broke as I had made a static size for the rows. Easy fix with with an iteration over
const row = this.$el.querySelectorAll("tr")
This gave me a way to find the height of each row and make the table dynamic

Next was to make a way to select an aircraft and show its track, bonus point for highlighting the row in the table.

The first was easy, just call the api based on the aircraft call-sign and hex_ident. Then realise that I need to remove the layer when the flight disappeared. 
Turns out the easiest way is to have the api return null if there are no point newer than 10 seconds, make for interesting watching when the flight is in a bad reception area and it keeps "flashing" but I can live with that

Next the row highlight. Initially I was was finding the array index from the list of flights, but this was only looked up when the track was toggled. As flight moved in the table they would be highlighted even tho they were not the one show its track
So next thought, lets use call-sign, sounds  good right?  The problem is that some flight transmit different call-signs as they are legs for differing flights, this a flight get a connection to another flight etc. This lead the table and the flight you select to "disappear" as its call-sign was unseen for a while
To fix this I changed over hex_idents as they are stable


Here is the result https://adsb.globelock.org/



(code) (container)

Where to from here

Just before I started this adventure I was working a GPS via LoRa ( not LoRaWAN ) and I was finding that the packet size was just killing the radio
My plan is to create a ADS-B transponder that will sent raw packets via LoRa to a base station. This should help as ADS-B is already a very compact communication protocol

No comments:

Post a Comment