# ADS-B

At the end you have your own live map of the aircraft around you, made from signals that your own antenna receives. You can also share the data with tracking networks, if you choose to.

Spurious Emissions · Rev B · checked 2026-10-21 · https://spurious-emissions.net/guides/receive-adsb/

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.

## 1. What this guide builds

One antenna, one USB dongle and one small computer. The computer decodes the position messages that aircraft broadcast and draws them on a map in your browser. Sharing the data with a tracking network is optional, and it comes last.

| Item | This guide |
|---|---|
| Signal | ADS-B: aircraft broadcast their own position, up to twice a second, with no encryption |
| Frequency | 1090 MHz |
| Antenna | A vertical antenna made for 1090 MHz, outdoors and in the clear |
| Radio | A USB dongle with an amplifier and a 1090 MHz filter built in |
| Decoder and map | Path A: dump1090-fa and SkyAware, in FlightAware's PiAware image. Path B: readsb and tar1090, in the ultrafeeder container |
| Computer | One Raspberry Pi, or any small computer that runs Docker |

A dongle is a small USB radio receiver. The computer does all of the decoding in software.

```
 outdoors                         indoors, near the cable entry         home network
 --------                         -----------------------------         ------------
 1090 MHz vertical -- coax -- dongle (amplifier + filter) -- USB -- computer
                                                                      |  decoder
                                                                      |  map in your browser
                                                                      |  feeds to networks (optional)
```

The guide has two paths. Choose one in section 6.

- **Path A** is a ready-made card image from FlightAware. It is the simplest start. It feeds FlightAware.
- **Path B** is the ultrafeeder container. It gives a better map and feeds many networks. It needs Docker and a text editor.

My own station uses both, on two computers. Section 12 shows that layout.

## 2. What you need

Prices are US dollars. Each price names its seller and its check date. They change. A part marked ◆ is what I use. A blank box is for your pencil.

### The parts I use

