d6338f85331293b4eda6656bcfb98a0b0ade8ade
[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_bitrate2 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(options.bitrate, self._demod_class.bits_per_symbol(), \
271                                         adc_rate, self.u.get_decim_rates())
272
273         self.u.set_decim(self._decim)
274         self.set_auto_tr(True)                 # enable Auto Transmit/Receive switching
275
276     def set_freq(self, target_freq):
277         """
278         Set the center frequency we're interested in.
279
280         @param target_freq: frequency in Hz
281         @rypte: bool
282
283         Tuning is a two step process.  First we ask the front-end to
284         tune as close to the desired frequency as it can.  Then we use
285         the result of that operation and our target_frequency to
286         determine the value for the digital up converter.
287         """
288         return self.u.set_center_freq(target_freq)
289
290     def set_gain(self, gain):
291         """
292         Sets the analog gain in the USRP
293         """
294         if gain is None:
295             r = self.u.gain_range()
296             gain = (r[0] + r[1])/2               # set gain to midpoint
297         self._rx_gain = gain
298         ret = self.u.set_gain(self._rx_gain)
299         return ret
300
301     def set_auto_tr(self, enable):
302         return self.u.set_auto_tr(enable)
303
304     def set_decim(self, decim):
305         self._decim = decim
306         self.u.set_decim(self._decim)
307
308         if(self.gui_on):
309             bw_in = self.u.adc_rate() / self._decim
310             self._bitrate = bw_in / self._samples_per_symbol
311             self.snk_rxin.set_frequency_range(0, bw_in)
312             self.snk_rx.set_frequency_range(0, self._bitrate)
313
314     def frequency(self):
315         return self._rx_freq
316
317     def gain(self):
318         return self._rx_gain
319
320     def decim(self):
321         return self._decim
322
323     def rx_gain_clock(self):
324         return self._gain_clock
325
326     def rx_gain_clock_beta(self):
327         return self._gain_clock_beta
328
329     def set_rx_gain_clock(self, gain):
330         self._gain_clock = gain
331         self._gain_clock_beta = .25 * self._gain_clock * self._gain_clock
332         self.rxpath.packet_receiver._demodulator.time_recov.set_alpha(self._gain_clock)
333         self.rxpath.packet_receiver._demodulator.time_recov.set_beta(self._gain_clock_beta)
334
335     def rx_gain_phase(self):
336         return self._gain_phase
337
338     def rx_gain_phase_beta(self):
339         return self._gain_phase_beta
340     
341     def set_rx_gain_phase(self, gain_phase):
342         self._gain_phase = gain_phase
343         self._gain_phase_beta = .25 * self._gain_phase * self._gain_phase
344         self.rxpath.packet_receiver._demodulator.phase_recov.set_alpha(self._gain_phase)
345         self.rxpath.packet_receiver._demodulator.phase_recov.set_beta(self._gain_phase_beta)
346
347
348     def rx_gain_freq(self):
349         return self._gain_freq
350
351     def set_rx_gain_freq(self, gain_freq):
352         self._gain_freq = gain_freq
353         #self._gain_freq_beta = .25 * self._gain_freq * self._gain_freq
354         self.rxpath.packet_receiver._demodulator.freq_recov.set_alpha(self._gain_freq)
355         #self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_fre_beta)
356
357
358     def add_options(normal, expert):
359         """
360         Adds usrp-specific options to the Options Parser
361         """
362         add_freq_option(normal)
363         normal.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
364                           help="select USRP Rx side A or B")
365         normal.add_option("", "--rx-gain", type="eng_float", default=None, metavar="GAIN",
366                           help="set receiver gain in dB [default=midpoint].  See also --show-rx-gain-range")
367         normal.add_option("", "--show-rx-gain-range", action="store_true", default=False, 
368                           help="print min and max Rx gain available on selected daughterboard")
369         normal.add_option("-v", "--verbose", action="store_true", default=False)
370         normal.add_option("-G", "--gui", action="store_true", default=False,
371                           help="Turn on the GUI [default=%default]")
372
373         expert.add_option("", "--rx-freq", type="eng_float", default=None,
374                           help="set Rx frequency to FREQ [default=%default]", metavar="FREQ")
375         expert.add_option("-d", "--decim", type="intx", default=128,
376                           help="set fpga decimation rate to DECIM [default=%default]")
377         expert.add_option("", "--snr", type="eng_float", default=30,
378                           help="set the SNR of the channel in dB [default=%default]")
379    
380
381     # Make a static method to call before instantiation
382     add_options = staticmethod(add_options)
383
384
385 def add_freq_option(parser):
386     """
387     Hackery that has the -f / --freq option set both tx_freq and rx_freq
388     """
389     def freq_callback(option, opt_str, value, parser):
390         parser.values.rx_freq = value
391         parser.values.tx_freq = value
392
393     if not parser.has_option('--freq'):
394         parser.add_option('-f', '--freq', type="eng_float",
395                           action="callback", callback=freq_callback,
396                           help="set Tx and/or Rx frequency to FREQ [default=%default]",
397                           metavar="FREQ")
398
399
400 # /////////////////////////////////////////////////////////////////////////////
401 #                                   main
402 # /////////////////////////////////////////////////////////////////////////////
403
404 global n_rcvd, n_right
405
406 def main():
407     global n_rcvd, n_right, pktno
408
409     n_rcvd = 0
410     n_right = 0
411     pktno = 1
412     
413     def rx_callback(ok, payload):
414         global n_rcvd, n_right, pktno
415         (pktno,) = struct.unpack('!H', payload[0:2])
416         n_rcvd += 1
417         if ok:
418             n_right += 1
419
420         if not options.gui:
421             print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
422                 ok, pktno, n_rcvd, n_right)
423
424
425     demods = modulation_utils.type_1_demods()
426
427     # Create Options Parser:
428     parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
429     expert_grp = parser.add_option_group("Expert")
430
431     parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), 
432                       default='dbpsk',
433                       help="Select modulation from: %s [default=%%default]"
434                             % (', '.join(demods.keys()),))
435
436     my_top_block.add_options(parser, expert_grp)
437     receive_path.add_options(parser, expert_grp)
438     usrp_options.add_rx_options(parser)
439
440     for mod in demods.values():
441         mod.add_options(expert_grp)
442
443     (options, args) = parser.parse_args ()
444
445     if len(args) != 0:
446         parser.print_help(sys.stderr)
447         sys.exit(1)
448
449     if options.rx_freq is None:
450         sys.stderr.write("You must specify -f FREQ or --freq FREQ\n")
451         parser.print_help(sys.stderr)
452         sys.exit(1)
453
454
455     # build the graph
456     tb = my_top_block(demods[options.modulation], rx_callback, options)
457
458     r = gr.enable_realtime_scheduling()
459     if r != gr.RT_OK:
460         print "Warning: Failed to enable realtime scheduling."
461
462     tb.start()        # start flow graph
463
464     if(options.gui):
465         tb.qapp.exec_()
466     else:
467         tb.wait()         # wait for it to finish
468
469 if __name__ == '__main__':
470     try:
471         main()
472     except KeyboardInterrupt:
473         pass