This project came about when I was looking for a way to build a cheap spectrum analyzer. So I did some research online and came across the RTL-SDR dongle. This little device can tune to any frequency between 25 MHz – 1750 MHz. It converts the IF signal to digital and makes the data available to programs via a USB port. There are many excellent free spectrum analyzer software out there that works with this device. Since I wanted to learn more about software defined radios, I decided to write my own spectrum analyzer program.
As with any python program, first I checked if there is a module that can read data from this device. Sure enough there is pyrtlsdr. Next I had to install the driver for the SDR dongle. The default driver that Windows installs when you plug the device the first time doesn’t really work. The quick start guide from the RTL-SDR website helped me install the correct driver.
The SDR outputs sampled I and Q data in the form of an array of complex numbers. The I and Q data are in the time domain. To see the spectrum, the data must be transformed to the frequency domain. In software this can be done quite easily using the Fast Fourier Transform (FFT).
The maximum frequency in the sampled data depends on the sampling rate. The maximum stable sampling rate of the RTL-SDR is 2.4 MS/s. So the maximum frequency is 1.2 MHz, which is half the sampling rate (according to Nyquist theorem) The frequency resolution of the spectrum depends on the number of sample points passed on to the FFT algorithm. More points give higher resolution. The trade off is that it takes more time to gather more points. I decided to keep the sampling rate fixed at 2.4 MS/s and adjust the number of sample points depending on the frequency resolution requested by the user. If the user requests a span greater than 2.4 MHz, then samples must be taken at more than one centre frequency to construct the full spectrum.
There are a few options in python to compute the FFT of the sampled data. I used the welch function of the Scipy.signal module:
# calculate power spectral density f, pxx = signal.welch(samples, fs=self.sdr.sample_rate, nperseg=self.numsamples)
The welch function returns the power spectral density and the associated frequencies. For plotting the spectrum I used the matplotlib module. I embedded the matplotlib plot window in a tkinter GUI with all the controls for the spectrum analyzer. To make the plot update in real time I used the FuncAnimation tool of matplotlib:
self.animation = ani.FuncAnimation(self.fig, self.animate, repeat=False)
Here’s a look at a few local FM stations with the spectrum analyzer:
The sweep rate is around a few hundreds of milliseconds, which is a lot slower than a typical spectrum analyzer, but adequate for general use. Here’s a capture of the zero modulation spectrum of one of the broadcast stations:
Complete source code for this program can be found here