PiCar - Raspberry Pi Car Radio Project - Part 7
Software Defined Radio
In the last installment of the PiCar project I talked about how I take Pulse-code modulated (PCM) data from the radio and convert it to sound which play on the car speakers. But how do I get PCM data from radio signals in the airwaves? To perform this magic PiCar uses Software Defined Radio (SDR).
Software defined radio is indeed a amazing piece of technology. Originally these devices were the purview of military and spook agencies. I remember hearing rumors of such devices back in my EE college days in the early 80s. But it wasn’t until the early 00s and the introduction of GNU radio did things start to get interesting.
Even so, the concept SDR was still out of the reach of most hobbyists
In 2008, Realtek, a Taiwanese semiconductor design company introduced a low cost USB dongle based on the Realtek RTL2832U chip that could be used for processing digital video broadcasting in TV sets. Two years later a hardware hacker named Eric Fry discovered that these chips could also be used to decode radio signals. This got picked up by the folks at Osmocom (Open Source Mobile Communications project) who were creating the RTL-SDR library.
What is interesting about the Realtek RTL2832U chips that it converts the radio signals to a stream of raw I/Q signals (more on that later) can be read over USB by fairly modest computers.
The low cost of these dongles, and the creation of a librtlsdr software driver opened the floodgate to the software radio projects. Today there are a plethora of apps that can talk to these devices and be used to scan the radio spectrum.
Software Radio quickly became a hit in the Ham Radio community as well as the security hacking community. SDR allows you to peer into the workings of things like realtime aircraft flight paths, home weather stations, utility meters, tire pressure sensors and even decode remote car keys.
If you want to see just how vulnerable common wireless devices are, I highly recommend you browse the notes from the EE26N: The Wireless World, and the Data You Leak class at Stanford.
There are a huge list of interesting applications and open source projects built on librtlsdr. I recommend you take a minute to survey what people have done. Similar, PiCar links to the open source librtlsdr library to communicate with a RTLDSR device.
Depending on what you want to do with SDR you have many hardware choices. HackRF One is very popular among the SDR community. It is a powerful tool and is capable of transmission and reception of radio signals from 1 MHz to 6 GHz.
Another great one is the SDRPlay RSP1A. Note that it has a different API than librtlsdr.
For my CarPi project I chose the Nooelec NESDR SMArt XTR receiver. It was in the $60 range and very well built, and came with a few test antennas.
You can find a collection of cheap SDR receivers, but the consensus is you get what you pay for. Look out hot heat sensitivity and noise issues.
I haven’t been able to find great documentation for librtlsdr but it wasn’t to hard to decipher the source code by looking at the headers to figure out how it works. I wrote an C++ interface class that did a lot of the work for me.
It was easy to get a list of devices ( you might have more than one) plugged into my USB bus:
At which point you make a call to rtlsdr_open to open the device you want, (or just assume it’s always the first device If you are that kind of programmer). Then you can set your desired parameters with functions like rtlsdr_set_sample_rate, rtlsdr_set_tuner_gain and rtlsdr_set_agc_mode. When you are ready to tune in a signal, you call rtlsdr_set_center_freq.
You can use the code from rtl_fm as working example.
When you are ready to stream, use the rtlsdr_read_sync or rtlsdr_read_async api to start getting data from the SDR.
I/Q and Quadrature
OK, I hand-waved the topic of I/Q signals. Let cover that now. This is the data stream that is emitted by the SDR devices. The rtlsdr read APIs will fill your buffer with a set of 32 bit integer pairs. These pairs represent the real and imaginary I/Q values.
Wait-What! What are you talking about and how does that get converted to a PCM stream?
I will start by saying that the I/Q data has to pass through demodulator code. Just like a radio has a demodulator circuit, so does our our software.
For example, the broadcast FM receiver in PiCar is based on SoftFM by Joris van Rantwijk. I liked Joris’s code because it could produce stereo signals. A more general approach can be found in is rtl_fm, the command line program that comes with the library.
To understand how decoding works we need to start with getting an understanding of how signals are broadcast. There is a lot to cover here and I am going to point you to a number of resources available depending on how deep you want to go. For now lets just discuss AM and FM signals
If we look at the simple audible sine wave, let say 261.625 Hz which is the musical Middle C. Let’s call that the signal. This signal can only travel so far in air.
To transmit on radio we use a higher frequency somewhere between 20 kHz to around 300 GHz that radiate off an antenna into space. We will call that the carrier. We can use the signal to either dynamically change, or more precisely, modulate the carrier’s amplitude (AM) or frequency or phase (FM). This is depicted in the diagram above.
The receiver reverses the process. It takes the higher frequency carrier signal, that has been modulated with level or frequency deviations corresponding to the the original signal .
Since these frequencies are rather high and typically above the data rate that our Raspberry Pi can handle, we need something that can convert these signals to a low enough data rate that it won’t overwhelm our CPU.
This is where the fancy math comes in. We capitalize on one of the characteristics of waves called superposition. Waves have the property that we can take combine two waves in such a way they will have an additive or subtractive interference effect depending on where they are in the peaks and valleys.
Let say we wanted to create a change in amplitude (AM) or change the phase (FM) of our carrier.
One way to do that is to combine two sine waves which are 90 degrees out of phase with each other and selectively change the amplitude of one or both of those sine waves. When we add them together, we see that the resultant wave is adjusted either in phase or amplitude. Just by controlling amplitude of one or both of those out of phase sine waves.
The we have two waves shifted 90 degrees out of phase, one is a called the sine wave and the other is a cosine wave. The technical term for this is quadrature. As 90 degrees is a quarter of a circle.
In this following example, I will refer to the amplitude of the cosine wave (90 degrees out of phase) as I and the value of the sine wave ( 0 degrees in phase) as Q.
We can then take a stream of I/Q pairs of digital data in our computer and feed them to a digital to analog converter that controls two oscillators that are 90 degrees out of phase (quadrature) and are tuned to our carrier frequency .
And thus generate a radio signal
Conversely we can the same method to decompose our modulated radio frequency signal into two sets of data streams that 90 degrees out of phase.
The interesting thing is can sample the data from the analog to digital converter at a substantially lower rate than the carrier frequency.
This process is effectively what the SDR hardware does.
I’d like to point you to a great video demonstrating the combination of waves in quadrature to create modulation of signal.
But how do we get to audio?
OK so the SDR devices gives us a stream of I/Q values. But how does that convert to PCM audio signals. Thats where the math comes in.
Let’s take the original sine wave, but instead of plotting it on an x/y graph of amplitude vs time as we did above, instead we take a given I/Q pair and plot it that on a graph.
What we will find is the I/Q point looks like this.
What we will discover is that for AM, the distance from the origin of the circle is equal to the amplitude of that sample, and the angle is the phase of the signal at that time.
Wait, these are polar coordinates, so if we use basic trigonometry, we could decode the instantaneous amplitude of a I/Q sample as thus:
Actually we don’t even have to take the square root, we could just sum the squares and adjust the gain of the output signal to one that is comfortable.
Graphing the I/Q pairs over a third dimension of time gives us something like:
FM signals a are a little bit more complicated. what we are looking for is not the change in distance from the center, but rather the change in phase, or angle from the last I/Q pair we sampled.
For FM the I/Q signals graphed over time looks like:
Going back to the I/Q graph, we would take the arc tangent of the I/Q pair to get the angle of the sample, and thus the phase, and then compare it to the previous one find the change in phase. Well, in theory…
You know what they say about the difference between theoretical and practical? In theory, there is no difference.
In practice there is a lot more that needs to done, not only do you need to filter out all the noise in the modulated signal, but there is lots of data in the signal, especially in broadcast FM that you need deal with.
In the software, it is convenient to treat those I/Q pairs as a complex number. Remember those. They have a real and imaginary component. We can use the complex number and convert back and forth from Cartesian (like X/Y graph) to polar (direction/magnitude) coordinates to make the math even simpler.
OK I skipped a whole lot of very elegant and in my opinion very cool math, that makes real reasons we use I/Q is much more interesting.
I can’t begin to do the topic justice, but Michael Ossmann of Great Scott Gadgets, the maker of HackRF certainly can. I really recommend his online series of videos on software radio.
These is certainly a lot more to be said here, and I will try and cover how I do this in PiCar in a later section. For now please take some time and checkout Michael Ossmann’s videos and the EE26N class notes I mention above.