This example is an AM radio receiver, implemented with a phase-locked loop for synchronous demodulation. It can be used to listen to broadcast stations on the MF (AM Broadcast) and HF (Shortwave Broadcast) bands, as well as aviation communication on the VHF airband. It uses the RTL-SDR as an SDR source, plays audio with PulseAudio, and shows two real-time plots: the RF spectrum and the demodulated audio spectrum.

This example requires an RF upconverter to listen to stations on the HF and MF bands with the RTL-SDR.

This AM synchronous demodulator composition is available in LuaRadio as the AMSynchronousDemodulator block.

Flow Graph

local radio = require('radio')

if #arg < 1 then
    io.stderr:write("Usage: " .. arg[0] .. " <frequency>\n")

local frequency = tonumber(arg[1])
local ifreq = 50e3
local bandwidth = 5e3

-- Blocks
local source = radio.RtlSdrSource(frequency - ifreq, 1102500)
local rf_decimator = radio.DecimatorBlock(5)
local if_filter = radio.ComplexBandpassFilterBlock(129, {ifreq - bandwidth, ifreq + bandwidth})
local pll = radio.PLLBlock(1000, ifreq - 100, ifreq + 100)
local mixer = radio.MultiplyConjugateBlock()
local am_demod = radio.ComplexToRealBlock()
local dcr_filter = radio.SinglepoleHighpassFilterBlock(100)
local af_filter = radio.LowpassFilterBlock(128, bandwidth)
local af_downsampler = radio.DownsamplerBlock(10)
local af_gain = radio.AGCBlock('slow')
local sink = os.getenv('DISPLAY') and radio.PulseAudioSink(1) or radio.WAVFileSink('am_synchronous.wav', 1)

-- Plotting sinks
local plot1 = radio.GnuplotSpectrumSink(2048, 'RF Spectrum', {xrange = {ifreq - 3*bandwidth,
                                                                        ifreq + 3*bandwidth},
                                                              yrange = {-120, -40}})
local plot2 = radio.GnuplotSpectrumSink(2048, 'AF Spectrum', {yrange = {-120, -40},
                                                              xrange = {0, bandwidth},
                                                              update_time = 0.05})

-- Connections
local top = radio.CompositeBlock()
top:connect(source, rf_decimator, if_filter)
top:connect(if_filter, pll)
top:connect(if_filter, 'out', mixer, 'in1')
top:connect(pll, 'out', mixer, 'in2')
top:connect(mixer, am_demod, dcr_filter, af_filter, af_downsampler, af_gain, sink)
if os.getenv('DISPLAY') then
    top:connect(rf_decimator, plot1)
    top:connect(af_downsampler, plot2)

Usage: examples/rtlsdr_am_synchronous.lua <frequency>

Running this example in a headless environment will inhibit plotting and record audio to the WAV file am_synchronous.wav.

Usage Example

Listen to WWV at 5 MHz (with a 125 MHz upconverter):

$ ./luaradio examples/rtlsdr_am_synchronous.lua 130e6

Listen to an AM radio station at 560 kHz (with a 125 MHz upconverter):

$ ./luaradio examples/rtlsdr_am_synchronous.lua 125.560e6