How-To: Host two ADS-B receivers on one Raspberry Pi

How-To: Host two ADS-B receivers on one Raspberry Pi

Using one Raspberry Pi to run two instances of dump1090-fa listening to two FlightAware FlightStick Pro Plus with two antennas

Firstly, this how-to assumes there’s already a pi running dump1090-fa with a FlightStick and antenna, and that it is all already working – like this.

Secondly, FlightSticks tend to all have the same serial number (00001000) and so to run two on one system requires that at least one of them be updated with a new serial number.

Amending the serial numbers

To amend the serial number, install RTL-SDR tools on the Pi with the FlightStick inserted and execute a command with the new serial number.

Install RTL-SDR tools on the Pi:

sudo apt install rtl-sdr

Stop the dump1090-fa service so that the FlightStick can be accessed by RTL-SDR tools:

sudo systemctl stop dump1090-fa

Then execute the following command to update the serial number to 00000101 (I numbered mine 00000101 & 00000102 for the two sticks on one Pi and 00000201 for the stick on my second Pi):

rtl_eeprom -s 00000101

I’d suggest then labelling the FlightStick that has this serial number. Then restart dump1090-fa and ensure it is still working as expected.

sudo systemctl start dump1090-fa

Then repeat the above with the second stick, using a unique serial number.

Installing and configuring a second instance of dump1090-fa to use the second stick

Firstly, create a blank script to start the new instance, and make it executable

sudo touch /etc/init.d/dump1090-fa-2
sudo chmod +x /etc/init.d/dump1090-fa-2

Next, open the new file to edit it

sudo nano /etc/init.d/dump1090-fa-2

and copy the following into it

#!/bin/sh
### BEGIN INIT INFO
# Provides:          dump1090-mutability
# Required-Start:    $remote_fs $network
# Required-Stop:     $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: dump1090 daemon (mutability variant)
# Description:       Receives ADS-B messages from a RTLSDR dongle
#                    and makes them available to other applications via
#                    a variety of network protocols.
### END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="dump1090-fa-2 daemon"
NAME=dump1090-fa-2
DAEMON=/usr/bin/dump1090-fa
ARGS="--measure-noise "
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# work out daemon args

# sanitize missing settings
[ -z "$START_DUMP1090" ] && START_DUMP1090=no
[ -z "$DUMP1090_USER" ] && DUMP1090_USER="missing-DUMP1090_USER-setting-in-config"
[ -z "$RAW_INPUT_PORT" ] && RAW_INPUT_PORT=0
[ -z "$RAW_OUTPUT_PORT" ] && RAW_OUTPUT_PORT=0
[ -z "$SBS_OUTPUT_PORT" ] && SBS_OUTPUT_PORT=0
[ -z "$BEAST_INPUT_PORT" ] && BEAST_INPUT_PORT=0
[ -z "$BEAST_OUTPUT_PORT" ] && BEAST_OUTPUT_PORT=0
[ -z "$NET_BUFFER" ] && NET_BUFFER=0
[ -z "$JSON_INTERVAL" ] && JSON_INTERVAL=0
[ -z "$MAX_RANGE" ] && MAX_RANGE=300

# receiver:
NICELEVEL="--nicelevel -5"
case "x$DEVICE" in
 x|x0) ARGS="$ARGS --net" ;;
 xnone) ARGS="$ARGS --net-only"; NICELEVEL="" ;;
 *) ARGS="$ARGS --net --device-index $DEVICE" ;;
esac
case "x$GAIN" in
 x|xmax) ;;
 xagc) ARGS="$ARGS --gain -10" ;;
 *) ARGS="$ARGS --gain $GAIN" ;;
esac
if [ -n "$PPM" ]; then ARGS="$ARGS --ppm $PPM"; fi

# decoder:
if [ "x$FIX_CRC" = "xyes" ]; then ARGS="$ARGS --fix"; fi
if [ -n "$LAT" ]; then ARGS="$ARGS --lat $LAT"; fi
if [ -n "$LON" ]; then ARGS="$ARGS --lon $LON"; fi
ARGS="$ARGS --max-range $MAX_RANGE"

# net:

