Imported Upstream version 3.2.2
[debian/gnuradio] / gr-gpio / src / python / gpio_usrp_fft.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2004,2005,2007,2008,2009 Free Software Foundation, Inc.
4
5 # This file is part of GNU Radio
6
7 # GNU Radio is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3, or (at your option)
10 # any later version.
11
12 # GNU Radio is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with GNU Radio; see the file COPYING.  If not, write to
19 # the Free Software Foundation, Inc., 51 Franklin Street,
20 # Boston, MA 02110-1301, USA.
21
22
23 from gnuradio import gr, gru
24 from gnuradio import usrp
25 from gnuradio import eng_notation
26 from gnuradio.eng_option import eng_option
27 from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, form, slider
28 from optparse import OptionParser
29 import wx
30 import sys
31 import numpy
32
33 from gnuradio import gpio
34
35 def pick_subdevice(u):
36     """
37     The user didn't specify a subdevice on the command line.
38     If there's a daughterboard on A, select A.
39     If there's a daughterboard on B, select B.
40     Otherwise, select A.
41     """
42     if u.db[0][0].dbid() >= 0:       # dbid is < 0 if there's no d'board or a problem
43         return (0, 0)
44     #if u.db[1][0].dbid() >= 0:  #disable the use of RXB
45     #    return (1, 0)
46     return (0, 0)
47
48
49 class app_top_block(stdgui2.std_top_block):
50     def __init__(self, frame, panel, vbox, argv):
51         stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv)
52
53         self.frame = frame
54         self.panel = panel
55         
56         parser = OptionParser(option_class=eng_option)
57         parser.add_option("-w", "--which", type="int", default=0,
58                           help="select which USRP (0, 1, ...) default is %default",
59                           metavar="NUM")
60 #        parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
61 #                          help="select USRP Rx side A or B (default=first one with a daughterboard)")
62         parser.add_option("-A", "--antenna", default=None,
63                           help="select Rx Antenna (only on RFX-series boards)")
64         parser.add_option("-d", "--decim", type="int", default=32,
65                           help="set fgpa decimation rate to DECIM [default=%default]")
66         parser.add_option("-f", "--freq", type="eng_float", default=0.0,
67                           help="set frequency to FREQ", metavar="FREQ")
68         parser.add_option("-g", "--gain", type="eng_float", default=None,
69                           help="set gain in dB (default is midpoint)")
70         parser.add_option("-W", "--waterfall", action="store_true", default=False,
71                           help="Enable waterfall display")
72         parser.add_option("-8", "--width-8", action="store_true", default=False,
73                           help="Enable 8-bit samples across USB")
74 #        parser.add_option( "--no-hb", action="store_true", default=False,
75 #                          help="don't use halfband filter in usrp")
76         parser.add_option("-S", "--oscilloscope", action="store_true", default=False,
77                           help="Enable oscilloscope display (default)")
78         parser.add_option("-F", "--fft", action="store_true", default=False,
79                           help="Enable FFT display")
80         parser.add_option("-n", "--frame-decim", type="int", default=1,
81                           help="set oscope frame decimation factor to n [default=1]")
82         parser.add_option("-v", "--v-scale", type="eng_float", default=1,
83                           help="set oscope initial V/div to SCALE [default=%default]")
84         parser.add_option("-t", "--t-scale", type="eng_float", default=10e-6,
85                           help="set oscope initial s/div to SCALE [default=10us]")
86         parser.add_option ("--digital", action="store_true", default=False, 
87                        help="show (only) the digital wave on lsb (will be input from gpio pins with special usrp firmware)")
88         parser.add_option ("--analog", action="store_true", default=False, 
89                        help="show (only) the analog wave on msbs (will be input from analog inputs)")
90         parser.add_option ("--file",  default=None, 
91                        help="input from file FILE in stead of USRP (will be input from raw file in interleaved short format)")
92         (options, args) = parser.parse_args()
93         if len(args) != 0:
94             parser.print_help()
95             sys.exit(1)
96         self.options = options
97         self.show_debug_info = True
98
99         self.u = usrp.source_s(which=options.which, decim_rate=options.decim, fpga_filename=gpio.fpga_filename)
100
101         print "Warning: This script only supports boards on RXA, change the script if you want otherwise"
102         #options.rx_subdev_spec=(0, 0)#force the use of RXA 
103         options.rx_subdev_spec=None   #force the use of RXA      
104
105         if options.rx_subdev_spec is None:
106             options.rx_subdev_spec = pick_subdevice(self.u)
107
108         #This hardcoded mux setting is why this script only supports RXA
109         #We want both I and Q active, even when using basicRX
110         #set to 0x10 for RXA
111         #set to 0x32 for RXB
112         self.u.set_mux(0x10) #usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
113
114         if options.width_8:
115             width = 8
116             shift = 8
117             format = self.u.make_format(width, shift)
118             print "format =", hex(format)
119             r = self.u.set_format(format)
120             print "set_format =", r
121             
122         # determine the daughterboard subdevice we're using
123         self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
124         #if options.rx_subdev_spec==(0,0):
125         #  rx_subdev_spec2=(0,1)
126         #  self.subdev2 = usrp.selected_subdev(self.u, rx_subdev_spec2)
127         input_rate = self.u.adc_freq() / self.u.decim_rate()
128
129         if options.waterfall:
130             self.scope = \
131               waterfallsink2.waterfall_sink_c (panel, fft_size=1024, sample_rate=input_rate)
132         elif options.fft:
133             self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, sample_rate=input_rate)
134         else: # options.oscilloscope:
135             #self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate)
136             self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate,
137                                             frame_decim=options.frame_decim,
138                                             v_scale=options.v_scale,
139                                             t_scale=options.t_scale)
140
141         self.is2c = gr.interleaved_short_to_complex()
142         if not (options.file is None):
143           self.filesrc=gr.file_source(gr.sizeof_short, options.file, True)
144           thr = gr.throttle(gr.sizeof_short, input_rate)
145           self.connect(self.filesrc,thr,self.is2c,self.scope)
146         elif options.digital:
147           self.select_dig=gr.and_const_ss(0x0001)
148           self.connect(self.u, self.select_dig,self.is2c,self.scope)
149         elif options.analog:
150           self.select_ana=gr.and_const_ss(0xFFFE)
151           self.connect(self.u, self.select_ana,self.is2c,self.scope)
152         else:
153           self.connect(self.u,self.is2c,self.scope)
154
155         self._build_gui(vbox)
156         self._setup_events()
157         
158         # set initial values
159
160         if options.gain is None:
161             # if no gain was specified, use the mid-point in dB
162             g = self.subdev.gain_range()
163             options.gain = float(g[0]+g[1])/2
164
165         if options.freq is None:
166             # if no freq was specified, use the mid-point
167             r = self.subdev.freq_range()
168             options.freq = float(r[0]+r[1])/2
169
170         self.set_gain(options.gain)
171
172         if options.antenna is not None:
173             print "Selecting antenna %s" % (options.antenna,)
174             self.subdev.select_rx_antenna(options.antenna)
175
176         if self.show_debug_info:
177             self.myform['decim'].set_value(self.u.decim_rate())
178             self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate())
179             self.myform['dbname'].set_value(self.subdev.name())
180             self.myform['baseband'].set_value(0)
181             self.myform['ddc'].set_value(0)
182
183         if not(self.set_freq(options.freq)):
184             self._set_status_msg("Failed to set initial frequency")
185
186     def _set_status_msg(self, msg):
187         self.frame.GetStatusBar().SetStatusText(msg, 0)
188
189     def _build_gui(self, vbox):
190
191         def _form_set_freq(kv):
192             return self.set_freq(kv['freq'])
193             
194         vbox.Add(self.scope.win, 10, wx.EXPAND)
195         
196         # add control area at the bottom
197         self.myform = myform = form.form()
198         hbox = wx.BoxSizer(wx.HORIZONTAL)
199         hbox.Add((5,0), 0, 0)
200         myform['freq'] = form.float_field(
201             parent=self.panel, sizer=hbox, label="Center freq", weight=1,
202             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
203
204         hbox.Add((5,0), 0, 0)
205         g = self.subdev.gain_range()
206         myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain",
207                                            weight=3,
208                                            min=int(g[0]), max=int(g[1]),
209                                            callback=self.set_gain)
210
211         hbox.Add((5,0), 0, 0)
212         vbox.Add(hbox, 0, wx.EXPAND)
213
214         self._build_subpanel(vbox)
215
216     def _build_subpanel(self, vbox_arg):
217         # build a secondary information panel (sometimes hidden)
218
219         # FIXME figure out how to have this be a subpanel that is always
220         # created, but has its visibility controlled by foo.Show(True/False)
221         
222         def _form_set_decim(kv):
223             return self.set_decim(kv['decim'])
224
225         if not(self.show_debug_info):
226             return
227
228         panel = self.panel
229         vbox = vbox_arg
230         myform = self.myform
231
232         #panel = wx.Panel(self.panel, -1)
233         #vbox = wx.BoxSizer(wx.VERTICAL)
234
235         hbox = wx.BoxSizer(wx.HORIZONTAL)
236         hbox.Add((5,0), 0)
237
238         myform['decim'] = form.int_field(
239             parent=panel, sizer=hbox, label="Decim",
240             callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg))
241
242         hbox.Add((5,0), 1)
243         myform['fs@usb'] = form.static_float_field(
244             parent=panel, sizer=hbox, label="Fs@USB")
245
246         hbox.Add((5,0), 1)
247         myform['dbname'] = form.static_text_field(
248             parent=panel, sizer=hbox)
249
250         hbox.Add((5,0), 1)
251         myform['baseband'] = form.static_float_field(
252             parent=panel, sizer=hbox, label="Analog BB")
253
254         hbox.Add((5,0), 1)
255         myform['ddc'] = form.static_float_field(
256             parent=panel, sizer=hbox, label="DDC")
257
258         hbox.Add((5,0), 0)
259         vbox.Add(hbox, 0, wx.EXPAND)
260
261         
262     def set_freq(self, target_freq):
263         """
264         Set the center frequency we're interested in.
265
266         @param target_freq: frequency in Hz
267         @rypte: bool
268
269         Tuning is a two step process.  First we ask the front-end to
270         tune as close to the desired frequency as it can.  Then we use
271         the result of that operation and our target_frequency to
272         determine the value for the digital down converter.
273         """
274         r = self.u.tune(0, self.subdev, target_freq)
275         
276         if r:
277             self.myform['freq'].set_value(target_freq)     # update displayed value
278             if self.show_debug_info:
279                 self.myform['baseband'].set_value(r.baseband_freq)
280                 self.myform['ddc'].set_value(r.dxc_freq)
281             if not self.options.waterfall and not self.options.oscilloscope:
282                 self.scope.win.set_baseband_freq(target_freq)
283             return True
284
285         return False
286
287     def set_gain(self, gain):
288         self.myform['gain'].set_value(gain)     # update displayed value
289         self.subdev.set_gain(gain)
290
291     def set_decim(self, decim):
292         ok = self.u.set_decim_rate(decim)
293         if not ok:
294             print "set_decim failed"
295         input_rate = self.u.adc_freq() / self.u.decim_rate()
296         self.scope.set_sample_rate(input_rate)
297         if self.show_debug_info:  # update displayed values
298             self.myform['decim'].set_value(self.u.decim_rate())
299             self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate())
300         return ok
301
302     def _setup_events(self):
303         if not self.options.waterfall and not self.options.oscilloscope:
304             self.scope.win.Bind(wx.EVT_LEFT_DCLICK, self.evt_left_dclick)
305             
306     def evt_left_dclick(self, event):
307         (ux, uy) = self.scope.win.GetXY(event)
308         if event.CmdDown():
309             # Re-center on maximum power
310             points = self.scope.win._points
311             if self.scope.win.peak_hold:
312                 if self.scope.win.peak_vals is not None:
313                     ind = numpy.argmax(self.scope.win.peak_vals)
314                 else:
315                     ind = int(points.shape()[0]/2)
316             else:
317                 ind = numpy.argmax(points[:,1])
318             (freq, pwr) = points[ind]
319             target_freq = freq/self.scope.win._scale_factor
320             print ind, freq, pwr
321             self.set_freq(target_freq)            
322         else:
323             # Re-center on clicked frequency
324             target_freq = ux/self.scope.win._scale_factor
325             self.set_freq(target_freq)
326             
327         
328 def main ():
329     app = stdgui2.stdapp(app_top_block, "USRP FFT", nstatus=1)
330     app.MainLoop()
331
332 if __name__ == '__main__':
333     main ()