Night Watch - an automated satellite signal capture and decoding platform. Tracks satellites across the sky, receiving and decoding weather imagery from METEOR-M satellites and SSTV transmissions from the ISS.
- Autonomous Operation: Automatically predicts, schedules, and captures satellite passes
- Real-Time Visualization: Web dashboard with live 3D globe, FFT waterfall/spectrum, and pass timeline
- Multi-Signal Support: LRPT (METEOR-M weather satellites) and SSTV (ISS + 2m ground) decoding
- 2m SSTV Scanner: Scans amateur SSTV frequencies during idle time using FFT-based signal detection
- Per-Band Gain Calibration: Automatic gain adjustment per frequency band (METEOR vs 2m)
- Persistent Waterfall: Server-side FFT history buffer — waterfall survives page refreshes
- Flexible Architecture: Single-device or distributed setups
| Satellite | Signal | Frequency | Status |
|---|---|---|---|
| METEOR-M N2-3 | LRPT | 137.9 MHz | Active |
| METEOR-M N2-4 | LRPT | 137.9 MHz | Active |
| ISS | SSTV | 145.800 MHz | Event-based |
| 2m SSTV | SSTV | 144.5 / 145.5 MHz | Ground |
Note: All NOAA APT satellites (NOAA-15, NOAA-18, NOAA-19) were decommissioned in 2025. This project now focuses on METEOR-M LRPT and ISS SSTV signals.
- RTL-SDR dongle (RTL2832U-based)
- VHF antenna (137MHz for METEOR-M, 145MHz for ISS)
- Docker and Docker Compose v2
- Raspberry Pi 4/5 or Linux machine
git clone https://github.com/milesburton/night-watch.git
cd night-watch
cp .env.example .env
vi .env # Set your coordinates
docker compose -f docker/compose.yaml up -d
``
## Configuration
All configuration via `.env` environment variables:
```env
# Station Location (required)
STATION_LATITUDE=51.5069
STATION_LONGITUDE=-0.1276
STATION_ALTITUDE=10
# SDR Hardware
SDR_GAIN=45
SDR_PPM_CORRECTION=0
SDR_SAMPLE_RATE=48000
# Capture Thresholds
MIN_ELEVATION=20
MIN_SIGNAL_STRENGTH=-30For rapid UI iterations without full Docker rebuilds:
# Builds frontend, regenerates version.json, and deploys to Pi
./scripts/deploy-frontend.sh
# Clients auto-reload within 30 seconds when commit changes| Mode | Description | Use Case |
|---|---|---|
| full (default) | All-in-one | Single Raspberry Pi with SDR |
| sdr-relay | Hardware interface only | Pi at antenna, server elsewhere |
| server | Processing + UI only | Powerful server, remote SDR |
See docs/DEPLOYMENT.md for details.
docker compose -f docker/compose.yaml up -d # Start
docker compose -f docker/compose.yaml logs -f # View logs
docker compose -f docker/compose.yaml restart # Restart
docker compose -f docker/compose.yaml down # Stoplsusb | grep RTL
docker compose exec rfcapture rtl_test -t
sudo chmod 666 /dev/bus/usb/*/*- Check antenna positioning (clear line of sight)
- Adjust
SDR_GAIN(try 30-50, or 'auto') — per-band gain is calibrated automatically at runtime - Calibrate
SDR_PPM_CORRECTIONusing FM stations - The Raspberry Pi 4 generates significant QRN (electrical noise) - use a USB extension cable to place the SDR dongle away from the Pi
The system uses a single RTL-SDR device shared between the FFT stream (waterfall display) and recording. The FFT stream is automatically stopped when recording begins, and restarted afterward. If you see "usb_claim_interface error -6", ensure no other processes (e.g. rtl_test, rtl_power) are using the device.
Runtime: Node.js 22.x LTS Backend: TypeScript, SQLite Frontend: React, Vite, Tailwind CSS, Zustand Signal Processing: rtl_sdr → fft.js (real-time waterfall), rtl_fm → sox → SatDump (LRPT recording)
MIT © 2025–2026 - See LICENSE for details.
- SatDump - LRPT signal decoder
- satellite.js - SGP4 implementation
- CelesTrak - TLE data provider
- Claude - AI pair programming assistant