ChannelSPURIOUS EMISSIONS

Sheet 4 · The written guides

Build the
station

A shopping list and the build steps for a station like mine. It receives aircraft, ships, the text messages that aircraft send, and most of shortwave. Every program in the main steps is free and public.

Rev B · checked 2026-10-21

Download the Markdown

The Markdown file is the whole guide in plain text. You can give it to a coding agent.

Some links are affiliate links: if you buy through them, I may earn a commission at no extra cost to you. As an Amazon Associate I earn from qualifying purchases. How links work.

Contents

1. What this guide builds

Two computers and five radios. Each radio sits where its antenna cable enters the house. Each computer decodes the signal there and sends only data over the home network. Antenna cable loses signal with every meter. Network cable loses nothing.

Signal What you get Frequency Antenna Radio Decoder Computer
ADS-B Aircraft positions 1090 MHz 1090 MHz vertical FlightAware Pro Stick Plus readsb (in ultrafeeder) vhf
AIS Ship positions 161.975 and 162.025 MHz AIS-band vertical Airspy Mini AIS-catcher (in shipfeeder) vhf
ACARS Aircraft text messages 129 to 132 MHz Discone, splitter leg A Airspy R2 acarsdec vhf
VDL Mode 2 Aircraft text messages, newer system 136.650 to 136.975 MHz Discone, splitter leg B RTL-SDR Blog V4L dumpvdl2 vhf
HFDL Aircraft messages over oceans 12 bands, 2.9 to 21.9 MHz Active loop RX-888 Mk II dumphfdl hf
FT8 and FT4 Amateur stations, worldwide 1.8 to 28 MHz Active loop RX-888 Mk II decode_ft8 hf
WSPR Low-power beacons, worldwide 0.136 to 28 MHz Active loop RX-888 Mk II wsprd hf

The two computers have these names in this guide:

  • vhf: a Raspberry Pi 5. It runs four radios in Docker containers.
  • hf: an x86 desktop PC. It runs the RX-888 Mk II with ka9q-radio, as system services.

My own station uses four computers, for reasons of history.1 Section 15 lists the signals I receive with my own programs, which are not public, and a free program for each job.

 roof                              cable entry                      home network
 ----                              -----------                      ------------
 1090 MHz vertical ---------------- Pro Stick Plus --+
 AIS vertical ------ FM filter ---- Airspy Mini -----+-- USB hub -- vhf (Pi 5, Docker)
 discone -- FM filter -- splitter + Airspy R2 -------+                 |  maps, message log,
                                  + RTL-SDR V4L -----+                 |  feeds to networks
                                                                       |
 loop -- injector -- 30 MHz filter -- limiter -- RX-888 Mk II -- hf (x86 PC)
                                                  (own USB 3 socket)   HFDL by UDP to vhf

Values to choose first

Write these down. The steps use them as placeholders.

Placeholder Meaning Example form
<LAT> <LON> Antenna position, decimal degrees xx.xxxxx
<ALT_M> Antenna height above sea level, meters 45
<TZ> Time zone, TZ database name America/Los_Angeles
<NAME> A station name with no personal detail my-station
<UUID> One random ID for the aircraft networks from cat /proc/sys/kernel/random/uuid
<SER_ADSB> <SER_VDL2> Serial numbers you write to the two RTL radios 1090, 136
<SER_MINI> <SER_R2> Factory serial numbers of the two Airspy radios 16 hexadecimal characters

Your position becomes public. Most tracking networks show their feeder stations on a map. Multilateration needs your exact position, so you cannot give a false one. Choose a station name that does not name you or your street. Sections 7 and 9 show the settings that hide the station mark on your own maps.

2. Bill of materials

Prices are US dollars, as listed by the named seller on 2026-10-07. They change. “See listing” means the price is on the linked page. A part marked ◆ is what I use; “an example” or “a similar one” is a link to a part like mine, not the one I have. A blank box is for your pencil.

Antennas

☐ Part Job Connector Price Note
☐ ◆ FlightAware 1090 MHz antenna, 66 cm, 5.5 dBi Aircraft N type $49.99, FlightAware store Mast bracket included
☐ ◆ AIS-band vertical, tuned near 162 MHz Ships See listing See listing A low-cost one is enough to start2
☐ ◆ Diamond D-130J discone, 25 to 1300 MHz receive, 1.7 m ACARS and VDL2 SO-239 (the D-130NJ has N type) See listing, Amazon One wide antenna, two radios
☐ ◆ MLA-30+ active loop with its power injector Shortwave SMA See listing, Amazon The amplifier is in the loop head3

Filters and protection

☐ Part Job Price Note
☐ ◆ RTL-SDR Blog FM band-stop filter, 88 to 108 MHz, over 50 dB First part after the discone See listing, Amazon Loss under 0.5 dB below 1 GHz
☐ ◆ Nooelec Flamingo FM, over 40 dB typical On the AIS antenna See listing, Amazon Optional4
☐ ◆ Mini-Circuits ZX10-2-12-S+ 2-way splitter, 2 to 1200 MHz, SMA Feeds two radios from the discone $42.20, Mini-Circuits Each leg gets a little less than half5
☐ ◆ Turn Island Systems F3060 low-pass filter In front of the RX-888 $80, Turn Island Systems Required at 64.8 MS/s (section 4)
☐ ◆ TAPR RF Limiter Last part before the RX-888 $20, TAPR Needed if you also transmit6

Do not buy an amplifier at the start. Section 17 says why.

Radios

☐ Part Job Price Note
☐ ◆ FlightAware Pro Stick Plus ADS-B $45.99, FlightAware store Amplifier (19 dB) and 1090 MHz filter are built in
☐ ◆ Airspy Mini, 24 to 1700 MHz, 12-bit AIS $99, airspy.us (out of stock that day) An RTL-SDR also works7
☐ ◆ Airspy R2, 24 to 1700 MHz, 12-bit ACARS $169, airspy.us An RTL-SDR also works7
☐ ◆ RTL-SDR Blog V4L VDL2 $37.95, rtl-sdr.com Needs a 2026 driver (section 17). The older V4 is no longer made
☐ ◆ Stick-on aluminum heat sinks, 50 x 25 x 10 mm (mine, a pack of two) On the dongles. Optional See seller My own addition, not part of the radios as sold. They run warm
☐ ◆ RX-888 Mk II, 16-bit, 10 kHz to 64 MHz direct All of shortwave at once $152.79, OpenSourceSDRLab, 2026-10-11 Copies exist. Mine is from OpenSourceSDRLab. Without it, see the note8

Computers and USB

