RTL-SDR frequency monitoring system for Raspberry Pi. Captures radio frequency spectrum in the 24-80 MHz band using an RTL2838 DVB-T USB dongle, stores data in SQLite, and provides visualization via REST API and web interface.
Continuous spectrum monitoring using RTL-SDR hardware on Raspberry Pi 4B. Frequency range: 24-80 MHz (solar radio band). Data stored in local SQLite database with REST API access. Web dashboards for visualization and analysis.
Required:
- Raspberry Pi 4B (2GB+ RAM)
- RTL2838 DVB-T USB Dongle (Realtek 0bda:2838)
- USB 2.0 port with extension cable (USB 3.0 causes interference)
Specifications:
- Tuner: Rafael Micro R828D
- Sample rate: 2 MSps
- Frequency range: 24-1766 MHz
- Frequency resolution: 500 points per scan = 0.12 MHz per point
- Scan interval: 60 seconds
Clone repository and set up Python environment:
git clone https://github.com/kajoty/SolarMonitor-RTL.git
cd SolarMonitor-RTL
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtConfigure .env:
cp .env.example .envInstall systemd services:
sudo bash install_service.shThis creates two services running automatically on boot:
solarmonitor-sqlite.service(port 8002)solarmonitor-app.service(port 5000)
Check status:
sudo systemctl status solarmonitor-sqlite.service
sudo systemctl status solarmonitor-app.serviceView logs:
sudo journalctl -u solarmonitor-app.service -fManual start (development):
source venv/bin/activate
python3 app.pyWeb interfaces:
- Heatmap Dashboard:
http://localhost:5000/ - Discovery Dashboard:
http://localhost:5000/discovery
RTL-SDR dongle → FFT analysis → SQLite database → REST API (port 8002) → Flask app (port 5000) → Web interface
Components:
frequency_scanner.py- RTL-SDR hardware interface, FFT computationsqlite_server.py- SQLite REST APIheatmap_generator.py- Matplotlib visualizationapp.py- Flask REST API servertemplates/- Web UI (dashboard.html)
.env file contains all settings. Key parameters:
RTL_DEVICE_INDEX=0 # USB device index
RTL_SAMPLE_RATE=2000000 # Samples per second
RTL_GAIN=auto # Gain setting (auto recommended)
SQLITE_DATABASE=spectrum.db # Database file
SCAN_INTERVAL_MINUTES=1 # Scan frequencyGain values: 0-49.6 dB or 'auto'. Recommended: 25.4 or 42.1 dB.
Heatmap endpoint:
GET /api/heatmap
Parameters:
time_range: '1h' | '6h' | '24h' | '7d' | '30d'
OR
start_time: ISO-8601 timestamp
end_time: ISO-8601 timestamp
cmap: colormap name (default: 'viridis')
format: 'json' | 'png' (default: 'png')
Returns: PNG image or JSON array
Example requests:
# Heatmap for last 24 hours as PNG
curl 'http://localhost:5000/api/heatmap?time_range=24h&cmap=viridis'
# Custom time interval as JSON
curl 'http://localhost:5000/api/heatmap?start_time=2025-11-17T00:00:00&end_time=2025-11-17T12:00:00&format=json'
# Available time range presets
curl 'http://localhost:5000/api/time-ranges'
# Available colormaps
curl 'http://localhost:5000/api/colormaps'
# System health status
curl 'http://localhost:5000/api/health'SQLite REST API (port 8002):
curl 'http://localhost:8002/api/stats'
curl 'http://localhost:8002/api/read?time_range=24h'
curl 'http://localhost:8002/health'SQLite file: spectrum.db
Table frequency_spectrum:
- timestamp (ISO-8601 UTC)
- frequency (MHz)
- power (dB)
- band_name
Typical storage: ~500 KB per day at 60-second scan interval.
RTL-SDR not detected:
lsusb | grep -i realtek
# Should show: ID 0bda:2838USB interference: Use USB 2.0 port with extension cable. USB 3.0 causes RF noise in this frequency range.
Service fails to start:
sudo journalctl -u solarmonitor-app.service -n 50RTL-SDR device busy:
ps aux | grep python3
sudo systemctl restart solarmonitor-app.serviceRTL-SDR Access Denied (Permission Error):
# Check if device is detected
lsusb | grep -i realtek
# Should show: ID 0bda:2838
# If device is detected but logs show "usb_open error -3" or "Access denied":
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo systemctl restart solarmonitor-app.serviceCore:
app.py- Flask REST API serversqlite_server.py- SQLite REST wrapperfrequency_scanner.py- RTL-SDR interfaceheatmap_generator.py- Visualization
Web:
templates/dashboard.html- Heatmap interface
Services:
solarmonitor-sqlite.servicesolarmonitor-app.serviceinstall_service.sh
Configuration:
.env.example- Configuration templaterequirements.txt- Python dependencies.gitignore- Git exclusion rules
Current system (Raspberry Pi 4B):
- CPU: 3-5% during scanning
- Memory: 120-250 MB
- Scan duration: 25-30 seconds
- Scan interval: 60 seconds
- API response: <100 ms
- Database growth: ~500 KB/day
MIT
Version: 1.0 | Updated: November 2025 | Status: Production
## Database Schema
### SQLite Table: frequency_spectrum
```sql
CREATE TABLE frequency_spectrum (
timestamp TEXT NOT NULL,
frequency REAL NOT NULL,
power REAL NOT NULL,
band_name TEXT
)
CREATE INDEX idx_timestamp ON frequency_spectrum(timestamp);
CREATE INDEX idx_band ON frequency_spectrum(band_name);
CREATE INDEX idx_ts_band ON frequency_spectrum(timestamp, band_name);
Data Characteristics:
- Timestamp: ISO-8601 format, UTC timezone
- Frequency: MHz (range: 20-80 for solar radio band)
- Power: dB scale
- Storage: ~500 KB per day for current scan interval
Non-intrusive analysis of stored data without accessing RTL-SDR hardware:
python3 analyze_signal_quality.py
python3 analyze_signal_quality.py --time-range 24h
python3 analyze_signal_quality.py --time-range 7dOutput: PNG visualization with 4 panels (SNR, dynamic range, activity, frequency distribution)
Automated testing to find optimal gain value:
sudo python3 optimize_gain.py
sudo python3 optimize_gain.py --duration 5 --tests 10Requires sudo (manages systemd services). Automatically:
- Stops Flask services
- Tests 8-10 different gain values
- Generates performance metrics and visualization
- Recommends optimal gain setting
Archive heatmaps by date with multiple colormaps:
python3 save_daily_heatmaps.py # Today
python3 save_daily_heatmaps.py --date yesterday
python3 save_daily_heatmaps.py --date 2025-11-15
python3 save_daily_heatmaps.py --cmap viridis --cmap plasma --cmap jet
# Date range
python3 save_daily_heatmaps.py --from 2025-11-01 --to 2025-11-15 --cmap viridisCronjob example (archive daily at 23:55):
55 23 * * * cd /home/pi/Projekte/solarmonitor/SolarMonitor-RTL && source venv/bin/activate && python3 save_daily_heatmaps.py
Directory structure created: heatmaps/YYYY-MM-DD/{heatmap_*.png, metadata.json}
| Metric | Value |
|---|---|
| Database Size | ~500 KB per day |
| Total Data Points | 42,500+ (85+ scans) |
| Scan Duration | 25-30 seconds per scan |
| Scan Interval | 60 seconds |
| Heatmap Generation | ~1 second |
| API Response Time | <100 ms |
| Memory Usage | ~80-120 MB (app + sqlite server) |
- CPU: 3-5% during normal scanning
- RAM: 120 MB baseline, 200-250 MB with heatmap generation
- Disk I/O: Minimal (sequential writes, ~1 KB/s during scans)
- Network: None (local SQLite only)
# Verify USB device
lsusb | grep -i realtek
# Expected output: "ID 0bda:2838 Realtek Semiconductor Corp."
# Check device permissions
ls -la /dev/bus/usb/001/
# Verify in Python
python3 -c "from rtlsdr import RtlSdr; r = RtlSdr(); print(r.is_connected)"# Verify REST API is running
curl http://localhost:8002/health
# Check database file
ls -lh spectrum.db
# Test direct SQLite access
sqlite3 spectrum.db "SELECT COUNT(*) FROM frequency_spectrum;"# Check service logs
sudo journalctl -u solarmonitor-app.service -n 100
# Verify manual execution
source venv/bin/activate
python3 app.py
# Check port binding
lsof -i :5000
# Verify .env file
cat .env | head -10# Check service status
sudo systemctl status solarmonitor-app.service
# View recent log entries
sudo journalctl -u solarmonitor-app.service --since "10 minutes ago"
# Manual service restart
sudo systemctl restart solarmonitor-app.service
# If RTL-SDR is busy
lsof | grep -i rtl
ps aux | grep python3
sudo pkill -9 python3 # Force kill if necessarySolarMonitor-RTL/
├── app.py # Flask REST API (main server)
├── sqlite_server.py # SQLite wrapper with REST API
├── frequency_scanner.py # RTL-SDR hardware interface
├── heatmap_generator.py # FFT visualization engine
├── spectrum_analyzer.py # Signal processing utilities
├── analyze_signal_quality.py # Stored data analysis tool
├── optimize_gain.py # Gain testing utility
├── save_daily_heatmaps.py # Daily archive generator
│
├── templates/
│ ├── dashboard.html # Heatmap web interface
│ └── discovery.html # Frequency discovery interface
│
├── solarmonitor-sqlite.service # SQLite service definition
├── solarmonitor-app.service # Flask service definition
├── install_service.sh # Service installation script
│
├── requirements.txt # Python dependencies
├── .env.example # Configuration template
├── .gitignore # Git exclusion rules
│
├── README.md # This file
├── QUICKSTART.md # Quick start guide
├── HEATMAP_GUIDE.md # Heatmap API documentation
├── FREQUENCY_DISCOVERY_GUIDE.md # Discovery system documentation
│
└── venv/ # Python virtual environment
app.py (470 lines)
- Flask application initialization
- Route definitions for REST API endpoints
- Background scheduler for RTL-SDR scans
- Health check implementation
- Time range preset calculation
sqlite_server.py (280 lines)
- SQLite database wrapper
- REST API server (Flask, port 8002)
- Query parameter parsing
- Data serialization and response formatting
heatmap_generator.py (380 lines)
- FFT data retrieval from SQLite
- Matplotlib figure generation
- Height scaling based on data volume
- Multiple colormap support
- PNG encoding and transmission
frequency_scanner.py (420 lines)
- RTL-SDR initialization and configuration
- FFT computation (numpy.fft)
- Background scanning thread
- Data validation and preprocessing
Analysis utilities (1000+ lines combined)
- Signal quality metrics computation
- Gain value testing orchestration
- Daily heatmap generation with metadata
Demo mode without RTL-SDR hardware:
python3 demo_test.pyUnit tests for core functionality:
python3 -m pytest tests/- RTL-SDR device requires exclusive access - systemd ensures clean management
- Heatmap height scales with data volume - large time ranges may exceed display area
- SQLite concurrent write limitations - sequential scan operations sufficient for current use case
- Frequency resolution fixed at 500 points per band - adjust in
frequency_scanner.pyif needed - Web dashboards require modern browser with JavaScript support
Daily heatmap archival (automated via cronjob):
python3 save_daily_heatmaps.pyWeekly backup of SQLite database:
cp spectrum.db spectrum.db.backup.$(date +%Y%m%d)Signal quality analysis to identify optimal frequency ranges:
python3 analyze_signal_quality.py --time-range 30dGain optimization for current environmental conditions:
sudo python3 optimize_gain.py --duration 30- RTL-SDR Documentation: https://osmocom.org/projects/rtl-sdr/
- Raspberry Pi: https://www.raspberrypi.org/
- Matplotlib: https://matplotlib.org/
- Flask: https://flask.palletsprojects.com/
- SQLite: https://www.sqlite.org/
MIT License
Version: 1.0
Last Updated: November 2025
Status: Production