From 2b77cc7bc535618c4bba6ce1f3d6fa388e843933 Mon Sep 17 00:00:00 2001 From: n4hy Date: Fri, 4 Jul 2008 03:05:33 +0000 Subject: [PATCH] This completes the msdd6000 update with all example code and other utilities git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@8786 221aa14e-8319-0410-a670-987f0aec2ac5 --- .../src/python-examples/msdd_dynamics.py | 99 ++++++ gr-msdd6000/src/python-examples/msdd_fft.py | 277 ++++++++++++++++ .../src/python-examples/msdd_plot_psd.py | 74 +++++ gr-msdd6000/src/python-examples/msdd_rcv.py | 287 ++++++++++++++++ .../python-examples/msdd_spectrum_sense.py | 296 +++++++++++++++++ .../msdd_spectrum_waterfall.py | 306 ++++++++++++++++++ .../src/python-examples/new_msdd_fft.py | 299 +++++++++++++++++ .../src/python-examples/playback_samples.m | 12 + .../src/python-examples/read_complex_binary.m | 48 +++ 9 files changed, 1698 insertions(+) create mode 100755 gr-msdd6000/src/python-examples/msdd_dynamics.py create mode 100755 gr-msdd6000/src/python-examples/msdd_fft.py create mode 100755 gr-msdd6000/src/python-examples/msdd_plot_psd.py create mode 100755 gr-msdd6000/src/python-examples/msdd_rcv.py create mode 100755 gr-msdd6000/src/python-examples/msdd_spectrum_sense.py create mode 100755 gr-msdd6000/src/python-examples/msdd_spectrum_waterfall.py create mode 100755 gr-msdd6000/src/python-examples/new_msdd_fft.py create mode 100644 gr-msdd6000/src/python-examples/playback_samples.m create mode 100644 gr-msdd6000/src/python-examples/read_complex_binary.m diff --git a/gr-msdd6000/src/python-examples/msdd_dynamics.py b/gr-msdd6000/src/python-examples/msdd_dynamics.py new file mode 100755 index 00000000..8cd1e52b --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_dynamics.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# +# Copyright 2008 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 +from gnuradio import msdd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import time + +class benchmark_msdd6000(gr.top_block): + def __init__(self, address, options): + gr.top_block.__init__(self) + + # Extract the initial options + self.frequency = options.frequency + self.filename = options.filename + self.decim = options.decim + self.gain = options.gain + self.address = address + + # Set up and initialize the MSDD receiver + self.port = 10001 # required port + self.src = msdd.source_c(0, 1, self.address, self.port) + self.src.set_decim_rate(self.decim) + self.src.set_desired_packet_size(0, 1460) + self.src.set_pga(0, self.gain) + self.src.set_rx_freq(0, self.frequency) + + # Display some info + print "Min PGA: ", self.src.pga_min() + print "Max PGA: ", self.src.pga_max() + print "PGA: ", self.src.pga(0) + print "Decim: ", self.src.decim_rate() + print "Freq: ", self.src.rx_freq(0) + + # Build a file sink to save the info for post analysis + self.snk = gr.file_sink(gr.sizeof_gr_complex, self.filename) + + # Connect the reciever source to file sink + self.connect(self.src, self.snk) + +def main(): + ''' This is a simple little script to play with retunning of the MSDD board. + You can cycle through frequencies or the attenuation of the board here. + ''' + + usage="%prog: [options] host_address" + parser = OptionParser(usage=usage, option_class=eng_option, conflict_handler="resolve") + parser.add_option("-f", "--frequency", type="eng_float", default=100e6, + help="set frequency (Hz) [default=%default]") + parser.add_option("-d", "--decim", type="int", default=256, + help="set decimation rate [default=%default]") + parser.add_option("-g", "--gain", type="int", default=32, + help="set receiver gain (dB) [default=%default]") + parser.add_option("-F", "--filename", type="string", default="output.dat", + help="set output filename [default=%default]") + (options, args) = parser.parse_args () + host_address = args[0] + + # Set up benchmark system that simply connects the MSDD source to a file sink + tb = benchmark_msdd6000(host_address, options) + tb.start() # start it here + + # Adjust your parameters here. Use the time.sleep(x) function to set a wait period + # between adjusting the parameter. + for i in range(7): + time.sleep(0.5) + if 0: + freq = (tb.src.rx_freq(0) + 1) * 1e6 + tb.src.set_rx_freq(0, freq) + print "Setting frequency: ", freq + if 1: + pga = tb.src.pga(0)+10 + tb.src.set_pga(0, pga) + print "Setting PGA: ", pga + + tb.stop() # stop the radio + +if __name__ == '__main__': + main() diff --git a/gr-msdd6000/src/python-examples/msdd_fft.py b/gr-msdd6000/src/python-examples/msdd_fft.py new file mode 100755 index 00000000..813a77d3 --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_fft.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007 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, gru +from gnuradio import msdd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, form, slider +from optparse import OptionParser +import wx +import sys + + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem + return (0, 0) + if u.db[1][0].dbid() >= 0: + return (1, 0) + return (0, 0) + + +class app_top_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.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-w", "--which", type="int", default=0, + help="select which MSDD (0, 1, ...) default is %default", + metavar="NUM") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select MSDD Rx side A or B (default=first one with a daughterboard)") + parser.add_option("-A", "--antenna", default=None, + help="select Rx Antenna (only on RFX-series boards)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [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("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + self.show_debug_info = True + + # build the graph + + #self.u = MSDD.source_simo(which=options.which, decim_rate=options.decim) + self.u = msdd.source_simple("192.168.1.200", 0) + self.u.set_decim_rate(options.decim) #(16) + +# msdd_src = gr.file_source(gr.sizeof_gr_complex, 'msdd.dat') +# thr = gr.throttle(gr.sizeof_gr_complex, 200000) +# self.connect(msdd_src, thr) + +# if options.rx_subdev_spec is None: +# options.rx_subdev_spec = pick_subdevice(self.u) +# self.u.set_mux(MSDD.determine_rx_mux_value(self.u, options.rx_subdev_spec)) + +# if options.width_8: +# width = 8 +# shift = 8 +# format = self.u.make_format(width, shift) +# print "format =", hex(format) +# r = self.u.set_format(format) +# print "set_format =", r + + # determine the daughterboard subdevice we're using +# self.subdev = MSDD.selected_subdev(self.u, options.rx_subdev_spec) + +# print "Initial Freq", self.u.rx_freq(0), "deci: ", self.u.decim_rate() +# input_rate = 50e6 / self.u.decim_rate() + input_rate = 50e6 / options.decim; + + if options.waterfall: + self.scope = \ + waterfallsink2.waterfall_sink_c (panel, fft_size=1024, sample_rate=input_rate) + elif options.oscilloscope: + self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate) + else: + self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, sample_rate=input_rate) + +# self.connect(self.u, self.scope) + + msdd_sink = gr.file_sink(gr.sizeof_gr_complex, 'schmen1.dat') + + self.conv = gr.interleaved_short_to_complex(); + self.connect(self.u, self.conv, msdd_sink) + self._build_gui(vbox) + + # set initial values + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + #g = self.subdev.gain_range() + self.gain_range = (20,70,.5); + options.gain = float(self.gain_range[0]+self.gain_range[1])/2 + + if options.freq is None: + # if no freq was specified, use the mid-point + #r = self.subdev.freq_range() + r = (30e6,6e9,1e6) + options.freq = float(r[0]+r[1])/2 + + self.set_gain(options.gain) +# +# if options.antenna is not None: +# print "Selecting antenna %s" % (options.antenna,) +# self.subdev.select_rx_antenna(options.antenna) + + if self.show_debug_info: + #self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['decim'].set_value(options.decim) + # self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + # self.myform['dbname'].set_value(self.subdev.name()) + self.myform['baseband'].set_value(0) + self.myform['ddc'].set_value(0) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + g = self.gain_range = (20,50,.5); + myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, + min=int(g[0]), max=int(g[1]), + callback=self.set_gain) + + hbox.Add((5,0), 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) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + 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.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + +# 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), 1) + myform['baseband'] = form.static_float_field( + parent=panel, sizer=hbox, label="Analog BB") + + hbox.Add((5,0), 1) + myform['ddc'] = form.static_float_field( + parent=panel, sizer=hbox, label="DDC") + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + 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_rx_freq (0, target_freq) + #r = self.u.tune(0, self.subdev, target_freq) + if r: + self.myform['freq'].set_value(target_freq) # update displayed value +# if self.show_debug_info: +# self.myform['baseband'].set_value(r.baseband_freq) +# self.myform['ddc'].set_value(r.dxc_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + #self.subdev.set_gain(gain) + self.u.set_pga(0, gain) + + def set_decim(self, decim): + ok = self.u.set_decim_rate(decim) + if not ok: + print "set_decim failed" + #input_rate = 20e6 / self.u.decim_rate() + #self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(decim) + #self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + return ok + +def main (): + app = stdgui2.stdapp(app_top_block, "MSDD FFT", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gr-msdd6000/src/python-examples/msdd_plot_psd.py b/gr-msdd6000/src/python-examples/msdd_plot_psd.py new file mode 100755 index 00000000..16869af1 --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_plot_psd.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +address = "10.45.4.43"; +fc = 3.5; +decim = 8; +num_avg = 10 + +fs = 102.4; +packet_size=1400; +gain = 0; +port = 10001; + +import math; +import time; +import Numeric; +from gnuradio import msdd,gr,window,wimax; +from pylab import *; + +src = msdd.source_simple(address, port) # build source object + +fft_size = 2048; + +w = window.blackmanharris(fft_size); +s2v = gr.stream_to_vector(2*gr.sizeof_float, fft_size); +fft = gr.fft_vcc(fft_size, True, w, True); +conj = wimax.conj_vcc(fft_size); +mul = gr.multiply_vcc(fft_size); +norm = wimax.norm_cf(); +avg = wimax.average_vXX(gr.sizeof_float, 2*fft_size, num_avg); +v2s = gr.vector_to_stream(2*gr.sizeof_float, fft_size); +#sink = gr.vector_sink_f(); + +src.set_decim_rate(decim); + +src.set_pga(0,gain); +src.set_rx_freq(0,fc); + +q = gr.msg_queue(fft_size); +sink = gr.message_sink(gr.sizeof_float, q, True); + +tb = gr.top_block(); +tb.connect(src,s2v,fft,(mul,0),avg,v2s,norm,sink); +tb.connect(fft,conj,(mul,1)); + +tb.start(); + +print "running" + +v = [] +x = []; + +bw = fs/(pow(2,decim)); +for i in range(0,fft_size): + norm_freq = (i - fft_size/2.0)/fft_size; + bin_frequency = fc + norm_freq * bw; + x.append( norm_freq ); + + + +for i in range(0,fft_size): + + d = q.delete_head(); + d = d.to_string(); + d = Numeric.fromstring(d, Numeric.Float32); + d = 10*log10(d); + + print "plotting\n"; + plot(x,d); + show(); + print "done\n" + + + + diff --git a/gr-msdd6000/src/python-examples/msdd_rcv.py b/gr-msdd6000/src/python-examples/msdd_rcv.py new file mode 100755 index 00000000..cc2f3e4a --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_rcv.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +# +# Copyright 2005,2006,2007 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, gru, eng_notation +from gnuradio import msdd +from gnuradio import blks2 +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import slider, powermate +from gnuradio.wxgui import stdgui2, form +from gnuradio.wxgui import fftsink2 +from optparse import OptionParser +#from usrpm import usrp_dbid +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("-R", "--rx-subdev-spec", type="subdev", default=None, +# help="select MSDD Rx side A or B (default=A)") + 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") + + (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.gain_range = (10, 70, .5) + self.state = "FREQ" + self.freq = 0 + msdd_decim = 2 + + # build graph + self.fft_size = 8192 + self.sample_rate = 200 + self.u = msdd.source_c(0, 1, "10.45.4.44", 10000) + self.u.set_decim_rate(4) + self.u.set_desired_packet_size(0, 1460*100) + + + #self.u.set_decim_rate(msdd_decim) +# usrp_rate = adc_rate / msdd_decim # 320 kS/s +# chanfilt_decim = 1 +# demod_rate = usrp_rate / chanfilt_decim +# audio_decimation = 10 +# audio_rate = demod_rate / audio_decimation # 32 kHz +# +# if options.rx_subdev_spec is None: +# options.rx_subdev_spec = pick_subdevice(self.u) +# +# self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) +# self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) +# print "Using RX d'board %s" % (self.subdev.side_and_name(),) +# +# +# chan_filt_coeffs = optfir.low_pass (1, # gain +# usrp_rate, # sampling rate +# 80e3, # passband cutoff +# 115e3, # stopband cutoff +# 0.1, # passband ripple +# 60) # stopband attenuation +# #print len(chan_filt_coeffs) +# chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) +# +# self.guts = blks2.wfm_rcv (demod_rate, audio_decimation) +# +# self.volume_control = gr.multiply_const_ff(self.vol) +# +# # sound card as final sink +# audio_sink = audio.sink (int (audio_rate), +# options.audio_output, +# False) # ok_to_block + + # now wire it all together + #self.connect (self.u, chan_filt, self.guts, self.volume_control, audio_sink) + + self._build_gui(vbox) + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + #g = self.subdev.gain_range() + g = self.gain_range + options.gain = float(g[0]+g[1])/2 +# +# 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 + + # 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") + print "Frequency: ", self.u.rx_freq(0) + + + def _set_status_msg(self, msg, which=0): + self.frame.GetStatusBar().SetStatusText(msg, which) + + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + self.src_fft = None + if 1: + self.src_fft = fftsink2.fft_sink_c(self.panel, title="Data from MSDD", + fft_size=512, sample_rate=512) +# self.s2f1 = gr.short_to_float() +# self.scope = scopesink2.scope_sink_f(self.panel, sample_rate=self.sample_rate*self.fft_size) + + self.connect (self.u, self.src_fft) + #self.connect (self.s2f1, self.scope) + 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=(30e6, 6e9, 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) + + myform['gain'] = \ + form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, range=self.gain_range, + 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_rx_freq(0, 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_pga(0,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, "MSDD FFT RX") + app.MainLoop () diff --git a/gr-msdd6000/src/python-examples/msdd_spectrum_sense.py b/gr-msdd6000/src/python-examples/msdd_spectrum_sense.py new file mode 100755 index 00000000..e3d182b0 --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_spectrum_sense.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python +# +# Copyright 2008 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, gru, eng_notation, optfir, window +from gnuradio import msdd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math +import struct +from pylab import * +from numpy import array +import time + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +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, sample_rate, percent, alpha=0.01): + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + self.fig = figure(1, facecolor="w", figsize=(12,9)) + self.sp = self.fig.add_subplot(1,1,1) + self.pl = self.sp.plot(range(100), 100*[1,]) + + params = {'backend': 'ps', + 'xtick.labelsize': self.axis_font_size, + 'ytick.labelsize': self.axis_font_size, + 'text.usetex': False} + rcParams.update(params) + + self.sp.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp.set_xlabel("Frequency (Hz)", fontsize=self.label_font_size, fontweight="bold") + self.sp.set_ylabel("Magnitude (dB)", fontsize=self.label_font_size, fontweight="bold") + self.text_alpha = figtext(0.10, 0.94, ('Moving average alpha: %s' % alpha), weight="heavy", size=self.text_size) + + self.cfreqs = list() + self.freqrange = list() + self.data = list() #array('f') + + self.alpha = alpha + + self.index = 0 + self.full = False + self.last_cfreq = 0 + + self.sample_rate = sample_rate + self.percent = (1.0-percent)/2.0 + + def parse(self, msg): + self.center_freq = msg.arg1() + self.vlen = int(msg.arg2()) + assert(msg.length() == self.vlen * gr.sizeof_float) + + + if(self.center_freq < self.last_cfreq): + print "Plotting spectrum\n" + self.full = True + + self.pl[0].set_data([self.freqrange, self.data]) + self.sp.set_ylim([min(self.data), max(self.data)]) + self.sp.set_xlim([min(self.freqrange), max(self.freqrange)]) + draw() + + self.index = 0 + del self.freqrange + self.freqrange = list() + #raw_input() + + self.last_cfreq = self.center_freq + + startind = int(self.percent * self.vlen) + endind = int((1.0 - self.percent) * self.vlen) + + fstep = self.sample_rate / self.vlen + f = [self.center_freq - self.sample_rate/2.0 + i*fstep for i in range(startind, endind)] + self.freqrange += f + + t = msg.to_string() + d = struct.unpack('%df' % (self.vlen,), t) + + if self.full: + for i in range(startind, endind): + self.data[self.index] = (1.0-self.alpha)*self.data[self.index] + (self.alpha)*d[i] + self.index += 1 + else: + self.data += [di for di in d[startind:endind]] + + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + usage = "usage: %prog [options] host min_freq max_freq" + parser = OptionParser(option_class=eng_option, usage=usage) + 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=5e-5, metavar="SECS", + help="time to delay (in seconds) after changing frequency [default=%default]") + parser.add_option("", "--dwell-delay", type="eng_float", default=50e-5, 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("-d", "--decim", type="intx", default=16, + help="set decimation to DECIM [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) != 3: + parser.print_help() + sys.exit(1) + + self.address = args[0] + self.min_freq = eng_notation.str_to_num(args[1]) + self.max_freq = eng_notation.str_to_num(args[2]) + + self.decim = options.decim + self.gain = options.gain + + if self.min_freq > self.max_freq: + self.min_freq, self.max_freq = self.max_freq, self.min_freq # swap them + + 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" + + adc_rate = 102.4e6 + self.int_rate = adc_rate / self.decim + print "Sampling rate: ", self.int_rate + + # build graph + self.port = 10001 + self.src = msdd.source_simple(self.address, self.port) + self.src.set_decim_rate(self.decim) + + self.set_gain(self.gain) + self.set_freq(self.min_freq) + + 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, True) + power = 0 + for tap in mywindow: + power += tap*tap + + norm = gr.multiply_const_cc(1.0/self.fft_size) + 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 % of the actual data throughput. + # This allows us to discard the bins on both ends of the spectrum. + self.percent = 0.4 + + self.freq_step = self.percent * self.int_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 * self.int_rate / self.fft_size))) # in fft_frames + dwell_delay = max(1, int(round(options.dwell_delay * self.int_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.src, s2v, fft, c2mag, log, stats) + + + 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 + + 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 + + """ + return self.src.set_rx_freq(0, target_freq) + + + def set_gain(self, gain): + self.src.set_pga(0, gain) + + +def main_loop(tb): + msgparser = parse_msg(tb.int_rate, tb.percent) + + 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 + msgparser.parse(tb.msgq.delete_head()) + + # Print center freq so we know that something is happening... + print msgparser.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__': + tb = my_top_block() + try: + tb.start() # start executing flow graph in another thread... + main_loop(tb) + + except KeyboardInterrupt: + pass diff --git a/gr-msdd6000/src/python-examples/msdd_spectrum_waterfall.py b/gr-msdd6000/src/python-examples/msdd_spectrum_waterfall.py new file mode 100755 index 00000000..05f047e1 --- /dev/null +++ b/gr-msdd6000/src/python-examples/msdd_spectrum_waterfall.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python +# +# Copyright 2008 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, gru, eng_notation, optfir, window +from gnuradio import msdd +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys +import math +import struct +from pylab import * +from numpy import array +import time + +matplotlib.interactive(True) +matplotlib.use('TkAgg') + +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, sample_rate, percent): + self.axis_font_size = 16 + self.label_font_size = 18 + self.title_font_size = 20 + self.text_size = 22 + + # Set up figures and subplots + self.fig = figure(1, facecolor="w", figsize=(12,9)) + self.sp = self.fig.add_subplot(1,1,1) + self.pl = self.sp.matshow(100*[range(100),]) + + params = {'xtick.labelsize': self.axis_font_size, + 'ytick.labelsize': self.axis_font_size} + rcParams.update(params) + + # Throw up some title info + self.sp.set_title(("FFT"), fontsize=self.title_font_size, fontweight="bold") + self.sp.set_xlabel("Frequency (Hz)", fontsize=self.label_font_size, fontweight="bold") + self.sp.set_ylabel("Sample index (should be time)", fontsize=self.label_font_size, fontweight="bold") + + self.freqrange = list() + self.data = list() + self.data3 = list() + + self.index = 0 + self.last_cfreq = 0 + + # So we know how to splice the data + self.sample_rate = sample_rate + self.percent = (1.0-percent)/2.0 + + def parse(self, msg): + self.center_freq = msg.arg1() # read the current center frequency + self.vlen = int(msg.arg2()) # read the length of the data set received + + # wait until we wrap around before plotting the entire collected band + if(self.center_freq < self.last_cfreq): + #print "Plotting spectrum\n" + + # If we have 100 sets, start dropping the oldest + if(len(self.data3) > 100): + self.data3.pop(0) + self.data3.append(self.data) + + # add the new data to the plot + self.pl.set_data(self.data3) + draw() + + # reset lists to collect next round + self.index = 0 + del self.freqrange + self.freqrange = list() + del self.data + self.data = list() + #raw_input() + + self.last_cfreq = self.center_freq + + startind = int(self.percent * self.vlen) + endind = int((1.0 - self.percent) * self.vlen) + + fstep = self.sample_rate / self.vlen + f = [self.center_freq - self.sample_rate/2.0 + i*fstep for i in range(startind, endind)] + self.freqrange += f + + t = msg.to_string(); + + d = struct.unpack('%df' % (self.vlen,), t) + + self.data += [di for di in d[startind:endind]] + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + # Build an options parser to bring in information from the user on usage + usage = "usage: %prog [options] host min_freq max_freq" + parser = OptionParser(option_class=eng_option, usage=usage) + parser.add_option("-g", "--gain", type="eng_float", default=32, + help="set gain in dB (default is midpoint)") + parser.add_option("", "--tune-delay", type="eng_float", default=5e-5, metavar="SECS", + help="time to delay (in seconds) after changing frequency [default=%default]") + parser.add_option("", "--dwell-delay", type="eng_float", default=50e-5, 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("-d", "--decim", type="intx", default=16, + help="set decimation to DECIM [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) != 3: + parser.print_help() + sys.exit(1) + + # get user-provided info on address of MSDD and frequency to sweep + self.address = args[0] + self.min_freq = eng_notation.str_to_num(args[1]) + self.max_freq = eng_notation.str_to_num(args[2]) + + self.decim = options.decim + self.gain = options.gain + + if self.min_freq > self.max_freq: + self.min_freq, self.max_freq = self.max_freq, self.min_freq # swap them + + 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" + + # Sampling rate is hardcoded and cannot be read off device + adc_rate = 102.4e6 + self.int_rate = adc_rate / self.decim + print "Sampling rate: ", self.int_rate + + # build graph + self.port = 10001 # required port for UDP packets + + # which board, op mode, adx, port +# self.src = msdd.source_c(0, 1, self.address, self.port) # build source object + + self.conv = gr.interleaved_short_to_complex(); + + self.src = msdd.source_simple(self.address,self.port); + self.src.set_decim_rate(self.decim) # set decimation rate +# self.src.set_desired_packet_size(0, 1460) # set packet size to collect + + self.set_gain(self.gain) # set receiver's attenuation + self.set_freq(self.min_freq) # set receiver's rx frequency + + # restructure into vector format for FFT input + s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size) + + # set up FFT processing block + mywindow = window.blackmanharris(self.fft_size) + fft = gr.fft_vcc(self.fft_size, True, mywindow, True) + power = 0 + for tap in mywindow: + power += tap*tap + + # calculate magnitude squared of output of FFT + 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 % of the actual data throughput. + # This allows us to discard the bins on both ends of the spectrum. + self.percent = 0.4 + + # Calculate the frequency steps to use in the collection over the whole bandwidth + self.freq_step = self.percent * self.int_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 + + # use these values to set receiver settling time between samples and sampling time + # the default values provided seem to work well with the MSDD over 100 Mbps ethernet + tune_delay = max(0, int(round(options.tune_delay * self.int_rate / self.fft_size))) # in fft_frames + dwell_delay = max(1, int(round(options.dwell_delay * self.int_rate / self.fft_size))) # in fft_frames + + # set up message callback routine to get data from bin_statistics_f block + self.msgq = gr.msg_queue(16) + self._tune_callback = tune(self) # hang on to this to keep it from being GC'd + + # FIXME this block doesn't like to work with negatives because of the "d_max[i]=0" on line + # 151 of gr_bin_statistics_f.cc file. Set this to -10000 or something to get it to work. + stats = gr.bin_statistics_f(self.fft_size, self.msgq, + self._tune_callback, tune_delay, dwell_delay) + + # FIXME there's a concern over the speed of the log calculation + # We can probably calculate the log inside the stats block + self.connect(self.src, self.conv, s2v, fft, c2mag, log, stats) + + + def set_next_freq(self): + ''' Find and set the next frequency of the reciver. After going past the maximum frequency, + the frequency is wrapped around to the start again''' + 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 + + 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 + + """ + return self.src.set_rx_freq(0, target_freq) + + + def set_gain(self, gain): + self.src.set_pga(0, gain) + + +def main_loop(tb): + # Set up parser to get data from stats block and display them. + msgparser = parse_msg(tb.int_rate, tb.percent) + + 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 + d = tb.msgq.delete_head(); + print d.to_string(); + msgparser.parse(d) + #print msgparser.center_freq + +if __name__ == '__main__': + tb = my_top_block() + try: + tb.start() # start executing flow graph in another thread... + main_loop(tb) + + except KeyboardInterrupt: + pass diff --git a/gr-msdd6000/src/python-examples/new_msdd_fft.py b/gr-msdd6000/src/python-examples/new_msdd_fft.py new file mode 100755 index 00000000..0b31a00d --- /dev/null +++ b/gr-msdd6000/src/python-examples/new_msdd_fft.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007,2008 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, gru +from gnuradio import usrp +from gnuradio import msdd +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, form, slider +from optparse import OptionParser +import wx +import sys +import numpy + +def pick_subdevice(u): + """ + The user didn't specify a subdevice on the command line. + If there's a daughterboard on A, select A. + If there's a daughterboard on B, select B. + Otherwise, select A. + """ + return (0, 0) + + +class app_top_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.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-w", "--which", type="int", default=0, + help="select which USRP (0, 1, ...) default is %default", + metavar="NUM") + parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, + help="select USRP Rx side A or B (default=first one with a daughterboard)") + parser.add_option("-A", "--antenna", default=None, + help="select Rx Antenna (only on RFX-series boards)") + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [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("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-8", "--width-8", action="store_true", default=False, + help="Enable 8-bit samples across USB") + parser.add_option( "--no-hb", action="store_true", default=False, + help="don't use halfband filter in usrp") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + parser.add_option("", "--avg-alpha", type="eng_float", default=1e-1, + help="Set fftsink averaging factor, default=[%default]") + parser.add_option("", "--ref-scale", type="eng_float", default=13490.0, + help="Set dBFS=0dB input value, default=[%default]") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + self.options = options + self.show_debug_info = True + + # build the graph + if options.no_hb or (options.decim<8): + #Min decimation of this firmware is 4. + #contains 4 Rx paths without halfbands and 0 tx paths. + self.fpga_filename="std_4rx_0tx.rbf" +# self.u = usrp.source_c(which=options.which, decim_rate=options.decim, fpga_filename=self.fpga_filename) + self.u = msdd.source_simple("192.168.1.200",0); + else: + #Min decimation of standard firmware is 8. + #standard fpga firmware "std_2rxhb_2tx.rbf" + #contains 2 Rx paths with halfband filters and 2 tx paths (the default) + #self.u = usrp.source_c(which=options.which, decim_rate=options.decim) + self.u = msdd.source_simple("192.168.1.200",0); + + + input_rate = self.u.adc_freq() / self.u.decim_rate() + + if options.waterfall: + self.scope = \ + waterfallsink2.waterfall_sink_c (panel, fft_size=1024, sample_rate=input_rate) + elif options.oscilloscope: + self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate) + else: + self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, sample_rate=input_rate, + ref_scale=options.ref_scale, ref_level=0.0, y_divs = 10, + avg_alpha=options.avg_alpha) + + self.conv = gr.interleaved_short_to_complex(); + self.connect(self.u, self.conv, self.scope) + + self._build_gui(vbox) + self._setup_events() + + # set initial values + + if options.gain is None: + # if no gain was specified, use the mid-point in dB + #g = self.subdev.gain_range() + #g = self.u.gain_range() + g = [0,10] + options.gain = float(g[0]+g[1])/2 + + if options.freq is None: + # if no freq was specified, use the mid-point + #r = self.subdev.freq_range() + #r = self.u.freq_range() + r = [30e6, 6e9] + options.freq = float(r[0]+r[1])/2 + + self.set_gain(options.gain) + + if options.antenna is not None: + print "Selecting antenna %s" % (options.antenna,) + self.subdev.select_rx_antenna(options.antenna) + + if self.show_debug_info: + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + self.myform['dbname'].set_value("no subdevs used") + self.myform['baseband'].set_value(0) + self.myform['ddc'].set_value(0) + + if not(self.set_freq(options.freq)): + self._set_status_msg("Failed to set initial frequency") + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + #g = self.subdev.gain_range() + #g = self.u.gain_range() + g = [0,10] + myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain", + weight=3, + min=int(g[0]), max=int(g[1]), + callback=self.set_gain) + + hbox.Add((5,0), 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) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + 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.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + + hbox.Add((5,0), 1) + myform['fs@usb'] = form.static_float_field( + parent=panel, sizer=hbox, label="Fs@gigE") + + hbox.Add((5,0), 1) + myform['dbname'] = form.static_text_field( + parent=panel, sizer=hbox) + + hbox.Add((5,0), 1) + myform['baseband'] = form.static_float_field( + parent=panel, sizer=hbox, label="Analog BB") + + hbox.Add((5,0), 1) + myform['ddc'] = form.static_float_field( + parent=panel, sizer=hbox, label="DDC") + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + 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.tune(0, self.subdev, target_freq) + r = self.u.set_rx_freq(0, target_freq) + + if r: + self.myform['freq'].set_value(target_freq) # update displayed value +# if self.show_debug_info: +# self.myform['baseband'].set_value(r.baseband_freq) +# self.myform['ddc'].set_value(r.dxc_freq) + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.set_baseband_freq(target_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + self.u.set_pga(0,gain) + + def set_decim(self, decim): + ok = self.u.set_decim_rate(decim) + if not ok: + print "set_decim failed" + input_rate = self.u.adc_freq() / self.u.decim_rate() + self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(self.u.decim_rate()) + self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) + return ok + + def _setup_events(self): + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.Bind(wx.EVT_LEFT_DCLICK, self.evt_left_dclick) + + def evt_left_dclick(self, event): + (ux, uy) = self.scope.win.GetXY(event) + if event.CmdDown(): + # Re-center on maximum power + points = self.scope.win._points + if self.scope.win.peak_hold: + if self.scope.win.peak_vals is not None: + ind = numpy.argmax(self.scope.win.peak_vals) + else: + ind = int(points.shape()[0]/2) + else: + ind = numpy.argmax(points[:,1]) + (freq, pwr) = points[ind] + target_freq = freq/self.scope.win._scale_factor + print ind, freq, pwr + self.set_freq(target_freq) + else: + # Re-center on clicked frequency + target_freq = ux/self.scope.win._scale_factor + self.set_freq(target_freq) + + +def main (): + app = stdgui2.stdapp(app_top_block, "USRP FFT", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/gr-msdd6000/src/python-examples/playback_samples.m b/gr-msdd6000/src/python-examples/playback_samples.m new file mode 100644 index 00000000..332296e7 --- /dev/null +++ b/gr-msdd6000/src/python-examples/playback_samples.m @@ -0,0 +1,12 @@ +t = read_complex_binary('msdd.dat',10000000); + +fftsize=256; +w = [0:pi/fftsize:pi]; + +for i = 0:length(t)/fftsize + fftdata = fft(i*fftsize:i*fftsize+fftsize); + clear plot; + plot(w,fftdata); +endfor + +pause; diff --git a/gr-msdd6000/src/python-examples/read_complex_binary.m b/gr-msdd6000/src/python-examples/read_complex_binary.m new file mode 100644 index 00000000..67158b52 --- /dev/null +++ b/gr-msdd6000/src/python-examples/read_complex_binary.m @@ -0,0 +1,48 @@ +% +% Copyright 2001 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. +% + +function v = read_complex_binary (filename, count) + + %% usage: read_complex_binary (filename, [count]) + %% + %% open filename and return the contents as a column vector, + %% treating them as 32 bit complex numbers + %% + + m = nargchk (1,2,nargin); + if (m) + usage (m); + end + + if (nargin < 2) + count = Inf; + end + + f = fopen (filename, 'rb'); + if (f < 0) + v = 0; + else + t = fread (f, [2, count], 'float'); + fclose (f); + v = t(1,:) + t(2,:)*i; + [r, c] = size (v); + v = reshape (v, c, r); + end -- 2.39.5