An open API service indexing awesome lists of open source software.

https://github.com/blindp3w/readsbstats

ADS-B flight history & web UI for readsb
https://github.com/blindp3w/readsbstats

acars ads-b adsb aviation fastapi flight-tracking mlat raspberry-pi readsb sqlite tar1090 vdl2

Last synced: 17 days ago
JSON representation

ADS-B flight history & web UI for readsb

Awesome Lists containing this project

README

          

# readsbstats

[![Tests](https://github.com/blindp3w/readsbstats/actions/workflows/test.yml/badge.svg)](https://github.com/blindp3w/readsbstats/actions/workflows/test.yml)
[![Release](https://img.shields.io/github/v/release/blindp3w/readsbstats)](https://github.com/blindp3w/readsbstats/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Extended flight history and track logging for [readsb](https://github.com/wiedehopf/readsb) ADS-B receivers, with a web UI for browsing historical flights and statistics.

Designed to run alongside readsb, tar1090, and feed clients on a **Raspberry Pi 4** without overwhelming it.

readsb and tar1090 give you a great live view β€” readsbstats adds the other half: a persistent SQLite history of every flight, a full-featured browser UI for exploring it, Telegram notifications for military and watchlist aircraft, and a receiver health dashboard backed by months of metrics.

> πŸ€– **About this project:** readsbstats was entirely vibecoded with [Claude Code](https://claude.com/claude-code). Every line of code, test, and configuration in this repository was generated by AI under the maintainer's direction β€” not a single line was written by hand.

## Contents

- [Screenshots](#screenshots)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Security model](#security-model)
- [Resource usage](#resource-usage)
- [Project structure](#project-structure)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [License](#license)

## Screenshots

| | |
|---|---|
| ![Live map](docs/screenshots/v2/map_v2_2.png) | ![Aircraft gallery](docs/screenshots/v2/gallery_v2_1.png) |
| Live map β€” Live / Rewind / HIST modes, bottom command bar, position heatmap, coverage overlay | Gallery β€” military, interesting, and anonymous (non-ICAO hex) aircraft with photos |
| ![Statistics](docs/screenshots/v2/statistics_v2_1.png) | ![Flight details](docs/screenshots/v2/flight_details_v2_1.png) |
| Statistics β€” summary tiles, hourly activity chart, DOWΓ—hour heatmap, top-N datasets | Flight detail β€” photo, route, altitude/speed chart, full position log |
| ![Receiver metrics](docs/screenshots/v2/metrics_v2_2.png) | ![Polar range and top statistics](docs/screenshots/v2/statistics_v2_2.png) |
| Metrics β€” 9 health checks (baseline-aware) above 10 time-series charts (ECharts, LTTB) | Statistics β€” polar coverage range plot and top aircraft types, airlines, routes |

## Features

- Collects aircraft positions every 5 seconds from readsb's `aircraft.json`
- Groups positions into flights (30-minute silence gap = new flight); tracks ADS-B vs MLAT per position
- Stores everything in SQLite β€” no extra database server
- Aircraft enrichment: registration, type, operator from [tar1090-db](https://github.com/wiedehopf/tar1090-db) (~620k aircraft)
- Aircraft photos: Planespotters.net β†’ airport-data.com β†’ hexdb.io β†’ Wikipedia type fallback; cached 30 days
- Military, interesting & anonymous (non-ICAO hex) aircraft detection, badges, and gallery
- Route enrichment via [adsbdb.com](https://www.adsbdb.com/) β€” origin/destination airport per flight
- Live map with **Live / Rewind / HIST** modes β€” rewind from "now" or jump to any moment within `map_history_hours` via a date+time picker; bottom command bar with playback scrubber, range pills, position heatmap, and coverage range overlay; responsive down to iPhone
- Telegram notifications for military/interesting/anonymous/watchlist/emergency-squawk events; interactive bot commands
- Aircraft watchlist β€” track by ICAO hex, registration, or callsign prefix
- Receiver metrics dashboard with 10 time-series charts (Apache ECharts canvas, cross-panel hover sync, wheel/pinch zoom, LTTB downsampling) and 9 health checks
- Statistics dashboard β€” receiver-wide aggregates (top aircraft/airlines/routes/airports, hourly & daily activity, DOWΓ—hour heatmap, polar coverage range). Defaults to the **last 7 days** for fast loads; the all-time view is background-warmed so it stays snappy
- Heatmap and coverage overlays served from collector-maintained daily rollup tables β€” millisecond responses at any history size, and the all-time views survive raw-position retention
- Optional **VDL2 / ACARS** integration β€” ingest VHF Data Link Mode 2 traffic from a separate SDR via [vdlm2dec](https://github.com/TLeconte/vdlm2dec): a Messages tab (live feed, filters, full-text search, human-readable label names) plus an ACARS panel on flight pages with OOOI block times (Out/Off/On/In from Q-series reports) and route confirmation, a "has ACARS" badge/filter in history, and a Stats card. Fully pluggable (`RSBS_VDL2_ENABLED`), stored in its own database (see [Operations](docs/operations.md#vdl2--acars-ingest))
- Human-readable decoded ACARS message bodies in the Messages log (client-side [`@airframes/acars-decoder`](https://github.com/airframes/acars-decoder), lazy-loaded; raw body always retained as fallback)
- `#M1BPOS` precise positions added to the map overlay; filed routes (departure/arrival/company route/SID/STAR/approach) surfaced on the message log
- Each message row is tagged with a category chip (ACMS / engine / maintenance / route / …) so the H1-dominated feed is readable at a glance; `RTE` route messages also surface their filed departureβ†’arrival
- Selectable decoder (`RSBS_VDL2_DECODER`): `vdlm2dec` (ACARS) or `dumpvdl2`, which additionally decodes ATN **CPDLC** controller–pilot messages
- Unit switching: Aeronautical / Metric / Imperial β€” persisted in browser
- SQLite crash-safety (WAL + `synchronous=NORMAL`, `RSBS_DB_SYNCHRONOUS=FULL` for per-commit durability) with dirty-shutdown detection (fail-closed on corruption β€” see [Operations](docs/operations.md#database-integrity--startup-recovery)) and weekly/monthly integrity checks via systemd timers

## Requirements

- Raspberry Pi 4 (or any Linux machine) running Ubuntu 22.04 / 24.04
- [readsb by wiedehopf](https://github.com/wiedehopf/readsb) writing JSON to `/run/readsb/`
- Python 3.10+
- nginx (for the `/stats/` reverse proxy)

## Installation

```bash
# 1. Sync source to the Pi (from your Mac/PC)
rsync -avz --delete \
--exclude='.git' --exclude='__pycache__' --exclude='*.pyc' \
--exclude='.venv' --exclude='*.egg-info' \
--exclude='docs' --exclude='db' --exclude='.DS_Store' \
--exclude='*.db' --exclude='*.db-wal' --exclude='*.db-shm' \
--exclude='frontend/node_modules' --exclude='frontend/.vite' --exclude='frontend/coverage' \
--exclude='internal_docs' --exclude='.claude' --exclude='CLAUDE.md' \
/path/to/readsbstats/ pi@your-pi:/tmp/readsbstats/

# 2. SSH into the Pi and run the installer as root
ssh pi@your-pi
sudo bash /tmp/readsbstats/scripts/install.sh
```

The installer creates a `readsbstats` system user, sets up `/opt/readsbstats/` with a Python virtualenv, creates the SQLite database, and installs systemd services.

After installation, set your receiver coordinates:

```bash
systemctl edit readsbstats-collector readsbstats-web
```

```ini
[Service]
Environment="RSBS_LAT=YOUR_LATITUDE"
Environment="RSBS_LON=YOUR_LONGITUDE"
```

Then restart services and open **`http://YOUR_PI_IP/stats/`**.

### nginx setup

Add one line to your nginx `server {}` block:

```nginx
server {
include /opt/readsbstats/nginx-readsbstats.conf;
}
```

The conf file proxies `/stats/` to uvicorn at `127.0.0.1:8080` and serves the SPA's hashed asset bundles directly with long-cache headers.

## Security model

readsbstats has **no built-in authentication or authorization by default**. It is designed to run bound to `127.0.0.1:8080` behind nginx on a **trusted LAN**. Anyone who can reach the web port can read all flight data and call every mutating endpoint (watchlist edits, settings).

- **The `X-Requested-With` CSRF header is not authentication.** It only blocks cross-site form posts from a browser; it does not identify or authorize a caller. Do not treat it as an access control.
- **If you expose the UI beyond a trusted LAN, put an authenticating reverse proxy in front** (HTTP basic auth, an OAuth2 proxy, or a VPN/Tailscale). Do not publish `127.0.0.1:8080` directly to the internet.
- **`RSBS_API_TOKEN`** β€” opt-in bearer-token gate for every mutating (POST/DELETE) `/api/*` endpoint (watchlist, settings, …). When set, every such call must carry `Authorization: Bearer `. When unset (the default), the web server logs a one-line startup warning so the open posture is never silent. Read endpoints stay open (they were already public on the trusted LAN). Useful as a thin extra layer if the app is reachable from devices you don't fully trust on the LAN; **not** a substitute for a reverse-proxy auth layer when exposed to the internet.
- Outbound HTTP (photo/route enrichment) is SSRF-guarded (`http_safe.py`) and provider photo URLs are host-allowlisted before caching (`RSBS_PHOTO_HOST_ENFORCE`) and at the API response boundary.

See [Operations β†’ Deployment security](docs/operations.md#deployment-security) for the full trust model.

## Resource usage

| Service | CPU quota | Memory limit |
|---|---|---|
| collector | 15% | 192 MB |
| web server | 50% | 1024 MB |

Database size: plateaus at ~0.6 GB at steady state with 180-day position retention (`RSBS_RETENTION_DAYS=180`; recommended), since the schema-v6 scaled-integer position storage. Without retention the DB grows ~20–60 MB/month unbounded.

## Project structure

```
readsbstats/
β”œβ”€β”€ src/readsbstats/ # Python package
β”‚ β”œβ”€β”€ collector.py # Polling daemon, flight detection, writes
β”‚ β”œβ”€β”€ web.py # FastAPI app factory + lifespan + SPA-shell routes
β”‚ β”œβ”€β”€ cache.py # Response cache + map-cache prewarmer thread
β”‚ β”œβ”€β”€ api/ # Per-domain APIRouter modules
β”‚ β”‚ β”œβ”€β”€ _deps.py # DB connection seam, shared SQL/allowlists, _csrf_check
β”‚ β”‚ β”œβ”€β”€ _photos.py # Photo-fetch ladder + per-type async locks
β”‚ β”‚ β”œβ”€β”€ flights.py # /api/flights*, /api/flights/{id}/*
β”‚ β”‚ β”œβ”€β”€ aircraft.py # /api/aircraft/*, /api/airlines/*, /api/types/*
β”‚ β”‚ β”œβ”€β”€ stats.py # /api/stats, /api/stats/records, /api/stats/polar
β”‚ β”‚ β”œβ”€β”€ map.py # /api/map/*, /api/live
β”‚ β”‚ β”œβ”€β”€ feeders.py # /api/feeders + systemd/port checkers
β”‚ β”‚ β”œβ”€β”€ settings.py # /api/settings + _settings_* helpers
β”‚ β”‚ β”œβ”€β”€ watchlist.py # /api/watchlist GET/POST/DELETE
β”‚ β”‚ β”œβ”€β”€ airspace.py # /api/airspace
β”‚ β”‚ β”œβ”€β”€ health.py # /api/health, /api/metrics, /api/metrics/health
β”‚ β”‚ β”œβ”€β”€ dates.py # /api/dates
β”‚ β”‚ └── vdl2.py # /api/vdl2/* (opt-in; reads the separate vdl2.db)
β”‚ β”œβ”€β”€ database.py # SQLite schema, WAL, migrations
β”‚ β”œβ”€β”€ config.py # RSBS_* env vars
β”‚ β”œβ”€β”€ http_safe.py # SSRF-safe HTTP helpers (HTTPS-only)
β”‚ β”œβ”€β”€ photo_sources.py # Planespotters β†’ airport-data β†’ hexdb β†’ Wikipedia
β”‚ β”œβ”€β”€ notifier.py # Telegram notifications
β”‚ β”œβ”€β”€ rollups.py # Daily heatmap/coverage rollups (collector-maintained)
β”‚ β”œβ”€β”€ posenc.py # Scaled-integer codecs for positions columns (schema v6)
β”‚ β”œβ”€β”€ health.py # Receiver health checks
β”‚ β”œβ”€β”€ geo.py # haversine_nm, bearing
β”‚ └── vdl2/ # Opt-in VDL2/ACARS ingest (separate vdl2.db)
β”œβ”€β”€ scripts/
β”‚ β”œβ”€β”€ install.sh # First-time installer
β”‚ β”œβ”€β”€ update.sh # Code sync + restart + optional DB update
β”‚ β”œβ”€β”€ purge_ghosts.py # One-shot: remove ghost positions
β”‚ β”œβ”€β”€ purge_bad_gs.py # One-shot: null implausible gs values
β”‚ └── purge_mlat_gs_spikes.py # One-shot: null MLAT gs spikes
β”œβ”€β”€ frontend/ # React 19 + Vite 8 SPA (440 Vitest tests)
β”œβ”€β”€ tests/ # pytest (2106 tests) + Playwright UI (84 tests)
β”œβ”€β”€ static/airspace/ # Bundled airspace GeoJSON
β”œβ”€β”€ systemd/ # Service + timer unit files
└── docs/ # Public documentation
β”œβ”€β”€ configuration.md # All RSBS_* environment variables
β”œβ”€β”€ api.md # API endpoints + database schema
β”œβ”€β”€ integrations.md # Telegram setup + ghost/GS filtering
β”œβ”€β”€ operations.md # Updating, maintenance, useful commands
β”œβ”€β”€ development.md # Local dev, testing, build, deploy
β”œβ”€β”€ decisions/ # Architecture Decision Records
└── screenshots/ # UI screenshots
```

## Documentation

Full guides live in [`docs/`](docs/), grouped by reference / how-to / explanation:

| Guide | Contents |
|---|---|
| [Configuration](docs/configuration.md) | All 83 `RSBS_*` env vars, logging, airspace config |
| [API Reference](docs/api.md) | All API endpoints, SPA routes, database schema |
| [Integrations](docs/integrations.md) | Telegram setup, bot commands, ghost/GS filtering |
| [Operations](docs/operations.md) | Updating code, DB sync, useful commands, backups |
| [Development](docs/development.md) | Local setup, tests, build, deploy to Pi |
| [Architecture decisions](docs/decisions/) | ADRs for key technical choices |

## Contributing

Bug reports and pull requests are welcome β€” see [CONTRIBUTING.md](CONTRIBUTING.md) for setup and style guidelines. This project follows the [Code of Conduct](CODE_OF_CONDUCT.md).

## License

MIT β€” see [`LICENSE`](LICENSE).

The bundled frontend (`frontend/dist/`) includes Apache ECharts (Apache-2.0)
and other open-source libraries. Required attribution lives in
[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).