diff options
Diffstat (limited to 'gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py')
-rw-r--r-- | gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py new file mode 100644 index 0000000000..8597ca6497 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py @@ -0,0 +1,428 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import blocks +from gnuradio import filter +import sys, time + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4 and gr-qtgui." + sys.exit(1) + +class GrDataPlotParent(gr.top_block, QtGui.QWidget): + # Setup signals + plotupdated = QtCore.pyqtSignal(QtGui.QWidget) + + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + QtGui.QWidget.__init__(self, None) + + self._name = name + self._npts = 500 + self._rate = rate + self.knobnames = [name,] + + self.layout = QtGui.QVBoxLayout() + self.setLayout(self.layout) + + self.setAcceptDrops(True) + + def _setup(self, nconnections): + self.stop() + self.wait() + + if(self.layout.count() > 0): + # Remove and disconnect. Making sure no references to snk + # remain so that the plot gets deleted. + self.layout.removeWidget(self.py_window) + self.disconnect(self.thr, (self.snk, 0)) + self.disconnect(self.src[0], self.thr) + for n in xrange(1, self._ncons): + self.disconnect(self.src[n], (self.snk,n)) + + self._ncons = nconnections + self._data_len = self._ncons*[0,] + + self.thr = blocks.throttle(self._datasize, self._rate) + self.snk = self.get_qtsink() + + self.connect(self.thr, (self.snk, 0)) + + self._last_data = [] + self.src = [] + for n in xrange(self._ncons): + self.set_line_label(n, self.knobnames[n]) + + self._last_data.append(int(self._npts)*[0,]) + self.src.append(self.get_vecsource()) + + if(n == 0): + self.connect(self.src[n], self.thr) + else: + self.connect(self.src[n], (self.snk,n)) + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + self.layout.addWidget(self.py_window) + + def __del__(self): + pass + + def close(self): + self.snk.close() + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def semilogy(self, en=True): + self.snk.enable_semilogy(en) + + def dragEnterEvent(self, e): + e.acceptProposedAction() + + def dropEvent(self, e): + if(e.mimeData().hasFormat("text/plain")): + data = str(e.mimeData().text()) + + #"PlotData:{0}:{1}".format(tag, iscomplex) + datalst = data.split(":::") + tag = datalst[0] + name = datalst[1] + cpx = datalst[2] != "0" + + if(tag == "PlotData" and cpx == self._iscomplex): + self.knobnames.append(name) + + # create a new qwidget plot with the new data stream. + self._setup(len(self.knobnames)) + + # emit that this plot has been updated with a new qwidget. + self.plotupdated.emit(self) + + e.acceptProposedAction() + + def data_to_complex(self, data): + if(self._iscomplex): + data_r = data[0::2] + data_i = data[1::2] + data = [complex(r,i) for r,i in zip(data_r, data_i)] + return data + + def update(self, data): + # Ask GUI if there has been a change in nsamps + npts = self.get_npts() + if(self._npts != npts): + + # Adjust buffers to accomodate new settings + for n in xrange(self._ncons): + if(npts < self._npts): + if(self._data_len[n] < npts): + self._last_data[n] = self._last_data[n][0:npts] + else: + self._last_data[n] = self._last_data[n][self._data_len[n]-npts:self._data_len[n]] + self._data_len[n] = npts + else: + self._last_data[n] += (npts - self._npts)*[0,] + self._npts = npts + self.snk.reset() + + if(self._stripchart): + # Update the plot data depending on type + for n in xrange(self._ncons): + if(type(data[n]) == list): + data[n] = self.data_to_complex(data[n]) + if(len(data[n]) > self._npts): + self.src[n].set_data(data[n]) + self._last_data[n] = data[n][-self._npts:] + else: + newdata = self._last_data[n][-(self._npts-len(data)):] + newdata += data[n] + self.src[n].set_data(newdata) + self._last_data[n] = newdata + + else: # single value update + if(self._iscomplex): + data[n] = complex(data[n][0], data[n][1]) + if(self._data_len[n] < self._npts): + self._last_data[n][self._data_len[n]] = data[n] + self._data_len[n] += 1 + else: + self._last_data[n] = self._last_data[n][1:] + self._last_data[n].append(data[n]) + self.src[n].set_data(self._last_data[n]) + else: + for n in xrange(self._ncons): + if(type(data[n]) != list): + data[n] = [data[n],] + data[n] = self.data_to_complex(data[n]) + self.src[n].set_data(data[n]) + + + +class GrDataPlotterC(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None, stripchart=False): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._stripchart = stripchart + self._datasize = gr.sizeof_gr_complex + self._iscomplex = True + + self._setup(1) + + def stem(self, en=True): + self.snk.enable_stem_plot(en) + + def get_qtsink(self): + snk = qtgui.time_sink_c(self._npts, 1.0, + self._name, self._ncons) + snk.enable_autoscale(True) + return snk + + def get_vecsource(self): + return blocks.vector_source_c([]) + + def get_npts(self): + self._npts = self.snk.nsamps() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(2*n+0, "Re{" + self.knobnames[n] + "}") + self.snk.set_line_label(2*n+1, "Im{" + self.knobnames[n] + "}") + + +class GrDataPlotterF(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None, stripchart=False): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._stripchart = stripchart + self._datasize = gr.sizeof_float + self._iscomplex = False + + self._setup(1) + + def stem(self, en=True): + self.snk.enable_stem_plot(en) + + def get_qtsink(self): + snk = qtgui.time_sink_f(self._npts, 1.0, + self._name, self._ncons) + snk.enable_autoscale(True) + return snk + + def get_vecsource(self): + return blocks.vector_source_f([]) + + def get_npts(self): + self._npts = self.snk.nsamps() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + + +class GrDataPlotterConst(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._datasize = gr.sizeof_gr_complex + self._stripchart = False + self._iscomplex = True + + self._setup(1) + + def get_qtsink(self): + snk = qtgui.const_sink_c(self._npts, + self._name, + self._ncons) + snk.enable_autoscale(True) + return snk + + def get_vecsource(self): + return blocks.vector_source_c([]) + + def get_npts(self): + self._npts = self.snk.nsamps() + return self._npts + + def scatter(self, en=True): + if(en): + self.snk.set_line_style(0, 0) + else: + self.snk.set_line_style(0, 1) + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + + +class GrDataPlotterPsdC(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._datasize = gr.sizeof_gr_complex + self._stripchart = True + self._iscomplex = True + + self._npts = 2048 + self._wintype = filter.firdes.WIN_BLACKMAN_hARRIS + self._fc = 0 + + self._setup(1) + + def get_qtsink(self): + snk = qtgui.freq_sink_c(self._npts, self._wintype, + self._fc, 1.0, + self._name, + self._ncons) + snk.enable_autoscale(True) + return snk + + def get_vecsource(self): + return blocks.vector_source_c([]) + + def get_npts(self): + self._npts = self.snk.fft_size() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + + +class GrDataPlotterPsdF(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._datasize = gr.sizeof_float + self._stripchart = True + self._iscomplex = False + + self._npts = 2048 + self._wintype = filter.firdes.WIN_BLACKMAN_hARRIS + self._fc = 0 + + self._setup(1) + + def get_qtsink(self): + snk = qtgui.freq_sink_f(self._npts, self._wintype, + self._fc, 1.0, + self._name, + self._ncons) + snk.enable_autoscale(True) + return snk + + def get_vecsource(self): + return blocks.vector_source_f([]) + + def get_npts(self): + self._npts = self.snk.fft_size() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + + +class GrTimeRasterF(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._npts = 10 + self._rows = 40 + + self._datasize = gr.sizeof_float + self._stripchart = False + self._iscomplex = False + + self._setup(1) + + def get_qtsink(self): + snk = qtgui.time_raster_sink_f(1.0, self._npts, self._rows, + [], [], self._name, + self._ncons) + return snk + + def get_vecsource(self): + return blocks.vector_source_f([]) + + def get_npts(self): + self._npts = self.snk.num_cols() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + +class GrTimeRasterB(GrDataPlotParent): + def __init__(self, name, rate, pmin=None, pmax=None): + GrDataPlotParent.__init__(self, name, rate, pmin, pmax) + + self._npts = 10 + self._rows = 40 + + self._datasize = gr.sizeof_char + self._stripchart = False + self._iscomplex = False + + self._setup(1) + + def get_qtsink(self): + snk = qtgui.time_raster_sink_b(1.0, self._npts, self._rows, + [], [], self._name, + self._ncons) + return snk + + def get_vecsource(self): + return blocks.vector_source_b([]) + + def get_npts(self): + self._npts = self.snk.num_cols() + return self._npts + + def set_line_label(self, n, name): + self.snk.set_line_label(n, self.knobnames[n]) + + +class GrDataPlotterValueTable: + def __init__(self, uid, parent, x, y, xsize, ysize, + headers=['Statistic Key ( Source Block :: Stat Name ) ', + 'Curent Value', 'Units', 'Description']): + # must encapsulate, cuz Qt's bases are not classes + self.uid = uid + self.treeWidget = QtGui.QTreeWidget(parent) + self.treeWidget.setColumnCount(len(headers)) + self.treeWidget.setGeometry(x,y,xsize,ysize) + self.treeWidget.setHeaderLabels(headers) + self.treeWidget.resizeColumnToContents(0) + + def updateItems(self, knobs, knobprops): + items = []; + self.treeWidget.clear() + for k, v in knobs.iteritems(): + items.append(QtGui.QTreeWidgetItem([str(k), str(v.value), + knobprops[k].units, + knobprops[k].description])) + self.treeWidget.insertTopLevelItems(0, items) |