ARGS="$ARGS \
--net-ri-port $RAW_INPUT_PORT --net-ro-port $RAW_OUTPUT_PORT \
--net-bi-port $BEAST_INPUT_PORT --net-bo-port $BEAST_OUTPUT_PORT \
--net-sbs-port $SBS_OUTPUT_PORT"
if [ -n "$NET_HEARTBEAT" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi
if [ -n "$NET_OUTPUT_SIZE" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi
if [ -n "$NET_OUTPUT_INTERVAL" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi
if [ "$NET_BUFFER" -le "65536" ]; then ARGS="$ARGS --net-buffer 0"
elif [ "$NET_BUFFER" -le "131072" ]; then ARGS="$ARGS --net-buffer 1"
elif [ "$NET_BUFFER" -le "262144" ]; then ARGS="$ARGS --net-buffer 2"
else ARGS="$ARGS --net-buffer 3"; fi
if [ -n "$NET_BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $NET_BIND_ADDRESS"; fi

# misc:
if [ -n "$STATS_INTERVAL" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi
if [ -n "$JSON_DIR" ]; then ARGS="$ARGS --write-json $JSON_DIR"; fi
if [ -n "$JSON_INTERVAL" ]; then ARGS="$ARGS --write-json-every $JSON_INTERVAL"; fi
case "x$JSON_LOCATION_ACCURACY" in
  xexact) ARGS="$ARGS --json-location-accuracy 2" ;;
  xapproximate) ARGS="$ARGS --json-location-accuracy 1" ;;
  *) ARGS="$ARGS --json-location-accuracy 0" ;;
esac

if [ "x$LOG_DECODED_MESSAGES" != "xyes" ]; then ARGS="$ARGS --quiet"; fi

if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started

        if [ "x$START_DUMP1090" != "xyes" ]; then
            log_warning_msg "Not starting $NAME daemon, disabled via /etc/default/$NAME"
            return 2
        fi

        start-stop-daemon --start --quiet --pidfile $PIDFILE --user "$DUMP1090_USER" --exec $DAEMON --test > /dev/null \
                || return 1

        # create JSON_DIR with the appropriate permissions
        # (it is on /run by default, so will be lost on reboot)
        if [ "x$JSON_DIR" != "x" ]; then
           if [ ! -e $JSON_DIR ]; then
             (mkdir $JSON_DIR && chmod 0755 $JSON_DIR && chown $DUMP1090_USER $JSON_DIR) || log_warning_msg "Failed to create $JSON_DIR"
           fi
        fi

        # create logfile with the appropriate permissions if not already there
        touch $LOGFILE
        chown "$DUMP1090_USER":root $LOGFILE

        start-stop-daemon --start $NICELEVEL --quiet --pidfile $PIDFILE --user "$DUMP1090_USER" --chuid "$DUMP1090_USER" --make-pidfile --background --no-close --exec $DAEMON -- \
                $ARGS >>$LOGFILE 2>&1 \
                || return 2
        sleep 1
}

#
# Function that stops the daemon/service
#
do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --retry=TERM/30/KILL/5 --pidfile $PIDFILE --user "$DUMP1090_USER" --exec $DAEMON
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        sleep 1
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}

case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  status)
        status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
        ;;
  restart|force-reload)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
        exit 3
        ;;
esac

:

Close and save the above file by pressing Ctrl+X, answering “yes” and pressing ENTER to keep the same filename

Next, enable autostart, and reload the newly installed daemon


sudo update-rc.d dump1090-fa-2 defaults   
sudo systemctl daemon-reload

Create a new blank file to hold the configuration

sudo nano /etc/default/dump1090-fa-2

And copy the following into it

# dump1090-fa-2 configuration file
# This is a POSIX shell fragment.
# You can edit this file directly.

# Set to "yes" to start dump1090 on boot.
START_DUMP1090="yes"

# User to run dump1090 as.
DUMP1090_USER="dump1090"

# Logfile to log to
LOGFILE="/var/log/dump1090-fa-2.log"

#
# Receiver options
#

# RTLSDR device index or serial number to use
# If set to "none", dump1090 will be started in --net-only mode
DEVICE="1"

# RTLSDR gain in dB.
# If set to "max" the maximum supported gain is used.
# If set to "-10" (the default) the tuner AGC is used to set the gain.
GAIN="-10"

# RTLSDR frequency correction in PPM
PPM="0"

#
# Decoding options
#

# If yes, fixes messages with correctable CRC errors.
FIX_CRC="yes"

# If set, supplies a reference location for local position decoding.
LAT=""
LON=""

# If set, provides the absolute maximum receiver range used to
# filter bad position reports, and to determine when local position
# decoding is safe to use. Specify this in nautical miles (NM).
MAX_RANGE="300"

#
# Networking options
#

# Port to listen on for raw (AVR-format) input connections. 0 disables.
RAW_INPUT_PORT="31001"

# Port to listen on for raw (AVR-format) output connections. 0 disables.
RAW_OUTPUT_PORT="31002"

# Port to listen on for SBS-format output connections. 0 disables.
SBS_OUTPUT_PORT="31003"

# Port to listen on for Beast-format input connections. 0 disables.
BEAST_INPUT_PORT="31004,31104"

