Skip to content

milesburton/aprs-station-map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

220 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

APRS Station Map

CI Docker Build Deploy License: MIT Node 22

Live: https://aprs-station-map.fly.dev

Map overview showing APRS stations around London with distance rings, toolbar filters, and diagnostics panel

Real-time APRS station map. Operates in two modes:

Mode Requires Use case
Read-only (APRS-IS) Internet connection only Cloud deployments, no hardware
SDR / TNC (KISS) RTL-SDR dongle or TNC hardware Home station, igating, full TX/RX

Quick Start

cp .env.example .env
# Edit .env with your settings
docker compose -f .appcontainer/compose.yaml up -d

Open http://localhost

Modes

Station popup showing callsign, symbol, position, distance, via path and follow button

Read-only mode (no hardware required)

Connect to the global APRS-IS network to receive packets — no radio hardware or licensed callsign required. Uses passcode -1 for receive-only access.

DATA_SOURCE=aprs-is
STATION_CALLSIGN=NOCALL
APRS_IS_PASSCODE=-1
APRS_IS_FILTER=r/51.5/-0.1/200

SDR / TNC mode (requires hardware)

Receives packets from a local Direwolf instance via KISS TCP. Supports RTL-SDR dongles, sound cards, or any KISS-compatible TNC. Set APRS_IS_PASSCODE to igate (relay) received RF packets to the internet.

DATA_SOURCE=kiss
STATION_CALLSIGN=M0LHA-10
AUDIO_SOURCE=rtl-sdr
APRS_IS_PASSCODE=12345

Generate your passcode from your base callsign (no SSID) at https://apps.magicbug.co.uk/passcode/

Configuration

Copy .env.example to .env and set your values. Never commit .env — it contains your callsign credentials.

Common

Variable Description Default
DATA_SOURCE kiss or aprs-is — sets the default for the enable flags below kiss
KISS_ENABLED true to connect to a local KISS TNC derived from DATA_SOURCE
APRS_IS_ENABLED true to also connect to APRS-IS (you can set both) derived from DATA_SOURCE
STATION_CALLSIGN Your callsign e.g. M0LHA-10 required
STATION_LATITUDE Station latitude (decimal degrees) required
STATION_LONGITUDE Station longitude (decimal degrees) required

Running both sources at once

Set KISS_ENABLED=true and APRS_IS_ENABLED=true to ingest from a local TNC and APRS-IS simultaneously — useful if you want your own RF reception plus internet coverage of stations outside your range. Duplicate packets (the same packet heard locally and relayed by APRS-IS) are counted and logged but not dropped; upsertStation keys by callsign so they don't double-create stations.

KISS / Direwolf mode (DATA_SOURCE=kiss)

Variable Description Default
AUDIO_SOURCE rtl-sdr, soundcard, or null rtl-sdr
RTL_FREQ RTL-SDR frequency 144.8M
RTL_GAIN RTL-SDR gain (dB) — start at 25–30 and adjust 30
RTL_PPM RTL-SDR PPM frequency correction 0
APRS_IS_SERVER APRS-IS server for igating rotate.aprs2.net
APRS_IS_PORT APRS-IS server port 14580
APRS_IS_PASSCODE Your passcode — leave empty to disable igating (disabled)

APRS-IS mode (DATA_SOURCE=aprs-is)

Variable Description Default
APRS_IS_SERVER APRS-IS server rotate.aprs2.net
APRS_IS_PORT APRS-IS server port 14580
APRS_IS_PASSCODE -1 for read-only, or your passcode to authenticate -1
APRS_IS_FILTER Server-side filter e.g. r/LAT/LON/KM (all packets)

Fly.io Deployment

The app deploys to Fly.io automatically on push to main (after tests pass). Read-only mode requires no licensed callsign.

First-time setup

flyctl auth login
flyctl apps create aprs-station-map
flyctl volumes create aprs_data --size 1 --region lhr

Set secrets

# Read-only mode (no callsign needed)
flyctl secrets set \
  DATA_SOURCE=aprs-is \
  STATION_CALLSIGN=NOCALL \
  APRS_IS_PASSCODE=-1 \
  APRS_IS_FILTER="r/51.5/-0.1/200"

# Authenticated mode (licensed callsign)
flyctl secrets set \
  DATA_SOURCE=aprs-is \
  STATION_CALLSIGN=M0LHA \
  APRS_IS_PASSCODE=<your-passcode> \
  STATION_LATITUDE=<lat> \
  STATION_LONGITUDE=<lon>

GitHub Actions deploy

Add FLY_API_TOKEN as a repository secret (generate with flyctl tokens create deploy). Pushes to main will deploy automatically after tests pass.

Development

Diagnostics panel showing station stats, packet feed, and connection status

Open in VS Code with the Dev Containers extension — fish shell, flyctl, and all tooling are pre-installed.

npm run validate    # lint + typecheck + tests
npm run dev:all     # frontend + backend dev servers

README screenshots in docs/screenshots/ are regenerated by the e2e job on every push to main (see e2e/screenshots.spec.ts), so they stay in sync with the live UI.

Docker

docker compose -f .appcontainer/compose.yaml up -d
docker compose -f .appcontainer/compose.yaml build --no-cache && docker compose -f .appcontainer/compose.yaml up -d
docker compose -f .appcontainer/compose.yaml logs -f
docker compose -f .appcontainer/compose.yaml down

Data Persistence

Stations and packet history are stored in a SQLite database inside the aprs-data Docker volume (or /app/data on Fly.io). Data survives container restarts.

History is retained for 7 days and cleaned up automatically overnight.

To wipe all data and start fresh (e.g. after changing location):

docker compose -f .appcontainer/compose.yaml down
docker volume rm aprs-data
docker compose -f .appcontainer/compose.yaml up -d

MIT Licence

About

Live APRS station map with real-time updates, filtering, and statistics. Intended for a RPi but you could use any 'Nix based system (containers are awesome).

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors