From 5524872f63432abaf25692df34dfed00c6bb887a Mon Sep 17 00:00:00 2001 From: stevie Date: Tue, 18 Jan 2011 07:49:48 +0000 Subject: [PATCH] Add simple c4fm demod no gui. git-svn-id: http://op25.osmocom.org/svn/trunk@255 65a5c917-d112-43f1-993d-58c26a4786be --- python/usrp_cf4m_rx_nogui.py | 160 +++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100755 python/usrp_cf4m_rx_nogui.py diff --git a/python/usrp_cf4m_rx_nogui.py b/python/usrp_cf4m_rx_nogui.py new file mode 100755 index 0000000..2f05d52 --- /dev/null +++ b/python/usrp_cf4m_rx_nogui.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# -*- mode: Python -*- + +# Copyright 2011 Steve Glass +# +# This file is part of OP25. +# +# OP25 is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# OP25 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OP25; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Boston, MA +# 02110-1301, USA. + +import math +import os +import sys +import threading + +from gnuradio import audio, fsk4, gr, gru, op25, usrp +from gnuradio.eng_option import eng_option +from math import pi + +# The P25 receiver +# +class p25_rx_block (gr.top_block): + + # Initialize the P25 receiver + # + def __init__(self, subdev_spec, center_freq, offset_freq, decim, squelch, gain): + + gr.top_block.__init__(self) + + # configure USRP + u = usrp.source_c() + u.set_decim_rate(decim) + capture_rate = u.adc_freq() / u.decim_rate() + if subdev_spec is None: + subdev_spec = usrp.pick_rx_subdevice(u) + subdev = usrp.selected_subdev(u, subdev_spec) + u.set_mux(usrp.determine_rx_mux_value(u, subdev_spec)) + print "Using RX d'board %s" % (subdev.side_and_name(),) + + if gain is None: + g = subdev.gain_range() + gain = float(g[0] + g[1]) / 2 + subdev.set_gain(gain) + u.tune(0, subdev, center_freq) + + # setup receiver attributes + channel_rate = 125000 + symbol_rate = 4800 + + # channel filter + self.channel_offset = offset_freq + channel_decim = capture_rate // channel_rate + channel_rate = capture_rate // channel_decim + trans_width = 12.5e3 / 2; + trans_centre = trans_width + (trans_width / 2) + coeffs = gr.firdes.low_pass(1.0, capture_rate, trans_centre, trans_width, gr.firdes.WIN_HANN) + self.channel_filter = gr.freq_xlating_fir_filter_ccf(channel_decim, coeffs, self.channel_offset, capture_rate) + self.connect(u, self.channel_filter) + + # power squelch + power_squelch = gr.pwr_squelch_cc(squelch, 1e-3, 0, True) + self.connect(self.channel_filter, power_squelch) + + # FM demodulator + self.symbol_deviation = 600.0 + fm_demod_gain = channel_rate / (2.0 * pi * self.symbol_deviation) + fm_demod = gr.quadrature_demod_cf(fm_demod_gain) + self.connect(power_squelch, fm_demod) + + # symbol filter + symbol_decim = 1 + samples_per_symbol = channel_rate // symbol_rate + symbol_coeffs = (1.0/samples_per_symbol,) * samples_per_symbol + symbol_filter = gr.fir_filter_fff(symbol_decim, symbol_coeffs) + self.connect(fm_demod, symbol_filter) + + # C4FM demodulator + autotuneq = gr.msg_queue(2) + self.demod_watcher = demod_watcher(autotuneq, self.adjust_channel_offset) + demod_fsk4 = fsk4.demod_ff(autotuneq, channel_rate, symbol_rate) + self.connect(symbol_filter, demod_fsk4) + + # Setup the decoder + decoder = op25.decoder_ff() + self.connect(demod_fsk4, decoder) + + # try to connect ALSA output device + try: + audio_sink = audio.sink(8000, "plughw:0,0", True) + self.connect(decoder, audio_sink) + except Exception: + sink = gr.null_sink(gr.sizeof_float) + self.connect(decoder, null); + + + # Adjust the channel offset + # + def adjust_channel_offset(self, delta_hz): + max_delta_hz = 12000.0 + delta_hz *= self.symbol_deviation + delta_hz = max(delta_hz, -max_delta_hz) + delta_hz = min(delta_hz, max_delta_hz) + self.channel_filter.set_center_freq(self.channel_offset - delta_hz) + + +# Demodulator frequency tracker +# +class demod_watcher(threading.Thread): + + def __init__(self, msgq, callback, **kwds): + threading.Thread.__init__ (self, **kwds) + self.setDaemon(1) + self.msgq = msgq + self.callback = callback + self.keep_running = True + self.start() + + def run(self): + while(self.keep_running): + msg = self.msgq.delete_head() + frequency_correction = msg.arg1() + self.callback(frequency_correction) + + +# Run the receiver +# +if '__main__' == __name__: + + from optparse import OptionParser + parser = OptionParser(option_class=eng_option) + parser.add_option("-R", "--subdev-spec", type="subdev", default=None, help="sub-device") + parser.add_option("-f", "--freq", type="eng_float", default=434.075e6, help="rx frequency [=%default]", metavar="Hz") + parser.add_option("-c", "--calibration", type="eng_float", default=0.0, help="channel frequency offset [=%default]", metavar="Hz") + parser.add_option("-d", "--decim", type="int", default=256, help="decimation factor [=%default]") + parser.add_option("-s", "--squelch", type="eng_float", default=15.0, help="squelch threshold [=%default]", metavar="dB") + parser.add_option("-g", "--gain", type="eng_float", default=None, help="sub-device gain", metavar="dB") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + self.options = options + + try: + rx = p25_rx_block(options.subdev_spec, options.freq, options.calibration, options.decim, options.squelch, options.gain) + rx.run() + except KeyboardInterrupt: + pass