Central place for discussing, creating, and managing projects
Now that we have our antenna up and running, the only thing left to do is to convert the analog signal from the antenna into a digital signal we can analyze and graph using software. To accomplish this, I decided to use GNU Radio, a Python-based software toolkit containing digital signal processing (DSP) blocks, which can be easily combined into flowgraphs, as we will be discussing in-depth. Here is a link to the GNU Radio download page for Windows: GNU Radio Windows Download; take a look at this page for information on downloads for other operating systems: GNU Radio Install Wiki. Once you’ve installed the software, you may want to check out these tutorials to get familiar with the GNU Radio Companion user interface: GNU Radio Beginner’s Tutorials.
To understand the steps we will take with GNU Radio, it helps to understand the “input” signal from the antenna, and the “output” signal we want to plot in graph form. The input signal is the voltage received by the antenna setup – if you hook up the antenna and circuit to an oscilloscope, you will see a waveform, but not a perfect one. You will notice imperfections or squiggles in the waveform which wouldn’t be present if the signal the antenna was picking up was a pure tone. The little “squiggles” in the waveform are very interesting to us – they represent different frequencies of radio waves that the antenna is picking up.
This concept is closely related to the output we want to produce. We want to make two graphs: one to show how much of each frequency there is, averaged over the entire time we recorded, which will tell us what VLF stations we are picking up. The other graph we need is a time series graph – a graph of signal strength over time for one particular frequency, i.e. the Cutler, Maine 24kHz VLF station.
After considering our input and output, it is pretty clear that we need to find a way to: 1) separate out the different frequencies contained in the analog signal from the antenna, 2) record information about all of the different frequency channels over time, and finally, 3) transfer this recorded information into a convenient display. Take a look at this flowgraph, which I created using GNU Radio – some of the blocks may not make sense at first, but just keep in mind that the blocks perform steps 1 and 2, and I’ll explain the finer details of them next.
So, going back to step 1, and the discussion we had about the input signal. The very first thing we need to do is convert the analog signal into a digital signal that the computer can understand. The SDR Dongle that we discussed earlier performs this step, by taking a sample (a measurement) of the voltage from the antenna several times per second. It is important to make sure you sample very frequently; if you don’t, you may lose vital information about your signal and wind up with useless data. The variable block titled “samp_rate” in the flowgraph assigns how many samples per second the flowgraph should take. For a more in-depth discussion of sampling, I would highly recommend looking into the book The Essential Guide to Digital Signal Processing, which explains these topics much more lucidly than I can. The block “RTL-SDR Source” in the flowgraph supplies the data samples from the dongle to the flowgraph so we can process it further. From there, the data samples flow through the “Stream to Vector” block, which converts each set of 10000 samples into a single vector for processing, which makes our next step easier.
Our next GNU Radio block – the “FFT Block” – goes straight to the heart of solving the problem in step one. Remember our earlier discussion about how the voltage of the antenna’s input signal is not a perfect sine wave, but instead has imperfections or “squiggles” that contain valuable information about the frequencies we are picking up? Well, the FFT block performs a mathematical computation called a Fast Fourier Transform (FFT) which extracts the frequency information from those squiggles. The FFT determines the magnitude (how much there is) of each frequency channel. In this flowgraph there are 8192 frequency channels, each 200 Hz wide, centered on the Local Oscillator frequency of 125 MHz (see Electronics post for more on Local Oscillators). The magnitude of each frequency channel is saved as one sample in a vector (for those of you familiar with coding, think of a vector as a list in Python or an array in C), and since each vector has 10000 samples in it, each of those samples is the magnitude of the frequency channel it represents.
Now we should take a look at something we have not paid much attention to up to now, and that is the color of the tabs on the blocks; as you can see, some are orange and some are blue, and each color represents a different data type: orange = float, blue = complex. Float data types are simple – just real numbers in decimal form. Complex data types are a bit more, well, complex, because they are made up of two components – real and imaginary. Real and imaginary numbers are used to represent two components of the waveform (or voltage) we are sampling; the phase and the magnitude. This is a very complicated topic, even for a lot of pros to understand. I’ll again resort to adding a link here to an article that helped me understand why complex numbers are used, and what they mean:
The short of all this is that in order for us to convert the data into the float form we need for the following steps, we had to use the “Power” block to square the resulting samples from the FFT block to eliminate complex numbers.
So, that completes Step 1 – we now have the frequency information we need in the right data format! The next blocks in our flowgraph work toward addressing Step 2, the problem of saving our data to a file for future use. The next block we come to is the “Integrate” block, and it takes the average of 10000 vectors, as indicated by the value 10k in the Decimation field of that block. The Python analog to the Integrate block would be a function that takes the average of element 0 for all 10000 lists, then takes the average of element 1, etc., and represents that as a single list of magnitudes). This has two purposes – first of all, it reduces the amount of data that we eventually will have to store in a file – we are now saving one vector for every ten thousand that pass through the flowgraph, which as you can imagine reduces the amount of data saved significantly. The second and more scientific reason to add this block is that averaging a lot of samples increases the signal to noise of the data, because if you are picking up white noise centered around 0 volts, it will average to about 0 even if the individual data points are above or below zero. Integrating (averaging) several samples also makes it clear if there is an actual signal being picked up, by averaging all of the high-magnitude samples together, which decreases the distracting effect of small, rapid random variations in the signal.
The vectors we have processed now move from the Integration block to the fairly self-explanatory “File Sink” block, which simply saves each vector to a file in order. The file can be accessed later by either another GNU Radio flowgraph, or a program written for the task.
This brings us to Step 3: displaying our data in a graph form. You might have noticed that there is another block in the flowgraph called a “Waterfall Sink” which I did not explain in the previous section. The waterfall sink displays a spectrogram plot that updates in real time, so you can see the signals you are picking up at the moment. I used this block solely for diagnostic purposes (i.e., making sure my antenna is working and that nothing has gone haywire), but it really isn’t all that helpful for actually interpreting data, because the signals we are looking for vary over timescales of hours rather than the few seconds that the spectrogram displays. I didn’t find any helpful GNU Radio blocks for the kind of graphs I wanted to make, so I decided to program my own display tool using Python.
I won’t get boring by going into all of the details of the program’s syntax, but I will attach a link to the code on Github here for reference (to see the program, click on plotData.py): Python Data-Graphing Program. This program creates both of the graphs we talked about earlier; it creates a spectrum plot (see Figure 1), which shows us which frequencies we picked up and the magnitude of each frequency channel (remember: the output from the FFT block), averaged over the entire time we recorded to get maximum signal-to-noise so that we could easily tell which frequencies we picked up. The program also created the time series plot (see Figure 2) we need to monitor the signal strength of the Cutler, Maine VLF station over time to look for solar flares!
By the way, if you are interested in learning more about Python coding so you can write up programs like this on your own, here is another great book to check out: Python Programming: An Introduction to Computer Science. I worked through the entire book and did most of the exercises in it, and by the time I finished I really understood what the code and its syntax actually mean, rather than just memorizing the language.
Digital signal processing is probably the most conceptually difficult part of amateur radio, with a big learning curve at first. But after you read more about it and do a lot of experiments, things begin to make sense. I think it’s really fascinating to understand how signals are transformed from physically measurable quantities in the real world to data represented on a computer screen that scientists (or anyone!) can analyze to learn more about our universe.
To learn about the science results of this antenna experiment, check back soon for my next blog post.