1. Hardware Prerequisites
2. Raspberry Pi Setup
sudo apt upgrade
sudo apt install cmake
sudo apt install librtlsdr-dev
sudo apt install -y cmake libad9361-dev libairspy-dev libairspyhf-dev libfftw3-dev libglfw3-dev libhackrf-dev libiio-dev librtaudio-dev libvolk2-dev libzstd-dev
3. Python script to sweep directly and show the plots locally
Consider creating a reference sweep first (see next step), but you can also create an empty reference file.
#!/usr/bin/env python3
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sys
from datetime import datetime
import subprocess
import os
csvfile = "/home/x/Desktop/sweep.csv"
reference_file = "/home/x/Desktop/reference.csv"
#Do the sweep through the bash command
# Build the rtl_power command
rtl_cmd = [
"rtl_power",
"-f", "50M:1760M:125k", #startfreq, stopfreq, bandwidth eg 125k
"-i", "0.5", # integration interval 0.5s
"-g", "30", # gain
"-1", #fast sweep
csvfile
]
# Run rtl_power
print("Starting sweep...")
subprocess.run(rtl_cmd)
print(f"Sweep finished. Results in {csvfile}")
# Read first line to get header info (some rtl_power versions have no header)
# Use pandas to parse; skip initial bad rows if present
df = pd.read_csv(csvfile, header=None)
# Extract all low/high ranges and flatten the data
freqs = []
powers = []
for i in range(len(df)):
lowHz = df.iloc[i, 2]
highHz = df.iloc[i, 3]
stepHz = df.iloc[i, 4]
pwr = df.iloc[i, 6:].astype(float).values.flatten()
# build freq axis for this row
f = lowHz + np.arange(len(pwr)) * stepHz
freqs.extend(f)
powers.extend(pwr)
freqs = np.array(freqs) / 1e6 # MHz
powers = np.array(powers)
# Sort by frequency (just in case)
idx = np.argsort(freqs)
freqs = freqs[idx]
powers = powers[idx]
if os.path.exists(reference_file):
ref_df = pd.read_csv(reference_file, sep=",")
# Compute center frequencies (in MHz)
ref_freqs = ((ref_df["start"] + ref_df["end"]) / 2) / 1e6
ref_power = ref_df["median"].values
# Interpolate reference powers to match current sweep frequencies
ref_interp = np.interp(freqs, ref_freqs, ref_power)
# Compute difference (current - reference)
diff = powers - ref_interp
# Plot reference vs current
plt.figure(figsize=(12,5))
plt.plot(freqs, powers, color="red", label="Current Sweep", linewidth=1.2, alpha=0.7)
plt.plot(ref_freqs, ref_power, color="black", label="Reference Sweep", linewidth=2)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Power (dB)")
plt.title("Current vs Reference Spectrum")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# Plot difference (ΔdB)
plt.figure(figsize=(12,4))
plt.plot(freqs, diff, color="red", linewidth=1)
plt.axhline(0, color="black", linestyle="--", linewidth=0.8)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Power Difference (dB)")
plt.title("Difference: Current Sweep − Reference Sweep")
plt.grid(True)
plt.tight_layout()
plt.show()
# Optional: print significant deviations
threshold = 5 # dB
sig_idx = np.where(diff > threshold)[0]
if len(sig_idx) > 0:
print("\nSignificant deviations (> +5 dB):")
for i in sig_idx:
print(f"{freqs[i]:8.3f} MHz Δ = {diff[i]:6.2f} dB")
else:
print("\nNo significant deviations from reference.")
else:
print(f"\n Reference file not found: {reference_file}")
4. Create a Reference sweep
Collect a bunch of sweeps throughout the time and calculate the median value for each frequency step. Then name it sweep_reference.csv. This way you can easily identify real signals and eliminate permanent ones, like radio broadcasting stations.
5. Python script to sweep regularly and save results as csv
#!/usr/bin/env python3
import sys
import time
from datetime import datetime, timedelta, timezone
import subprocess
import os
import requests
import base64
RESULTS_DIR = "/home/x/Desktop/results/"
def rtl_sdr_sweep():
"""Perform a frequency sweep and save it as sweep_.csv"""
now = datetime.now(timezone.utc)
hour = now.hour
minute = now.minute
csvfile = os.path.join(RESULTS_DIR, f"sweep_{hour:02d}{minute:02d}.csv")
#Do the sweep through the bash command
# Build the rtl_power command
rtl_cmd = [
"rtl_power",
"-f", "50M:1760M:125k", #startfreq, stopfreq, bandwidth eg 125k
"-i", "1",
"-g", "30", # gain
"-e", "10m", #stop sweeping after 10 min
csvfile
]
# Run rtl_power
subprocess.run(rtl_cmd)
print(f"Sweep finished. Results in {csvfile}")
return csvfile
try:
csvfile = rtl_sdr_sweep()
#you could then also upload each csv file through a function here. I use a function to upload them to my server on guerillamap.com
except Exception as e:
print(f"An error occurred: {e}")
6. HTML for visualization
I use the csv’s previously uploaded to guerillamap.com – change accordingly to local files or upload them to your own server. Of course we can also arrange an upload to guerillamap and display your station on guerillamap.com. Just write us a mail to info@guerillamap.com
50 - 1760 MHz Frequency Sweeper
50 - 1750 MHz Frequency Sweeper
Choose a time in UTC to show the corresponding sweep
Alerts
Date/Time Frequency Band Peak ΔPower (dB)
Detected Peaks (ΔPower > 7 dB)
Date Frequency (MHz) ΔPower (dB)
7. Installation of SDR++, icecast and darkice to listen on the SDR remotely
(If you just want to sweep, without the possibility to actually listen live, then you can skip all the next steps.)
You can only listen in SDR++ as long as your RTL-SDR is not used for sweeping – therefore you might want to attach 2 RTL-SDR dongles to your PI; one for sweeping, the other to listen.
cd ~/Downloads
wget https://github.com/AlexandreRouma/SDRPlusPlus/archive/refs/heads/master.zip
unzip master.zip
cd ~/Downloads/SDRPlusPlus-master
mkdir build
cd build
cmake ..
make -j4
cd ..
sh ~/Downloads/SDRPlusPlus-master/create_root.sh
cd ~/Downloads/SDRPlusPlus-master/build
sudo make install
sudo apt-get install icecast2
//There will be a mask to choose your stream name and password. Set it to mystream and choose a password
sudo apt-get install darkice
sudo nano /etc/darkice.cfg
Duration = 0
bufferSecs = 5
reconnect = yes
[input]
device = plughw:2,1
sampleRate = 48000
bitsPerSample = 16
channel = 2
[icecast2-0]
bitrateMode = cbr
format = mp3
bitrate = 128
server = 127.0.0.1
port = 8000
password = *yourpassword*
mountPoint = mystream
name = My Stream
description = My live stream
url = http://mywebsite.com
genre = myGenre
public = no
(Control+X to save)
sudomodprobesnd-aloop
echo snd-aloop | sudo tee -a /etc/modules
arecord -l
card 1: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
Subdevices: 8/8
8. Listen remotely to your SDR++
sudosystemctl start icecast2
sudodarkice