Subscribe

RSS Feed (xml)

Powered By

Skin Design:
Free Blogger Skins

Powered by Blogger

Network Security Map Network Protocol Map

Thursday, May 29, 2008

Exploring GNU Radio by Eric Blossom - A Complete FM Receiver

Example 2 shows a somewhat simplified but complete broadcast FM receiver. It includes control of the RF front end and all required signal processing. This example uses an RF front end built from a cable modem tuner and a 20M sample/sec analog-to-digital converter.

Example 2. Broadcast FM Receiver#!/usr/bin/env python
from gnuradio import gr
from gnuradio import audio
from gnuradio import mc4020
import sys
def high_speed_adc (fg, input_rate):
# return gr.file_source (gr.sizeof_short, "dummy.dat", False)
return mc4020.source (input_rate, mc4020.MCC_CH3_EN mc4020.MCC_ALL_1V)
#
# return a gr.flow_graph
#
def build_graph (freq1, freq2):
input_rate = 20e6
cfir_decimation = 125
audio_decimation = 5
quad_rate = input_rate / cfir_decimation
audio_rate = quad_rate / audio_decimation
fg = gr.flow_graph ()

# use high speed ADC as input source
src = high_speed_adc (fg, input_rate)

# compute FIR filter taps for channel selection
channel_coeffs = \
gr.firdes.low_pass (1.0, # gain
input_rate, # sampling rate
250e3, # low pass cutoff freq
8*100e3, # width of trans. band
gr.firdes.WIN_HAMMING)
# input: short; output: complex
chan_filter1 = \
gr.freq_xlating_fir_filter_scf (cfir_decimation,
channel_coeffs,
freq1, # 1st station freq
input_rate)

(head1, tail1) = build_pipeline (fg, quad_rate, audio_decimation)

# sound card as final sink
audio_sink = audio.sink (int (audio_rate))
# now wire it all together
fg.connect (src, chan_filter1)
fg.connect (chan_filter1, head1)
fg.connect (tail1, (audio_sink, 0))
return fg
def build_pipeline (fg, quad_rate, audio_decimation):
'''Given a flow_graph, fg, construct a pipeline
for demodulating a broadcast FM signal. The
input is the downconverted complex baseband
signal. The output is the demodulated audio.
build_pipeline returns a two element tuple
containing the input and output endpoints.
'''
fm_demod_gain = 2200.0/32768.0
audio_rate = quad_rate / audio_decimation
volume = 1.0
# input: complex; output: float
fm_demod = gr.quadrature_demod_cf (volume*fm_demod_gain)
# compute FIR filter taps for audio filter
width_of_transition_band = audio_rate / 32
audio_coeffs = gr.firdes.low_pass (1.0, # gain
quad_rate, # sampling rate
audio_rate/2 - width_of_transition_band,
width_of_transition_band,
gr.firdes.WIN_HAMMING)
# input: float; output: float
audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
fg.connect (fm_demod, audio_filter)
return ((fm_demod, 0), (audio_filter, 0))

def main (args):
nargs = len (args)
if nargs == 1:
# get station frequency from command line
freq1 = float (args[0]) * 1e6
else:
sys.stderr.write ('usage: fm_demod freq\n')
sys.exit (1)
# connect to RF front end
rf_front_end = gr.microtune_4937_eval_board ()
if not rf_front_end.board_present_p ():
raise IOError, 'RF front end not found'
# set front end gain
rf_front_end.set_AGC (300)
# determine the front end's "Intermediate Frequency"
IF_freq = rf_front_end.get_output_freq () # 5.75e6
# Tell the front end to tune to freq1.
# I.e., freq1 is translated down to the IF frequency
rf_front_end.set_RF_freq (freq1)
# build the flow graph
fg = build_graph (IF_freq, None)

fg.start () # fork thread(s) and return
raw_input ('Press Enter to quit: ')
fg.stop ()
if __name__ == '__main__':
main (sys.argv[1:])
Like the Hello World example, we build a graph, connect the blocks together and start it. In this case, our source, mc4020.source, is an interface to the Measurement Computing PCI-DAS 4020/12 high-speed ADC. We follow it with gr.freq_xlating_fir_filter_scf, a finite impulse response (FIR) filter that selects the FM station we're looking for and translates it to baseband (0Hz, DC). With the 20M sample/sec converter and cable modem tuner, we're really grabbing something in the neighborhood of a 6 MHz chunk of the spectrum. This single chunk may contain ten or more FM stations, and gr.freq_xlating_fir_filter_scf allows us to select the one we want.
In this case, we select the one at the exact center of the IF of the RF front end (5.75 MHz). The output of gr.freq_xlating_fir_filter_scf is a stream of complex samples at 160,000 samples/second. We feed the complex baseband signal into gr.quadrature_demod_cf, the block that does the actual FM demodulation.
gr.quadrature_demod_cf works by subtracting the angle of each adjacent complex sample, effectively differentiating the frequency. The output of gr.quadrature_demod_cf contains the left-plus-right FM mono audio signal, the stereo pilot tone at 19kHz, the left-minus-right stereo information centered at 38kHz and any other sub-carriers above that. For this simplified receiver, we finish off by low pass filtering and decimating the stream, keeping only the left-plus-right audio information, and send that to the sound card at 32,000 samples/sec.
For a more indepth look at how the FM receiver works, please see "Listening to FM, Step by Step."

No comments:

TCP/IP Quick Guide 3G Wireless Quick Guide (electronic and printed bundle)