Deploy "ultrafeeder"

The "ultrafeeder" container is the heart of our "adsb" application. It receives 1090MHz ADS-B ES signals from your SDR, and demodulates ADS-B messages, making them available for all other containers.

It also provides a website with a map based on tar1090, station statistics (graphs1090), mlat-client, and an mlat-hub to aggregate MLAT results.

In your favorite text editor, create a file named docker-compose.yml in your application directory (/opt/adsb if you've been following along verbatim).

nano docker-compose.yml
services:
  ultrafeeder:
  # ultrafeeder combines a number of functions:
  # - it retrieves and decodes 1090MHz Mode A/C/S data from the SDR(s) using Wiedehopf's branch of readsb
  # - it implements a `tar1090` based map on port 80 (mapped to port 8080 on the host)
  # - it includes graph1090 (system statistics website) on http://xxxxx/graphs1090
  # - it sends ADSB data directly (without the need of additional containers) to the
  #   "new" aggregators, and, if desired, also to ADSBExchange
  # - it includes mlat-client to send MLAT data to these aggregators
  # - it includes an MLAT Hub to consolidate MLAT results and make them available to the built-in map and other services

    image: ghcr.io/sdr-enthusiasts/docker-adsb-ultrafeeder
    container_name: ultrafeeder
    hostname: ultrafeeder
    restart: unless-stopped
    device_cgroup_rules:
      - 'c 189:* rwm'
    ports:
      - 8080:80               # to expose the web interface
    environment:
      # --------------------------------------------------
      # general parameters:
      - LOGLEVEL=error
      - TZ=${FEEDER_TZ}
      # --------------------------------------------------
      # SDR related parameters:
      - READSB_DEVICE_TYPE=rtlsdr
      - READSB_RTLSDR_DEVICE=${ADSB_SDR_SERIAL}
      - READSB_RTLSDR_PPM=${ADSB_SDR_PPM}
      #
      # --------------------------------------------------
      # readsb/decoder parameters:
      - READSB_LAT=${FEEDER_LAT}
      - READSB_LON=${FEEDER_LONG}
      - READSB_ALT=${FEEDER_ALT_M}m
      - READSB_RX_LOCATION_ACCURACY=2
      - READSB_STATS_RANGE=true
      #
      # --------------------------------------------------
      # Sources and Aggregator connections:
      # Note - remove the ones you are not using / feeding
      # Make sure that each line ends with a semicolon ";"
      # if you are not using dump978, feel free to remove the first line
      - ULTRAFEEDER_CONFIG=
          adsb,dump978,30978,uat_in;
          adsb,feed.adsb.fi,30004,beast_reduce_plus_out;
          mlat,feed.adsb.fi,31090;
          adsb,in.adsb.lol,30004,beast_reduce_plus_out;
          mlat,in.adsb.lol,31090;
          adsb,feed.airplanes.live,30004,beast_reduce_plus_out;
          mlat,feed.airplanes.live,31090;
          adsb,feed.planespotters.net,30004,beast_reduce_plus_out;
          mlat,mlat.planespotters.net,31090;
          adsb,feed.theairtraffic.com,30004,beast_reduce_plus_out;
          mlat,feed.theairtraffic.com,31090;
          adsb,data.avdelphi.com,24999,beast_reduce_plus_out;
          adsb,skyfeed.hpradar.com,30004,beast_reduce_plus_out;
          mlat,skyfeed.hpradar.com,31090;
          adsb,dati.flyitalyadsb.com,4905,beast_reduce_plus_out;
          mlat,dati.flyitalyadsb.com,30100;
          adsb,feed1.adsbexchange.com,30004,beast_reduce_plus_out;
          mlat,feed.adsbexchange.com,31090;
          mlathub,piaware,30105,beast_in;
          mlathub,rbfeeder,30105,beast_in;
          mlathub,radarvirtuel,30105,beast_in;
          mlathub,planewatch,30105,beast_in;
      # --------------------------------------------------
      - UUID=${ULTRAFEEDER_UUID}
      - MLAT_USER=${FEEDER_NAME}
      - READSB_FORWARD_MLAT_SBS=true
      #
      # --------------------------------------------------
      # TAR1090 (Map Web Page) parameters:
      - UPDATE_TAR1090=true
      - TAR1090_MESSAGERATEINTITLE=true
      - TAR1090_PAGETITLE=${FEEDER_NAME}
      - TAR1090_PLANECOUNTINTITLE=true
      - TAR1090_ENABLE_AC_DB=true
      - TAR1090_FLIGHTAWARELINKS=true
      - HEYWHATSTHAT_PANORAMA_ID=${FEEDER_HEYWHATSTHAT_ID}
      - HEYWHATSTHAT_ALTS=${FEEDER_HEYWHATSTHAT_ALTS}
      - TAR1090_SITESHOW=true
      - TAR1090_RANGE_OUTLINE_COLORED_BY_ALTITUDE=true
      - TAR1090_RANGE_OUTLINE_WIDTH=2.0
      - TAR1090_RANGERINGSDISTANCES=50,100,150,200
      - TAR1090_RANGERINGSCOLORS='#1A237E','#0D47A1','#42A5F5','#64B5F6'
      - TAR1090_USEROUTEAPI=true
      #
      # --------------------------------------------------
      # GRAPHS1090 (Decoder and System Status Web Page) parameters:
      - GRAPHS1090_DARKMODE=true
      #
      # --------------------------------------------------
    volumes:
      - /opt/adsb/ultrafeeder/globe_history:/var/globe_history
      - /opt/adsb/ultrafeeder/graphs1090:/var/lib/collectd
      - /proc/diskstats:/proc/diskstats:ro
      - /dev/bus/usb:/dev/bus/usb:rw
    tmpfs:
      - /run:exec,size=256M
      - /tmp:size=128M
      - /var/log:size=32M

In the file above, you will find several parameters that have values denoted as ${xxxx}. These values are read from a file in the same directory named .env that we created earlier. Alternatively, you can simply replace ${xxxx} with the value you want to use, for example READSB_RTLSDR_DEVICE=${ADSB_SDR_SERIAL} --> READSB_RTLSDR_DEVICE=0000001090.

The docker-compose.yml file above will:

  • Create a few mapped docker volumes to store historic message values (/var/globe_history), statistics for the graphs (/var/lib/collectd), and make the disk statistics (/proc/diskstats) and USB devices (/dev) available to the container.

  • Create a service named ultrafeeder that will run the ghcr.io/sdr-enthusiasts/docker-adsb-ultrafeeder container.

    • We're mapping TCP port 8080 through to the container so we can access the web interface.

    • The variable READSB_RTLSDR_DEVICE tells readsb to look for an RTL-SDR device with the serial of 1090 (that we re-serialized in an earlier step).

    • We're passing several environment variables through, including our timezone, latitude and longitude from the .env file (denoted by ${VARIABLE}).

  • We're using tmpfs for volumes that have regular I/O. Any files stored in a tmpfs mount are temporarily stored outside the container's writable layer. This helps to reduce:

    • The size of the container, by not writing changes to the underlying container; and

    • SD Card or SSD wear

You can find an expanded example of the docker-compose.yml file that you can download and edit here if you want to see other options, but the sample above is a good start.

Feeding directly from Ultrafeeder

There are several aggregators, both non-profit and commercial, that can directly be sent data from ultrafeeder without the need for an additional feeder container. We have added them in the example docker-compose.yml file above. Here is a partial list of these aggregators. All of them use the beast_reduce_plus format for feeding ADS-B data, and mlat-client for feeding MLAT:

Name

(C)ommercial/ (N)on-profit

Description

Feed details

Airplanes.live

N

Run by volunteers that used to be related to adsbexchange

adsb:feed.airplanes.live port 30004 mlat: feed.airplanes.live port 31090

ADSB.fi

N

Run by a Finnish IT and aviation enthusiast

adsb:feed.adsb.fi port 30004 mlat: feed.adsb.fi port 31090

ADSB.lol

N

Run by an aviation enthusiast located in the Netherlands

adsb:in.adsb.lol port 30004 mlat: in.adsb.lol port 31090

Planespotters

N

planespotters.net

adsb:feed.planespotters.net port 30004 mlat: mlat.planespotters.net port 31090

The Air Traffic

N

Run by an aviation enthusiast

adsb:feed.theairtraffic.com port 30004 mlat: mlat.theairtraffic.com port 31090

AVDelphi

N

Aviation data-science company (non-profit)

adsb:data.avdelphi.com port 24999 mlat: no MLAT

ADSB Exchange

C

Large aggregator owned by JetNet

adsb:feed1.adsbexchange.com port 30004 mlat: feed.adsbexchange.com port 31090

Fly Italy ADSB

N

Run by a few aviation enthusiasts in Italy

adsb: dati.flyitalyadsb.com port 4905 mlat: dati.flyitalyadsb.com port 30100

When feeding AdsbExchange, Ultrafeeder will send statistics to adsbexchange.com by default. See the description of the ADSBX_STATS parameter on how to disable this.

Using the MLAT results

A working MLAT configuration is already provided in the example above. See https://github.com/sdr-enthusiasts/docker-adsb-ultrafeeder/blob/main/README.md#configuring-the-built-in-mlat-hub for more details on how to configure more advanced features.

Deploying ultrafeeder

Once the docker-compose.yml file is created, issue the command docker compose up -d to bring up the environment.

You should see the following output:

We can view the logs for the environment with the command docker compose logs, or continually "tail" them with docker compose logs -f. At this stage, the logs will be fairly extensive and unexciting and look like this:

We can see our container running with the command docker ps:

We can see the adsb_default network with the command docker network ls:

Ultrafeeder Web Pages

If configured and started using the example above, the container will make a website available at port 8080 of your host machine. Here are a few web pages that are generated (replace my_host_ip with the name or IP address of your host machine):

  • http://my_host_ip:8080/ : tar1090 map and table of all aircraft received

  • http://my_host_ip:8080/graphs1090/ : page with graphs and operations statistics of your station

  • http://my_host_ip:8080?pTracks : showing all aircraft tracks received in the last 24 hours

  • http://my_host_ip:8080?heatmap&realheat : showing a heatmap of all aircraft in the last 24 hours

  • http://my_host_ip:8080?replay : showing a time-lapse replay of the past few days

Viewing Live Data in Text Format

To see the data being received and decoded by our new container, run the command docker exec -it ultrafeeder viewadsb. This should display a real-time departure-lounge-style screen showing all the aircraft being tracked, for example:

Press CTRL-C to escape this screen.

You should also be able to point your web browser to http://docker.host.ip.addr:8080/ to view the web interface (change docker.host.ip.addr to the IP address or hostname of your docker host). You should see a map showing your currently tracked aircraft, and a link to the "Performance Graphs".

UUID security

The example files above use the same UUID for all feeders. Doing so makes it possible that your information from one site could be tracked on another. An alternative approach is to generate a unique UUID for each website, load those into a variable in .env, and append the UUID to the row in the ULTRAFEEDER_CONFIG section. For example:

Preparing and setting up ultrafeeder with Prometheus and Grafana

See readme-grafana.MD at the container's Github repository web page.

Minimalist setup

If you want to use ultrafeeder only as a SDR decoder but without any mapping or stats/graph websites, without MLAT connections or MLAT-hub, etc., for example to minimize CPU and RAM needs on a low CPU/memory single board computer, then do the following:

  • in the ULTRAFEEDER_CONFIG parameter, remove any entry that starts with mlat or mlathub. This will prevent any mlat-clients or mlathub instances to be launched. If you want to connect the mlat-client(s) to external MLAT servers but you don't want to run the overhead of a MLATHUB, you can leave any entries starting with mlat in the ULTRAFEEDER_CONFIG parameter, and set MLATHUB_DISABLE=true

  • Set the parameter TAR1090_DISABLE=true. This will prevent the nginx web server and any websites from being launched

  • Make sure not to use the ghcr.io/sdr-enthusiasts/docker-adsb-ultrafeeder:telegraf label as Telegraf adds a LOT of CPU, disk, and memory use to the container

Advanced

If you want to look at more options and examples for the ultrafeeder container, you can find the repository here.

Last updated

Was this helpful?