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_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())
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.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)
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_gain_clock(self, gain):
149 self.gui.gainClockEdit.setText(QtCore.QString("%1").arg(gain))
151 def set_gain_phase(self, gain_phase):
152 self.gui.gainPhaseEdit.setText(QtCore.QString("%1").arg(gain_phase))
154 def set_gain_freq(self, gain_freq):
155 self.gui.gainFreqEdit.setText(QtCore.QString("%1").arg(gain_freq))
158 def set_alpha_time(self, alpha):
159 self.gui.alphaTimeEdit.setText(QtCore.QString("%1").arg(alpha))
161 def set_beta_time(self, beta):
162 self.gui.betaTimeEdit.setText(QtCore.QString("%1").arg(beta))
164 def set_alpha_phase(self, alpha):
165 self.gui.alphaPhaseEdit.setText(QtCore.QString("%1").arg(alpha))
167 def gainPhaseEditText(self):
169 gain_phase = self.gui.gainPhaseEdit.text().toDouble()[0]
170 self.fg.set_rx_gain_phase(gain_phase)
174 def gainClockEditText(self):
176 gain = self.gui.gainClockEdit.text().toDouble()[0]
177 self.fg.set_rx_gain_clock(gain)
181 def gainFreqEditText(self):
183 gain = self.gui.gainFreqEdit.text().toDouble()[0]
184 self.fg.set_rx_gain_freq(gain)
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
194 per = float(n_rcvd - n_right)/float(pktno)
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(per, 0, 'e', 4))
203 # ////////////////////////////////////////////////////////////////////
204 # Define the GNU Radio Top Block
205 # ////////////////////////////////////////////////////////////////////
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)
213 self._sample_rate = options.sample_rate
217 self.gui_on = options.gui
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
224 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
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)
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
236 self.channel = gr.channel_model(self._noise_voltage,
237 self.frequency_offset(),
238 self.timing_offset())
240 if options.discontinuous:
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)])
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)
252 self.connect(self.txpath, self.throttle, self.channel, self.rxpath)
255 self.qapp = QtGui.QApplication(sys.argv)
258 self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
260 "Tx", True, True, False, True, True)
261 self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
263 "Rx", True, True, False, True, True)
265 self.snk_tx.set_frequency_axis(-80, 0)
266 self.snk_rx.set_frequency_axis(-60, 20)
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)
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)
283 pyTxQt = self.snk_tx.pyqwidget()
284 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
286 pyRxQt = self.snk_rx.pyqwidget()
287 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
289 self.main_box = dialog_box(pyTx, pyRx, self)
294 self.connect(self.txpath, self.throttle, self.rxpath)
299 def sample_rate(self):
300 return self._sample_rate
302 def set_sample_rate(self, sr):
303 self._sample_rate = sr
304 #self.throttle.set_samples_per_second(self._sample_rate)
306 # Channel Model Parameters
310 def set_snr(self, snr):
312 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
313 self.channel.set_noise_voltage(self._noise_voltage)
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)
322 def frequency_offset(self):
323 return self._frequency_offset * self.sample_rate()
325 def set_frequency_offset(self, fo):
326 self._frequency_offset = fo / self.sample_rate()
327 self.channel.set_frequency_offset(self._frequency_offset)
329 def timing_offset(self):
330 return self._timing_offset
332 def set_timing_offset(self, to):
333 self._timing_offset = to
334 self.channel.set_timing_offset(self._timing_offset)
337 # Receiver Parameters
338 def rx_gain_clock(self):
339 return self._gain_clock
341 def rx_gain_clock_beta(self):
342 return self._gain_clock_beta
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)
350 def rx_gain_phase(self):
351 return self._gain_phase
353 def rx_gain_phase_beta(self):
354 return self._gain_phase_beta
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)
363 def rx_gain_freq(self):
364 return self._gain_freq
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)
373 # /////////////////////////////////////////////////////////////////////////////
374 # Thread to handle the packet sending procedure
375 # Operates in parallel with qApp.exec_()
376 # /////////////////////////////////////////////////////////////////////////////
380 class th_send(Thread):
381 def __init__(self, send_fnc, megs, sz):
382 Thread.__init__(self)
384 self.nbytes = int(1e6 * megs)
385 self.pkt_size = int(sz)
388 # generate and send packets
392 while n < self.nbytes:
393 self.send(struct.pack('!H', pktno & 0xffff) +
394 (self.pkt_size - 2) * chr(pktno & 0xff))
405 # /////////////////////////////////////////////////////////////////////////////
407 # /////////////////////////////////////////////////////////////////////////////
413 global n_rcvd, n_right, pktno
419 def rx_callback(ok, payload):
420 global n_rcvd, n_right, pktno
421 (pktno,) = struct.unpack('!H', payload[0:2])
427 print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % (
428 ok, pktno, n_rcvd, n_right)
431 def send_pkt(payload='', eof=False):
432 return tb.txpath.send_pkt(payload, eof)
434 mods = modulation_utils.type_1_mods()
435 demods = modulation_utils.type_1_demods()
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")
441 parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
443 help="Select modulation from: %s [default=%%default]"
444 % (', '.join(mods.keys()),))
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]")
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]")
466 transmit_path.add_options(parser, expert_grp)
467 receive_path.add_options(parser, expert_grp)
469 for mod in mods.values():
470 mod.add_options(expert_grp)
471 for demod in demods.values():
472 demod.add_options(expert_grp)
474 (options, args) = parser.parse_args ()
480 r = gr.enable_realtime_scheduling()
482 print "Warning: failed to enable realtime scheduling"
484 # Create an instance of a hierarchical block
485 tb = my_top_block(mods[options.modulation],
486 demods[options.modulation],
487 rx_callback, options)
490 packet_sender = th_send(send_pkt, options.megabytes, options.size)
491 packet_sender.start()
497 # Process until done; hack in to the join to stop on an interrupt
498 while(packet_sender.isAlive()):
500 packet_sender.join(1)
501 except KeyboardInterrupt:
505 if __name__ == '__main__':
508 except KeyboardInterrupt: