Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-examples / python / multi_usrp / multi_usrp_oscope.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2004,2005,2006 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 2, 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 stdgui, fftsink, waterfallsink, scopesink, form, slider
30 from optparse import OptionParser
31 import wx
32 import sys
33
34 import time
35 from gnuradio import usrp_multi
36
37
38 def pick_subdevice(u):
39     """
40     The user didn't specify a subdevice on the command line.
41     If there's a daughterboard on A, select A.
42     If there's a daughterboard on B, select B.
43     Otherwise, select A.
44     """
45     if u.db[0][0].dbid() >= 0:       # dbid is < 0 if there's no d'board or a problem
46         return (0, 0)
47     if u.db[1][0].dbid() >= 0:
48         return (1, 0)
49     return (0, 0)
50
51
52 class app_flow_graph(stdgui.gui_flow_graph):
53     def __init__(self, frame, panel, vbox, argv):
54         stdgui.gui_flow_graph.__init__(self)
55
56         self.frame = frame
57         self.panel = panel
58         
59         parser = OptionParser(option_class=eng_option)
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("-d", "--decim", type="int", default=128,
63                           help="set fgpa decimation rate to DECIM [default=%default]")
64         parser.add_option("-f", "--freq", type="eng_float", default=None,
65                           help="set frequency to FREQ", metavar="FREQ")
66         parser.add_option("-g", "--gain", type="eng_float", default=None,
67                           help="set gain in dB (default is midpoint)")
68         #align interval is default not yet enabled in gr.align_on_samplenumbers_ss
69         #parser.add_option("-a", "--align_interval", type="int", default=-1,
70         #                  help="Align master and slave every so much samples.")
71
72         # width 8 does not work yet with multi_usrp because it interferes with the 32 bit samplecounter
73         #parser.add_option("-8", "--width-8", action="store_true", default=False,
74         #                  help="Enable 8-bit samples across USB")
75         parser.add_option("-m", "--mux", type="intx", default=None,
76                           help="set fpga FR_RX_MUX register to MUX")
77         parser.add_option("-n", "--frame-decim", type="int", default=1,
78                           help="set oscope frame decimation factor to n [default=1]")
79         parser.add_option("-N", "--nchan", type="int", default=2,
80                           help="set nchannels to NCHAN")
81         parser.add_option("-q", "--show-q", action="store_true", default=False,
82                           help="show the q value of the complex samples")
83         parser.add_option("-s", "--show-counters", action="store_true", default=False,
84                           help="show the counters")
85         parser.add_option("-v", "--v-scale", type="eng_float", default=1000,
86                           help="set oscope initial V/div to SCALE [default=%default]")
87         parser.add_option("-t", "--t-scale", type="eng_float", default=49e-6,
88                           help="set oscope initial s/div to SCALE [default=50us]")
89         parser.add_option("-x", "--master-serialno", type="string", default=None, 
90                           help="Serial_no of the usrp which should be the MASTER (default= select any)")
91         (options, args) = parser.parse_args()
92         if len(args) != 0:
93             parser.print_help()
94             sys.exit(1)
95
96         self.show_debug_info = True
97         
98         # build the graph
99
100         #self.u = usrp.source_c(which=options.which_usrp,decim_rate=options.decim)
101         if (options.mux is None) | (4==options.nchan):
102            init_mux=None #use default mux which is 0x10321032
103         else:
104            init_mux=options.mux
105
106         init_gain=0.0
107         init_freq=0.0
108         init_align_interval=-1
109
110         self.multi=usrp_multi.multi_source_align( self,   options.master_serialno, options.decim,
111                                                  options.nchan, init_gain, init_freq, init_mux, init_align_interval)
112         self.um=self.multi.get_master_usrp()
113         self.us=self.multi.get_slave_usrp()
114
115         if options.rx_subdev_spec is None:
116             options.rx_subdev_spec = pick_subdevice(self.um)
117         if (options.mux==None) and (options.nchan!=4):
118           mux=usrp.determine_rx_mux_value(self.um, options.rx_subdev_spec)
119           mux= (mux<<8 & 0xffffffff) | (mux & 0xff)
120           self.um.set_mux(mux)
121           self.us.set_mux(mux)
122
123         # width 8 does not work yet with multi_usrp because it interferes with the 32 bit samplecounter
124         #if options.width_8:
125         #    width = 8
126         #    shift = 8
127         #    format = self.um.make_format(width, shift)
128         #    r = self.um.set_format(format)
129         #    r = self.us.set_format(format)
130             
131         # determine the daughterboard subdevice of the first channel we're using
132         self.subdevm = usrp.selected_subdev(self.um, options.rx_subdev_spec)
133         self.subdevs = usrp.selected_subdev(self.us, options.rx_subdev_spec)
134
135         input_rate = self.um.adc_freq() / self.um.decim_rate()
136
137         self.scope = scopesink.scope_sink_f(self, panel, sample_rate=input_rate,
138                                             frame_decim=options.frame_decim,
139                                             v_scale=options.v_scale,
140                                             t_scale=options.t_scale)
141         self.sink_count=0
142         self.add_to_scope((self.multi.get_master_source_c(),1),options.show_q)
143         self.add_to_scope((self.multi.get_slave_source_c(),1),options.show_q)
144         if 4==options.nchan:
145           self.add_to_scope((self.multi.get_master_source_c(),2),options.show_q)
146           self.add_to_scope((self.multi.get_slave_source_c(),2),options.show_q)
147
148         if options.show_counters:
149           self.add_to_scope((self.multi.get_master_source_c(),0),options.show_q)
150           self.add_to_scope((self.multi.get_slave_source_c(),0),options.show_q)
151
152         self._build_gui(vbox)
153
154         # set initial values
155
156         if options.gain is None:
157             # if no gain was specified, use the mid-point in dB
158             g = self.subdevm.gain_range()
159             options.gain = float(g[0]+g[1])/2
160
161         if options.freq is None:
162             # if no freq was specified, use the mid-point
163             r = self.subdevm.freq_range()
164             options.freq = float(r[0]+r[1])/2
165
166         self.set_gain(options.gain)
167
168         if self.show_debug_info:
169             self.myform['decim'].set_value(self.um.decim_rate())
170             self.myform['fs@usb'].set_value(self.um.adc_freq() / self.um.decim_rate())
171             self.myform['dbname'].set_value(self.subdevm.name())
172             self.myform['baseband'].set_value(0)
173             self.myform['ddc'].set_value(0)
174                         
175         if not(self.set_freq(options.freq)):
176             self._set_status_msg("Failed to set initial frequency")
177
178         self.multi.print_db_info()
179         self.unsynced=True
180         frame.Bind(wx.EVT_IDLE, self.onIdle)
181
182     def add_to_scope(self,source_c,show_q):
183         c2f= gr.complex_to_float ()
184         self.connect(source_c, c2f)
185         self.connect((c2f,0), (self.scope,self.sink_count))
186         self.sink_count=self.sink_count+1
187         if show_q:
188           self.connect((c2f,1), (self.scope,self.sink_count))
189           self.sink_count=self.sink_count+1
190         
191         
192     def _set_status_msg(self, msg):
193         self.frame.GetStatusBar().SetStatusText(msg, 0)
194
195     def _build_gui(self, vbox):
196
197         def _form_set_freq(kv):
198             return self.set_freq(kv['freq'])
199             
200         vbox.Add(self.scope.win, 10, wx.EXPAND)
201         
202         # add control area at the bottom
203         self.myform = myform = form.form()
204         hbox = wx.BoxSizer(wx.HORIZONTAL)
205         hbox.Add((5,0), 0, 0)
206         myform['freq'] = form.float_field(
207             parent=self.panel, sizer=hbox, label="Center freq", weight=1,
208             callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
209
210         hbox.Add((5,0), 0, 0)
211         g = self.subdevm.gain_range()
212         myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain",
213                                            weight=3,
214                                            min=int(g[0]), max=int(g[1]),
215                                            callback=self.set_gain)
216
217         hbox.Add((5,0), 0, 0)
218         buttonSync = form.button_with_callback(parent=self.panel, label='sync',callback=self.sync_usrps)
219         hbox.Add(buttonSync,0,wx.EXPAND)
220
221         hbox.Add((5,0), 0, 0)
222         vbox.Add(hbox, 0, wx.EXPAND)
223
224         self._build_subpanel(vbox)
225
226     def _build_subpanel(self, vbox_arg):
227         # build a secondary information panel (sometimes hidden)
228
229         # FIXME figure out how to have this be a subpanel that is always
230         # created, but has its visibility controlled by foo.Show(True/False)
231         
232         if not(self.show_debug_info):
233             return
234
235         panel = self.panel
236         vbox = vbox_arg
237         myform = self.myform
238
239         #panel = wx.Panel(self.panel, -1)
240         #vbox = wx.BoxSizer(wx.VERTICAL)
241
242         hbox = wx.BoxSizer(wx.HORIZONTAL)
243         hbox.Add((5,0), 0)
244         myform['decim'] = form.static_float_field(
245             parent=panel, sizer=hbox, label="Decim")
246
247         hbox.Add((5,0), 1)
248         myform['fs@usb'] = form.static_float_field(
249             parent=panel, sizer=hbox, label="Fs@USB")
250
251         hbox.Add((5,0), 1)
252         myform['dbname'] = form.static_text_field(
253             parent=panel, sizer=hbox)
254
255         hbox.Add((5,0), 1)
256         myform['baseband'] = form.static_float_field(
257             parent=panel, sizer=hbox, label="Analog BB")
258
259         hbox.Add((5,0), 1)
260         myform['ddc'] = form.static_float_field(
261             parent=panel, sizer=hbox, label="DDC")
262
263         hbox.Add((5,0), 0)
264         vbox.Add(hbox, 0, wx.EXPAND)
265
266
267     def set_freq(self, target_freq, sync=True):
268         """
269         Set the center frequency we're interested in for all channels,
270         on all rx daughterboards on MASTER and SLAVE.
271
272         @param target_freq: frequency in Hz
273         @param sync: sync the usrps after setting the freqs (this will clear any phase differences in the DDCS)
274         @rypte: bool
275
276         Tuning is a two step process.  First we ask the front-end to
277         tune as close to the desired frequency as it can.  Then we use
278         the result of that operation and our target_frequency to
279         determine the value for the digital down converter.
280         """
281         result,r1,r2,r3,r4 = self.multi.tune_all_rx(target_freq)
282         if sync:
283           self.sync_usrps() #sync master and slave and clear any DDC phase differences
284         if r1:
285             self.myform['freq'].set_value(target_freq)     # update displayed value
286             if self.show_debug_info:
287                 self.myform['baseband'].set_value(r1.baseband_freq)
288                 self.myform['ddc'].set_value(r1.dxc_freq)
289         return result
290
291     def set_freq_chan0(self, target_freq, sync=True):
292         """
293         Set the center frequency we're interested in for rx chan 0 only on MASTER and SLAVE.
294
295         @param target_freq: frequency in Hz
296         @param sync: sync the usrps after setting the freqs (this will clear any phase differences in the DDCS)
297         @rypte: bool
298
299         Tuning is a two step process.  First we ask the front-end to
300         tune as close to the desired frequency as it can.  Then we use
301         the result of that operation and our target_frequency to
302         determine the value for the digital down converter.
303         """
304         rm = usrp.tune(self.um, 0, self.subdevm, target_freq)
305         rs = usrp.tune(self.us, 0, self.subdevs, target_freq)
306         r=rm
307         if sync:
308           self.sync_usrps()  #sync master and slave and clear any DDC phase differences
309         if r:
310             self.myform['freq'].set_value(target_freq)     # update displayed value
311             if self.show_debug_info:
312                 self.myform['baseband'].set_value(r.baseband_freq)
313                 self.myform['ddc'].set_value(r.dxc_freq)
314             return True
315
316         return False
317
318     def set_gain(self, gain):
319         self.myform['gain'].set_value(gain)     # update displayed value
320         self.multi.set_gain_all_rx(gain)
321
322     def set_gain_chan0(self, gain):
323         self.myform['gain'].set_value(gain)     # update displayed value
324         self.subdevm.set_gain(gain)
325         self.subdevs.set_gain(gain)
326
327     def onIdle(self,evt):
328         if self.unsynced:
329           time.sleep(0.5)
330           self.unsynced=True
331           self.multi.sync()
332           self.unsynced=False
333           #print 'synced'
334
335     def sync_usrps(self):
336         self.multi.sync()
337
338 def main ():
339     app = stdgui.stdapp(app_flow_graph, "MULTI_USRP O'scope", nstatus=1)
340     app.MainLoop()
341
342 if __name__ == '__main__':
343     main ()