A Software Defined Radio Receiver
A Software Defined Radio Receiver
A Software Defined Radio Receiver
Ben Gelb
June 8, 2004
Abstract
The current availability of high-performance computing has given rise to the era of
digital signal processing. This paper will outline the design of a software radio receiver
which combines high speed analog to digital conversion with digital signal processing
techniques to demodulate radio-frequency signals.
1
1 Introduction
As an electo-magnetic wave moves past a conductor, it causes the electrons in the wire to
oscillate and produce a measurable voltage at points within the wire. The frequency of this
oscillation is what is called a signal’s frequency.
Most radio receivers use analog circuitry to downconvert the signal to a lower frequency
which is audible to the human ear, then demodulates the signal into intelligible speech.
A software defined radio receiver, on the other hand, works by directly measuring the
voltage present in the conductor (this conductor, by the way, is called an antenna) with an
analog to digital converter (ADC) and quantizing it as an integer relative to a fixed voltage
reference.
Through a process called sampling, voltage samples are collected at a high rate (in the
case of the design detailed herein, at a rate of 20MHz).
2 Sampling Theory
Sampling is the process of digitizing a signal by recording discrete samples of the signal’s
amplitude at specific points in time. If done right, a signal can be captured and perfectly
Like any good theory, the sampling theory is bound by a few conditions.
One of the first conditions is that the signal we are digitizing be band-limited. This means
that we must guarantee that the frequncies contained in the input signal fall within a certain
range - or bandwidth. This is accomplished by the use of a filter (usually a low-pass filter)
2
which allows certain frequencies to pass, and drops others.
It turns out that any non-sinosoudial waveform is comprised of an infinite series of sine
and cosine functions oscillating at different frequencies. A square wave, for example, contains
harmonics that are much higher in frequncy than the fundamental wave. Removing these
components can alter the shape of a wave, so its important to take this into accont when
calculating the bandwidth.
The next condition says that if we sample a signal fast enough, we won’t lose any infor-
mation. But how fast is fast enough?
Eq (1), known as the Nyqist Rate Equation, tells us that our sampling rate must be
twice as fast as the bandwidth of the signal we want to digitize (in a simple case, highest
Eq (1) fs = 2fbw
To see why Nyquist’s equation is true, consider what would happen if we ignored it, and
0.4
0.2
0
-0.2
-0.4
-0.6
-0.8
-1
0 1 2
Time (µs)
The 1 MHz wave would be sampled properly, but the 5 MHz wave would not. Notice
that if the sample points were at 0µs, .25µs, .50µs, .75µs and 1µs, the samples will be the
same for both signals. As a result, the 5 MHz signal will not appear to be a 5 MHz signal,
but rather as an alias at 1 MHz, right on top of the real 1 MHz signal. This is bad for
business. Two signals on top of each other are worthless, because you can’t tell them apart,
and can’t accurately do any processing with them.
3
If we wanted to use both signals we’d need to up the sample rate to at least 10 MHz, but
signal would be filtered out and the 1 MHz signal could be digitized properly.
But what if we were concerned with the 5 MHz signal? As long as the passband we care
about is less than 2 MHz, we should be able to do it. Suppose we install a bandpass filter
that passes signals from 4 to 6 MHz through, and drops all others. The effects of aliasing
suddenly turn in our favor. Because we can predict exactly where the alias will appear,
and because we’ve made sure no other signals will collide with that alias, we can effectively
downconvert the 4 to 6 MHz passband to 0 to 2 MHz.
In either case, we’re limiting the bandwidth as prescribed by Eq (1). The band-limiting
filter, incidentally, is called an anti-aliasing filter, because it prevents the appearance of
aliases in the sampled data.
It’s probably occurred to you by now that despite the fact that an analog signal varies
thing? By capturing only discrete steps of the signal, it seems certain that something must
have been left out, or that there are ”holes” in the signal.
I must admit the concept that we haven’t lost anything is a bit counterintuitive, but my
claim is actually quite real. Consider what would happen if we took a 10 kHz sine wave, and
sampled it at 40 kHz.
4
10 kHz Sine Wave
1
Sampled at 40kHz
10kHz Signal
0.5
Amplitude (V)
-0.5
-1
0 100 200
Time (µs)
It would appear from this example that we’ve lost a part of our signal. It definitely looks
inferior to the original waveform (dotted line), and the effect of the digitization is obvious.
How is it then that we have not lost any information?
The original signal is a sine wave and the digitized version looks like a sort of stair-
steppy squareish wave. In any case, it certainly doesn’t look like perfect recovery. Remember
though, that the input signal was band limited at half the sampling rate. The square wave is
composed of many sine waves, one at the fundamental frequency (10kHz), and several higher
frequency components - one at the sampling frequency (40kHz), one at every harmonic of the
sampling frequency (80kHz, 120kHz, etc.), and the sum and difference of the fundamental
and the sampling frequency and each of its harmonics. The overall effect is the occurrence
positive and negative images of the frequencies in the Nyquist range centered around each
of the harmonics of the sampling frequency.
All of the harmonics have one thing in common - they’re all outside of the allowable
range for the original signal, so it’s easy to determine that they aren’t supposed to be there.
If the same band-limiting filter (now called an anti-imaging filter instead of an anti-aliasing
filter) is applied to the output as was applied to the input, the extraneous components are
removed and all that’s left is the original 10kHz sine wave, perfectly recovered. Cool, huh?
5
3 Modulation Theory
If all radio signals were just simple sine waves, they would not be terribly useful. You’d be
severely limited in your ability to communicate, in that you’d be able to communicate two
states - on or off. Once upon a time, all radio communication was actually done in this way.
It was called Morse Code, and it worked pretty well in its day, but it hardly stacks up to
forms of modulation, but all of them work by modifying one or more of three properties of
a carrier wave; frequency, phase, and amplitude.
One of the simplest forms of modulation, called Amplitude Modulation, puts an audio signal
on a radio-frequency signal by varying the amplitude of the carrier. The following illustration
6
3.2 Traditional AM Demodulation
A electronic circuit, shown below in its absolute simplest form, can reverse this process -
7
A nearby radio transmitter will cause a voltage in the antenna wire. The tunable LC
circuit will cause all signals except the one on the resonant frequency to be shorted to ground,
so they will not be demodulated. This forms a rudimentary filter which allows the tuning
of a specific station. The signal that is tuned-in will look like the AM modulated signal in
section 3.1.
The average voltage is 0 volts, as the signal is symmetrical with respect to the x-axis.
By passing the signal through the diode shown in the circuit above, it’s as if the bottom
half of the waveform was chopped off (a diode allows current to flow in only one direction).
Once the signal is aymmetrical with respect to the x-axis, the voltage is not 0 and it is not
constant. It varies exactly as the audio signal used to modulate the wave, and is then sent
into the earphone (we can ignore the bumpiness of because it’s way beyond hearing range,
or, if we feel like being espescially elegant, we could filter out the radio-frequency component
with a small capacitor, which looks like a conductor at high frequencies, but an insulator at
low ones).
Before we can go any further, we must leave modulation for a minute to explain a
8
particular method of sampling, critical to digital signal processing.
If you remember what we said about modulation just a moment ago, there are three prop-
erties of a wave that can be used to convey information; phase, frequency (rate of change
of phase), and amplitude. A stream of individual discrete samples is great for storing and
recalling analog information, for example, a CD player, or computer soundcard. A single dis-
crete sample, however, fails to tell much about any of the three quantities we’re so concerned
with, which is why a technique called quadrature sampling is employed in DSP applications.
The idea behind quadrature sampling is to use two sets of samples which are 90 degrees out
of phase with each other. By pairing each in-phase sample with a quadrature sample (90
degrees later) we can determine the instantaneous amplitude and phase of a signal from any
In order to demodulate an AM signal in software, we need to find the amplitude of the carrier
wave. The amplitude is what carries the voice information on an AM signal. Below I derive
the math for finding the amplitude.
where m is the amplitude of of the wave, and φ cycles from 0 to 2π at the carrier frequency.
A cosine function shifted 90 degrees is equal to a sine function, so the quadrature com-
ponent Q can be written
Q = mcosφ
9
To find m start by adding the squares of I and Q.
I 2 + Q2 = m2
Take the square root of both sides, and voila!
√
m = I 2 + Q2
4 System Setup
In this section I describe the configuration I used, both the GnuRadio software and the
hardware I used for my experimentation with software defined radio technology.
4.1 Hardware
There were two main pieces of hardware used in my project (besides the computer itself).
First and foremost was a large wire antenna, 102 feet in length, strung between two trees at
my residence. The antenna was fed with coaxial cable, which ran from the antenna, inside
my house to my computer.
The computer was outfitted with a PCI-DAS4020 data acquisition card supplied free of
of the data acquisition card and the RF voltages on the antenna were measured.
The GnuRadio package was used as a platform for experimentation with software radio.
The GnuRadio codebase is organized into many logical modular pieces of code which can be
assembled together into a processing chain. The topology of the processing chain determines
10
what function the software radio will perform. In my case, it was fairly straightforward.
Data was captured from the data acquisition card, decimated into I and Q and downmixed
to a 0 Hz center, filtered so that only one signal was being processed (not every AM station
at once), demodulated, filtered and decimated again, and written to the sound card.
Two methods were used to capture data from the data acquisition card. The first, and
simplest, is a command line utility which collects data from the card at writes it to a file.
The data can later be read out of the file and processed. This method has some serious
limitations, however. Each sample is 12 bits long, and is stored as a 16-bit integer (there
are no 12-bit datatypes). At a sampling rate of 20 MHz, that is 40 MB of data per second!
This exceeds the sustained write capability of most hard drives, so data must be collected
into RAM. With 512 MB of RAM, this limits capture to about 10 seconds worth.
The other method of data capture is to capture the data directly into the program
doing signal processing. This way, the data does not have to be written to disk because
it is captured and processed in real time. This functionality is provided by a data source
object which communicates with the data acquisition board and can be connected to other
processing blocks.
The Fast Fourier Transform, a type of Discrete Fourier Transform, is a powerful digital signal
processing tool. It transforms a signal in the time domain into the frequency domain. This
can be used for many purposes. In my case, I used it to help me visualize signals at various
stages in the processing chain by plotting the output of an FFT onto a display.
Below is the output of an FFT plot which ranges from 0 to 10 MHz. The peaks show
11
strong radio signals. The large cluster to the left of the plot shows the American AM
broadcast band. The peaks over the rest of the spectrum are from shortwave broadcasting
stations, some domestic, and others based elsewhere around the globe.
Digital filtering is very important to a software defined radio system. Digital filters, just
like analog filters, drop signal of certain frequencies while allow others to pass unattenuated.
Filters are used in my project to seperate out a single radio station from an entire 10 MHz
section of spectrum (the bandwidth allowed by Nyquist equation).
Filters also will decimate the sample data. The bandwidth allowed to pass through
a filter is often considerably smaller than the range of signals going into a filter. After
filtering is performed, the sampling rate is often far faster than is necessary. Decimation
combines samples together to lower the sampling rate, which means less data to process, so
less CPU time is used. There is no loss of information here (as long as Nyquist’s rules are
12
followed). The other reason for reducing the sampling rate is so the data can be written to
the sound card for output. The soundcard supports only a few sampling rates, like 32 kHz,
for example. So, the original sampling rate of 20 MHz must be reduced to 32 kHz by the
The data acquisition card does not perform quadrature sampling on its own. A special
processing block transforms the one dimensional set of samples into a two dimensional set
by pairing up sets of samples and reducing the overall sampling rate. The same processing
block also performs a transform with shifts a particular frequency to 0 Hz. This is necessary
so that all other signals can be filtered out.
The following figure depicts a filtered and shifted AM broadcast signal. The carrier is
the peak in the center, and the audio energy is in the sidebands.
13
4.7 Demodulation
The demodulation math is another processing block. It uses the math outlined in section
3.4. I wrote my own version of this block, which is contained in the appendix of this paper.
5 Appendix
5.1 am demod.cc
/*
* File: am_demod.cc
* Program that uses GNURadio libraries to demodulate
* AM signals.
*
* Date: 11/7/2003
* (c) 2003 Ben Gelb
*
*/
#include <make_GrMC4020Source.h>
#include <GrFFTAvgSink.h>
#include <GrFFTSink.h>
#include <VrFixOffset.h>
#include <GrFreqXlatingFIRfilterSCF.h>
#include "GrAMDemod.h" //AM Demodulator Code
#include <GrFIRfilterFSF.h>
#include <VrAudioSink.h>
#include <VrConnect.h>
#include <VrMultiTask.h>
#include <VrGUI.h>
#include <gr_firdes.h>
#include <gr_fir_builderF.h>
#include <VrNullSink.h>
#include <VrFileSource.h>
#include <getopt.h>
14
const int chanTaps = 75;
const int CFIRdecimate = 125;
const float chanGain = 2.0;
VrGUI *guimain = 0;
VrGUILayout *horiz = 0;
VrGUILayout *vert = 0;
cerr << "Input Sampling Rate: " << inputRate << endl;
cerr << "Complex FIR decimation factor: " << CFIRdecimate << endl;
cerr << "AM Demodulator Sampling Rate: " << demodRate << endl;
cerr << "Real FIR decimation factor: " << RFIRdecimate << endl;
cerr << "Audio Sampling Rate: " << audioRate << endl;
VrSource<short> *source;
// --> short
source = make_GrMC4020SourceS(inputRate, MCC_CH2_EN | MCC_ALL_1V);
15
// short --> short
VrFixOffset<short,short> *offset_fixer = new VrFixOffset<short,short>();
VrSink<VrComplex> *fft_sink1 = 0;
VrSink<float> *fft_sink2 = 0;
VrSink<short> *fft_sink3 = 0;
16
fft_sink1 = new GrFFTSink<VrComplex>(vert, 0, 80, 512);
m->add (band_scope);
m->add (fft_sink1);
m->add (fft_sink3);
m->add (fft_sink2);
m->add (final_sink);
m->start ();
guimain->start ();
while (1){
guimain->processEvents(10 /*ms*/);
m->process();
}
}
5.2 GrAMDemod.h
/*
* File: GrAMDemod.h
17
* Code to find the magnitude of I an Q, thus demodulating AM.
* The math was supplied by me (Ben Gelb) but this code is based
* on VrQuadratureDemod.h in src/pspectra/lib/vrp/.
*
* Date: 11/7/2003
* (c) 2003 Ben Gelb
*/
#ifndef _GRAMDEMOD_H_
#define _GRAMDEMOD_H_
#include <VrHistoryProc.h>
#include <math.h>
template<class oType>
class GrAMDemod : public VrHistoryProc<complex,oType> {
protected:
float gain;
public:
virtual const char *name() { return "GrAMDemod"; }
void setGain(float g){ gain = g; return;}
virtual int work(VrSampleRange output, void *o[],
VrSampleRange inputs[], void *i[]);
virtual void initialize();
GrAMDemod(oType g);
GrAMDemod();
};
18
//I is thre real component, J is the imaginary component
//find the magnitude
*o[0]++=(oType)(gain * sqrt(real(val)*real(val)+imag(val)*imag(val)));
}
return output.size;
}
template<class oType>
GrAMDemod<oType>::GrAMDemod(oType g)
: VrHistoryProc<complex, oType>(1), gain(g)
{
}
#endif
#define a filter to carve out a 20kHz (or so) "channel" centered around the rx_freq
#this filter "decimates" by a factor of 125, which means that it averages groups
#of 125 samples together, reducing the overall sampling rate. It also uses this
#process to create I and Q components so the signal can be demodulated using
19
#quadrature techniques.
fg = rx_FlowGraph ()
sample_rate = fg.get_sampling_rate()
20
#define a secondary filter that selects a single station out of the 20kHz passband
channel_coeffs = gr_firdes_low_pass (1.0, 160e3, 6e3, 1e3, gr_firdes.WIN_HAMMING)
# radio code really stops here. GUI the rest of the way down.
# -------------------------------------------------------------------
if __name__ == ’__main__’:
self.frame = frame
21
hbox.Add (1, 1, 1, wx.EXPAND)
hbox.Add (wx.StaticText (self, -1, "Set Rx Freqency: "), 0, wx.ALIGN_CENTER)
self.tc_freq = wx.TextCtrl (self, -1, "", style=wx.TE_PROCESS_ENTER)
hbox.Add (self.tc_freq, 0, wx.ALIGN_CENTER)
hbox.Add (1, 1, 1, wx.EXPAND)
self.sizer = vbox
self.SetSizer (self.sizer)
self.SetAutoLayout (True)
self.sizer.Fit (self)
wx.EVT_CLOSE (self, self.OnCloseWindow)
wx.EVT_TEXT_ENTER (self, self.tc_freq.GetId(), self.EvtTextEnter)
wx.EVT_COMMAND_SCROLL (self, self.vol.GetId(), self.EvtVolAdj)
self.UpdateStatusBar ()
22
class MainFrame (wx.Frame):
def __init__ (self):
wx.Frame.__init__(self, None, -1, "MC4020 HF Receiver")
self.CreateStatusBar ()
mainmenu = wx.MenuBar ()
menu = wx.Menu ()
menu.Append (200, ’E&xit’, ’Get outta here!’)
mainmenu.Append (menu, "&File")
self.SetMenuBar (mainmenu)
wx.EVT_MENU (self, 200, self.OnExit)
self.panel = TestPanel (self, self)
wx.EVT_CLOSE (self, self.OnCloseWindow)
def OnInit(self):
print "TestApp: pid = ", os.getpid ()
frame = MainFrame ()
frame.Show (True)
self.SetTopWindow (frame)
print "FlowGraph: ", frame.panel.fg
frame.panel.fg.start ()
return True
# ----------------------------------------------------------------
23
6 References
<http://aurora.phys.utk.edu/ forrest/papers/fourier/>
KR7A SSTV Demodulator.
<http://www.agurk.dk/bjarke/DSP/DSP%20FM%20Demodulator%20for%20SSTV.htm>
Panter, Philip F.. Modulation, Noise, and Spectral Analysis. 1965. McGraw-Hill Inc: New
York.
Pohler, Ken C.. Principles of Digital Audio. 3rd Ed. 1995. McGraw-Hill
Inc: New York.
Youngblood, Gerald. A Software Defined Radio for the Masses. QEX, Jul 2002.
24