Close

Testing the Receiver

A project log for Homebrew SDR Receiver

My first attempt at building a phasing direct conversion receiver.

paul-horsfallPaul Horsfall 08/25/2024 at 15:290 Comments

The Receiver

I've now built most of the building blocks of my receiver design. The only thing I don't have is the anti-aliasing filters, and I guess I can live without those for now. So I decided to assemble the entire receiver for an end to end test.

Here's what it looks like:

Set-up

I configured the LO for 101.1 MHz to tune the receiver to Classic FM, since I know its signal is strong here. I set the IF amplifiers to their max gain since I know the overall system only just has enough gain, even for strong signals.

Each IF amplifier has a DC offset adjustment which needs to be manually tuned. I'm currently doing this by looking at the output of the IF stage on the scope. I initially had trouble here, because I didn't have enough adjustment to trim out the DC offset I saw in practice. I based the initial adjustment range on the DC offset I saw when testing the mixer standalone, but in practice the offset is much larger. My current guess is that energy is radiating directly from the LO to the antenna, causing a lot more self mixing than I saw when testing without an antenna. I temporarily worked around this by reducing the value of the resistors between the pots and the op-amp inputs, which increases the range of available adjustment.

Sampling, a little DSP, and some success?

With the DC offset adjusted as best I could, I captured three short chunks of IQ data from the ADC. This is just a case of piping data from a serial device to a file on my laptop. I then wrote a little bit of Python code to demodulate the captured IQ data and extract the mono audio channel.

Here's the result: receiver-test.mp3

Clearly the quality is terrible, but at least it does something. In truth, I only managed to get recognisable audio out of one of the captures I made, and I don't yet know why.

Next Steps

Now that I have something working I can think about how to improve it. I've not decided how to go about that, but some initial ideas are:

Code

For posterity, here's the gist of the Python code I mentioned above:

import sys
import numpy as np
from scipy.io import wavfile

def pk(arr):
    return np.max(np.abs(arr))

def norm(chan):
    chan0 = chan - np.mean(chan)
    out = chan0 / pk(chan0)
    return out

def demod(xt):
    return np.diff(np.unwrap(np.angle(xt)))

def main(infile, outfile):
    channel = np.load(infile)

    sample_rate = 250e3
    D = 6 # decimate by this

    iq = norm(channel[0]) + 1j*norm(channel[1])
    x = demod(iq)
    x = x[::D] # decimate
    x /= pk(x) # normalize
    x *= 32767
    x = x.astype(np.int16)

    wavfile.write(outfile, int(sample_rate / D), x)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print(f'usage: {sys.argv[0]} <infile> <outfile>')
    else:
        main(sys.argv[1], sys.argv[2])

Discussions