# Port to listen on for Beast-format output connections. 0 disables.
BEAST_OUTPUT_PORT="31005"

# TCP heartbeat interval in seconds. 0 disables.
NET_HEARTBEAT="60"

# Minimum output buffer size per write, in bytes.
NET_OUTPUT_SIZE="500"

# Maximum buffering time before writing, in seconds.
NET_OUTPUT_INTERVAL="1"

# TCP buffer size, in bytes
NET_BUFFER="262144"

# Bind ports on a particular address. If unset, binds to all interfaces.
# This defaults to binding to localhost. If you need to allow remote
# connections, change this.
#NET_BIND_ADDRESS="127.0.0.1"

#
# Misc options
#

# Interval (in seconds) between logging stats to the logfile. 0 disables.
STATS_INTERVAL="3600"

# Path to write json state to (for use with an external webserver). Blank disables.
JSON_DIR="/run/dump1090-fa-2"

# Interval between writing json state (in seconds). 0 disables.
JSON_INTERVAL="1"

# Accuracy of receiver location to write to json state, one of "exact" / "approximate" / "none"
JSON_LOCATION_ACCURACY="approximate"

# Set to yes to log all decoded messages
# This can get large fast!
LOG_DECODED_MESSAGES="no"

# Additional options that are passed to the Daemon.
EXTRA_ARGS=""
NET_BIND_ADDRESS=""

Edit the following lines within the above to reflect the location of the receiver

# If set, supplies a reference location for local position decoding._
LAT=""
LON=""

Enabling a map to show the new receiver’s aircraft separately

Make a copy of the existing dump1090-fa folder

cd /usr/share
sudo cp -r dump1090-fa dump1090-fa-2

Create a new file for the config

sudo nano /etc/lighttpd/conf-available/89-dump1090-fa-2.conf

and copy the following into it

# Allows access to the static files that provide the dump1090 map view,
# and also to the dynamically-generated json parts that contain aircraft
# data and are periodically written by the dump1090 daemon.


alias.url += (
  "/dump1090-fa-2/data/" => "/run/dump1090-fa-2/",
  "/dump1090-fa-2/" => "/usr/share/dump1090-fa-2/html/"
)


# redirect the slash-less URL
url.redirect += (
  "^/dump1090-fa-2$" => "/dump1090-fa-2/"
)

Finally, enable the lighttpd module for the second dump1090-fa instance and reboot the Pi

sudo lighty-enable-mod dump1090-fa-2   
sudo /etc/init.d/lighttpd force-reload
sudo shutdown -r now

The new receiver’s map is available at

http://<ip-address>/dump1090-fa-2

The map & table can be customised by editing the config.js file:

sudo nano /usr/local/share/tar1090/html-combine1090/config.js

Adding the plane count into the page title allows easy reference in a browser tab:

PlaneCountInTitle = true;

The columns that show in the table by default when opening the page can be amended by uncommenting relevant rows in this block

// Columns that have a // in front of them are shown.
// add slash-star at start of line to default to not  modify columns (and the one at the end)
HideCols = [
        "#icao",
//      "#flag",
//      "#flight",
        "#registration",
//      "#aircraft_type",
        "#squawk",
//      "#altitude",
        "#speed",
        "#vert_rate",
//      "#distance",
        "#track",
        "#msgs",
//      "#seen",
        "#rssi",
        "#lat",
        "#lon",
        "#data_source",
]
// add star-slash at start of line to default to not modify columns (and the one at the start)

The page title itself can be changed from “tar1090” should you wish

// Controls page title, righthand pane when nothing is selected
PageName = "My ADS-B";

Range rings can be edited

// Range rings

// Also called range rings :)
SiteCircles = true; // true to show circles (only shown if the center marker is shown)
// In miles, nautical miles, or km (depending settings value 'DisplayUnits')
SiteCirclesDistances = new Array(100,150,200,250);
// When more circles defined than cirle colors last color will be used or black by default
//SiteCirclesColors = ['#FF0000', '#0000FF', '#00FF00'];
// Show circles using dashed line (CAUTION, can be slow, especially when zooming in a lot)
//SiteCirclesLineDash = [5, 5]; // null - solid line, [5, 5] - dashed line with 5 pixel lines and spaces in between

Once any changes are made to the config file, don’t forget to restart combine1090

sudo systemctl restart combine1090

Useful links

https://discussions.flightaware.com/t/one-pi-two-dongles-two-maps-two-receivers/33978/14

https://discussions.flightaware.com/t/how-to-change-serial-number-of-dongle-adsb-1090-uat-978-dongles-plugged-into-same-pi/31980

Leave a Reply

Your email address will not be published. Required fields are marked *