☐ Part Job Price Note
☐ ◆ Raspberry Pi 5, 8 GB vhf $175 list (4 GB: $110) Requirements in section 3
☐ ◆ Raspberry Pi 27 W USB-C power supply Power for vhf See seller With a weaker supply the Pi limits its USB sockets to 600 mA in total
☐ microSD card, 32 GB or more (an example), or an NVMe drive with a Pi 5 adapter Storage for vhf See seller
☐ ◆ Powered USB hub with its own power supply (mine: Rosonway RSH-A10) The four vhf radios See seller A Pi alone can power about three radios
☐ ◆ x86-64 desktop PC with a USB 3.0 socket on the main board (mine: HP EliteDesk 800 G1 SFF, Core i5-4570, 12 GB, bought used; a similar one) hf Used market Requirements in section 3
☐ ◆ SATA solid-state drive, 120 GB or more (mine: Crucial MX500 500 GB, bought used) Storage for hf See seller The FT8 recorder writes files all day
☐ ◆ Battery backup (UPS), 1500 VA (mine: CyberPower CP1500PFCLCD) Keeps almost the whole station running through a short power cut See seller Optional. A long cut still stops the station: section 11, step 2 covers that

Cable and small parts

☐ Part Job
☐ Low-loss 50 ohm coax for 1090 MHz, with an N plug at the antenna end and an SMA plug at the radio end (an example) Aircraft antenna. Keep the loss under 6 dB and the length under 15 m (FlightAware’s advice)
☐ 50 ohm coax with a PL-259 plug (an example), and an SMA plug or adapter at the radio end (an example) Discone
☐ Coax to suit the AIS antenna, SMA at the radio end AIS antenna
☐ ◆ RG-6 quad-shield coax (an example), and two F to SMA adapters (an example) Loop to hf. On shortwave, 10 to 20 m of it loses under 1 dB
☐ Short SMA plug-to-plug jumpers, 5 or more (an example) Filter to splitter to radios
☐ ◆ Snap-on ferrites (an example) Both ends of the loop cable, and the USB cables
☐ ◆ Grounding block for each cable where it enters the house (an example), and bonding wire Section 4, “Ground the cables”
☐ 50 ohm SMA terminator (an example) The noise test in section 11
☐ Short USB extension cables (an example) So wide radios do not block the next hub socket

3. Computer requirements

I can only state what I measured. The table says where a number is measured and where it is an estimate.

Computer I recommend What I measured on my station (2026-10-07)
vhf Raspberry Pi 5 with 8 GB, 64-bit Raspberry Pi OS, wired Ethernet, a powered USB hub. Any 64-bit ARM or x86 computer that runs Docker also works. A Pi 5 with the AIS, ACARS and VDL2 containers used 1.6 GB of memory and a load average near 1 on 4 cores. The aircraft containers run on another computer here. There they used about 1.5 GB more. The screenshot container alone used 0.66 GB. So 4 GB is tight, and I did not test it.
hf x86-64, 4 cores of Intel Haswell class (2013) or newer, 8 GB, a USB 3.0 socket on the main board, a solid-state drive, Ubuntu Server 24.04 LTS, wired Ethernet. A Core i5-4570 (4 cores, 2013) ran the receiver at 64.8 MS/s with about 150 channels. The receiver program used 69 % of one core. The whole machine was 22 % busy with HFDL and 29 % busy with every decoder. It dropped no data in a 19-hour test. Memory in use was under 2.5 GB.

Rules for both:

  1. Use wired Ethernet. Multilateration needs steady timing.
  2. Give each computer a fixed address on your network, set on the computer itself.
  3. Keep the clocks synchronized. The ultrafeeder project recommends chrony.
  4. Do not run the aircraft receiver in a virtual machine. Multilateration often fails there (ultrafeeder README).
  5. Plug the RX-888 Mk II into a USB 3.0 socket on the PC itself. Never use a hub for it. At 64.8 MS/s it sends about 1 gigabit each second.

4. The radio-frequency chains

Build each chain in this order, antenna first.

A. Aircraft     1090 MHz vertical -- coax -- Pro Stick Plus
                (the radio has its own amplifier and filter: add nothing)

B. Ships        AIS vertical -- coax -- [Flamingo FM, optional] -- Airspy Mini

C. Messages     discone -- coax -- FM band-stop filter -- 2-way splitter
                                                            |-- leg A -- Airspy R2      (ACARS)
                                                            `-- leg B -- RTL-SDR V4L    (VDL2)

D. Shortwave    loop -- RG-6 -- power injector -- F3060 (30 MHz low-pass in,
                shelf filter bypassed) -- RF limiter -- RX-888 Mk II, HF input

The reasons for the order:

  • Filter before splitter. One filter then cleans both legs.
  • No amplifier on a short, direct run. Add one only to pay for a loss you have measured, and count messages before and after.
  • Bias tee off on every radio behind the splitter and on the RX-888. A radio that puts DC on the cable can feed it into the other leg. The loop takes its power from its own injector.
  • Injector first on the loop cable. Every part after it then sees no DC. The limiter blocks DC, so it must come after the injector.
  • Low-pass filter before the limiter. A limiter makes harmonics when it clamps. TAPR’s manual says to filter ahead of it.
  • Why the 30 MHz filter is not optional. At 64.8 million samples each second, the receiver cannot tell a signal above 32.4 MHz from one below it. A signal from 32 to 65 MHz folds back onto shortwave. The F3060 removes it: 36 dB down at 32.5 MHz and 64 dB down at 51 MHz, by its manual. It costs 3.7 dB at 28 MHz.
  • Leave the F3060 shelf filter bypassed. It cuts 28 dB below 1 MHz and 20 dB at 3.5 MHz. Turn it on only if strong AM broadcast stations overload the receiver at night.

Put the antennas outside

This matters more than any part on the list. My ship antenna heard about 5 messages a minute indoors. The same radio and program heard about 30 a minute with a low-cost antenna on the roof. For the loop, height matters little. Distance from the electronics in the house matters a lot.

Ground the cables

An outdoor antenna cable needs a path to ground where it enters the building. In the US the rule is National Electrical Code Article 810. Bond the grounding block to the building’s own grounding electrode system. If you add a ground rod, bond it to that system with 6 AWG copper or heavier. Never use a rod that stands alone: a surge then goes through your equipment. I read a published summary of the rule, not the code text.9 Use a licensed electrician if you are not sure.

5. Accounts

Create these as each phase asks for them. All are free. Most give a paid-level account in return for your data.

Network Phase What identifies your station Where to sign up
adsb.fi, airplanes.live, ADS-B Exchange 7 <UUID>, no account Nothing to do
FlightAware 7 A feeder ID that the container prints https://flightaware.com/adsb/piaware/claim
Flightradar24 7 A sharing key from the sign-up command Section 7
Plane Finder, AirNav Radar, OpenSky Network, plane.watch 7 A share code, key, user name or API key Each container’s README (section 19)
aiscatcher.org 9 Nothing: sharing is one setting Nothing to do
MarineTraffic, AISHub, VesselFinder, ShipXplorer 9 A host and number that the network sends you Each network’s “add a station” page
airframes.io 10 The station ID you choose https://app.airframes.io
PSK Reporter, WSPRnet 13, 14 A callsign and a grid locator Sections 13 and 14
Discord 8 A channel webhook address Channel, Edit Channel, Integrations, Webhooks

Keep every key in a password manager. The compose file on vhf will hold them in plain text: do not publish that file.

6. Phase 1: prepare vhf

  1. Write Raspberry Pi OS Lite (64-bit) to the card with Raspberry Pi Imager. In the Imager settings, set the host name to vhf, turn SSH on, and add your public key.

  2. Start the Pi on wired Ethernet. Log in with SSH.

  3. Update the system.

    sudo apt update && sudo apt full-upgrade -y && sudo reboot
    
  4. Install Docker with the sdr-enthusiasts script. It also stops the kernel’s TV drivers from taking the RTL radios.

    bash <(curl -s https://raw.githubusercontent.com/sdr-enthusiasts/docker-install/main/docker-install.sh)
    sudo reboot
    
  5. Check Docker and the driver block.

    docker compose version
    cat /etc/modprobe.d/exclusions-rtl2832.conf      # expect 9 "blacklist" lines
    lsmod | grep -c dvb_usb_rtl28xxu                 # expect 0
    
  6. Install the Airspy tools and the clock service.

    sudo apt install -y airspy chrony
    
  7. Connect the powered hub. Plug in one RTL radio at a time and write its serial number. Do the Pro Stick Plus first, then the V4L with 136.

    docker run --rm -it --device /dev/bus/usb --entrypoint rtl_eeprom \
      ghcr.io/sdr-enthusiasts/docker-adsb-ultrafeeder -s 1090
    

    Change only the serial number. Do not change the maker or product text on an RTL-SDR Blog radio: its driver looks for that text. Then unplug every radio and plug it in again. A restart of the computer does not load the new serial numbers.

  8. Plug in the two Airspy radios, one at a time, and read each factory serial number.

    airspy_info | grep -i serial
    

    Label each radio. Both Airspy models report the same USB identity (1d50:60a1). Only the serial number tells them apart.

  9. Make the project folder. Every container in this guide goes into one compose file, so the containers can find each other by name.

    sudo mkdir -p -m 777 /opt/station && cd /opt/station
    cat /proc/sys/kernel/random/uuid                 # this is <UUID>
    
  10. Create /opt/station/.env with your values.

    FEEDER_TZ=<TZ>
    FEEDER_LAT=<LAT>
    FEEDER_LONG=<LON>
    FEEDER_ALT_M=<ALT_M>
    FEEDER_NAME=<NAME>
    FEEDER_UUID=<UUID>
    ADSB_SDR_SERIAL=1090
    VDL2_SDR_SERIAL=136
    AIS_SDR_SERIAL=<SER_MINI>
    ACARS_SDR_SERIAL=<SER_R2>
    ACARS_STATION_ID=<STATION_ID>
    

7. Phase 2: aircraft (ADS-B)

Program: docker-adsb-ultrafeeder. One container owns the radio, decodes, draws the map and feeds the networks.

  1. Create /opt/station/docker-compose.yml.

    services:
      ultrafeeder:
        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
        environment:
          - TZ=${FEEDER_TZ}
          - READSB_DEVICE_TYPE=rtlsdr
          - READSB_RTLSDR_DEVICE=${ADSB_SDR_SERIAL}
          - READSB_GAIN=auto
          - READSB_LAT=${FEEDER_LAT}
          - READSB_LON=${FEEDER_LONG}
          - READSB_ALT=${FEEDER_ALT_M}m
          - READSB_RX_LOCATION_ACCURACY=2
          - ULTRAFEEDER_CONFIG=
            adsb,feed.adsb.fi,30004,beast_reduce_plus_out;
            adsb,feed.airplanes.live,30004,beast_reduce_plus_out;
            adsb,feed1.adsbexchange.com,30004,beast_reduce_plus_out;
            mlat,feed.adsb.fi,31090;
            mlat,feed.airplanes.live,31090;
            mlat,feed.adsbexchange.com,31090
          - UUID=${FEEDER_UUID}
          - MLAT_USER=${FEEDER_NAME}
          - UPDATE_TAR1090=true
          - TAR1090_ENABLE_AC_DB=true
        volumes:
          - /opt/station/ultrafeeder/globe_history:/var/globe_history
          - /opt/station/ultrafeeder/graphs1090:/var/lib/collectd
          - /proc/diskstats:/proc/diskstats:ro
          - /dev/bus/usb:/dev/bus/usb
        tmpfs:
          - /run:exec,size=256M
    

    Each ULTRAFEEDER_CONFIG line ends with a semicolon, except the last. The README lists more networks in the same form.

  2. Start it and check.

    cd /opt/station && docker compose up -d
    docker ps --filter name=ultrafeeder        # expect "(healthy)" after a few minutes
    curl -s http://localhost:8080/data/aircraft.json | head -c 300
    

    The map is at http://vhf:8080/. The performance graphs are at http://vhf:8080/graphs1090/.

  3. To hide the station mark on your own map, set READSB_RX_LOCATION_ACCURACY=1 (two decimal places) or 0. The networks still receive the exact position.

More networks, one container each

Each of these reads the decoded stream from ultrafeeder at 30005/tcp and sends it to one network. Add each as a service in the same file. FlightAware and Flightradar24 show the pattern.

  piaware:
    image: ghcr.io/sdr-enthusiasts/docker-piaware:latest
    container_name: piaware
    restart: unless-stopped
    environment:
      - TZ=${FEEDER_TZ}
      - RECEIVER_TYPE=relay
      - BEASTHOST=ultrafeeder
      - BEASTPORT=30005
      - FEEDER_ID=${PIAWARE_FEEDER_ID}
      - MLAT_RESULTS_BEASTHOST=ultrafeeder
      - MLAT_RESULTS_BEASTPORT=31004
    tmpfs:
      - /run:exec,size=64M
      - /var/log

  fr24:
    image: ghcr.io/sdr-enthusiasts/docker-flightradar24:latest
    container_name: fr24
    restart: unless-stopped
    dns_search: .
    environment:
      - BEASTHOST=ultrafeeder
      - FR24KEY=${FR24_KEY}
      - MLAT=no
Network Image Required settings How to get the key
FlightAware ghcr.io/sdr-enthusiasts/docker-piaware RECEIVER_TYPE=relay, BEASTHOST, FEEDER_ID timeout 30 docker run --rm ghcr.io/sdr-enthusiasts/docker-piaware:latest | grep "my feeder ID", then claim it on the FlightAware page
Flightradar24 ghcr.io/sdr-enthusiasts/docker-flightradar24 BEASTHOST, FR24KEY docker run -it --rm --entrypoint /usr/bin/fr24feed ghcr.io/sdr-enthusiasts/docker-flightradar24:latest --signup --configfile=/tmp/config.txt
Plane Finder ghcr.io/sdr-enthusiasts/docker-planefinder:5.0.161_arm64 on a Pi 5 BEASTHOST, SHARECODE, LAT, LONG See its README
AirNav Radar ghcr.io/sdr-enthusiasts/docker-airnavradar BEASTHOST, LAT, LONG, ALT (meters, no unit), SHARING_KEY The first run prints a key in the log. On a Pi 5, read section 17 first
OpenSky Network ghcr.io/sdr-enthusiasts/docker-opensky-network BEASTHOST, OPENSKY_USERNAME, OPENSKY_SERIAL, LAT, LONG, ALT The first run creates the serial. Save it, or each start makes a new station
plane.watch ghcr.io/plane-watch/docker-plane-watch BEASTHOST, API_KEY, LAT, LONG, ALT https://atc.plane.watch

Three rules from the ultrafeeder README:

  1. Never send multilateration results on to any network.
  2. If you feed several networks, leave multilateration off for Flightradar24 (MLAT=no). They ask for this.
  3. Set FEEDER_ID for FlightAware. Without it, each start creates a new site.

8. Phase 3: alerts for unusual aircraft

Program: docker-planefence. It keeps a log of aircraft that pass low and close. Its plane-alert part compares every aircraft with a public watch list, plane-alert-db.

  1. Add two services to the compose file.

      planefence:
        image: ghcr.io/sdr-enthusiasts/docker-planefence
        container_name: planefence
        hostname: planefence
        restart: unless-stopped
        ports:
          - 8088:80
          - 9999:9999
        environment:
          - TZ=${FEEDER_TZ}
        volumes:
          - /opt/station/planefence/config:/usr/share/planefence/persist
          - /opt/station/planefence/html:/usr/share/planefence/html
        tmpfs:
          - /run:rw,nosuid,nodev,exec,relatime,size=512M
          - /tmp:rw,nosuid,nodev,noexec,relatime,size=128M
    
      screenshot:
        image: ghcr.io/kx1t/screenshot
        container_name: screenshot
        hostname: screenshot
        restart: unless-stopped
        shm_size: 1gb
        environment:
          - MAP_ARGS=zoom=7&hideSidebar&hideButtons&mapDim=0.2&monochromeMarkers=ff0000&outlineColor=505050&iconScale=1.5&enableLabels&extendedLabels=2&trackLabels&screenshot&rangerings=0
          - LOAD_SLEEP_TIME=25
          - BASE_URL=http://ultrafeeder
          - MAXTIME=60
          - CANVAS_SIZE=800,1200
    

    The host name of the second container must be screenshot. Planefence looks for that name.

  2. Run docker compose up -d. Then copy planefence.config.RENAME-and-EDIT-me to planefence.config in /opt/station/planefence/config/, or use the settings page at http://vhf:9999/.

  3. Set these lines in planefence.config.

    FEEDER_LAT=<LAT>
    FEEDER_LON=<LON>
    PF_SOCK30003HOST=ultrafeeder
    PF_MAXDIST=2.0
    PF_MAXALT=5000
    PF_ALERTLIST=https://raw.githubusercontent.com/sdr-enthusiasts/plane-alert-db/main/plane-alert-pia.csv,https://raw.githubusercontent.com/sdr-enthusiasts/plane-alert-db/main/plane-alert-db-images.csv
    PF_PA_SQUAWKS=7400,7500,7600,7700
    PA_DISCORD=ON
    PA_DISCORD_WEBHOOKS=<your webhook address>
    PF_DISCORD=OFF
    DISCORD_FEEDER_NAME=<NAME>
    DISCORD_MEDIA=screenshot+photo
    PF_SCREENSHOTURL=http://screenshot:5042
    

    This file spells the longitude FEEDER_LON. The .env file spells it FEEDER_LONG.

  4. Apply and check.

    docker compose up -d planefence --force-recreate
    docker exec planefence curl -s -o /tmp/t.png http://screenshot:5042/snap/<hex>   # <hex> from aircraft.json
    

    The log page is at http://vhf:8088/. The alert page is at http://vhf:8088/plane-alert.

Notes from my station:

  • Lists match first to last. I put the list of aircraft that hide their identity (plane-alert-pia.csv) first.
  • I keep PF_DISCORD=OFF. With it on, every low aircraft near the house sends a message.
  • zoom=7 and LOAD_SLEEP_TIME=25 are my values. At the default 15 seconds the map tiles had not loaded, and the picture was grey.
  • DISCORD_FEEDER_NAME appears in every alert. Do not put your town in it if the channel is shared.

9. Phase 4: ships (AIS)

Program: docker-shipfeeder, which runs AIS-catcher.

  1. Add the service.

      shipfeeder:
        image: ghcr.io/sdr-enthusiasts/docker-shipfeeder
        container_name: shipfeeder
        hostname: shipfeeder
        restart: unless-stopped
        environment:
          - SDR_TYPE=AIRSPY
          - RTLSDR_DEVICE_SERIAL=${AIS_SDR_SERIAL}
          - STATION_NAME=${FEEDER_NAME}
          - FEEDER_LAT=${FEEDER_LAT}
          - FEEDER_LONG=${FEEDER_LONG}
          - AISCATCHER_SHAREDATA=true
          # one line for each network, as each sends you its host and number:
          # - MARINETRAFFIC_UDP_PORT=<host>:<number>
          # - AISHUB_UDP_PORT=<host>:<number>
          # - VESSELFINDER_UDP_PORT=<host>:<number>
          # - SHIPXPLORER_UDP_PORT=<host>:<number>
        ports:
          - 90:80
        device_cgroup_rules:
          - "c 189:* rwm"
        tmpfs:
          - /tmp
        volumes:
          - /opt/station/shipfeeder/data:/data
          - /dev:/dev:rw
    

    The setting names say RTLSDR, but they apply to an Airspy too when SDR_TYPE=AIRSPY. Leave SITESHOW unset: when set, the map shows your station as a dot.

  2. Start and check.

    docker compose up -d
    docker logs shipfeeder 2>&1 | grep "Searching for device"     # must name <SER_MINI>, not <SER_R2>
    

    The ship map is at http://vhf:90/. Near salt water, the first messages arrive within minutes.

  3. Sign up with each network. Each one sends a host name and a number. Put them in the matching line, remove the #, and run docker compose up -d again.

  4. For ShipXplorer on a Pi 5, choose “NMEA over UDP” when you add the station, and use SHIPXPLORER_UDP_PORT. Its sharing-key program does not run on the standard Pi 5 kernel (section 17). Never set both methods.

10. Phase 5: aircraft messages (ACARS and VDL Mode 2)

Four containers: acarsdec and dumpvdl2 decode, acars_router removes duplicates and sends the messages on, and ACARS Hub stores and shows them. The HFDL lines are ready for section 12.

  1. Choose <STATION_ID>. The airframes.io form is XX-YYYY: your initials, then the code of the nearest large airport. This ID is public.

  2. Add the services.

      acarsdec:
        image: ghcr.io/sdr-enthusiasts/docker-acarsdec:latest
        container_name: acarsdec
        restart: unless-stopped
        environment:
          - TZ=${FEEDER_TZ}
          - AIRSPY_SERIAL=${ACARS_SDR_SERIAL}
          - FEED_ID=${ACARS_STATION_ID}-ACARS
          - FREQUENCIES=130.025;130.425;130.450;131.125;131.550
          - OUTPUT_SERVER=acars_router
          - OUTPUT_SERVER_PORT=5550
          - OUTPUT_SERVER_MODE=udp
        tmpfs:
          - /run:exec,size=64M
          - /var/log
        device_cgroup_rules:
          - "c 189:* rwm"
        volumes:
          - /dev/bus/usb:/dev/bus/usb:ro
    
      dumpvdl2:
        image: ghcr.io/sdr-enthusiasts/docker-dumpvdl2:latest
        container_name: dumpvdl2
        restart: unless-stopped
        environment:
          - TZ=${FEEDER_TZ}
          - RTL_SERIAL=${VDL2_SDR_SERIAL}
          - FEED_ID=${ACARS_STATION_ID}-VDL2
          - GAIN=40
          - FREQUENCIES=136.650;136.700;136.800;136.975
          - ZMQ_MODE=server
          - ZMQ_ENDPOINT=tcp://0.0.0.0:45555
        tmpfs:
          - /run:exec,size=64M
          - /var/log
        device_cgroup_rules:
          - "c 189:* rwm"
        volumes:
          - /dev/bus/usb:/dev/bus/usb:ro
    
      acars_router:
        image: ghcr.io/sdr-enthusiasts/acars_router:latest
        container_name: acars_router
        restart: unless-stopped
        ports:
          - 5556:5556/udp          # HFDL arrives here from the hf computer
        environment:
          - TZ=${FEEDER_TZ}
          - AR_OVERRIDE_STATION_NAME=${ACARS_STATION_ID}
          - AR_ENABLE_DEDUPE=true
          - AR_RECV_ZMQ_VDLM2=dumpvdl2:45555
          - AR_SKEW_WINDOW=30
          - AR_SEND_UDP_ACARS=feed.airframes.io:5550
          - AR_SEND_TCP_VDLM2=feed.airframes.io:5553
          - AR_SEND_TCP_HFDL=feed.airframes.io:5556
        tmpfs:
          - /run:exec,size=64M
          - /var/log:size=64M
    
      acarshub:
        image: ghcr.io/sdr-enthusiasts/docker-acarshub:latest
        container_name: acarshub
        restart: unless-stopped
        ports:
          - 8081:80
        volumes:
          - /opt/station/acars_data:/run/acars
        tmpfs:
          - /database:exec,size=64M
          - /run:exec,size=64M
          - /var/log:size=64M
        environment:
          - TZ=${FEEDER_TZ}
          - ENABLE_ACARS=true
          - ACARS_CONNECTIONS=zmq://acars_router:45550
          - ENABLE_VDLM=true
          - VDLM_CONNECTIONS=zmq://acars_router:45555
          - ENABLE_HFDL=true
          - HFDL_CONNECTIONS=zmq://acars_router:45556
          - ENABLE_ADSB=true
          - ADSB_URL=http://ultrafeeder/data/aircraft.json
          - ADSB_LAT=${FEEDER_LAT}
          - ADSB_LON=${FEEDER_LONG}
    
  3. Start and check.

    docker compose up -d
    docker logs acarsdec 2>&1 | grep "Starting:"        # must show --airspy <SER_R2>
    docker logs dumpvdl2 2>&1 | grep -i "detected"     # a V4L must be named here
    

    The message log is at http://vhf:8081/. Near a large airport, expect the first VDL2 messages in minutes. ACARS is slower and comes in bursts.

  4. Find your station on https://app.airframes.io by its ID.

Facts to know:

  • Frequencies. These are the airframes.io lists for North America. An RTL radio covers about 2 MHz, so every channel of one decoder must fit in 2 MHz. The Airspy R2 covers more: I add 129.125 MHz to the ACARS list on mine.10 Always include 136.975 MHz, the worldwide calling channel for VDL2. Other regions use other lists.
  • AR_SKEW_WINDOW=30. The default is 5 seconds. HFDL messages arrive later than that and would be dropped.
  • Gain on an Airspy. I run acarsdec with the container’s default gain and it works. The decoder’s own README gives the Airspy a different gain scale (0 to 21). I did not compare the two.
  • After you unplug the hub, run docker restart acarsdec. On my station the two Airspy radios come back in a race, and acarsdec can hold the wrong one. Then both feeds stop.

11. Phase 6: prepare hf and the receiver

Receiver program: ka9q-radio. Its radiod service reads the whole of shortwave from the RX-888 Mk II and gives each decoder its own channel over the computer’s internal network. Nothing here uses Docker.

The project changes every week, and some of its documents are behind the code. These steps build the same version that my station runs (commit b5bbb9aa, 2026-09-25). The project now also offers ready-made packages for Ubuntu 24.04. I have not used them.

  1. Install Ubuntu Server 24.04 LTS. Set the host name to hf. Turn SSH on. Use wired Ethernet.

  2. In the PC’s firmware settings, set it to start again after a power cut. A UPS rides through a short cut; this setting covers a long one.

  3. Add the author’s package source. Two of the libraries are only there.

    sudo wget -P /usr/share/keyrings http://www.ka9q.net/debian/ka9q-radio.asc
    echo "deb [signed-by=/usr/share/keyrings/ka9q-radio.asc arch=amd64] http://www.ka9q.net/debian noble main" \
      | sudo tee /etc/apt/sources.list.d/ka9q.list
    sudo apt update
    
  4. Install the build tools and libraries (the list is from the project’s docs/INSTALL.md).

    sudo apt install -y git avahi-utils build-essential make gcc libairspy-dev libairspyhf-dev \
      libbsd-dev libfftw3-dev libfftw3-bin libhackrf-dev libiniparser-dev libncurses-dev libopus-dev \
      librtlsdr-dev libusb-1.0-0-dev libusb-dev portaudio19-dev libasound2-dev rsync libogg-dev \
      libsamplerate0-dev libliquid-dev libncursesw5-dev libbladerf-dev libhydrasdr-dev libfobos-dev \
      libcsv-dev libsoapysdr-dev chrony
    
  5. Build and install.

    cd ~ && git clone https://github.com/ka9q/ka9q-radio.git && cd ka9q-radio
    git checkout b5bbb9aa
    make -j && sudo make install
    sudo adduser $(whoami) radio
    sudo systemctl enable --now set_lo_multicast
    

    The install creates the user and group radio, the service files, and the rules that load the receiver’s firmware when you plug it in. Log out and in again so the new group applies.

  6. Connect the chain from section 4. Plug the RX-888 Mk II into a USB 3.0 socket on the PC. Check it.

    lsusb | grep 04b4          # expect 04b4:00f1 "RX888mk2". 00f3 means the firmware did not load
    lsusb -t | grep -B2 usbfs  # the receiver's line must end in 5000M
    
  7. Write the receiver settings. Start from the project’s example.

    sudo cp ~/ka9q-radio/config/examples/radiod@rx888-generic.conf /etc/radio/radiod@hf.conf
    sudoedit /etc/radio/radiod@hf.conf
    

    Set these lines in the first two sections. Leave the rest as it is for now.

    [global]
    hardware = rx888
    status = hf.local
    
    [rx888]
    device = "rx888"
    description = "<STATION_ID>-HFDL"
    samprate = 64m8
    gain = 10
    

    Always write samprate = 64m8. One of the project’s default files uses the full rate, which makes more heat and needs a faster PC. The description text is sent to airframes.io as your HFDL station name.

  8. Make the speed tables for this PC. This took 36 minutes on my Core i5-4570. radiod must not run during it.

    cd /etc/fftw && sudo touch nwisdom && sudo chown $(whoami):radio nwisdom wisdomf 2>/dev/null
    time fftwf-wisdom -v -T 1 -o nwisdom rof1620000 cob1920 cob1200 cob960 cob800 cob600 \
      cob480 cob320 cob300 cob200 cob160 cob150
    ls -l nwisdom && cp nwisdom wisdomf
    

    Do not leave out -T 1. Do not copy the file from another computer.

  9. Start the receiver and look at it.

    sudo systemctl enable --now radiod@hf
    systemctl is-active radiod@hf        # expect "active"
    control hf.local                     # the live panel. Press q to leave
    

    The service name is radiod@hf, not radiod@hf.conf.

  10. Set the gain. In the panel, read the level of the analog-to-digital converter. The project’s rule is an average of -20 to -25 dBFS. I run 10 dB of gain, which gives about -32 dBFS here and leaves room for strong stations at night. A count of “overranges” that keeps rising means too much gain.

  11. Test that the antenna, not the receiver, sets the noise. Note the noise level on a quiet frequency. Replace the antenna cable with the 50 ohm terminator. The noise must fall by 6 dB or more. On my station it fell by 30 to 40 dB. If it does not fall on some band, an amplifier may help on that band, and a filter will not.

  12. Keep the receiver in its case, and keep air around it. It runs hot.

12. Phase 7: aircraft over the oceans (HFDL)

Decoder: dumphfdl. The hfdl service of ka9q-radio starts one copy for each of 12 bands.

  1. Build dumphfdl. Ubuntu 24.04 has no package for it.

    sudo apt install -y cmake pkg-config libglib2.0-dev libconfig++-dev libacars2-dev
    cd ~ && git clone https://github.com/szpajder/dumphfdl.git && cd dumphfdl
    git checkout v1.7.0
    mkdir build && cd build && cmake ../ && make && sudo make install
    dumphfdl --version                   # expect 1.7.0
    
  2. Give radiod the 12 HFDL channels. In /etc/radio/radiod@hf.conf, add the line disable = yes to the section [HFDL]. Then add the project’s HFDL file at the end.

    cat ~/ka9q-radio/config/examples/fragments/hfdl.conf | sudo tee -a /etc/radio/radiod@hf.conf >/dev/null
    sudo systemctl restart radiod@hf
    
  3. Make the helper send each message to the router on vhf, not straight to airframes.io. Work on a copy, so a later install does not undo it.

    sudo cp /usr/local/sbin/start-hfdl /usr/local/sbin/start-hfdl-local
    sudo sed -i 's/decoded:json:tcp:address=feed.airframes.io,port=5556/decoded:json:udp:address=vhf.local,port=5556/' \
      /usr/local/sbin/start-hfdl-local
    grep -c "udp:address=vhf.local" /usr/local/sbin/start-hfdl-local     # expect 1
    

    If hf cannot find the name vhf.local, use the fixed address of vhf.

  4. Point the service at the copy. Run sudo systemctl edit hfdl and enter these three lines.

    [Service]
    ExecStart=
    ExecStart=/usr/local/bin/pcmrecord --raw -v --exec "/usr/local/sbin/start-hfdl-local \"$k\" \"$d\" \"$r\"" ${MCAST}
    
  5. Set the decoder’s location and give it the ground-station table. In /etc/radio/hfdl.conf, change the DUMPHFDL line to DUMPHFDL=/usr/local/bin/dumphfdl.

    sudo install -D -o radio -g radio -m 664 ~/dumphfdl/etc/systable.conf /var/lib/hfdl/systable.conf
    sudo systemctl enable --now hfdl
    tail -f /var/log/hfdl.log
    
  6. Check on vhf: the HFDL list of ACARS Hub fills. My first message came 20 minutes after the first start, with the loop indoors. Low bands carry traffic at night and high bands by day.

To skip the router, leave out steps 3 and 4. The messages then go to airframes.io only.

13. Phase 8: amateur stations (FT8 and FT4)

Decoder: decode_ft8 from ka9q/ft8_lib. Uploader: ftlib-pskreporter. The method is in the project’s docs/ft8.md.

  1. Build the decoder.

    cd ~ && git clone https://github.com/ka9q/ft8_lib.git && cd ft8_lib
    make && sudo make install
    ls /usr/local/bin/decode_ft8
    
  2. Give radiod the channels. In /etc/radio/radiod@hf.conf, add disable = yes to the section [FT8]. Then add the two files.

    cd ~/ka9q-radio/config/examples/fragments
    cat ft8.conf ft4.conf | sudo tee -a /etc/radio/radiod@hf.conf >/dev/null
    
  3. In the lines you added, and in the section [WSPR], delete every frequency above 30 MHz (50.310, 50.318, 50.293 and 144.489 MHz). The receiver cannot hear them at this rate. It would report false stations.

  4. Start the recorders and decoders.

    sudo systemctl restart radiod@hf
    sudo systemctl enable --now ft8-record ft4-record ft8-decode@1 ft4-decode@1
    tail -f /var/log/ft8.log             # new lines every 15 seconds on an open band
    
  5. Optional: report what you hear to PSK Reporter.

    cd ~ && git clone https://github.com/pjsg/ftlib-pskreporter.git && cd ftlib-pskreporter
    sudo apt install -y python3-docopt
    sudo install -m 755 pskreporter-sender /usr/local/bin/
    sudo install -m 644 pskreporter.py /usr/local/lib/python3.12/dist-packages/
    sudo cp pskreporter@.service /etc/systemd/system/
    sudo cp ft8-pskreporter.conf ft4-pskreporter.conf /etc/radio/
    sudoedit /etc/radio/ft8-pskreporter.conf /etc/radio/ft4-pskreporter.conf     # CALLSIGN, LOCATOR, ANTENNA
    sudo systemctl daemon-reload && sudo systemctl enable --now pskreporter@ft8 pskreporter@ft4
    sudo ss -tnp | grep 4739             # an open connection means it uploads
    

Facts to know:

  • No licence is needed to report. PSK Reporter says a listener can pick a name with the country prefix, such as W/SWL/BOSTON. I hold a licence and report with my callsign and a number after a dash. That keeps the receiver apart from my transmitting station.
  • The locator is public and needs 6 characters. That places you within a few kilometers. The antenna text is public too.
  • The reports carry no signal strength. The decoder’s number is a score, not a measurement.
  • The recorders write short sound files all day. That is why hf has a solid-state drive.

14. Phase 9: low-power beacons (WSPR)

Decoder: wsprd, from the WSJT-X package. ka9q-radio’s wspr-decoded service records two minutes on each band and runs it.

  1. Install the decoder.

    sudo apt install -y wsjtx
    ls /usr/bin/wsprd
    
  2. Make the two names agree. Find the data = line in the section [WSPR] of /etc/radio/radiod@hf.conf. Put the same name after MCAST= in /etc/radio/wspr-decode.conf. On my station they did not match after the install, and nothing was decoded.

  3. Start it.

    sudo systemctl enable --now wspr-decoded
    grep -v DecodeFinished /var/log/wspr.log | tail      # lines with a time, a level in dB, a frequency, a callsign
    

    Expect about 10 stations an hour at night on an indoor loop, and far more outdoors.

  4. To report: ftlib-pskreporter has a wspr-pskreporter.conf that sends these to PSK Reporter in the same way as step 5 of section 13. I have not tested it. For reports to WSPRnet, the usual public program is wsprdaemon. It installs and manages its own copy of ka9q-radio, so do not add it to this build without a plan. I upload with a small program of my own.

15. Other signals: what I receive with my own programs

On the same receiver I also decode the signals below. I wrote those programs for my own station and they are not public. Each one asks ka9q-radio for one more channel and reads the sound from it. This table names a free public program for each job, and says so where I found none that runs without a screen.

Signal Frequencies Free public program Limits
Weather fax, US Coast Guard station NMC 4346, 8682, 12786, 17151.2, 22527 kHz. Tune 1.9 kHz lower, upper sideband fldigi (WEFAX mode), or Isobar, which decodes a recording from the command line fldigi needs a screen and a sound input. Isobar is a new project, and I have not tested it
NAVTEX warnings for ships 518 kHz fventuri/navtex, which reads a sound stream with no screen, or fldigi Mostly a night signal here. Keep the F3060 shelf filter bypassed
SSTV pictures from amateurs 14.230, 21.340, 28.680 MHz QSSTV Needs a screen. colaclanth/sstv decodes one recording at a time, in fewer modes
DSC distress and calling 2187.5, 4207.5, 6312, 8414.5, 12577, 16804.5 kHz SDRangel, or YaDD on Windows I found no free public decoder for Linux that runs without a screen
NCDXF/IARU beacons 14.100, 18.110, 21.150, 24.930, 28.200 MHz Faros, on Windows, $25 after a 30-day trial ka9q-radio includes a channel file for these (ibp.conf), but no program that measures them
US Air Force voice messages (HFGCS) 4724, 8992, 11175, 15016 kHz, upper sideband. Hobby lists add 6739 and 13200 pcmrecord from ka9q-radio to record, then whisper.cpp or faster-whisper to make text I found no finished public program that does both
The sun’s effect on very low frequencies 24.8 kHz (NLK) and other navy transmitters SuperSID, with NOAA’s X-ray data to compare SuperSID reads a sound card. I did not confirm that it can read an SDR
One screen for the whole station Homepage with docker-socket-proxy (read-only) shows which containers are healthy Never open the proxy to the internet

16. Final checks

Run these after the last phase. Each line is one feed.

Feed Check on Command or page Pass
Aircraft vhf docker ps --filter name=ultrafeeder (healthy)
Aircraft browser http://vhf:8080/ Aircraft on the map
Alerts Discord The screenshot test in section 8 A picture with a map
Ships browser http://vhf:90/ Ships on the map
Ships vhf docker logs shipfeeder 2>&1 | grep "Searching for device" Names <SER_MINI>
ACARS, VDL2 browser http://vhf:8081/ New messages in both lists
HFDL browser http://vhf:8081/ Messages in the HFDL list. Night and day use different bands
Receiver hf systemctl is-active radiod@hf active
FT8, WSPR hf tail /var/log/ft8.log /var/log/wspr.log New lines every 15 seconds and every 2 minutes
USB both lsusb -t The RX-888 at 5000M. No radio missing

Then write down the message rate of every feed for one normal day. That number is your baseline. Change one thing at a time, and compare with it.

17. Known traps

Each of these cost me time.

Symptom Cause What to do
usb_claim_interface error -6, “Device or resource busy” The kernel’s TV driver took an RTL radio Check step 5 of section 6. Restart the computer
The ship feed stops when the ACARS decoder starts, or the reverse Two Airspy radios with one USB identity, and a decoder that opened “the first one” Give every container its radio’s serial number. Never leave one unset
After the hub was unplugged: no ACARS, and the ship log repeats “cannot open device” acarsdec holds the wrong Airspy docker restart acarsdec
An RTL-SDR Blog V4L opens, the container is “healthy”, and it decodes nothing A driver older than August 2026 does not know its tuner docker compose pull && docker compose up -d. The log must name the V4L
Fewer messages after you add an amplifier The strong signals now overload the radio, and the weak ones are lost Remove it. On my station a 20 dB amplifier cut ACARS from 95 to 42 messages an hour
AirNav Radar or the ShipXplorer key program stops at once on a Pi 5 Those programs need 4 kB memory pages. getconf PAGE_SIZE shows 16384 Add kernel=kernel8.img to /boot/firmware/config.txt and restart. For ShipXplorer, the UDP method avoids this
Plane Finder fails on a Pi 5 Same kind of problem Use the image tag 5.0.161_arm64 (sdr-enthusiasts guide)
A new FlightAware or OpenSky station appears at every start The feeder ID or serial is not in the compose file Save it in the file after the first run
The alert picture is a grey map The map tiles had not loaded Raise LOAD_SLEEP_TIME
Shortwave is full of signals that should not be there No 30 MHz low-pass filter, so signals above 32.4 MHz fold back Fit the filter (section 4)
radiod reports dropped data The RX-888 is on a hub or a USB 2 socket Use a USB 3.0 socket on the main board
The loop has no signal, or a radio behind the splitter runs hot DC is wrong: the limiter blocks it, or a bias tee is on Injector first on the loop cable. Bias tee off everywhere else
NAVTEX and the low bands are weak The F3060 shelf filter is on Bypass it
A decoder for 22.5 kHz hears nothing In ka9q-radio, 22500 means 22.5 MHz Write 22k5

18. Law and manners

  • I am not a lawyer. This is what I read, for the United States. Other countries differ.
  • US law (47 U.S.C. section 605) says that a person who receives a radio message not meant for the public must not tell others its contents, or use it for gain. It makes exceptions for broadcasts to the public, for amateur and CB radio, and for messages about ships, aircraft or people in distress.
  • So I show that a message arrived, and what kind it was. I do not publish the text of real aircraft messages.
  • Each network has terms for the data it gives back to you. Read them before you republish a map or a list.
  • A feeder station is a small public service. Keep it running, keep its clock right, and keep its position honest.

19. Sources

Every repository and page that this guide depends on. All were read on 2026-10-07.

Host preparation. sdr-enthusiasts guide: https://sdr-enthusiasts.gitbook.io/ads-b. Install script: https://github.com/sdr-enthusiasts/docker-install.

Aircraft. https://github.com/sdr-enthusiasts/docker-adsb-ultrafeeder · https://github.com/sdr-enthusiasts/docker-piaware · https://github.com/sdr-enthusiasts/docker-flightradar24 · https://github.com/sdr-enthusiasts/docker-planefinder · https://github.com/sdr-enthusiasts/docker-airnavradar · https://github.com/sdr-enthusiasts/docker-opensky-network · https://github.com/plane-watch/docker-plane-watch

Alerts. https://github.com/sdr-enthusiasts/docker-planefence · https://github.com/sdr-enthusiasts/plane-alert-db · https://github.com/kx1t/browser-screenshot-service

Ships. https://github.com/sdr-enthusiasts/docker-shipfeeder · https://github.com/jvde-github/AIS-catcher

Aircraft messages. https://github.com/sdr-enthusiasts/docker-acarsdec · https://github.com/f00b4r0/acarsdec · https://github.com/sdr-enthusiasts/docker-dumpvdl2 · https://github.com/szpajder/dumpvdl2 · https://github.com/sdr-enthusiasts/acars_router · https://github.com/sdr-enthusiasts/docker-acarshub · https://docs.airframes.io

Shortwave. https://github.com/ka9q/ka9q-radio (read docs/INSTALL.md, docs/SDR/rx888.md, docs/FFTW3.md, docs/hfdl.md, docs/ft8.md) · https://github.com/szpajder/dumphfdl · https://github.com/ka9q/ft8_lib · https://github.com/pjsg/ftlib-pskreporter · https://github.com/rrobinett/wsprdaemon · Heat in the RX-888: https://ka7oei.blogspot.com/2023/08/improving-thermal-management-of-rx-888.html

Parts. The maker or seller page linked from each row of section 2. Filter figures: the Turn Island F3060 manual and the TAPR RF Limiter manual.

Rules. 47 U.S.C. 605: https://www.law.cornell.edu/uscode/text/47/605. Weather fax schedule: https://www.weather.gov/media/marine/rfax.pdf. NAVTEX: https://www.navcen.uscg.gov/navtex-maritime-safety-broadcasts. DSC frequencies: https://www.law.cornell.edu/cfr/text/47/80.359. Beacons: https://www.ncdxf.org/beacon/.

What I could not confirm

  • That 4 GB is enough for vhf. I measured an 8 GB Pi 5.
  • The connector of the low-cost AIS antenna, and its price on the day. Read the listing.
  • That the Mini-Circuits splitter passes or blocks DC. Its data sheet does not say. So keep every bias tee off.
  • The best gain setting for acarsdec on an Airspy R2.
  • The Airspy prices at the maker. Its site did not answer that day, so the prices are the US distributor’s.
  • The price of the RX-888 Mk II. I found no seller page I could read. The maker warns of copies and names the sellers it trusts: read https://www.rx-888.com before you buy.
  • That the newest ka9q-radio builds with these exact steps. I built the commit named in section 11.
  • The ready-made ka9q-radio packages, the WSPR path to PSK Reporter, Isobar, and every program in section 15. I have not run them.
  • The rule of WSPRnet for a listener with no callsign. Its site did not answer.
  • No step in this guide was run on a new, empty computer as written. The settings are the ones on my station, checked against each project’s documents.

Footnotes

  1. My aircraft radio is on a Raspberry Pi 4 with FlightAware’s own PiAware system. A small server (an Intel Atom E3950 with 16 GB) runs ultrafeeder, the other feeder containers and planefence, and reads the Pi 4 over the network. A Raspberry Pi 5 with 8 GB runs the ship and message containers. An HP EliteDesk 800 G1 runs the shortwave receiver. I built it in that order, one part at a time. One Pi 5 for all of the first three is simpler, and section 3 gives the numbers I have for it. ↩

  2. I started with a small whip indoors, then put a low-cost AIS-band vertical on the roof. The rate went up about six times. A marine-grade antenna is a later step, if at all. ↩

  3. My loop is still indoors, near the computer, and that is the worst place for it. I own a second one as a spare. The amplifier in the loop head is the part most open to static. ↩

  4. I added it and the message rate did not change. I left it in as cheap protection. On the wide discone, the FM filter is not optional. ↩

  5. I bought mine from a surplus dealer. I have not measured what the split costs me in messages. ↩

  6. I transmit on shortwave from the same property, at 5 to 20 watts. The limiter is there for that, and for static. If you never transmit, it is still cheap insurance for an expensive receiver. ↩

  7. For ships, ACARS and VDL2, three RTL-SDR radios do the job in place of the two Airspy radios: set RTL_SERIAL in place of AIRSPY_SERIAL for acarsdec, and leave SDR_TYPE unset for shipfeeder. I ran VDL2 on a Nooelec NESDR SMArt v5 for six weeks. I use the Airspy radios because I owned them. ↩ ↩2

  8. I use and recommend the RX-888 Mk II because it hears all of shortwave at once, so every decoder runs all day. A narrow radio hears one slice. An Airspy HF+ Discovery (768 kHz wide) can run dumphfdl on one HFDL band at a time. The dumphfdl README also calls the RTL-SDR Blog V4 a decent low-budget choice; I have not tried its replacement, the V4L, on shortwave. That is a real station, at a fraction of the cost. It is not the build in sections 11 to 14. ↩

  9. Mike Holt’s summary of NEC 810.21, 2020 edition: https://www.mikeholt.com/files/PDF/20BG_810.21.pdf. ↩

  10. My six ACARS channels run from 129.125 to 131.550 MHz. That is wider than 2 MHz, and it works on the Airspy R2. My VDL2 list has eight channels from 136.650 to 136.975 MHz, which includes channels that airframes.io lists for Europe. I have not checked which of the eight carry traffic here. ↩

SpurThis guide lists what I use. A part on the list worked for me in one place. It may not be the best part for your place.