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