Fixing copyright dates.
[debian/gnuradio] / gnuradio-examples / python / digital / benchmark_qt_rx2.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2005,2006,2007,2009,2010 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_utils2
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._phase_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                                         options.samples_per_symbol, options.decim, \
272                                         adc_rate, 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_freq/10.0)
357         #self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_fre_beta)
358
359
360     def add_options(normal, expert):
361         """
362         Adds usrp-specific options to the Options Parser
363         """
364         add_freq_option(normal)
365         normal.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
366                           help="select USRP Rx side A or B")
367         normal.add_option("", "--rx-gain", type="eng_float", default=None, metavar="GAIN",
368                           help="set receiver gain in dB [default=midpoint].  See also --show-rx-gain-range")
369         normal.add_option("", "--show-rx-gain-range", action="store_true", default=False, 
370                           help="print min and max Rx gain available on selected daughterboard")
371         normal.add_option("-v", "--verbose", action="store_true", default=False)
372         normal.add_option("-G", "--gui", action="store_true", default=False,
373                           help="Turn on the GUI [default=%default]")
374
375         expert.add_option("", "--rx-freq", type="eng_float", default=None,
376                           help="set Rx frequency to FREQ [default=%default]", metavar="FREQ")
377         expert.add_option("-d", "--decim", type="intx", default=128,
378                           help="set fpga decimation rate to DECIM [default=%default]")
379         expert.add_option("", "--snr", type="eng_float", default=30,
380                           help="set the SNR of the channel in dB [default=%default]")
381    
382
383     # Make a static method to call before instantiation
384     add_options = staticmethod(add_options)
385
386
387 def add_freq_option(parser):
388     """
389     Hackery that has the -f / --freq option set both tx_freq and rx_freq
390     """
391     def freq_callback(option, opt_str, value, parser):
392         parser.values.rx_freq = value
393         parser.values.tx_freq = value
394
395     if not parser.has_option('--freq'):
396         parser.add_option('-f', '--freq', type="eng_float",
397                           action="callback", callback=freq_callback,
398                           help="set Tx and/or Rx frequency to FREQ [default=%default]",
399                           metavar="FREQ")
400
401
402 # /////////////////////////////////////////////////////////////////////////////
403 #                                   main
404 # /////////////////////////////////////////////////////////////////////////////
405
406 global n_rcvd, n_right
407
408 def main():
409     global n_rcvd, n_right, pktno
410
411     n_rcvd = 0
412     n_right = 0
413     pktno = 1
414     
415     def rx_callback(ok, payload):
416         global n_rcvd, n_right, pktno
417         (pktno,) = struct.unpack('!H', payload[0:2])
418         n_rcvd += 1
419         if ok:
420             n_right += 1
421
422         if not options.gui:
423             print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
424                 ok, pktno, n_rcvd, n_right)
425
426
427     demods = modulation_utils2.type_1_demods()
428
429     # Create Options Parser:
430     parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
431     expert_grp = parser.add_option_group("Expert")
432
433     parser.add_option("-m", "--modulation", type="choice", choices=demods.keys(), 
434                       default='dbpsk2',
435                       help="Select modulation from: %s [default=%%default]"
436                             % (', '.join(demods.keys()),))
437
438     my_top_block.add_options(parser, expert_grp)
439     receive_path.add_options(parser, expert_grp)
440     usrp_options.add_rx_options(parser)
441
442     for mod in demods.values():
443         mod.add_options(expert_grp)
444
445     (options, args) = parser.parse_args ()
446
447     if len(args) != 0:
448         parser.print_help(sys.stderr)
449         sys.exit(1)
450
451     if options.rx_freq is None:
452         sys.stderr.write("You must specify -f FREQ or --freq FREQ\n")
453         parser.print_help(sys.stderr)
454         sys.exit(1)
455
456
457     # build the graph
458     tb = my_top_block(demods[options.modulation], rx_callback, options)
459
460     r = gr.enable_realtime_scheduling()
461     if r != gr.RT_OK:
462         print "Warning: Failed to enable realtime scheduling."
463
464     tb.start()        # start flow graph
465
466     if(options.gui):
467         tb.qapp.exec_()
468     else:
469         tb.wait()         # wait for it to finish
470
471 if __name__ == '__main__':
472     try:
473         main()
474     except KeyboardInterrupt:
475         pass