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(float(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 # With new interface, sps does not get set by default, but
227 # in the loopback, we don't recalculate it; so just force it here
228 if(options.samples_per_symbol == None):
229 options.samples_per_symbol = 2
231 self.txpath = transmit_path(mod_class, options)
232 self.throttle = gr.throttle(gr.sizeof_gr_complex, self.sample_rate())
233 self.rxpath = receive_path(demod_class, rx_callback, options)
235 # FIXME: do better exposure to lower issues for control
236 self._gain_clock = self.rxpath.packet_receiver._demodulator._timing_alpha
237 self._gain_phase = self.rxpath.packet_receiver._demodulator._costas_alpha
238 self._gain_freq = self.rxpath.packet_receiver._demodulator._freq_alpha
241 self.channel = gr.channel_model(self._noise_voltage,
242 self.frequency_offset(),
243 self.timing_offset())
245 if options.discontinuous:
247 self.zeros = gr.vector_source_c(z, True)
248 packet_size = 5*((4+8+4+1500+4) * 8)
249 self.mux = gr.stream_mux(gr.sizeof_gr_complex, [packet_size-0, int(9e5)])
252 self.connect(self.txpath, self.throttle, (self.mux,0))
253 self.connect(self.zeros, (self.mux,1))
254 self.connect(self.mux, self.channel, self.rxpath)
257 self.connect(self.txpath, self.throttle, self.channel, self.rxpath)
260 self.qapp = QtGui.QApplication(sys.argv)
263 self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
265 "Tx", True, True, False, True, True)
266 self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS,
268 "Rx", True, True, False, True, True)
270 self.snk_tx.set_frequency_axis(-80, 0)
271 self.snk_rx.set_frequency_axis(-60, 20)
273 self.freq_recov = self.rxpath.packet_receiver._demodulator.freq_recov
274 self.phase_recov = self.rxpath.packet_receiver._demodulator.phase_recov
275 self.time_recov = self.rxpath.packet_receiver._demodulator.time_recov
276 self.freq_recov.set_alpha(self._gain_freq)
277 self.freq_recov.set_beta(self._gain_freq/10.0)
278 self.phase_recov.set_alpha(self._gain_phase)
279 self.phase_recov.set_beta(0.25*self._gain_phase*self._gain_phase)
280 self.time_recov.set_alpha(self._gain_clock)
281 self.time_recov.set_beta(0.25*self._gain_clock*self._gain_clock)
283 # Connect to the QT sinks
284 # FIXME: make better exposure to receiver from rxpath
285 self.connect(self.channel, self.snk_tx)
286 self.connect(self.phase_recov, self.snk_rx)
287 #self.connect(self.freq_recov, self.snk_rx)
289 pyTxQt = self.snk_tx.pyqwidget()
290 pyTx = sip.wrapinstance(pyTxQt, QtGui.QWidget)
292 pyRxQt = self.snk_rx.pyqwidget()
293 pyRx = sip.wrapinstance(pyRxQt, QtGui.QWidget)
295 self.main_box = dialog_box(pyTx, pyRx, self)
300 self.connect(self.txpath, self.throttle, self.rxpath)
305 def sample_rate(self):
306 return self._sample_rate
308 def set_sample_rate(self, sr):
309 self._sample_rate = sr
310 #self.throttle.set_samples_per_second(self._sample_rate)
312 # Channel Model Parameters
316 def set_snr(self, snr):
318 self._noise_voltage = self.get_noise_voltage(self._snr_dB)
319 self.channel.set_noise_voltage(self._noise_voltage)
321 def get_noise_voltage(self, SNR):
322 snr = 10.0**(SNR/10.0)
323 power_in_signal = abs(self._tx_amplitude)**2
324 noise_power = power_in_signal/snr
325 noise_voltage = math.sqrt(noise_power)
328 def frequency_offset(self):
329 return self._frequency_offset * self.sample_rate()
331 def set_frequency_offset(self, fo):
332 self._frequency_offset = fo / self.sample_rate()
333 self.channel.set_frequency_offset(self._frequency_offset)
335 def timing_offset(self):
336 return self._timing_offset
338 def set_timing_offset(self, to):
339 self._timing_offset = to
340 self.channel.set_timing_offset(self._timing_offset)
343 # Receiver Parameters
344 def rx_gain_clock(self):
345 return self._gain_clock
347 def rx_gain_clock_beta(self):
348 return self._gain_clock_beta
350 def set_rx_gain_clock(self, gain):
351 self._gain_clock = gain
352 self._gain_clock_beta = .25 * self._gain_clock * self._gain_clock
353 self.rxpath.packet_receiver._demodulator.time_recov.set_alpha(self._gain_clock)
354 self.rxpath.packet_receiver._demodulator.time_recov.set_beta(self._gain_clock_beta)
356 def rx_gain_phase(self):
357 return self._gain_phase
359 def rx_gain_phase_beta(self):
360 return self._gain_phase_beta
362 def set_rx_gain_phase(self, gain_phase):
363 self._gain_phase = gain_phase
364 self._gain_phase_beta = .25 * self._gain_phase * self._gain_phase
365 self.rxpath.packet_receiver._demodulator.phase_recov.set_alpha(self._gain_phase)
366 self.rxpath.packet_receiver._demodulator.phase_recov.set_beta(self._gain_phase_beta)
369 def rx_gain_freq(self):
370 return self._gain_freq
372 def set_rx_gain_freq(self, gain_freq):
373 self._gain_freq = gain_freq
374 #self._gain_freq_beta = .25 * self._gain_freq * self._gain_freq
375 self.rxpath.packet_receiver._demodulator.freq_recov.set_alpha(self._gain_freq)
376 self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_freq/10.0)
377 #self.rxpath.packet_receiver._demodulator.freq_recov.set_beta(self._gain_fre_beta)
380 # /////////////////////////////////////////////////////////////////////////////
381 # Thread to handle the packet sending procedure
382 # Operates in parallel with qApp.exec_()
383 # /////////////////////////////////////////////////////////////////////////////
387 class th_send(Thread):
388 def __init__(self, send_fnc, megs, sz):
389 Thread.__init__(self)
391 self.nbytes = int(1e6 * megs)
392 self.pkt_size = int(sz)
395 # generate and send packets
399 while n < self.nbytes:
400 self.send(struct.pack('!H', pktno & 0xffff) +
401 (self.pkt_size - 2) * chr(pktno & 0xff))
412 # /////////////////////////////////////////////////////////////////////////////
414 # /////////////////////////////////////////////////////////////////////////////
420 global n_rcvd, n_right, pktno
426 def rx_callback(ok, payload):
427 global n_rcvd, n_right, pktno
428 (pktno,) = struct.unpack('!H', payload[0:2])
434 print "ok = %5s pktno = %4d n_rcvd = %4d n_right = %4d" % (
435 ok, pktno, n_rcvd, n_right)
438 def send_pkt(payload='', eof=False):
439 return tb.txpath.send_pkt(payload, eof)
441 mods = modulation_utils.type_1_mods()
442 demods = modulation_utils.type_1_demods()
444 parser = OptionParser(option_class=eng_option, conflict_handler="resolve")
445 expert_grp = parser.add_option_group("Expert")
446 channel_grp = parser.add_option_group("Channel")
448 parser.add_option("-m", "--modulation", type="choice", choices=mods.keys(),
450 help="Select modulation from: %s [default=%%default]"
451 % (', '.join(mods.keys()),))
453 parser.add_option("-s", "--size", type="eng_float", default=1500,
454 help="set packet size [default=%default]")
455 parser.add_option("-M", "--megabytes", type="eng_float", default=1.0,
456 help="set megabytes to transmit [default=%default]")
457 parser.add_option("","--discontinuous", action="store_true", default=False,
458 help="enable discontinous transmission (bursts of 5 packets)")
459 parser.add_option("-G", "--gui", action="store_true", default=False,
460 help="Turn on the GUI [default=%default]")
462 channel_grp.add_option("", "--sample-rate", type="eng_float", default=1e5,
463 help="set speed of channel/simulation rate to RATE [default=%default]")
464 channel_grp.add_option("", "--snr", type="eng_float", default=30,
465 help="set the SNR of the channel in dB [default=%default]")
466 channel_grp.add_option("", "--frequency-offset", type="eng_float", default=0,
467 help="set frequency offset introduced by channel [default=%default]")
468 channel_grp.add_option("", "--timing-offset", type="eng_float", default=1.0,
469 help="set timing offset introduced by channel [default=%default]")
470 channel_grp.add_option("", "--seed", action="store_true", default=False,
471 help="use a random seed for AWGN noise [default=%default]")
473 transmit_path.add_options(parser, expert_grp)
474 receive_path.add_options(parser, expert_grp)
476 for mod in mods.values():
477 mod.add_options(expert_grp)
478 for demod in demods.values():
479 demod.add_options(expert_grp)
481 (options, args) = parser.parse_args ()
487 r = gr.enable_realtime_scheduling()
489 print "Warning: failed to enable realtime scheduling"
491 # Create an instance of a hierarchical block
492 tb = my_top_block(mods[options.modulation],
493 demods[options.modulation],
494 rx_callback, options)
497 packet_sender = th_send(send_pkt, options.megabytes, options.size)
498 packet_sender.start()
504 # Process until done; hack in to the join to stop on an interrupt
505 while(packet_sender.isAlive()):
507 packet_sender.join(1)
508 except KeyboardInterrupt:
512 if __name__ == '__main__':
515 except KeyboardInterrupt: