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