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
9 from threading import Thread
12 from transmit_path import transmit_path
13 from receive_path import receive_path
16 from gnuradio.qtgui import qtgui
17 from PyQt4 import QtGui, QtCore
20 print "Please install gr-qtgui."
24 from qt_digital_window import Ui_DigitalWindow
26 print "Error: could not find qt_digital_window.py:"
27 print "\t\"pyuic4 qt_digital_window.ui -o qt_digital_window.py\""
35 # ////////////////////////////////////////////////////////////////////
36 # Define the QT Interface and Control Dialog
37 # ////////////////////////////////////////////////////////////////////
40 class dialog_box(QtGui.QMainWindow):
41 def __init__(self, snkTx, snkRx, fg, parent=None):
43 QtGui.QWidget.__init__(self, parent)
44 self.gui = Ui_DigitalWindow()
45 self.gui.setupUi(self)
49 self.set_sample_rate(self.fg.sample_rate())
51 self.set_snr(self.fg.snr())
52 self.set_frequency(self.fg.frequency_offset())
53 self.set_time_offset(self.fg.timing_offset())
55 self.set_alpha_time(self.fg.rx_timing_gain_alpha())
56 self.set_beta_time(self.fg.rx_timing_gain_beta())
57 self.set_alpha_freq(self.fg.rx_freq_gain_alpha())
59 # Add the qtsnk widgets to the hlayout box
60 self.gui.sinkLayout.addWidget(snkTx)
61 self.gui.sinkLayout.addWidget(snkRx)
64 # Connect up some signals
65 self.connect(self.gui.pauseButton, QtCore.SIGNAL("clicked()"),
68 self.connect(self.gui.sampleRateEdit, QtCore.SIGNAL("editingFinished()"),
69 self.sampleRateEditText)
71 self.connect(self.gui.snrEdit, QtCore.SIGNAL("editingFinished()"),
73 self.connect(self.gui.freqEdit, QtCore.SIGNAL("editingFinished()"),
75 self.connect(self.gui.timeEdit, QtCore.SIGNAL("editingFinished()"),
78 self.connect(self.gui.alphaTimeEdit, QtCore.SIGNAL("editingFinished()"),
79 self.alphaTimeEditText)
80 self.connect(self.gui.betaTimeEdit, QtCore.SIGNAL("editingFinished()"),
81 self.betaTimeEditText)
82 self.connect(self.gui.alphaFreqEdit, QtCore.SIGNAL("editingFinished()"),
83 self.alphaFreqEditText)
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)
93 if(self.gui.pauseButton.text() == "Pause"):
96 self.gui.pauseButton.setText("Unpause")
99 self.gui.pauseButton.setText("Pause")
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))
106 def sampleRateEditText(self):
108 rate = self.gui.sampleRateEdit.text().toAscii()
109 srate = eng_notation.str_to_num(rate)
110 #self.fg.set_sample_rate(srate)
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))
119 def set_frequency(self, fo):
120 self.gui.freqEdit.setText(QtCore.QString("%1").arg(fo))
122 def set_time_offset(self, to):
123 self.gui.timeEdit.setText(QtCore.QString("%1").arg(to))
125 def snrEditText(self):
127 snr = self.gui.snrEdit.text().toDouble()[0]
132 def freqEditText(self):
134 freq = self.gui.freqEdit.text().toDouble()[0]
135 self.fg.set_frequency_offset(freq)
139 def timeEditText(self):
141 to = self.gui.timeEdit.text().toDouble()[0]
142 self.fg.set_timing_offset(to)
147 # Accessor functions for Gui to manipulate receiver parameters
148 def set_alpha_time(self, alpha):
149 self.gui.alphaTimeEdit.setText(QtCore.QString("%1").arg(alpha))
151 def set_beta_time(self, beta):
152 self.gui.betaTimeEdit.setText(QtCore.QString("%1").arg(beta))
154 def set_alpha_freq(self, alpha):
155 self.gui.alphaFreqEdit.setText(QtCore.QString("%1").arg(alpha))
157 def alphaFreqEditText(self):
159 alpha = self.gui.alphaFreqEdit.text().toDouble()[0]
160 self.fg.set_rx_freq_gain_alpha(alpha)
164 def alphaTimeEditText(self):
166 alpha = self.gui.alphaTimeEdit.text().toDouble()[0]
167 self.fg.set_rx_timing_gain_alpha(alpha)
171 def betaTimeEditText(self):
173 beta = self.gui.betaTimeEdit.text().toDouble()[0]
174 self.fg.set_rx_timing_gain_beta(beta)
178 # Accessor functions for packet error reporting
179 def updatePacketInfo(self):
180 # Pull these globals in from the main thread
181 global n_rcvd, n_right, pktno
184 per = float(n_rcvd - n_right)/float(pktno)
187 self.gui.pktsRcvdEdit.setText(QtCore.QString("%1").arg(n_rcvd))
188 self.gui.pktsCorrectEdit.setText(QtCore.QString("%1").arg(n_right))
189 self.gui.perEdit.setText(QtCore.QString("%1").arg(per))
193 # ////////////////////////////////////////////////////////////////////
194 # Define the GNU Radio Top Block
195 # ////////////////////////////////////////////////////////////////////
199 class my_top_block(gr.top_block):
200 def __init__(self, mod_class, demod_class, rx_callback, options):
201 gr.top_block.__init__(self)
203 self._sample_rate = options.sample_rate
207 self.gui_on = options.gui
209 self._frequency_offset = options.frequency_offset
210 self._timing_offset = options.timing_offset
211 self._tx_amplitude = options.tx_amplitude
212 self._snr_dB = options.snr
214 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
216 self.txpath = transmit_path(mod_class, options)
217 self.throttle = gr.throttle(gr.sizeof_gr_complex, self.sample_rate())
218 self.rxpath = receive_path(demod_class, rx_callback, options)
220 # FIXME: do better exposure to lower issues for control
221 self._timing_gain_alpha = self.rxpath.packet_receiver._demodulator._timing_alpha
222 self._timing_gain_beta = self.rxpath.packet_receiver._demodulator._timing_beta
223 self._freq_gain_alpha = self.rxpath.packet_receiver._demodulator._costas_alpha
226 self.channel = gr.channel_model(self._noise_voltage,
227 self.frequency_offset(),
228 self.timing_offset())
230 if options.discontinuous:
232 self.zeros = gr.vector_source_c(z, True)
233 packet_size = 5*((4+8+4+1500+4) * 8)
234 self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(9e5)])
237 self.connect(self.txpath, self.throttle, (self.mux,0))
238 self.connect(self.zeros, (self.mux,1))
239 self.connect(self.mux, self.channel, self.rxpath)
242 self.connect(self.txpath, self.throttle, self.channel, self.rxpath)
245 self.qapp = QtGui.QApplication(sys.argv)
248 self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
250 "Tx", True, True, False, True, True)
251 self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
253 "Rx", True, True, False, True, True)
255 self.snk_tx.set_frequency_axis(-80, 0)
256 self.snk_rx.set_frequency_axis(-60, 20)
258 # Connect to the QT sinks
259 # FIXME: make better exposure to receiver from rxpath
260 self.freq_recov = self.rxpath.packet_receiver._demodulator.clock_recov
261 self.time_recov = self.rxpath.packet_receiver._demodulator.time_recov
262 self.freq_recov.set_alpha(self._freq_gain_alpha)
263 self.freq_recov.set_beta(0.25*self._freq_gain_alpha*self._freq_gain_alpha)
264 self.time_recov.set_alpha(self._timing_gain_alpha)
265 self.time_recov.set_beta(self._timing_gain_beta)
266 self.connect(self.channel, self.snk_tx)
267 self.connect(self.time_recov, self.snk_rx)
269 pyTxQt = self.snk_tx.pyqwidget()
270 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
272 pyRxQt = self.snk_rx.pyqwidget()
273 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
275 self.main_box = dialog_box(pyTx, pyRx, self)
280 self.connect(self.txpath, self.throttle, self.rxpath)
285 def sample_rate(self):
286 return self._sample_rate
288 def set_sample_rate(self, sr):
289 self._sample_rate = sr
290 #self.throttle.set_samples_per_second(self._sample_rate)
292 # Channel Model Parameters
296 def set_snr(self, snr):
298 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
299 self.channel.set_noise_voltage(self._noise_voltage)
301 def get_noise_voltage(self, SNR):
302 snr = 10.0**(SNR/10.0)
303 power_in_signal = abs(self._tx_amplitude)**2
304 noise_power = power_in_signal/snr
305 noise_voltage = math.sqrt(noise_power)
308 def frequency_offset(self):
309 return self._frequency_offset * self.sample_rate()
311 def set_frequency_offset(self, fo):
312 self._frequency_offset = fo / self.sample_rate()
313 self.channel.set_frequency_offset(self._frequency_offset)
315 def timing_offset(self):
316 return self._timing_offset
318 def set_timing_offset(self, to):
319 self._timing_offset = to
320 self.channel.set_timing_offset(self._timing_offset)
323 # Receiver Parameters
324 def rx_timing_gain_alpha(self):
325 return self._timing_gain_alpha
327 def rx_timing_gain_beta(self):
328 return self._timing_gain_beta
330 def set_rx_timing_gain_alpha(self, gain):
331 self._timing_gain_alpha = gain
332 self.time_recov.set_alpha(self._timing_gain_alpha)
334 def set_rx_timing_gain_beta(self, gain):
335 self._timing_gain_beta = gain
336 self.time_recov.set_beta(self._timing_gain_beta)
338 def rx_freq_gain_alpha(self):
339 return self._freq_gain_alpha
341 def rx_freq_gain_beta(self):
342 return self._freq_gain_beta
344 def set_rx_freq_gain_alpha(self, alpha):
345 self._freq_gain_alpha = alpha
346 self._freq_gain_beta = .25 * self._freq_gain_alpha * self._freq_gain_alpha
347 self.freq_recov.set_alpha(self._freq_gain_alpha)
348 self.freq_recov.set_beta(self._freq_gain_beta)
352 # /////////////////////////////////////////////////////////////////////////////
353 # Thread to handle the packet sending procedure
354 # Operates in parallel with qApp.exec_()
355 # /////////////////////////////////////////////////////////////////////////////
359 class th_send(Thread):
360 def __init__(self, send_fnc, megs, sz):
361 Thread.__init__(self)
363 self.nbytes = int(1e6 * megs)
364 self.pkt_size = int(sz)
367 # generate and send packets
371 while n < self.nbytes:
372 self.send(struct.pack('!H', pktno & 0xffff) +
373 (self.pkt_size - 2) * chr(pktno & 0xff))
384 # /////////////////////////////////////////////////////////////////////////////
386 # /////////////////////////////////////////////////////////////////////////////
392 global n_rcvd, n_right, pktno
398 def rx_callback(ok, payload):
399 global n_rcvd, n_right, pktno
400 (pktno,) = struct.unpack('!H', payload[0:2])
406 print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % (
407 ok, pktno, n_rcvd, n_right)
410 def send_pkt(payload='', eof=False):
411 return tb.txpath.send_pkt(payload, eof)
413 mods = modulation_utils.type_1_mods()
414 demods = modulation_utils.type_1_demods()
416 parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
417 expert_grp = parser.add_option_group("Expert")
418 channel_grp = parser.add_option_group("Channel")
420 parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
422 help="Select modulation from: %s [default=%%default]"
423 % (', '.join(mods.keys()),))
425 parser.add_option("-s", "--size", type="eng_float", default=1500,
426 help="set packet size [default=%default]")
427 parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
428 help="set megabytes to transmit [default=%default]")
429 parser.add_option("","--discontinuous", action="store_true", default=False,
430 help="enable discontinous transmission (bursts of 5 packets)")
431 parser.add_option("-G", "--gui", action="store_true", default=False,
432 help="Turn on the GUI [default=%default]")
434 channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
435 help="set speed of channel/simulation rate to RATE [default=%default]")
436 channel_grp.add_option("", "--snr", type="eng_float", default=30,
437 help="set the SNR of the channel in dB [default=%default]")
438 channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
439 help="set frequency offset introduced by channel [default=%default]")
440 channel_grp.add_option("", "--timing-offset", type="eng_float", default=1.0,
441 help="set timing offset introduced by channel [default=%default]")
442 channel_grp.add_option("", "--seed", action="store_true", default=False,
443 help="use a random seed for AWGN noise [default=%default]")
445 transmit_path.add_options(parser, expert_grp)
446 receive_path.add_options(parser, expert_grp)
448 for mod in mods.values():
449 mod.add_options(expert_grp)
450 for demod in demods.values():
451 demod.add_options(expert_grp)
453 (options, args) = parser.parse_args ()
459 r = gr.enable_realtime_scheduling()
461 print "Warning: failed to enable realtime scheduling"
463 # Create an instance of a hierarchical block
464 tb = my_top_block(mods[options.modulation],
465 demods[options.modulation],
466 rx_callback, options)
469 packet_sender = th_send(send_pkt, options.megabytes, options.size)
470 packet_sender.start()
476 # Process until done; hack in to the join to stop on an interrupt
477 while(packet_sender.isAlive()):
479 packet_sender.join(1)
480 except KeyboardInterrupt:
484 if __name__ == '__main__':
487 except KeyboardInterrupt: