How to Make an SDR CW Receiver

I've been working on an SDR (Software Defined Radio) receiver that I can control via computer so I can use my TV receiver dongle to work linear transponder satellites. It hasn't work or, rather, has kind of worked strangely so last night I took it apart, metaphorically, and experimented with locally-generated signals so I could try to figure out what was going on. Having identified the errors, corrected those errors, and tested the correction, I can now explain how to make one of those things that works. The receiver part, anyway. I haven't done anything toward a working computer control of the system just yet.

Describing the way that the previous attempts didn't work is difficult. I'd put together a receiver in the way I thought was obvious that such things would work, but when I tried it I couldn't tune it properly. As I reported in earlier posts, I could actually hear the VO-52 satellite's beacon, but the tuning was wonky. That is, I could hear the CW beacon, but if I shifted the receiver's center frequency the signal did not shift up or down in pitch, it would disappear or make large jumps in pitch, like from 2000 Hz to 20 Hz in one step and sometimes shifting the center frequency down would shift the pitch down instead of up. I also could see a number of narrow-bandwidth signals on the spectrum analyzer output, but those signals would not produce bell-like notes in the speaker.

I felt confident that I didn't understand how the parts worked, so I decided to use an internal signal generator to generate a tone of known frequency and then test to see if it works by connecting it directly to the speaker. I would then add the parts of the receiver one at a time, starting from the speaker and working backwards, until it all worked. Eventually, I found the conceptual error in what I previously thought was obvious and fixed the receiver.

So how does an SDR CW receiver work? Well, a CW signal is just a single frequency that is turned on and off. To be received, that frequency is made to be some audio tone, usually between 500 and 1000 Hz, in the speaker. So, if I am listening for a signal at 145.860 MHz, and I want it to make 1000 Hz sounds, I just have to shift the signal downwards by 145.859 MHz and send it to the speaker. Simple, right? Well, actually, yes it is. By the way, you can receive single sideband (SSB) voice signals using almost exactly the same technique, except there you shift the signal by the carrier frequency without offset. If I think of it, I'll write a post about just SSB reception sometime.

The software receiver itself starts with an "ezcap" TV receiver dongle. It has a receiver that can be tuned from roughly 70 MHz to roughly 1000 MHZ, or so I understand. I've only attempted to use it for 145 and 450 MHz, and for this example I'll use the frequency of the Dutch CW beacon on VO-52, which is 145.86 MHz. To use this receiver, you tell it what frequency you want it to listen to, and then you get a whole range of signals on either side of the frequency you told it to receive. The range is limited by the sample rate, or how fast you poll the dongle for data, and it shows up with signals below the specified frequency as negative frequency signals and signals above the specified frequency as positive frequency signals.

Wait a minute! Negative frequency signals? Actually, yes. You see, you don't get just one stream of numbers, you get two. You get the signal itself and it's quadrature which is the same signal phase shifted 90-degrees. If the "real" signal is a cosine, the quadrature signal is the sine. That means that negative frequency signals have a quadrature that is 180 degrees different from the same signal at a positive frequency, so you can tell them apart. It turns out that my problem was that I was confused about how to convert the two quadrature signals into a real valued signal that could be sent out the speaker, but I'm getting ahead of myself.

Anyway, for the VO-52 receiver, I'm sampling the signals around 145.66 MHz at 705,600 sample per second. That gives me everything from 145.3072 MHz to 146.0128 MHz. I've offset it from the original 145.86 MHz by about 200KHz which is a nice convenient frequency and the doppler shift for satellites (which is approximately plus or minus about 3KHz at 2 meters and about 9KHz at 70cm) fits entirely within the signals I'm receiving. Then, I use a magic function called the "Frequency Xlating FIR Filter" to shift everything down by 199,300 Hz, filter it to 11 KHz of the center, and decimate the signal by 64. Decimate? Yes, I don't need all the data that's contained in 705,600 samples per second. If I don't need that much data, I can give my computer a break by doing the rest of the data processing at 11,025 samples per second. It's a lot easier to make a 1200 Hz filter with steep skirts at the lower sample rate. Speaking of which, the next thing I do is run the signal through a bandpass filter from 100 Hz to 1300 Hz with 100 Hz skirt width. This is a filter that uses "complex taps" so all of the negative frequency signals are removed at this point.

Then, I multiply the samples of the signal by a number. This is amplification in an SDR, and I've got a "knob" to turn to adjust it. At this point, I need to convert the signal to a real signal which, as I mentioned, where I screwed up. One of the real values associated with a complex number, which is what a signal is at that point, is a magnitude, so I thought that I wanted to send the magnitude of the signal to the speaker. The problem is, for a quadrature signal, you're adding the square of the sine of the phase with the square of the cosine of the phase, and that number is always one. So, I didn't hear anything unless there was some other signal nearby that caused that relationship to get screwed up. The proper thing to do is to simply take either the real part or the imaginary part and send that to the speaker. At that point, they carry exactly the same information (if they didn't, then the magnitude wouldn't always be 1) and there are no negative frequency signals to worry about because I filtered them out at a previous step.

At this point, I'm not quite done. The signal is at 11,025 samples per second, and the speaker likes 44,100 samples per second, so I do the opposite of interpolation, a process known as "decimation", to convert the sample rate up to 44,100 samples per second. Now, I'm done. Feed it to the speaker and I can hear it.