Merging trondeau/qtdigital branch r11210-11215 to add some QT GUI's to the digital...
[debian/gnuradio] / gnuradio-examples / python / digital / benchmark_qt_rx.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2005,2006,2007,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, modulation_utils
24 from gnuradio import usrp
25 from gnuradio import eng_notation
26 from gnuradio.eng_option import eng_option
27 from optparse import OptionParser
28
29 import random
30 import struct
31 import sys
32
33 # from current dir
34 from receive_path import receive_path
35
36 try:
37     from gnuradio.qtgui import qtgui
38     from PyQt4 import QtGui, QtCore
39     import sip
40 except ImportError:
41     print "Please install gr-qtgui."
42     sys.exit(1)
43     
44 try:
45     from qt_rx_window import Ui_DigitalWindow
46 except ImportError:
47     print "Error: could not find qt_rx_window.py:"
48     print "\t\"pyuic4 qt_rx_window.ui -o qt_rx_window.py\""
49     sys.exit(1)
50
51 #import os
52 #print os.getpid()
53 #raw_input('Attach and press enter: ')
54
55 # ////////////////////////////////////////////////////////////////////
56 #        Define the QT Interface and Control Dialog
57 # ////////////////////////////////////////////////////////////////////
58
59
60 class dialog_box(QtGui.QMainWindow):
61     def __init__(self, snkRxIn, snkRx, fg, parent=None):
62
63         QtGui.QWidget.__init__(self, parent)
64         self.gui = Ui_DigitalWindow()
65         self.gui.setupUi(self)
66
67         self.fg = fg
68
69         self.set_frequency(self.fg.frequency())
70         self.set_gain(self.fg.gain())
71         self.set_decim(self.fg.decim())
72         self.set_gain_mu(self.fg.rx_gain_mu())
73         self.set_alpha(self.fg.rx_alpha())
74
75         # Add the qtsnk widgets to the hlayout box
76         self.gui.sinkLayout.addWidget(snkRxIn)
77         self.gui.sinkLayout.addWidget(snkRx)
78
79
80         # Connect up some signals
81         self.connect(self.gui.freqEdit, QtCore.SIGNAL("editingFinished()"),
82                      self.freqEditText)
83         self.connect(self.gui.gainEdit, QtCore.SIGNAL("editingFinished()"),
84                      self.gainEditText)
85         self.connect(self.gui.decimEdit, QtCore.SIGNAL("editingFinished()"),
86                      self.decimEditText)
87         self.connect(self.gui.gainMuEdit, QtCore.SIGNAL("editingFinished()"),
88                      self.gainMuEditText)
89         self.connect(self.gui.alphaEdit, QtCore.SIGNAL("editingFinished()"),
90                      self.alphaEditText)
91
92         # Build a timer to update the packet number and PER fields
93         self.update_delay = 250  # time between updating packet rate fields
94         self.pkt_timer = QtCore.QTimer(self)
95         self.connect(self.pkt_timer, QtCore.SIGNAL("timeout()"),
96                      self.updatePacketInfo)
97         self.pkt_timer.start(self.update_delay)
98
99
100     # Accessor functions for Gui to manipulate receiver parameters
101     def set_frequency(self, fo):
102         self.gui.freqEdit.setText(QtCore.QString("%1").arg(fo))
103
104     def set_gain(self, gain):
105         self.gui.gainEdit.setText(QtCore.QString("%1").arg(gain))
106
107     def set_decim(self, decim):
108         self.gui.decimEdit.setText(QtCore.QString("%1").arg(decim))
109
110     def set_gain_mu(self, gain):
111         self.gui.gainMuEdit.setText(QtCore.QString("%1").arg(gain))
112
113     def set_alpha(self, alpha):
114         self.gui.alphaEdit.setText(QtCore.QString("%1").arg(alpha))
115         
116     def freqEditText(self):
117         try:
118             freq = self.gui.freqEdit.text().toDouble()[0]
119             self.fg.set_freq(freq)
120         except RuntimeError:
121             pass
122
123     def gainEditText(self):
124         try:
125             gain = self.gui.gainEdit.text().toDouble()[0]
126             self.fg.set_gain(gain)
127         except RuntimeError:
128             pass
129
130     def decimEditText(self):
131         try:
132             decim = self.gui.decimEdit.text().toInt()[0]
133             self.fg.set_decim(decim)
134         except RuntimeError:
135             pass
136
137     def alphaEditText(self):
138         try:
139             alpha = self.gui.alphaEdit.text().toDouble()[0]
140             self.fg.set_rx_alpha(alpha)
141         except RuntimeError:
142             pass
143
144     def gainMuEditText(self):
145         try:
146             gain = self.gui.gainMuEdit.text().toDouble()[0]
147             self.fg.set_rx_gain_mu(gain)
148         except RuntimeError:
149             pass
150
151
152     # Accessor function for packet error reporting
153     def updatePacketInfo(self):
154         # Pull these globals in from the main thread
155         global n_rcvd, n_right, pktno
156
157         per = float(n_rcvd - n_right)/float(pktno)
158         self.gui.pktsRcvdEdit.setText(QtCore.QString("%1").arg(n_rcvd))
159         self.gui.pktsCorrectEdit.setText(QtCore.QString("%1").arg(n_right))
160         self.gui.perEdit.setText(QtCore.QString("%1").arg(per))
161
162
163
164 # ////////////////////////////////////////////////////////////////////
165 #        Define the GNU Radio Top Block
166 # ////////////////////////////////////////////////////////////////////
167
168
169 class my_top_block(gr.top_block):
170     def __init__(self, demodulator, rx_callback, options):
171         gr.top_block.__init__(self)
172
173         self._rx_freq            = options.rx_freq         # receiver's center frequency
174         self._rx_gain            = options.rx_gain         # receiver's gain
175         self._rx_subdev_spec     = options.rx_subdev_spec  # daughterboard to use
176         self._decim              = options.decim           # Decimating rate for the USRP (prelim)
177         self.gui_on              = options.gui
178                
179         if self._rx_freq is None:
180             sys.stderr.write("-f FREQ or --freq FREQ or --rx-freq FREQ must be specified\n")
181             raise SystemExit
182
183         # Set up USRP source
184         self._setup_usrp_source()
185         ok = self.set_freq(self._rx_freq)
186         if not ok:
187             print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(self._rx_freq))
188             raise ValueError, eng_notation.num_to_str(self._rx_freq)
189         g = self.subdev.gain_range()
190         if options.show_rx_gain_range:
191             print "Rx Gain Range: minimum = %g, maximum = %g, step size = %g" \
192                   % (g[0], g[1], g[2])
193         self.set_gain(options.rx_gain)
194         self.set_auto_tr(True)                 # enable Auto Transmit/Receive switching
195
196         # Set up receive path
197         self.rxpath = receive_path(demodulator, rx_callback, options) 
198
199         # FIXME: do better exposure to lower issues for control
200         self._gain_mu = self.rxpath.packet_receiver._demodulator._mm_gain_mu
201         self._alpha = self.rxpath.packet_receiver._demodulator._costas_alpha
202
203         self.connect(self.u, self.rxpath)
204
205         if self.gui_on:
206             self.qapp = QtGui.QApplication(sys.argv)
207             fftsize = 2048
208             
209             self.snk_rxin = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
210                                          -1/2, 1/2,
211                                          "Received", True, True, False, True, True)
212             self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
213                                        -1/2, 1/2,
214                                        "Post-Synchronizer", True, True, False, True, True)
215
216             self.snk_rxin.set_frequency_axis(-60, 20)
217             self.snk_rx.set_frequency_axis(-60, 20)
218             
219             # Connect to the QT sinks
220             # FIXME: make better exposure to receiver from rxpath
221             self.receiver = self.rxpath.packet_receiver._demodulator.receiver
222             self.connect(self.u, self.snk_rxin)
223             self.connect(self.receiver, self.snk_rx)
224             
225             pyRxInQt  = self.snk_rxin.pyqwidget()
226             pyRxIn = sip.wrapinstance(pyRxInQt, QtGui.QWidget)
227             
228             pyRxQt  = self.snk_rx.pyqwidget()
229             pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
230             
231             self.main_box = dialog_box(pyRxIn, pyRx, self)
232             self.main_box.show()
233
234     def _setup_usrp_source(self):
235         self.u = usrp.source_c ()
236         adc_rate = self.u.adc_rate()
237
238         self.u.set_decim_rate(self._decim)
239
240         # determine the daughterboard subdevice we're using
241         if self._rx_subdev_spec is None:
242             self._rx_subdev_spec = usrp.pick_rx_subdevice(self.u)
243         self.subdev = usrp.selected_subdev(self.u, self._rx_subdev_spec)
244
245         self.u.set_mux(usrp.determine_rx_mux_value(self.u, self._rx_subdev_spec))
246
247     def set_freq(self, target_freq):
248         """
249         Set the center frequency we're interested in.
250
251         @param target_freq: frequency in Hz
252         @rypte: bool
253
254         Tuning is a two step process.  First we ask the front-end to
255         tune as close to the desired frequency as it can.  Then we use
256         the result of that operation and our target_frequency to
257         determine the value for the digital up converter.
258         """
259         r = self.u.tune(0, self.subdev, target_freq)
260         if r:
261             self._rx_freq = target_freq
262             return True
263
264         return False
265
266     def set_gain(self, gain):
267         """
268         Sets the analog gain in the USRP
269         """
270         if gain is None:
271             r = self.subdev.gain_range()
272             gain = (r[0] + r[1])/2               # set gain to midpoint
273         self._rx_gain = gain
274         return self.subdev.set_gain(self._rx_gain)
275
276     def set_auto_tr(self, enable):
277         return self.subdev.set_auto_tr(enable)
278
279     def set_decim(self, decim):
280         self._decim = decim
281         self.u.set_decim_rate(self._decim)
282
283     def frequency(self):
284         return self._rx_freq
285
286     def gain(self):
287         return self._rx_gain
288
289     def decim(self):
290         return self._decim
291
292     def rx_gain_mu(self):
293         return self._gain_mu
294
295     def rx_gain_omega(self):
296         return self.gain_omega
297     
298     def set_rx_gain_mu(self, gain):
299         self._gain_mu = gain
300         self.gain_omega = .25 * self._gain_mu * self._gain_mu
301         self.receiver.set_gain_mu(self._gain_mu)
302         self.receiver.set_gain_omega(self.gain_omega)
303
304     def rx_alpha(self):
305         return self._alpha
306
307     def rx_beta(self):
308         return self.beta
309     
310     def set_rx_alpha(self, alpha):
311         self._alpha = alpha
312         self.beta = .25 * self._alpha * self._alpha
313         self.receiver.set_alpha(self._alpha)
314         self.receiver.set_beta(self.beta)
315
316     def add_options(normal, expert):
317         """
318         Adds usrp-specific options to the Options Parser
319         """
320         add_freq_option(normal)
321         normal.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
322                           help="select USRP Rx side A or B")
323         normal.add_option("", "--rx-gain", type="eng_float", default=None, metavar="GAIN",
324                           help="set receiver gain in dB [default=midpoint].  See also --show-rx-gain-range")
325         normal.add_option("", "--show-rx-gain-range", action="store_true", default=False, 
326                           help="print min and max Rx gain available on selected daughterboard")
327         normal.add_option("-v", "--verbose", action="store_true", default=False)
328         normal.add_option("-G", "--gui", action="store_true", default=False,
329                           help="Turn on the GUI [default=%default]")
330
331         expert.add_option("", "--rx-freq", type="eng_float", default=None,
332                           help="set Rx frequency to FREQ [default=%default]", metavar="FREQ")
333         expert.add_option("-d", "--decim", type="intx", default=128,
334                           help="set fpga decimation rate to DECIM [default=%default]")
335         expert.add_option("", "--snr", type="eng_float", default=30,
336                           help="set the SNR of the channel in dB [default=%default]")
337    
338
339     # Make a static method to call before instantiation
340     add_options = staticmethod(add_options)
341
342
343 def add_freq_option(parser):
344     """
345     Hackery that has the -f / --freq option set both tx_freq and rx_freq
346     """
347     def freq_callback(option, opt_str, value, parser):
348         parser.values.rx_freq = value
349         parser.values.tx_freq = value
350
351     if not parser.has_option('--freq'):
352         parser.add_option('-f', '--freq', type="eng_float",
353                           action="callback", callback=freq_callback,
354                           help="set Tx and/or Rx frequency to FREQ [default=%default]",
355                           metavar="FREQ")
356
357
358 # /////////////////////////////////////////////////////////////////////////////
359 #                                   main
360 # /////////////////////////////////////////////////////////////////////////////
361
362 global n_rcvd, n_right
363
364 def main():
365     global n_rcvd, n_right, pktno
366
367     n_rcvd = 0
368     n_right = 0
369     pktno = 1
370     
371     def rx_callback(ok, payload):
372         global n_rcvd, n_right, pktno
373         (pktno,) = struct.unpack('!H', payload[0:2])
374         n_rcvd += 1
375         if ok:
376             n_right += 1
377
378         if not options.gui:
379             print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
380                 ok, pktno, n_rcvd, n_right)
381
382
383     demods = modulation_utils.type_1_demods()
384
385     # Create Options Parser:
386     parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
387     expert_grp = parser.add_option_group("Expert")
388
389     parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), 
390                       default='dbpsk',
391                       help="Select modulation from: %s [default=%%default]"
392                             % (', '.join(demods.keys()),))
393
394     my_top_block.add_options(parser, expert_grp)
395     receive_path.add_options(parser, expert_grp)
396
397     for mod in demods.values():
398         mod.add_options(expert_grp)
399
400     (options, args) = parser.parse_args ()
401
402     if len(args) != 0:
403         parser.print_help(sys.stderr)
404         sys.exit(1)
405
406     if options.rx_freq is None:
407         sys.stderr.write("You must specify -f FREQ or --freq FREQ\n")
408         parser.print_help(sys.stderr)
409         sys.exit(1)
410
411
412     # build the graph
413     tb = my_top_block(demods[options.modulation], rx_callback, options)
414
415     r = gr.enable_realtime_scheduling()
416     if r != gr.RT_OK:
417         print "Warning: Failed to enable realtime scheduling."
418
419     tb.start()        # start flow graph
420
421     if(options.gui):
422         tb.qapp.exec_()
423     else:
424         tb.wait()         # wait for it to finish
425
426 if __name__ == '__main__':
427     try:
428         main()
429     except KeyboardInterrupt:
430         pass