Skip to content

Instantly share code, notes, and snippets.

@VeggieVampire
Last active January 2, 2023 08:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VeggieVampire/28e71e662793c400489166e1819e11b2 to your computer and use it in GitHub Desktop.
Save VeggieVampire/28e71e662793c400489166e1819e11b2 to your computer and use it in GitHub Desktop.
import rtlsdr
import numpy as np
from scipy import signal
# Connect to the SDR and configure it
sdr = rtlsdr.RtlSdr()
sdr.sample_rate = 2.4e6 # 2.4 MHz sample rate
sdr.gain = 'auto'
# Define the frequency ranges to scan
ranges = [
{'lower_freq': 29.7e6, 'upper_freq': 49.99e6, 'mode': 'NFM', 'step': 10e3, 'band': 'VHF Low Band'},
{'lower_freq': 50e6, 'upper_freq': 53.98e6, 'mode': 'NFM', 'step': 20e3, 'band': '6 Meter Amateur Band'},
{'lower_freq': 54e6, 'upper_freq': 71.95e6, 'mode': 'WFM', 'step': 50e3, 'band': 'VHF TV Broadcast 2 – 4'},
{'lower_freq': 144e6, 'upper_freq': 147.995e6, 'mode': 'NFM', 'step': 5e3, 'band': '2 Meter Amateur Band'},
{'lower_freq': 225e6, 'upper_freq': 379.975e6, 'mode': 'AM', 'step': 25e3, 'band': 'UHF Aircraft Band'},
{'lower_freq': 1240e6, 'upper_freq': 1300e6, 'mode': 'NFM', 'step': 25e3, 'band': '25 cm Amateur Band'},
]
# Initialize the array to hold the results
results = []
# Calculate the total number of steps to take
num_steps = sum((r['upper_freq'] - r['lower_freq']) // r['step'] for r in ranges)
# Initialize the step counter
steps_taken = 0
# Scan each frequency range
for r in ranges:
# Set the lower and upper bounds of the frequency range
lower_freq = r['lower_freq']
upper_freq = r['upper_freq']
mode = r['mode']
band = r['band']
step_size = r['step']
# Scan the frequency range in steps
freq = lower_freq
while freq <= upper_freq:
# Set the center frequency of the SDR
sdr.center_freq = freq
# Read in samples from the SDR
samples = sdr.read_samples(256 * 1024)
# Calculate the power spectrum of the signal
f, Pxx_den = signal.welch(np.real(samples), fs=sdr.sample_rate)
# Find the indices of the power spectrum corresponding to the frequencies of interest
indices = np.where(np.logical_and(f >= lower_freq, f <= upper_freq))[0]
# Get the frequencies and power values at those indices
frequencies = f[indices]
power = Pxx_den[indices]
# Check if there are any frequencies in the specified range
if len(frequencies) > 0:
# Find the frequencies with the highest power
max_power_indices = np.argmax(power)
max_frequencies = frequencies[max_power_indices]
# Append the results to the array and save them to a file
for freq, pwr in zip(max_frequencies, power[max_power_indices]):
results.append((freq, pwr, mode, band))
print(f"Signal found at frequency {freq / 1e6:.2f} MHz in mode {mode} in the {band} band")
with open('frequencies.txt', 'a') as f:
f.write(f"{freq}: {pwr}: {mode}: {band}\n")
# Increment the frequency counter by the step size
lower_freq += step_size
steps_taken += 1
# Print the progress and current frequency
total_progress = 100 * steps_taken / num_steps
progress = 100 * (lower_freq - r['lower_freq']) / (upper_freq - r['lower_freq'])
print(f"Frequency: {lower_freq / 1e6:.2f} MHz | mode: {mode} | {band} progress: {progress:.2f}% | Total progress: {total_progress:.2f}%")
# Clean up
sdr.close()
print ("Scan completed. Check the frequencies.txt file for the found frequencies if any.")
del sdr
@VeggieVampire
Copy link
Author

The script reads in samples from the SDR using sdr.read_samples(), which takes in the number of samples to read as an argument. The number of samples to read is set to 256 * 1024, which means that the script is trying to read in 256KB of data from the SDR at a time.

After the samples are read in, the script calculates the power spectrum of the signal using signal.welch(), which takes in the samples and the sample rate as arguments. The sample rate is set to sdr.sample_rate, which is the sample rate of the SDR.

The script then looks for frequencies in the specified range using the np.where() function and the logical and operator. It gets the frequencies and power values at those indices using indexing.

If there are any frequencies in the specified range, the script finds the frequencies with the highest power using np.argmax() and appends the results to the results array. The script then increments the frequency counter by the step size and prints the progress and current frequency.

Finally, the script writes the results to a text file and cleans up by closing the SDR and deleting the sdr object.

@VeggieVampire
Copy link
Author

VeggieVampire commented Jan 2, 2023

adding more bands. change the range to this.

Define the frequency ranges to scan

ranges = [
{'lower_freq': 25e6, 'upper_freq': 26.96e6, 'mode': 'AM', 'step': 5e3, 'band': 'Petroleum Products & Broadcast Pickup'},
{'lower_freq': 26.9650e6, 'upper_freq': 27.405e6, 'mode': 'AM', 'step': 5e3, 'band': 'CB Class D Channel'},
{'lower_freq': 27.41e6, 'upper_freq': 27.995e6, 'mode': 'AM', 'step': 5e3, 'band': 'Business & Forest Products'},
{'lower_freq': 28e6, 'upper_freq': 29.68e6, 'mode': 'NFM', 'step': 20e3, 'band': '10 Meter Amateur Band'},
{'lower_freq': 29.7e6, 'upper_freq': 49.99e6, 'mode': 'NFM', 'step': 10e3, 'band': 'VHF Low Band'},
{'lower_freq': 50e6, 'upper_freq': 53.98e6, 'mode': 'NFM', 'step': 20e3, 'band': '6 Meter Amateur Band'},
{'lower_freq': 54e6, 'upper_freq': 71.95e6, 'mode': 'WFM', 'step': 50e3, 'band': 'VHF TV Broadcast 2 – 4'},
{'lower_freq': 72e6, 'upper_freq': 75.95e6, 'mode': 'FM', 'step': 5e3, 'band': 'Intersystem & Astronomy'},
{'lower_freq': 76e6, 'upper_freq': 87.95e6, 'mode': 'WFM', 'step': 50e3, 'band': 'VHF TV Broadcast 5 – 6'},
{'lower_freq': 88e6, 'upper_freq': 107.9e6, 'mode': 'FMB', 'step': 100e3, 'band': 'FM Broadcast'},
{'lower_freq': 108e6, 'upper_freq': 136.9916e6, 'mode': 'AM', 'step': 8.33e3, 'band': 'Commercial Aircraft'},
{'lower_freq': 137e6, 'upper_freq': 143.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Military Land Mobile'},
{'lower_freq': 144e6, 'upper_freq': 147.9950e6, 'mode': 'NFM', 'step': 5e3, 'band': '2 Meter Amateur Band'},
{'lower_freq': 148e6, 'upper_freq': 150.7875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Military Land Mobile'},
{'lower_freq': 150.8e6, 'upper_freq': 161.995e6, 'mode': 'NFM', 'step': 5e3, 'band': 'VHF High Band'},
{'lower_freq': 162e6, 'upper_freq': 173.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Federal Government'},
{'lower_freq': 174e6, 'upper_freq': 215.995e6, 'mode': 'FWM', 'step': 50e3, 'band': 'TV Broadcast 7 – 13'},
{'lower_freq': 216e6, 'upper_freq': 224.98e6, 'mode': 'NFM', 'step': 20e3, 'band': '1.25 Meter Amateur Band'},
{'lower_freq': 225e6, 'upper_freq': 379.975e6, 'mode': 'AM', 'step': 25e3, 'band': 'UHF Aircraft Band'},
{'lower_freq': 380e6, 'upper_freq': 399.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Trunked Military Band'},
{'lower_freq': 400e6, 'upper_freq': 405.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Miscellaneous'},
{'lower_freq': 406e6, 'upper_freq': 419.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Federal Government Band'},
{'lower_freq': 420e6, 'upper_freq': 449.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': '70 cm Amateur Band'},
{'lower_freq': 450e6, 'upper_freq': 469.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'UHF Standard Band'},
{'lower_freq': 470e6, 'upper_freq': 512e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'UHF-T Band'},
{'lower_freq': 758e6, 'upper_freq': 787.99375e6, 'mode': 'NFM', 'step': 6.25e3, 'band': 'Public Service Band'},
{'lower_freq': 788e6, 'upper_freq': 805.99375e6, 'mode': 'NFM', 'step': 6.25e3, 'band': 'Public Service Band'},
{'lower_freq': 806e6, 'upper_freq': 823.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Public Service Band'},
{'lower_freq': 849.0125e6, 'upper_freq': 868.9875e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Public Service Band'},
{'lower_freq': 894.0125e6, 'upper_freq': 960e6, 'mode': 'NFM', 'step': 12.5e3, 'band': 'Public Service Band'},
{'lower_freq': 1240e6, 'upper_freq': 1300e6, 'mode': 'NFM', 'step': 25e3, 'band': '25 cm Amateur Band'},
]

You can download a user manual for a Uniden scanner. They show a bandplan that should match if Uniden have looked at what FCC have assigned to different users. This is from the Uniden SDS200 manual and what is used when the scanner are set to use auto settings.

DEFAULT BAND SETTINGS
FREQUENCY RANGE MODE STEP (KHZ) BAND

25.0000 26.9600 AM 5 Petroleum Products & Broadcast Pickup
26.9650 27.4050 AM 5 CB Class D Channel
27.4100 27.9950 AM 5 Business & Forest Products
28.0000 29.6800 NFM 20 10 Meter Amateur Band
29.7000 49.9900 NFM 10 VHF Low Band
50.0000 53.9800 NFM 20 6 Meter Amateur Band
54.0000 71.9500 WFM 50 VHF TV Broadcast 2 – 4
72.0000 75.9500 FM 5 Intersystem & Astronomy
76.0000 87.9500 WFM 50 VHF TV Broadcast 5 – 6
88.0000 107.900 FMB 100 FM Broadcast
108.0000 136.9916 AM 8.33 Commercial Aircraft
137.0000 143.9875 NFM 12.5 Military Land Mobile
144.0000 147.9950 NFM 5 2 Meter Amateur Band
148.0000 150.7875 NFM 12.5 Military Land Mobile
150.8000 161.9950 NFM 5 VHF High Band
162.0000 173.9875 NFM 12.5 Federal Government
174.0000 215.9950 FWM 50 TV Broadcast 7 – 13
216.0000 224.9800 NFM 20 1.25 Meter Amateur Band
225.0000 379.9750 AM 25 UHF Aircraft Band
380.0000 399.9875 NFM 12.5 Trunked Military Band
400.0000 405.9875 NFM 12.5 Miscellaneous
406.0000 419.9875 NFM 12.5 Federal Government Band
420.0000 449.9875 NFM 12.5 70 cm Amateur Band
450.0000 469.9875 NFM 12.5 UHF Standard Band
470.0000 512.0000 NFM 12.5 UHF-T Band
758.0000 787.99375 NFM 6.25 Public Service Band
788.0000 805.99375 NFM 6.25 Public Service Band
806.0000 823.9875 NFM 12.5 Public Service Band
849.0125 868.9875 NFM 12.5 Public Service Band
894.0125 960.0000 NFM 12.5 Public Service Band
1240.0000 1300.0000 NFM 25 25 cm Amateur Band

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment