5 from scipy import fftpack
7 print "Please install SciPy to run this script (http://www.scipy.org/)"
11 from PyQt4 import Qt, QtCore, QtGui
12 import PyQt4.Qwt5 as Qwt
13 from matplotlib import mlab
14 from optparse import OptionParser
15 from gnuradio import eng_notation
17 from pyqt_plot import Ui_MainWindow
19 class gr_plot_qt(QtGui.QMainWindow):
20 def __init__(self, qapp, filename, options, parent=None):
21 QtGui.QWidget.__init__(self, parent)
22 self.gui = Ui_MainWindow()
23 self.gui.setupUi(self)
25 self.block_length = options.block_length
26 self.start = options.start
27 self.sample_rate = options.sample_rate
28 self.psdfftsize = options.psd_size
29 self.specfftsize = options.spec_size
30 self.winfunc = scipy.blackman
32 self.datatype = scipy.complex64
36 # Set up basic plot attributes
37 self.gui.timePlot.setAxisTitle(self.gui.timePlot.xBottom, "Time (sec)")
38 self.gui.timePlot.setAxisTitle(self.gui.timePlot.yLeft, "Amplitude (V)")
39 self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.xBottom, "Frequency (Hz)")
40 self.gui.freqPlot.setAxisTitle(self.gui.freqPlot.yLeft, "Magnitude (dB)")
42 # Set up FFT size combo box
43 self.gui.fftComboBox.addItems(["128", "256", "512", "1024", "2048",
44 "4096", "8192", "16384", "32768"])
45 pos = self.gui.fftComboBox.findText(Qt.QString("%1").arg(self.psdfftsize))
46 self.gui.fftComboBox.setCurrentIndex(pos)
47 self.connect(self.gui.fftComboBox,
48 Qt.SIGNAL("activated (const QString&)"),
51 # Set up color scheme box
52 self.color_modes = {"Black on White" : self.color_black_on_white,
53 "White on Black" : self.color_white_on_black,
54 "Blue on Black" : self.color_blue_on_black,
55 "Green on Black" : self.color_green_on_black}
56 self.gui.colorComboBox.addItems(self.color_modes.keys())
57 pos = self.gui.colorComboBox.findText("Blue on Black")
58 self.gui.colorComboBox.setCurrentIndex(pos)
59 self.connect(self.gui.colorComboBox,
60 Qt.SIGNAL("activated (const QString&)"),
61 self.colorComboBoxEdit)
64 # Create zoom functionality for the plots
65 self.timeZoomer = Qwt.QwtPlotZoomer(self.gui.timePlot.xBottom,
66 self.gui.timePlot.yLeft,
67 Qwt.QwtPicker.PointSelection,
68 Qwt.QwtPicker.AlwaysOn,
69 self.gui.timePlot.canvas())
71 self.freqZoomer = Qwt.QwtPlotZoomer(self.gui.freqPlot.xBottom,
72 self.gui.freqPlot.yLeft,
73 Qwt.QwtPicker.PointSelection,
74 Qwt.QwtPicker.AlwaysOn,
75 self.gui.freqPlot.canvas())
77 self.picker = Qwt.QwtPlotPicker(self.gui.timePlot.xBottom,
78 self.gui.timePlot.yLeft,
79 Qwt.QwtPicker.PointSelection,
80 Qwt.QwtPlotPicker.CrossRubberBand,
81 Qwt.QwtPicker.AlwaysOn,
82 self.gui.timePlot.canvas())
83 self.picker.connect(self.picker,
84 Qt.SIGNAL('selected(const QwtDoublePoint&)'),
87 # Set up action when tab is changed
88 self.connect(self.gui.tabGroup,
89 Qt.SIGNAL("currentChanged (int)"),
92 # Add a legend to the Time plot
93 legend_real = Qwt.QwtLegend()
94 self.gui.timePlot.insertLegend(legend_real)
97 self.gui.plotHBar.setSingleStep(1)
98 self.gui.plotHBar.setPageStep(self.block_length)
99 self.gui.plotHBar.setMinimum(0)
100 self.gui.plotHBar.setMaximum(self.block_length)
101 self.connect(self.gui.plotHBar,
102 Qt.SIGNAL("valueChanged(int)"),
105 # Connect Open action to Open Dialog box
106 self.connect(self.gui.action_open,
107 Qt.SIGNAL("activated()"),
110 # Set up file position boxes to update current figure
111 self.connect(self.gui.filePosStartLineEdit,
112 Qt.SIGNAL("editingFinished()"),
113 self.file_position_changed)
114 self.connect(self.gui.filePosStopLineEdit,
115 Qt.SIGNAL("editingFinished()"),
116 self.file_position_changed)
117 self.connect(self.gui.filePosLengthLineEdit,
118 Qt.SIGNAL("editingFinished()"),
119 self.file_length_changed)
121 self.connect(self.gui.fileTimeStartLineEdit,
122 Qt.SIGNAL("editingFinished()"),
123 self.file_time_changed)
124 self.connect(self.gui.fileTimeStopLineEdit,
125 Qt.SIGNAL("editingFinished()"),
126 self.file_time_changed)
127 self.connect(self.gui.fileTimeLengthLineEdit,
128 Qt.SIGNAL("editingFinished()"),
129 self.file_time_length_changed)
131 self.rcurve = Qwt.QwtPlotCurve("Real")
132 self.icurve = Qwt.QwtPlotCurve("Imaginary")
134 self.icurve.attach(self.gui.timePlot)
135 self.rcurve.attach(self.gui.timePlot)
137 self.psdcurve = Qwt.QwtPlotCurve("PSD")
138 self.psdcurve.attach(self.gui.freqPlot)
140 # Set up initial color scheme
141 self.color_modes["Blue on Black"]()
143 self.set_sample_rate(self.sample_rate)
144 self.connect(self.gui.sampleRateLineEdit,
145 Qt.SIGNAL("editingFinished()"),
146 self.sample_rate_changed)
148 if(filename is not None):
149 self.initialize(filename)
154 filename = Qt.QFileDialog.getOpenFileName(self, "Open", ".")
157 self.initialize(filename)
159 def initialize(self, filename):
160 self.hfile = open(filename, "r")
162 self.setWindowTitle(("GNU Radio File Plot Utility: %s" % filename))
164 self.gui.filePosStartLineEdit.setText("0")
165 self.gui.filePosStopLineEdit.setText("0")
166 self.gui.fileTimeStartLineEdit.setText("0")
167 self.gui.fileTimeStopLineEdit.setText("0")
170 self.cur_stop = self.block_length
172 self.init_data_input()
173 self.get_data(self.cur_start, self.cur_stop)
175 self.gui.plotHBar.setSliderPosition(0)
176 self.gui.plotHBar.setMaximum(self.signal_size)
179 self.update_time_curves()
180 self.update_psd_curves()
182 def init_data_input(self):
183 self.hfile.seek(0, os.SEEK_END)
184 self.signal_size = self.hfile.tell()/self.sizeof_data
185 print "Sizeof File: ", self.signal_size
186 self.hfile.seek(0, os.SEEK_SET)
188 def get_data(self, start, end):
190 self.hfile.seek(start*self.sizeof_data, os.SEEK_SET)
191 self.position = start
193 iq = scipy.fromfile(self.hfile, dtype=self.datatype,
196 if(len(iq) < (end-start)):
198 self.gui.filePosLengthLineEdit.setText(Qt.QString("%1").arg(end))
199 self.gui.plotHBar.setMaximum(end)
200 self.gui.plotHBar.setSingleStep(end)
201 self.file_length_changed()
203 tstep = 1.0 / self.sample_rate
205 self.time = [tstep*(self.position + i) for i in xrange(len(self.iq))]
207 self.set_file_pos_box(start, end)
211 # Do we want to do anything about this?
215 winpoints = self.winfunc(self.psdfftsize)
216 iq_psd, freq = mlab.psd(self.iq, Fs=self.sample_rate,
217 NFFT=self.psdfftsize,
218 noverlap=self.psdfftsize/4.0,
221 self.iq_psd = 10.0*scipy.log10(abs(fftpack.fftshift(iq_psd)))
222 self.freq = freq - self.sample_rate/2.0
225 def clickMe(self, qPoint):
228 def fftComboBoxEdit(self, fftSize):
229 self.psdfftsize = fftSize.toInt()[0]
231 self.update_psd_curves()
233 def colorComboBoxEdit(self, colorSelection):
234 colorstr = str(colorSelection.toAscii())
235 color_func = self.color_modes[colorstr]
238 def sliderMoved(self, value):
240 pos_end = value + self.gui.plotHBar.pageStep()
242 self.get_data(pos_start, pos_end)
244 self.update_time_curves()
245 self.update_psd_curves()
247 def set_sample_rate(self, sr):
248 self.sample_rate = sr
249 srstr = eng_notation.num_to_str(self.sample_rate)
250 self.gui.sampleRateLineEdit.setText(Qt.QString("%1").arg(srstr))
252 def sample_rate_changed(self):
253 srstr = self.gui.sampleRateLineEdit.text().toAscii()
254 self.sample_rate = eng_notation.str_to_num(srstr)
255 self.set_file_pos_box(self.cur_start, self.cur_stop)
256 self.get_data(self.cur_start, self.cur_stop)
258 self.update_time_curves()
259 self.update_psd_curves()
261 def set_file_pos_box(self, start, end):
262 tstart = start / self.sample_rate
263 tend = end / self.sample_rate
265 self.gui.filePosStartLineEdit.setText(Qt.QString("%1").arg(start))
266 self.gui.filePosStopLineEdit.setText(Qt.QString("%1").arg(end))
267 self.gui.filePosLengthLineEdit.setText(Qt.QString("%1").arg(end-start))
269 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
270 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
271 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tend-tstart))
273 def file_position_changed(self):
274 start = self.gui.filePosStartLineEdit.text().toInt()
275 end = self.gui.filePosStopLineEdit.text().toInt()
276 if((start[1] == True) and (end[1] == True)):
277 self.cur_start = start[0]
278 self.cur_stop = end[0]
280 tstart = self.cur_start / self.sample_rate
281 tend = self.cur_stop / self.sample_rate
282 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
283 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
285 self.get_data(self.cur_start, self.cur_stop)
287 self.update_time_curves()
288 self.update_psd_curves()
290 # If there's a non-digit character, reset box
292 self.set_file_pos_box(self.cur_start, self.cur_stop)
294 def file_time_changed(self):
295 tstart = self.gui.fileTimeStartLineEdit.text().toDouble()
296 tstop = self.gui.fileTimeStopLineEdit.text().toDouble()
297 if((tstart[1] == True) and (tstop[1] == True)):
298 self.cur_start = int(tstart[0] * self.sample_rate)
299 self.cur_stop = int(tstop[0] * self.sample_rate)
300 self.get_data(self.cur_start, self.cur_stop)
302 self.gui.filePosStartLineEdit.setText(Qt.QString("%1").arg(self.cur_start))
303 self.gui.filePosStopLineEdit.setText(Qt.QString("%1").arg(self.cur_stop))
305 self.update_time_curves()
306 self.update_psd_curves()
307 # If there's a non-digit character, reset box
309 self.set_file_pos_box(self.cur_start, self.cur_stop)
311 def file_length_changed(self):
312 start = self.gui.filePosStartLineEdit.text().toInt()
313 length = self.gui.filePosLengthLineEdit.text().toInt()
315 if((start[1] == True) and (length[1] == True)):
316 self.cur_start = start[0]
317 self.block_length = length[0]
318 self.cur_stop = self.cur_start + self.block_length
320 tstart = self.cur_start / self.sample_rate
321 tend = self.cur_stop / self.sample_rate
322 tlen = self.block_length / self.sample_rate
323 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
324 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
325 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tlen))
327 self.gui.plotHBar.setPageStep(self.block_length)
329 self.get_data(self.cur_start, self.cur_stop)
332 self.update_time_curves()
333 self.update_psd_curves()
334 # If there's a non-digit character, reset box
336 self.set_file_pos_box(self.cur_start, self.cur_stop)
338 def file_time_length_changed(self):
339 tstart = self.gui.fileTimeStartLineEdit.text().toDouble()
340 tlength = self.gui.fileTimeLengthLineEdit.text().toDouble()
341 if((tstart[1] == True) and (tlength[1] == True)):
342 self.cur_start = int(tstart[0] * self.sample_rate)
343 self.block_length = int(tlength[0] * self.sample_rate)
344 self.cur_stop = self.cur_start + self.block_length
346 tstart = self.cur_start / self.sample_rate
347 tend = self.cur_stop / self.sample_rate
348 tlen = self.block_length / self.sample_rate
349 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
350 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
351 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tlen))
353 self.get_data(self.cur_start, self.cur_stop)
356 self.update_time_curves()
357 self.update_psd_curves()
358 # If there's a non-digit character, reset box
360 self.set_file_pos_box(self.cur_start, self.cur_stop)
363 def update_time_curves(self):
364 self.icurve.setData(self.time, self.iq.imag)
365 self.rcurve.setData(self.time, self.iq.real)
367 # Reset the x-axis to the new time scale
368 iqmax = 1.5 * max(max(self.iq.real), max(self.iq.imag))
369 iqmin = 1.5 * min(min(self.iq.real), min(self.iq.imag))
370 self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
373 self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
377 # Set the zoomer base to unzoom to the new axis
378 self.timeZoomer.setZoomBase()
380 self.gui.timePlot.replot()
382 def update_psd_curves(self):
383 self.psdcurve.setData(self.freq, self.iq_psd)
385 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
389 # Set the zoomer base to unzoom to the new axis
390 self.freqZoomer.setZoomBase()
392 self.gui.freqPlot.replot()
394 def tabChanged(self, index):
395 self.gui.timePlot.replot()
396 self.gui.freqPlot.replot()
398 def color_black_on_white(self):
399 blue = QtGui.qRgb(0x00, 0x00, 0xFF)
400 red = QtGui.qRgb(0xFF, 0x00, 0x00)
402 blackBrush = Qt.QBrush(Qt.QColor("black"))
403 blueBrush = Qt.QBrush(Qt.QColor(blue))
404 redBrush = Qt.QBrush(Qt.QColor(red))
406 self.gui.timePlot.setCanvasBackground(Qt.QColor("white"))
407 self.gui.freqPlot.setCanvasBackground(Qt.QColor("white"))
408 self.picker.setTrackerPen(Qt.QPen(blackBrush, 2))
409 self.timeZoomer.setTrackerPen(Qt.QPen(blackBrush, 2))
410 self.timeZoomer.setRubberBandPen(Qt.QPen(blackBrush, 2))
411 self.freqZoomer.setTrackerPen(Qt.QPen(blackBrush, 2))
412 self.freqZoomer.setRubberBandPen(Qt.QPen(blackBrush, 2))
413 self.psdcurve.setPen(Qt.QPen(blueBrush, 1))
414 self.rcurve.setPen(Qt.QPen(blueBrush, 2))
415 self.icurve.setPen(Qt.QPen(redBrush, 2))
417 self.gui.timePlot.replot()
418 self.gui.freqPlot.replot()
420 def color_white_on_black(self):
421 white = QtGui.qRgb(0xFF, 0xFF, 0xFF)
422 red = QtGui.qRgb(0xFF, 0x00, 0x00)
424 whiteBrush = Qt.QBrush(Qt.QColor("white"))
425 whiteBrush = Qt.QBrush(Qt.QColor(white))
426 redBrush = Qt.QBrush(Qt.QColor(red))
428 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
429 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
430 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
431 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
432 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
433 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
434 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
435 self.psdcurve.setPen(Qt.QPen(whiteBrush, 1))
436 self.rcurve.setPen(Qt.QPen(whiteBrush, 2))
437 self.icurve.setPen(Qt.QPen(redBrush, 2))
439 self.gui.timePlot.replot()
440 self.gui.freqPlot.replot()
443 def color_green_on_black(self):
444 green = QtGui.qRgb(0x00, 0xFF, 0x00)
445 red = QtGui.qRgb(0xFF, 0x00, 0x50)
447 whiteBrush = Qt.QBrush(Qt.QColor("white"))
448 greenBrush = Qt.QBrush(Qt.QColor(green))
449 redBrush = Qt.QBrush(Qt.QColor(red))
451 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
452 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
453 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
454 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
455 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
456 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
457 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
458 self.psdcurve.setPen(Qt.QPen(greenBrush, 1))
459 self.rcurve.setPen(Qt.QPen(greenBrush, 2))
460 self.icurve.setPen(Qt.QPen(redBrush, 2))
462 self.gui.timePlot.replot()
463 self.gui.freqPlot.replot()
465 def color_blue_on_black(self):
466 blue = QtGui.qRgb(0x00, 0x00, 0xFF)
467 red = QtGui.qRgb(0xFF, 0x00, 0x00)
469 whiteBrush = Qt.QBrush(Qt.QColor("white"))
470 blueBrush = Qt.QBrush(Qt.QColor(blue))
471 redBrush = Qt.QBrush(Qt.QColor(red))
473 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
474 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
475 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
476 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
477 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
478 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
479 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
480 self.psdcurve.setPen(Qt.QPen(blueBrush, 1))
481 self.rcurve.setPen(Qt.QPen(blueBrush, 2))
482 self.icurve.setPen(Qt.QPen(redBrush, 2))
484 self.gui.timePlot.replot()
485 self.gui.freqPlot.replot()
488 usage="%prog: [options] (input_filename)"
491 parser = OptionParser(conflict_handler="resolve", usage=usage, description=description)
492 parser.add_option("-B", "--block-length", type="int", default=8192,
493 help="Specify the block size [default=%default]")
494 parser.add_option("-s", "--start", type="int", default=0,
495 help="Specify where to start in the file [default=%default]")
496 parser.add_option("-R", "--sample-rate", type="float", default=1.0,
497 help="Set the sampler rate of the data [default=%default]")
498 parser.add_option("", "--psd-size", type="int", default=2048,
499 help="Set the size of the PSD FFT [default=%default]")
500 parser.add_option("", "--spec-size", type="int", default=256,
501 help="Set the size of the spectrogram FFT [default=%default]")
506 parser = setup_options()
507 (options, args) = parser.parse_args ()
514 app = Qt.QApplication(args)
515 gplt = gr_plot_qt(app, filename, options)
518 if __name__ == '__main__':