diff options
Diffstat (limited to 'gr-uhd/examples/python')
-rw-r--r-- | gr-uhd/examples/python/CMakeLists.txt | 42 | ||||
-rwxr-xr-x | gr-uhd/examples/python/fm_tx4.py | 208 | ||||
-rwxr-xr-x | gr-uhd/examples/python/fm_tx_2_daughterboards.py | 212 | ||||
-rwxr-xr-x | gr-uhd/examples/python/max_power.py | 140 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_am_mw_rcv.py | 315 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_nbfm_ptt.py | 489 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_nbfm_rcv.py | 380 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_spectrum_sense.py | 258 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_tv_rcv.py | 443 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_tv_rcv_nogui.py | 214 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv.py | 286 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv2_nogui.py | 154 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv_fmdet.py | 349 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv_nogui.py | 175 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv_pll.py | 346 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wfm_rcv_sca.py | 403 | ||||
-rwxr-xr-x | gr-uhd/examples/python/usrp_wxapt_rcv.py | 282 |
17 files changed, 4696 insertions, 0 deletions
diff --git a/gr-uhd/examples/python/CMakeLists.txt b/gr-uhd/examples/python/CMakeLists.txt new file mode 100644 index 000000000..7642b536b --- /dev/null +++ b/gr-uhd/examples/python/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright 2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +include(GrPython) + +GR_PYTHON_INSTALL( + PROGRAMS + fm_tx4.py + fm_tx_2_daughterboards.py + max_power.py + usrp_am_mw_rcv.py + usrp_nbfm_ptt.py + usrp_nbfm_rcv.py + usrp_spectrum_sense.py + usrp_tv_rcv_nogui.py + usrp_tv_rcv.py + usrp_wfm_rcv2_nogui.py + usrp_wfm_rcv_fmdet.py + usrp_wfm_rcv_nogui.py + usrp_wfm_rcv_pll.py + usrp_wfm_rcv.py + usrp_wfm_rcv_sca.py + usrp_wxapt_rcv.py + DESTINATION ${GR_PKG_UHD_EXAMPLES_DIR} + COMPONENT "uhd_python" +) diff --git a/gr-uhd/examples/python/fm_tx4.py b/gr-uhd/examples/python/fm_tx4.py new file mode 100755 index 000000000..f412ffb5d --- /dev/null +++ b/gr-uhd/examples/python/fm_tx4.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +Transmit N simultaneous narrow band FM signals. + +They will be centered at the frequency specified on the command line, +and will spaced at 25kHz steps from there. + +The program opens N files with names audio-N.dat where N is in [0,7]. +These files should contain floating point audio samples in the range [-1,1] +sampled at 32kS/sec. You can create files like this using +audio_to_file.py +""" + +from gnuradio import gr, eng_notation +from gnuradio import uhd +from gnuradio import blks2 +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import math +import sys + +from gnuradio.wxgui import stdgui2, fftsink2 +import wx + + +######################################################## +# instantiate one transmit chain for each call + +class pipeline(gr.hier_block2): + def __init__(self, filename, lo_freq, audio_rate, if_rate): + + gr.hier_block2.__init__(self, "pipeline", + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + + try: + src = gr.file_source (gr.sizeof_float, filename, True) + except RuntimeError: + sys.stderr.write(("\nError: Could not open file '%s'\n\n" % \ + filename)) + sys.exit(1) + + print audio_rate, if_rate + fmtx = blks2.nbfm_tx (audio_rate, if_rate, max_dev=5e3, tau=75e-6) + + # Local oscillator + lo = gr.sig_source_c (if_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + lo_freq, #frequency + 1.0, # amplitude + 0) # DC Offset + mixer = gr.multiply_cc () + + self.connect (src, fmtx, (mixer, 0)) + self.connect (lo, (mixer, 1)) + self.connect (mixer, self) + +class fm_tx_block(stdgui2.std_top_block): + def __init__(self, frame, panel, vbox, argv): + MAX_CHANNELS = 7 + stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) + + parser = OptionParser (option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=400e3, + help="set sample rate (bandwidth) [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-n", "--nchannels", type="int", default=4, + help="number of Tx channels [1,4]") + #parser.add_option("","--debug", action="store_true", default=False, + # help="Launch Tx debugger") + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.nchannels < 1 or options.nchannels > MAX_CHANNELS: + sys.stderr.write ("fm_tx4: nchannels out of range. Must be in [1,%d]\n" % MAX_CHANNELS) + sys.exit(1) + + if options.freq is None: + sys.stderr.write("fm_tx4: must specify frequency with -f FREQ\n") + parser.print_help() + sys.exit(1) + + # ---------------------------------------------------------------- + # Set up constants and parameters + + self.u = uhd.usrp_sink(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + self.usrp_rate = options.samp_rate + self.u.set_samp_rate(self.usrp_rate) + self.usrp_rate = self.u.get_samp_rate() + + self.sw_interp = 10 + self.audio_rate = self.usrp_rate / self.sw_interp # 32 kS/s + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2 + + self.set_gain(options.gain) + self.set_freq(options.freq) + + self.sum = gr.add_cc () + + # Instantiate N NBFM channels + step = 25e3 + offset = (0 * step, 1 * step, -1 * step, + 2 * step, -2 * step, 3 * step, -3 * step) + + for i in range (options.nchannels): + t = pipeline("audio-%d.dat" % (i % 4), offset[i], + self.audio_rate, self.usrp_rate) + self.connect(t, (self.sum, i)) + + self.gain = gr.multiply_const_cc (1.0 / options.nchannels) + + # connect it all + self.connect (self.sum, self.gain) + self.connect (self.gain, self.u) + + # plot an FFT to verify we are sending what we want + if 1: + post_mod = fftsink2.fft_sink_c(panel, title="Post Modulation", + fft_size=512, + sample_rate=self.usrp_rate, + y_per_div=20, + ref_level=40) + self.connect (self.gain, post_mod) + vbox.Add (post_mod.win, 1, wx.EXPAND) + + + #if options.debug: + # self.debugger = tx_debug_gui.tx_debug_gui(self.subdev) + # self.debugger.Show(True) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital up converter. Finally, we feed + any residual_freq to the s/w freq translater. + """ + + r = self.u.set_center_freq(target_freq, 0) + if r: + print "Frequency =", eng_notation.num_to_str(self.u.get_center_freq()) + return True + + return False + + def set_gain(self, gain): + self.u.set_gain(gain, 0) + + +def main (): + app = stdgui2.stdapp(fm_tx_block, "Multichannel FM Tx", nstatus=1) + app.MainLoop () + +if __name__ == '__main__': + main () diff --git a/gr-uhd/examples/python/fm_tx_2_daughterboards.py b/gr-uhd/examples/python/fm_tx_2_daughterboards.py new file mode 100755 index 000000000..b5763e8e1 --- /dev/null +++ b/gr-uhd/examples/python/fm_tx_2_daughterboards.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +Transmit 2 signals, one out each daughterboard. + +Outputs SSB (USB) signals on side A and side B at frequencies +specified on command line. + +Side A is 600 Hz tone. +Side B is 350 + 440 Hz tones. +""" + +from gnuradio import gr, uhd, blks2 +from gnuradio.eng_notation import num_to_str, str_to_num +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import math +import sys + + +class example_signal_0(gr.hier_block2): + """ + Sinusoid at 600 Hz. + """ + def __init__(self, sample_rate): + gr.hier_block2.__init__(self, "example_signal_0", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + src = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 600, # frequency + 1.0, # amplitude + 0) # DC Offset + + self.connect(src, self) + + +class example_signal_1(gr.hier_block2): + """ + North American dial tone (350 + 440 Hz). + """ + def __init__(self, sample_rate): + gr.hier_block2.__init__(self, "example_signal_1", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + src0 = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 350, # frequency + 1.0, # amplitude + 0) # DC Offset + + src1 = gr.sig_source_c (sample_rate, # sample rate + gr.GR_SIN_WAVE, # waveform type + 440, # frequency + 1.0, # amplitude + 0) # DC Offset + sum = gr.add_cc() + self.connect(src0, (sum, 0)) + self.connect(src1, (sum, 1)) + self.connect(sum, self) + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + usage="%prog: [options] tx-freq0 tx-freq1" + parser = OptionParser (option_class=eng_option, usage=usage) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=320e3, + help="set sample rate [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + (options, args) = parser.parse_args () + + if len(args) != 2: + parser.print_help() + raise SystemExit + else: + freq0 = str_to_num(args[0]) + freq1 = str_to_num(args[1]) + + # ---------------------------------------------------------------- + # Set up USRP to transmit on both daughterboards + + d = uhd.find_devices(uhd.device_addr(options.args)) + uhd_type = d[0].get('type') + + stream_args = uhd.stream_args('fc32', channels=range(2)) + self.u = uhd.usrp_sink(device_addr=options.args, stream_args=stream_args) + + # Set up USRP system based on type + if(uhd_type == "usrp"): + self.u.set_subdev_spec("A:0 B:0") + tr0 = uhd.tune_request(freq0) + tr1 = uhd.tune_request(freq1) + + else: + if abs(freq0 - freq1) > 5.5e6: + sys.stderr.write("\nError: When not using two separate d'boards, frequencies must bewithin 5.5MHz of each other.\n") + raise SystemExit + + self.u.set_subdev_spec("A:0 A:0") + + mid_freq = (freq0 + freq1)/2.0 + tr0 = uhd.tune_request(freq0, rf_freq=mid_freq, + rf_freq_policy=uhd.tune_request.POLICY_MANUAL) + + tr1 = uhd.tune_request(freq1, rf_freq=mid_freq, + rf_freq_policy=uhd.tune_request.POLICY_MANUAL) + + # Use the tune requests to tune each channel + self.set_freq(tr0, 0) + self.set_freq(tr1, 1) + + self.usrp_rate = options.samp_rate + + self.u.set_samp_rate(self.usrp_rate) + dev_rate = self.u.get_samp_rate() + + # ---------------------------------------------------------------- + # build two signal sources, interleave them, amplify and + # connect them to usrp + + sig0 = example_signal_0(self.usrp_rate) + sig1 = example_signal_1(self.usrp_rate) + + intl = gr.interleave(gr.sizeof_gr_complex) + self.connect(sig0, (intl, 0)) + self.connect(sig1, (intl, 1)) + + # Correct for any difference in requested and actual rates + rrate = self.usrp_rate / dev_rate + resamp = blks2.pfb_arb_resampler_ccf(rrate) + + # and wire them up + self.connect(intl, resamp, self.u) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + self.set_gain(options.gain, 0) + self.set_gain(options.gain, 1) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + self.u.set_antenna(options.antenna, 1) + + def set_freq(self, target_freq, chan): + """ + Set the center frequency we're interested in. + + @param side: 0 = side A, 1 = side B + @param target_freq: frequency in Hz + @rtype: bool + """ + + print "Tuning channel %s to %sHz" % \ + (chan, num_to_str(target_freq)) + + r = self.u.set_center_freq(target_freq, chan) + + if r: + return True + + else: + print " Set Frequency Failed!" + + return False + + def set_gain(self, gain, chan): + self.u.set_gain(gain, chan) + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/gr-uhd/examples/python/max_power.py b/gr-uhd/examples/python/max_power.py new file mode 100755 index 000000000..5d23f16af --- /dev/null +++ b/gr-uhd/examples/python/max_power.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# +# Copyright 2004,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +Setup USRP for maximum power consumption. +""" + + +from gnuradio import gr +from gnuradio import uhd +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +from gnuradio import eng_notation + +n2s = eng_notation.num_to_str + +# Set this to a huge number; UHD will adjust to the +# maximum the USRP xxxx device can handle +MAX_RATE = 1000e6 + +class build_block(gr.top_block): + def __init__(self, args, tx_enable, rx_enable): + gr.top_block.__init__(self) + + d = uhd.find_devices(uhd.device_addr(args)) + uhd_type = d[0].get('type') + + print "\nFound '%s' at args '%s'" % \ + (uhd_type, args) + + # Test the type of USRP; if it's a USRP (v1), it has + # 2 channels; otherwise, it has 1 channel + if uhd_type == "usrp": + tx_nchan = 2 + rx_nchan = 2 + else: + tx_nchan = 1 + rx_nchan = 1 + + if tx_enable: + print "\nTRANSMIT CHAIN" + stream_args = uhd.stream_args('fc32', channels=range(tx_nchan)) + self.u_tx = uhd.usrp_sink(device_addr=args, stream_args=stream_args) + self.u_tx.set_samp_rate(MAX_RATE) + + self.tx_src0 = gr.sig_source_c(self.u_tx.get_samp_rate(), + gr.GR_CONST_WAVE, + 0, 1.0, 0) + + # Get dboard gain range and select maximum + tx_gain_range = self.u_tx.get_gain_range() + tx_gain = tx_gain_range.stop() + + # Get dboard freq range and select midpoint + tx_freq_range = self.u_tx.get_freq_range() + tx_freq_mid = (tx_freq_range.start() + tx_freq_range.stop())/2.0 + + for i in xrange(tx_nchan): + self.u_tx.set_center_freq (tx_freq_mid + i*1e6, i) + self.u_tx.set_gain(tx_gain, i) + + print "\nTx Sample Rate: %ssps" % (n2s(self.u_tx.get_samp_rate())) + for i in xrange(tx_nchan): + print "Tx Channel %d: " % (i) + print "\tFrequency = %sHz" % \ + (n2s(self.u_tx.get_center_freq(i))) + print "\tGain = %f dB" % (self.u_tx.get_gain(i)) + print "" + + self.connect (self.tx_src0, self.u_tx) + + if rx_enable: + print "\nRECEIVE CHAIN" + self.u_rx = uhd.usrp_source(device_addr=args, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=rx_nchan) + self.rx_dst0 = gr.null_sink (gr.sizeof_gr_complex) + + self.u_rx.set_samp_rate(MAX_RATE) + + # Get dboard gain range and select maximum + rx_gain_range = self.u_rx.get_gain_range() + rx_gain = rx_gain_range.stop() + + # Get dboard freq range and select midpoint + rx_freq_range = self.u_rx.get_freq_range() + rx_freq_mid = (rx_freq_range.start() + rx_freq_range.stop())/2.0 + + for i in xrange(tx_nchan): + self.u_rx.set_center_freq (rx_freq_mid + i*1e6, i) + self.u_rx.set_gain(rx_gain, i) + + print "\nRx Sample Rate: %ssps" % (n2s(self.u_rx.get_samp_rate())) + for i in xrange(rx_nchan): + print "Rx Channel %d: " % (i) + print "\tFrequency = %sHz" % \ + (n2s(self.u_rx.get_center_freq(i))) + print "\tGain = %f dB" % (self.u_rx.get_gain(i)) + print "" + + self.connect (self.u_rx, self.rx_dst0) + +def main (): + parser = OptionParser (option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("-t", action="store_true", dest="tx_enable", + default=False, help="enable Tx path") + parser.add_option("-r", action="store_true", dest="rx_enable", + default=False, help="enable Rx path") + (options, args) = parser.parse_args () + + tb = build_block (options.args, options.tx_enable, options.rx_enable) + + tb.start () + raw_input ('Press Enter to quit: ') + tb.stop () + +if __name__ == '__main__': + main () diff --git a/gr-uhd/examples/python/usrp_am_mw_rcv.py b/gr-uhd/examples/python/usrp_am_mw_rcv.py new file mode 100755 index 000000000..28262f2bd --- /dev/null +++ b/gr-uhd/examples/python/usrp_am_mw_rcv.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, eng_notation, optfir +from gnuradio import audio +from gnuradio import uhd +from gnuradio import blks2 +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import math +import wx + +class wfm_rx_block (stdgui2.std_top_block): + def __init__(self, frame, panel, vbox, argv): + stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=1e6, + help="set sample rate (bandwidth) [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=1008.0e3, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-I", "--use-if-freq", action="store_true", default=False, + help="use intermediate freq (compensates DC problems in quadrature boards)" ) + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is maximum)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + self.use_IF=options.use_if_freq + if self.use_IF: + self.IF_freq=64000.0 + else: + self.IF_freq=0.0 + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 256e3 + demod_rate = 64e3 + audio_rate = 32e3 + chanfilt_decim = int(usrp_rate // demod_rate) + audio_decim = int(demod_rate // audio_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + # Resample signal to exactly self.usrp_rate + # FIXME: make one of the follow-on filters an arb resampler + rrate = usrp_rate / dev_rate + self.resamp = blks2.pfb_arb_resampler_ccf(rrate) + + chan_filt_coeffs = gr.firdes.low_pass_2 (1, # gain + usrp_rate, # sampling rate + 8e3, # passband cutoff + 4e3, # transition bw + 60) # stopband attenuation + + if self.use_IF: + # Turn If to baseband and filter. + self.chan_filt = gr.freq_xlating_fir_filter_ccf (chanfilt_decim, + chan_filt_coeffs, + self.IF_freq, + usrp_rate) + else: + self.chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) + + self.agc = gr.agc_cc(0.1, 1, 1, 100000) + self.am_demod = gr.complex_to_mag() + self.volume_control = gr.multiply_const_ff(self.vol) + + audio_filt_coeffs = gr.firdes.low_pass_2 (1, # gain + demod_rate, # sampling rate + 8e3, # passband cutoff + 2e3, # transition bw + 60) # stopband attenuation + self.audio_filt=gr.fir_filter_fff(audio_decim, audio_filt_coeffs) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.resamp, self.chan_filt, self.agc, + self.am_demod, self.audio_filt, + self.volume_control, self.audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + g = self.u.get_gain_range() + if True: + # if no gain was specified, use the mid gain + options.gain = (g.start() + g.stop())/2.0 + options.gain = g.stop() + + if options.volume is None: + v = self.volume_range() + options.volume = float(v[0]*3+v[1])/4.0 + + if abs(options.freq) < 1e3: + options.freq *= 1e3 + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 0: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0.0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 0: + self.post_filt_fft = fftsink2.fft_sink_c(self.panel, title="Post Channel filter", + fft_size=512, sample_rate=demod_rate) + self.connect (self.chan_filt, self.post_filt_fft) + vbox.Add (self.post_filt_fft.win, 4, wx.EXPAND) + + if 0: + post_demod_fft = fftsink2.fft_sink_f(self.panel, title="Post Demod", + fft_size=1024, sample_rate=demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.am_demod, post_demod_fft) + vbox.Add (post_demod_fft.win, 4, wx.EXPAND) + + if 1: + audio_fft = fftsink2.fft_sink_f(self.panel, title="Audio", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=20) + self.connect (self.audio_filt, audio_fft) + vbox.Add (audio_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(520.0e3, 1611.0e3, 1.0e3), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + r = self.u.set_center_freq(target_freq + self.IF_freq, 0) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + try: + self.src_fft.set_baseband_freq(self.freq) + except: + None + + def volume_range(self): + return (-40.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wfm_rx_block, "USRP Broadcast AM MW RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_nbfm_ptt.py b/gr-uhd/examples/python/usrp_nbfm_ptt.py new file mode 100755 index 000000000..8d26e656e --- /dev/null +++ b/gr-uhd/examples/python/usrp_nbfm_ptt.py @@ -0,0 +1,489 @@ +#!/usr/bin/env python +# +# Copyright 2005,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import math +import sys +import wx +from optparse import OptionParser + +from gnuradio import gr, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui2, fftsink2, scopesink2, slider, form + +from numpy import convolve, array + +#import os +#print "pid =", os.getpid() +#raw_input('Press Enter to continue: ') + +# //////////////////////////////////////////////////////////////////////// +# Control Stuff +# //////////////////////////////////////////////////////////////////////// + +class ptt_block(stdgui2.std_top_block): + def __init__(self, frame, panel, vbox, argv): + stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) + + self.frame = frame + self.space_bar_pressed = False + + parser = OptionParser (option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option ("-f", "--freq", type="eng_float", default=442.1e6, + help="set Tx and Rx frequency to FREQ", metavar="FREQ") + parser.add_option ("-g", "--rx-gain", type="eng_float", default=None, + help="set rx gain [default=midpoint in dB]") + parser.add_option ("", "--tx-gain", type="eng_float", default=None, + help="set tx gain [default=midpoint in dB]") + parser.add_option("-I", "--audio-input", type="string", default="", + help="pcm input device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm output device name. E.g., hw:0,0 or /dev/dsp") + parser.add_option ("-N", "--no-gui", action="store_true", default=False) + (options, args) = parser.parse_args () + + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + self.txpath = transmit_path(options.args, options.spec, + options.antenna, options.tx_gain, + options.audio_input) + self.rxpath = receive_path(options.args, options.spec, + options.antenna, options.rx_gain, + options.audio_output) + self.connect(self.txpath) + self.connect(self.rxpath) + + self._build_gui(frame, panel, vbox, argv, options.no_gui) + + self.set_transmit(False) + self.set_freq(options.freq) + self.set_rx_gain(self.rxpath.gain) # update gui + self.set_volume(self.rxpath.volume) # update gui + self.set_squelch(self.rxpath.threshold()) # update gui + + + def set_transmit(self, enabled): + self.txpath.set_enable(enabled) + self.rxpath.set_enable(not(enabled)) + if enabled: + self.frame.SetStatusText ("Transmitter ON", 1) + else: + self.frame.SetStatusText ("Receiver ON", 1) + + + def set_rx_gain(self, gain): + self.myform['rx_gain'].set_value(gain) # update displayed value + self.rxpath.set_gain(gain) + + def set_tx_gain(self, gain): + self.txpath.set_gain(gain) + + def set_squelch(self, threshold): + self.rxpath.set_squelch(threshold) + self.myform['squelch'].set_value(self.rxpath.threshold()) + + def set_volume (self, vol): + self.rxpath.set_volume(vol) + self.myform['volume'].set_value(self.rxpath.volume) + #self.update_status_bar () + + def set_freq(self, freq): + r1 = self.txpath.set_freq(freq) + r2 = self.rxpath.set_freq(freq) + #print "txpath.set_freq =", r1 + #print "rxpath.set_freq =", r2 + if r1 and r2: + self.myform['freq'].set_value(freq) # update displayed value + return r1 and r2 + + def _build_gui(self, frame, panel, vbox, argv, no_gui): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + self.panel = panel + + # FIXME This REALLY needs to be replaced with a hand-crafted button + # that sends both button down and button up events + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((10,0), 1) + self.status_msg = wx.StaticText(panel, -1, "Press Space Bar to Transmit") + of = self.status_msg.GetFont() + self.status_msg.SetFont(wx.Font(15, of.GetFamily(), of.GetStyle(), of.GetWeight())) + hbox.Add(self.status_msg, 0, wx.ALIGN_CENTER) + hbox.Add((10,0), 1) + vbox.Add(hbox, 0, wx.EXPAND | wx.ALIGN_CENTER) + + panel.Bind(wx.EVT_KEY_DOWN, self._on_key_down) + panel.Bind(wx.EVT_KEY_UP, self._on_key_up) + panel.Bind(wx.EVT_KILL_FOCUS, self._on_kill_focus) + panel.SetFocus() + + if 1 and not(no_gui): + rx_fft = fftsink2.fft_sink_c(panel, title="Rx Input", fft_size=512, + sample_rate=self.rxpath.if_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.u, rx_fft) + vbox.Add (rx_fft.win, 1, wx.EXPAND) + + if 1 and not(no_gui): + rx_fft = fftsink2.fft_sink_c(panel, title="Post s/w Resampler", + fft_size=512, sample_rate=self.rxpath.quad_rate, + ref_level=80, y_per_div=20) + self.connect (self.rxpath.resamp, rx_fft) + vbox.Add (rx_fft.win, 1, wx.EXPAND) + + if 0 and not(no_gui): + foo = scopesink2.scope_sink_f(panel, title="Squelch", + sample_rate=32000) + self.connect (self.rxpath.fmrx.div, (foo,0)) + self.connect (self.rxpath.fmrx.gate, (foo,1)) + self.connect (self.rxpath.fmrx.squelch_lpf, (foo,2)) + vbox.Add (foo.win, 1, wx.EXPAND) + + if 0 and not(no_gui): + tx_fft = fftsink2.fft_sink_c(panel, title="Tx Output", + fft_size=512, sample_rate=self.txpath.usrp_rate) + self.connect (self.txpath.amp, tx_fft) + vbox.Add (tx_fft.win, 1, wx.EXPAND) + + + # add control area at the bottom + + self.myform = myform = form.form() + + # first row + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + # second row + hbox = wx.BoxSizer(wx.HORIZONTAL) + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.rxpath.volume_range(), + callback=self.set_volume) + hbox.Add((5,0), 0) + myform['squelch'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Squelch", + weight=3, range=self.rxpath.squelch_range(), + callback=self.set_squelch) + + g = self.rxpath.u.get_gain_range() + hbox.Add((5,0), 0) + myform['rx_gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Rx Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_rx_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + #if not(self.show_debug_info): + # return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + #myform['decim'] = form.static_float_field( + # parent=panel, sizer=hbox, label="Decim") + + #hbox.Add((5,0), 1) + #myform['fs@usb'] = form.static_float_field( + # parent=panel, sizer=hbox, label="Fs@USB") + + #hbox.Add((5,0), 1) + #myform['dbname'] = form.static_text_field( + # parent=panel, sizer=hbox) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + def _on_key_down(self, evt): + # print "key_down:", evt.m_keyCode + if evt.m_keyCode == wx.WXK_SPACE and not(self.space_bar_pressed): + self.space_bar_pressed = True + self.set_transmit(True) + + def _on_key_up(self, evt): + # print "key_up", evt.m_keyCode + if evt.m_keyCode == wx.WXK_SPACE: + self.space_bar_pressed = False + self.set_transmit(False) + + def _on_kill_focus(self, evt): + # if we lose the keyboard focus, turn off the transmitter + self.space_bar_pressed = False + self.set_transmit(False) + + +# //////////////////////////////////////////////////////////////////////// +# Transmit Path +# //////////////////////////////////////////////////////////////////////// + +class transmit_path(gr.hier_block2): + def __init__(self, args, spec, antenna, gain, audio_input): + gr.hier_block2.__init__(self, "transmit_path", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self.u = uhd.usrp_sink(device_addr=args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(spec): + self.u.set_subdev_spec(spec, 0) + + # Set the antenna + if(antenna): + self.u.set_antenna(antenna, 0) + + self.if_rate = 320e3 + self.audio_rate = 32e3 + + self.u.set_samp_rate(self.if_rate) + dev_rate = self.u.get_samp_rate() + + self.audio_gain = 10 + self.normal_gain = 32000 + + self.audio = audio.source(int(self.audio_rate), audio_input) + self.audio_amp = gr.multiply_const_ff(self.audio_gain) + + lpf = gr.firdes.low_pass (1, # gain + self.audio_rate, # sampling rate + 3800, # low pass cutoff freq + 300, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + hpf = gr.firdes.high_pass (1, # gain + self.audio_rate, # sampling rate + 325, # low pass cutoff freq + 50, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + audio_taps = convolve(array(lpf),array(hpf)) + self.audio_filt = gr.fir_filter_fff(1,audio_taps) + + self.pl = blks2.ctcss_gen_f(self.audio_rate,123.0) + self.add_pl = gr.add_ff() + self.connect(self.pl,(self.add_pl,1)) + + self.fmtx = blks2.nbfm_tx(self.audio_rate, self.if_rate) + self.amp = gr.multiply_const_cc (self.normal_gain) + + rrate = dev_rate / self.if_rate + self.resamp = blks2.pfb_arb_resampler_ccf(rrate) + + self.connect(self.audio, self.audio_amp, self.audio_filt, + (self.add_pl,0), self.fmtx, self.amp, + self.resamp, self.u) + + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + gain = float(g.start() + g.stop())/2.0 + + self.set_gain(gain) + + self.set_enable(False) + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + r = self.u.set_center_freq(target_freq) + if r: + return True + return False + + def set_gain(self, gain): + self.gain = gain + self.u.set_gain(gain) + + def set_enable(self, enable): + if enable: + self.amp.set_k (self.normal_gain) + else: + self.amp.set_k (0) + + + +# //////////////////////////////////////////////////////////////////////// +# Receive Path +# //////////////////////////////////////////////////////////////////////// + +class receive_path(gr.hier_block2): + def __init__(self, args, gain, audio_output): + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self.u = uhd.usrp_source(device_addr=args, + io_type=uhd.io_type.COMPLEX_FLOAT32, + num_channels=1) + + self.if_rate = 256e3 + self.quad_rate = 64e3 + self.audio_rate = 32e3 + + self.u.set_samp_rate(self.if_rate) + dev_rate = self.u.get_samp_rate() + + # Create filter to get actual channel we want + nfilts = 32 + chan_coeffs = gr.firdes.low_pass (nfilts, # gain + nfilts*dev_rate, # sampling rate + 13e3, # low pass cutoff freq + 4e3, # width of trans. band + gr.firdes.WIN_HANN) # filter type + + rrate = self.quad_rate / dev_rate + self.resamp = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + # instantiate the guts of the single channel receiver + self.fmrx = blks2.nbfm_rx(self.audio_rate, self.quad_rate) + + # standard squelch block + self.squelch = blks2.standard_squelch(self.audio_rate) + + # audio gain / mute block + self._audio_gain = gr.multiply_const_ff(1.0) + + # sound card as final sink + audio_sink = audio.sink (int(self.audio_rate), audio_output) + + # now wire it all together + self.connect (self.u, self.resamp, self.fmrx, self.squelch, + self._audio_gain, audio_sink) + + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + gain = float(g.start() + g.stop())/2.0 + + self.enabled = True + self.set_gain(gain) + v = self.volume_range() + self.set_volume((v[0]+v[1])/2) + s = self.squelch_range() + self.set_squelch((s[0]+s[1])/2) + + # Set the subdevice spec + if(spec): + self.u.set_subdev_spec(spec, 0) + + # Set the antenna + if(antenna): + self.u.set_antenna(antenna, 0) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + def set_volume (self, vol): + g = self.volume_range() + self.volume = max(g[0], min(g[1], vol)) + self._update_audio_gain() + + def set_enable(self, enable): + self.enabled = enable + self._update_audio_gain() + + def _update_audio_gain(self): + if self.enabled: + self._audio_gain.set_k(10**(self.volume/10)) + else: + self._audio_gain.set_k(0) + + def squelch_range(self): + return self.squelch.squelch_range() + + def set_squelch(self, threshold): + print "SQL =", threshold + self.squelch.set_threshold(threshold) + + def threshold(self): + return self.squelch.threshold() + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + r = self.u.set_center_freq(target_freq) + if r: + return True + return False + + def set_gain(self, gain): + self.gain = gain + self.u.set_gain(gain) + + +# //////////////////////////////////////////////////////////////////////// +# Main +# //////////////////////////////////////////////////////////////////////// + +def main(): + app = stdgui2.stdapp(ptt_block, "NBFM Push to Talk") + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/gr-uhd/examples/python/usrp_nbfm_rcv.py b/gr-uhd/examples/python/usrp_nbfm_rcv.py new file mode 100755 index 000000000..571abe8c9 --- /dev/null +++ b/gr-uhd/examples/python/usrp_nbfm_rcv.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python +# +# Copyright 2005,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import math +import wx + +#//////////////////////////////////////////////////////////////////////// +# Control Stuff +#//////////////////////////////////////////////////////////////////////// + +class my_top_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=146.585e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("-N", "--no-gui", action="store_true", default=False) + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if options.freq < 1e6: + options.freq *= 1e6 + + self.frame = frame + self.panel = panel + + self.state = "FREQ" + self.freq = 0 + self.freq_step = 25e3 + + self.rxpath = receive_path(options.args, options.spec, options.antenna, + options.gain, options.audio_output) + self.connect(self.rxpath) + + self._build_gui(vbox, options.no_gui) + + # set initial values + + if options.volume is not None: + self.set_volume(options.volume) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + self.set_gain(self.rxpath.gain) # update gui + self.set_volume(self.rxpath.volume) # update gui + self.set_squelch(self.rxpath.threshold()) # update gui + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, no_gui): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + self.src_fft = None + if 0 and not(no_gui): + self.src_fft = fftsink2.fft_sink_c(self.panel, + title="Data from USRP", + fft_size=512, + sample_rate=self.rxpath.if_rate, + ref_scale=32768.0, + ref_level=0, + y_per_div=10, + y_divs=12) + self.connect (self.rxpath.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + if 1 and not(no_gui): + rx_fft = fftsink2.fft_sink_c(self.panel, + title="Post s/w Resampling", + fft_size=512, + sample_rate=self.rxpath.quad_rate, + ref_level=80, + y_per_div=20) + self.connect (self.rxpath.resamp, rx_fft) + vbox.Add (rx_fft.win, 4, wx.EXPAND) + + if 1 and not(no_gui): + post_deemph_fft = fftsink2.fft_sink_f(self.panel, + title="Post Deemph", + fft_size=512, + sample_rate=self.rxpath.audio_rate, + y_per_div=10, + ref_level=-40) + self.connect (self.rxpath.fmrx.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + if 0: + post_filt_fft = fftsink2.fft_sink_f(self.panel, + title="Post Filter", + fft_size=512, + sample_rate=audio_rate, + y_per_div=10, + ref_level=-40) + self.connect (self.guts.audio_filter, post_filt) + vbox.Add (fft_win4, 4, wx.EXPAND) + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, + self._set_status_msg)) + + #hbox.Add((5,0), 0) + #myform['freq_slider'] = \ + # form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + # range=(87.9e6, 108.1e6, 0.1e6), + # callback=self.set_freq) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_volume) + hbox.Add((5,0), 0) + myform['squelch'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Squelch", + weight=3, range=self.rxpath.squelch_range(), + callback=self.set_squelch) + g = self.rxpath.u.get_gain_range() + hbox.Add((5,0), 0) + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + self.freq_step) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - self.freq_step) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_volume(self.rxpath.volume + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_volume(self.rxpath.volume - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_squelch(self, threshold_in_db): + self.rxpath.set_squelch(threshold_in_db) + self.myform['squelch'].set_value(self.rxpath.threshold()) + + def set_volume (self, vol): + self.rxpath.set_volume(vol) + self.myform['volume'].set_value(self.rxpath.volume) + self.update_status_bar () + + def set_freq(self, target_freq): + r = self.rxpath.set_freq(target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + #self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.rxpath.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.rxpath.volume, self.state) + self._set_status_msg(msg, 1) + if self.src_fft: + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +#//////////////////////////////////////////////////////////////////////// +# Receive Path +#//////////////////////////////////////////////////////////////////////// + +USE_SIMPLE_SQUELCH = False + +class receive_path(gr.hier_block2): + def __init__(self, args, spec, antenna, gain, audio_output): + gr.hier_block2.__init__(self, "receive_path", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(0, 0, 0)) # Output signature + + self.u = uhd.usrp_source(device_addr=args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(spec): + self.u.set_subdev_spec(spec, 0) + + # Set the antenna + if(antenna): + self.u.set_antenna(antenna, 0) + + self.if_rate = 256e3 + self.quad_rate = 64e3 + self.audio_rate = 32e3 + + self.u.set_samp_rate(self.if_rate) + dev_rate = self.u.get_samp_rate() + + # Create filter to get actual channel we want + nfilts = 32 + chan_coeffs = gr.firdes.low_pass (nfilts, # gain + nfilts*dev_rate, # sampling rate + 8e3, # low pass cutoff freq + 2e3, # width of trans. band + gr.firdes.WIN_HANN) # filter type + rrate = self.quad_rate / dev_rate + self.resamp = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + if USE_SIMPLE_SQUELCH: + self.squelch = gr.simple_squelch_cc(20) + else: + self.squelch = blks2.standard_squelch(self.audio_rate) + + # instantiate the guts of the single channel receiver + self.fmrx = blks2.nbfm_rx(self.audio_rate, self.quad_rate) + + # audio gain / mute block + self._audio_gain = gr.multiply_const_ff(1.0) + + # sound card as final sink + audio_sink = audio.sink (int(self.audio_rate), audio_output) + + # now wire it all together + if USE_SIMPLE_SQUELCH: + self.connect (self.u, self.resamp, self.squelch, self.fmrx, + self._audio_gain, audio_sink) + else: + self.connect (self.u, self.resamp, self.fmrx, self.squelch, + self._audio_gain, audio_sink) + + if gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + gain = float(g.start()+g.stop())/2 + + self.set_gain(gain) + + v = self.volume_range() + self.set_volume((v[0]+v[1])/2) + + s = self.squelch_range() + self.set_squelch((s[0]+s[1])/2) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + def set_volume (self, vol): + g = self.volume_range() + self.volume = max(g[0], min(g[1], vol)) + self._update_audio_gain() + + def _update_audio_gain(self): + self._audio_gain.set_k(10**(self.volume/10)) + + def squelch_range(self): + r = self.squelch.squelch_range() + #print "squelch_range: ", r + return r + + def set_squelch(self, threshold): + #print "SQL =", threshold + self.squelch.set_threshold(threshold) + + def threshold(self): + t = self.squelch.threshold() + #print "t =", t + return t + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + if r: + return True + return False + + def set_gain(self, gain): + self.gain = gain + self.u.set_gain(gain) + + +# //////////////////////////////////////////////////////////////////////// +# Main +# //////////////////////////////////////////////////////////////////////// + +if __name__ == '__main__': + app = stdgui2.stdapp (my_top_block, "USRP NBFM RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_spectrum_sense.py b/gr-uhd/examples/python/usrp_spectrum_sense.py new file mode 100755 index 000000000..32980adbf --- /dev/null +++ b/gr-uhd/examples/python/usrp_spectrum_sense.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python +# +# Copyright 2005,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, eng_notation, window +from gnuradio import audio +from gnuradio import uhd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math +import struct +import threading + +sys.stderr.write("Warning: this may have issues on some machines+Python version combinations to seg fault due to the callback in bin_statitics.\n\n") + +class ThreadClass(threading.Thread): + def run(self): + return + +class tune(gr.feval_dd): + """ + This class allows C++ code to callback into python. + """ + def __init__(self, tb): + gr.feval_dd.__init__(self) + self.tb = tb + + def eval(self, ignore): + """ + This method is called from gr.bin_statistics_f when it wants + to change the center frequency. This method tunes the front + end to the new center frequency, and returns the new frequency + as its result. + """ + + try: + # We use this try block so that if something goes wrong + # from here down, at least we'll have a prayer of knowing + # what went wrong. Without this, you get a very + # mysterious: + # + # terminate called after throwing an instance of + # 'Swig::DirectorMethodException' Aborted + # + # message on stderr. Not exactly helpful ;) + + new_freq = self.tb.set_next_freq() + return new_freq + + except Exception, e: + print "tune: Exception: ", e + + +class parse_msg(object): + def __init__(self, msg): + self.center_freq = msg.arg1() + self.vlen = int(msg.arg2()) + assert(msg.length() == self.vlen * gr.sizeof_float) + + # FIXME consider using NumPy array + t = msg.to_string() + self.raw_data = t + self.data = struct.unpack('%df' % (self.vlen,), t) + + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + usage = "usage: %prog [options] min_freq max_freq" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=1e6, + help="set sample rate [default=%default]") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("", "--tune-delay", type="eng_float", + default=1e-3, metavar="SECS", + help="time to delay (in seconds) after changing frequency [default=%default]") + parser.add_option("", "--dwell-delay", type="eng_float", + default=10e-3, metavar="SECS", + help="time to dwell (in seconds) at a given frequncy [default=%default]") + parser.add_option("-F", "--fft-size", type="int", default=256, + help="specify number of FFT bins [default=%default]") + parser.add_option("", "--real-time", action="store_true", default=False, + help="Attempt to enable real-time scheduling") + + (options, args) = parser.parse_args() + if len(args) != 2: + parser.print_help() + sys.exit(1) + + self.min_freq = eng_notation.str_to_num(args[0]) + self.max_freq = eng_notation.str_to_num(args[1]) + + if self.min_freq > self.max_freq: + # swap them + self.min_freq, self.max_freq = self.max_freq, self.min_freq + + self.fft_size = options.fft_size + + if not options.real_time: + realtime = False + else: + # Attempt to enable realtime scheduling + r = gr.enable_realtime_scheduling() + if r == gr.RT_OK: + realtime = True + else: + realtime = False + print "Note: failed to enable realtime scheduling" + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, + stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = options.samp_rate + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size) + + mywindow = window.blackmanharris(self.fft_size) + fft = gr.fft_vcc(self.fft_size, True, mywindow) + power = 0 + for tap in mywindow: + power += tap*tap + + c2mag = gr.complex_to_mag_squared(self.fft_size) + + # FIXME the log10 primitive is dog slow + log = gr.nlog10_ff(10, self.fft_size, + -20*math.log10(self.fft_size)-10*math.log10(power/self.fft_size)) + + # Set the freq_step to 75% of the actual data throughput. + # This allows us to discard the bins on both ends of the spectrum. + + self.freq_step = 0.75 * usrp_rate + self.min_center_freq = self.min_freq + self.freq_step/2 + nsteps = math.ceil((self.max_freq - self.min_freq) / self.freq_step) + self.max_center_freq = self.min_center_freq + (nsteps * self.freq_step) + + self.next_freq = self.min_center_freq + + tune_delay = max(0, int(round(options.tune_delay * usrp_rate / self.fft_size))) # in fft_frames + dwell_delay = max(1, int(round(options.dwell_delay * usrp_rate / self.fft_size))) # in fft_frames + + self.msgq = gr.msg_queue(16) + self._tune_callback = tune(self) # hang on to this to keep it from being GC'd + stats = gr.bin_statistics_f(self.fft_size, self.msgq, + self._tune_callback, tune_delay, + dwell_delay) + + # FIXME leave out the log10 until we speed it up + #self.connect(self.u, s2v, fft, c2mag, log, stats) + self.connect(self.u, s2v, fft, c2mag, stats) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + self.set_gain(options.gain) + print "gain =", options.gain + + def set_next_freq(self): + target_freq = self.next_freq + self.next_freq = self.next_freq + self.freq_step + if self.next_freq >= self.max_center_freq: + self.next_freq = self.min_center_freq + + if not self.set_freq(target_freq): + print "Failed to set frequency to", target_freq + sys.exit(1) + + return target_freq + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + r = self.u.set_center_freq(target_freq) + if r: + return True + + return False + + def set_gain(self, gain): + self.u.set_gain(gain) + + +def main_loop(tb): + while 1: + + # Get the next message sent from the C++ code (blocking call). + # It contains the center frequency and the mag squared of the fft + m = parse_msg(tb.msgq.delete_head()) + + # Print center freq so we know that something is happening... + print m.center_freq + + # FIXME do something useful with the data... + + # m.data are the mag_squared of the fft output (they are in the + # standard order. I.e., bin 0 == DC.) + # You'll probably want to do the equivalent of "fftshift" on them + # m.raw_data is a string that contains the binary floats. + # You could write this as binary to a file. + + +if __name__ == '__main__': + t = ThreadClass() + t.start() + + tb = my_top_block() + try: + tb.start() + main_loop(tb) + + except KeyboardInterrupt: + pass diff --git a/gr-uhd/examples/python/usrp_tv_rcv.py b/gr-uhd/examples/python/usrp_tv_rcv.py new file mode 100755 index 000000000..b49a5ea71 --- /dev/null +++ b/gr-uhd/examples/python/usrp_tv_rcv.py @@ -0,0 +1,443 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +Realtime capture and display of analog Tv stations. + +Can also use a file as source or sink + +When you use an output file you can show the results frame-by-frame +using ImageMagick + +When you want to use the realtime sdl display window you must first +install gr-video-sdl. + +When you use a file source, instead of the usrp, make sure you +capture interleaved shorts. (Use usrp_rx_file.py, or use +usrp_rx_cfile.py --output-shorts if you have a recent enough +usrp_rx_cfile.py) + +There is no synchronisation yet. The sync blocks are in development +but not yet in cvs. +""" + +from gnuradio import gr +try: + from gnuradio import video_sdl +except: + print "FYI: gr-video-sdl is not installed" + print "realtime SDL video output window will not be available" +from gnuradio import uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import wx + + +class tv_rx_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + usage="%prog: [options] [input_filename]. \n If you don't specify an input filename the usrp will be used as source\n " \ + "Make sure your input capture file containes interleaved shorts not complex floats" + parser=OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=1e6, + help="set sample rate") + parser.add_option("-f", "--freq", type="eng_float", default=519.25e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-c", "--contrast", type="eng_float", default=1.0, + help="set contrast (default is 1.0)") + parser.add_option("-b", "--brightness", type="eng_float", default=0.0, + help="set brightness (default is 0)") + parser.add_option("-p", "--pal", action="store_true", default=False, + help="PAL video format (this is the default)") + parser.add_option("-n", "--ntsc", action="store_true", default=False, + help="NTSC video format") + parser.add_option("-o", "--out-filename", type="string", default="sdl", + help="For example out_raw_uchar.gray. If you don't specify an output filename you will get a video_sink_sdl realtime output window. You then need to have gr-video-sdl installed)") + parser.add_option("-r", "--repeat", action="store_false", default=True, + help="repeat file in a loop") + parser.add_option("", "--freq-min", type="eng_float", default=50.25e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=900.25e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if not ((len(args) == 1) or (len(args) == 0)): + parser.print_help() + sys.exit(1) + + if len(args) == 1: + filename = args[0] + else: + filename = None + + self.frame = frame + self.panel = panel + + self.contrast = options.contrast + self.brightness = options.brightness + self.state = "FREQ" + self.freq = 0 + + self.tv_freq_min = options.freq_min + self.tv_freq_max = options.freq_max + + # build graph + self.u=None + + if not (options.out_filename=="sdl"): + options.repeat=False + + usrp_rate = options.samp_rate + + if not ((filename is None) or (filename=="usrp")): + # file is data source + self.filesource = gr.file_source(gr.sizeof_short,filename,options.repeat) + self.istoc = gr.interleaved_short_to_complex() + self.connect(self.filesource,self.istoc) + self.src=self.istoc + + options.gain=0.0 + self.gain=0.0 + + else: # use a UHD device + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + self.src=self.u + + self.gain = options.gain + + f2uc=gr.float_to_uchar() + + # sdl window as final sink + if not (options.pal or options.ntsc): + options.pal=True #set default to PAL + + if options.pal: + lines_per_frame=625.0 + frames_per_sec=25.0 + show_width=768 + + elif options.ntsc: + lines_per_frame=525.0 + frames_per_sec=29.97002997 + show_width=640 + + width=int(usrp_rate/(lines_per_frame*frames_per_sec)) + height=int(lines_per_frame) + + if (options.out_filename=="sdl"): + #Here comes the tv screen, you have to build and install + #gr-video-sdl for this (subproject of gnuradio, only in cvs + #for now) + try: + video_sink = video_sdl.sink_uc ( frames_per_sec, width, height, 0, + show_width, height) + except: + print "gr-video-sdl is not installed" + print "realtime \"sdl\" video output window is not available" + raise SystemExit, 1 + self.dst=video_sink + else: + print "You can use the imagemagick display tool to show the resulting imagesequence" + print "use the following line to show the demodulated TV-signal:" + print "display -depth 8 -size " +str(width)+ "x" + str(height) \ + + " gray:" + options.out_filename + print "(Use the spacebar to advance to next frames)" + options.repeat=False + file_sink=gr.file_sink(gr.sizeof_char, options.out_filename) + self.dst =file_sink + + self.agc=gr.agc_cc(1e-7,1.0,1.0) #1e-7 + self.am_demod = gr.complex_to_mag () + self.set_blacklevel=gr.add_const_ff(0.0) + self.invert_and_scale = gr.multiply_const_ff (0.0) #-self.contrast *128.0*255.0/(200.0) + + # now wire it all together + #sample_rate=options.width*options.height*options.framerate + + process_type='do_no_sync' + if process_type=='do_no_sync': + self.connect (self.src, self.agc,self.am_demod, + self.invert_and_scale, self.set_blacklevel, + f2uc,self.dst) + elif process_type=='do_tv_sync_adv': + #defaults: gr.tv_sync_adv (double sampling_freq, unsigned + #int tv_format,bool output_active_video_only=false, bool + #do_invert=false, double wanted_black_level=0.0, double + #wanted_white_level=255.0, double avg_alpha=0.1, double + #initial_gain=1.0, double initial_offset=0.0,bool + #debug=false) + + #note, this block is not yet in cvs + self.tv_sync_adv=gr.tv_sync_adv(usrp_rate, 0, False, False, + 0.0, 255.0, 0.01, 1.0, 0.0, False) + self.connect (self.src, self.am_demod, self.invert_and_scale, + self.tv_sync_adv, s2f, f2uc, self.dst) + + elif process_type=='do_nullsink': + #self.connect (self.src, self.am_demod,self.invert_and_scale,f2uc,video_sink) + c2r=gr.complex_to_real() + nullsink=gr.null_sink(gr.sizeof_float) + self.connect (self.src, c2r,nullsink) #video_sink) + elif process_type=='do_tv_sync_corr': + frame_size=width*height #int(usrp_rate/25.0) + nframes=10# 32 + search_window=20*nframes + debug=False + video_alpha=0.3 #0.1 + corr_alpha=0.3 + + #Note: this block is not yet in cvs + tv_corr=gr.tv_correlator_ff(frame_size,nframes, search_window, + video_alpha, corr_alpha,debug) + shift=gr.add_const_ff(-0.7) + + self.connect (self.src, self.agc, self.am_demod, tv_corr, + self.invert_and_scale, self.set_blacklevel, + f2uc, self.dst) + else: # process_type=='do_test_image': + src_vertical_bars = gr.sig_source_f (usrp_rate, gr.GR_SIN_WAVE, + 10.0 *usrp_rate/320, 255,128) + self.connect(src_vertical_bars, f2uc, self.dst) + + self._build_gui(vbox, usrp_rate, usrp_rate, usrp_rate) + + + frange = self.u.get_freq_range() + if(frange.start() > self.tv_freq_max or frange.stop() < self.tv_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.tv_freq_min or options.freq > self.tv_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + self.set_gain(options.gain) + self.set_contrast(self.contrast) + self.set_brightness(options.brightness) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 0: + self.src_fft = fftsink.fft_sink_c (self, self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate) + self.connect (self.src, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 0: + post_demod_fft = fftsink.fft_sink_f (self, self.panel, title="Post Demod", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=-40) + self.connect (self.am_demod, post_demod_fft) + vbox.Add (post_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-40) + self.connect (self.set_blacklevel, post_filt) + vbox.Add (fft_win4, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + if not (self.u is None): + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.tv_freq_min, self.tv_freq_max, 0.25e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['contrast'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Contrast", + weight=3, range=(-2.0, 2.0, 0.1), + callback=self.set_contrast) + hbox.Add((5,0), 1) + + myform['brightness'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Brightness", + weight=3, range=(-255.0, 255.0, 1.0), + callback=self.set_brightness) + hbox.Add((5,0), 0) + + if not (self.u is None): + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + elif (self.state == "CONTRAST"): + step = 0.1 + if self.rot >= 3: + self.set_contrast(self.contrast + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_contrast(self.contrast - step) + self.rot += 3 + else: + step = 1 + if self.rot >= 3: + self.set_brightness(self.brightness + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_brightness(self.brightness - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "CONTRAST" + elif self.state == "CONTRAST": + self.state = "BRIGHTNESS" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_contrast (self, contrast): + self.contrast = contrast + self.invert_and_scale.set_k(-self.contrast *128.0*255.0/(200.0)) + self.myform['contrast'].set_value(self.contrast) + self.update_status_bar () + + def set_brightness (self, brightness): + self.brightness = brightness + self.set_blacklevel.set_k(self.brightness +255.0) + self.myform['brightness'].set_value(self.brightness) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital down converter. + """ + if not (self.u is None): + r = self.u.set_center_freq(target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + if not (self.u is None): + self.gain=gain + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + self.update_status_bar() + + def update_status_bar (self): + msg = "Setting:%s Contrast:%r Brightness:%r Gain: %r" % \ + (self.state, self.contrast,self.brightness,self.gain) + self._set_status_msg(msg, 1) + #self.src_fft.set_baseband_freq(self.freq) + + +if __name__ == '__main__': + app = stdgui2.stdapp (tv_rx_block, "USRP TV RX black-and-white") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_tv_rcv_nogui.py b/gr-uhd/examples/python/usrp_tv_rcv_nogui.py new file mode 100755 index 000000000..cfb36222c --- /dev/null +++ b/gr-uhd/examples/python/usrp_tv_rcv_nogui.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +""" +Reads from a file and generates PAL TV pictures in black and white +which can be displayed using ImageMagick or realtime using +gr-video-sdl (To capture the input file Use usrp_rx_file.py, or use +usrp_rx_cfile.py --output-shorts if you have a recent enough +usrp_rx_cfile.py) + +Can also use usrp directly as capture source, but then you need a +higher decimation factor (64) and thus get a lower horizontal +resulution. There is no synchronisation yet. The sync blocks are in +development but not yet in cvs. + +""" + +from gnuradio import gr, eng_notation +from gnuradio import audio +from gnuradio import uhd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +try: + from gnuradio import video_sdl +except: + print "FYI: gr-video-sdl is not installed" + print "realtime \"sdl\" video output window will not be available" + + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + usage=("%prog: [options] output_filename.\nSpecial output_filename" + \ + "\"sdl\" will use video_sink_sdl as realtime output window. " + \ + "You then need to have gr-video-sdl installed.\n" +\ + "Make sure your input capture file containes interleaved " + \ + "shorts not complex floats") + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-s", "--samp-rate", type="eng_float", default=1e6, + help="set sample rate") + parser.add_option("-c", "--contrast", type="eng_float", default=1.0, + help="set contrast (default is 1.0)") + parser.add_option("-b", "--brightness", type="eng_float", default=0.0, + help="set brightness (default is 0)") + parser.add_option("-i", "--in-filename", type="string", default=None, + help="Use input file as source. samples must be " + \ + "interleaved shorts \n Use usrp_rx_file.py or " + \ + "usrp_rx_cfile.py --output-shorts.\n Special " + \ + "name \"usrp\" results in realtime capturing " + \ + "and processing using usrp.\n" + \ + "You then probably need a decimation factor of 64 or higher.") + parser.add_option("-f", "--freq", type="eng_float", default=519.25e6, + help="set frequency to FREQ.\nNote that the frequency of the video carrier is not at the middle of the TV channel", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-p", "--pal", action="store_true", default=False, + help="PAL video format (this is the default)") + parser.add_option("-n", "--ntsc", action="store_true", default=False, + help="NTSC video format") + parser.add_option("-r", "--repeat", action="store_false", default=True, + help="repeat in_file in a loop") + parser.add_option("-N", "--nframes", type="eng_float", default=None, + help="number of frames to collect [default=+inf]") + parser.add_option("", "--freq-min", type="eng_float", default=50.25e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=900.25e6, + help="Set a maximum frequency [default=%default]") + (options, args) = parser.parse_args () + if not (len(args) == 1): + parser.print_help() + sys.stderr.write('You must specify the output. FILENAME or sdl \n'); + sys.exit(1) + + filename = args[0] + + self.tv_freq_min = options.freq_min + self.tv_freq_max = options.freq_max + + if options.in_filename is None: + parser.print_help() + sys.stderr.write('You must specify the input -i FILENAME or -i usrp\n'); + raise SystemExit, 1 + + if not (filename=="sdl"): + options.repeat=False + + input_rate = options.samp_rate + print "video sample rate %s" % (eng_notation.num_to_str(input_rate)) + + if not (options.in_filename=="usrp"): + # file is data source, capture with usr_rx_csfile.py + self.filesource = gr.file_source(gr.sizeof_short, + options.in_filename, + options.repeat) + self.istoc = gr.interleaved_short_to_complex() + self.connect(self.filesource,self.istoc) + self.src=self.istoc + else: + if options.freq is None: + parser.print_help() + sys.stderr.write('You must specify the frequency with -f FREQ\n'); + raise SystemExit, 1 + + # build the graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + self.u.set_samp_rate(input_rate) + dev_rate = self.u.get_samp_rate() + + self.src=self.u + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + self.u.set_gain(options.gain) + + r = self.u.set_center_freq(options.freq) + if not r: + sys.stderr.write('Failed to set frequency\n') + raise SystemExit, 1 + + + self.agc = gr.agc_cc(1e-7,1.0,1.0) #1e-7 + self.am_demod = gr.complex_to_mag () + self.set_blacklevel = gr.add_const_ff(options.brightness +255.0) + self.invert_and_scale = gr.multiply_const_ff (-options.contrast *128.0*255.0/(200.0)) + self.f2uc = gr.float_to_uchar() + + # sdl window as final sink + if not (options.pal or options.ntsc): + options.pal=True #set default to PAL + if options.pal: + lines_per_frame=625.0 + frames_per_sec=25.0 + show_width=768 + elif options.ntsc: + lines_per_frame=525.0 + frames_per_sec=29.97002997 + show_width=640 + width=int(input_rate/(lines_per_frame*frames_per_sec)) + height=int(lines_per_frame) + + if filename=="sdl": + #Here comes the tv screen, you have to build and install + #gr-video-sdl for this (subproject of gnuradio, only in cvs + #for now) + try: + video_sink = video_sdl.sink_uc(frames_per_sec, width, height, 0, + show_width,height) + except: + print "gr-video-sdl is not installed" + print "realtime \"sdl\" video output window is not available" + raise SystemExit, 1 + self.dst=video_sink + else: + print "You can use the imagemagick display tool to show the resulting imagesequence" + print "use the following line to show the demodulated TV-signal:" + print "display -depth 8 -size " +str(width)+ "x" + str(height) + " gray:" +filename + print "(Use the spacebar to advance to next frames)" + file_sink=gr.file_sink(gr.sizeof_char, filename) + self.dst =file_sink + + if options.nframes is None: + self.connect(self.src, self.agc) + else: + self.head = gr.head(gr.sizeof_gr_complex, int(options.nframes*width*height)) + self.connect(self.src, self.head, self.agc) + + self.connect (self.agc, self.am_demod, self.invert_and_scale, + self.set_blacklevel, self.f2uc, self.dst) + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/gr-uhd/examples/python/usrp_wfm_rcv.py b/gr-uhd/examples/python/usrp_wfm_rcv.py new file mode 100755 index 000000000..bbb599033 --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2009,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import wx + + +class wfm_rx_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=40, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 32e3 + audio_decim = int(demod_rate / audio_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = optfir.low_pass (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + self.guts = blks2.wfm_rcv (demod_rate, audio_decim) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.chan_filt, self.guts, + self.volume_control, self.audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.fm_freq_min or options.freq > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_filt_fft = fftsink2.fft_sink_f(self.panel, title="Post Demod", + fft_size=1024, sample_rate=usrp_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.fm_demod, post_filt_fft) + vbox.Add (post_filt_fft.win, 4, wx.EXPAND) + + if 0: + post_deemph_fft = fftsink2.fft_sink_f(self.panel, title="Post Deemph", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.fm_freq_min, self.fm_freq_max, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wfm_rx_block, "USRP WFM RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_wfm_rcv2_nogui.py b/gr-uhd/examples/python/usrp_wfm_rcv2_nogui.py new file mode 100755 index 000000000..f7e72ae6f --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv2_nogui.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math + +class wfm_rx_block (gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default="A:0 A:0", + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("", "--f1", type="eng_float", default=100.7e6, + help="set 1st station frequency to FREQ", metavar="FREQ") + parser.add_option("", "--f2", type="eng_float", default=102.5e6, + help="set 2nd station freq to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=40, + help="set gain in dB (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + if abs(options.f1 - options.f2) > 5.5e6: + print "Sorry, two stations must be within 5.5MHz of each other" + raise SystemExit + + f = (options.f1, options.f2) + + self.vol = .1 + self.state = "FREQ" + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + stream_args = uhd.stream_args('fc32', channels=range(2)) + self.u = uhd.usrp_source(device_addr=options.args, stream_args=stream_args) + + # Set front end channel mapping + self.u.set_subdev_spec(options.spec) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 32e3 + audio_decim = int(demod_rate / audio_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + # Make sure dboard can suppor the required frequencies + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + + # sound card as final sink + self.audio_sink = audio.sink(int(audio_rate), options.audio_output) + + # taps for channel filter + nfilts = 32 + chan_coeffs = optfir.low_pass (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + rrate = usrp_rate / dev_rate + + # set front end PLL to middle frequency + mid_freq = (f[0] + f[1]) / 2.0 + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + for n in range(2): + chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + guts = blks2.wfm_rcv (demod_rate, audio_decim) + volume_control = gr.multiply_const_ff(self.vol) + + #self.connect((self.di, n), chan_filt) + self.connect((self.u, n), chan_filt) + self.connect(chan_filt, guts, volume_control) + self.connect(volume_control, (self.audio_sink, n)) + + # Test the the requested frequencies are in range + if(f[n] < self.fm_freq_min or f[n] > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # Tune each channel by setting the RF freq to mid_freq and the + # DDC freq to f[n]. + tr = uhd.tune_request(f[n], rf_freq=mid_freq, + rf_freq_policy=uhd.tune_request.POLICY_MANUAL) + self.u.set_center_freq(tr, n) + + # Set gain for each channel + self.set_gain(options.gain, n) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, n) + + def set_vol (self, vol): + self.vol = vol + self.volume_control.set_k(self.vol) + + + def set_gain(self, gain, n): + self.u.set_gain(gain, n) + +if __name__ == '__main__': + tb = wfm_rx_block() + try: + tb.run() + except KeyboardInterrupt: + pass diff --git a/gr-uhd/examples/python/usrp_wfm_rcv_fmdet.py b/gr-uhd/examples/python/usrp_wfm_rcv_fmdet.py new file mode 100755 index 000000000..514bd4faf --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv_fmdet.py @@ -0,0 +1,349 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form, scopesink2 +from optparse import OptionParser +import sys +import wx + +class wfm_rx_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=65, + help="set gain in dB (default is midpoint)") + parser.add_option("-s", "--squelch", type="eng_float", default=0, + help="set squelch level (default is 0)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 48e3 + audio_decim = 10 + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = gr.firdes.low_pass_2(10*nfilts, # gain + nfilts*usrp_rate, # sampling rate + 90e3, # passband cutoff + 30e3, # transition bw + 70) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + self.guts = blks2.wfm_rcv_fmdet (demod_rate, audio_decim) + + chan_rate = audio_rate / (demod_rate/audio_decim) + self.rchan_filt = blks2.pfb_arb_resampler_fff(chan_rate) + self.lchan_filt = blks2.pfb_arb_resampler_fff(chan_rate) + + # FIXME rework {add,multiply}_const_* to handle multiple streams + self.volume_control_l = gr.multiply_const_ff(self.vol) + self.volume_control_r = gr.multiply_const_ff(self.vol) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.chan_filt, self.guts) + self.connect((self.guts, 0), self.lchan_filt, + self.volume_control_l, (self.audio_sink,0)) + self.connect((self.guts, 1), self.rchan_filt, + self.volume_control_r, (self.audio_sink,1)) + + try: + self.guts.stereo_carrier_pll_recovery.squelch_enable(True) + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + if abs(options.freq) < 1e6: + options.freq *= 1e6 + + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.fm_freq_min or options.freq > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + self.set_gain(options.gain) + self.set_vol(options.volume) + try: + self.guts.stereo_carrier_pll_recovery.set_lock_threshold(options.squelch) + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_fm_demod_fft = fftsink2.fft_sink_f(self.panel, title="Post FM Demod", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.fm_demod, post_fm_demod_fft) + vbox.Add (post_fm_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_stereo_carrier_generator_fft = fftsink2.fft_sink_c (self.panel, title="Post Stereo_carrier_generator", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.stereo_carrier_generator, post_stereo_carrier_generator_fft) + vbox.Add (post_stereo_carrier_generator_fft.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_left = fftsink2.fft_sink_f (self.panel, title="Post_Deemphasis_Left", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.deemph_Left, post_deemphasis_left) + vbox.Add (post_deemphasis_left.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_right = fftsink2.fft_sink_f(self.panel, title="Post_Deemphasis_Right", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph_Left, post_deemphasis_right) + vbox.Add (post_deemphasis_right.win, 4, wx.EXPAND) + + + if 0: + LmR_fft = fftsink2.fft_sink_f(self.panel, title="LmR", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.LmR_real,LmR_fft) + vbox.Add (LmR_fft.win, 4, wx.EXPAND) + + if 0: + self.scope = scopesink2.scope_sink_f(self.panel, sample_rate=demod_rate) + self.connect (self.guts.fm_demod,self.scope) + vbox.Add (self.scope.win,4,wx.EXPAND) + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.fm_freq_min, self.fm_freq_max, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + + myform['sqlch_thrsh'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Stereo Squelch Threshold", + weight=3, range=(0.0,1.0,0.01), + callback=self.set_squelch) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control_l.set_k(10**(self.vol/10)) + self.volume_control_r.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_squelch(self,squelch_threshold): + try: + self.guts.stereo_carrier_pll_recovery.set_lock_threshold(squelch_threshold); + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wfm_rx_block, "USRP WFM RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_wfm_rcv_nogui.py b/gr-uhd/examples/python/usrp_wfm_rcv_nogui.py new file mode 100755 index 000000000..7d042983e --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv_nogui.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +class wfm_rx_block (gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.state = "FREQ" + self.freq = 0 + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 32e3 + audio_decim = int(demod_rate / audio_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = optfir.low_pass (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 80e3, # passband cutoff + 115e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + self.guts = blks2.wfm_rcv (demod_rate, audio_decim) + + self.volume_control = gr.multiply_const_ff(1) + + # sound card as final sink + self.audio_sink = audio.sink(int(audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.chan_filt, self.guts, + self.volume_control, self.audio_sink) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.fm_freq_min or options.freq > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + + if r: + self.freq = target_freq + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Freq: %s Volume:%f Setting:%s" % ( + eng_notation.num_to_str(self.freq), self.vol, self.state) + self._set_status_msg(msg, 1) + + def _set_status_msg(self, msg, which=0): + print msg + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + tb = wfm_rx_block() + try: + tb.run() + except KeyboardInterrupt: + pass diff --git a/gr-uhd/examples/python/usrp_wfm_rcv_pll.py b/gr-uhd/examples/python/usrp_wfm_rcv_pll.py new file mode 100755 index 000000000..53b1e6fea --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv_pll.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form, scopesink2 +from optparse import OptionParser +import sys +import wx + +class wfm_rx_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=65, + help="set gain in dB (default is midpoint)") + parser.add_option("-s", "--squelch", type="eng_float", default=0, + help="set squelch level (default is 0)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 48e3 + audio_decim = 10 + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = gr.firdes.low_pass_2 (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 90e3, # passband cutoff + 30e3, # stopband cutoff + 70) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + + self.guts = blks2.wfm_rcv_pll (demod_rate, audio_decim) + + chan_rate = audio_rate / (demod_rate/audio_decim) + self.rchan_filt = blks2.pfb_arb_resampler_fff(chan_rate) + self.lchan_filt = blks2.pfb_arb_resampler_fff(chan_rate) + + # FIXME rework {add,multiply}_const_* to handle multiple streams + self.volume_control_l = gr.multiply_const_ff(self.vol) + self.volume_control_r = gr.multiply_const_ff(self.vol) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.chan_filt, self.guts) + self.connect((self.guts, 0), self.lchan_filt, + self.volume_control_l, (self.audio_sink,0)) + self.connect((self.guts, 1), self.rchan_filt, + self.volume_control_r, (self.audio_sink,1)) + + try: + self.guts.stereo_carrier_pll_recovery.squelch_enable(True) + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.fm_freq_min or options.freq > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + self.set_gain(options.gain) + self.set_vol(options.volume) + try: + self.guts.stereo_carrier_pll_recovery.set_lock_threshold(options.squelch) + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_fm_demod_fft = fftsink2.fft_sink_f(self.panel, title="Post FM Demod", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.fm_demod, post_fm_demod_fft) + vbox.Add (post_fm_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_stereo_carrier_generator_fft = fftsink2.fft_sink_c (self.panel, title="Post Stereo_carrier_generator", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.stereo_carrier_generator, post_stereo_carrier_generator_fft) + vbox.Add (post_stereo_carrier_generator_fft.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_left = fftsink2.fft_sink_f (self.panel, title="Post_Deemphasis_Left", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.deemph_Left, post_deemphasis_left) + vbox.Add (post_deemphasis_left.win, 4, wx.EXPAND) + + if 0: + post_deemphasis_right = fftsink2.fft_sink_f(self.panel, title="Post_Deemphasis_Right", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph_Left, post_deemphasis_right) + vbox.Add (post_deemphasis_right.win, 4, wx.EXPAND) + + + if 0: + LmR_fft = fftsink2.fft_sink_f(self.panel, title="LmR", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.LmR_real,LmR_fft) + vbox.Add (LmR_fft.win, 4, wx.EXPAND) + + if 0: + self.scope = scopesink2.scope_sink_f(self.panel, sample_rate=demod_rate) + self.connect (self.guts.fm_demod,self.scope) + vbox.Add (self.scope.win,4,wx.EXPAND) + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.fm_freq_min, self.fm_freq_max, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + + myform['sqlch_thrsh'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Stereo Squelch Threshold", + weight=3, range=(0.0,1.0,0.01), + callback=self.set_squelch) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control_l.set_k(10**(self.vol/10)) + self.volume_control_r.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_squelch(self,squelch_threshold): + try: + self.guts.stereo_carrier_pll_recovery.set_lock_threshold(squelch_threshold); + except: + print "FYI: This implementation of the stereo_carrier_pll_recovery has no squelch implementation yet" + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wfm_rx_block, "USRP WFM RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_wfm_rcv_sca.py b/gr-uhd/examples/python/usrp_wfm_rcv_sca.py new file mode 100755 index 000000000..646fe8587 --- /dev/null +++ b/gr-uhd/examples/python/usrp_wfm_rcv_sca.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python +# +# Copyright 2006,2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +""" +Here is a bit of code that will receive SCA analog subcarriers of FM +Broadcast Stations using the USRP. It is a modified version of +usrp_wfm_rcv.py. + +Common SCA frequencies are 67 kHz and 92 kHz. SCA is used for Reading +Services for the Blind, Background Music, Foreign Language Services, and +other services. Remember you may hear static when tuned to a FM station +because this code only outputs SCA audio. + +The USRP gain is critical for good decoding. Adjust for minimum noise. + I use the Post FM Demod FFT to check for SCA subcarriers and to adjust +the USRP gain for the lowest noise floor. The stereo pilot at 19 KHz, +the stereo difference signal around 38 KHz, and RDS at 57 KHz are also +displayed on the Post FM Demod FFT if present. + +The range below 67 kHz is used for SCA only when Stereo is not used. + +The SCA recieve range is not as far as the main FM carrier receive range +so tune in strong local stations first. + +I tried to comment the code with the various parameters. There seems to +be several choices for a couple of them. I coded the common ones I see +here. + +In the local area there are a couple of stations using digital SCA. +These look similar to narrow DRM signals and I wonder if they are using +OFDM. +""" + + +from gnuradio import gr, optfir, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import math +import wx + +class wfm_rx_sca_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=40, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=87.9e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=108.1e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + self.fm_freq_min = options.freq_min + self.fm_freq_max = options.freq_max + + # build graph + + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 32e3 + sca_demod_rate = 64e3 + audio_decim = int(demod_rate / audio_rate) + sca_chanfilt_decim = int(demod_rate / sca_demod_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = optfir.low_pass (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 100e3, # passband cutoff + 140e3, # stopband cutoff + 0.1, # passband ripple + 60) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + #Create demodulator block for Main FM Channel + max_dev = 75e3 + fm_demod_gain = demod_rate/(2*math.pi*max_dev) + self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) + + # Note - deemphasis is not applied to the Main FM Channel as + # main audio is not decoded + + # SCA Devation is 10% of carrier but some references say 20% + # if mono with one SCA (6 KHz seems typical) + max_sca_dev = 6e3 + + # Create filter to get SCA channel we want + sca_chan_coeffs = gr.firdes.low_pass (1.0, # gain + demod_rate, # sampling rate + max_sca_dev, # cutoff freq + max_sca_dev/3, # trans. band + gr.firdes.WIN_HANN) # filter type + + self.ddc = gr.freq_xlating_fir_filter_fcf(sca_chanfilt_decim, # decim rate + sca_chan_coeffs, # taps + 0, # freq translation amount (Gets set by the UI) + demod_rate) # input sample rate + + #Create demodulator block for SCA Channel + sca_demod_gain = sca_demod_rate/(2*math.pi*max_sca_dev) + self.fm_demod_sca = gr.quadrature_demod_cf (sca_demod_gain) + + + # SCA analog audio is bandwidth limited to 5 KHz + max_sca_audio_freq = 5.0e3 + + # SCA analog deephasis is 150 uS (75 uS may be used) + sca_tau = 150e-6 + + # compute FIR filter taps for SCA audio filter + audio_coeffs = gr.firdes.low_pass (1.0, # gain + sca_demod_rate, # sampling rate + max_sca_audio_freq, # cutoff freq + max_sca_audio_freq/2.5, # trans. band + gr.firdes.WIN_HAMMING) + + # input: float; output: float + self.audio_filter = gr.fir_filter_fff (audio_decim, audio_coeffs) + + # Create deemphasis block that is applied after SCA demodulation + self.deemph = blks2.fm_deemph (audio_rate, sca_tau) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), + options.audio_output, + False) # ok_to_block + + # now wire it all together + self.connect (self.u, self.chan_filt, self.fm_demod, + self.ddc, self.fm_demod_sca) + self.connect (self.fm_demod_sca, self.audio_filter, + self.deemph, self.volume_control, + self.audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, sca_demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + frange = self.u.get_freq_range() + if(frange.start() > self.fm_freq_max or frange.stop() < self.fm_freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.fm_freq_min or options.freq > self.fm_freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + self.set_sca_freq(67000) # A common SCA Frequency + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, sca_demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + def _form_set_sca_freq(kv): + return self.set_sca_freq(kv['sca_freq']) + + if 1: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_demod_fft = fftsink2.fft_sink_f(self.panel, title="Post FM Demod", + fft_size=2048, sample_rate=demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.fm_demod, post_demod_fft) + vbox.Add (post_demod_fft.win, 4, wx.EXPAND) + + if 0: + post_demod_sca_fft = fftsink2.fft_sink_f(self.panel, title="Post SCA Demod", + fft_size=1024, sample_rate=sca_demod_rate, + y_per_div=10, ref_level=0) + self.connect (self.fm_demod_sca, post_demod_sca_fft) + vbox.Add (post_demod_sca_fft.win, 4, wx.EXPAND) + + if 0: + post_deemph_fft = fftsink2.fft_sink_f (self.panel, title="Post SCA Deemph", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=-20) + self.connect (self.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.fm_freq_min, self.fm_freq_max, 0.1e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['sca_freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="SCA", weight=1, + callback=myform.check_input_and_call(_form_set_sca_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['sca_freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(38e3, 100e3, 1.0e3), + callback=self.set_sca_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.stop(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital down converter. + """ + r = self.u.set_center_freq(target_freq) + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + self._set_status_msg("Failed", 0) + return False + + def set_sca_freq(self, target_sca_freq): + + self.ddc.set_center_freq(-target_sca_freq) + self.myform['sca_freq'].set_value(target_sca_freq) # update displayed value + self.myform['sca_freq_slider'].set_value(target_sca_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wfm_rx_sca_block, "USRP WFM SCA RX") + app.MainLoop () diff --git a/gr-uhd/examples/python/usrp_wxapt_rcv.py b/gr-uhd/examples/python/usrp_wxapt_rcv.py new file mode 100755 index 000000000..6b57de3bc --- /dev/null +++ b/gr-uhd/examples/python/usrp_wxapt_rcv.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python +# +# Copyright 2005-2007,2011 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, audio, blks2, uhd +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, fftsink2, form +from optparse import OptionParser +import sys +import wx + + +class wxapt_rx_block (stdgui2.std_top_block): + def __init__(self,frame,panel,vbox,argv): + stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) + + parser=OptionParser(option_class=eng_option) + parser.add_option("-a", "--args", type="string", default="", + help="UHD device address args, [default=%default]") + parser.add_option("", "--spec", type="string", default=None, + help="Subdevice of UHD device where appropriate") + parser.add_option("-A", "--antenna", type="string", default=None, + help="select Rx Antenna where appropriate") + parser.add_option("-f", "--freq", type="eng_float", default=137.5e6, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="eng_float", default=None, + help="set gain in dB (default is midpoint)") + parser.add_option("-V", "--volume", type="eng_float", default=None, + help="set volume (default is midpoint)") + parser.add_option("-O", "--audio-output", type="string", default="", + help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") + parser.add_option("", "--freq-min", type="eng_float", default=137e6, + help="Set a minimum frequency [default=%default]") + parser.add_option("", "--freq-max", type="eng_float", default=138e6, + help="Set a maximum frequency [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.frame = frame + self.panel = panel + + self.vol = 0 + self.state = "FREQ" + self.freq = 0 + + self.freq_min = options.freq_min + self.freq_max = options.freq_max + + # build graph + self.u = uhd.usrp_source(device_addr=options.args, stream_args=uhd.stream_args('fc32')) + + # Set the subdevice spec + if(options.spec): + self.u.set_subdev_spec(options.spec, 0) + + # Set the antenna + if(options.antenna): + self.u.set_antenna(options.antenna, 0) + + usrp_rate = 320e3 + demod_rate = 320e3 + audio_rate = 32e3 + audio_decim = int(demod_rate / audio_rate) + + self.u.set_samp_rate(usrp_rate) + dev_rate = self.u.get_samp_rate() + + nfilts = 32 + chan_coeffs = gr.firdes.low_pass_2 (nfilts, # gain + nfilts*usrp_rate, # sampling rate + 40e3, # passband cutoff + 20e3, # transition bw + 60) # stopband attenuation + rrate = usrp_rate / dev_rate + self.chan_filt = blks2.pfb_arb_resampler_ccf(rrate, chan_coeffs, nfilts) + + self.guts = blks2.wfm_rcv (demod_rate, audio_decim) + + self.volume_control = gr.multiply_const_ff(self.vol) + + # sound card as final sink + self.audio_sink = audio.sink (int (audio_rate), options.audio_output) + + # now wire it all together + self.connect (self.u, self.chan_filt, self.guts, + self.volume_control, self.audio_sink) + + self._build_gui(vbox, usrp_rate, demod_rate, audio_rate) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + g = self.u.get_gain_range() + options.gain = float(g.start()+g.stop())/2.0 + + if options.volume is None: + g = self.volume_range() + options.volume = float(g[0]+g[1])/2 + + frange = self.u.get_freq_range() + if(frange.start() > self.freq_max or frange.stop() < self.freq_min): + sys.stderr.write("Radio does not support required frequency range.\n") + sys.exit(1) + if(options.freq < self.freq_min or options.freq > self.freq_max): + sys.stderr.write("Requested frequency is outside of required frequency range.\n") + sys.exit(1) + + # set initial values + self.set_gain(options.gain) + self.set_vol(options.volume) + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + + if 1: + self.src_fft = fftsink2.fft_sink_c (self.panel, title="Data from USRP", + fft_size=512, sample_rate=usrp_rate, + ref_scale=32768.0, ref_level=0, y_divs=12) + self.connect (self.u, self.src_fft) + vbox.Add (self.src_fft.win, 4, wx.EXPAND) + + if 1: + post_deemph_fft = fftsink2.fft_sink_f (self.panel, title="Post Deemph", + fft_size=512, sample_rate=demod_rate, + y_per_div=10, ref_level=-20) + self.connect (self.guts.deemph, post_deemph_fft) + vbox.Add (post_deemph_fft.win, 4, wx.EXPAND) + + if 1: + post_filt_fft = fftsink2.fft_sink_f (self.panel, title="Post Filter", + fft_size=512, sample_rate=audio_rate, + y_per_div=10, ref_level=0) + self.connect (self.guts.audio_filter, post_filt_fft) + vbox.Add (post_filt_fft.win, 4, wx.EXPAND) + + + # control area form at bottom + self.myform = myform = form.form() + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0) + myform['freq_slider'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3, + range=(self.freq_min, self.freq_max, 0.0005e6), + callback=self.set_freq) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['volume'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume", + weight=3, range=self.volume_range(), + callback=self.set_vol) + hbox.Add((5,0), 1) + + g = self.u.get_gain_range() + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=(g.start(), g.start(), g.step()), + callback=self.set_gain) + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + try: + self.knob = powermate.powermate(self.frame) + self.rot = 0 + powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate) + powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button) + except: + print "FYI: No Powermate or Contour Knob found" + + + def on_rotate (self, event): + self.rot += event.delta + if (self.state == "FREQ"): + if self.rot >= 3: + self.set_freq(self.freq + .1e6) + self.rot -= 3 + elif self.rot <=-3: + self.set_freq(self.freq - .1e6) + self.rot += 3 + else: + step = self.volume_range()[2] + if self.rot >= 3: + self.set_vol(self.vol + step) + self.rot -= 3 + elif self.rot <=-3: + self.set_vol(self.vol - step) + self.rot += 3 + + def on_button (self, event): + if event.value == 0: # button up + return + self.rot = 0 + if self.state == "FREQ": + self.state = "VOL" + else: + self.state = "FREQ" + self.update_status_bar () + + + def set_vol (self, vol): + g = self.volume_range() + self.vol = max(g[0], min(g[1], vol)) + self.volume_control.set_k(10**(self.vol/10)) + self.myform['volume'].set_value(self.vol) + self.update_status_bar () + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + """ + + r = self.u.set_center_freq(target_freq) + + if r: + self.freq = target_freq + self.myform['freq'].set_value(target_freq) # update displayed value + self.myform['freq_slider'].set_value(target_freq) # update displayed value + self.update_status_bar() + self._set_status_msg("OK", 0) + return True + + self._set_status_msg("Failed", 0) + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_gain(gain) + + def update_status_bar (self): + msg = "Volume:%r Setting:%s" % (self.vol, self.state) + self._set_status_msg(msg, 1) + self.src_fft.set_baseband_freq(self.freq) + + def volume_range(self): + return (-20.0, 0.0, 0.5) + + +if __name__ == '__main__': + app = stdgui2.stdapp (wxapt_rx_block, "USRP WXAPT RX") + app.MainLoop () |