A web application for logging ADS-B aircraft traffic and correlating sighting reports with known aircraft positions. Built for sky-watch groups to quickly determine if a reported object matches tracked aircraft.
git clone https://github.com/Mattjhagen/SkyRewind.git
cd SkyRewind
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
IMPORTANT: The rtl_adsb decoder is required to collect live ADS-B data.
# Install rtl-sdr package (includes rtl_adsb)
sudo apt-get update
sudo apt-get install rtl-sdr
# Verify rtl_adsb is available
which rtl_adsb
Note: The RTL-ADS-B bridge owns the RTL-SDR dongle exclusively while running. Stop the bridge service if you need the dongle for other applications.
The database will be created automatically when you first run the application, but you can also initialize it manually:
python backend/app.py # Starts the web server and initializes DB
ADS-B Bridge (decodes rtl_adsb output and writes aircraft.json):
# From the project root
python decoder/rtl_adsb_bridge.py
Logger (reads aircraft.json every 10 seconds and stores in SQLite):
# From the project root
python logger/adsb_logger.py
Keep both running continuously (see Production Deployment below for systemd setup).
# From the project root
python backend/app.py
The web interface will be available at http://localhost:5000
Access it from other devices on your network using the server’s IP address: http://<server-ip>:5000
If dump1090 is not running or you want to test with sample data:
python logger/seed_demo_data.py
This generates:
aircraft.json file for testing the loggerTo use the mock data with the logger, edit logger/adsb_logger.py and change:
AIRCRAFT_JSON_PATH = '/path/to/skyrewind/data/mock_aircraft.json'
Monitor r410 server status on a physical display:
# Open on the server or from LAN
chromium --kiosk http://localhost:5001
# or from another LAN device:
chromium --kiosk http://192.168.0.169:5001
Dashboard displays:
⚠️ WARNING: DO NOT EXPOSE PUBLICLY
~/.cloudflared/config.ymlNote: Dashboard shows when the bridge is LOOPING but does not fix it. The loop is a known issue with rtl_adsb dying and systemd restarting it continuously.
Edit these constants in the source files:
Frontend (frontend/app.js):
const DEFAULT_CENTER = [41.25, -95.95]; // [latitude, longitude]
const DEFAULT_ZOOM = 9;
Logger (logger/adsb_logger.py):
POLL_INTERVAL = 10 # seconds
PRUNE_INTERVAL_HOURS = 24 # how often to prune old data
Database (backend/database.py):
def prune_old_positions(days=30): # data retention period
Use the included deployment script for a complete production setup:
sudo ./deploy.sh
This script will:
skyrewind service user/opt/skyrewindThe systemd service files are included in the systemd/ directory:
systemd/skyrewind-adsb-bridge.service - RTL-ADS-B decoder bridgesystemd/skyrewind-logger.service - ADS-B position loggersystemd/skyrewind-web.service - Gunicorn web server (4 workers)After deployment, access the application at http://<server-ip>:5000
# Check service status
sudo systemctl status skyrewind-adsb-bridge
sudo systemctl status skyrewind-logger
sudo systemctl status skyrewind-web
# Restart services
sudo systemctl restart skyrewind-adsb-bridge
sudo systemctl restart skyrewind-logger
sudo systemctl restart skyrewind-web
# View logs
sudo journalctl -u skyrewind-adsb-bridge -f
sudo journalctl -u skyrewind-logger -f
sudo journalctl -u skyrewind-web -f
For HTTPS and better performance, configure Nginx as a reverse proxy:
server {
listen 80;
server_name skyrewind.example.com;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
skyrewind/
├── backend/
│ ├── app.py # Flask API server
│ └── database.py # SQLite database functions
├── frontend/
│ ├── index.html # Main HTML
│ ├── style.css # Styling
│ └── app.js # Frontend logic
├── logger/
│ ├── adsb_logger.py # ADS-B position logger
│ └── seed_demo_data.py # Demo data generator
├── data/ # SQLite DB and uploads (not in git)
├── requirements.txt # Python dependencies
├── .gitignore
└── README.md
GET /api/sightings - List all sightingsPOST /api/sightings - Create new sightingGET /api/sightings/<id> - Get specific sightingGET /api/positions/live - Current aircraft positionsGET /api/positions/rewind?time=<iso>&lat=<lat>&lon=<lon>&radius=<miles> - Historical positionsGET /api/positions/match?sighting_id=<id>&radius=<miles> - Find matching aircraftThis application identifies cooperative aircraft broadcasting ADS-B signals. Absence of a match means no cooperative aircraft was detected in the specified area and time window—it does not imply the object was anomalous or unidentified. Many aircraft do not broadcast ADS-B, and coverage depends on receiver range and line-of-sight.
sudo systemctl status dump1090-mutabilityls -l /var/run/dump1090-mutability/aircraft.jsoncat /var/run/dump1090-mutability/aircraft.jsonlsusb | grep RTLsudo journalctl -u dump1090-mutability -flogger/adsb_logger.pyls -l data/skyrewind.dbsqlite3 data/skyrewind.db "SELECT COUNT(*) FROM adsb_positions;"Change the port in backend/app.py:
debug_mode = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
app.run(host='0.0.0.0', port=8080, debug=debug_mode)
This project is designed for a specific use case but can be adapted for other locations and requirements. Key areas for customization:
MIT License - See LICENSE file for details
MUFON members will be able to discuss sightings in-app. Requires:
Why not shipped yet: Intentionally not built as anonymous unmoderated chat. A proper group chat feature needs identity, moderation, and abuse prevention mechanisms to maintain a productive community discussion space.
python3 generate_icons.pyFor issues, questions, or feature requests, please open an issue on GitHub.