#!/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)