3 # Copyright 2010 Free Software Foundation, Inc.
5 # This file is part of GNU Radio
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)
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.
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.
23 from gnuradio import gr, gru, modulation_utils2
24 from gnuradio import eng_notation
25 from gnuradio.eng_option import eng_option
26 from optparse import OptionParser
27 import random, time, struct, sys, os, math
29 from threading import Thread
32 from transmit_path import transmit_path
33 from receive_path import receive_path
36 from gnuradio.qtgui import qtgui
37 from PyQt4 import QtGui, QtCore
40 print "Please install gr-qtgui."
44 from qt_digital_window2 import Ui_DigitalWindow
46 print "Error: could not find qt_digital_window2.py:"
47 print "\t\"pyuic4 qt_digital_window2.ui -o qt_digital_window2.py\""
55 # ////////////////////////////////////////////////////////////////////
56 # Define the QT Interface and Control Dialog
57 # ////////////////////////////////////////////////////////////////////
60 class dialog_box(QtGui.QMainWindow):
61 def __init__(self, snkTx, snkRx, fg, parent=None):
63 QtGui.QWidget.__init__(self, parent)
64 self.gui = Ui_DigitalWindow()
65 self.gui.setupUi(self)
69 self.set_sample_rate(self.fg.sample_rate())
71 self.set_snr(self.fg.snr())
72 self.set_frequency(self.fg.frequency_offset())
73 self.set_time_offset(self.fg.timing_offset())
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())
79 # Add the qtsnk widgets to the hlayout box
80 self.gui.sinkLayout.addWidget(snkTx)
81 self.gui.sinkLayout.addWidget(snkRx)
84 # Connect up some signals
85 self.connect(self.gui.pauseButton, QtCore.SIGNAL("clicked()"),
88 self.connect(self.gui.sampleRateEdit, QtCore.SIGNAL("editingFinished()"),
89 self.sampleRateEditText)
91 self.connect(self.gui.snrEdit, QtCore.SIGNAL("editingFinished()"),
93 self.connect(self.gui.freqEdit, QtCore.SIGNAL("editingFinished()"),
95 self.connect(self.gui.timeEdit, QtCore.SIGNAL("editingFinished()"),
98 self.connect(self.gui.gainClockEdit, QtCore.SIGNAL("editingFinished()"),
99 self.gainClockEditText)
100 self.connect(self.gui.gainPhaseEdit, QtCore.SIGNAL("editingFinished()"),
101 self.gainPhaseEditText)
102 self.connect(self.gui.gainFreqEdit, QtCore.SIGNAL("editingFinished()"),
103 self.gainFreqEditText)
105 # Build a timer to update the packet number and PER fields
106 self.update_delay = 250 # time between updating packet rate fields
107 self.pkt_timer = QtCore.QTimer(self)
108 self.connect(self.pkt_timer, QtCore.SIGNAL("timeout()"),
109 self.updatePacketInfo)
110 self.pkt_timer.start(self.update_delay)
113 if(self.gui.pauseButton.text() == "Pause"):
116 self.gui.pauseButton.setText("Unpause")
119 self.gui.pauseButton.setText("Pause")
121 # Accessor functions for Gui to manipulate system parameters
122 def set_sample_rate(self, sr):
123 ssr = eng_notation.num_to_str(sr)
124 self.gui.sampleRateEdit.setText(QtCore.QString("%1").arg(ssr))
126 def sampleRateEditText(self):
128 rate = self.gui.sampleRateEdit.text().toAscii()
129 srate = eng_notation.str_to_num(rate)
130 #self.fg.set_sample_rate(srate)
135 # Accessor functions for Gui to manipulate channel model
136 def set_snr(self, snr):
137 self.gui.snrEdit.setText(QtCore.QString("%1").arg(snr))
139 def set_frequency(self, fo):
140 self.gui.freqEdit.setText(QtCore.QString("%1").arg(fo))
142 def set_time_offset(self, to):
143 self.gui.timeEdit.setText(QtCore.QString("%1").arg(to))
145 def snrEditText(self):
147 snr = self.gui.snrEdit.text().toDouble()[0]
152 def freqEditText(self):
154 freq = self.gui.freqEdit.text().toDouble()[0]
155 self.fg.set_frequency_offset(freq)
159 def timeEditText(self):
161 to = self.gui.timeEdit.text().toDouble()[0]
162 self.fg.set_timing_offset(to)
167 # Accessor functions for Gui to manipulate receiver parameters
168 def set_gain_clock(self, gain):
169 self.gui.gainClockEdit.setText(QtCore.QString("%1").arg(gain))
171 def set_gain_phase(self, gain_phase):
172 self.gui.gainPhaseEdit.setText(QtCore.QString("%1").arg(gain_phase))
174 def set_gain_freq(self, gain_freq):
175 self.gui.gainFreqEdit.setText(QtCore.QString("%1").arg(gain_freq))
178 def set_alpha_time(self, alpha):
179 self.gui.alphaTimeEdit.setText(QtCore.QString("%1").arg(alpha))
181 def set_beta_time(self, beta):
182 self.gui.betaTimeEdit.setText(QtCore.QString("%1").arg(beta))
184 def set_alpha_phase(self, alpha):
185 self.gui.alphaPhaseEdit.setText(QtCore.QString("%1").arg(alpha))
187 def gainPhaseEditText(self):
189 gain_phase = self.gui.gainPhaseEdit.text().toDouble()[0]
190 self.fg.set_rx_gain_phase(gain_phase)
194 def gainClockEditText(self):
196 gain = self.gui.gainClockEdit.text().toDouble()[0]
197 self.fg.set_rx_gain_clock(gain)
201 def gainFreqEditText(self):
203 gain = self.gui.gainFreqEdit.text().toDouble()[0]
204 self.fg.set_rx_gain_freq(gain)
208 # Accessor functions for packet error reporting
209 def updatePacketInfo(self):
210 # Pull these globals in from the main thread
211 global n_rcvd, n_right, pktno
214 per = float(n_rcvd - n_right)/float(pktno)
217 self.gui.pktsRcvdEdit.setText(QtCore.QString("%1").arg(n_rcvd))
218 self.gui.pktsCorrectEdit.setText(QtCore.QString("%1").arg(n_right))
219 self.gui.perEdit.setText(QtCore.QString("%1").arg(float(per), 0, 'e', 4))
223 # ////////////////////////////////////////////////////////////////////
224 # Define the GNU Radio Top Block
225 # ////////////////////////////////////////////////////////////////////
229 class my_top_block(gr.top_block):
230 def __init__(self, mod_class, demod_class, rx_callback, options):
231 gr.top_block.__init__(self)
233 self._sample_rate = options.sample_rate
237 self.gui_on = options.gui
239 self._frequency_offset = options.frequency_offset
240 self._timing_offset = options.timing_offset
241 self._tx_amplitude = options.tx_amplitude
242 self._snr_dB = options.snr
244 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
246 # With new interface, sps does not get set by default, but
247 # in the loopback, we don't recalculate it; so just force it here
248 if(options.samples_per_symbol == None):
249 options.samples_per_symbol = 2
251 self.txpath = transmit_path(mod_class, options)
252 self.throttle = gr.throttle(gr.sizeof_gr_complex, self.sample_rate())
253 self.rxpath = receive_path(demod_class, rx_callback, options)
255 # FIXME: do better exposure to lower issues for control
256 self._gain_clock = self.rxpath.packet_receiver._demodulator._timing_alpha
257 self._gain_phase = self.rxpath.packet_receiver._demodulator._phase_alpha
258 self._gain_freq = self.rxpath.packet_receiver._demodulator._freq_alpha
261 self.channel = gr.channel_model(self._noise_voltage,
262 self.frequency_offset(),
263 self.timing_offset())
265 if options.discontinuous:
267 self.zeros = gr.vector_source_c(z, True)
268 packet_size = 5*((4+8+4+1500+4) * 8)
269 self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(9e5)])
272 self.connect(self.txpath, self.throttle, (self.mux,0))
273 self.connect(self.zeros, (self.mux,1))
274 self.connect(self.mux, self.channel, self.rxpath)
277 self.connect(self.txpath, self.throttle, self.channel, self.rxpath)
280 self.qapp = QtGui.QApplication(sys.argv)
283 self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
285 "Tx", True, True, False, True, True)
286 self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
288 "Rx", True, True, False, True, True)
290 self.snk_tx.set_frequency_axis(-80, 0)
291 self.snk_rx.set_frequency_axis(-60, 20)
293 self.freq_recov = self.rxpath.packet_receiver._demodulator.freq_recov
294 self.phase_recov = self.rxpath.packet_receiver._demodulator.phase_recov
295 self.time_recov = self.rxpath.packet_receiver._demodulator.time_recov
296 self.freq_recov.set_alpha(self._gain_freq)
297 self.freq_recov.set_beta(self._gain_freq/10.0)
298 self.phase_recov.set_alpha(self._gain_phase)
299 self.phase_recov.set_beta(0.25*self._gain_phase*self._gain_phase)
300 self.time_recov.set_alpha(self._gain_clock)
301 self.time_recov.set_beta(0.25*self._gain_clock*self._gain_clock)
303 # Connect to the QT sinks
304 # FIXME: make better exposure to receiver from rxpath
305 self.connect(self.channel, self.snk_tx)
306 self.connect(self.phase_recov, self.snk_rx)
307 #self.connect(self.freq_recov, self.snk_rx)
309 pyTxQt = self.snk_tx.pyqwidget()
310 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
312 pyRxQt = self.snk_rx.pyqwidget()
313 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
315 self.main_box = dialog_box(pyTx, pyRx, self)
320 self.connect(self.txpath, self.throttle, self.rxpath)
325 def sample_rate(self):
326 return self._sample_rate
328 def set_sample_rate(self, sr):
329 self._sample_rate = sr
330 #self.throttle.set_samples_per_second(self._sample_rate)
332 # Channel Model Parameters
336 def set_snr(self, snr):
338 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
339 self.channel.set_noise_voltage(self._noise_voltage)
341 def get_noise_voltage(self, SNR):
342 snr = 10.0**(SNR/10.0)
343 power_in_signal = abs(self._tx_amplitude)**2
344 noise_power = power_in_signal/snr
345 noise_voltage = math.sqrt(noise_power)
348 def frequency_offset(self):
349 return self._frequency_offset * self.sample_rate()
351 def set_frequency_offset(self, fo):
352 self._frequency_offset = fo / self.sample_rate()
353 self.channel.set_frequency_offset(self._frequency_offset)
355 def timing_offset(self):
356 return self._timing_offset
358 def set_timing_offset(self, to):
359 self._timing_offset = to
360 self.channel.set_timing_offset(self._timing_offset)
363 # Receiver Parameters
364 def rx_gain_clock(self):
365 return self._gain_clock
367 def rx_gain_clock_beta(self):
368 return self._gain_clock_beta
370 def set_rx_gain_clock(self, gain):
371 self._gain_clock = gain
372 self._gain_clock_beta = .25 * self._gain_clock * self._gain_clock
373 self.rxpath.packet_receiver._demodulator.time_recov.set_alpha(self._gain_clock)
374 self.rxpath.packet_receiver._demodulator.time_recov.set_beta(self._gain_clock_beta)
376 def rx_gain_phase(self):
377 return self._gain_phase
379 def rx_gain_phase_beta(self):
380 return self._gain_phase_beta
382 def set_rx_gain_phase(self, gain_phase):
383 self._gain_phase = gain_phase
384 self._gain_phase_beta = .25 * self._gain_phase * self._gain_phase
385 self.rxpath.packet_receiver._demodulator.phase_recov.set_alpha(self._gain_phase)
386 self.rxpath.packet_receiver._demodulator.phase_recov.set_beta(self._gain_phase_beta)
389 def rx_gain_freq(self):
390 return self._gain_freq
392 def set_rx_gain_freq(self, gain_freq):
393 self._gain_freq = gain_freq
394 #self._gain_freq_beta = .25 * self._gain_freq * self._gain_freq
395 self.rxpath.packet_receiver._demodulator.freq_recov.set_alpha(self._gain_freq)
396 self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_freq/10.0)
397 #self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_fre_beta)
400 # /////////////////////////////////////////////////////////////////////////////
401 # Thread to handle the packet sending procedure
402 # Operates in parallel with qApp.exec_()
403 # /////////////////////////////////////////////////////////////////////////////
407 class th_send(Thread):
408 def __init__(self, send_fnc, megs, sz):
409 Thread.__init__(self)
411 self.nbytes = int(1e6 * megs)
412 self.pkt_size = int(sz)
415 # generate and send packets
419 while n < self.nbytes:
420 self.send(struct.pack('!H', pktno & 0xffff) +
421 (self.pkt_size - 2) * chr(pktno & 0xff))
432 # /////////////////////////////////////////////////////////////////////////////
434 # /////////////////////////////////////////////////////////////////////////////
440 global n_rcvd, n_right, pktno
446 def rx_callback(ok, payload):
447 global n_rcvd, n_right, pktno
448 (pktno,) = struct.unpack('!H', payload[0:2])
454 print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % (
455 ok, pktno, n_rcvd, n_right)
458 def send_pkt(payload='', eof=False):
459 return tb.txpath.send_pkt(payload, eof)
461 mods = modulation_utils2.type_1_mods()
462 demods = modulation_utils2.type_1_demods()
464 parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
465 expert_grp = parser.add_option_group("Expert")
466 channel_grp = parser.add_option_group("Channel")
468 parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
470 help="Select modulation from: %s [default=%%default]"
471 % (', '.join(mods.keys()),))
473 parser.add_option("-s", "--size", type="eng_float", default=1500,
474 help="set packet size [default=%default]")
475 parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
476 help="set megabytes to transmit [default=%default]")
477 parser.add_option("","--discontinuous", action="store_true", default=False,
478 help="enable discontinous transmission (bursts of 5 packets)")
479 parser.add_option("-G", "--gui", action="store_true", default=False,
480 help="Turn on the GUI [default=%default]")
482 channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
483 help="set speed of channel/simulation rate to RATE [default=%default]")
484 channel_grp.add_option("", "--snr", type="eng_float", default=30,
485 help="set the SNR of the channel in dB [default=%default]")
486 channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
487 help="set frequency offset introduced by channel [default=%default]")
488 channel_grp.add_option("", "--timing-offset", type="eng_float", default=1.0,
489 help="set timing offset introduced by channel [default=%default]")
490 channel_grp.add_option("", "--seed", action="store_true", default=False,
491 help="use a random seed for AWGN noise [default=%default]")
493 transmit_path.add_options(parser, expert_grp)
494 receive_path.add_options(parser, expert_grp)
496 for mod in mods.values():
497 mod.add_options(expert_grp)
498 for demod in demods.values():
499 demod.add_options(expert_grp)
501 (options, args) = parser.parse_args ()
507 r = gr.enable_realtime_scheduling()
509 print "Warning: failed to enable realtime scheduling"
511 # Create an instance of a hierarchical block
512 tb = my_top_block(mods[options.modulation],
513 demods[options.modulation],
514 rx_callback, options)
517 packet_sender = th_send(send_pkt, options.megabytes, options.size)
518 packet_sender.start()
524 # Process until done; hack in to the join to stop on an interrupt
525 while(packet_sender.isAlive()):
527 packet_sender.join(1)
528 except KeyboardInterrupt:
532 if __name__ == '__main__':
535 except KeyboardInterrupt: