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