| ☐ | Part | Job | Connector | Price | Note |
|---|---|---|---|---|---|
| ☐ | ◆ [FlightAware 1090 MHz antenna](https://flightaware.store/products/antenna-1090mhz), 66 cm, 5.5 dBi | Receives the aircraft | N type | $49.99, FlightAware store, 2026-10-10 | A bracket for a 2.5 to 4 cm pole is included. The store showed "sold out" that day[^antenna] |
| ☐ | ◆ [FlightAware Pro Stick Plus](https://flightaware.com/adsb/prostick/) | The dongle | SMA socket | $45.99, FlightAware store, 2026-10-07 | A 19 dB amplifier and a 1090 MHz filter are built in |
| ☐ | ◆ [Raspberry Pi 4](https://www.amazon.com/dp/B07TC2BK1X?tag=spuriousemiss-20) | The computer, path A | USB | See listing | FlightAware lists the Pi 3, Pi 4 and Zero W series for its image |
| ☐ | [Raspberry Pi 5](https://www.amazon.com/dp/B0CK2FCG1K?tag=spuriousemiss-20), 8 GB | The computer, path B | USB | $175 list (4 GB: $110), 2026-10-07 | Any 64-bit ARM or x86 computer that runs Docker also works |
| ☐ | ◆ [ZimaBlade](https://www.zimaspace.com/products/blade-personal-nas) | My second computer (section 12) | Ethernet | See listing | Not needed. One computer does the whole job |

### 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 dongle end | Antenna to dongle. Keep the loss under 6 dB and the length under 15 m (FlightAware's advice) |
| ☐ | microSD card. FlightAware asks for 8 GB or more for its image. I recommend 32 GB or more for path B | Storage |
| ☐ | The power supply made for your computer. FlightAware asks for 5.1 V and 2.5 A for a Pi 3 or Pi 4. A Pi 5 needs its 27 W supply | Power |
| ☐ | Ethernet cable | The network. Section 3 says why not Wi-Fi |
| ☐ | Short USB extension cable | Keeps the warm dongle away from the computer |
| ☐ | ◆ Stick-on aluminum heat sink, 50 x 25 x 10 mm ([mine](https://www.amazon.com/dp/B08QM84KPZ?tag=spuriousemiss-20), a pack of two) | Optional. My own addition, not part of the dongle as sold. I put one on most of my dongles, because they run warm |
| ☐ | Grounding block for the coax where it enters the house, and bonding wire | Section 4, "Ground the cable" |
| ☐ | Self-amalgamating tape | Seals the outdoor connector against water |

### The lowest-cost path

| ☐ | Part | Job | Price | What it gives up |
|---|---|---|---|---|
| ☐ | A Raspberry Pi 3 or Pi 4 that you own | The computer, path A | Nothing | Nothing. The decoder is a light load |
| ☐ | ◆ FlightAware Pro Stick Plus | The dongle | $45.99, FlightAware store, 2026-10-07 | Nothing. This is the same dongle |
| ☐ | A small indoor antenna with an SMA plug, at a window | Receives the aircraft | See listing | Range. Walls and trees block 1090 MHz |
| ☐ | The PiAware card image | Decoder, map and feed | Free | The map has fewer functions. It feeds one network |

FlightAware gives one tip for a telescopic antenna. Close it to 6.9 cm, which is a quarter wavelength at 1090 MHz.

A plain RTL-SDR dongle also works. The sdr-enthusiasts guide says a low-cost RTL2832U dongle with an R820T2 tuner does the job. It has no amplifier and no filter, so a strong transmitter near you can block it. The [RTL-SDR Blog V4L](https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/) cost $37.95 at rtl-sdr.com on 2026-10-07. That is 8 dollars less than the Pro Stick Plus, and I did not test it for aircraft (section 16).

Do not buy an extra amplifier or an extra filter at the start. The Pro Stick Plus has both. FlightAware says most users of it need no external filter.

## 3. Before you start

**Skills.** You can write a card image with a desktop program. You can type commands in a terminal over SSH. Path B also asks you to edit two text files.

**Time.** I estimate one hour for path A and two hours for path B, with the antenna work on top. I did not time a new build.

**Accounts.** Path A needs a free FlightAware account. Path B needs no account for the map. Section 11 lists the accounts for each network.

**The network.** Use wired Ethernet. Multilateration needs steady timing. Give the computer a fixed address on your network.

### 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. Letters, digits, dashes and underscores only | `my-station` |
| `<UUID>` | One random ID for the networks in section 11 | from `cat /proc/sys/kernel/random/uuid` |
| `<PI_ADDRESS>` | The fixed address of the computer on your network | Set in your router |
| `<FEEDER_ID>` | The ID that FlightAware gives your station | 36 characters |

The computer has the name `pi` in this guide.

> **Your position becomes public if you feed.** Most tracking networks show their feeder stations on a map. Multilateration needs your exact position, so you cannot give a false one. Your own map stays private until you choose to feed. Section 11 repeats this before the step.

### The law, in short

I am not a lawyer. This is what I read, for the United States. Other countries differ. This guide receives only, and it never transmits.

ADS-B is a broadcast with no encryption, and many public web sites show the same data. US law (47 U.S.C. section 605) limits what you may pass on from a radio message that is not meant for the public. Each network also has terms for the data it gives back to you. Read them before you republish a map or a list.

## 4. The antenna and the cable

A signal at 1090 MHz travels in a straight line. It does not bend over a hill and it does not pass through one. Trees, walls and roofs weaken it. So the position of the antenna matters more than any part on the list.

### Choose the position

1. Find the place with the most open sky down to the horizon, in every direction.
2. Put the antenna above the roof line if you can. A roof beside the antenna blocks one half of the sky.
3. Keep the antenna vertical. Aircraft transmit with vertical polarization.
4. Plan the shortest cable run from that place to the dongle.

A higher antenna helps because it clears the objects near you. Height alone adds little on flat, open ground. Section 10 gives the numbers.

An indoor antenna at a window works for a first test. Expect much less range. The sdr-enthusiasts guide says an outdoor antenna gives far better results in most cases.

### Mount the antenna

> **Warning.** A fall from a roof or a ladder can kill. A mast that touches a power line can kill. Work with a second person. Keep the antenna and the mast far from every power line, at least twice the mast length.

1. Attach the bracket to a pole of 2.5 to 4 cm diameter.
2. Attach the antenna to the bracket with the connector down.
3. Connect the N plug of the coax to the antenna. Tighten it by hand.
4. Wrap the connector with self-amalgamating tape, from the cable up onto the antenna base.
5. Make a small loop in the cable below the connector. Water then drips off before it reaches the entry.
6. Fasten the cable down the mast and into the house.

### Choose the cable

Coax loses much more signal at 1090 MHz than on shortwave. Every decibel that the cable loses comes off the weak signals first.

- Use low-loss 50 ohm coax. Read its loss at 1000 MHz on the data sheet before you buy.
- Keep the total loss under 6 dB and the length under 15 m. That is FlightAware's advice.
- Use one cable with the right plug on each end. Each adapter adds a small loss and a place for water.
- Do not use 75 ohm television coax with adapters as a first choice. I did not test it at 1090 MHz.

If the run must be long, move the computer, not the cable. Put the dongle and the computer near the cable entry. Network cable loses nothing.

### Ground the cable

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.[^nec] Use a licensed electrician if you are not sure.

## 5. The dongle

The Pro Stick Plus does three jobs in one case. Its amplifier lifts the signal by 19 dB, with a noise figure of 0.4 dB. Its filter passes 1075 to 1105 MHz and cuts other frequencies by 30 dB. Its receiver chip then measures the signal 2.4 million times a second for the decoder. FlightAware's data gives the filter a loss of 2.3 dB.

The filter matters in a town. A mobile phone tower or a broadcast transmitter near you can fill the receiver with a signal you do not want. The filter removes it before the receiver chip sees it. FlightAware says that a filter is generally not needed in the country.

> **Caution.** A static discharge from an outdoor antenna can destroy the amplifier in the dongle. Touch the shell of the coax plug to the grounding block before you connect it.

1. Connect the SMA plug of the coax to the dongle. Tighten it by hand.
2. Connect the dongle to the short USB extension cable.
3. Connect the extension cable to a USB socket of the computer. Do this when section 7 or section 8 tells you to.

The dongle draws 300 mA. One dongle needs no powered hub. It gets warm, and that is normal. Keep air around it.

## 6. Choose a path

| | Path A: PiAware image | Path B: ultrafeeder |
|---|---|---|
| What you install | One card image. No commands are necessary | Raspberry Pi OS, Docker, one container |
| Decoder | dump1090-fa | readsb |
| Map | SkyAware | tar1090, with tracks, a range outline, a heat map and graphs |
| Networks | FlightAware | Many, each with one line or one small container |
| Account | A FlightAware account is necessary | None for the map |
| Computer | Raspberry Pi 3, Pi 4 or Zero W series (FlightAware's list) | A 64-bit Pi, or an x86 computer |
| Use it if | You want aircraft on a map today | You want the better map or several networks |

You can start with path A and change later. The dongle, the antenna and the cable stay the same. Do one path only on one computer: two decoders cannot share one dongle.

## 7. Path A: the PiAware image

Program: [PiAware](https://www.flightaware.com/adsb/piaware/build), FlightAware's ready-made system for a Raspberry Pi. It holds the decoder (dump1090-fa), a local map (SkyAware) and the program that feeds FlightAware.

> **Your position becomes public.** This path feeds FlightAware from the first start. FlightAware asks for the position of your antenna. Read section 11 first if that is a problem for you.

### Write the card

1. Download the image from the PiAware build page. On 2026-10-10 the file was `piaware-sd-card-11.2.img.xz`.
2. Install [Raspberry Pi Imager](https://www.raspberrypi.com/software/) on your desktop computer. Start it.
3. Choose your Raspberry Pi model.
4. In the list of systems, go to the end and choose "Custom OS". Select the file from step 1.
5. Choose the microSD card as the storage. Check the name twice: the write erases the card.
6. Answer **No** when Imager asks to apply customisation settings.
7. Confirm the write. Wait until it is complete.

Result: the card has a volume with the name `piaware`.

### Set the network and SSH

1. Remove the card and put it in again. Open the volume `piaware`.
2. For wired Ethernet, change nothing. The image uses it by default.
3. For Wi-Fi only: open `piaware-config.txt` as plain text and set these three lines.

   ```
   wireless-network yes
   wireless-ssid <your network name>
   wireless-password <your network password>
   ```

4. To turn SSH on, create an empty file with the name `ssh` and no extension on this volume.
5. Eject the card safely.

### Start it

1. Put the card in the Pi.
2. Connect the dongle, with the antenna cable on it.
3. Connect the Ethernet cable. Then connect the power.
4. Wait 5 minutes.
5. Find the address of the Pi in the device list of your router. Give it a fixed address there.
6. Open `http://<PI_ADDRESS>/` in a browser on the same network.

Result: a status page from the Pi. If the receiver has no owner, the page shows a link to claim it.

### Claim the receiver and set the position

1. Log in to your FlightAware account in the same browser.
2. Click the claim link on the status page of the Pi.
3. Open <https://www.flightaware.com/adsb/stats>. Data appears there within 30 minutes.
4. Click the gear symbol beside the site name. Set the position and the height of the antenna.

Result: the stats page shows your site, the local address of the Pi and a link to SkyAware. SkyAware is the map of the aircraft that your own dongle receives.

### Log in and change the password

The image has a known default password. Change it before anything else.

1. Log in with SSH. The user is `pi` and the default password is `flightaware`.

   ```bash
   ssh pi@<PI_ADDRESS>
   ```

2. Set a new password.

   ```bash
   passwd
   ```

3. Check the programs.

   ```bash
   sudo piaware-status
   ```

Result: the report says if dump1090 and piaware run, which program gives data at `30005/tcp`, and if piaware is connected to FlightAware.

### Settings you may need

Change a setting with `sudo piaware-config <setting> <value>`. The command `piaware-config` alone shows the settings. Restart the Pi after a change to a receiver setting.

| Setting | Default | Meaning |
|---|---|---|
| `rtlsdr-gain` | `max` | Gain of the dongle. Lower it if strong aircraft near you overload it (section 15) |
| `allow-mlat` | `yes` | Take part in multilateration (section 11 explains the word) |
| `mlat-results` | `yes` | Show the multilateration results on your own map |
| `allow-auto-updates` | `yes` on the card image | FlightAware may update the software |
| `wired-type` | `dhcp` | `static` lets you set the address on the Pi itself, with `wired-address`, `wired-netmask`, `wired-broadcast` and `wired-gateway` |

Go to section 9.

## 8. Path B: ultrafeeder

Program: [docker-adsb-ultrafeeder](https://github.com/sdr-enthusiasts/docker-adsb-ultrafeeder). A Docker container is a program packed together with everything it needs, which runs apart from the rest of the system. This one container owns the dongle, decodes with readsb, draws the map with tar1090 and can feed the networks.

### Prepare the computer

1. Write Raspberry Pi OS Lite (64-bit) to the card with Raspberry Pi Imager. In the Imager settings, set the host name to `pi`, turn SSH on, and add your public key.
2. Start the Pi on wired Ethernet. Log in with SSH.
3. Update the system.

   ```bash
   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 dongle. Read the script before you run it: its authors say so too.

   ```bash
   bash <(curl -s https://raw.githubusercontent.com/sdr-enthusiasts/docker-install/main/docker-install.sh)
   sudo reboot
   ```

5. Check Docker and the driver block.

   ```bash
   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 clock service. The script may have installed it already.

   ```bash
   sudo apt install -y chrony
   ```

Result: `docker compose version` prints a version number, and the count of loaded TV drivers is 0.

Do not run the aircraft receiver in a virtual machine. Multilateration often fails there (ultrafeeder README).

### Give the dongle a serial number

A serial number lets the decoder find the right dongle if you add a second one later.

1. Connect the dongle. No other dongle may be connected.
2. Write the serial number `1090`. Answer `y` when the program asks.

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

3. Disconnect the dongle and connect it again. A restart of the computer does not load the new serial number.

Result: the program prints the new configuration with `Serial number: 1090`.

Change only the serial number. Do not change the maker or product text on an RTL-SDR Blog dongle: its driver looks for that text.

### Write the two files

1. Make the project folder.

   ```bash
   sudo mkdir -p -m 777 /opt/adsb && cd /opt/adsb
   ```

2. Create `/opt/adsb/.env` with your values.

   ```ini
   FEEDER_TZ=<TZ>
   FEEDER_LAT=<LAT>
   FEEDER_LONG=<LON>
   FEEDER_ALT_M=<ALT_M>
   FEEDER_NAME=<NAME>
   ADSB_SDR_SERIAL=1090
   ```

3. Create `/opt/adsb/docker-compose.yml`.

   ```yaml
   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
         - UPDATE_TAR1090=true
         - TAR1090_ENABLE_AC_DB=true
       volumes:
         - /opt/adsb/ultrafeeder/globe_history:/var/globe_history
         - /opt/adsb/ultrafeeder/graphs1090:/var/lib/collectd
         - /proc/diskstats:/proc/diskstats:ro
         - /dev/bus/usb:/dev/bus/usb
       tmpfs:
         - /run:exec,size=256M
   ```

This file feeds no network. Your position stays on your own computer.

What the settings do:

| Setting | Meaning |
|---|---|
| `8080:80` | The map is on the computer at `8080` |
| `READSB_GAIN=auto` | readsb sets the gain by itself, from the noise and the strong signals it measures. The README recommends this |
| `READSB_LAT`, `READSB_LON`, `READSB_ALT` | The antenna position. The decoder needs it to compute distance and range |
| `READSB_RX_LOCATION_ACCURACY` | How exactly your own map shows the station: `2` exact, `1` two decimal places, `0` not at all |
| `TAR1090_ENABLE_AC_DB=true` | Adds the aircraft type and registration to the map. It uses about 50 MB more memory |
| `device_cgroup_rules`, `/dev/bus/usb` | Let the container use the dongle, also after you disconnect and connect it |

### Start it

1. Start the container. The first start downloads it, which takes some minutes.

   ```bash
   cd /opt/adsb && docker compose up -d
   ```

2. Check the container.

   ```bash
   docker ps --filter name=ultrafeeder        # expect "(healthy)" after a few minutes
   ```

3. Look at the decoded data.

   ```bash
   curl -s http://localhost:8080/data/aircraft.json | head -c 300
   ```

Result: text that starts with `{` and holds a list `aircraft`. With aircraft in view, the list has entries with a `hex` address.

## 9. The first aircraft: read the map

Open the map in a browser on your home network.

| Path | Map | Address |
|---|---|---|
| A | SkyAware | The SkyAware link on your FlightAware stats page |
| B | tar1090 | `http://<PI_ADDRESS>:8080/` |
| B | Performance graphs | `http://<PI_ADDRESS>:8080/graphs1090/` |

With an outdoor antenna and air traffic in your region, the first aircraft appear within a minute. The rest of this section describes tar1090.

- **The map** shows one mark for each aircraft with a known position. Click a mark to see its track and its data.
- **The table** lists every aircraft the dongle hears, also those with no position yet. A position needs two messages, an even one and an odd one.
- **The 24-bit address** in the table is unique to one aircraft. The episode shows where it sits in the message.
- **The callsign, the altitude and the speed** each come from a different message type. A new aircraft fills in over some seconds.

Keys that tar1090 gives you:

| Key | Action |
|---|---|
| Q and E | Zoom out and in |
| A, D, W, S | Move west, east, north, south |
| C or Esc | Clear the selection |
| M | Select several aircraft |
| T | Select all aircraft |
| B | Change the map brightness |

More views, from the ultrafeeder README:

| Address | View |
|---|---|
| `http://<PI_ADDRESS>:8080/?pTracks` | The tracks of all aircraft for the past 24 hours. This is the picture of your coverage |
| `http://<PI_ADDRESS>:8080/?heatmap` | A heat map for the past 24 hours |
| `http://<PI_ADDRESS>:8080/?replay` | A replay of past data |

To see single messages as text, run this command. Press Ctrl-C to stop it.

```bash
docker exec -it ultrafeeder viewadsb
```

> **A screenshot of your map can show where you live.** The station mark and the range rings are centered on your antenna. To hide the mark, set `READSB_RX_LOCATION_ACCURACY=1` or `0`. To hide the rings, add `TAR1090_RANGERINGS=false`. Then run `docker compose up -d` again.

## 10. Range: what to expect and why

The horizon sets the limit. The signal travels in a straight line, and the earth curves away under it. An aircraft that is below your horizon is silent to you, at any signal strength.

The usual formula for the radio horizon, with normal bending in the air, is:

```
distance in km = 4.12 x ( square root of antenna height in m
                        + square root of aircraft height in m )
```

| Aircraft altitude | Horizon from an antenna at 10 m |
|---|---|
| 300 m (1,000 ft) | About 85 km (46 nautical miles) |
| 3,000 m (10,000 ft) | About 240 km (130 nautical miles) |
| 12,200 m (40,000 ft) | About 470 km (250 nautical miles) |

These numbers are for flat, open ground or open water. Hills, buildings and trees cut them, often by a lot, and often in one direction only.

What the table says:

- **High aircraft set your maximum range.** An airliner in cruise is visible from far away. A low aircraft drops below the horizon quickly.
- **Antenna height adds little by itself.** From 10 m to 30 m, the antenna's own part grows from 13 km to 23 km. Height helps because it lifts the antenna over the objects near you.
- **A good station comes near the horizon.** FlightAware says a roof installation can pass 400 km. It gives the Pro Stick Plus more than 550 km, "depending on installation quality".

My own numbers: my station tracks a few dozen aircraft at any moment. On one evening, the farthest aircraft in each 15 minutes was 131 to 144 nautical miles away. My horizon is not open in every direction.

### See your own range

1. Let the station run for 24 hours.
2. Open `http://<PI_ADDRESS>:8080/?pTracks`.

Result: the tracks draw the shape of your coverage. A direction with short tracks has an obstacle. tar1090 also draws an outline of the farthest positions of the past 24 hours.

To start the outline again after you move the antenna, run this command.

```bash
docker exec -it ultrafeeder bash -c "echo resetRangeOutline > /run/readsb/setGain"
```

A web site, heywhatsthat.com, can compute the horizon of your position from terrain data. tar1090 can draw that line on your map (`HEYWHATSTHAT_PANORAMA_ID`). You must give that site your exact position, and the README says the line reveals it in the map. Its model ignores trees and objects nearer than about 30 m.

## 11. Feeding networks (optional)

A tracking network collects the data of many home stations and shows one map of the world. Most give a free account at a paid level in return. Your own map works without any of this.

> **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. A person can find the area of a feeder from those maps. Choose a station name that does not name you or your street. If that is not acceptable, do not feed, or feed with no `mlat` lines and read each network's terms first.

Multilateration (MLAT) finds an aircraft that sends no position. Several stations receive the same message, and a server compares the arrival times. It needs your exact antenna position and a steady clock.

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.

### Path A

Path A feeds FlightAware already. For more networks, change to path B, or add a second computer as in section 12.

### Path B: networks that need no account

1. Make the random ID. Add it to `/opt/adsb/.env` as a new line.

   ```bash
   cat /proc/sys/kernel/random/uuid                 # this is <UUID>
   ```

   ```ini
   FEEDER_UUID=<UUID>
   ```

2. Add these lines to the `environment` list of `ultrafeeder` in `docker-compose.yml`.

   ```yaml
         - 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}
   ```

3. Apply the change.

   ```bash
   cd /opt/adsb && docker compose up -d
   docker compose logs -f                           # Ctrl-C to stop
   ```

Result: the log shows one connection for each line, and no repeated error for a host name.

Each `ULTRAFEEDER_CONFIG` line ends with a semicolon, except the last. An `adsb` line sends the decoded messages. An `mlat` line takes part in multilateration. The README lists more networks in the same form. `MLAT_USER` is the name that the multilateration servers show for your station.

### Path B: FlightAware and Flightradar24

Each of these networks has its own container. The container reads the decoded messages from `ultrafeeder` at `30005/tcp`. The messages are in Beast format, a compact binary format that most aircraft decoders use between programs.

1. Get a feeder ID from FlightAware. The command runs for 30 seconds.

   ```bash
   timeout 30 docker run --rm ghcr.io/sdr-enthusiasts/docker-piaware:latest | grep "my feeder ID"
   ```

2. Claim the ID at <https://flightaware.com/adsb/piaware/claim>. Add it to `.env` as `PIAWARE_FEEDER_ID=<FEEDER_ID>`.
3. Get a sharing key from Flightradar24. Answer the questions. Add the key to `.env` as `FR24_KEY=<key>`.

   ```bash
   docker run -it --rm --entrypoint /usr/bin/fr24feed \
     ghcr.io/sdr-enthusiasts/docker-flightradar24:latest --signup --configfile=/tmp/config.txt
   ```

4. Add the two services at the end of `docker-compose.yml`. Then run `docker compose up -d`.

   ```yaml
     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
   ```

Result: after about 30 minutes, your FlightAware stats page shows data from the new site.

### More networks

| Network | Image | Required settings | How to get the key |
|---|---|---|---|
| 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 15 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> |

Keep every key in a password manager. The `.env` file holds them in plain text: do not publish that file.

## 12. What I run: two computers

My station grew one part at a time, so it has both paths.

```
 antenna -- Pro Stick Plus -- Raspberry Pi 4 (PiAware image)
                                |  feeds FlightAware
                                |  Beast format at 30005/tcp
                                |
                          home network
                                |
                              small x86 PC (ultrafeeder, no dongle)
                                |  the tar1090 map
                                |  feeds 8 more networks
                                |  watches for unusual aircraft
```

The Pi 4 sits near the cable entry and does the radio work. The second computer reads the one stream of decoded messages over the network. One computer for both jobs is simpler. I do not recommend my layout for a new build.

To build it, do path A on the Pi. Then do path B on the second computer with these changes.

1. Leave out the section "Give the dongle a serial number".
2. In `docker-compose.yml`, delete the lines `READSB_DEVICE_TYPE`, `READSB_RTLSDR_DEVICE` and `READSB_GAIN`.
3. Delete the lines `device_cgroup_rules`, `- "c 189:* rwm"` and `- /dev/bus/usb:/dev/bus/usb`.
4. Add this line to the `environment` list. It reads the Beast stream from the Pi.

   ```yaml
         - ULTRAFEEDER_CONFIG=adsb,<PI_ADDRESS>,30005,beast_in
   ```

5. Run `docker compose up -d`.

Result: the tar1090 map on the second computer shows the aircraft that the Pi receives.

For feeds from the second computer, put the `adsb` lines of section 11 in the same `ULTRAFEEDER_CONFIG`, after the `beast_in` line. Leave the `mlat` lines out. The README says not to send multilateration data from a central computer when the receiver is remote. Let the Pi do multilateration for FlightAware.

## 13. Run it unattended

A receiver is only useful if it runs all day. These steps keep it running with no work from you.

1. Keep `restart: unless-stopped` in every service. Docker then starts the containers after a power cut.
2. Keep the clock service `chrony` installed on path B. Multilateration needs a correct clock.
3. Keep the fixed address. A new address breaks your bookmarks and the layout of section 12.
4. Update path B once a month.

   ```bash
   cd /opt/adsb && docker compose pull && docker compose up -d
   ```

5. On path A, leave `allow-auto-updates` at `yes`.
6. Look at the graphs once a week: `http://<PI_ADDRESS>:8080/graphs1090/`.

Write down the message rate and the maximum range of one normal day. That is your baseline. Change one thing at a time, and compare with it.

## 14. Check that it works

| No. | Check | Path | Command or page | Pass | Wait |
|---|---|---|---|---|---|
| 1 | The system sees the dongle | A, B | `lsusb` | A line with `RTL2838` or `RTL2832U` | None |
| 2 | The programs run | A | `sudo piaware-status` | dump1090 and piaware run, and piaware is connected | 5 minutes |
| 3 | The container runs | B | `docker ps --filter name=ultrafeeder` | `(healthy)` | A few minutes |
| 4 | The decoder gives data | B | `curl -s http://localhost:8080/data/aircraft.json \| head -c 300` | A list `aircraft` with entries | 1 minute |
| 5 | The map shows aircraft | A, B | The map address in section 9 | Marks that move | 1 minute |
| 6 | The range is plausible | B | `http://<PI_ADDRESS>:8080/?pTracks` | Tracks out to 100 nautical miles or more in your open directions | 24 hours |
| 7 | FlightAware has data | A, or B with the feed | <https://www.flightaware.com/adsb/stats> | Your site, with data | 30 minutes |

Check 6 depends on your horizon and your traffic. In a valley, or far from air routes, a correct station shows less.

## 15. If it does not work

| Symptom | Likely cause | What to do |
|---|---|---|
| `usb_claim_interface error -6`, "Device or resource busy" | The kernel's TV driver took the dongle | Check step 5 of "Prepare the computer". Restart the computer |
| The same error, and the driver block is correct | Two decoders want one dongle | Run one path only on one computer. Stop the other decoder |
| The container is not healthy, and the log says no device with that serial | The new serial number is not loaded, or it is not `1090` | Disconnect the dongle and connect it again. Check `ADSB_SDR_SERIAL` in `.env` |
| The map loads, and the aircraft table stays empty | No signal reaches the dongle | Check both coax plugs. Test with the antenna at a window. Check that the antenna is for 1090 MHz |
| Aircraft appear only when they are near | The antenna is indoors or blocked, or the coax loses too much | Move the antenna outdoors and higher. Shorten the cable or use lower-loss coax |
| Good range in one direction only | An obstacle: a roof, trees, a hill | Look at the `?pTracks` view. Move the antenna to see over the obstacle |
| Near aircraft drop out, and far ones stay | Strong signals overload the dongle | Path B: keep `READSB_GAIN=auto`. Path A: lower `rtlsdr-gain` from `max` one step at a time, and compare |
| Fewer aircraft after you add an amplifier | The dongle has an amplifier already. Two overload it | Remove it. On my message receiver, a 20 dB amplifier cut the count from 95 to 42 an hour |
| The table lists aircraft that never get a position | Normal for some. Those aircraft send no position, or you hear only half of each pair | Nothing. Multilateration can place some of them if you feed (section 11) |
| Multilateration never starts, or the log shows high `bad_sync_timeout` values | A wrong antenna position, a clock that drifts, or a virtual machine | Check `<LAT>`, `<LON>`, `<ALT_M>`. Install `chrony`. Use real hardware and wired Ethernet |
| 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 |
| AirNav Radar stops at once on a Pi 5 | That program needs 4 kB memory pages. `getconf PAGE_SIZE` shows 16384 | Add `kernel=kernel8.img` to `/boot/firmware/config.txt` and restart |
| Plane Finder fails on a Pi 5 | Same kind of problem | Use the image tag `5.0.161_arm64` (sdr-enthusiasts guide) |
| The Docker install script stops on a new system release | The script's README names Debian 10, 11 and 12 as supported | Use a Raspberry Pi OS based on one of those, or read the project's issue list |
| A reception that was good gets worse over weeks | Water in the outdoor connector | Open it, dry or replace it, and seal it again |
| Path A: no claim link and no data after 5 minutes | The Pi is not on the network | Restart the Pi. Check the cable, or the Wi-Fi lines in `piaware-config.txt` |

## 16. Not run and not confirmed

- **No path in this guide was run from an empty card as written.** My station runs the layout of section 12. The path B files are the ones in my full-station guide, which I checked against my station on 2026-10-07.
- **The path B file in section 8 with no feed lines.** My station always runs with feeds. The settings are from the ultrafeeder README.
- **The `beast_in` line of section 12 as written here.** The form is from the README. I did not copy the line from my station for this guide.
- **How my own station handles multilateration with its remote receiver.** Section 12 gives the README's advice.
- **Path A on a Raspberry Pi 5.** FlightAware's page lists the Pi 3, Pi 4 and Zero W series. My image runs on a Pi 4.
- **The Wi-Fi steps of path A.** I use wired Ethernet. FlightAware's page says a supported Wi-Fi adapter is necessary.
- **The local address of SkyAware.** FlightAware's pages give only the link on the stats page, so this guide does the same.
- **The PiAware image version.** The build page named file version 11.2 on 2026-10-10. The newest tag in the PiAware source repository was v11.1 that day. I did not find out why they differ.
- **The Docker install script on the newest Raspberry Pi OS.** Its README names Debian 10, 11 and 12. I did not test a later release.
- **The exact text of the `lsusb` line** for the Pro Stick Plus, and the text of the log lines for a healthy feed.
- **The RTL-SDR Blog V4L for aircraft.** In another container of my station, it needed a driver from August 2026 or later. I did not test it with ultrafeeder or PiAware.
- **That 4 GB of memory is enough for path B on a Pi.** I did not measure this container alone.
- **The times** in sections 3, 9 and 14, except the 5 minutes and 30 minutes that FlightAware states.
- **75 ohm coax, and any coax type by name.** I give FlightAware's limit of 6 dB and 15 m and no cable model.
- **The safe distance to power lines** in section 4 is common antenna-safety advice. I did not read a standard for it.
- **The meaning of the colors on the tar1090 map.** I did not find it in the README, so section 9 leaves it out.
- **The prices.** The Pro Stick Plus and Pi 5 prices are from 2026-10-07. The antenna was sold out at the FlightAware store on 2026-10-10.
- **The horizon table** is my arithmetic with the published formula. It is not a measurement.
- **The law.** I read the US statute and a summary of the electrical code. I am not a lawyer or an electrician.

## 17. Sources

All were read on 2026-10-10, except where a date is given.

**The episode.** Episode 1 of Spurious Emissions, "ADS-B", and its fact sheet. The message format in the episode is from Junzi Sun, *The 1090 Megahertz Riddle*: <https://mode-s.org/1090mhz/>.

**The full-station guide.** [Build the station](/guides/build-the-station/), sections 2 to 7 and 17, checked against my station on 2026-10-07. The compose files, the serial number step, the network table and several traps are from it.

**Path A.** PiAware build page: <https://www.flightaware.com/adsb/piaware/build> · Optional steps (SSH, default login, `piaware-status`): <https://www.flightaware.com/adsb/piaware/build/optional> · Settings and their defaults: <https://www.flightaware.com/adsb/piaware/advanced_configuration> · Install on an existing system: <https://www.flightaware.com/adsb/piaware/install> · Source: <https://github.com/flightaware/piaware> · Decoder: <https://github.com/flightaware/dump1090> (the default sample rate of 2.4 MHz is in `dump1090.c`)

**Path B.** <https://github.com/sdr-enthusiasts/docker-adsb-ultrafeeder> (README, `docker-compose.yml`, `.env`) · <https://github.com/wiedehopf/readsb> · <https://github.com/wiedehopf/tar1090> · <https://github.com/wiedehopf/graphs1090> · <https://github.com/sdr-enthusiasts/docker-install> · sdr-enthusiasts guide: <https://sdr-enthusiasts.gitbook.io/ads-b> (the pages "Equipment needed", "Re-serialise SDRs" and "Deploy ultrafeeder container")

**Feeding.** <https://github.com/sdr-enthusiasts/docker-piaware> · <https://github.com/sdr-enthusiasts/docker-flightradar24> (2026-10-07) · <https://github.com/sdr-enthusiasts/docker-planefinder> (2026-10-07) · <https://github.com/sdr-enthusiasts/docker-airnavradar> (2026-10-07) · <https://github.com/sdr-enthusiasts/docker-opensky-network> (2026-10-07) · <https://github.com/plane-watch/docker-plane-watch> (2026-10-07)

**Parts.** Pro Stick Plus data: <https://flightaware.com/adsb/prostick/> · Antenna: <https://flightaware.store/products/antenna-1090mhz>

**Range.** Radio horizon formula: <https://en.wikipedia.org/wiki/Line-of-sight_propagation> · Horizon from terrain: <http://www.heywhatsthat.com/>

**Rules.** 47 U.S.C. 605: <https://www.law.cornell.edu/uscode/text/47/605> (2026-10-07).

[^antenna]: The episode's link list gives the same antenna at another seller: <https://www.amazon.com/dp/B0CWQ68DZP>. I did not read a price there.
[^nec]: Mike Holt's summary of NEC 810.21, 2020 edition: <https://www.mikeholt.com/files/PDF/20BG_810.21.pdf>.
