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", ".")
156 self.initialize(filename)
158 def initialize(self, filename):
159 self.hfile = open(filename, "r")
161 self.setWindowTitle(("GNU Radio File Plot Utility: %s" % filename))
164 self.cur_stop = self.block_length
166 self.init_data_input()
167 self.get_data(self.cur_start, self.cur_stop)
169 self.gui.plotHBar.setSliderPosition(0)
170 self.gui.plotHBar.setMaximum(self.signal_size)
172 self.update_time_curves()
173 self.update_psd_curves()
175 def init_data_input(self):
176 self.hfile.seek(0, os.SEEK_END)
177 self.signal_size = self.hfile.tell()/self.sizeof_data
178 print "Sizeof File: ", self.signal_size
179 self.hfile.seek(0, os.SEEK_SET)
181 def get_data(self, start, end):
183 self.hfile.seek(start*self.sizeof_data, os.SEEK_SET)
184 self.position = start
185 iq = scipy.fromfile(self.hfile, dtype=self.datatype,
187 if(len(iq) < (end-start)):
190 tstep = 1.0 / self.sample_rate
192 self.time = [tstep*(self.position + i) for i in xrange(len(self.iq))]
194 self.set_file_pos_box(start, end)
196 # Do we want to do anything about this?
200 winpoints = self.winfunc(self.psdfftsize)
201 iq_psd, freq = mlab.psd(self.iq, Fs=self.sample_rate,
202 NFFT=self.psdfftsize,
203 noverlap=self.psdfftsize/4.0,
206 self.iq_psd = 10.0*scipy.log10(abs(fftpack.fftshift(iq_psd)))
207 self.freq = freq - self.sample_rate/2.0
210 def clickMe(self, qPoint):
213 def fftComboBoxEdit(self, fftSize):
214 self.psdfftsize = fftSize.toInt()[0]
216 self.update_psd_curves()
218 def colorComboBoxEdit(self, colorSelection):
219 colorstr = str(colorSelection.toAscii())
220 color_func = self.color_modes[colorstr]
223 def sliderMoved(self, value):
225 pos_end = value + self.gui.plotHBar.pageStep()
227 self.get_data(pos_start, pos_end)
229 self.update_time_curves()
230 self.update_psd_curves()
232 def set_sample_rate(self, sr):
233 self.sample_rate = sr
234 srstr = eng_notation.num_to_str(self.sample_rate)
235 self.gui.sampleRateLineEdit.setText(Qt.QString("%1").arg(srstr))
237 def sample_rate_changed(self):
238 srstr = self.gui.sampleRateLineEdit.text().toAscii()
239 self.sample_rate = eng_notation.str_to_num(srstr)
240 self.set_file_pos_box(self.cur_start, self.cur_stop)
241 self.get_data(self.cur_start, self.cur_stop)
243 self.update_time_curves()
244 self.update_psd_curves()
246 def set_file_pos_box(self, start, end):
247 tstart = start / self.sample_rate
248 tend = end / self.sample_rate
250 self.gui.filePosStartLineEdit.setText(Qt.QString("%1").arg(start))
251 self.gui.filePosStopLineEdit.setText(Qt.QString("%1").arg(end))
252 self.gui.filePosLengthLineEdit.setText(Qt.QString("%1").arg(end-start))
254 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
255 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
256 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tend-tstart))
258 def file_position_changed(self):
259 start = self.gui.filePosStartLineEdit.text().toInt()
260 end = self.gui.filePosStopLineEdit.text().toInt()
261 if((start[1] == True) and (end[1] == True)):
262 self.cur_start = start[0]
263 self.cur_stop = end[0]
265 tstart = self.cur_start / self.sample_rate
266 tend = self.cur_stop / self.sample_rate
267 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
268 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
270 self.get_data(self.cur_start, self.cur_stop)
272 self.update_time_curves()
273 self.update_psd_curves()
275 # If there's a non-digit character, reset box
277 self.set_file_pos_box(self.cur_start, self.cur_stop)
279 def file_time_changed(self):
280 tstart = self.gui.fileTimeStartLineEdit.text().toDouble()
281 tstop = self.gui.fileTimeStopLineEdit.text().toDouble()
282 if((tstart[1] == True) and (tstop[1] == True)):
283 self.cur_start = int(tstart[0] * self.sample_rate)
284 self.cur_stop = int(tstop[0] * self.sample_rate)
285 self.get_data(self.cur_start, self.cur_stop)
287 self.gui.filePosStartLineEdit.setText(Qt.QString("%1").arg(self.cur_start))
288 self.gui.filePosStopLineEdit.setText(Qt.QString("%1").arg(self.cur_stop))
290 self.update_time_curves()
291 self.update_psd_curves()
292 # If there's a non-digit character, reset box
294 self.set_file_pos_box(self.cur_start, self.cur_stop)
296 def file_length_changed(self):
297 start = self.gui.filePosStartLineEdit.text().toInt()
298 length = self.gui.filePosLengthLineEdit.text().toInt()
299 if((start[1] == True) and (length[1] == True)):
300 self.cur_start = start[0]
301 self.block_length = length[0]
302 self.cur_stop = self.cur_start + self.block_length
304 tstart = self.cur_start / self.sample_rate
305 tend = self.cur_stop / self.sample_rate
306 tlen = self.block_length / self.sample_rate
307 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
308 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
309 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tlen))
311 self.gui.plotHBar.setPageStep(self.block_length)
313 self.get_data(self.cur_start, self.cur_stop)
316 self.update_time_curves()
317 self.update_psd_curves()
318 # If there's a non-digit character, reset box
320 self.set_file_pos_box(self.cur_start, self.cur_stop)
322 def file_time_length_changed(self):
323 tstart = self.gui.fileTimeStartLineEdit.text().toDouble()
324 tlength = self.gui.fileTimeLengthLineEdit.text().toDouble()
325 if((tstart[1] == True) and (tlength[1] == True)):
326 self.cur_start = int(tstart[0] * self.sample_rate)
327 self.block_length = int(tlength[0] * self.sample_rate)
328 self.cur_stop = self.cur_start + self.block_length
330 tstart = self.cur_start / self.sample_rate
331 tend = self.cur_stop / self.sample_rate
332 tlen = self.block_length / self.sample_rate
333 self.gui.fileTimeStartLineEdit.setText(Qt.QString("%1").arg(tstart))
334 self.gui.fileTimeStopLineEdit.setText(Qt.QString("%1").arg(tend))
335 self.gui.fileTimeLengthLineEdit.setText(Qt.QString("%1").arg(tlen))
337 self.get_data(self.cur_start, self.cur_stop)
340 self.update_time_curves()
341 self.update_psd_curves()
342 # If there's a non-digit character, reset box
344 self.set_file_pos_box(self.cur_start, self.cur_stop)
347 def update_time_curves(self):
348 self.icurve.setData(self.time, self.iq.imag)
349 self.rcurve.setData(self.time, self.iq.real)
351 # Reset the x-axis to the new time scale
352 iqmax = 1.5 * max(max(self.iq.real), max(self.iq.imag))
353 iqmin = 1.5 * min(min(self.iq.real), min(self.iq.imag))
354 self.gui.timePlot.setAxisScale(self.gui.timePlot.xBottom,
357 self.gui.timePlot.setAxisScale(self.gui.timePlot.yLeft,
361 # Set the zoomer base to unzoom to the new axis
362 self.timeZoomer.setZoomBase()
364 self.gui.timePlot.replot()
366 def update_psd_curves(self):
367 self.psdcurve.setData(self.freq, self.iq_psd)
369 self.gui.freqPlot.setAxisScale(self.gui.freqPlot.xBottom,
373 # Set the zoomer base to unzoom to the new axis
374 self.freqZoomer.setZoomBase()
376 self.gui.freqPlot.replot()
378 def tabChanged(self, index):
379 self.gui.timePlot.replot()
380 self.gui.freqPlot.replot()
382 def color_black_on_white(self):
383 blue = QtGui.qRgb(0x00, 0x00, 0xFF)
384 red = QtGui.qRgb(0xFF, 0x00, 0x00)
386 blackBrush = Qt.QBrush(Qt.QColor("black"))
387 blueBrush = Qt.QBrush(Qt.QColor(blue))
388 redBrush = Qt.QBrush(Qt.QColor(red))
390 self.gui.timePlot.setCanvasBackground(Qt.QColor("white"))
391 self.gui.freqPlot.setCanvasBackground(Qt.QColor("white"))
392 self.picker.setTrackerPen(Qt.QPen(blackBrush, 2))
393 self.timeZoomer.setTrackerPen(Qt.QPen(blackBrush, 2))
394 self.timeZoomer.setRubberBandPen(Qt.QPen(blackBrush, 2))
395 self.freqZoomer.setTrackerPen(Qt.QPen(blackBrush, 2))
396 self.freqZoomer.setRubberBandPen(Qt.QPen(blackBrush, 2))
397 self.psdcurve.setPen(Qt.QPen(blueBrush, 1))
398 self.rcurve.setPen(Qt.QPen(blueBrush, 2))
399 self.icurve.setPen(Qt.QPen(redBrush, 2))
401 self.gui.timePlot.replot()
402 self.gui.freqPlot.replot()
404 def color_white_on_black(self):
405 white = QtGui.qRgb(0xFF, 0xFF, 0xFF)
406 red = QtGui.qRgb(0xFF, 0x00, 0x00)
408 whiteBrush = Qt.QBrush(Qt.QColor("white"))
409 whiteBrush = Qt.QBrush(Qt.QColor(white))
410 redBrush = Qt.QBrush(Qt.QColor(red))
412 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
413 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
414 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
415 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
416 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
417 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
418 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
419 self.psdcurve.setPen(Qt.QPen(whiteBrush, 1))
420 self.rcurve.setPen(Qt.QPen(whiteBrush, 2))
421 self.icurve.setPen(Qt.QPen(redBrush, 2))
423 self.gui.timePlot.replot()
424 self.gui.freqPlot.replot()
427 def color_green_on_black(self):
428 green = QtGui.qRgb(0x00, 0xFF, 0x00)
429 red = QtGui.qRgb(0xFF, 0x00, 0x50)
431 whiteBrush = Qt.QBrush(Qt.QColor("white"))
432 greenBrush = Qt.QBrush(Qt.QColor(green))
433 redBrush = Qt.QBrush(Qt.QColor(red))
435 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
436 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
437 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
438 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
439 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
440 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
441 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
442 self.psdcurve.setPen(Qt.QPen(greenBrush, 1))
443 self.rcurve.setPen(Qt.QPen(greenBrush, 2))
444 self.icurve.setPen(Qt.QPen(redBrush, 2))
446 self.gui.timePlot.replot()
447 self.gui.freqPlot.replot()
449 def color_blue_on_black(self):
450 blue = QtGui.qRgb(0x00, 0x00, 0xFF)
451 red = QtGui.qRgb(0xFF, 0x00, 0x00)
453 whiteBrush = Qt.QBrush(Qt.QColor("white"))
454 blueBrush = Qt.QBrush(Qt.QColor(blue))
455 redBrush = Qt.QBrush(Qt.QColor(red))
457 self.gui.timePlot.setCanvasBackground(QtGui.QColor("black"))
458 self.gui.freqPlot.setCanvasBackground(QtGui.QColor("black"))
459 self.picker.setTrackerPen(Qt.QPen(whiteBrush, 2))
460 self.timeZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
461 self.timeZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
462 self.freqZoomer.setTrackerPen(Qt.QPen(whiteBrush, 2))
463 self.freqZoomer.setRubberBandPen(Qt.QPen(whiteBrush, 2))
464 self.psdcurve.setPen(Qt.QPen(blueBrush, 1))
465 self.rcurve.setPen(Qt.QPen(blueBrush, 2))
466 self.icurve.setPen(Qt.QPen(redBrush, 2))
468 self.gui.timePlot.replot()
469 self.gui.freqPlot.replot()
472 usage="%prog: [options] (input_filename)"
475 parser = OptionParser(conflict_handler="resolve", usage=usage, description=description)
476 parser.add_option("-B", "--block-length", type="int", default=8192,
477 help="Specify the block size [default=%default]")
478 parser.add_option("-s", "--start", type="int", default=0,
479 help="Specify where to start in the file [default=%default]")
480 parser.add_option("-R", "--sample-rate", type="float", default=1.0,
481 help="Set the sampler rate of the data [default=%default]")
482 parser.add_option("", "--psd-size", type="int", default=2048,
483 help="Set the size of the PSD FFT [default=%default]")
484 parser.add_option("", "--spec-size", type="int", default=256,
485 help="Set the size of the spectrogram FFT [default=%default]")
490 parser = setup_options()
491 (options, args) = parser.parse_args ()
498 app = Qt.QApplication(args)
499 gplt = gr_plot_qt(app, filename, options)
502 if __name__ == '__main__':