62f9f34a35d6b868991860ed85b389d87edd0dad
[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             self.snk_rxin = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
218                                          -1/2, 1/2,
219                                          "Received", True, True, False, True, True, False)
220             self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
221                                        -1/2, 1/2,
222                                        "Post-Synchronizer", True, True, False, True, True, False)
223
224             self.snk_rxin.set_frequency_axis(-60, 60)
225             self.snk_rx.set_frequency_axis(-60, 20)
226             self.snk_rxin.set_time_domain_axis(-2000,2000)
227             
228             # Connect to the QT sinks
229             # FIXME: make better exposure to receiver from rxpath
230             self.receiver = self.rxpath.packet_receiver._demodulator.receiver
231             self.connect(self.u, self.snk_rxin)
232             self.connect(self.receiver, self.snk_rx)
233             
234             pyRxInQt  = self.snk_rxin.pyqwidget()
235             pyRxIn = sip.wrapinstance(pyRxInQt, QtGui.QWidget)
236             
237             pyRxQt  = self.snk_rx.pyqwidget()
238             pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
239             
240             self.main_box = dialog_box(pyRxIn, pyRx, self)
241             self.main_box.show()
242
243     def _setup_usrp_source(self, options):
244         self.u = usrp_options.create_usrp_source(options)
245         adc_rate = self.u.adc_rate()
246
247         self.u.set_decim(self._decim)
248
249         (self._bitrate, self._samples_per_symbol, self._decim) = \
250                         pick_rx_bitrate(self._bitrate, self._demod_class.bits_per_symbol(), \
251                                         self._samples_per_symbol, self._decim, adc_rate,  \
252                                         self.u.get_decim_rates())
253
254         self.u.set_decim(self._decim)
255         self.set_auto_tr(True)                 # enable Auto Transmit/Receive switching
256
257     def set_freq(self, target_freq):
258         """
259         Set the center frequency we're interested in.
260
261         @param target_freq: frequency in Hz
262         @rypte: bool
263
264         Tuning is a two step process.  First we ask the front-end to
265         tune as close to the desired frequency as it can.  Then we use
266         the result of that operation and our target_frequency to
267         determine the value for the digital up converter.
268         """
269         return self.u.set_center_freq(target_freq)
270
271     def set_gain(self, gain):
272         """
273         Sets the analog gain in the USRP
274         """
275         if gain is None:
276             r = self.u.gain_range()
277             gain = (r[0] + r[1])/2               # set gain to midpoint
278         self._rx_gain = gain
279         return self.u.set_gain(self._rx_gain)
280
281     def set_auto_tr(self, enable):
282         return self.u.set_auto_tr(enable)
283
284     def set_decim(self, decim):
285         self._decim = decim
286         self.u.set_decim(self._decim)
287
288     def frequency(self):
289         return self._rx_freq
290
291     def gain(self):
292         return self._rx_gain
293
294     def decim(self):
295         return self._decim
296
297     def rx_gain_mu(self):
298         return self._gain_mu
299
300     def rx_gain_omega(self):
301         return self.gain_omega
302     
303     def set_rx_gain_mu(self, gain):
304         self._gain_mu = gain
305         self.gain_omega = .25 * self._gain_mu * self._gain_mu
306         self.receiver.set_gain_mu(self._gain_mu)
307         self.receiver.set_gain_omega(self.gain_omega)
308
309     def rx_alpha(self):
310         return self._alpha
311
312     def rx_beta(self):
313         return self.beta
314     
315     def set_rx_alpha(self, alpha):
316         self._alpha = alpha
317         self.beta = .25 * self._alpha * self._alpha
318         self.receiver.set_alpha(self._alpha)
319         self.receiver.set_beta(self.beta)
320
321     def add_options(normal, expert):
322         """
323         Adds usrp-specific options to the Options Parser
324         """
325         add_freq_option(normal)
326         normal.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
327                           help="select USRP Rx side A or B")
328         normal.add_option("", "--rx-gain", type="eng_float", default=None, metavar="GAIN",
329                           help="set receiver gain in dB [default=midpoint].  See also --show-rx-gain-range")
330         normal.add_option("", "--show-rx-gain-range", action="store_true", default=False, 
331                           help="print min and max Rx gain available on selected daughterboard")
332         normal.add_option("-v", "--verbose", action="store_true", default=False)
333         normal.add_option("-G", "--gui", action="store_true", default=False,
334                           help="Turn on the GUI [default=%default]")
335
336         expert.add_option("", "--rx-freq", type="eng_float", default=None,
337                           help="set Rx frequency to FREQ [default=%default]", metavar="FREQ")
338         expert.add_option("-d", "--decim", type="intx", default=128,
339                           help="set fpga decimation rate to DECIM [default=%default]")
340         expert.add_option("", "--snr", type="eng_float", default=30,
341                           help="set the SNR of the channel in dB [default=%default]")
342    
343
344     # Make a static method to call before instantiation
345     add_options = staticmethod(add_options)
346
347
348 def add_freq_option(parser):
349     """
350     Hackery that has the -f / --freq option set both tx_freq and rx_freq
351     """
352     def freq_callback(option, opt_str, value, parser):
353         parser.values.rx_freq = value
354         parser.values.tx_freq = value
355
356     if not parser.has_option('--freq'):
357         parser.add_option('-f', '--freq', type="eng_float",
358                           action="callback", callback=freq_callback,
359                           help="set Tx and/or Rx frequency to FREQ [default=%default]",
360                           metavar="FREQ")
361
362
363 # /////////////////////////////////////////////////////////////////////////////
364 #                                   main
365 # /////////////////////////////////////////////////////////////////////////////
366
367 global n_rcvd, n_right
368
369 def main():
370     global n_rcvd, n_right, pktno
371
372     n_rcvd = 0
373     n_right = 0
374     pktno = 1
375     
376     def rx_callback(ok, payload):
377         global n_rcvd, n_right, pktno
378         (pktno,) = struct.unpack('!H', payload[0:2])
379         n_rcvd += 1
380         if ok:
381             n_right += 1
382
383         if not options.gui:
384             print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
385                 ok, pktno, n_rcvd, n_right)
386
387
388     demods = modulation_utils.type_1_demods()
389
390     # Create Options Parser:
391     parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
392     expert_grp = parser.add_option_group("Expert")
393
394     parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), 
395                       default='dbpsk',
396                       help="Select modulation from: %s [default=%%default]"
397                             % (', '.join(demods.keys()),))
398
399     my_top_block.add_options(parser, expert_grp)
400     receive_path.add_options(parser, expert_grp)
401     usrp_options.add_rx_options(parser)
402
403     for mod in demods.values():
404         mod.add_options(expert_grp)
405
406     (options, args) = parser.parse_args ()
407
408     if len(args) != 0:
409         parser.print_help(sys.stderr)
410         sys.exit(1)
411
412     if options.rx_freq is None:
413         sys.stderr.write("You must specify -f FREQ or --freq FREQ\n")
414         parser.print_help(sys.stderr)
415         sys.exit(1)
416
417
418     # build the graph
419     tb = my_top_block(demods[options.modulation], rx_callback, options)
420
421     r = gr.enable_realtime_scheduling()
422     if r != gr.RT_OK:
423         print "Warning: Failed to enable realtime scheduling."
424
425     tb.start()        # start flow graph
426
427     if(options.gui):
428         tb.qapp.exec_()
429     else:
430         tb.wait()         # wait for it to finish
431
432 if __name__ == '__main__':
433     try:
434         main()
435     except KeyboardInterrupt:
436         pass