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_window2 import Ui_DigitalWindow
26 print "Error: could not find qt_digital_window2.py:"
27 print "\t\"pyuic4 qt_digital_window2.ui -o qt_digital_window2.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_phase(self.fg.rx_phase_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.alphaPhaseEdit, QtCore.SIGNAL("editingFinished()"),
83 self.alphaPhaseEditText)
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_phase(self, alpha):
155 self.gui.alphaPhaseEdit.setText(QtCore.QString("%1").arg(alpha))
157 def alphaPhaseEditText(self):
159 alpha = self.gui.alphaPhaseEdit.text().toDouble()[0]
160 self.fg.set_rx_phase_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._phase_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.freq_recov
261 self.phase_recov = self.rxpath.packet_receiver._demodulator.phase_recov
262 self.time_recov = self.rxpath.packet_receiver._demodulator.time_recov
263 self.phase_recov.set_alpha(self._phase_gain_alpha)
264 self.phase_recov.set_beta(0.25*self._phase_gain_alpha*self._phase_gain_alpha)
265 self.time_recov.set_alpha(self._timing_gain_alpha)
266 self.time_recov.set_beta(self._timing_gain_beta)
267 self.connect(self.channel, self.snk_tx)
268 self.connect(self.phase_recov, self.snk_rx)
269 #self.connect(self.freq_recov, self.snk_rx)
271 pyTxQt = self.snk_tx.pyqwidget()
272 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
274 pyRxQt = self.snk_rx.pyqwidget()
275 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
277 self.main_box = dialog_box(pyTx, pyRx, self)
282 self.connect(self.txpath, self.throttle, self.rxpath)
287 def sample_rate(self):
288 return self._sample_rate
290 def set_sample_rate(self, sr):
291 self._sample_rate = sr
292 #self.throttle.set_samples_per_second(self._sample_rate)
294 # Channel Model Parameters
298 def set_snr(self, snr):
300 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
301 self.channel.set_noise_voltage(self._noise_voltage)
303 def get_noise_voltage(self, SNR):
304 snr = 10.0**(SNR/10.0)
305 power_in_signal = abs(self._tx_amplitude)**2
306 noise_power = power_in_signal/snr
307 noise_voltage = math.sqrt(noise_power)
310 def frequency_offset(self):
311 return self._frequency_offset * self.sample_rate()
313 def set_frequency_offset(self, fo):
314 self._frequency_offset = fo / self.sample_rate()
315 self.channel.set_frequency_offset(self._frequency_offset)
317 def timing_offset(self):
318 return self._timing_offset
320 def set_timing_offset(self, to):
321 self._timing_offset = to
322 self.channel.set_timing_offset(self._timing_offset)
325 # Receiver Parameters
326 def rx_timing_gain_alpha(self):
327 return self._timing_gain_alpha
329 def rx_timing_gain_beta(self):
330 return self._timing_gain_beta
332 def set_rx_timing_gain_alpha(self, gain):
333 self._timing_gain_alpha = gain
334 self.time_recov.set_alpha(self._timing_gain_alpha)
336 def set_rx_timing_gain_beta(self, gain):
337 self._timing_gain_beta = gain
338 self.time_recov.set_beta(self._timing_gain_beta)
340 def rx_phase_gain_alpha(self):
341 return self._phase_gain_alpha
343 def rx_phase_gain_beta(self):
344 return self._phase_gain_beta
346 def set_rx_phase_gain_alpha(self, alpha):
347 self._phase_gain_alpha = alpha
348 self._phase_gain_beta = .25 * self._phase_gain_alpha * self._phase_gain_alpha
349 self.phase_recov.set_alpha(self._phase_gain_alpha)
350 self.phase_recov.set_beta(self._phase_gain_beta)
354 # /////////////////////////////////////////////////////////////////////////////
355 # Thread to handle the packet sending procedure
356 # Operates in parallel with qApp.exec_()
357 # /////////////////////////////////////////////////////////////////////////////
361 class th_send(Thread):
362 def __init__(self, send_fnc, megs, sz):
363 Thread.__init__(self)
365 self.nbytes = int(1e6 * megs)
366 self.pkt_size = int(sz)
369 # generate and send packets
373 while n < self.nbytes:
374 self.send(struct.pack('!H', pktno & 0xffff) +
375 (self.pkt_size - 2) * chr(pktno & 0xff))
386 # /////////////////////////////////////////////////////////////////////////////
388 # /////////////////////////////////////////////////////////////////////////////
394 global n_rcvd, n_right, pktno
400 def rx_callback(ok, payload):
401 global n_rcvd, n_right, pktno
402 (pktno,) = struct.unpack('!H', payload[0:2])
408 print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % (
409 ok, pktno, n_rcvd, n_right)
412 def send_pkt(payload='', eof=False):
413 return tb.txpath.send_pkt(payload, eof)
415 mods = modulation_utils.type_1_mods()
416 demods = modulation_utils.type_1_demods()
418 parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
419 expert_grp = parser.add_option_group("Expert")
420 channel_grp = parser.add_option_group("Channel")
422 parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
424 help="Select modulation from: %s [default=%%default]"
425 % (', '.join(mods.keys()),))
427 parser.add_option("-s", "--size", type="eng_float", default=1500,
428 help="set packet size [default=%default]")
429 parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
430 help="set megabytes to transmit [default=%default]")
431 parser.add_option("","--discontinuous", action="store_true", default=False,
432 help="enable discontinous transmission (bursts of 5 packets)")
433 parser.add_option("-G", "--gui", action="store_true", default=False,
434 help="Turn on the GUI [default=%default]")
436 channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
437 help="set speed of channel/simulation rate to RATE [default=%default]")
438 channel_grp.add_option("", "--snr", type="eng_float", default=30,
439 help="set the SNR of the channel in dB [default=%default]")
440 channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
441 help="set frequency offset introduced by channel [default=%default]")
442 channel_grp.add_option("", "--timing-offset", type="eng_float", default=1.0,
443 help="set timing offset introduced by channel [default=%default]")
444 channel_grp.add_option("", "--seed", action="store_true", default=False,
445 help="use a random seed for AWGN noise [default=%default]")
447 transmit_path.add_options(parser, expert_grp)
448 receive_path.add_options(parser, expert_grp)
450 for mod in mods.values():
451 mod.add_options(expert_grp)
452 for demod in demods.values():
453 demod.add_options(expert_grp)
455 (options, args) = parser.parse_args ()
461 r = gr.enable_realtime_scheduling()
463 print "Warning: failed to enable realtime scheduling"
465 # Create an instance of a hierarchical block
466 tb = my_top_block(mods[options.modulation],
467 demods[options.modulation],
468 rx_callback, options)
471 packet_sender = th_send(send_pkt, options.megabytes, options.size)
472 packet_sender.start()
478 # Process until done; hack in to the join to stop on an interrupt
479 while(packet_sender.isAlive()):
481 packet_sender.join(1)
482 except KeyboardInterrupt:
486 if __name__ == '__main__':
489 except KeyboardInterrupt: