1. What this guide builds
A receiving station for HFDL. HFDL means High Frequency Data Link: aircraft over oceans send position reports and messages by shortwave to ground stations. A free program, dumphfdl, decodes them. This guide gives two complete paths.
- Path A is the lowest-cost start. One tuned radio listens to one HFDL band at a time.
- Path B is what I use. One wideband receiver listens to all 12 HFDL bands at the same time.
| Path A: one band | Path B: every band ◆ | |
|---|---|---|
| Signal | HFDL, 2.9 to 21.9 MHz | The same |
| Antenna | Small active loop | Small active loop |
| Radio | One tuned SDR (Airspy HF+ Discovery, or an RTL-SDR Blog V4) | RX-888 Mk II |
| Decoder | One dumphfdl, in a Docker container | 12 copies of dumphfdl, one for each band |
| Computer | Raspberry Pi 5, or any 64-bit Linux computer | x86 desktop PC, plus a small computer for the message screen |
| Gives up | The other 11 bands. You change band by hand, or a script does it | Nothing. It costs more and needs more work |
Path A
loop -- coax -- power injector -- tuned SDR -- USB -- pi (Docker)
| dumphfdl container
| acars_router (optional)
| ACARS Hub (optional)
`-- airframes.io (optional)
Path B
loop -- coax -- power injector -- 30 MHz filter -- limiter -- RX-888 Mk II
| USB 3
hf (x86 PC, ka9q-radio)
12 band channels
12 x dumphfdl
| UDP, home network
pi (Docker)
acars_router -- ACARS Hub
`-- airframes.io (optional)
The two computers have these names in this guide: pi runs Docker, and hf runs the wideband receiver.
2. What you need
A part marked ◆ is what I use. Prices are US dollars from the named seller on 2026-10-07, as listed in my full station guide. I did not check them again for this guide. A blank box is for your pencil.
Path A: the lowest-cost path
| ☐ | Part | Job | Connector | Price | Note |
|---|---|---|---|---|---|
| ☐ | ◆ MLA-30+ active loop with its power injector | Antenna | SMA | See listing, Amazon | The amplifier is in the loop head |
| ☐ | Airspy HF+ Discovery | Radio, 768 kHz wide | See listing | See listing | dumphfdl is tested on the Airspy HF+ (its README). I do not own one |
| ☐ | or: RTL-SDR Blog V4L | Radio, the cheapest | See listing | $37.95, rtl-sdr.com | Read the caution below |
| ☐ | Raspberry Pi 5, or any 64-bit Linux computer | Computer pi |
USB | $175 list for 8 GB (4 GB: $110) | The container runs on amd64 and arm64 |
| ☐ | Raspberry Pi 27 W USB-C power supply, and a microSD card of 32 GB or more | Power and storage | See seller | ||
| ☐ | RG-6 quad-shield coax, and two F to SMA adapters | Loop to injector to radio | F and SMA | See seller | On shortwave, 10 to 20 m of it loses under 1 dB |
| ☐ | Snap-on ferrites | Both ends of the loop cable, and the USB cable | See seller |
Caution. A low-cost RTL-SDR radio is the weakest choice for shortwave. The dumphfdl README says normal RTL-SDR radios are not suitable. It calls the RTL-SDR Blog V4 “a decent low-budget option”. The container’s README says V4 radios “work but are not optimal for HF”. The V4 is no longer made. I did not try its replacement, the V4L, on shortwave. Buy the Airspy HF+ Discovery if you can.
Path B: what I use
| ☐ | Part | Job | Price | Note |
|---|---|---|---|---|
| ☐ | ◆ MLA-30+ active loop with its power injector | Antenna | See listing, Amazon | The same loop as path A |
| ☐ | ◆ Turn Island Systems F3060 low-pass filter | In front of the receiver | $80, Turn Island Systems | Required at 64.8 MS/s |
| ☐ | ◆ TAPR RF Limiter | Last part before the receiver | $20, TAPR | Needed if you also transmit |
| ☐ | ◆ 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 |
| ☐ | ◆ x86-64 desktop PC with a USB 3.0 socket on the main board | Computer hf |
Used market | Mine is a Core i5-4570 from 2013, bought used |
| ☐ | ◆ Raspberry Pi 5, 8 GB | Computer pi: the message screen |
$175 list | Optional. Section 9 says what you lose without it |
| ☐ | RG-6 quad-shield coax, two F to SMA adapters, short SMA jumpers, ferrites | The chain in section 4 | See seller |
The receiver itself (the PC, ka9q-radio, the gain, the noise test) has its own guide: /guides/shortwave-receiver/. Build that first for path B. This guide then adds everything that is specific to HFDL.
3. Before you start
Skills. You type commands in a terminal on a Linux computer. You edit a text file. Each step gives the exact text.
Time. I did not time either path. In path B, my first message came 20 minutes after the first start.
Accounts. You need none to receive. To share, you need a free account at https://app.airframes.io.
Values to choose first. The steps use them as placeholders.
| Placeholder | Meaning | Example form |
|---|---|---|
<TZ> |
Time zone, TZ database name | America/Los_Angeles |
<STATION_ID> |
A station name with no personal detail. The airframes.io form is XX-YYYY: two letters, then the code of the nearest large airport |
XX-YYYY |
dumphfdl does not need your position, and this guide never asks for it.
The law, for the United States. I am not a lawyer. You only receive: this guide has no step that transmits. 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. So I show that a message arrived and what kind it was. I do not publish the text of real aircraft messages. Other countries differ.
4. The antenna and the radio-frequency chain
HFDL uses 12 bands from 2.9 to 21.9 MHz. One wideband receive antenna covers them all. The dumphfdl README recommends a magnetic loop or a mini whip. I use a small active loop.
Build the chain in this order, antenna first.
Path A loop -- RG-6 -- power injector -- tuned SDR
Path B loop -- RG-6 -- power injector -- F3060 (30 MHz low-pass in,
shelf filter bypassed) -- RF limiter -- RX-888 Mk II, HF input
Caution. Direct current on the cable can damage a filter or a radio input. Put the power injector first on the loop cable. Keep the bias tee of the radio off.
- Put the loop outdoors if you can. Distance from the electronics in the house matters more than height.
- Run the coax from the loop to the power injector.
- Connect the radio side of the injector to the next part in the chain.
- Clip a ferrite on each end of the loop cable.
- Path B only: connect the F3060, then the limiter, then the receiver. Leave the F3060 shelf filter bypassed.
Why path B needs the 30 MHz filter: at 64.8 million samples each second, a signal from 32 to 65 MHz folds back onto shortwave. A tuned radio in path A has its own filters, so path A does not need it.
Warning. An outdoor antenna cable can carry a surge into the house. Fit a grounding block where the cable enters the building. Bond it to the building’s own grounding electrode system (in the US: National Electrical Code Article 810). Use a licensed electrician if you are not sure.
My own loop is still indoors, near the computer. That is the worst place for it, and it still decodes several thousand messages a day.
5. How HFDL uses its bands
Read this section before you set a frequency. It tells you where to listen.
The ground stations
The public list of ground stations is the HFDL system table. Version 52 has 16 stations. Each station has 6 to 19 frequencies. Each station sends a short beacon, called a squitter, on each frequency it uses at that time. The squitter names the station and the frequencies in use. An aircraft logs on to a station it can hear.
A reader in North America is most likely to hear the stations below. The frequencies are from system table version 52, in kHz. A frequency marked * is also used by another station.
| Station | Frequencies in kHz |
|---|---|
| San Francisco, California | 5508, 6559, 8927*, 10081*, 11327, 13276*, 17919*, 21934 |
| Molokai, Hawaii | 5514, 6565, 8912*, 8936*, 10027*, 11312, 11348*, 13276*, 13312*, 13324, 17919*, 21937* |
| Riverhead, New York | 5652, 6661, 8912*, 11387, 13276*, 17919*, 21931* |
| Barrow, Alaska | 2944, 2992, 3007, 3497, 4654, 4687, 5529*, 5538, 5544*, 6646*, 8927*, 8936*, 10027*, 10093, 11354, 17919*, 17934, 21928*, 21937* |
| Albrook, Panama | 5589, 6589, 8894, 10063, 13264, 17901 |
| Reykjavik, Iceland | 3900, 5720, 6712, 8977, 11184, 15025, 17985 |
What I measured in the Puget Sound, with an indoor loop: 61 % of my messages were on frequencies that only Molokai uses. 7 % were on Riverhead’s own frequencies. Reykjavik, Auckland, Hat Yai, Muan and Agana had about 1 % or less each. The nearest station is not always the one you hear best. A message on a station’s frequency can come from an aircraft that talks to that station. It does not prove that I heard the station itself.
The 12 bands
The channel lists are the ones that ka9q-radio gives to dumphfdl. Use them for both paths.
| Band | Channels in kHz | Width |
|---|---|---|
| 21 MHz | 21928 21931 21934 21937 21949 21955 21982 21990 21997 | 69 kHz |
| 17 MHz | 17901 17912 17916 17919 17922 17928 17934 17958 17967 17985 | 84 kHz |
| 15 MHz | 15025 | one channel |
| 13 MHz | 13264 13270 13276 13303 13312 13315 13321 13324 13342 13351 13354 | 90 kHz |
| 11 MHz | 11184 11306 11312 11318 11321 11327 11348 11354 11384 11387 | 203 kHz |
| 10 MHz | 10027 10030 10060 10063 10066 10075 10081 10084 10087 10093 | 66 kHz |
| 8 MHz | 8825 8834 8843 8885 8886 8894 8912 8921 8927 8936 8939 8942 8948 8957 8977 | 152 kHz |
| 6 MHz | 6529 6532 6535 6559 6565 6589 6596 6619 6628 6646 6652 6661 6712 | 183 kHz |
| 5 MHz | 5451 5502 5508 5514 5529 5538 5544 5547 5583 5589 5622 5652 5655 5720 | 269 kHz |
| 4 MHz | 4654 4660 4681 4687 | 33 kHz |
| 3 MHz | 3455 3497 | 42 kHz |
| 2 MHz | 2941 2944 2992 2998 3007 3016 | 75 kHz |
Choose a band by time of day
The best frequency depends on the sun. As a general rule, the higher bands travel far in daylight, and the lower bands travel far at night. The real split changes with the season, the sun and the path.
| When, at your station | Try first | Then |
|---|---|---|
| Daylight | 13 MHz | 17 MHz, 11 MHz, 21 MHz |
| Around sunrise and sunset | 8 MHz | 10 MHz, 11 MHz |
| Night | 6 MHz | 8 MHz, 5 MHz |
The table is a starting point from the general rule and from my own counts. In about ten days, 47 % of my messages came in the 13 MHz band, 25 % in the 6 MHz band, and 9 % in the 8 MHz band. The 2, 3 and 4 MHz bands gave me no message at all in that time. The scanner of the container in section 7 also leaves out the bands below 5 MHz, “due to very low activity”.
The airframes.io documents name https://hfdl.observer as a place to see the frequencies in use now. I did not read that site for this guide.
6. Path A: prepare the computer
A Docker container is a packaged program with everything it needs. You do not build dumphfdl or install radio drivers by hand. These steps are for a Raspberry Pi 5. On another Linux computer, start at step 3.
-
Write Raspberry Pi OS Lite (64-bit) to the card with Raspberry Pi Imager. In the Imager settings, set the host name to
piand turn SSH on. -
Start the Pi on wired Ethernet. Log in with SSH.
-
Update the system.
sudo apt update && sudo apt full-upgrade -y && sudo reboot -
Install Docker with the sdr-enthusiasts script. It also stops the kernel’s TV drivers from taking an RTL radio.
bash <(curl -s https://raw.githubusercontent.com/sdr-enthusiasts/docker-install/main/docker-install.sh) sudo reboot -
Check Docker.
docker compose versionResult: one line with a version number.
-
Make the project folder.
sudo mkdir -p -m 777 /opt/station && cd /opt/station -
Create
/opt/station/.envwith your values.FEEDER_TZ=<TZ> STATION_ID=<STATION_ID> -
Connect the radio to a USB socket of the computer.
7. Path A: the decoder
Program: docker-dumphfdl, which runs dumphfdl.
-
Create
/opt/station/docker-compose.yml. This one is for the Airspy HF+ Discovery.services: dumphfdl: image: ghcr.io/sdr-enthusiasts/docker-dumphfdl:latest container_name: dumphfdl restart: always device_cgroup_rules: - "c 189:* rwm" environment: - TZ=${FEEDER_TZ} - SOAPYSDRDRIVER=driver=airspyhf - GAIN_TYPE=--gain - SOAPYSAMPLERATE=768000 - FEED_ID=${STATION_ID}-HFDL - ZMQ_MODE=server - ZMQ_ENDPOINT=tcp://0.0.0.0:45555 - DUMP_HFDL_COMMAND_EXTRA=--output decoded:text:file:path=- tmpfs: - /run:exec,size=64M - /var/log - /tmp volumes: - /dev:/dev:ro - /opt/station/dumphfdl-data:/opt/dumphfdl-data - /opt/station/dumphfdl-scanner:/opt/scannerWhat the lines do:
SOAPYSDRDRIVERnames the radio driver. With no serial number, the first radio of that type is used.GAIN_TYPE=--gainwith noGAINline leaves the gain to the radio’s automatic control.SOAPYSAMPLERATEis the width that the radio hears, in samples each second. 768000 is the highest rate of the Airspy HF+.FEED_IDis your station name in every message.DUMP_HFDL_COMMAND_EXTRAadds one more output: readable text in the container log.- The first volume keeps the system table. The second keeps the band that the scanner chose.
-
For an RTL-SDR Blog V4 or V4L, change two lines. The rate 250000 is the one in the dumphfdl README example. I did not confirm it for an RTL radio.
- SOAPYSDRDRIVER=driver=rtlsdr - SOAPYSAMPLERATE=250000 -
Start the container.
cd /opt/station && docker compose up -d docker logs -f dumphfdlResult: the log says “Running through test to determine best frequencies.” Press Ctrl and C to leave the log. The container keeps running.
What the container does by itself
With no frequency list, the container runs a scanner. The scanner listens to 8 groups of channels, 90 seconds each. It scores each group by the messages it heard. Then it stays on the best group. A full scan takes about 13 minutes.
After 30 minutes the container counts messages over the last 30 minutes. Below an average of 5, it scans again. So the container follows the bands from day to night with no help.
To scan again now:
docker exec -it dumphfdl /reset-dumphfdl.sh
Set a band by hand
-
Choose a band from section 5.
-
Add one line to the
environmentlist. The channels are in kHz, with spaces between them. This example is the 13 MHz band.- FREQUENCIES=13264 13270 13276 13303 13312 13315 13321 13324 13342 13351 13354 -
Apply the change.
docker compose up -d docker logs dumphfdl 2>&1 | grep "Frequencies were supplied"Result: one line, “Frequencies were supplied, skipping test.”
Rule for the channel list: every channel must fit inside about 80 % of the sample rate. At 768000 that is 614 kHz, so every band in section 5 fits. At 250000 it is 200 kHz. The 5 MHz and 11 MHz bands are wider than that. Leave out the channels at the edges (5451 and 5720, or 11184).
8. Path A: the first signal
-
Wait for the scanner to finish, or set a band by hand.
-
Watch the log.
docker logs -f dumphfdl -
Wait up to 30 minutes on a band that fits the time of day.
Result: blocks of text appear, one for each message. Each block starts with a line in square brackets: the time, the frequency in kHz, and the speed in bps. Section 10 explains one block.
If the scan ends with “No messages, repeating scan.”, go to section 15.
9. Path B: every band at once
Build the receiver first: /guides/shortwave-receiver/. At the end of that guide, radiod@hf is active on the computer hf, and the gain is set. The steps below are the ones on my station. They build dumphfdl 1.7.0 and use ka9q-radio at commit b5bbb9aa.
The hfdl service of ka9q-radio starts one copy of dumphfdl for each of the 12 bands. radiod gives each copy its own band as a stream of samples.
Your station name becomes public in step 6. As installed, the ka9q-radio helper sends every message straight to airframes.io. Steps 3 and 4 change that: the messages then go to your own router, and section 11 decides what leaves the house. Do steps 3 and 4 before step 6 if you do not want to share.
-
Build dumphfdl on
hf. 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 -
Give
radiodthe 12 HFDL channels. In/etc/radio/radiod@hf.conf, add the linedisable = yesto 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@hfResult: 12 new sections,
[HFDL21]to[HFDL2]. Each one is a band, with its center frequency and its width. All 12 send to one group on the computer’s internal network,hfdl.local. -
Make the helper send each message to the router on
pi, 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=pi.local,port=5556/' \ /usr/local/sbin/start-hfdl-local grep -c "udp:address=pi.local" /usr/local/sbin/start-hfdl-local # expect 1If
hfcannot find the namepi.local, use the fixed address ofpi. -
Point the service at the copy. Run
sudo systemctl edit hfdland enter these three lines.[Service] ExecStart= ExecStart=/usr/local/bin/pcmrecord --raw -v --exec "/usr/local/sbin/start-hfdl-local \"$k\" \"$d\" \"$r\"" ${MCAST} -
Set your station name. It is the
descriptionline in the[rx888]section of/etc/radio/radiod@hf.conf. The helper gives that text to dumphfdl as the station name.description = "<STATION_ID>-HFDL"Then start the receiver again, so it reads the new line.
sudo systemctl restart radiod@hf -
Set the decoder’s location and give it the ground-station table. In
/etc/radio/hfdl.conf, change theDUMPHFDLline toDUMPHFDL=/usr/local/bin/dumphfdl. Then run: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.logResult: 12 lines that start with the helper’s name. Each one gives a center frequency, a sample rate and a list of channels. After that, one long line of JSON arrives for each message. JSON is a text format for programs.
-
Prepare
piwith section 6, steps 1 to 7. Then build the router and the message screen with section 11.
Path B with no second computer
Leave out steps 3, 4 and 7. Then every message goes to airframes.io, and to the file /var/log/hfdl.log as JSON. You have no message screen of your own. You cannot receive without sharing in this form.
10. Read one message
I never show a received message, because a real message can hold private details. I wrote this one myself. The flight, the aircraft number and the position do not exist.
EXAMPLE MESSAGE
[2026-01-01 12:00:00 UTC] [13324.0 kHz] [1.5 Hz] [-60.0/-75.0 dBFS] [15.0 dB] [300 bps] [S]
Downlink LPDU:
Src AC: 42
Dst GS: Molokai, Hawaii
Type: Unnumbered data
HFNPDU:
Type: Performance data
Flight ID: ZZ0000
Lat: 30.0000000
Lon: -140.0000000
The first line, from left to right (dumphfdl README):
| Field | Meaning |
|---|---|
[2026-01-01 12:00:00 UTC] |
The time the frame arrived |
[13324.0 kHz] |
The channel. In the table of section 5, only Molokai uses it |
[1.5 Hz] |
How far the signal was off the channel frequency |
[-60.0/-75.0 dBFS] |
Signal level, then noise level, relative to the full scale of the radio |
[15.0 dB] |
Signal-to-noise ratio: the first number minus the second |
[300 bps] |
The speed: 300, 600, 1200 or 1800 bits each second |
[S] |
The frame fills one time slot. D means two slots |
The lines below it:
- Downlink means from an aircraft to the ground. Uplink means from a ground station.
- Src AC is the aircraft. The number is a short ID that the ground station gave it at logon. It is not the aircraft’s registration.
- Dst GS is the ground station. You see a name here only with the system table (section 12). Without the table you see a number.
- Type: Performance data is an automatic report. It carries the flight identifier and the position.
Other kinds of block that you see often:
| Block | What it is |
|---|---|
Uplink SPDU |
A squitter: the beacon of a ground station, with the frequencies in use |
Logon request, Logon confirm |
An aircraft joins a ground station |
Frequency data |
An aircraft reports which stations it can hear |
A block with ACARS |
A text message, the same kind as on VHF |
The dumphfdl README shows a full squitter. Mine is a simplified layout: a real block has more lines.
11. A message screen, and sharing with airframes.io
All of this section is optional. Two containers run on pi. acars_router takes in the messages, removes duplicates and sends them on. ACARS Hub stores them and shows them in a browser.
Sharing makes your station name public. airframes.io shows
<STATION_ID>to everyone, with the messages you send. It also sees the internet address of your house. Choose a name that does not name you or your street. The lineAR_SEND_TCP_HFDLis the only one that sends data out of your house. Leave it out to keep everything at home.
-
Add the two services to
/opt/station/docker-compose.ymlonpi, underservices:.acars_router: image: ghcr.io/sdr-enthusiasts/acars_router:latest container_name: acars_router restart: unless-stopped ports: - 5556:5556/udp # path B: HFDL arrives here from the hf computer environment: - TZ=${FEEDER_TZ} - AR_ENABLE_DEDUPE=true - AR_SKEW_WINDOW=30 - AR_RECV_ZMQ_HFDL=dumphfdl:45555 # path A only - AR_SEND_TCP_HFDL=feed.airframes.io:5556 # sharing: optional 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: - 80: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_HFDL=true - HFDL_CONNECTIONS=zmq://acars_router:45556 -
Path A: keep the line
AR_RECV_ZMQ_HFDLand delete the twoportslines ofacars_router. -
Path B: delete the line
AR_RECV_ZMQ_HFDL. Keep theportslines. -
Start the containers.
cd /opt/station && docker compose up -d -
Open
http://pi/in a browser on your home network.Result: the ACARS Hub page. New HFDL messages appear at the top as they arrive.
-
To share: create an account at https://app.airframes.io. Find your station by its name and claim it. Do this from your home network: the ka9q-radio document says the claim must come from the address that sends the data.
Facts to know:
AR_SKEW_WINDOW=30. The default is 5 seconds. The router rejects a message that is older than this. Its README says HFDL needs a larger value. 30 is the value on my station.- Share one time only. In path B, do not keep the airframes.io output in the helper and also set
AR_SEND_TCP_HFDL. Each message would then arrive two times. - Do not send HFDL positions to an aircraft tracking network. The dumphfdl README says this in strong words. HFDL positions are late, and they make a mess on a map of live aircraft.
12. The system table
The system table is the list of ground stations: number, name, position and frequencies. A message carries only numbers. With the table, dumphfdl prints “Molokai, Hawaii” in place of a number, and kHz in place of a frequency number.
The table changes from time to time. The network then raises its version number. Every squitter carries the current number, in the line System table version.
How both paths keep it current. dumphfdl runs with two options that name the same file: --system-table reads the file at the start, and --system-table-save writes a new version to it. Ground stations send the full table over the air when an aircraft asks for it. When dumphfdl receives all parts of a newer table, it replaces the file. You do nothing.
| Path A | Path B | |
|---|---|---|
| The file | /opt/station/dumphfdl-data/systable.conf on pi |
/var/lib/hfdl/systable.conf on hf |
| Where it first comes from | The container downloads it from the dumphfdl repository if the file is missing | You copied it in section 9, step 6 |
-
Read the version of your file. Use the path for your build.
head -1 /var/lib/hfdl/systable.confResult:
version = 52;or a higher number. On 2026-10-10 the dumphfdl repository and the ka9q-radio repository both held version 52. -
Compare it with the number in a new squitter. If the squitter has a higher number, wait. The new table arrives when an aircraft asks for it.
-
After an update, look for a station with no name. A table from the air has positions but no names. dumphfdl copies each name from the old table when the station number and the position match. A new station has no name: open the file in a text editor and add one.
Do not change the frequencies in the file by hand. The file must match the table that the network uses.
13. Run it unattended
Path A. The line restart: always starts the container again after an error and after a restart of the computer. To get a newer version of the container:
cd /opt/station && docker compose pull && docker compose up -d
Path B. sudo systemctl enable --now hfdl in section 9 made the service start with the computer. Its service file starts it again 5 seconds after an error.
-
In the PC’s firmware settings, set it to start again after a power cut.
-
Check that the log is rotated. The file
/var/log/hfdl.loggrows all day.ls /etc/logrotate.d/ | grep hfdlResult: one file name. ka9q-radio has a rule file that keeps 30 daily logs. If the command prints nothing, copy
~/ka9q-radio/aux/hfdl.rotateto/etc/logrotate.d/. -
Write down the number of messages for one normal day. That number is your baseline. Change one thing at a time, and compare with it.
14. Check that it works
| # | Check | Command or page | Pass | Wait |
|---|---|---|---|---|
| 1 | Path A: the container runs | docker ps --filter name=dumphfdl |
Status “Up” | 1 minute |
| 2 | Path A: the scanner chose a band | docker logs dumphfdl 2>&1 | grep wins |
One line with a group name, such as 11M13Mx2 wins |
About 13 minutes |
| 3 | Path A: messages arrive | docker logs -f dumphfdl |
Text blocks as in section 10 | Up to 30 minutes |
| 4 | Path B: the service runs | systemctl is-active hfdl |
active |
At once |
| 5 | Path B: 12 decoders run | pgrep -c dumphfdl |
12 |
1 minute |
| 6 | Path B: messages arrive | tail -f /var/log/hfdl.log |
New JSON lines | My first came after 20 minutes |
| 7 | The table loaded | docker logs dumphfdl 2>&1 | grep "System table loaded", or the same grep in /var/log/hfdl.log |
One line for each decoder | At the start |
| 8 | The message screen | http://pi/ |
Messages in the HFDL list | Minutes |
| 9 | Sharing | https://app.airframes.io | Your station name, with a recent message | Minutes |
What to expect in numbers: my station, path B with an indoor loop, decodes about 6,000 messages a day. That is 4 to 8 a minute. Path A hears one band, so expect a fraction of that, and long quiet times on a wrong band. Ground stations with names, not numbers, show that the system table works.
15. If it does not work
| Symptom | Likely cause | What to do |
|---|---|---|
| Path A: “No messages, repeating scan.” again and again | No signal reaches the radio, or the radio is overloaded | Check the injector has power. Move the loop away from electronics. Try at another time of day |
| Path A: the container stops at once | A required setting is missing. The log names it | Read docker logs dumphfdl. SOAPYSDRDRIVER, SOAPYSAMPLERATE and FEED_ID must be set |
| The radio runs, the band is busy, and nothing decodes | The sample rate is one the radio does not support. The radio then runs at another rate with no error (dumphfdl README) | Use a rate from the radio’s own list |
| Some channels of a band never decode | They are too near the edge of what the radio hears | Keep all channels inside 80 % of the sample rate. Leave out the outer ones |
usb_claim_interface error -6 with an RTL radio |
The kernel’s TV driver took the radio | Run the install script of section 6 again. Restart the computer |
| An RTL-SDR Blog V4L opens and decodes nothing | A driver older than August 2026 does not know its tuner. I saw this on VHF | docker compose pull && docker compose up -d |
| Messages only by day, or only at night | The band does not fit the time | Section 5. In path A, remove FREQUENCIES and let the scanner choose |
| Ground stations show as numbers | No system table | Check the file and its path (section 12) |
| The container has no table after a restart | The volume points at the wrong folder. The example in the container’s README maps /opt/dumphfdl, but its scripts use /opt/dumphfdl-data |
Use the volume line from section 7 |
Path B: hfdl is active, but the log has no decoder lines |
The DUMPHFDL line points at /usr/bin/dumphfdl, and your build is in /usr/local/bin |
Section 9, step 6 |
| Path B: no data on any band | radiod has no HFDL channels, or the old [HFDL] section is still on |
Section 9, step 2. Then sudo systemctl restart radiod@hf hfdl |
| Path B: shortwave is full of signals that should not be there | No 30 MHz low-pass filter | Fit the filter (section 4) |
Path B: 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 | The limiter blocks the loop’s power, or the injector is in the wrong place | Injector first on the loop cable |
| The log has messages, and ACARS Hub shows none | The router rejects them as too old | Set AR_SKEW_WINDOW=30 |
Path B: nothing reaches pi |
hf cannot find the name pi.local, or the UDP line is missing |
Use the fixed address of pi. Check the ports lines of acars_router |
| Fewer messages after you add an amplifier | Strong signals now overload the radio, and the weak ones are lost | Remove it |
16. What I did wrong
- The loop is still indoors. It sits near the computer, in the noise of the house. It works, and I expect more when it moves outside. Put yours outside from the start.
- Do not count on the nearest station. The California station is nearer to me than Hawaii. Most of my traffic is for Hawaii. Shortwave does not go by distance.
- “Heard” is a strong word. A message on a station’s frequency can come from an aircraft that talks to that station. So I say “traffic for” a station, not “I heard the station”.
17. Not run and not confirmed
- All of path A. My station does not run the docker-dumphfdl container, and I own no Airspy HF+ Discovery. Sections 6 to 8 come from the container’s README and its scripts, read on 2026-10-10. No command in them was run.
- The line
DUMP_HFDL_COMMAND_EXTRA=--output decoded:text:file:path=-. The dumphfdl README sayspath=-prints to standard output. I did not confirm that the text reachesdocker logs. - The sample rate 250000 for an RTL-SDR Blog V4 or V4L, and any RTL radio on shortwave. I did not try the V4L on shortwave.
- That the automatic gain of each radio suits HFDL. The container’s README marks
GAINas required, but its start script accepts a file without it. - The scan time of “about 13 minutes”. I computed it from the script: 8 groups, 90 seconds each, plus pauses.
- The price of the Airspy HF+ Discovery and of the RX-888 Mk II. The other prices are from 2026-10-07 and were not checked again.
- Path B on one new computer. The steps are the settings on my station, checked against each project’s documents. They were not run on a new, empty computer as written.
- That the newest ka9q-radio builds and behaves the same. I use commit
b5bbb9aa. On 2026-10-10 the HFDL helper, service file and channel file were the same in both versions. - The ka9q-radio document says
make installputs a first system table in/var/lib/hfdl/. I copy the file by hand, and did not check which is true for my commit. - That 12 copies of dumphfdl can all write one table file with no fault. This is the project’s own arrangement. My table has not changed version since the start, so I have not seen an update happen.
- That version 52 is the table in use on the air today.
- The channel lists in section 5 do not match table version 52 exactly. They include 10075 and 13354 kHz, which no station in the table has. They leave out 2986 kHz (Bahrain) and 3900 kHz (Reykjavik). The scanner of the container has a few more differences.
- The
ACARS Hubsettings without the aircraft-map lines. My station runs it with ACARS, VDL2 and an aircraft map as well. - The logrotate rule in path B. I did not check that the install put it in place.
- Checks 5 and 7 of section 14 (
pgrep -c dumphfdl, and the “System table loaded” line in each log). I wrote them from the documents and did not run them. - “Path B with no second computer”. It is the form the project ships. My station has never run it.
- The body of the example message in section 10. The first line and the names
Src,Dst,TypeandPerformance dataare in the dumphfdl README. I wrote the rest from memory of the layout, and simplified it. - The day and night table in section 5. It is a general rule plus ten days of my own counts, not a forecast.
- That a squitter repeats every 32 seconds, as the episode shows. I found no source for it today, so this guide gives no number.
- How airframes.io lets you claim a station. The rule about the same address is from the ka9q-radio document. The airframes.io feeding pages did not load on 2026-10-10.
- The site hfdl.observer. I did not read it.
18. Sources
All read on 2026-10-10, unless a line says another date.
The decoder. dumphfdl README, etc/systable.conf (version 52) and the option list in src/main.c: https://github.com/szpajder/dumphfdl. Release 1.7.0, 2025-11-02.
Path A. docker-dumphfdl README and the scripts in rootfs/: https://github.com/sdr-enthusiasts/docker-dumphfdl. Docker install script: https://github.com/sdr-enthusiasts/docker-install (read 2026-10-07 for the full station guide).
Path B. ka9q-radio docs/hfdl.md, aux/start-hfdl, config/hfdl.conf, service/hfdl.service.in, config/examples/fragments/hfdl.conf and aux/hfdl.rotate, at commit b5bbb9aa and at the newest commit: https://github.com/ka9q/ka9q-radio. The receiver: /guides/shortwave-receiver/.
The message screen and sharing. acars_router README: https://github.com/sdr-enthusiasts/acars_router. ACARS Hub README: https://github.com/sdr-enthusiasts/docker-acarshub. Airframes, introduction to HFDL: https://docs.airframes.io/docs/technology/hfdl/intro/.
My station. The commands of path B, the parts and the prices: /guides/build-the-station/, checked against my station on 2026-10-07. The counts and shares: the episode’s fact sheet, from my station on 2026-10-06.
Rules. 47 U.S.C. 605: https://www.law.cornell.edu/uscode/text/47/605 (read 2026-10-07).