Preventing an error message by casting an integer (0) to the requested float.
[debian/gnuradio] / gnuradio-examples / python / digital / benchmark_qt_loopback2.py
1 #!/usr/bin/env python
2
3 from gnuradio import gr, gru, modulation_utils
4 from gnuradio import eng_notation
5 from gnuradio.eng_option import eng_option
6 from optparse import OptionParser
7 import random, time, struct, sys, os, math
8
9 from threading import Thread
10
11 # from current dir
12 from transmit_path import transmit_path
13 from receive_path import receive_path
14
15 try:
16     from gnuradio.qtgui import qtgui
17     from PyQt4 import QtGui, QtCore
18     import sip
19 except ImportError:
20     print "Please install gr-qtgui."
21     sys.exit(1)
22     
23 try:
24     from qt_digital_window2 import Ui_DigitalWindow
25 except ImportError:
26     print "Error: could not find qt_digital_window2.py:"
27     print "\t\"pyuic4 qt_digital_window2.ui -o qt_digital_window2.py\""
28     sys.exit(1)
29
30
31 #print os.getpid()
32 #raw_input()
33
34
35 # ////////////////////////////////////////////////////////////////////
36 #        Define the QT Interface and Control Dialog
37 # ////////////////////////////////////////////////////////////////////
38
39
40 class dialog_box(QtGui.QMainWindow):
41     def __init__(self, snkTx, snkRx, fg, parent=None):
42
43         QtGui.QWidget.__init__(self, parent)
44         self.gui = Ui_DigitalWindow()
45         self.gui.setupUi(self)
46
47         self.fg = fg
48
49         self.set_sample_rate(self.fg.sample_rate())
50
51         self.set_snr(self.fg.snr())
52         self.set_frequency(self.fg.frequency_offset())
53         self.set_time_offset(self.fg.timing_offset())
54
55         self.set_gain_clock(self.fg.rx_gain_clock())
56         self.set_gain_phase(self.fg.rx_gain_phase())
57         self.set_gain_freq(self.fg.rx_gain_freq())
58
59         # Add the qtsnk widgets to the hlayout box
60         self.gui.sinkLayout.addWidget(snkTx)
61         self.gui.sinkLayout.addWidget(snkRx)
62
63
64         # Connect up some signals
65         self.connect(self.gui.pauseButton, QtCore.SIGNAL("clicked()"),
66                      self.pauseFg)
67
68         self.connect(self.gui.sampleRateEdit, QtCore.SIGNAL("editingFinished()"),
69                      self.sampleRateEditText)
70
71         self.connect(self.gui.snrEdit, QtCore.SIGNAL("editingFinished()"),
72                      self.snrEditText)
73         self.connect(self.gui.freqEdit, QtCore.SIGNAL("editingFinished()"),
74                      self.freqEditText)
75         self.connect(self.gui.timeEdit, QtCore.SIGNAL("editingFinished()"),
76                      self.timeEditText)
77
78         self.connect(self.gui.gainClockEdit, QtCore.SIGNAL("editingFinished()"),
79                      self.gainClockEditText)
80         self.connect(self.gui.gainPhaseEdit, QtCore.SIGNAL("editingFinished()"),
81                      self.gainPhaseEditText)
82         self.connect(self.gui.gainFreqEdit, QtCore.SIGNAL("editingFinished()"),
83                      self.gainFreqEditText)
84
85         # Build a timer to update the packet number and PER fields
86         self.update_delay = 250  # time between updating packet rate fields
87         self.pkt_timer = QtCore.QTimer(self)
88         self.connect(self.pkt_timer, QtCore.SIGNAL("timeout()"),
89                      self.updatePacketInfo)
90         self.pkt_timer.start(self.update_delay)
91
92     def pauseFg(self):
93         if(self.gui.pauseButton.text() == "Pause"):
94             self.fg.stop()
95             self.fg.wait()
96             self.gui.pauseButton.setText("Unpause")
97         else:
98             self.fg.start()
99             self.gui.pauseButton.setText("Pause")
100
101     # Accessor functions for Gui to manipulate system parameters
102     def set_sample_rate(self, sr):
103         ssr = eng_notation.num_to_str(sr)
104         self.gui.sampleRateEdit.setText(QtCore.QString("%1").arg(ssr))
105
106     def sampleRateEditText(self):
107         try:
108             rate = self.gui.sampleRateEdit.text().toAscii()
109             srate = eng_notation.str_to_num(rate)
110             #self.fg.set_sample_rate(srate)
111         except RuntimeError:
112             pass
113
114
115     # Accessor functions for Gui to manipulate channel model
116     def set_snr(self, snr):
117         self.gui.snrEdit.setText(QtCore.QString("%1").arg(snr))
118
119     def set_frequency(self, fo):
120         self.gui.freqEdit.setText(QtCore.QString("%1").arg(fo))
121
122     def set_time_offset(self, to):
123         self.gui.timeEdit.setText(QtCore.QString("%1").arg(to))
124
125     def snrEditText(self):
126         try:
127             snr = self.gui.snrEdit.text().toDouble()[0]
128             self.fg.set_snr(snr)
129         except RuntimeError:
130             pass
131
132     def freqEditText(self):
133         try:
134             freq = self.gui.freqEdit.text().toDouble()[0]
135             self.fg.set_frequency_offset(freq)
136         except RuntimeError:
137             pass
138
139     def timeEditText(self):
140         try:
141             to = self.gui.timeEdit.text().toDouble()[0]
142             self.fg.set_timing_offset(to)
143         except RuntimeError:
144             pass
145
146
147     # Accessor functions for Gui to manipulate receiver parameters
148     def set_gain_clock(self, gain):
149         self.gui.gainClockEdit.setText(QtCore.QString("%1").arg(gain))
150
151     def set_gain_phase(self, gain_phase):
152         self.gui.gainPhaseEdit.setText(QtCore.QString("%1").arg(gain_phase))
153
154     def set_gain_freq(self, gain_freq):
155         self.gui.gainFreqEdit.setText(QtCore.QString("%1").arg(gain_freq))
156         
157
158     def set_alpha_time(self, alpha):
159         self.gui.alphaTimeEdit.setText(QtCore.QString("%1").arg(alpha))
160
161     def set_beta_time(self, beta):
162         self.gui.betaTimeEdit.setText(QtCore.QString("%1").arg(beta))
163
164     def set_alpha_phase(self, alpha):
165         self.gui.alphaPhaseEdit.setText(QtCore.QString("%1").arg(alpha))
166
167     def gainPhaseEditText(self):
168         try:
169             gain_phase = self.gui.gainPhaseEdit.text().toDouble()[0]
170             self.fg.set_rx_gain_phase(gain_phase)
171         except RuntimeError:
172             pass
173
174     def gainClockEditText(self):
175         try:
176             gain = self.gui.gainClockEdit.text().toDouble()[0]
177             self.fg.set_rx_gain_clock(gain)
178         except RuntimeError:
179             pass
180
181     def gainFreqEditText(self):
182         try:
183             gain = self.gui.gainFreqEdit.text().toDouble()[0]
184             self.fg.set_rx_gain_freq(gain)
185         except RuntimeError:
186             pass
187
188     # Accessor functions for packet error reporting
189     def updatePacketInfo(self):
190         # Pull these globals in from the main thread
191         global n_rcvd, n_right, pktno
192
193         if(pktno > 0):
194             per = float(n_rcvd - n_right)/float(pktno)
195         else:
196             per = 0
197         self.gui.pktsRcvdEdit.setText(QtCore.QString("%1").arg(n_rcvd))
198         self.gui.pktsCorrectEdit.setText(QtCore.QString("%1").arg(n_right))
199         self.gui.perEdit.setText(QtCore.QString("%1").arg(float(per), 0, 'e', 4))
200
201
202
203 # ////////////////////////////////////////////////////////////////////
204 #        Define the GNU Radio Top Block
205 # ////////////////////////////////////////////////////////////////////
206
207
208
209 class my_top_block(gr.top_block):
210     def __init__(self, mod_class, demod_class, rx_callback, options):
211         gr.top_block.__init__(self)
212
213         self._sample_rate = options.sample_rate
214
215         channelon = True;
216
217         self.gui_on = options.gui
218
219         self._frequency_offset = options.frequency_offset
220         self._timing_offset = options.timing_offset
221         self._tx_amplitude = options.tx_amplitude
222         self._snr_dB = options.snr
223
224         self._noise_voltage = self.get_noise_voltage(self._snr_dB)
225
226         self.txpath = transmit_path(mod_class, options)
227         self.throttle = gr.throttle(gr.sizeof_gr_complex, self.sample_rate())
228         self.rxpath = receive_path(demod_class, rx_callback, options)
229
230         # FIXME: do better exposure to lower issues for control
231         self._gain_clock = self.rxpath.packet_receiver._demodulator._timing_alpha
232         self._gain_phase = self.rxpath.packet_receiver._demodulator._costas_alpha
233         self._gain_freq  = self.rxpath.packet_receiver._demodulator._freq_alpha
234
235         if channelon:
236             self.channel = gr.channel_model(self._noise_voltage,
237                                             self.frequency_offset(),
238                                             self.timing_offset())
239             
240             if options.discontinuous:
241                 z = 20000*[0,]
242                 self.zeros = gr.vector_source_c(z, True)
243                 packet_size = 5*((4+8+4+1500+4) * 8)
244                 self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(9e5)])
245
246                 # Connect components
247                 self.connect(self.txpath, self.throttle, (self.mux,0))
248                 self.connect(self.zeros, (self.mux,1))
249                 self.connect(self.mux, self.channel, self.rxpath)
250
251             else:
252                 self.connect(self.txpath, self.throttle, self.channel, self.rxpath)
253
254             if self.gui_on:
255                 self.qapp = QtGui.QApplication(sys.argv)
256                 fftsize = 2048
257
258                 self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
259                                            0, 1,
260                                            "Tx", True, True, False, True, True)
261                 self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
262                                            0, 1,
263                                            "Rx", True, True, False, True, True)
264
265                 self.snk_tx.set_frequency_axis(-80, 0)
266                 self.snk_rx.set_frequency_axis(-60, 20)
267
268                 self.freq_recov = self.rxpath.packet_receiver._demodulator.freq_recov
269                 self.phase_recov = self.rxpath.packet_receiver._demodulator.phase_recov
270                 self.time_recov = self.rxpath.packet_receiver._demodulator.time_recov
271                 self.freq_recov.set_alpha(self._gain_freq)
272                 self.phase_recov.set_alpha(self._gain_phase)
273                 self.phase_recov.set_beta(0.25*self._gain_phase*self._gain_phase)
274                 self.time_recov.set_alpha(self._gain_clock)
275                 self.time_recov.set_beta(0.25*self._gain_clock*self._gain_clock)
276
277                 # Connect to the QT sinks
278                 # FIXME: make better exposure to receiver from rxpath
279                 self.connect(self.channel, self.snk_tx)
280                 self.connect(self.phase_recov, self.snk_rx)
281                 #self.connect(self.freq_recov, self.snk_rx)
282
283                 pyTxQt  = self.snk_tx.pyqwidget()
284                 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
285                  
286                 pyRxQt  = self.snk_rx.pyqwidget()
287                 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
288
289                 self.main_box = dialog_box(pyTx, pyRx, self)
290                 self.main_box.show()
291                 
292         else:
293             # Connect components
294             self.connect(self.txpath, self.throttle, self.rxpath)
295
296
297
298     # System Parameters
299     def sample_rate(self):
300         return self._sample_rate
301     
302     def set_sample_rate(self, sr):
303         self._sample_rate = sr
304         #self.throttle.set_samples_per_second(self._sample_rate)
305
306     # Channel Model Parameters
307     def snr(self):
308         return self._snr_dB
309     
310     def set_snr(self, snr):
311         self._snr_dB = snr
312         self._noise_voltage = self.get_noise_voltage(self._snr_dB)
313         self.channel.set_noise_voltage(self._noise_voltage)
314
315     def get_noise_voltage(self, SNR):
316         snr = 10.0**(SNR/10.0)        
317         power_in_signal = abs(self._tx_amplitude)**2
318         noise_power = power_in_signal/snr
319         noise_voltage = math.sqrt(noise_power)
320         return noise_voltage
321
322     def frequency_offset(self):
323         return self._frequency_offset * self.sample_rate()
324
325     def set_frequency_offset(self, fo):
326         self._frequency_offset = fo / self.sample_rate()
327         self.channel.set_frequency_offset(self._frequency_offset)
328
329     def timing_offset(self):
330         return self._timing_offset
331     
332     def set_timing_offset(self, to):
333         self._timing_offset = to
334         self.channel.set_timing_offset(self._timing_offset)
335
336
337     # Receiver Parameters
338     def rx_gain_clock(self):
339         return self._gain_clock
340
341     def rx_gain_clock_beta(self):
342         return self._gain_clock_beta
343
344     def set_rx_gain_clock(self, gain):
345         self._gain_clock = gain
346         self._gain_clock_beta = .25 * self._gain_clock * self._gain_clock
347         self.rxpath.packet_receiver._demodulator.time_recov.set_alpha(self._gain_clock)
348         self.rxpath.packet_receiver._demodulator.time_recov.set_beta(self._gain_clock_beta)
349
350     def rx_gain_phase(self):
351         return self._gain_phase
352
353     def rx_gain_phase_beta(self):
354         return self._gain_phase_beta
355     
356     def set_rx_gain_phase(self, gain_phase):
357         self._gain_phase = gain_phase
358         self._gain_phase_beta = .25 * self._gain_phase * self._gain_phase
359         self.rxpath.packet_receiver._demodulator.phase_recov.set_alpha(self._gain_phase)
360         self.rxpath.packet_receiver._demodulator.phase_recov.set_beta(self._gain_phase_beta)
361
362
363     def rx_gain_freq(self):
364         return self._gain_freq
365
366     def set_rx_gain_freq(self, gain_freq):
367         self._gain_freq = gain_freq
368         #self._gain_freq_beta = .25 * self._gain_freq * self._gain_freq
369         self.rxpath.packet_receiver._demodulator.freq_recov.set_alpha(self._gain_freq)
370         #self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_fre_beta)
371
372
373 # /////////////////////////////////////////////////////////////////////////////
374 #       Thread to handle the packet sending procedure
375 #          Operates in parallel with qApp.exec_()       
376 # /////////////////////////////////////////////////////////////////////////////
377
378
379
380 class th_send(Thread):
381     def __init__(self, send_fnc, megs, sz):
382         Thread.__init__(self)
383         self.send = send_fnc
384         self.nbytes = int(1e6 * megs)
385         self.pkt_size = int(sz)
386
387     def run(self):
388         # generate and send packets
389         n = 0
390         pktno = 0
391         
392         while n < self.nbytes:
393             self.send(struct.pack('!H', pktno & 0xffff) +
394                       (self.pkt_size - 2) * chr(pktno & 0xff))
395             n += self.pkt_size
396             pktno += 1
397             
398         self.send(eof=True)
399
400     def stop(self):
401         self.nbytes = 0
402
403
404
405 # /////////////////////////////////////////////////////////////////////////////
406 #                                   main
407 # /////////////////////////////////////////////////////////////////////////////
408
409
410
411 def main():
412
413     global n_rcvd, n_right, pktno
414
415     n_rcvd = 0
416     n_right = 0
417     pktno = 0
418     
419     def rx_callback(ok, payload):
420         global n_rcvd, n_right, pktno
421         (pktno,) = struct.unpack('!H', payload[0:2])
422         n_rcvd += 1
423         if ok:
424             n_right += 1
425
426         if not options.gui:
427             print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
428                 ok, pktno, n_rcvd, n_right)
429         
430
431     def send_pkt(payload='', eof=False):
432         return tb.txpath.send_pkt(payload, eof)
433
434     mods = modulation_utils.type_1_mods()
435     demods = modulation_utils.type_1_demods()
436
437     parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
438     expert_grp = parser.add_option_group("Expert")
439     channel_grp = parser.add_option_group("Channel")
440
441     parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
442                       default='dbpsk',
443                       help="Select modulation from: %s [default=%%default]"
444                             % (', '.join(mods.keys()),))
445
446     parser.add_option("-s", "--size", type="eng_float", default=1500,
447                       help="set packet size [default=%default]")
448     parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
449                       help="set megabytes to transmit [default=%default]")
450     parser.add_option("","--discontinuous", action="store_true", default=False,
451                       help="enable discontinous transmission (bursts of 5 packets)")
452     parser.add_option("-G", "--gui", action="store_true", default=False,
453                       help="Turn on the GUI [default=%default]")
454
455     channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
456                            help="set speed of channel/simulation rate to RATE [default=%default]") 
457     channel_grp.add_option("", "--snr", type="eng_float", default=30,
458                            help="set the SNR of the channel in dB [default=%default]")
459     channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
460                            help="set frequency offset introduced by channel [default=%default]")
461     channel_grp.add_option("", "--timing-offset", type="eng_float", default=1.0,
462                            help="set timing offset introduced by channel [default=%default]")
463     channel_grp.add_option("", "--seed", action="store_true", default=False,
464                            help="use a random seed for AWGN noise [default=%default]")
465
466     transmit_path.add_options(parser, expert_grp)
467     receive_path.add_options(parser, expert_grp)
468
469     for mod in mods.values():
470         mod.add_options(expert_grp)
471     for demod in demods.values():
472         demod.add_options(expert_grp)
473
474     (options, args) = parser.parse_args ()
475
476     if len(args) != 0:
477         parser.print_help()
478         sys.exit(1)
479         
480     r = gr.enable_realtime_scheduling()
481     if r != gr.RT_OK:
482         print "Warning: failed to enable realtime scheduling"
483         
484     # Create an instance of a hierarchical block
485     tb = my_top_block(mods[options.modulation],
486                       demods[options.modulation],
487                       rx_callback, options)
488     tb.start()
489
490     packet_sender = th_send(send_pkt, options.megabytes, options.size)
491     packet_sender.start()
492
493     if(options.gui):
494         tb.qapp.exec_()
495         packet_sender.stop()
496     else:
497         # Process until done; hack in to the join to stop on an interrupt
498         while(packet_sender.isAlive()):
499             try:
500                 packet_sender.join(1)
501             except KeyboardInterrupt:
502                 packet_sender.stop()
503         
504     
505 if __name__ == '__main__':
506     try:
507         main()
508     except KeyboardInterrupt:
509         pass