Everything runs off one RTL-SDR Blog V3 dongle and a local web dashboard
(http://localhost:8073). Each piece is honest about what it can and cannot do.
python -m venv venv
./venv/bin/pip install -e .Set your location with env vars or a gitignored station_local.py:
# station_local.py (gitignored; place it in your working directory)
LAT, LON, ELEV = 40.0, -105.0, 1600
NAME = "your location"or export STATION_LAT=... STATION_LON=... STATION_ELEV=.... Run commands from
the repo root; runtime data (captures, logs, TLE cache) lands in the working
directory, or wherever AETHERSCOPE_DATA points.
src/aetherscope/
config.py station location + data directory
dashboard.py local web UI
scheduler.py single dongle arbiter (time-slices the jobs below)
devices.py rtl_433 device decode (433/315 MHz)
capture.py raw IQ capture + spectrogram
scan.py detect -> capture -> characterize, in one command
satellites/
noaa.py autonomous NOAA APT weather-sat capture
optical.py naked-eye visible pass prediction (ISS/Tiangong)
cellular/
survey.py cellular tower band survey
uplink.py passive uplink burst watch (logs bursts.jsonl)
parse.py energy-detect bursts in a raw IQ capture
characterize.py off-air vs DC artifact, offset clustering, timing
aetherscope-dashboard # web dashboard at localhost:8073
aetherscope-noaa --list # upcoming passes (no --list = capture daemon)
aetherscope-scheduler # owns the dongle: rtl_433 + uplink sweep, pausable
aetherscope-survey # cellular tower survey
aetherscope-uplink 75 # log cellular uplink bursts with frequency
aetherscope-scan 15 15 # detect the loudest source, capture, characterize
aetherscope-capture 829.5e6 20 # raw IQ + spectrogram at a frequency
aetherscope-parse file.cu8 # find bursts in a capture
aetherscope-characterize file.cu8 # classify + time themThe LTE cell decoder is a separate C++ build under lte-scanner/ (see
lte-scanner/BUILD.md): ./lte-scanner/cellsearch -s 750e6 -e 752e6.
- Reach: the RTL-SDR stops at ~1.7 GHz. Low-band cellular (600/700/850) is in range; PCS/AWS (1.9/2.1 GHz) and 5G mid-band are not, that's a HackRF.
- Cellular: we read broadcast identity (towers) and detect uplink presence. We never decode traffic content, that is illegal interception.
- Uplink attribution: an omnidirectional dipole hears every direction, so a burst means "a cellular device transmitted nearby," not which one. Pinning a burst to a specific device needs a directional antenna.
- NOAA: a real image needs the dipole set as a 137 MHz V-dipole with clear sky; indoors or mistuned gives noise.
Depends on: librtlsdr (rtl_433, rtl_power, rtl_sdr), rtl_433, SatDump, and
the Python packages declared in pyproject.toml.