Imported Upstream version 3.2.2
[debian/gnuradio] / gr-utils / src / python / usrp_oscope.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2004,2005,2006,2007 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 # print "Loading revised usrp_oscope with additional options for scopesink..."
24
25 from gnuradio import gr, gru
26 from gnuradio import usrp
27 from gnuradio import eng_notation
28 from gnuradio.eng_option import eng_option
29 from gnuradio.wxgui import stdgui2, scopesink2, form, slider
30 from optparse import OptionParser
31 import wx
32 import sys
33 from usrpm import usrp_dbid
34
35
36 def pick_subdevice(u):
37     """
38     The user didn't specify a subdevice on the command line.
39     If there's a daughterboard on A, select A.
40     If there's a daughterboard on B, select B.
41     Otherwise, select A.
42     """
43     if u.db(0, 0).dbid() >= 0:       # dbid is < 0 if there's no d'board or a problem
44         return (0, 0)
45     if u.db(0, 0).dbid() >= 0:
46         return (1, 0)
47     return (0, 0)
48
49
50 class app_top_block(stdgui2.std_top_block):
51     def __init__(self, frame, panel, vbox, argv):
52         stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv)
53
54         self.frame = frame
55         self.panel = panel
56         
57         parser = OptionParser(option_class=eng_option)
58         parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
59                           help="select USRP Rx side A or B (default=first one with a daughterboard)")
60         parser.add_option("-d", "--decim", type="int", default=16,
61                           help="set fgpa decimation rate to DECIM [default=%default]")
62         parser.add_option("-f", "--freq", type="eng_float", default=None,
63                           help="set frequency to FREQ", metavar="FREQ")
64         parser.add_option("-g", "--gain", type="eng_float", default=None,
65                           help="set gain in dB (default is midpoint)")
66         parser.add_option("-8", "--width-8", action="store_true", default=False,
67                           help="Enable 8-bit samples across USB")
68         parser.add_option( "--no-hb", action="store_true", default=False,
69                           help="don't use halfband filter in usrp")
70         parser.add_option("-C", "--basic-complex", action="store_true", default=False,
71                           help="Use both inputs of a basicRX or LFRX as a single Complex input channel")
72         parser.add_option("-D", "--basic-dualchan", action="store_true", default=False,
73                           help="Use both inputs of a basicRX or LFRX as seperate Real input channels")
74         parser.add_option("-n", "--frame-decim", type="int", default=1,
75                           help="set oscope frame decimation factor to n [default=1]")
76         parser.add_option("-v", "--v-scale", type="eng_float", default=1000,
77                           help="set oscope initial V/div to SCALE [default=%default]")
78         parser.add_option("-t", "--t-scale", type="eng_float", default=49e-6,
79                           help="set oscope initial s/div to SCALE [default=50us]")
80         (options, args) = parser.parse_args()
81         if len(args) != 0:
82             parser.print_help()
83             sys.exit(1)
84
85         self.show_debug_info = True
86         
87         # build the graph
88         if options.basic_dualchan:
89           self.num_inputs=2
90         else:
91           self.num_inputs=1
92         if options.no_hb or (options.decim<8):
93           #Min decimation of this firmware is 4. 
94           #contains 4 Rx paths without halfbands and 0 tx paths.
95           self.fpga_filename="std_4rx_0tx.rbf"
96           self.u = usrp.source_c(nchan=self.num_inputs,decim_rate=options.decim, fpga_filename=self.fpga_filename)
97         else:
98           #Min decimation of standard firmware is 8. 
99           #standard fpga firmware "std_2rxhb_2tx.rbf" 
100           #contains 2 Rx paths with halfband filters and 2 tx paths (the default)
101           self.u = usrp.source_c(nchan=self.num_inputs,decim_rate=options.decim)
102
103         if options.rx_subdev_spec is None:
104             options.rx_subdev_spec = pick_subdevice(self.u)
105
106         if options.width_8:
107             width = 8
108             shift = 8
109             format = self.u.make_format(width, shift)
110             #print "format =", hex(format)
111             r = self.u.set_format(format)
112             #print "set_format =", r
113             
114         # determine the daughterboard subdevice we're using
115         self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
116         if (options.basic_complex  or options.basic_dualchan ):
117           if ((self.subdev.dbid()==usrp_dbid.BASIC_RX) or (self.subdev.dbid()==usrp_dbid.LF_RX)):
118             side = options.rx_subdev_spec[0]  # side A = 0, side B = 1
119             if options.basic_complex:
120               #force Basic_RX and LF_RX in complex mode (use both I and Q channel)
121               print "Receiver daughterboard forced in complex mode. Both inputs will combined to form a single complex channel."
122               self.dualchan=False
123               if side==0:
124                 self.u.set_mux(0x00000010) #enable adc 0 and 1 to form a single complex input on side A
125               else: #side ==1
126                 self.u.set_mux(0x00000032) #enable adc 3 and 2 to form a single complex input on side B
127             elif options.basic_dualchan:
128               #force Basic_RX and LF_RX in dualchan mode (use input A  for channel 0 and input B for channel 1)
129               print "Receiver daughterboard forced in dualchannel mode. Each input will be used to form a seperate channel."
130               self.dualchan=True
131               if side==0:
132                 self.u.set_mux(gru.hexint(0xf0f0f1f0)) #enable adc 0, side A to form a real input on channel 0 and adc1,side A to form a real input on channel 1
133               else: #side ==1
134                 self.u.set_mux(0xf0f0f3f2) #enable adc 2, side B to form a real input on channel 0 and adc3,side B to form a real input on channel 1 
135           else:
136             sys.stderr.write('options basic_dualchan or basic_complex is only supported for Basic Rx or LFRX at the moment\n')
137             sys.exit(1)
138         else:
139           self.dualchan=False
140           self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
141
142         input_rate = self.u.adc_freq() / self.u.decim_rate()
143
144         self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate,
145                                             frame_decim=options.frame_decim,
146                                             v_scale=options.v_scale,
147                                             t_scale=options.t_scale,
148                                             num_inputs=self.num_inputs)
149         if self.dualchan:
150           # deinterleave two channels from FPGA
151           self.di = gr.deinterleave(gr.sizeof_gr_complex) 
152           self.connect(self.u,self.di) 
153           self.connect((self.di,0),(self.scope,0))
154           self.connect((self.di,1),(self.scope,1))
155         else:
156           self.connect(self.u, self.scope)
157
158         self._build_gui(vbox)
159
160         # set initial values
161
162         if options.gain is None:
163             # if no gain was specified, use the mid-point in dB
164             g = self.subdev.gain_range()
165             options.gain = float(g[0]+g[1])/2
166
167         if options.freq is None:
168             if ((self.subdev.dbid()==usrp_dbid.BASIC_RX) or (self.subdev.dbid()==usrp_dbid.LF_RX)):
169               #for Basic RX and LFRX if no freq is specified you probably want 0.0 Hz and not 45 GHz
170               options.freq=0.0
171             else:
172               # if no freq was specified, use the mid-point
173               r = self.subdev.freq_range()
174               options.freq = float(r[0]+r[1])/2
175
176         self.set_gain(options.gain)
177
178         if self.show_debug_info:
179             self.myform['decim'].set_value(self.u.decim_rate())
180             self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate())
181             self.myform['dbname'].set_value(self.subdev.name())
182             self.myform['baseband'].set_value(0)
183             self.myform['ddc'].set_value(0)
184             if self.num_inputs==2:
185               self.myform['baseband2'].set_value(0)
186               self.myform['ddc2'].set_value(0)              
187                         
188         if not(self.set_freq(options.freq)):
189             self._set_status_msg("Failed to set initial frequency")
190         if self.num_inputs==2:
191           if not(self.set_freq2(options.freq)):
192             self._set_status_msg("Failed to set initial frequency for channel 2")          
193
194
195     def _set_status_msg(self, msg):
196         self.frame.GetStatusBar().SetStatusText(msg, 0)
197
198     def _build_gui(self, vbox):
199
200         def _form_set_freq(kv):
201             return self.set_freq(kv['freq'])
202
203         def _form_set_freq2(kv):
204             return self.set_freq2(kv['freq2'])            
205         vbox.Add(self.scope.win, 10, wx.EXPAND)
206         
207         # add control area at the bottom
208         self.myform = myform = form.form()
209         hbox = wx.BoxSizer(wx.HORIZONTAL)
210         hbox.Add((5,0), 0, 0)
211         myform['freq'] = form.float_field(
212             parent=self.panel, sizer=hbox, label="Center freq", weight=1,
213             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
214         if self.num_inputs==2:
215           myform['freq2'] = form.float_field(
216               parent=self.panel, sizer=hbox, label="Center freq2", weight=1,
217               callback=myform.check_input_and_call(_form_set_freq2, self._set_status_msg))          
218           hbox.Add((5,0), 0, 0)
219         g = self.subdev.gain_range()
220         myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain",
221                                            weight=3,
222                                            min=int(g[0]), max=int(g[1]),
223                                            callback=self.set_gain)
224
225         hbox.Add((5,0), 0, 0)
226         vbox.Add(hbox, 0, wx.EXPAND)
227
228         self._build_subpanel(vbox)
229
230     def _build_subpanel(self, vbox_arg):
231         # build a secondary information panel (sometimes hidden)
232
233         # FIXME figure out how to have this be a subpanel that is always
234         # created, but has its visibility controlled by foo.Show(True/False)
235         
236         def _form_set_decim(kv):
237             return self.set_decim(kv['decim'])
238
239         if not(self.show_debug_info):
240             return
241
242         panel = self.panel
243         vbox = vbox_arg
244         myform = self.myform
245
246         #panel = wx.Panel(self.panel, -1)
247         #vbox = wx.BoxSizer(wx.VERTICAL)
248
249         hbox = wx.BoxSizer(wx.HORIZONTAL)
250         hbox.Add((5,0), 0)
251
252         myform['decim'] = form.int_field(
253             parent=panel, sizer=hbox, label="Decim",
254             callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg))
255
256         hbox.Add((5,0), 1)
257         myform['fs@usb'] = form.static_float_field(
258             parent=panel, sizer=hbox, label="Fs@USB")
259
260         hbox.Add((5,0), 1)
261         myform['dbname'] = form.static_text_field(
262             parent=panel, sizer=hbox)
263
264         hbox.Add((5,0), 1)
265         myform['baseband'] = form.static_float_field(
266             parent=panel, sizer=hbox, label="Analog BB")
267
268         hbox.Add((5,0), 1)
269         myform['ddc'] = form.static_float_field(
270             parent=panel, sizer=hbox, label="DDC")
271         if self.num_inputs==2:
272           hbox.Add((1,0), 1)
273           myform['baseband2'] = form.static_float_field(
274               parent=panel, sizer=hbox, label="BB2")
275           hbox.Add((1,0), 1)
276           myform['ddc2'] = form.static_float_field(
277             parent=panel, sizer=hbox, label="DDC2")          
278
279         hbox.Add((5,0), 0)
280         vbox.Add(hbox, 0, wx.EXPAND)
281
282         
283     def set_freq(self, target_freq):
284         """
285         Set the center frequency we're interested in.
286
287         @param target_freq: frequency in Hz
288         @rypte: bool
289
290         Tuning is a two step process.  First we ask the front-end to
291         tune as close to the desired frequency as it can.  Then we use
292         the result of that operation and our target_frequency to
293         determine the value for the digital down converter.
294         """
295         r = usrp.tune(self.u, 0, self.subdev, target_freq)
296         
297         if r:
298             self.myform['freq'].set_value(target_freq)     # update displayed value
299             if self.show_debug_info:
300                 self.myform['baseband'].set_value(r.baseband_freq)
301                 self.myform['ddc'].set_value(r.dxc_freq)
302             return True
303
304         return False
305
306     def set_freq2(self, target_freq):
307         """
308         Set the center frequency of we're interested in for the second channel.
309
310         @param target_freq: frequency in Hz
311         @rypte: bool
312
313         Tuning is a two step process.  First we ask the front-end to
314         tune as close to the desired frequency as it can.  Then we use
315         the result of that operation and our target_frequency to
316         determine the value for the digital down converter.
317         """
318         r = usrp.tune(self.u, 1, self.subdev, target_freq)
319         
320         if r:
321             self.myform['freq2'].set_value(target_freq)     # update displayed value
322             if self.show_debug_info:
323                 self.myform['baseband2'].set_value(r.baseband_freq)
324                 self.myform['ddc2'].set_value(r.dxc_freq)
325             return True
326
327         return False
328
329     def set_gain(self, gain):
330         self.myform['gain'].set_value(gain)     # update displayed value
331         self.subdev.set_gain(gain)
332
333     def set_decim(self, decim):
334         ok = self.u.set_decim_rate(decim)
335         if not ok:
336             print "set_decim failed"
337         input_rate = self.u.adc_freq() / self.u.decim_rate()
338         self.scope.set_sample_rate(input_rate)
339         if self.show_debug_info:  # update displayed values
340             self.myform['decim'].set_value(self.u.decim_rate())
341             self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate())
342         return ok
343
344 def main ():
345     app = stdgui2.stdapp(app_top_block, "USRP O'scope", nstatus=1)
346     app.MainLoop()
347
348 if __name__ == '__main__':
349     main ()