diff options
Diffstat (limited to 'gr-qtgui')
117 files changed, 12427 insertions, 3050 deletions
diff --git a/gr-qtgui/CMakeLists.txt b/gr-qtgui/CMakeLists.txt index f6260bc7e9..be35beeec2 100644 --- a/gr-qtgui/CMakeLists.txt +++ b/gr-qtgui/CMakeLists.txt @@ -46,7 +46,11 @@ GR_REGISTER_COMPONENT("gr-qtgui" ENABLE_GR_QTGUI Boost_FOUND QT4_FOUND QWT_FOUND + ENABLE_GRUEL + ENABLE_VOLK ENABLE_GR_CORE + ENABLE_GR_FFT + ENABLE_GR_FILTER PYTHONLIBS_FOUND ${qt_gui_python_deps} ) @@ -103,7 +107,7 @@ CPACK_COMPONENT("qtgui_swig" ######################################################################## # Add subdirectories ######################################################################## -add_subdirectory(include) +add_subdirectory(include/qtgui) add_subdirectory(lib) add_subdirectory(doc) if(ENABLE_PYTHON) @@ -111,6 +115,7 @@ if(ENABLE_PYTHON) add_subdirectory(swig) add_subdirectory(python) add_subdirectory(examples) + add_subdirectory(apps) endif(ENABLE_PYTHON) ######################################################################## diff --git a/gr-qtgui/apps/CMakeLists.txt b/gr-qtgui/apps/CMakeLists.txt new file mode 100644 index 0000000000..07a8298701 --- /dev/null +++ b/gr-qtgui/apps/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright 2012 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. + +include(GrPython) + +GR_PYTHON_INSTALL( + FILES + plot_form.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/qtgui + COMPONENT "qtgui_python" +) + +GR_PYTHON_INSTALL( + PROGRAMS + gr_time_plot_c + gr_time_plot_f + gr_time_plot_i + gr_time_plot_s + gr_time_plot_b + gr_psd_plot_c + gr_psd_plot_f + gr_psd_plot_i + gr_psd_plot_s + gr_psd_plot_b + gr_spectrogram_plot_c + gr_spectrogram_plot_f + gr_spectrogram_plot_i + gr_spectrogram_plot_s + gr_spectrogram_plot_b + gr_constellation_plot + DESTINATION ${GR_RUNTIME_DIR} + COMPONENT "qtgui_python" +) diff --git a/gr-qtgui/apps/gr_constellation_plot b/gr-qtgui/apps/gr_constellation_plot new file mode 100755 index 0000000000..66d227ecc9 --- /dev/null +++ b/gr-qtgui/apps/gr_constellation_plot @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_gr_complex, 0) + data = scipy.fromfile(fhandle, dtype=scipy.complex64, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, start, nsamples, max_nsamples): + gr.top_block.__init__(self) + + self._filelist = filelist + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_gr_complex, self._start) + self.gui_snk = qtgui.const_sink_c(self._nsamps, + "GNU Radio Constellation Plot", + self._nsigs) + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_c(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, (self.gui_snk, i+1)) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots the constellations of a list of files." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_gr_complex) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.start, nsamples, max_nsamples); + + main_box = dialog_box(tb, 'GNU Radio Constellation Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_psd_plot_b b/gr-qtgui/apps/gr_psd_plot_b new file mode 100755 index 0000000000..e3e86a271f --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_b @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_char, 0) + data = scipy.fromfile(fhandle, dtype=scipy.uint8, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.uint8(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.freq_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_b(data)) + self.cnvrt.append(gr.char_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of chars/bytes." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(8-1)-1, + help="Set a scaling factor for the char->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_char) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average) + + main_box = dialog_box(tb, 'GNU Radio PSD Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_psd_plot_c b/gr-qtgui/apps/gr_psd_plot_c new file mode 100755 index 0000000000..faa0fc60a4 --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_c @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_gr_complex, 0) + data = scipy.fromfile(fhandle, dtype=scipy.complex64, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [complex(0,0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_gr_complex, self._start) + self.gui_snk = qtgui.freq_sink_c(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_c(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of complex floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_gr_complex) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.average); + + main_box = dialog_box(tb, 'GNU Radio PSD Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_psd_plot_f b/gr-qtgui/apps/gr_psd_plot_f new file mode 100755 index 0000000000..326618f848 --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_f @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_float, 0) + data = scipy.fromfile(fhandle, dtype=scipy.float32, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.float32(0.0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.freq_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_f(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_float) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.average); + + main_box = dialog_box(tb, 'GNU Radio PSD Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_psd_plot_i b/gr-qtgui/apps/gr_psd_plot_i new file mode 100755 index 0000000000..a0264ac1c9 --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_i @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_int, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int32, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.int32(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.freq_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_i(data)) + self.cnvrt.append(gr.int_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of integers." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(32-1)-1, + help="Set a scaling factor for the int->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_int) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average) + + main_box = dialog_box(tb, 'GNU Radio PSD Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_psd_plot_s b/gr-qtgui/apps/gr_psd_plot_s new file mode 100755 index 0000000000..6852cf7d0f --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_s @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_short, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int16, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.int16(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.freq_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_s(data)) + self.cnvrt.append(gr.short_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of shorts." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(16-1)-1, + help="Set a scaling factor for the short->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_short) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average) + + main_box = dialog_box(tb, 'GNU Radio PSD Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_spectrogram_plot_b b/gr-qtgui/apps/gr_spectrogram_plot_b new file mode 100755 index 0000000000..41c1af521a --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_b @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_char, 0) + data = scipy.fromfile(fhandle, dtype=scipy.uint8, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.uint8(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.add = gr.add_ff() + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot") + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_b(data)) + self.cnvrt.append(gr.char_to_float(1, self._scale)) + n += 1 + + self.connect(self.add, self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs): + self.connect(s, self.cnvrt[i], (self.add, i)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of chars/bytes." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(8-1)-1, + help="Set a scaling factor for the char->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_char) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average); + + main_box = dialog_box(tb, 'GNU Radio Spectrogram Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_spectrogram_plot_c b/gr-qtgui/apps/gr_spectrogram_plot_c new file mode 100755 index 0000000000..28fa82f298 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_c @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_gr_complex, 0) + data = scipy.fromfile(fhandle, dtype=scipy.complex64, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [complex(0,0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_gr_complex, self._start) + self.add = gr.add_cc() + self.gui_snk = qtgui.waterfall_sink_c(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot") + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_c(data)) + n += 1 + + self.connect(self.add, self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs): + self.connect(s, (self.add, i)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of complex floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_gr_complex) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.average); + + main_box = dialog_box(tb, 'GNU Radio Spectrogram Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_spectrogram_plot_f b/gr-qtgui/apps/gr_spectrogram_plot_f new file mode 100755 index 0000000000..d1bb73212a --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_f @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_float, 0) + data = scipy.fromfile(fhandle, dtype=scipy.float32, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.float32(0.0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.add = gr.add_ff() + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot") + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_f(data)) + n += 1 + + self.connect(self.add, self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs): + self.connect(s, (self.add, i)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_float) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.average); + + main_box = dialog_box(tb, 'GNU Radio Spectrogram Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_spectrogram_plot_i b/gr-qtgui/apps/gr_spectrogram_plot_i new file mode 100755 index 0000000000..a617f9087d --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_i @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_int, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int32, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.int32(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.add = gr.add_ff() + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot") + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_i(data)) + self.cnvrt.append(gr.int_to_float(1, self._scale)) + n += 1 + + self.connect(self.add, self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs): + self.connect(s, self.cnvrt[i], (self.add, i)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of integers." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(32-1)-1, + help="Set a scaling factor for the int->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_int) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average); + + main_box = dialog_box(tb, 'GNU Radio Spectrogram Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_spectrogram_plot_s b/gr-qtgui/apps/gr_spectrogram_plot_s new file mode 100755 index 0000000000..29f3d3df77 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_s @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples_and_pad(filename, start, in_size, min_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_short, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int16, count=in_size) + data = data.tolist() + fhandle.close() + + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [scipy.int16(0)] + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, scale, avg=1.0): + gr.top_block.__init__(self) + + self._filelist = filelist + self._center_freq = fc + self._samp_rate = samp_rate + self._psd_size = psdsize + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + self._avg = avg + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.add = gr.add_ff() + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, gr.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot") + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + self.srcs.append(gr.vector_source_s(data)) + self.cnvrt.append(gr.short_to_float(1, self._scale)) + n += 1 + + self.connect(self.add, self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs): + self.connect(s, self.cnvrt[i], (self.add, i)) + + self.gui_snk.set_update_time(0); + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + self._nsamps = newnsamps + + for s,f in zip(self.srcs, self._filelist): + data = read_samples_and_pad(f, self._start, + self._nsamps, self._psd_size) + s.set_data(data) + + self.start() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of shorts." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-L", "--psd-size", type="int", default=2048, + help="Set the FFT size of the PSD [default=%default]") + parser.add_option("-f", "--center-frequency", type="eng_float", default=0.0, + help="Set the center frequency of the signal [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-a", "--average", type="float", default=1.0, + help="Set amount of averaging (smaller=more averaging) [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(16-1)-1, + help="Set a scaling factor for the short->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_short) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, nsamples, max_nsamples, + options.scale, options.average); + + main_box = dialog_box(tb, 'GNU Radio Spectrogram Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_time_plot_b b/gr-qtgui/apps/gr_time_plot_b new file mode 100755 index 0000000000..90043c91f6 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_b @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_char, 0) + data = scipy.fromfile(fhandle, dtype=scipy.uint8, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class gr_time_plot_f(gr.top_block): + def __init__(self, filelist, samp_rate, start, nsamples, max_nsamples, scale): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_b(data)) + self.cnvrt.append(gr.char_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of chars/bytes." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(8-1)-1, + help="Set a scaling factor for the char->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_char) + max_nsamples = min(filesizes) + + tb = gr_time_plot_f(filelist, options.sample_rate, + options.start, nsamples, max_nsamples, + options.scale); + + main_box = dialog_box(tb, 'GNU Radio Time Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_time_plot_c b/gr-qtgui/apps/gr_time_plot_c new file mode 100755 index 0000000000..858bbce240 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_c @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_gr_complex, 0) + data = scipy.fromfile(fhandle, dtype=scipy.complex64, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class my_top_block(gr.top_block): + def __init__(self, filelist, samp_rate, start, nsamples, max_nsamples): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_gr_complex, self._start) + self.gui_snk = qtgui.time_sink_c(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_c(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "Re{{{0}}}".format(fname)) + self.gui_snk.set_title(n+1, "Im{{{0}}}".format(fname)) + n += 2 + + self.connect(self.srcs[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of complex floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_gr_complex) + max_nsamples = min(filesizes) + + tb = my_top_block(filelist, options.sample_rate, + options.start, nsamples, max_nsamples); + + main_box = dialog_box(tb, 'GNU Radio Time Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_time_plot_f b/gr-qtgui/apps/gr_time_plot_f new file mode 100755 index 0000000000..30834aca7a --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_f @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_float, 0) + data = scipy.fromfile(fhandle, dtype=scipy.float32, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class gr_time_plot_f(gr.top_block): + def __init__(self, filelist, samp_rate, start, nsamples, max_nsamples): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + n = 0 + self.srcs = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_f(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of floats." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_float) + max_nsamples = min(filesizes) + + tb = gr_time_plot_f(filelist, options.sample_rate, + options.start, nsamples, max_nsamples); + + main_box = dialog_box(tb, 'GNU Radio Time Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_time_plot_i b/gr-qtgui/apps/gr_time_plot_i new file mode 100755 index 0000000000..202fc32793 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_i @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_int, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int32, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class gr_time_plot_f(gr.top_block): + def __init__(self, filelist, samp_rate, start, nsamples, max_nsamples, scale): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_i(data)) + self.cnvrt.append(gr.int_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of integers." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(32-1)-1, + help="Set a scaling factor for the int->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_int) + max_nsamples = min(filesizes) + + tb = gr_time_plot_f(filelist, options.sample_rate, + options.start, nsamples, max_nsamples, + options.scale); + + main_box = dialog_box(tb, 'GNU Radio Time Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/gr_time_plot_s b/gr-qtgui/apps/gr_time_plot_s new file mode 100755 index 0000000000..6788284833 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_s @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# Copyright 2012 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.eng_option import eng_option +from optparse import OptionParser +import os, sys + +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) + +try: + import scipy +except ImportError: + print "Error: Scipy required (www.scipy.org)." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_form import * +except ImportError: + from plot_form import * + +def read_samples(filename, start, in_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_short, 0) + data = scipy.fromfile(fhandle, dtype=scipy.int16, count=in_size) + data = data.tolist() + fhandle.close() + + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + + return data + +class gr_time_plot_f(gr.top_block): + def __init__(self, filelist, samp_rate, start, nsamples, max_nsamples, scale): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._start = start + self._max_nsamps = max_nsamples + self._scale = scale + self._nsigs = len(self._filelist) + + if(nsamples is None): + self._nsamps = max_nsamples + else: + self._nsamps = nsamples + + self.qapp = QtGui.QApplication(sys.argv) + + self.skip = gr.skiphead(gr.sizeof_float, self._start) + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + n = 0 + self.srcs = list() + self.cnvrt = list() + for f in filelist: + data = read_samples(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_s(data)) + self.cnvrt.append(gr.short_to_float(1, self._scale)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_title(n, "{0}".format(fname)) + n += 1 + + self.connect(self.srcs[0], self.cnvrt[0], self.skip) + self.connect(self.skip, (self.gui_snk, 0)) + + for i,s in enumerate(self.srcs[1:]): + self.connect(s, self.cnvrt[i], (self.gui_snk, i+1)) + + self.gui_snk.set_update_time(0); + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + def get_gui(self): + return self.pyWin + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + for s,f in zip(self.srcs, self._filelist): + data = read_samples(f, self._start, newnsamps) + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of shorts." + parser = OptionParser(option_class=eng_option, description=description, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=None, + help="Set the number of samples to display [default=prints entire file]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-r", "--sample-rate", type="eng_float", default=1.0, + help="Set the sample rate of the signal [default=%default]") + parser.add_option("-s", "--scale", type="eng_float", default=2**(16-1)-1, + help="Set a scaling factor for the short->float conversion [default=%default]") + (options, args) = parser.parse_args() + + if(len(args) < 1): + parser.print_help() + sys.exit(0) + + filelist = list(args) + + nsamples = options.nsamples + + # Find the smallest number of samples in all files and use that as + # a maximum value possible. + filesizes = [] + for f in filelist: + if(os.path.exists(f)): + filesizes.append(os.path.getsize(f) / gr.sizeof_short) + max_nsamples = min(filesizes) + + tb = gr_time_plot_f(filelist, options.sample_rate, + options.start, nsamples, max_nsamples, + options.scale); + + main_box = dialog_box(tb, 'GNU Radio Time Plot') + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass + diff --git a/gr-qtgui/apps/plot_form.py b/gr-qtgui/apps/plot_form.py new file mode 100644 index 0000000000..2fb19d72b8 --- /dev/null +++ b/gr-qtgui/apps/plot_form.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +class dialog_box(QtGui.QWidget): + def __init__(self, top_block, title=''): + QtGui.QWidget.__init__(self, None) + + self._start = 0 + self._end = 0 + + self.top_block = top_block + + self.setWindowTitle(title) + + self.size_val = QtGui.QIntValidator(0, top_block._max_nsamps, self) + + self.start_form = QtGui.QFormLayout() + self.start_edit = QtGui.QLineEdit(self) + self.start_edit.setMinimumWidth(100) + self.start_edit.setText(QtCore.QString("%1").arg(top_block._start)) + self.start_edit.setValidator(self.size_val) + self.start_form.addRow("Start:", self.start_edit) + self.connect(self.start_edit, QtCore.SIGNAL("returnPressed()"), + self.update_points) + + end = top_block._start + top_block._nsamps + self.end_form = QtGui.QFormLayout() + self.end_edit = QtGui.QLineEdit(self) + self.end_edit.setMinimumWidth(100) + self.end_edit.setText(QtCore.QString("%1").arg(end)) + self.end_edit.setValidator(self.size_val) + self.end_form.addRow("End:", self.end_edit) + self.connect(self.end_edit, QtCore.SIGNAL("returnPressed()"), + self.update_points) + + self.layout = QtGui.QGridLayout(self) + self.layout.addWidget(top_block.get_gui(), 0,0,1,2) + self.layout.addLayout(self.start_form, 1,0,1,1) + self.layout.addLayout(self.end_form, 1,1,1,1) + + self.resize(800, 500) + + def update_points(self): + newstart = self.start_edit.text().toUInt()[0] + newend = self.end_edit.text().toUInt()[0] + if(newstart != self._start or newend != self._end): + if(newend < newstart): + QtGui.QMessageBox.information( + self, "Warning", + "End sample is less than start sample.", + QtGui.QMessageBox.Ok); + else: + newnsamps = newend - newstart + self.top_block.reset(newstart, newnsamps) + self._start = newstart + self._end = newend diff --git a/gr-qtgui/apps/qt_digital.py b/gr-qtgui/apps/qt_digital.py index 2bc039a31f..d353e0041c 100755 --- a/gr-qtgui/apps/qt_digital.py +++ b/gr-qtgui/apps/qt_digital.py @@ -20,7 +20,7 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks2 +from gnuradio import gr, digital from gnuradio import eng_notation import sys @@ -60,7 +60,7 @@ class dialog_box(QtGui.QMainWindow): self.set_time_offset(self.fg.timing_offset()) self.set_gain_mu(self.fg.rx_gain_mu()) - self.set_alpha(self.fg.rx_alpha()) + self.set_loop_bw(self.fg.loop_bw()) # Add the qtsnk widgets to the hlayout box self.gui.sinkLayout.addWidget(snkTx) @@ -146,13 +146,13 @@ class dialog_box(QtGui.QMainWindow): def set_gain_mu(self, gain): self.gui.gainMuEdit.setText(QtCore.QString("%1").arg(gain)) - def set_alpha(self, alpha): - self.gui.alphaEdit.setText(QtCore.QString("%1").arg(alpha)) + def set_loop_bw(self, bw): + self.gui.alphaEdit.setText(QtCore.QString("%1").arg(bw)) def alphaEditText(self): try: - alpha = self.gui.alphaEdit.text().toDouble()[0] - self.fg.set_rx_alpha(alpha) + bw = self.gui.alphaEdit.text().toDouble()[0] + self.fg.set_loop_bw(bw) except RuntimeError: pass @@ -174,13 +174,16 @@ class my_top_block(gr.top_block): self.sps = 2 self.excess_bw = 0.35 - self.gray_code = True + self.gray_code = digital.mod_codes.GRAY_CODE fftsize = 2048 self.data = scipy.random.randint(0, 255, 1000) self.src = gr.vector_source_b(self.data.tolist(), True) - self.mod = blks2.dqpsk_mod(self.sps, self.excess_bw, self.gray_code, False, False) + self.mod = digital.dqpsk_mod(self.gray_code, + samples_per_symbol=self.sps, + excess_bw=self.excess_bw, + verbose=False, log=False) self.rrctaps = gr.firdes.root_raised_cosine(1, self.sps, 1, self.excess_bw, 21) self.rx_rrc = gr.fir_filter_ccf(1, self.rrctaps) @@ -194,17 +197,16 @@ class my_top_block(gr.top_block): self.gain_omega = .25 * self.gain_mu * self.gain_mu self.omega_rel_lim = 0.05 - self.alpha = 0.15 - self.beta = 0.25 * self.alpha * self.alpha + self._loop_bw = 2*scipy.pi/100.0 self.fmin = -1000/self.sample_rate() self.fmax = 1000/self.sample_rate() - self.receiver = gr.mpsk_receiver_cc(self.arity, 0, - self.alpha, self.beta, - self.fmin, self.fmax, - self.mu, self.gain_mu, - self.omega, self.gain_omega, - self.omega_rel_lim) + self.receiver = digital.mpsk_receiver_cc(self.arity, 0, + self._loop_bw, + self.fmin, self.fmax, + self.mu, self.gain_mu, + self.omega, self.gain_omega, + self.omega_rel_lim) self.snr_dB = 15 @@ -288,18 +290,12 @@ class my_top_block(gr.top_block): self.receiver.set_gain_mu(self.gain_mu) self.receiver.set_gain_omega(self.gain_omega) - def rx_alpha(self): - return self.alpha - - def rx_beta(self): - return self.beta - - def set_rx_alpha(self, alpha): - self.alpha = alpha - self.beta = .25 * self.alpha * self.alpha - self.receiver.set_alpha(self.alpha) - self.receiver.set_beta(self.beta) + def set_loop_bw(self, loop_bw): + self._loop_bw = bw + self.receiver.set_loop_bw(self._loop_bw) + def loop_bw(self): + return self._loop_bw if __name__ == "__main__": tb = my_top_block(); diff --git a/gr-qtgui/apps/uhd_display.py b/gr-qtgui/apps/uhd_display.py index d02fbad9ee..250d757b6c 100755 --- a/gr-qtgui/apps/uhd_display.py +++ b/gr-qtgui/apps/uhd_display.py @@ -24,12 +24,11 @@ from gnuradio import gr from gnuradio import uhd from gnuradio import eng_notation from gnuradio.eng_option import eng_option -from gnuradio.qtgui import qtgui from optparse import OptionParser import sys try: - from gnuradio.qtgui import qtgui + from gnuradio import qtgui from PyQt4 import QtGui, QtCore import sip except ImportError: diff --git a/gr-qtgui/doc/qtgui.dox b/gr-qtgui/doc/qtgui.dox index 8664af163d..c12fbf0106 100644 --- a/gr-qtgui/doc/qtgui.dox +++ b/gr-qtgui/doc/qtgui.dox @@ -51,7 +51,7 @@ while the GUI is alive. \code from PyQt4 import Qt -from gnuradio.qtgui import qtgui +from gnuradio import qtgui import sys, sip class grclass(gr.top_block): diff --git a/gr-qtgui/examples/pyqt_const_c.py b/gr-qtgui/examples/pyqt_const_c.py new file mode 100755 index 0000000000..e3cc3e527e --- /dev/null +++ b/gr-qtgui/examples/pyqt_const_c.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# Copyright 2011 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 +import sys + +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 dialog_box(QtGui.QWidget): + def __init__(self, display, control): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + self.boxlayout.addWidget(control) + + self.resize(800, 500) + +class control_box(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle('Control Panel') + + self.setToolTip('Control the signals') + QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10)) + + self.layout = QtGui.QFormLayout(self) + + # Control the first signal + self.freq1Edit = QtGui.QLineEdit(self) + self.freq1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Frequency:", self.freq1Edit) + self.connect(self.freq1Edit, QtCore.SIGNAL("editingFinished()"), + self.freq1EditText) + + self.amp1Edit = QtGui.QLineEdit(self) + self.amp1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Amplitude:", self.amp1Edit) + self.connect(self.amp1Edit, QtCore.SIGNAL("editingFinished()"), + self.amp1EditText) + + + # Control the second signal + self.freq2Edit = QtGui.QLineEdit(self) + self.freq2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Frequency:", self.freq2Edit) + self.connect(self.freq2Edit, QtCore.SIGNAL("editingFinished()"), + self.freq2EditText) + + + self.amp2Edit = QtGui.QLineEdit(self) + self.amp2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Amplitude:", self.amp2Edit) + self.connect(self.amp2Edit, QtCore.SIGNAL("editingFinished()"), + self.amp2EditText) + + self.quit = QtGui.QPushButton('Close', self) + self.quit.setMinimumWidth(100) + self.layout.addWidget(self.quit) + + self.connect(self.quit, QtCore.SIGNAL('clicked()'), + QtGui.qApp, QtCore.SLOT('quit()')) + + def attach_signal1(self, signal): + self.signal1 = signal + self.freq1Edit.setText(QtCore.QString("%1").arg(self.signal1.frequency())) + self.amp1Edit.setText(QtCore.QString("%1").arg(self.signal1.amplitude())) + + def attach_signal2(self, signal): + self.signal2 = signal + self.freq2Edit.setText(QtCore.QString("%1").arg(self.signal2.frequency())) + self.amp2Edit.setText(QtCore.QString("%1").arg(self.signal2.amplitude())) + + def freq1EditText(self): + try: + newfreq = float(self.freq1Edit.text()) + self.signal1.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp1EditText(self): + try: + newamp = float(self.amp1Edit.text()) + self.signal1.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + + def freq2EditText(self): + try: + newfreq = float(self.freq2Edit.text()) + self.signal2.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp2EditText(self): + try: + newamp = float(self.amp2Edit.text()) + self.signal2.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + Rs = 8000 + f1 = 100 + f2 = 200 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f1, 0.5, 0) + src2 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f2, 0.5, 0) + src = gr.add_cc() + channel = gr.channel_model(0.001) + thr = gr.throttle(gr.sizeof_gr_complex, 100*npts) + self.snk1 = qtgui.const_sink_c(npts, "Constellation Example", 1) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, channel, thr, (self.snk1, 0)) + + self.ctrl_win = control_box() + self.ctrl_win.attach_signal1(src1) + self.ctrl_win.attach_signal2(src2) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self.main_box = dialog_box(pyWin, self.ctrl_win) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/examples/pyqt_freq_c.py b/gr-qtgui/examples/pyqt_freq_c.py new file mode 100755 index 0000000000..0422046204 --- /dev/null +++ b/gr-qtgui/examples/pyqt_freq_c.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# +# Copyright 2012 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 +import sys + +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 dialog_box(QtGui.QWidget): + def __init__(self, display, control): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + self.boxlayout.addWidget(control) + + self.resize(800, 500) + +class control_box(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle('Control Panel') + + self.setToolTip('Control the signals') + QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10)) + + self.layout = QtGui.QFormLayout(self) + + # Control the first signal + self.freq1Edit = QtGui.QLineEdit(self) + self.freq1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Frequency:", self.freq1Edit) + self.connect(self.freq1Edit, QtCore.SIGNAL("editingFinished()"), + self.freq1EditText) + + self.amp1Edit = QtGui.QLineEdit(self) + self.amp1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Amplitude:", self.amp1Edit) + self.connect(self.amp1Edit, QtCore.SIGNAL("editingFinished()"), + self.amp1EditText) + + + # Control the second signal + self.freq2Edit = QtGui.QLineEdit(self) + self.freq2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Frequency:", self.freq2Edit) + self.connect(self.freq2Edit, QtCore.SIGNAL("editingFinished()"), + self.freq2EditText) + + + self.amp2Edit = QtGui.QLineEdit(self) + self.amp2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Amplitude:", self.amp2Edit) + self.connect(self.amp2Edit, QtCore.SIGNAL("editingFinished()"), + self.amp2EditText) + + self.quit = QtGui.QPushButton('Close', self) + self.quit.setMinimumWidth(100) + self.layout.addWidget(self.quit) + + self.connect(self.quit, QtCore.SIGNAL('clicked()'), + QtGui.qApp, QtCore.SLOT('quit()')) + + def attach_signal1(self, signal): + self.signal1 = signal + self.freq1Edit.setText(QtCore.QString("%1").arg(self.signal1.frequency())) + self.amp1Edit.setText(QtCore.QString("%1").arg(self.signal1.amplitude())) + + def attach_signal2(self, signal): + self.signal2 = signal + self.freq2Edit.setText(QtCore.QString("%1").arg(self.signal2.frequency())) + self.amp2Edit.setText(QtCore.QString("%1").arg(self.signal2.amplitude())) + + def freq1EditText(self): + try: + newfreq = float(self.freq1Edit.text()) + self.signal1.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp1EditText(self): + try: + newamp = float(self.amp1Edit.text()) + self.signal1.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + + def freq2EditText(self): + try: + newfreq = float(self.freq2Edit.text()) + self.signal2.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp2EditText(self): + try: + newamp = float(self.amp2Edit.text()) + self.signal2.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + Rs = 8000 + f1 = 100 + f2 = 200 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f1, 0.1, 0) + src2 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f2, 0.1, 0) + src = gr.add_cc() + channel = gr.channel_model(0.01) + thr = gr.throttle(gr.sizeof_gr_complex, 100*npts) + self.snk1 = qtgui.freq_sink_c(npts, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Complex Freq Example", 3) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, channel, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + self.connect(src2, (self.snk1, 2)) + + self.ctrl_win = control_box() + self.ctrl_win.attach_signal1(src1) + self.ctrl_win.attach_signal2(src2) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + #pyWin.show() + self.main_box = dialog_box(pyWin, self.ctrl_win) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/examples/pyqt_freq_f.py b/gr-qtgui/examples/pyqt_freq_f.py new file mode 100755 index 0000000000..fb7a9f93c6 --- /dev/null +++ b/gr-qtgui/examples/pyqt_freq_f.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2012 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 +import sys + +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 dialog_box(QtGui.QWidget): + def __init__(self, display, control): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + self.boxlayout.addWidget(control) + + self.resize(800, 500) + +class control_box(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle('Control Panel') + + self.setToolTip('Control the signals') + QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10)) + + self.layout = QtGui.QFormLayout(self) + + # Control the first signal + self.freq1Edit = QtGui.QLineEdit(self) + self.freq1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Frequency:", self.freq1Edit) + self.connect(self.freq1Edit, QtCore.SIGNAL("editingFinished()"), + self.freq1EditText) + + self.amp1Edit = QtGui.QLineEdit(self) + self.amp1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Amplitude:", self.amp1Edit) + self.connect(self.amp1Edit, QtCore.SIGNAL("editingFinished()"), + self.amp1EditText) + + + # Control the second signal + self.freq2Edit = QtGui.QLineEdit(self) + self.freq2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Frequency:", self.freq2Edit) + self.connect(self.freq2Edit, QtCore.SIGNAL("editingFinished()"), + self.freq2EditText) + + + self.amp2Edit = QtGui.QLineEdit(self) + self.amp2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Amplitude:", self.amp2Edit) + self.connect(self.amp2Edit, QtCore.SIGNAL("editingFinished()"), + self.amp2EditText) + + self.quit = QtGui.QPushButton('Close', self) + self.quit.setMinimumWidth(100) + self.layout.addWidget(self.quit) + + self.connect(self.quit, QtCore.SIGNAL('clicked()'), + QtGui.qApp, QtCore.SLOT('quit()')) + + def attach_signal1(self, signal): + self.signal1 = signal + self.freq1Edit.setText(QtCore.QString("%1").arg(self.signal1.frequency())) + self.amp1Edit.setText(QtCore.QString("%1").arg(self.signal1.amplitude())) + + def attach_signal2(self, signal): + self.signal2 = signal + self.freq2Edit.setText(QtCore.QString("%1").arg(self.signal2.frequency())) + self.amp2Edit.setText(QtCore.QString("%1").arg(self.signal2.amplitude())) + + def freq1EditText(self): + try: + newfreq = float(self.freq1Edit.text()) + self.signal1.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp1EditText(self): + try: + newamp = float(self.amp1Edit.text()) + self.signal1.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + + def freq2EditText(self): + try: + newfreq = float(self.freq2Edit.text()) + self.signal2.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp2EditText(self): + try: + newamp = float(self.amp2Edit.text()) + self.signal2.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + Rs = 8000 + f1 = 100 + f2 = 200 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = gr.sig_source_f(Rs, gr.GR_SIN_WAVE, f1, 0.1, 0) + src2 = gr.sig_source_f(Rs, gr.GR_SIN_WAVE, f2, 0.1, 0) + src = gr.add_ff() + thr = gr.throttle(gr.sizeof_float, 100*npts) + self.snk1 = qtgui.freq_sink_f(npts, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Real freq Example", 3) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + self.connect(src2, (self.snk1, 2)) + + self.ctrl_win = control_box() + self.ctrl_win.attach_signal1(src1) + self.ctrl_win.attach_signal2(src2) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + #pyWin.show() + self.main_box = dialog_box(pyWin, self.ctrl_win) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/examples/pyqt_time_c.py b/gr-qtgui/examples/pyqt_time_c.py index 04425e11f1..255cd1c855 100755 --- a/gr-qtgui/examples/pyqt_time_c.py +++ b/gr-qtgui/examples/pyqt_time_c.py @@ -145,13 +145,13 @@ class my_top_block(gr.top_block): channel = gr.channel_model(0.01) thr = gr.throttle(gr.sizeof_gr_complex, 100*npts) self.snk1 = qtgui.time_sink_c(npts, Rs, - "Complex Time Example", 3) + "Complex Time Example", 1) self.connect(src1, (src,0)) self.connect(src2, (src,1)) self.connect(src, channel, thr, (self.snk1, 0)) - self.connect(src1, (self.snk1, 1)) - self.connect(src2, (self.snk1, 2)) + #self.connect(src1, (self.snk1, 1)) + #self.connect(src2, (self.snk1, 2)) self.ctrl_win = control_box() self.ctrl_win.attach_signal1(src1) @@ -169,14 +169,16 @@ class my_top_block(gr.top_block): pyWin, QtCore.SLOT("setTitle(int, QString)")) pyWin.emit(QtCore.SIGNAL("setTitle(int, QString)"), 0, "Re{sum}") self.snk1.set_title(1, "Im{Sum}") - self.snk1.set_title(2, "Re{src1}") - self.snk1.set_title(3, "Im{src1}") - self.snk1.set_title(4, "Re{src2}") - self.snk1.set_title(5, "Im{src2}") + #self.snk1.set_title(2, "Re{src1}") + #self.snk1.set_title(3, "Im{src1}") + #self.snk1.set_title(4, "Re{src2}") + #self.snk1.set_title(5, "Im{src2}") # Can also set the color of a curve #self.snk1.set_color(5, "blue") + self.snk1.set_update_time(0.5) + #pyWin.show() self.main_box = dialog_box(pyWin, self.ctrl_win) self.main_box.show() diff --git a/gr-qtgui/examples/pyqt_waterfall_c.py b/gr-qtgui/examples/pyqt_waterfall_c.py new file mode 100755 index 0000000000..b7f8396ff3 --- /dev/null +++ b/gr-qtgui/examples/pyqt_waterfall_c.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# +# Copyright 2012 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 +import sys + +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 dialog_box(QtGui.QWidget): + def __init__(self, display, control): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + self.boxlayout.addWidget(control) + + self.resize(800, 500) + +class control_box(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle('Control Panel') + + self.setToolTip('Control the signals') + QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10)) + + self.layout = QtGui.QFormLayout(self) + + # Control the first signal + self.freq1Edit = QtGui.QLineEdit(self) + self.freq1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Frequency:", self.freq1Edit) + self.connect(self.freq1Edit, QtCore.SIGNAL("editingFinished()"), + self.freq1EditText) + + self.amp1Edit = QtGui.QLineEdit(self) + self.amp1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Amplitude:", self.amp1Edit) + self.connect(self.amp1Edit, QtCore.SIGNAL("editingFinished()"), + self.amp1EditText) + + + # Control the second signal + self.freq2Edit = QtGui.QLineEdit(self) + self.freq2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Frequency:", self.freq2Edit) + self.connect(self.freq2Edit, QtCore.SIGNAL("editingFinished()"), + self.freq2EditText) + + + self.amp2Edit = QtGui.QLineEdit(self) + self.amp2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Amplitude:", self.amp2Edit) + self.connect(self.amp2Edit, QtCore.SIGNAL("editingFinished()"), + self.amp2EditText) + + self.quit = QtGui.QPushButton('Close', self) + self.quit.setMinimumWidth(100) + self.layout.addWidget(self.quit) + + self.connect(self.quit, QtCore.SIGNAL('clicked()'), + QtGui.qApp, QtCore.SLOT('quit()')) + + def attach_signal1(self, signal): + self.signal1 = signal + self.freq1Edit.setText(QtCore.QString("%1").arg(self.signal1.frequency())) + self.amp1Edit.setText(QtCore.QString("%1").arg(self.signal1.amplitude())) + + def attach_signal2(self, signal): + self.signal2 = signal + self.freq2Edit.setText(QtCore.QString("%1").arg(self.signal2.frequency())) + self.amp2Edit.setText(QtCore.QString("%1").arg(self.signal2.amplitude())) + + def freq1EditText(self): + try: + newfreq = float(self.freq1Edit.text()) + self.signal1.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp1EditText(self): + try: + newamp = float(self.amp1Edit.text()) + self.signal1.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + + def freq2EditText(self): + try: + newfreq = float(self.freq2Edit.text()) + self.signal2.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp2EditText(self): + try: + newamp = float(self.amp2Edit.text()) + self.signal2.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + Rs = 8000 + f1 = 100 + f2 = 200 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f1, 0.1, 0) + src2 = gr.sig_source_c(Rs, gr.GR_SIN_WAVE, f2, 0.1, 0) + src = gr.add_cc() + channel = gr.channel_model(0.01) + thr = gr.throttle(gr.sizeof_gr_complex, 100*npts) + self.snk1 = qtgui.waterfall_sink_c(npts, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Complex Waterfall Example") + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, channel, thr, (self.snk1, 0)) + + self.ctrl_win = control_box() + self.ctrl_win.attach_signal1(src1) + self.ctrl_win.attach_signal2(src2) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + #pyWin.show() + self.main_box = dialog_box(pyWin, self.ctrl_win) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/examples/pyqt_waterfall_f.py b/gr-qtgui/examples/pyqt_waterfall_f.py new file mode 100755 index 0000000000..29ae259e5f --- /dev/null +++ b/gr-qtgui/examples/pyqt_waterfall_f.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +# +# Copyright 2012 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 +import sys + +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 dialog_box(QtGui.QWidget): + def __init__(self, display, control): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + self.boxlayout.addWidget(control) + + self.resize(800, 500) + +class control_box(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setWindowTitle('Control Panel') + + self.setToolTip('Control the signals') + QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10)) + + self.layout = QtGui.QFormLayout(self) + + # Control the first signal + self.freq1Edit = QtGui.QLineEdit(self) + self.freq1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Frequency:", self.freq1Edit) + self.connect(self.freq1Edit, QtCore.SIGNAL("editingFinished()"), + self.freq1EditText) + + self.amp1Edit = QtGui.QLineEdit(self) + self.amp1Edit.setMinimumWidth(100) + self.layout.addRow("Signal 1 Amplitude:", self.amp1Edit) + self.connect(self.amp1Edit, QtCore.SIGNAL("editingFinished()"), + self.amp1EditText) + + + # Control the second signal + self.freq2Edit = QtGui.QLineEdit(self) + self.freq2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Frequency:", self.freq2Edit) + self.connect(self.freq2Edit, QtCore.SIGNAL("editingFinished()"), + self.freq2EditText) + + + self.amp2Edit = QtGui.QLineEdit(self) + self.amp2Edit.setMinimumWidth(100) + self.layout.addRow("Signal 2 Amplitude:", self.amp2Edit) + self.connect(self.amp2Edit, QtCore.SIGNAL("editingFinished()"), + self.amp2EditText) + + self.quit = QtGui.QPushButton('Close', self) + self.quit.setMinimumWidth(100) + self.layout.addWidget(self.quit) + + self.connect(self.quit, QtCore.SIGNAL('clicked()'), + QtGui.qApp, QtCore.SLOT('quit()')) + + def attach_signal1(self, signal): + self.signal1 = signal + self.freq1Edit.setText(QtCore.QString("%1").arg(self.signal1.frequency())) + self.amp1Edit.setText(QtCore.QString("%1").arg(self.signal1.amplitude())) + + def attach_signal2(self, signal): + self.signal2 = signal + self.freq2Edit.setText(QtCore.QString("%1").arg(self.signal2.frequency())) + self.amp2Edit.setText(QtCore.QString("%1").arg(self.signal2.amplitude())) + + def freq1EditText(self): + try: + newfreq = float(self.freq1Edit.text()) + self.signal1.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp1EditText(self): + try: + newamp = float(self.amp1Edit.text()) + self.signal1.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + + def freq2EditText(self): + try: + newfreq = float(self.freq2Edit.text()) + self.signal2.set_frequency(newfreq) + except ValueError: + print "Bad frequency value entered" + + def amp2EditText(self): + try: + newamp = float(self.amp2Edit.text()) + self.signal2.set_amplitude(newamp) + except ValueError: + print "Bad amplitude value entered" + + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + Rs = 8000 + f1 = 100 + f2 = 200 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = gr.sig_source_f(Rs, gr.GR_SIN_WAVE, f1, 0.1, 0) + src2 = gr.sig_source_f(Rs, gr.GR_SIN_WAVE, f2, 0.1, 0) + src = gr.add_ff() + thr = gr.throttle(gr.sizeof_float, 100*npts) + self.snk1 = qtgui.waterfall_sink_f(npts, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Real Waterfall Example") + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, thr, (self.snk1, 0)) + + self.ctrl_win = control_box() + self.ctrl_win.attach_signal1(src1) + self.ctrl_win.attach_signal2(src2) + + # Get the reference pointer to the SpectrumDisplayForm QWidget + pyQt = self.snk1.pyqwidget() + + # Wrap the pointer as a PyQt SIP object + # This can now be manipulated as a PyQt4.QtGui.QWidget + pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + #pyWin.show() + self.main_box = dialog_box(pyWin, self.ctrl_win) + self.main_box.show() + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.qapp.exec_() + tb.stop() + diff --git a/gr-qtgui/grc/qtgui_block_tree.xml b/gr-qtgui/grc/qtgui_block_tree.xml new file mode 100644 index 0000000000..898c1c0044 --- /dev/null +++ b/gr-qtgui/grc/qtgui_block_tree.xml @@ -0,0 +1,48 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2012 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. +--> + +<!-- +################################################### +##Block Tree for GR QTGUI blocks. +################################################### + --> +<cat> + <name></name> <!-- Blank for Root Name --> + <cat> + <name>QT GUI Sinks</name> + <block>qtgui_sink_x</block> + <block>qtgui_freq_sink_x</block> + <block>qtgui_time_sink_x</block> + <block>qtgui_const_sink_x</block> + <block>qtgui_waterfall_sink_x</block> + </cat> + <cat> + <name>QT GUI Widgets</name> + <block>qtgui_tab_widget</block> + <block>variable_qtgui_range</block> + <block>variable_qtgui_chooser</block> + <block>variable_qtgui_check_box</block> + <block>variable_qtgui_entry</block> + <block>variable_qtgui_label</block> + </cat> +</cat> diff --git a/gr-qtgui/grc/qtgui_check_box.xml b/gr-qtgui/grc/qtgui_check_box.xml index 95f4f968a6..e3a00d002e 100644 --- a/gr-qtgui/grc/qtgui_check_box.xml +++ b/gr-qtgui/grc/qtgui_check_box.xml @@ -8,7 +8,6 @@ <block> <name>QT GUI Check Box</name> <key>variable_qtgui_check_box</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#set $win = '_%s_check_box'%$id diff --git a/gr-qtgui/grc/qtgui_chooser.xml b/gr-qtgui/grc/qtgui_chooser.xml index cb50902897..6a8760cbe6 100644 --- a/gr-qtgui/grc/qtgui_chooser.xml +++ b/gr-qtgui/grc/qtgui_chooser.xml @@ -8,7 +8,6 @@ <block> <name>QT GUI Chooser</name> <key>variable_qtgui_chooser</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <var_make>self.$(id) = $(id) = $value</var_make> <make>#slurp diff --git a/gr-qtgui/grc/qtgui_const_sink_x.xml b/gr-qtgui/grc/qtgui_const_sink_x.xml new file mode 100644 index 0000000000..536237d164 --- /dev/null +++ b/gr-qtgui/grc/qtgui_const_sink_x.xml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Sink +################################################### + --> +<block> + <name>QT GUI Constellation Sink</name> + <key>qtgui_const_sink_x</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.$(type.fcn)( + $size, \#size + $name, \#name + $nconnections \#number of inputs +) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_resize($width, $height)</callback> + <callback>set_update_time($t)</callback> + <callback>set_title($which, $title)</callback> + <callback>set_color($which, $color)</callback> + <param> + <name>Type</name> + <key>type</key> + <value>complex</value> + <type>enum</type> + <option><name>Complex</name><key>complex</key><opt>fcn:const_sink_c</opt></option> +<!-- <option><name>Float</name><key>float</key><opt>fcn:const_sink_f</opt></option> --> + </param> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>Number of Points</name> + <key>size</key> + <value>1024</value> + <type>int</type> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + <sink> + <name>in</name> + <type>$type</type> + <nports>$nconnections</nports> + </sink> + <doc> +The GUI hint can be used to position the widget within the application. \ +The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ +Both the tab specification and the grid position are optional. + </doc> +</block> diff --git a/gr-qtgui/grc/qtgui_entry.xml b/gr-qtgui/grc/qtgui_entry.xml index 1a98402a0f..514071afab 100644 --- a/gr-qtgui/grc/qtgui_entry.xml +++ b/gr-qtgui/grc/qtgui_entry.xml @@ -8,7 +8,6 @@ <block> <name>QT GUI Entry</name> <key>variable_qtgui_entry</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <import>from gnuradio import eng_notation</import> <var_make>self.$(id) = $(id) = $value</var_make> diff --git a/gr-qtgui/grc/qtgui_freq_sink_x.xml b/gr-qtgui/grc/qtgui_freq_sink_x.xml new file mode 100644 index 0000000000..69ce5f4e68 --- /dev/null +++ b/gr-qtgui/grc/qtgui_freq_sink_x.xml @@ -0,0 +1,122 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Sink +################################################### + --> +<block> + <name>QT GUI Frequency Sink</name> + <key>qtgui_freq_sink_x</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>from gnuradio.gr import firdes</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.$(type.fcn)( + $fftsize, \#size + $wintype, \#wintype + $fc, \#fc + $bw, \#bw + $name, \#name + $nconnections \#number of inputs +) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_frequency_range($fc, $bw)</callback> + <callback>set_update_time($t)</callback> + <callback>set_title($which, $title)</callback> + <callback>set_color($which, $color)</callback> + <param> + <name>Type</name> + <key>type</key> + <value>complex</value> + <type>enum</type> + <option><name>Complex</name><key>complex</key><opt>fcn:freq_sink_c</opt></option> + <option><name>Float</name><key>float</key><opt>fcn:freq_sink_f</opt></option> + </param> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>FFT Size</name> + <key>fftsize</key> + <value>1024</value> + <type>int</type> + </param> + <param> + <name>Window Type</name> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + <type>int</type> + <hide>part</hide> + <option> + <name>Blackman-harris</name> + <key>firdes.WIN_BLACKMAN_hARRIS</key> + </option> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Center Frequency (Hz)</name> + <key>fc</key> + <value>0</value> + <type>real</type> + </param> + <param> + <name>Bandwidth (Hz)</name> + <key>bw</key> + <value>samp_rate</value> + <type>real</type> + </param> + <param> + <name>Update Rate</name> + <key>rate</key> + <value>10</value> + <type>real</type> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + <sink> + <name>in</name> + <type>$type</type> + <nports>$nconnections</nports> + </sink> + <doc> +The GUI hint can be used to position the widget within the application. \ +The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ +Both the tab specification and the grid position are optional. + </doc> +</block> diff --git a/gr-qtgui/grc/qtgui_label.xml b/gr-qtgui/grc/qtgui_label.xml index 5049118c47..3615574c4c 100644 --- a/gr-qtgui/grc/qtgui_label.xml +++ b/gr-qtgui/grc/qtgui_label.xml @@ -8,7 +8,6 @@ <block> <name>QT GUI Label</name> <key>variable_qtgui_label</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <import>from gnuradio import eng_notation</import> <var_make>self.$(id) = $(id) = $value</var_make> diff --git a/gr-qtgui/grc/qtgui_range.xml b/gr-qtgui/grc/qtgui_range.xml index 6b0555f98e..f2d42f1a5f 100644 --- a/gr-qtgui/grc/qtgui_range.xml +++ b/gr-qtgui/grc/qtgui_range.xml @@ -8,7 +8,6 @@ <block> <name>QT GUI Range</name> <key>variable_qtgui_range</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <import>import PyQt4.Qwt5 as Qwt</import> <var_make>self.$(id) = $(id) = $value</var_make> diff --git a/gr-qtgui/grc/qtgui_sink_x.xml b/gr-qtgui/grc/qtgui_sink_x.xml index 83d6ec2879..9ea9dc583f 100644 --- a/gr-qtgui/grc/qtgui_sink_x.xml +++ b/gr-qtgui/grc/qtgui_sink_x.xml @@ -7,9 +7,8 @@ <block> <name>QT GUI Sink</name> <key>qtgui_sink_x</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> - <import>from gnuradio.qtgui import qtgui</import> + <import>from gnuradio import qtgui</import> <import>from gnuradio.gr import firdes</import> <import>import sip</import> <make>#set $win = 'self._%s_win'%$id @@ -24,8 +23,18 @@ qtgui.$(type.fcn)( $plottime, \#plottime $plotconst, \#plotconst ) +self.$(id).set_update_time(1.0/$rate) self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) -$(gui_hint()($win))</make> +$(gui_hint()($win)) + +#if $freqchangevar() is not None +def $(id)_callback(p, num): + if num == 1 or num == 2: self.set_$(freqchangevar)(p.x()) + +Qt.QObject.connect(self._$(id)_win, Qt.SIGNAL("plotPointSelected(QPointF, int)"), $(id)_callback ) +#end if + +</make> <callback>set_frequency_range($fc, $bw)</callback> <param> <name>Type</name> @@ -91,6 +100,12 @@ $(gui_hint()($win))</make> <type>real</type> </param> <param> + <name>Update Rate</name> + <key>rate</key> + <value>10</value> + <type>real</type> + </param> + <param> <name>Plot Frequency</name> <key>plotfreq</key> <value>True</value> @@ -133,6 +148,13 @@ $(gui_hint()($win))</make> <type>gui_hint</type> <hide>part</hide> </param> + <param> + <name>Clicked freq variable</name> + <key>freqchangevar</key> + <value>None</value> + <type>raw</type> + <hide>part</hide> + </param> <sink> <name>in</name> <type>$type</type> diff --git a/gr-qtgui/grc/qtgui_tab_widget.xml b/gr-qtgui/grc/qtgui_tab_widget.xml index f900541093..c098c9fef5 100644 --- a/gr-qtgui/grc/qtgui_tab_widget.xml +++ b/gr-qtgui/grc/qtgui_tab_widget.xml @@ -7,7 +7,6 @@ <block> <name>QT GUI Tab Widget</name> <key>qtgui_tab_widget</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> <make>#set $win = 'self.%s'%$id Qt.QTabWidget() diff --git a/gr-qtgui/grc/qtgui_time_sink_x.xml b/gr-qtgui/grc/qtgui_time_sink_x.xml index 9c8da6fbcf..17b2cfd016 100644 --- a/gr-qtgui/grc/qtgui_time_sink_x.xml +++ b/gr-qtgui/grc/qtgui_time_sink_x.xml @@ -7,9 +7,8 @@ <block> <name>QT GUI Time Sink</name> <key>qtgui_time_sink_x</key> - <category>QT GUI Widgets</category> <import>from PyQt4 import Qt</import> - <import>from gnuradio.qtgui import qtgui</import> + <import>from gnuradio import qtgui</import> <import>from gnuradio.gr import firdes</import> <import>import sip</import> <make>#set $win = 'self._%s_win'%$id diff --git a/gr-qtgui/grc/qtgui_waterfall_sink_x.xml b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml new file mode 100644 index 0000000000..47de5b593c --- /dev/null +++ b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Sink +################################################### + --> +<block> + <name>QT GUI Waterfall Sink</name> + <key>qtgui_waterfall_sink_x</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>from gnuradio.gr import firdes</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.$(type.fcn)( + $fftsize, \#size + $wintype, \#wintype + $fc, \#fc + $bw, \#bw + $name, \#name +) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_frequency_range($fc, $bw)</callback> + <callback>set_update_time($t)</callback> + <callback>set_title($which, $title)</callback> + <callback>set_color($which, $color)</callback> + <param> + <name>Type</name> + <key>type</key> + <value>complex</value> + <type>enum</type> + <option><name>Complex</name><key>complex</key><opt>fcn:waterfall_sink_c</opt></option> + <option><name>Float</name><key>float</key><opt>fcn:waterfall_sink_f</opt></option> + </param> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>FFT Size</name> + <key>fftsize</key> + <value>1024</value> + <type>int</type> + </param> + <param> + <name>Window Type</name> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + <type>int</type> + <hide>part</hide> + <option> + <name>Blackman-harris</name> + <key>firdes.WIN_BLACKMAN_hARRIS</key> + </option> + <option> + <name>Hamming</name> + <key>firdes.WIN_HAMMING</key> + </option> + <option> + <name>Hann</name> + <key>firdes.WIN_HANN</key> + </option> + <option> + <name>Blackman</name> + <key>firdes.WIN_BLACKMAN</key> + </option> + <option> + <name>Rectangular</name> + <key>firdes.WIN_RECTANGULAR</key> + </option> + <option> + <name>Kaiser</name> + <key>firdes.WIN_KAISER</key> + </option> + </param> + <param> + <name>Center Frequency (Hz)</name> + <key>fc</key> + <value>0</value> + <type>real</type> + </param> + <param> + <name>Bandwidth (Hz)</name> + <key>bw</key> + <value>samp_rate</value> + <type>real</type> + </param> + <param> + <name>Update Rate</name> + <key>rate</key> + <value>10</value> + <type>real</type> + </param> + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + <sink> + <name>in</name> + <type>$type</type> + <nports>1</nports> + </sink> + <doc> +The GUI hint can be used to position the widget within the application. \ +The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ +Both the tab specification and the grid position are optional. + </doc> +</block> diff --git a/gr-qtgui/include/CMakeLists.txt b/gr-qtgui/include/qtgui/CMakeLists.txt index ef4cb11752..d0d3d13fb5 100644 --- a/gr-qtgui/include/CMakeLists.txt +++ b/gr-qtgui/include/qtgui/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2010-2011 Free Software Foundation, Inc. +# Copyright 2010-2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -21,12 +21,17 @@ # Install the header files ######################################################################## install(FILES - gr_qtgui_api.h - qtgui_time_sink_c.h - qtgui_time_sink_f.h - qtgui_sink_c.h - qtgui_sink_f.h - qtgui_util.h - DESTINATION ${GR_INCLUDE_DIR}/gnuradio + api.h + time_sink_c.h + time_sink_f.h + freq_sink_c.h + freq_sink_f.h + const_sink_c.h + waterfall_sink_c.h + waterfall_sink_f.h + sink_c.h + sink_f.h + utils.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio/qtgui COMPONENT "qtgui_devel" ) diff --git a/gr-qtgui/include/gr_qtgui_api.h b/gr-qtgui/include/qtgui/api.h index c39add1e82..e88948e07b 100644 --- a/gr-qtgui/include/gr_qtgui_api.h +++ b/gr-qtgui/include/qtgui/api.h @@ -19,15 +19,15 @@ * Boston, MA 02110-1301, USA. */ -#ifndef INCLUDED_GR_QTGUI_API_H -#define INCLUDED_GR_QTGUI_API_H +#ifndef INCLUDED_QTGUI_API_H +#define INCLUDED_QTGUI_API_H #include <gruel/attributes.h> #ifdef gnuradio_qtgui_EXPORTS -# define GR_QTGUI_API __GR_ATTR_EXPORT +# define QTGUI_API __GR_ATTR_EXPORT #else -# define GR_QTGUI_API __GR_ATTR_IMPORT +# define QTGUI_API __GR_ATTR_IMPORT #endif -#endif /* INCLUDED_GR_QTGUI_API_H */ +#endif /* INCLUDED_QTGUI_API_H */ diff --git a/gr-qtgui/include/qtgui/const_sink_c.h b/gr-qtgui/include/qtgui/const_sink_c.h new file mode 100644 index 0000000000..48da4b9fa1 --- /dev/null +++ b/gr-qtgui/include/qtgui/const_sink_c.h @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_CONST_SINK_C_H +#define INCLUDED_QTGUI_CONST_SINK_C_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display the IQ constellation of multiple signals. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a complex + * streams and plots them on an IQ constellation plot. + */ + class QTGUI_API const_sink_c : virtual public gr_sync_block + { + public: + // gr::qtgui::const_sink_c::sptr + typedef boost::shared_ptr<const_sink_c> sptr; + + /*! + * \brief Build a complex PSD sink. + * + * \param size number of points to plot at once + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(int which, const std::string &title) = 0; + virtual void set_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, Qt::PenStyle style) = 0; + virtual void set_line_marker(int which, QwtSymbol::Style marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_CONST_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui/freq_sink_c.h b/gr-qtgui/include/qtgui/freq_sink_c.h new file mode 100644 index 0000000000..2bef9035e8 --- /dev/null +++ b/gr-qtgui/include/qtgui/freq_sink_c.h @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_FREQ_SINK_C_H +#define INCLUDED_QTGUI_FREQ_SINK_C_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals in frequency. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a complex + * streams and plots the PSD. Each signal is plotted with a + * different color, and the \a set_title and \a set_color + * functions can be used to change the lable and color for a given + * input number. + */ + class QTGUI_API freq_sink_c : virtual public gr_sync_block + { + public: + // gr::qtgui::freq_sink_c::sptr + typedef boost::shared_ptr<freq_sink_c> sptr; + + /*! + * \brief Build a complex PSD sink. + * + * \param fftsize size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + virtual void set_fft_average(const float fftavg) = 0; + virtual float fft_average() const = 0; + + virtual void set_frequency_range(const double centerfreq, const double bandwidth) = 0; + virtual void set_fft_power_db(double min, double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(int which, const std::string &title) = 0; + virtual void set_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, Qt::PenStyle style) = 0; + virtual void set_line_marker(int which, QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_FREQ_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui/freq_sink_f.h b/gr-qtgui/include/qtgui/freq_sink_f.h new file mode 100644 index 0000000000..51c4ac10a1 --- /dev/null +++ b/gr-qtgui/include/qtgui/freq_sink_f.h @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_FREQ_SINK_F_H +#define INCLUDED_QTGUI_FREQ_SINK_F_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals in frequency. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a floating + * point streams and plots the PSD. Each signal is plotted with a + * different color, and the \a set_title and \a set_color + * functions can be used to change the lable and color for a given + * input number. + */ + class QTGUI_API freq_sink_f : virtual public gr_sync_block + { + public: + // gr::qtgui::freq_sink_f::sptr + typedef boost::shared_ptr<freq_sink_f> sptr; + + /*! + * \brief Build a floating point PSD sink. + * + * \param fftsize size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + virtual void set_fft_average(const float fftavg) = 0; + virtual float fft_average() const = 0; + + virtual void set_frequency_range(const double centerfreq, const double bandwidth) = 0; + virtual void set_fft_power_db(double min, double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(int which, const std::string &title) = 0; + virtual void set_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, Qt::PenStyle style) = 0; + virtual void set_line_marker(int which, QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_FREQ_SINK_F_H */ diff --git a/gr-qtgui/include/qtgui/sink_c.h b/gr-qtgui/include/qtgui/sink_c.h new file mode 100644 index 0000000000..f8d58ddbe0 --- /dev/null +++ b/gr-qtgui/include/qtgui/sink_c.h @@ -0,0 +1,98 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_SINK_C_H +#define INCLUDED_QTGUI_SINK_C_H + +#include <qtgui/api.h> +#include <gr_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display freq, spec, time, and const plots. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes a complex stream and + * plots it. The default action is to plot the signal as a PSD (FFT), + * spectrogram (waterfall), time domain I&Q, and constellation (I + * vs. Q) plots. The plots may be turned off by setting the + * appropriate boolean value in the constructor to False. + */ + + class QTGUI_API sink_c : virtual public gr_block + { + public: + // gr::qtgui::sink_c::sptr + typedef boost::shared_ptr<sink_c> sptr; + + /*! + * \brief Build a complex qtgui sink. + * + * \param fftsize size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param plotfreq Toggle frequency plot on/off + * \param plotwaterfall Toggle waterfall plot on/off + * \param plottime Toggle time plot on/off + * \param plotconst Toggle constellation plot on/off + * \param parent a QWidget parent object, if any + */ + static sptr make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + virtual void set_fft_power_db(double min, double max) = 0; + + //void set_time_domain_axis(double min, double max); + //void set_constellation_axis(double xmin, double xmax, + // double ymin, double ymax); + //void set_constellation_pen_size(int size); + + virtual void set_update_time(double t) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui/sink_f.h b/gr-qtgui/include/qtgui/sink_f.h new file mode 100644 index 0000000000..0af49ba9e2 --- /dev/null +++ b/gr-qtgui/include/qtgui/sink_f.h @@ -0,0 +1,98 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_SINK_F_H +#define INCLUDED_QTGUI_SINK_F_H + +#include <qtgui/api.h> +#include <gr_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display freq, spec, and time. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes a float stream and + * plots it. The default action is to plot the signal as a PSD (FFT), + * spectrogram (waterfall), and time domain plots. The plots may be + * turned off by setting the appropriate boolean value in the + * constructor to False. + */ + + class QTGUI_API sink_f : virtual public gr_block + { + public: + // gr::qtgui::sink_f::sptr + typedef boost::shared_ptr<sink_f> sptr; + + /*! + * \brief Build a floating point qtgui sink. + * + * \param fftsize size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param plotfreq Toggle frequency plot on/off + * \param plotwaterfall Toggle waterfall plot on/off + * \param plottime Toggle time plot on/off + * \param plotconst Toggle constellation plot on/off + * \param parent a QWidget parent object, if any + */ + static sptr make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + virtual void set_fft_power_db(double min, double max) = 0; + + //void set_time_domain_axis(double min, double max); + //void set_constellation_axis(double xmin, double xmax, + // double ymin, double ymax); + //void set_constellation_pen_size(int size); + + virtual void set_update_time(double t) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_SINK_F_H */ diff --git a/gr-qtgui/include/qtgui/time_sink_c.h b/gr-qtgui/include/qtgui/time_sink_c.h new file mode 100644 index 0000000000..17176e65f9 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_sink_c.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_C_H +#define INCLUDED_QTGUI_TIME_SINK_C_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals in time. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a complex + * streams and plots them in the time domain. For each signal, both + * the signal's I and Q parts are plotted, and they are all plotted + * with a different color, and the \a set_title and \a set_color + * functions can be used to change the lable and color for a given + * input number. + */ + class QTGUI_API time_sink_c : virtual public gr_sync_block + { + public: + // gr::qtgui::time_sink_c::sptr + typedef boost::shared_ptr<time_sink_c> sptr; + + /*! + * \brief Build complex time sink + * + * \param size number of points to plot at once + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_time_domain_axis(double min, double max) = 0; + virtual void set_update_time(double t) = 0; + virtual void set_title(int which, const std::string &title) = 0; + virtual void set_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, Qt::PenStyle style) = 0; + virtual void set_line_marker(int which, QwtSymbol::Style marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui/time_sink_f.h b/gr-qtgui/include/qtgui/time_sink_f.h new file mode 100644 index 0000000000..df6ba9f554 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_sink_f.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 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. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_F_H +#define INCLUDED_QTGUI_TIME_SINK_F_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals in time. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a float streams + * and plots them in the time domain. Each signal is plotted with a + * different color, and the \a set_title and \a set_color functions + * can be used to change the lable and color for a given input number. + */ + class QTGUI_API time_sink_f : virtual public gr_sync_block + { + public: + // gr::qtgui::time_sink_f::sptr + typedef boost::shared_ptr<time_sink_f> sptr; + + /*! + * \brief Build floating point time sink + * + * \param size number of points to plot at once + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_time_domain_axis(double min, double max) = 0; + virtual void set_update_time(double t) = 0; + virtual void set_title(int which, const std::string &title) = 0; + virtual void set_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, Qt::PenStyle style) = 0; + virtual void set_line_marker(int which, QwtSymbol::Style marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_SINK_F_H */ diff --git a/gr-qtgui/include/qtgui_util.h b/gr-qtgui/include/qtgui/utils.h index 2deaddb947..f490c48567 100644 --- a/gr-qtgui/include/qtgui_util.h +++ b/gr-qtgui/include/qtgui/utils.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2011 Free Software Foundation, Inc. + * Copyright 2011,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -20,16 +20,15 @@ * Boston, MA 02110-1301, USA. */ -#ifndef INCLUDED_QTGUI_UTIL_H -#define INCLUDED_QTGUI_UTIL_H +#ifndef INCLUDED_QTGUI_UTILS_H +#define INCLUDED_QTGUI_UTILS_H #include <qevent.h> -#include <gr_qtgui_api.h> +#include <qtgui/api.h> #include <qwt_plot_picker.h> #include <qwt_picker_machine.h> - -class GR_QTGUI_API QwtDblClickPlotPicker: public QwtPlotPicker +class QTGUI_API QwtDblClickPlotPicker: public QwtPlotPicker { public: QwtDblClickPlotPicker(QwtPlotCanvas *); @@ -38,20 +37,19 @@ public: virtual QwtPickerMachine * stateMachine(int) const; }; -class GR_QTGUI_API QwtPickerDblClickPointMachine: public QwtPickerMachine +class QTGUI_API QwtPickerDblClickPointMachine: public QwtPickerMachine { public: QwtPickerDblClickPointMachine(); ~QwtPickerDblClickPointMachine(); #if QWT_VERSION < 0x060000 - virtual CommandList transition( const QwtEventPattern &eventPattern, - const QEvent *e); + virtual CommandList #else virtual QList<QwtPickerMachine::Command> - transition( const QwtEventPattern &eventPattern, - const QEvent *e); #endif + transition( const QwtEventPattern &eventPattern, + const QEvent *e); }; -#endif /* INCLUDED_QTGUI_UTIL_H */ +#endif /* INCLUDED_QTGUI_UTILS_H */ diff --git a/gr-qtgui/include/qtgui/waterfall_sink_c.h b/gr-qtgui/include/qtgui/waterfall_sink_c.h new file mode 100644 index 0000000000..838d50f44d --- /dev/null +++ b/gr-qtgui/include/qtgui/waterfall_sink_c.h @@ -0,0 +1,100 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_WATERFALL_SINK_C_H +#define INCLUDED_QTGUI_WATERFALL_SINK_C_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * waterfall (spectrogram) plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a complex + * streams and plots a waterfall (spectrogram) plot. + * + * Note that unlike the other qtgui sinks, this one does not + * support multiple input streams. We have yet to figure out a + * good way to display multiple, independent signals on this kind + * of a plot. If there are any suggestions or examples of this, we + * would love to see them. Otherwise, to display multiple signals + * here, it's probably best to sum the signals together and + * connect that here. + */ + class QTGUI_API waterfall_sink_c : virtual public gr_sync_block + { + public: + // gr::qtgui::waterfall_sink_c::sptr + typedef boost::shared_ptr<waterfall_sink_c> sptr; + + + /*! + * \brief Build a complex waterfall sink. + * + * \param size size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + virtual void set_fft_average(const float fftavg) = 0; + virtual float fft_average() const = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_color(const std::string &color) = 0; + virtual void set_line_width(int width) = 0; + virtual void set_line_style(Qt::PenStyle style) = 0; + virtual void set_line_marker(QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_WATERFALL_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui/waterfall_sink_f.h b/gr-qtgui/include/qtgui/waterfall_sink_f.h new file mode 100644 index 0000000000..0ee38256c3 --- /dev/null +++ b/gr-qtgui/include/qtgui/waterfall_sink_f.h @@ -0,0 +1,99 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_WATERFALL_SINK_F_H +#define INCLUDED_QTGUI_WATERFALL_SINK_F_H + +#include <qtgui/api.h> +#include <gr_sync_block.h> + +#include <Python.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * waterfall (spectrogram) plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink the takes set of a floating + * point streams and plots a waterfall (spectrogram) plot. + * + * Note that unlike the other qtgui sinks, this one does not + * support multiple input streams. We have yet to figure out a + * good way to display multiple, independent signals on this kind + * of a plot. If there are any suggestions or examples of this, we + * would love to see them. Otherwise, to display multiple signals + * here, it's probably best to sum the signals together and + * connect that here. + */ + class QTGUI_API waterfall_sink_f : virtual public gr_sync_block + { + public: + // gr::qtgui::waterfall_sink_f::sptr + typedef boost::shared_ptr<waterfall_sink_f> sptr; + + /*! + * \brief Build a floating point waterfall sink. + * + * \param size size of the FFT to compute and display + * \param wintype type of window to apply (see filter/firdes.h) + * \param fc center frequency of signal (use for x-axis labels) + * \param bw bandwidth of signal (used to set x-axis labels) + * \param name title for the plot + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_fft_size(const int fftsize) = 0; + virtual int fft_size() const = 0; + virtual void set_fft_average(const float fftavg) = 0; + virtual float fft_average() const = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_color(const std::string &color) = 0; + virtual void set_line_width(int width) = 0; + virtual void set_line_style(Qt::PenStyle style) = 0; + virtual void set_line_marker(QwtSymbol::Style marker) = 0; + + virtual void set_size(int width, int height) = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_WATERFALL_SINK_F_H */ diff --git a/gr-qtgui/include/qtgui_sink_c.h b/gr-qtgui/include/qtgui_sink_c.h deleted file mode 100644 index 68fff368a9..0000000000 --- a/gr-qtgui/include/qtgui_sink_c.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2011 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. - */ - -#ifndef INCLUDED_QTGUI_SINK_C_H -#define INCLUDED_QTGUI_SINK_C_H - -#include <Python.h> -#include <gr_qtgui_api.h> -#include <gr_block.h> -#include <gr_firdes.h> -#include <gri_fft.h> -#include <qapplication.h> -#include <gruel/high_res_timer.h> -#include "SpectrumGUIClass.h" - -class qtgui_sink_c; -typedef boost::shared_ptr<qtgui_sink_c> qtgui_sink_c_sptr; - -GR_QTGUI_API qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, - double fc=0, double bandwidth=1.0, - const std::string &name="Spectrum Display", - bool plotfreq=true, bool plotwaterfall=true, - bool plottime=true, bool plotconst=true, - QWidget *parent=NULL); - -/*! - * \brief A graphical sink to display freq, spec, time, and const plots. - * \ingroup qtgui_blk - * - * This is a QT-based graphical sink the takes a complex stream and - * plots it. The default action is to plot the signal as a PSD (FFT), - * spectrogram (waterfall), time domain I&Q, and constellation (I - * vs. Q) plots. The plots may be turned off by setting the - * appropriate boolean value in the constructor to False. - */ - -class GR_QTGUI_API qtgui_sink_c : public gr_block -{ -private: - friend GR_QTGUI_API qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - qtgui_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - - void forecast(int noutput_items, gr_vector_int &ninput_items_required); - - void initialize(); - - int d_fftsize; - gr_firdes::win_type d_wintype; - std::vector<float> d_window; - double d_center_freq; - double d_bandwidth; - std::string d_name; - gruel::high_res_timer_type d_last_update; - bool d_update_active; - - bool d_shift; - gri_fft_complex *d_fft; - - int d_index; - gr_complex *d_residbuf; - - bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst; - - gruel::high_res_timer_type d_update_time; - - QWidget *d_parent; - SpectrumGUIClass *d_main_gui; - - void windowreset(); - void buildwindow(); - void fftresize(); - void fft(const gr_complex *data_in, int size); - -public: - ~qtgui_sink_c(); - void exec_(); - QWidget* qwidget(); - PyObject* pyqwidget(); - - void set_frequency_range(const double centerfreq, - const double bandwidth); - - void set_time_domain_axis(double min, double max); - void set_constellation_axis(double xmin, double xmax, - double ymin, double ymax); - void set_constellation_pen_size(int size); - void set_frequency_axis(double min, double max); - - void set_update_time(double t); - - QApplication *d_qApplication; - - int general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif /* INCLUDED_QTGUI_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui_sink_f.h b/gr-qtgui/include/qtgui_sink_f.h deleted file mode 100644 index 709f02a2f5..0000000000 --- a/gr-qtgui/include/qtgui_sink_f.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2011 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. - */ - -#ifndef INCLUDED_QTGUI_SINK_F_H -#define INCLUDED_QTGUI_SINK_F_H - -#include <Python.h> -#include <gr_qtgui_api.h> -#include <gr_block.h> -#include <gr_firdes.h> -#include <gri_fft.h> -#include <qapplication.h> -#include "SpectrumGUIClass.h" - -class qtgui_sink_f; -typedef boost::shared_ptr<qtgui_sink_f> qtgui_sink_f_sptr; - -GR_QTGUI_API qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, - double fc=0, double bw=1.0, - const std::string &name="Spectrum Display", - bool plotfreq=true, bool plotwaterfall=true, - bool plottime=true, bool plotconst=false, - QWidget *parent=NULL); - -/*! - * \brief A graphical sink to display freq, spec, and time. - * \ingroup qtgui_blk - * - * This is a QT-based graphical sink the takes a float stream and - * plots it. The default action is to plot the signal as a PSD (FFT), - * spectrogram (waterfall), and time domain plots. The plots may be - * turned off by setting the appropriate boolean value in the - * constructor to False. - */ - -class GR_QTGUI_API qtgui_sink_f : public gr_block -{ -private: - friend GR_QTGUI_API qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - qtgui_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - - void forecast(int noutput_items, gr_vector_int &ninput_items_required); - - void initialize(); - - int d_fftsize; - gr_firdes::win_type d_wintype; - std::vector<float> d_window; - double d_center_freq; - double d_bandwidth; - std::string d_name; - - bool d_shift; - gri_fft_complex *d_fft; - - int d_index; - float *d_residbuf; - - bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst; - - double d_update_time; - - QWidget *d_parent; - SpectrumGUIClass *d_main_gui; - - void windowreset(); - void buildwindow(); - void fftresize(); - void fft(const float *data_in, int size); - -public: - ~qtgui_sink_f(); - void exec_(); - QWidget* qwidget(); - PyObject* pyqwidget(); - - void set_frequency_range(const double centerfreq, - const double bandwidth); - - void set_time_domain_axis(double min, double max); - void set_constellation_axis(double xmin, double xmax, - double ymin, double ymax); - void set_constellation_pen_size(int size); - void set_frequency_axis(double min, double max); - - void set_update_time(double t); - - QApplication *d_qApplication; - - int general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif /* INCLUDED_QTGUI_SINK_F_H */ diff --git a/gr-qtgui/include/qtgui_time_sink_c.h b/gr-qtgui/include/qtgui_time_sink_c.h deleted file mode 100644 index 561d796cfa..0000000000 --- a/gr-qtgui/include/qtgui_time_sink_c.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -#ifndef INCLUDED_QTGUI_TIME_SINK_C_H -#define INCLUDED_QTGUI_TIME_SINK_C_H - -#include <Python.h> -#include <gr_qtgui_api.h> -#include <gr_sync_block.h> -#include <gr_firdes.h> -#include <gri_fft.h> -#include <qapplication.h> -#include <timedisplayform.h> - -class qtgui_time_sink_c; -typedef boost::shared_ptr<qtgui_time_sink_c> qtgui_time_sink_c_sptr; - -GR_QTGUI_API qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, - const std::string &name, - int nconnectons=1, - QWidget *parent=NULL); - -/*! - * \brief A graphical sink to display multiple signals in time. - * \ingroup qtgui_blk - * - * This is a QT-based graphical sink the takes set of a complex - * streams and plots them in the time domain. For each signal, both - * the signal's I and Q parts are plotted, and they are all plotted - * with a different color, and the \a set_title and \a set_color - * functions can be used to change the lable and color for a given - * input number. - */ -class GR_QTGUI_API qtgui_time_sink_c : public gr_sync_block -{ -private: - friend GR_QTGUI_API qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent); - qtgui_time_sink_c(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent=NULL); - - void initialize(); - - int d_size; - double d_bandwidth; - std::string d_name; - int d_nconnections; - - int d_index; - std::vector<double*> d_residbufs; - - double d_update_time; - - QWidget *d_parent; - TimeDisplayForm *d_main_gui; - - gruel::high_res_timer_type d_current_time; - gruel::high_res_timer_type d_last_time; - -public: - ~qtgui_time_sink_c(); - void exec_(); - QWidget* qwidget(); - PyObject* pyqwidget(); - - void set_time_domain_axis(double min, double max); - void set_update_time(double t); - void set_title(int which, const std::string &title); - void set_color(int which, const std::string &color); - - QApplication *d_qApplication; - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif /* INCLUDED_QTGUI_TIME_SINK_C_H */ diff --git a/gr-qtgui/include/qtgui_time_sink_f.h b/gr-qtgui/include/qtgui_time_sink_f.h deleted file mode 100644 index 993e0556cf..0000000000 --- a/gr-qtgui/include/qtgui_time_sink_f.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -#ifndef INCLUDED_QTGUI_TIME_SINK_F_H -#define INCLUDED_QTGUI_TIME_SINK_F_H - -#include <Python.h> -#include <gr_qtgui_api.h> -#include <gr_sync_block.h> -#include <gr_firdes.h> -#include <gri_fft.h> -#include <qapplication.h> -#include <timedisplayform.h> - -class qtgui_time_sink_f; -typedef boost::shared_ptr<qtgui_time_sink_f> qtgui_time_sink_f_sptr; - -GR_QTGUI_API qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, - const std::string &name, - int nconnectons=1, - QWidget *parent=NULL); - -/*! - * \brief A graphical sink to display multiple signals in time. - * \ingroup qtgui_blk - * - * This is a QT-based graphical sink the takes set of a float streams - * and plots them in the time domain. Each signal is plotted with a - * different color, and the \a set_title and \a set_color functions - * can be used to change the lable and color for a given input number. - */ -class GR_QTGUI_API qtgui_time_sink_f : public gr_sync_block -{ -private: - friend GR_QTGUI_API qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent); - qtgui_time_sink_f(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent=NULL); - - void initialize(); - - int d_size; - double d_bandwidth; - std::string d_name; - int d_nconnections; - - int d_index; - std::vector<double*> d_residbufs; - - double d_update_time; - - QWidget *d_parent; - TimeDisplayForm *d_main_gui; - - gruel::high_res_timer_type d_current_time; - gruel::high_res_timer_type d_last_time; - -public: - ~qtgui_time_sink_f(); - void exec_(); - QWidget* qwidget(); - PyObject* pyqwidget(); - - void set_time_domain_axis(double min, double max); - void set_update_time(double t); - void set_title(int which, const std::string &title); - void set_color(int which, const std::string &color); - - QApplication *d_qApplication; - - int work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); -}; - -#endif /* INCLUDED_QTGUI_TIME_SINK_F_H */ diff --git a/gr-qtgui/lib/CMakeLists.txt b/gr-qtgui/lib/CMakeLists.txt index 2dc35e81cb..beebd98cc5 100644 --- a/gr-qtgui/lib/CMakeLists.txt +++ b/gr-qtgui/lib/CMakeLists.txt @@ -22,7 +22,13 @@ ######################################################################## set(qtgui_moc_hdrs spectrumdisplayform.h + displayform.h timedisplayform.h + freqdisplayform.h + constellationdisplayform.h + waterfalldisplayform.h + form_menus.h + DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h WaterfallDisplayPlot.h @@ -41,20 +47,30 @@ endif(NOT EXISTS ${spectrum_ui_hdr}) set(qtgui_srcs ${qtgui_moc_srcs} ${qtgui_ui_hdrs} + DisplayPlot.cc FrequencyDisplayPlot.cc TimeDomainDisplayPlot.cc WaterfallDisplayPlot.cc waterfallGlobalData.cc ConstellationDisplayPlot.cc spectrumdisplayform.cc + displayform.cc timedisplayform.cc + freqdisplayform.cc + constellationdisplayform.cc + waterfalldisplayform.cc SpectrumGUIClass.cc spectrumUpdateEvents.cc plot_waterfall.cc - qtgui_sink_c.cc - qtgui_sink_f.cc - qtgui_time_sink_c.cc - qtgui_time_sink_f.cc + sink_c_impl.cc + sink_f_impl.cc + time_sink_c_impl.cc + time_sink_f_impl.cc + freq_sink_c_impl.cc + freq_sink_f_impl.cc + const_sink_c_impl.cc + waterfall_sink_c_impl.cc + waterfall_sink_f_impl.cc qtgui_util.cc ) @@ -63,12 +79,18 @@ set(qtgui_srcs ######################################################################## include_directories( ${GNURADIO_CORE_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} ${GR_QTGUI_INCLUDE_DIRS} + ${VOLK_INCLUDE_DIRS} ) include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) +include_directories(${FFTW3F_INCLUDE_DIRS}) +link_directories(${FFTW3F_LIBRARY_DIRS}) + include_directories(${QWT_INCLUDE_DIRS}) link_directories(${QWT_LIBRARY_DIRS}) @@ -80,9 +102,13 @@ include_directories(${PYTHON_INCLUDE_PATH}) #deprecated for dirs (cmake 2.6) ######################################################################## list(APPEND qtgui_libs gnuradio-core + gnuradio-fft + gnuradio-filter + volk ${QT_LIBRARIES} ${QWT_LIBRARIES} ${PYTHON_LIBRARIES} + ${FFTW3F_LIBRARIES} ) add_definitions(-DQWT_DLL) #setup QWT library linkage @@ -94,6 +120,7 @@ GR_LIBRARY_FOO(gnuradio-qtgui RUNTIME_COMPONENT "qtgui_runtime" DEVEL_COMPONENT # Install the header files ######################################################################## install(FILES + DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h WaterfallDisplayPlot.h @@ -101,7 +128,12 @@ install(FILES ConstellationDisplayPlot.h plot_waterfall.h spectrumdisplayform.h + displayform.h timedisplayform.h + freqdisplayform.h + constellationdisplayform.h + waterfalldisplayform.h + form_menus.h SpectrumGUIClass.h spectrumUpdateEvents.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.cc b/gr-qtgui/lib/ConstellationDisplayPlot.cc index 7a595fef45..5bc89f83fa 100644 --- a/gr-qtgui/lib/ConstellationDisplayPlot.cc +++ b/gr-qtgui/lib/ConstellationDisplayPlot.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -27,6 +27,7 @@ #include <qwt_scale_draw.h> #include <qwt_legend.h> +#include <QColor> #include <iostream> class ConstellationDisplayZoomer: public QwtPlotZoomer @@ -47,42 +48,22 @@ public: protected: using QwtPlotZoomer::trackerText; - virtual QwtText trackerText( const QwtDoublePoint& p ) const + virtual QwtText trackerText( const QPoint& p ) const { - QwtText t(QString("(%1, %2)").arg(p.x(), 0, 'f', 4). - arg(p.y(), 0, 'f', 4)); + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + QwtText t(QString("(%1, %2)").arg(dp.x(), 0, 'f', 4). + arg(dp.y(), 0, 'f', 4)); return t; } }; -ConstellationDisplayPlot::ConstellationDisplayPlot(QWidget* parent) - : QwtPlot(parent) +ConstellationDisplayPlot::ConstellationDisplayPlot(int nplots, QWidget* parent) + : DisplayPlot(nplots, parent) { - _lastReplot = 0; - resize(parent->width(), parent->height()); _numPoints = 1024; _penSize = 5; - _realDataPoints = new double[_numPoints]; - _imagDataPoints = new double[_numPoints]; - - // Disable polygon clipping -#if QWT_VERSION < 0x060000 - QwtPainter::setDeviceClipping(false); -#else - QwtPainter::setPolylineSplitting(false); -#endif - -#if QWT_VERSION < 0x060000 - // We don't need the cache here - canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); - canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); -#endif - - QPalette palette; - palette.setColor(canvas()->backgroundRole(), QColor("white")); - canvas()->setPalette(palette); setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); set_xaxis(-2.0, 2.0); @@ -92,21 +73,6 @@ ConstellationDisplayPlot::ConstellationDisplayPlot(QWidget* parent) set_yaxis(-2.0, 2.0); setAxisTitle(QwtPlot::yLeft, "Quadrature"); - // Automatically deleted when parent is deleted - _plot_curve = new QwtPlotCurve("Constellation Points"); - _plot_curve->attach(this); - _plot_curve->setPen(QPen(Qt::blue, _penSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - _plot_curve->setStyle(QwtPlotCurve::Dots); - -#if QWT_VERSION < 0x060000 - _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints); -#else - _plot_curve->setRawSamples(_realDataPoints, _imagDataPoints, _numPoints); -#endif - - memset(_realDataPoints, 0x0, _numPoints*sizeof(double)); - memset(_imagDataPoints, 0x0, _numPoints*sizeof(double)); - _zoomer = new ConstellationDisplayZoomer(canvas()); #if QWT_VERSION < 0x060000 @@ -118,56 +84,55 @@ ConstellationDisplayPlot::ConstellationDisplayPlot(QWidget* parent) _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, Qt::RightButton); - _panner = new QwtPlotPanner(canvas()); - _panner->setAxisEnabled(QwtPlot::yRight, false); - _panner->setMouseButton(Qt::MidButton); - - // Avoid jumping when labels with more/less digits - // appear/disappear when scrolling vertically - - const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); - QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); - sd->setMinimumExtent( fm.width("100.00") ); - const QColor c(Qt::darkRed); _zoomer->setRubberBandPen(c); _zoomer->setTrackerPen(c); - // emit the position of clicks on widget - _picker = new QwtDblClickPlotPicker(canvas()); + QList<QColor> colors; + colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) + << QColor(Qt::black) << QColor(Qt::cyan) << QColor(Qt::magenta) + << QColor(Qt::yellow) << QColor(Qt::gray) << QColor(Qt::darkRed) + << QColor(Qt::darkGreen) << QColor(Qt::darkBlue) << QColor(Qt::darkGray); + + // Setup dataPoints and plot vectors + // Automatically deleted when parent is deleted + for(int i = 0; i < _nplots; i++) { + _realDataPoints.push_back(new double[_numPoints]); + _imagDataPoints.push_back(new double[_numPoints]); + memset(_realDataPoints[i], 0x0, _numPoints*sizeof(double)); + memset(_imagDataPoints[i], 0x0, _numPoints*sizeof(double)); + + _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); + _plot_curve[i]->attach(this); + _plot_curve[i]->setPen(QPen(colors[i])); + + QwtSymbol *symbol = new QwtSymbol(QwtSymbol::NoSymbol, QBrush(colors[i]), QPen(colors[i]), QSize(7,7)); #if QWT_VERSION < 0x060000 - connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), - this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); + _plot_curve[i]->setRawData(_realDataPoints[i], _imagDataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(*symbol); #else - connect(_picker, SIGNAL(selected(const QPointF &)), - this, SLOT(OnPickerPointSelected6(const QPointF &))); + _plot_curve[i]->setRawSamples(_realDataPoints[i], _imagDataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(symbol); #endif - connect(this, SIGNAL(legendChecked(QwtPlotItem *, bool ) ), - this, SLOT(LegendEntryChecked(QwtPlotItem *, bool ) )); + setLineStyle(i, Qt::NoPen); + setLineMarker(i, QwtSymbol::Ellipse); + } } ConstellationDisplayPlot::~ConstellationDisplayPlot() { - delete[] _realDataPoints; - delete[] _imagDataPoints; + for(int i = 0; i < _nplots; i++) { + delete [] _realDataPoints[i]; + delete [] _imagDataPoints[i]; + } // _fft_plot_curves deleted when parent deleted // _zoomer and _panner deleted when parent deleted } void -ConstellationDisplayPlot::set_pen_size(int size) -{ - if(size > 0 && size < 30){ - _penSize = size; - _plot_curve->setPen(QPen(Qt::blue, _penSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - } -} - - -void ConstellationDisplayPlot::set_xaxis(double min, double max) { setAxisScale(QwtPlot::xBottom, min, max); @@ -187,69 +152,72 @@ ConstellationDisplayPlot::set_axis(double xmin, double xmax, set_yaxis(ymin, ymax); } -void ConstellationDisplayPlot::replot() +void +ConstellationDisplayPlot::set_pen_size(int size) { - QwtPlot::replot(); + if(size > 0 && size < 30){ + _penSize = size; + for(int i = 0; i < _nplots; i++) { + _plot_curve[i]->setPen(QPen(Qt::blue, _penSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + } + } } void -ConstellationDisplayPlot::resizeSlot( QSize *s ) +ConstellationDisplayPlot::replot() { - resize(s->width(), s->height()); + QwtPlot::replot(); } -void ConstellationDisplayPlot::PlotNewData(const double* realDataPoints, - const double* imagDataPoints, - const int64_t numDataPoints, - const double timeInterval) -{ - if((numDataPoints > 0) && - (gruel::high_res_timer_now() - _lastReplot > timeInterval*gruel::high_res_timer_tps())) { - if(numDataPoints != _numPoints){ - _numPoints = numDataPoints; - - delete[] _realDataPoints; - delete[] _imagDataPoints; - _realDataPoints = new double[_numPoints]; - _imagDataPoints = new double[_numPoints]; +void +ConstellationDisplayPlot::PlotNewData(const std::vector<double*> realDataPoints, + const std::vector<double*> imagDataPoints, + const int64_t numDataPoints, + const double timeInterval) +{ + if(!_stop) { + if((numDataPoints > 0)) { + if(numDataPoints != _numPoints) { + _numPoints = numDataPoints; + + for(int i = 0; i < _nplots; i++) { + delete [] _realDataPoints[i]; + delete [] _imagDataPoints[i]; + _realDataPoints[i] = new double[_numPoints]; + _imagDataPoints[i] = new double[_numPoints]; #if QWT_VERSION < 0x060000 - _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints); + _plot_curve[i]->setRawData(_realDataPoints[i], _imagDataPoints[i], _numPoints); #else - _plot_curve->setRawSamples(_realDataPoints, _imagDataPoints, _numPoints); + _plot_curve[i]->setRawSamples(_realDataPoints[i], _imagDataPoints[i], _numPoints); #endif - } + } + } - memcpy(_realDataPoints, realDataPoints, numDataPoints*sizeof(double)); - memcpy(_imagDataPoints, imagDataPoints, numDataPoints*sizeof(double)); + for(int i = 0; i < _nplots; i++) { + memcpy(_realDataPoints[i], realDataPoints[i], numDataPoints*sizeof(double)); + memcpy(_imagDataPoints[i], imagDataPoints[i], numDataPoints*sizeof(double)); + } - replot(); + replot(); - _lastReplot = gruel::high_res_timer_now(); + } } } void -ConstellationDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on) -{ - plotItem->setVisible(!on); -} - -void -ConstellationDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) -{ - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); -} - -void -ConstellationDisplayPlot::OnPickerPointSelected6(const QPointF & p) +ConstellationDisplayPlot::PlotNewData(const double* realDataPoints, + const double* imagDataPoints, + const int64_t numDataPoints, + const double timeInterval) { - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); + std::vector<double*> vecRealDataPoints; + std::vector<double*> vecImagDataPoints; + vecRealDataPoints.push_back((double*)realDataPoints); + vecImagDataPoints.push_back((double*)imagDataPoints); + PlotNewData(vecRealDataPoints, vecImagDataPoints, + numDataPoints, timeInterval); } #endif /* CONSTELLATION_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.h b/gr-qtgui/lib/ConstellationDisplayPlot.h index 9e0f6f26a8..f3cbeef1cb 100644 --- a/gr-qtgui/lib/ConstellationDisplayPlot.h +++ b/gr-qtgui/lib/ConstellationDisplayPlot.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -20,43 +20,34 @@ * Boston, MA 02110-1301, USA. */ -#ifndef CONSTELLATION_DISPLAY_PLOT_HPP -#define CONSTELLATION_DISPLAY_PLOT_HPP +#ifndef CONSTELLATION_DISPLAY_PLOT_H +#define CONSTELLATION_DISPLAY_PLOT_H #include <stdint.h> #include <cstdio> -#include <qwt_plot.h> -#include <qwt_painter.h> -#include <qwt_plot_canvas.h> -#include <qwt_plot_curve.h> -#include <qwt_scale_engine.h> -#include <qwt_scale_widget.h> -#include <qwt_plot_zoomer.h> -#include <qwt_plot_panner.h> -#include <qwt_plot_marker.h> -#include <gruel/high_res_timer.h> -#include <qwt_symbol.h> -#include <qtgui_util.h> +#include <vector> +#include "DisplayPlot.h" -#if QWT_VERSION >= 0x060000 -#include <qwt_point_3d.h> // doesn't seem necessary, but is... -#include <qwt_compat.h> -#endif - -class ConstellationDisplayPlot : public QwtPlot +class ConstellationDisplayPlot : public DisplayPlot { Q_OBJECT public: - ConstellationDisplayPlot(QWidget*); + ConstellationDisplayPlot(int nplots, QWidget*); virtual ~ConstellationDisplayPlot(); + void PlotNewData(const std::vector<double*> realDataPoints, + const std::vector<double*> imagDataPoints, + const int64_t numDataPoints, + const double timeInterval); + + // Old method to be removed void PlotNewData(const double* realDataPoints, const double* imagDataPoints, const int64_t numDataPoints, const double timeInterval); - virtual void replot(); + void replot(); void set_xaxis(double min, double max); void set_yaxis(double min, double max); @@ -65,37 +56,13 @@ public: void set_pen_size(int size); public slots: - void resizeSlot( QSize *s ); - - // Because of the preprocessing of slots in QT, these are no - // easily separated by the version check. Make one for each - // version until it's worked out. - void OnPickerPointSelected(const QwtDoublePoint & p); - void OnPickerPointSelected6(const QPointF & p); - -signals: - void plotPointSelected(const QPointF p); - -protected slots: - void LegendEntryChecked(QwtPlotItem *plotItem, bool on); - -protected: + // set axis private: - QwtPlotCurve* _plot_curve; - - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - - QwtDblClickPlotPicker *_picker; - - double* _realDataPoints; - double* _imagDataPoints; - - gruel::high_res_timer_type _lastReplot; + std::vector<double*> _realDataPoints; + std::vector<double*> _imagDataPoints; - int64_t _numPoints; int64_t _penSize; }; -#endif /* CONSTELLATION_DISPLAY_PLOT_HPP */ +#endif /* CONSTELLATION_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/DisplayPlot.cc b/gr-qtgui/lib/DisplayPlot.cc new file mode 100644 index 0000000000..577b4f1483 --- /dev/null +++ b/gr-qtgui/lib/DisplayPlot.cc @@ -0,0 +1,221 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <DisplayPlot.h> + +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <QColor> +#include <cmath> +#include <iostream> + +DisplayPlot::DisplayPlot(int nplots, QWidget* parent) + : QwtPlot(parent), _nplots(nplots), _stop(false) +{ + resize(parent->width(), parent->height()); + + // Disable polygon clipping +#if QWT_VERSION < 0x060000 + QwtPainter::setDeviceClipping(false); +#else + QwtPainter::setPolylineSplitting(false); +#endif + +#if QWT_VERSION < 0x060000 + // We don't need the cache here + canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); + canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); +#endif + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // emit the position of clicks on widget + _picker = new QwtDblClickPlotPicker(canvas()); + +#if QWT_VERSION < 0x060000 + connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), + this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); +#else + _picker->setStateMachine(new QwtPickerDblClickPointMachine()); + connect(_picker, SIGNAL(selected(const QPointF &)), + this, SLOT(OnPickerPointSelected6(const QPointF &))); +#endif + + // Configure magnify on mouse wheel + _magnifier = new QwtPlotMagnifier(canvas()); + _magnifier->setAxisEnabled(QwtPlot::xBottom, false); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + QwtLegend* legendDisplay = new QwtLegend(this); + legendDisplay->setItemMode(QwtLegend::CheckableItem); + insertLegend(legendDisplay); + + connect(this, SIGNAL(legendChecked(QwtPlotItem *, bool)), + this, SLOT(LegendEntryChecked(QwtPlotItem *, bool))); +} + +DisplayPlot::~DisplayPlot() +{ + // _zoomer and _panner deleted when parent deleted +} + +void +DisplayPlot::setYaxis(double min, double max) +{ + setAxisScale(QwtPlot::yLeft, min, max); + _zoomer->setZoomBase(); +} + +void +DisplayPlot::setXaxis(double min, double max) +{ + setAxisScale(QwtPlot::xBottom, min, max); + _zoomer->setZoomBase(); +} + +void +DisplayPlot::setTitle(int which, QString title) +{ + _plot_curve[which]->setTitle(title); +} + +QString +DisplayPlot::title(int which) +{ + return _plot_curve[which]->title().text(); +} + +void +DisplayPlot::setColor(int which, QString color) +{ + // Set the color of the pen + QPen pen(_plot_curve[which]->pen()); + pen.setColor(color); + _plot_curve[which]->setPen(pen); + + // And set the color of the markers +#if QWT_VERSION < 0x060000 + //_plot_curve[which]->setBrush(QBrush(QColor(color))); + _plot_curve[which]->setPen(pen); + + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + setLineMarker(which, sym.style()); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + sym->setColor(color); + sym->setPen(pen); + _plot_curve[which]->setSymbol(sym); +#endif +} + +void +DisplayPlot::setLineWidth(int which, int width) +{ + // Set the new line width + QPen pen(_plot_curve[which]->pen()); + pen.setWidth(width); + _plot_curve[which]->setPen(pen); + + // Scale the marker size proportionally +#if QWT_VERSION < 0x060000 + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + sym.setSize(7+10*log10(width), 7+10*log10(width)); + _plot_curve[which]->setSymbol(sym); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + sym->setSize(7+10*log10(width), 7+10*log10(width)); + _plot_curve[which]->setSymbol(sym); +#endif +} + +void +DisplayPlot::setLineStyle(int which, Qt::PenStyle style) +{ + QPen pen(_plot_curve[which]->pen()); + pen.setStyle(style); + _plot_curve[which]->setPen(pen); +} + +void +DisplayPlot::setLineMarker(int which, QwtSymbol::Style marker) +{ +#if QWT_VERSION < 0x060000 + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + QPen pen(_plot_curve[which]->pen()); + QBrush brush(pen.color()); + sym.setStyle(marker); + sym.setPen(pen); + sym.setBrush(brush); + _plot_curve[which]->setSymbol(sym); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + sym->setStyle(marker); + _plot_curve[which]->setSymbol(sym); +#endif +} + +void +DisplayPlot::setStop(bool on) +{ + _stop = on; +} + +void +DisplayPlot::resizeSlot( QSize *s ) +{ + // -10 is to spare some room for the legend and x-axis label + resize(s->width()-10, s->height()-10); +} + +void DisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on) +{ + plotItem->setVisible(!on); + replot(); +} + +void +DisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); + emit plotPointSelected(point); +} + +void +DisplayPlot::OnPickerPointSelected6(const QPointF & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); + emit plotPointSelected(point); +} diff --git a/gr-qtgui/lib/DisplayPlot.h b/gr-qtgui/lib/DisplayPlot.h new file mode 100644 index 0000000000..975a92a18d --- /dev/null +++ b/gr-qtgui/lib/DisplayPlot.h @@ -0,0 +1,101 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2010,2011,2012 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. + */ + +#ifndef DOMAIN_DISPLAY_PLOT_H +#define DOMAIN_DISPLAY_PLOT_H + +#include <stdint.h> +#include <cstdio> +#include <vector> +#include <qwt_plot.h> +#include <qwt_painter.h> +#include <qwt_plot_canvas.h> +#include <qwt_plot_curve.h> +#include <qwt_scale_engine.h> +#include <qwt_scale_widget.h> +#include <qwt_plot_zoomer.h> +#include <qwt_plot_panner.h> +#include <qwt_plot_magnifier.h> +#include <qwt_plot_marker.h> +#include <qwt_symbol.h> +#include <qtgui/utils.h> + +#if QWT_VERSION >= 0x060000 +#include <qwt_compat.h> +#endif + +class DisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + DisplayPlot(int nplots, QWidget*); + virtual ~DisplayPlot(); + + virtual void replot() = 0; + + // Make sure to create your won PlotNewData method in the derived + // class: + // void PlotNewData(...); + +public slots: + void setYaxis(double min, double max); + void setXaxis(double min, double max); + void setTitle(int which, QString title); + void setColor(int which, QString color); + void setLineWidth(int which, int width); + void setLineStyle(int which, Qt::PenStyle style); + void setLineMarker(int which, QwtSymbol::Style marker); + + void setStop(bool on); + + QString title(int which); + + void resizeSlot(QSize *s); + + // Because of the preprocessing of slots in QT, these are not + // easily separated by the version check. Make one for each + // version until it's worked out. + void OnPickerPointSelected(const QwtDoublePoint & p); + void OnPickerPointSelected6(const QPointF & p); + +signals: + void plotPointSelected(const QPointF p); + +protected slots: + void LegendEntryChecked(QwtPlotItem *plotItem, bool on); + +protected: + int _nplots; + std::vector<QwtPlotCurve*> _plot_curve; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + QwtDblClickPlotPicker *_picker; + QwtPlotMagnifier *_magnifier; + + int64_t _numPoints; + + bool _stop; +}; + +#endif /* DOMAIN_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.cc b/gr-qtgui/lib/FrequencyDisplayPlot.cc index b74d460150..c9fb94ad68 100644 --- a/gr-qtgui/lib/FrequencyDisplayPlot.cc +++ b/gr-qtgui/lib/FrequencyDisplayPlot.cc @@ -25,73 +25,28 @@ #include <FrequencyDisplayPlot.h> +#include "qtgui_types.h" #include <qwt_scale_draw.h> - -class FreqPrecisionClass -{ -public: - FreqPrecisionClass(const int freqPrecision) - { - _frequencyPrecision = freqPrecision; - } - - virtual ~FreqPrecisionClass() - { - } - - virtual unsigned int GetFrequencyPrecision() const - { - return _frequencyPrecision; - } - - virtual void SetFrequencyPrecision(const unsigned int newPrecision) - { - _frequencyPrecision = newPrecision; - } -protected: - unsigned int _frequencyPrecision; - -private: - -}; - -class FreqDisplayScaleDraw: public QwtScaleDraw, public FreqPrecisionClass -{ -public: - FreqDisplayScaleDraw(const unsigned int precision) - : QwtScaleDraw(), FreqPrecisionClass(precision) - { - } - - virtual ~FreqDisplayScaleDraw() - { - } - - virtual QwtText label(double value) const - { - return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision()); - } - -protected: - -private: - -}; - -class FreqDisplayZoomer: public QwtPlotZoomer, public FreqPrecisionClass +#include <qwt_legend.h> +#include <qwt_legend_item.h> +#include <QColor> +#include <iostream> + +/*********************************************************************** + * Widget to provide mouse pointer coordinate text + **********************************************************************/ +class FreqDisplayZoomer: public QwtPlotZoomer, public FreqOffsetAndPrecisionClass { public: FreqDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision) - : QwtPlotZoomer(canvas),FreqPrecisionClass(freqPrecision) + : QwtPlotZoomer(canvas), + FreqOffsetAndPrecisionClass(freqPrecision) { setTrackerMode(QwtPicker::AlwaysOn); } - - virtual ~FreqDisplayZoomer(){ - - } - - virtual void updateTrackerText(){ + + virtual void updateTrackerText() + { updateDisplay(); } @@ -102,11 +57,12 @@ public: protected: using QwtPlotZoomer::trackerText; - virtual QwtText trackerText( const QwtDoublePoint& p ) const + virtual QwtText trackerText(QPoint const &p) const { - QwtText t(QString("%1 %2, %3 dB"). - arg(p.x(), 0, 'f', GetFrequencyPrecision()). - arg(_unitType.c_str()).arg(p.y(), 0, 'f', 2)); + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + QwtText t(QString("%1 %2, %3 dB") + .arg(dp.x(), 0, 'f', GetFrequencyPrecision()) + .arg(_unitType.c_str()).arg(dp.y(), 0, 'f', 2)); return t; } @@ -114,41 +70,23 @@ private: std::string _unitType; }; -FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) - : QwtPlot(parent) + +/*********************************************************************** + * Main frequency display plotter widget + **********************************************************************/ +FrequencyDisplayPlot::FrequencyDisplayPlot(int nplots, QWidget* parent) + : DisplayPlot(nplots, parent) { _startFrequency = 0; _stopFrequency = 4000; - _lastReplot = 0; - - resize(parent->width(), parent->height()); - _useCenterFrequencyFlag = false; _numPoints = 1024; - _dataPoints = new double[_numPoints]; _minFFTPoints = new double[_numPoints]; _maxFFTPoints = new double[_numPoints]; _xAxisPoints = new double[_numPoints]; - // Disable polygon clipping -#if QWT_VERSION < 0x060000 - QwtPainter::setDeviceClipping(false); -#else - QwtPainter::setPolylineSplitting(false); -#endif - -#if QWT_VERSION < 0x060000 - // We don't need the cache here - canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); - canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); -#endif - - QPalette palette; - palette.setColor(canvas()->backgroundRole(), QColor("white")); - canvas()->setPalette(palette); - setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); @@ -158,16 +96,31 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis); setAxisTitle(QwtPlot::yLeft, "Power (dB)"); - // Automatically deleted when parent is deleted - _fft_plot_curve = new QwtPlotCurve("Power Spectrum"); - _fft_plot_curve->attach(this); - _fft_plot_curve->setPen(QPen(Qt::blue)); + QList<QColor> colors; + colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) + << QColor(Qt::black) << QColor(Qt::cyan) << QColor(Qt::magenta) + << QColor(Qt::yellow) << QColor(Qt::gray) << QColor(Qt::darkRed) + << QColor(Qt::darkGreen) << QColor(Qt::darkBlue) << QColor(Qt::darkGray); + // Automatically deleted when parent is deleted + for(int i = 0; i < _nplots; i++) { + _dataPoints.push_back(new double[_numPoints]); + memset(_dataPoints[i], 0x0, _numPoints*sizeof(double)); + + _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); + _plot_curve[i]->attach(this); + _plot_curve[i]->setPen(QPen(colors[i])); + + const QwtSymbol *symbol = new QwtSymbol(QwtSymbol::NoSymbol, QBrush(colors[i]), QPen(colors[i]), QSize(7,7)); + #if QWT_VERSION < 0x060000 - _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); + _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(*symbol); #else - _fft_plot_curve->setRawSamples(_xAxisPoints, _dataPoints, _numPoints); + _plot_curve[i]->setRawSamples(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(symbol); #endif + } _min_fft_plot_curve = new QwtPlotCurve("Minimum Power"); _min_fft_plot_curve->attach(this); @@ -203,7 +156,6 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) _upper_intensity_marker->setLinePen(QPen(Qt::green, 0, Qt::DotLine)); _upper_intensity_marker->attach(this); - memset(_dataPoints, 0x0, _numPoints*sizeof(double)); memset(_xAxisPoints, 0x0, _numPoints*sizeof(double)); for(int64_t number = 0; number < _numPoints; number++){ @@ -248,21 +200,6 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) replot(); - // emit the position of clicks on widget - _picker = new QwtDblClickPlotPicker(canvas()); - -#if QWT_VERSION < 0x060000 - connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), - this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); -#else - connect(_picker, SIGNAL(selected(const QPointF &)), - this, SLOT(OnPickerPointSelected6(const QPointF &))); -#endif - - // Configure magnify on mouse wheel - _magnifier = new QwtPlotMagnifier(canvas()); - _magnifier->setAxisEnabled(QwtPlot::xBottom, false); - _zoomer = new FreqDisplayZoomer(canvas(), 0); #if QWT_VERSION < 0x060000 @@ -274,16 +211,6 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, Qt::RightButton); - _panner = new QwtPlotPanner(canvas()); - _panner->setAxisEnabled(QwtPlot::yRight, false); - _panner->setMouseButton(Qt::MidButton); - - // Avoid jumping when labels with more/less digits - // appear/disappear when scrolling vertically - - const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); - QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); - sd->setMinimumExtent( fm.width("100.00") ); const QColor c(Qt::darkRed); _zoomer->setRubberBandPen(c); @@ -291,17 +218,24 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) // Do this after the zoomer has been built _resetXAxisPoints(); + + // Turn off min/max hold plots in legend + QWidget *w; + QwtLegend* legendDisplay = legend(); + w = legendDisplay->find(_min_fft_plot_curve); + ((QwtLegendItem*)w)->setChecked(true); + w = legendDisplay->find(_max_fft_plot_curve); + ((QwtLegendItem*)w)->setChecked(true); } FrequencyDisplayPlot::~FrequencyDisplayPlot() { - delete[] _dataPoints; + for(int i = 0; i < _nplots; i++) + delete [] _dataPoints[i]; + delete[] _maxFFTPoints; delete[] _minFFTPoints; delete[] _xAxisPoints; - - // _fft_plot_curves deleted when parent deleted - // _zoomer and _panner deleted when parent deleted } void @@ -390,73 +324,83 @@ FrequencyDisplayPlot::replot() } void -FrequencyDisplayPlot::resizeSlot( QSize *s ) -{ - resize(s->width(), s->height()); -} - -void -FrequencyDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, +FrequencyDisplayPlot::PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude, const double timeInterval) { - // Only update plot if there is data and if the time interval has elapsed - if((numDataPoints > 0) && - (gruel::high_res_timer_now() - _lastReplot > timeInterval*gruel::high_res_timer_tps())) { - - if(numDataPoints != _numPoints) { - _numPoints = numDataPoints; - - delete[] _dataPoints; - delete[] _minFFTPoints; - delete[] _maxFFTPoints; - delete[] _xAxisPoints; - _dataPoints = new double[_numPoints]; - _xAxisPoints = new double[_numPoints]; - _minFFTPoints = new double[_numPoints]; - _maxFFTPoints = new double[_numPoints]; + if(!_stop) { + if(numDataPoints > 0) { + if(numDataPoints != _numPoints) { + _numPoints = numDataPoints; + + delete[] _minFFTPoints; + delete[] _maxFFTPoints; + delete[] _xAxisPoints; + _xAxisPoints = new double[_numPoints]; + _minFFTPoints = new double[_numPoints]; + _maxFFTPoints = new double[_numPoints]; + + for(int i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + _dataPoints[i] = new double[_numPoints]; #if QWT_VERSION < 0x060000 - _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); - _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); - _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); + _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); #else - _fft_plot_curve->setRawSamples(_xAxisPoints, _dataPoints, _numPoints); - _min_fft_plot_curve->setRawSamples(_xAxisPoints, _minFFTPoints, _numPoints); - _max_fft_plot_curve->setRawSamples(_xAxisPoints, _maxFFTPoints, _numPoints); + _plot_curve[i]->setRawSamples(_xAxisPoints, _dataPoints[i], _numPoints); + _min_fft_plot_curve->setRawSamples(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawSamples(_xAxisPoints, _maxFFTPoints, _numPoints); #endif + } - _resetXAxisPoints(); - ClearMaxData(); - ClearMinData(); - } - - memcpy(_dataPoints, dataPoints, numDataPoints*sizeof(double)); - for(int64_t point = 0; point < numDataPoints; point++){ - if(dataPoints[point] < _minFFTPoints[point]){ - _minFFTPoints[point] = dataPoints[point]; + _resetXAxisPoints(); + ClearMaxData(); + ClearMinData(); } - if(dataPoints[point] > _maxFFTPoints[point]){ - _maxFFTPoints[point] = dataPoints[point]; + + for(int i = 0; i < _nplots; i++) { + memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); } - } - _noiseFloorAmplitude = noiseFloorAmplitude; - _peakFrequency = peakFrequency; - _peakAmplitude = peakAmplitude; + for(int64_t point = 0; point < numDataPoints; point++){ + if(dataPoints[0][point] < _minFFTPoints[point]) { + _minFFTPoints[point] = dataPoints[0][point]; + } + if(dataPoints[0][point] > _maxFFTPoints[point]) { + _maxFFTPoints[point] = dataPoints[0][point]; + } + } - SetUpperIntensityLevel(_peakAmplitude); + _noiseFloorAmplitude = noiseFloorAmplitude; + _peakFrequency = peakFrequency; + _peakAmplitude = peakAmplitude; - replot(); + SetUpperIntensityLevel(_peakAmplitude); - _lastReplot = gruel::high_res_timer_now(); + replot(); + } } } void +FrequencyDisplayPlot::PlotNewData(const double* dataPoints, + const int64_t numDataPoints, + const double noiseFloorAmplitude, const double peakFrequency, + const double peakAmplitude, const double timeInterval) +{ + std::vector<double*> vecDataPoints; + vecDataPoints.push_back((double*)dataPoints); + PlotNewData(vecDataPoints, numDataPoints, noiseFloorAmplitude, + peakFrequency, peakAmplitude, timeInterval); +} + +void FrequencyDisplayPlot::ClearMaxData() { - for(int64_t number = 0; number < _numPoints; number++){ + for(int64_t number = 0; number < _numPoints; number++) { _maxFFTPoints[number] = _minYAxis; } } @@ -464,7 +408,7 @@ FrequencyDisplayPlot::ClearMaxData() void FrequencyDisplayPlot::ClearMinData() { - for(int64_t number = 0; number < _numPoints; number++){ + for(int64_t number = 0; number < _numPoints; number++) { _minFFTPoints[number] = _maxYAxis; } } @@ -484,9 +428,10 @@ FrequencyDisplayPlot::SetMinFFTVisible(const bool visibleFlag) void FrequencyDisplayPlot::_resetXAxisPoints() { - double fft_bin_size = (_stopFrequency-_startFrequency) / static_cast<double>(_numPoints); + double fft_bin_size = (_stopFrequency-_startFrequency) + / static_cast<double>(_numPoints); double freqValue = _startFrequency; - for(int64_t loc = 0; loc < _numPoints; loc++){ + for(int64_t loc = 0; loc < _numPoints; loc++) { _xAxisPoints[loc] = freqValue; freqValue += fft_bin_size; } @@ -506,23 +451,23 @@ FrequencyDisplayPlot::_resetXAxisPoints() void FrequencyDisplayPlot::SetLowerIntensityLevel(const double lowerIntensityLevel) { - _lower_intensity_marker->setYValue( lowerIntensityLevel ); + _lower_intensity_marker->setYValue(lowerIntensityLevel); } void FrequencyDisplayPlot::SetUpperIntensityLevel(const double upperIntensityLevel) { - _upper_intensity_marker->setYValue( upperIntensityLevel ); + _upper_intensity_marker->setYValue(upperIntensityLevel); } void -FrequencyDisplayPlot::SetTraceColour (QColor c) +FrequencyDisplayPlot::SetTraceColour(QColor c) { - _fft_plot_curve->setPen(QPen(c)); + _plot_curve[0]->setPen(QPen(c)); } void -FrequencyDisplayPlot::SetBGColour (QColor c) +FrequencyDisplayPlot::SetBGColour(QColor c) { QPalette palette; palette.setColor(canvas()->backgroundRole(), c); @@ -530,7 +475,7 @@ FrequencyDisplayPlot::SetBGColour (QColor c) } void -FrequencyDisplayPlot::ShowCFMarker (const bool show) +FrequencyDisplayPlot::ShowCFMarker(const bool show) { if (show) _markerCF->show(); diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.h b/gr-qtgui/lib/FrequencyDisplayPlot.h index 5c3ea708c3..17bb9d2391 100644 --- a/gr-qtgui/lib/FrequencyDisplayPlot.h +++ b/gr-qtgui/lib/FrequencyDisplayPlot.h @@ -25,29 +25,15 @@ #include <stdint.h> #include <cstdio> -#include <qwt_plot.h> -#include <qwt_painter.h> -#include <qwt_plot_canvas.h> -#include <qwt_plot_curve.h> -#include <qwt_scale_engine.h> -#include <qwt_scale_widget.h> -#include <qwt_plot_zoomer.h> -#include <qwt_plot_panner.h> -#include <qwt_plot_marker.h> -#include <qwt_plot_magnifier.h> -#include <gruel/high_res_timer.h> -#include <qwt_symbol.h> -#include <qtgui_util.h> - -#if QWT_VERSION >= 0x060000 -#include <qwt_compat.h> -#endif - -class FrequencyDisplayPlot:public QwtPlot{ +#include <vector> +#include "DisplayPlot.h" + +class FrequencyDisplayPlot: public DisplayPlot +{ Q_OBJECT public: - FrequencyDisplayPlot(QWidget*); + FrequencyDisplayPlot(int nplots, QWidget*); virtual ~FrequencyDisplayPlot(); void SetFrequencyRange(const double, const double, @@ -57,7 +43,14 @@ public: double GetStartFrequency()const; double GetStopFrequency()const; - void PlotNewData(const double* dataPoints, const int64_t numDataPoints, + void PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, + const double noiseFloorAmplitude, const double peakFrequency, + const double peakAmplitude, const double timeInterval); + + // Old method to be removed + void PlotNewData(const double* dataPoints, + const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude, const double timeInterval); @@ -67,7 +60,7 @@ public: void SetMaxFFTVisible(const bool); void SetMinFFTVisible(const bool); - virtual void replot(); + void replot(); void set_yaxis(double min, double max); @@ -76,63 +69,43 @@ public: void ShowCFMarker (const bool); public slots: - void resizeSlot( QSize *e ); void SetLowerIntensityLevel(const double); void SetUpperIntensityLevel(const double); - // Because of the preprocessing of slots in QT, these are no - // easily separated by the version check. Make one for each - // version until it's worked out. void OnPickerPointSelected(const QwtDoublePoint & p); void OnPickerPointSelected6(const QPointF & p); -signals: - void plotPointSelected(const QPointF p); - -protected: - private: - void _resetXAxisPoints(); + std::vector<double*> _dataPoints; + + QwtPlotCurve* _min_fft_plot_curve; + QwtPlotCurve* _max_fft_plot_curve; + double _startFrequency; double _stopFrequency; double _maxYAxis; double _minYAxis; - QwtPlotCurve* _fft_plot_curve; - QwtPlotCurve* _min_fft_plot_curve; - QwtPlotCurve* _max_fft_plot_curve; - QwtPlotMarker* _lower_intensity_marker; QwtPlotMarker* _upper_intensity_marker; - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - QwtPlotMarker *_markerPeakAmplitude; QwtPlotMarker *_markerNoiseFloorAmplitude; QwtPlotMarker *_markerCF; - QwtDblClickPlotPicker *_picker; - - QwtPlotMagnifier *_magnifier; - - double* _dataPoints; double* _xAxisPoints; int _xAxisMultiplier; double* _minFFTPoints; double* _maxFFTPoints; - int64_t _numPoints; double _peakFrequency; double _peakAmplitude; double _noiseFloorAmplitude; - gruel::high_res_timer_type _lastReplot; - bool _useCenterFrequencyFlag; }; diff --git a/gr-qtgui/lib/SpectrumGUIClass.cc b/gr-qtgui/lib/SpectrumGUIClass.cc index d2dbc77723..e73ee2d50c 100644 --- a/gr-qtgui/lib/SpectrumGUIClass.cc +++ b/gr-qtgui/lib/SpectrumGUIClass.cc @@ -27,6 +27,7 @@ //Added by qt3to4: #include <QEvent> #include <QCustomEvent> +#include <volk/volk.h> const long SpectrumGUIClass::MAX_FFT_SIZE = 32768; const long SpectrumGUIClass::MIN_FFT_SIZE = 256; @@ -88,13 +89,12 @@ SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent, if(!_windowOpennedFlag){ if(!_fftBuffersCreatedFlag){ - _fftPoints = new std::complex<float>[_dataPoints]; + _fftPoints = new float[_dataPoints]; _realTimeDomainPoints = new double[_dataPoints]; _imagTimeDomainPoints = new double[_dataPoints]; _fftBuffersCreatedFlag = true; - - - memset(_fftPoints, 0x0, _dataPoints*sizeof(std::complex<float>)); + + memset(_fftPoints, 0x0, _dataPoints*sizeof(float)); memset(_realTimeDomainPoints, 0x0, _dataPoints*sizeof(double)); memset(_imagTimeDomainPoints, 0x0, _dataPoints*sizeof(double)); } @@ -223,7 +223,7 @@ SpectrumGUIClass::GetCenterFrequency() void SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, - const std::complex<float>* fftBuffer, + const float* fftBuffer, const uint64_t inputBufferSize, const float* realTimeDomainData, const uint64_t realTimeDomainDataSize, @@ -242,9 +242,11 @@ SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, if(updateDisplayFlag){ if((fftBuffer != NULL) && (bufferSize > 0)){ - memcpy(_fftPoints, fftBuffer, bufferSize * sizeof(std::complex<float>)); + memcpy(_fftPoints, fftBuffer, bufferSize * sizeof(float)); } + //ALL OF THIS SHIT SHOULD BE COMBINED WITH THE FFTSHIFT + //USE VOLK_32FC_DEINTERLEAVE_64F_X2_A TO GET REAL/IMAG FROM COMPLEX32 // Can't do a memcpy since ths is going from float to double data type if((realTimeDomainData != NULL) && (realTimeDomainDataSize > 0)){ const float* realTimeDomainDataPtr = realTimeDomainData; @@ -252,24 +254,18 @@ SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, double* realTimeDomainPointsPtr = _realTimeDomainPoints; timeDomainBufferSize = realTimeDomainDataSize; - memset( _imagTimeDomainPoints, 0x0, realTimeDomainDataSize*sizeof(double)); - for( uint64_t number = 0; number < realTimeDomainDataSize; number++){ + memset(_imagTimeDomainPoints, 0x0, realTimeDomainDataSize*sizeof(double)); + for(uint64_t number = 0; number < realTimeDomainDataSize; number++){ *realTimeDomainPointsPtr++ = *realTimeDomainDataPtr++; } } - // Can't do a memcpy since ths is going from float to double data type if((complexTimeDomainData != NULL) && (complexTimeDomainDataSize > 0)){ - const float* complexTimeDomainDataPtr = complexTimeDomainData; - - double* realTimeDomainPointsPtr = _realTimeDomainPoints; - double* imagTimeDomainPointsPtr = _imagTimeDomainPoints; - + volk_32fc_deinterleave_64f_x2_a(_realTimeDomainPoints, + _imagTimeDomainPoints, + (const lv_32fc_t *)complexTimeDomainData, + complexTimeDomainDataSize); timeDomainBufferSize = complexTimeDomainDataSize; - for( uint64_t number = 0; number < complexTimeDomainDataSize; number++){ - *realTimeDomainPointsPtr++ = *complexTimeDomainDataPtr++; - *imagTimeDomainPointsPtr++ = *complexTimeDomainDataPtr++; - } } } diff --git a/gr-qtgui/lib/SpectrumGUIClass.h b/gr-qtgui/lib/SpectrumGUIClass.h index e0612413b0..a1150199b2 100644 --- a/gr-qtgui/lib/SpectrumGUIClass.h +++ b/gr-qtgui/lib/SpectrumGUIClass.h @@ -62,7 +62,7 @@ public: double GetStopFrequency(); double GetCenterFrequency(); - void UpdateWindow(const bool, const std::complex<float>*, + void UpdateWindow(const bool, const float*, const uint64_t, const float*, const uint64_t, const float*, const uint64_t, @@ -122,7 +122,7 @@ private: SpectrumDisplayForm* _spectrumDisplayForm; - std::complex<float>* _fftPoints; + float* _fftPoints; double* _realTimeDomainPoints; double* _imagTimeDomainPoints; }; diff --git a/gr-qtgui/lib/TimeDomainDisplayPlot.cc b/gr-qtgui/lib/TimeDomainDisplayPlot.cc index 84b09af90f..a10418bddf 100644 --- a/gr-qtgui/lib/TimeDomainDisplayPlot.cc +++ b/gr-qtgui/lib/TimeDomainDisplayPlot.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -28,6 +28,7 @@ #include <qwt_scale_draw.h> #include <qwt_legend.h> #include <QColor> +#include <cmath> #include <iostream> class TimePrecisionClass @@ -80,11 +81,12 @@ public: protected: using QwtPlotZoomer::trackerText; - virtual QwtText trackerText( const QwtDoublePoint& p ) const + virtual QwtText trackerText( const QPoint& p ) const { - QwtText t(QString("%1 %2, %3 V").arg(p.x(), 0, 'f', GetTimePrecision()). + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + QwtText t(QString("%1 %2, %3 V").arg(dp.x(), 0, 'f', GetTimePrecision()). arg(_unitType.c_str()). - arg(p.y(), 0, 'f', 4)); + arg(dp.y(), 0, 'f', 4)); return t; } @@ -93,11 +95,13 @@ private: std::string _unitType; }; + +/*********************************************************************** + * Main Time domain plotter widget + **********************************************************************/ TimeDomainDisplayPlot::TimeDomainDisplayPlot(int nplots, QWidget* parent) - : QwtPlot(parent), _nplots(nplots) + : DisplayPlot(nplots, parent) { - resize(parent->width(), parent->height()); - _numPoints = 1024; _xAxisPoints = new double[_numPoints]; memset(_xAxisPoints, 0x0, _numPoints*sizeof(double)); @@ -108,22 +112,14 @@ TimeDomainDisplayPlot::TimeDomainDisplayPlot(int nplots, QWidget* parent) _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); #endif - // Disable polygon clipping -#if QWT_VERSION < 0x060000 - QwtPainter::setDeviceClipping(false); -#else - QwtPainter::setPolylineSplitting(false); -#endif - -#if QWT_VERSION < 0x060000 - // We don't need the cache here - canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); - canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); -#endif + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); - QPalette palette; - palette.setColor(canvas()->backgroundRole(), QColor("white")); - canvas()->setPalette(palette); + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); setXaxis(0, _numPoints); @@ -149,57 +145,19 @@ TimeDomainDisplayPlot::TimeDomainDisplayPlot(int nplots, QWidget* parent) _plot_curve[i]->attach(this); _plot_curve[i]->setPen(QPen(colors[i])); + QwtSymbol *symbol = new QwtSymbol(QwtSymbol::NoSymbol, QBrush(colors[i]), QPen(colors[i]), QSize(7,7)); + #if QWT_VERSION < 0x060000 _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(*symbol); #else _plot_curve[i]->setRawSamples(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setSymbol(symbol); #endif -} + } _sampleRate = 1; _resetXAxisPoints(); - - _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, - Qt::RightButton, Qt::ControlModifier); - _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, - Qt::RightButton); - - _panner = new QwtPlotPanner(canvas()); - _panner->setAxisEnabled(QwtPlot::yRight, false); - _panner->setMouseButton(Qt::MidButton); - - // emit the position of clicks on widget - _picker = new QwtDblClickPlotPicker(canvas()); - -#if QWT_VERSION < 0x060000 - connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), - this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); -#else - connect(_picker, SIGNAL(selected(const QPointF &)), - this, SLOT(OnPickerPointSelected6(const QPointF &))); -#endif - - // Configure magnify on mouse wheel - _magnifier = new QwtPlotMagnifier(canvas()); - _magnifier->setAxisEnabled(QwtPlot::xBottom, false); - - // Avoid jumping when labels with more/less digits - // appear/disappear when scrolling vertically - - const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); - QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); - sd->setMinimumExtent( fm.width("100.00") ); - - const QColor c(Qt::darkRed); - _zoomer->setRubberBandPen(c); - _zoomer->setTrackerPen(c); - - QwtLegend* legendDisplay = new QwtLegend(this); - legendDisplay->setItemMode(QwtLegend::CheckableItem); - insertLegend(legendDisplay); - - connect(this, SIGNAL( legendChecked(QwtPlotItem *, bool ) ), - this, SLOT( LegendEntryChecked(QwtPlotItem *, bool ) )); } TimeDomainDisplayPlot::~TimeDomainDisplayPlot() @@ -212,74 +170,45 @@ TimeDomainDisplayPlot::~TimeDomainDisplayPlot() } void -TimeDomainDisplayPlot::setYaxis(double min, double max) -{ - setAxisScale(QwtPlot::yLeft, min, max); - _zoomer->setZoomBase(); -} - -void -TimeDomainDisplayPlot::setXaxis(double min, double max) -{ - setAxisScale(QwtPlot::xBottom, min, max); - _zoomer->setZoomBase(); -} - -void -TimeDomainDisplayPlot::setTitle(int which, QString title) -{ - _plot_curve[which]->setTitle(title); -} - -void -TimeDomainDisplayPlot::setColor(int which, QString color) -{ - _plot_curve[which]->setPen(QPen(color)); -} - -void TimeDomainDisplayPlot::replot() +TimeDomainDisplayPlot::replot() { QwtPlot::replot(); } void -TimeDomainDisplayPlot::resizeSlot( QSize *s ) -{ - // -10 is to spare some room for the legend and x-axis label - resize(s->width()-10, s->height()-10); -} - -void TimeDomainDisplayPlot::PlotNewData(const std::vector<double*> dataPoints, - const int64_t numDataPoints, - const double timeInterval) +TimeDomainDisplayPlot::PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, + const double timeInterval) { - if((numDataPoints > 0)) { - if(numDataPoints != _numPoints){ - _numPoints = numDataPoints; + if(!_stop) { + if((numDataPoints > 0)) { + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; - delete[] _xAxisPoints; - _xAxisPoints = new double[_numPoints]; + delete[] _xAxisPoints; + _xAxisPoints = new double[_numPoints]; - for(int i = 0; i < _nplots; i++) { - delete[] _dataPoints[i]; - _dataPoints[i] = new double[_numPoints]; + for(int i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + _dataPoints[i] = new double[_numPoints]; #if QWT_VERSION < 0x060000 - _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); #else - _plot_curve[i]->setRawSamples(_xAxisPoints, _dataPoints[i], _numPoints); + _plot_curve[i]->setRawSamples(_xAxisPoints, _dataPoints[i], _numPoints); #endif + } + + setXaxis(0, numDataPoints); + _resetXAxisPoints(); } - setXaxis(0, numDataPoints); - _resetXAxisPoints(); - } + for(int i = 0; i < _nplots; i++) { + memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); + } - for(int i = 0; i < _nplots; i++) { - memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); + replot(); } - - replot(); } } @@ -301,11 +230,6 @@ void TimeDomainDisplayPlot::_resetXAxisPoints() _zoomer->zoom(0); } -void TimeDomainDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on) -{ - plotItem->setVisible(!on); -} - void TimeDomainDisplayPlot::SetSampleRate(double sr, double units, const std::string &strunits) @@ -325,21 +249,4 @@ TimeDomainDisplayPlot::SetSampleRate(double sr, double units, } } - -void -TimeDomainDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) -{ - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); -} - -void -TimeDomainDisplayPlot::OnPickerPointSelected6(const QPointF & p) -{ - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); -} - #endif /* TIME_DOMAIN_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/TimeDomainDisplayPlot.h b/gr-qtgui/lib/TimeDomainDisplayPlot.h index 356da25ad4..9c07e96ba0 100644 --- a/gr-qtgui/lib/TimeDomainDisplayPlot.h +++ b/gr-qtgui/lib/TimeDomainDisplayPlot.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -20,30 +20,16 @@ * Boston, MA 02110-1301, USA. */ -#ifndef TIME_DOMAIN_DISPLAY_PLOT_HPP -#define TIME_DOMAIN_DISPLAY_PLOT_HPP +#ifndef TIME_DOMAIN_DISPLAY_PLOT_H +#define TIME_DOMAIN_DISPLAY_PLOT_H #include <stdint.h> #include <cstdio> #include <vector> -#include <qwt_plot.h> -#include <qwt_painter.h> -#include <qwt_plot_canvas.h> -#include <qwt_plot_curve.h> -#include <qwt_scale_engine.h> -#include <qwt_scale_widget.h> -#include <qwt_plot_zoomer.h> -#include <qwt_plot_panner.h> -#include <qwt_plot_magnifier.h> -#include <qwt_plot_marker.h> -#include <qwt_symbol.h> -#include <qtgui_util.h> +#include "DisplayPlot.h" -#if QWT_VERSION >= 0x060000 -#include <qwt_compat.h> -#endif - -class TimeDomainDisplayPlot:public QwtPlot{ +class TimeDomainDisplayPlot: public DisplayPlot +{ Q_OBJECT public: @@ -53,50 +39,19 @@ public: void PlotNewData(const std::vector<double*> dataPoints, const int64_t numDataPoints, const double timeInterval); - virtual void replot(); + void replot(); public slots: - void setYaxis(double min, double max); - void setXaxis(double min, double max); - void setTitle(int which, QString title); - void setColor(int which, QString color); - - void resizeSlot( QSize *s ); void SetSampleRate(double sr, double units, const std::string &strunits); - // Because of the preprocessing of slots in QT, these are no - // easily separated by the version check. Make one for each - // version until it's worked out. - void OnPickerPointSelected(const QwtDoublePoint & p); - void OnPickerPointSelected6(const QPointF & p); - -signals: - void plotPointSelected(const QPointF p); - -protected slots: - void LegendEntryChecked(QwtPlotItem *plotItem, bool on); - -protected: - private: void _resetXAxisPoints(); - int _nplots; - std::vector<QwtPlotCurve*> _plot_curve; - - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - - QwtDblClickPlotPicker *_picker; - QwtPlotMagnifier *_magnifier; - std::vector<double*> _dataPoints; double* _xAxisPoints; double _sampleRate; - - int64_t _numPoints; }; -#endif /* TIME_DOMAIN_DISPLAY_PLOT_HPP */ +#endif /* TIME_DOMAIN_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.cc b/gr-qtgui/lib/WaterfallDisplayPlot.cc index 63eb57ffe8..40d16d0072 100644 --- a/gr-qtgui/lib/WaterfallDisplayPlot.cc +++ b/gr-qtgui/lib/WaterfallDisplayPlot.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,128 +25,23 @@ #include <WaterfallDisplayPlot.h> +#include "qtgui_types.h" #include <qwt_color_map.h> -#include <qwt_scale_widget.h> #include <qwt_scale_draw.h> -#include <qwt_plot_zoomer.h> -#include <qwt_plot_panner.h> +#include <qwt_legend.h> +#include <qwt_legend_item.h> #include <qwt_plot_layout.h> - -#include <qapplication.h> +#include <QColor> +#include <iostream> #include <boost/date_time/posix_time/posix_time.hpp> namespace pt = boost::posix_time; -class FreqOffsetAndPrecisionClass -{ -public: - FreqOffsetAndPrecisionClass(const int freqPrecision) - { - _frequencyPrecision = freqPrecision; - _centerFrequency = 0; - } - - virtual ~FreqOffsetAndPrecisionClass() - { - } - - virtual unsigned int GetFrequencyPrecision() const - { - return _frequencyPrecision; - } - - virtual void SetFrequencyPrecision(const unsigned int newPrecision) - { - _frequencyPrecision = newPrecision; - } - - virtual double GetCenterFrequency() const - { - return _centerFrequency; - } - - virtual void SetCenterFrequency(const double newFreq) - { - _centerFrequency = newFreq; - } - -protected: - unsigned int _frequencyPrecision; - double _centerFrequency; - -private: - -}; - -class WaterfallFreqDisplayScaleDraw: public QwtScaleDraw, public FreqOffsetAndPrecisionClass{ -public: - WaterfallFreqDisplayScaleDraw(const unsigned int precision) - : QwtScaleDraw(), FreqOffsetAndPrecisionClass(precision) - { - } - - virtual ~WaterfallFreqDisplayScaleDraw() - { - } - - QwtText label(double value) const - { - return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision()); - } - - virtual void initiateUpdate() - { - invalidateCache(); - } - -protected: - -private: - -}; - -class TimeScaleData -{ -public: - TimeScaleData() - { - _zeroTime = 0; - _secondsPerLine = 1.0; - } - - virtual ~TimeScaleData() - { - } - - virtual gruel::high_res_timer_type GetZeroTime() const - { - return _zeroTime; - } - - virtual void SetZeroTime(const gruel::high_res_timer_type newTime) - { - _zeroTime = newTime - gruel::high_res_timer_epoch(); - } - - virtual void SetSecondsPerLine(const double newTime) - { - _secondsPerLine = newTime; - } - - virtual double GetSecondsPerLine() const - { - return _secondsPerLine; - } - - -protected: - gruel::high_res_timer_type _zeroTime; - double _secondsPerLine; - -private: - -}; +#include <QDebug> +/*********************************************************************** + * Create a time label HH:MM:SS.SSS from an input time + **********************************************************************/ static QString make_time_label(double secs) { @@ -160,6 +55,9 @@ make_time_label(double secs) return QString("").sprintf("%s.%03ld", time_str.c_str(), long(std::fmod(secs*1000, 1000))); } +/*********************************************************************** + * Text scale widget to provide Y (time) axis text + **********************************************************************/ class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData { public: @@ -173,8 +71,8 @@ public: virtual QwtText label(double value) const { - double secs = GetZeroTime()/double(gruel::high_res_timer_tps()) - (value * GetSecondsPerLine()); - return QwtText(make_time_label(secs)); + double secs = double(value * GetSecondsPerLine()); + return QwtText(QString("").sprintf("%.1f", secs)); } virtual void initiateUpdate() @@ -190,7 +88,10 @@ private: }; -class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, +/*********************************************************************** + * Widget to provide mouse pointer coordinate text + **********************************************************************/ +class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass { public: @@ -217,12 +118,14 @@ public: protected: using QwtPlotZoomer::trackerText; - virtual QwtText trackerText( const QwtDoublePoint& p ) const + virtual QwtText trackerText( QPoint const &p ) const { - double secs = GetZeroTime()/double(gruel::high_res_timer_tps()) - (p.y() * GetSecondsPerLine()); - QwtText t(QString("%1 %2, %3"). - arg(p.x(), 0, 'f', GetFrequencyPrecision()). - arg(_unitType.c_str()).arg(make_time_label(secs))); + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + double secs = double(dp.y() * GetSecondsPerLine()); + QwtText t(QString("%1 %2, %3 s") + .arg(dp.x(), 0, 'f', GetFrequencyPrecision()) + .arg(_unitType.c_str()) + .arg(secs, 0, 'f', 2)); return t; } @@ -230,100 +133,59 @@ private: std::string _unitType; }; -class ColorMap_MultiColor: public QwtLinearColorMap -{ -public: - ColorMap_MultiColor(): - QwtLinearColorMap(Qt::darkCyan, Qt::white) - { - addColorStop(0.25, Qt::cyan); - addColorStop(0.5, Qt::yellow); - addColorStop(0.75, Qt::red); - } -}; - -class ColorMap_WhiteHot: public QwtLinearColorMap -{ -public: - ColorMap_WhiteHot(): - QwtLinearColorMap(Qt::black, Qt::white) - { - } -}; - -class ColorMap_BlackHot: public QwtLinearColorMap -{ -public: - ColorMap_BlackHot(): - QwtLinearColorMap(Qt::white, Qt::black) - { - } -}; - -class ColorMap_Incandescent: public QwtLinearColorMap -{ -public: - ColorMap_Incandescent(): - QwtLinearColorMap(Qt::black, Qt::white) - { - addColorStop(0.5, Qt::darkRed); - } -}; - -class ColorMap_UserDefined: public QwtLinearColorMap -{ -public: - ColorMap_UserDefined(QColor low, QColor high): - QwtLinearColorMap(low, high) - { - } -}; - /********************************************************************* -MAIN WATERFALL PLOT WIDGET +* Main waterfall plot widget *********************************************************************/ - -WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent) - : QwtPlot(parent) +WaterfallDisplayPlot::WaterfallDisplayPlot(int nplots, QWidget* parent) + : DisplayPlot(nplots, parent) { - _zoomer = NULL; + _zoomer = NULL; // need this for proper init _startFrequency = 0; _stopFrequency = 4000; + _useCenterFrequencyFlag = false; resize(parent->width(), parent->height()); _numPoints = 1024; - QPalette palette; - palette.setColor(canvas()->backgroundRole(), QColor("white")); - canvas()->setPalette(palette); - setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); - setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(0)); + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); - setAxisTitle(QwtPlot::yLeft, "Time"); + setAxisTitle(QwtPlot::yLeft, "Time (s)"); setAxisScaleDraw(QwtPlot::yLeft, new QwtTimeScaleDraw()); _lastReplot = 0; - _intensityColorMapType = INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR; + QList<int> colormaps; + colormaps << INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR + << INTENSITY_COLOR_MAP_TYPE_WHITE_HOT + << INTENSITY_COLOR_MAP_TYPE_BLACK_HOT + << INTENSITY_COLOR_MAP_TYPE_INCANDESCENT + << INTENSITY_COLOR_MAP_TYPE_USER_DEFINED; - d_data = new WaterfallData(_startFrequency, _stopFrequency, - _numPoints, 200); + for(int i = 0; i < _nplots; i++) { + d_data.push_back(new WaterfallData(_startFrequency, _stopFrequency, + _numPoints, 200)); #if QWT_VERSION < 0x060000 - d_spectrogram = new PlotWaterfall(d_data, "Waterfall Display"); - - ColorMap_MultiColor colorMap; - d_spectrogram->setColorMap(colorMap); + d_spectrogram.push_back(new PlotWaterfall(d_data[i], "Spectrogram")); #else - d_spectrogram = new QwtPlotSpectrogram("Spectrogram"); - d_spectrogram->setData(d_data); - d_spectrogram->setDisplayMode(QwtPlotSpectrogram::ImageMode, true); - d_spectrogram->setColorMap(new ColorMap_MultiColor()); + d_spectrogram.push_back(new QwtPlotSpectrogram("Spectrogram")); + d_spectrogram[i]->setData(d_data[i]); + d_spectrogram[i]->setDisplayMode(QwtPlotSpectrogram::ImageMode, true); + d_spectrogram[i]->setColorMap(new ColorMap_MultiColor()); #endif - d_spectrogram->attach(this); + // a hack around the fact that we aren't using plot curves for the + // spectrogram plots. + _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); + + d_spectrogram[i]->attach(this); + + _intensityColorMapType.push_back(colormaps[i%colormaps.size()]); + SetIntensityColorMapType(i, _intensityColorMapType[i], + QColor("white"), QColor("white")); + } // LeftButton for the zooming // MidButton for the panning @@ -338,27 +200,6 @@ WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent) _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, Qt::RightButton); - _panner = new QwtPlotPanner(canvas()); - _panner->setAxisEnabled(QwtPlot::yRight, false); - _panner->setMouseButton(Qt::MidButton); - - // emit the position of clicks on widget - _picker = new QwtDblClickPlotPicker(canvas()); -#if QWT_VERSION < 0x060000 - connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), - this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); -#else - connect(_picker, SIGNAL(selected(const QPointF &)), - this, SLOT(OnPickerPointSelected6(const QPointF &))); -#endif - - // Avoid jumping when labels with more/less digits - // appear/disappear when scrolling vertically - - const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); - QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); - sd->setMinimumExtent( fm.width("100.00") ); - const QColor c(Qt::black); _zoomer->setRubberBandPen(c); _zoomer->setTrackerPen(c); @@ -370,15 +211,15 @@ WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent) WaterfallDisplayPlot::~WaterfallDisplayPlot() { - delete d_data; - delete d_spectrogram; } void WaterfallDisplayPlot::Reset() { - d_data->ResizeData(_startFrequency, _stopFrequency, _numPoints); - d_data->Reset(); + for(int i = 0; i < _nplots; i++) { + d_data[i]->ResizeData(_startFrequency, _stopFrequency, _numPoints); + d_data[i]->Reset(); + } setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency); @@ -406,7 +247,7 @@ WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq, _useCenterFrequencyFlag = useCenterFrequencyFlag; - if(_useCenterFrequencyFlag){ + if(_useCenterFrequencyFlag) { startFreq = (startFreq + centerFreq); stopFreq = (stopFreq + centerFreq); } @@ -419,9 +260,9 @@ WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq, _startFrequency = startFreq; _stopFrequency = stopFreq; - if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){ + if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)) { double display_units = ceil(log10(units)/2.0); - setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(display_units)); + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(display_units)); setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str())); if(reset) { @@ -448,31 +289,35 @@ WaterfallDisplayPlot::GetStopFrequency() const } void -WaterfallDisplayPlot::PlotNewData(const double* dataPoints, +WaterfallDisplayPlot::PlotNewData(const std::vector<double*> dataPoints, const int64_t numDataPoints, const double timePerFFT, const gruel::high_res_timer_type timestamp, const int droppedFrames) { - if(numDataPoints > 0){ - if(numDataPoints != _numPoints){ - _numPoints = numDataPoints; + if(!_stop) { + if(numDataPoints > 0){ + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + Reset(); - Reset(); + for(int i = 0; i < _nplots; i++) { + d_spectrogram[i]->invalidateCache(); + d_spectrogram[i]->itemChanged(); + } - d_spectrogram->invalidateCache(); - d_spectrogram->itemChanged(); + if(isVisible()) { + replot(); + } - if(isVisible()){ - replot(); + _lastReplot = gruel::high_res_timer_now(); } - _lastReplot = gruel::high_res_timer_now(); - } - - if(gruel::high_res_timer_now() - _lastReplot > timePerFFT*gruel::high_res_timer_tps()) { - d_data->addFFTData(dataPoints, numDataPoints, droppedFrames); - d_data->IncrementNumLinesToUpdate(); + for(int i = 0; i < _nplots; i++) { + d_data[i]->addFFTData(dataPoints[i], numDataPoints, droppedFrames); + d_data[i]->IncrementNumLinesToUpdate(); + } QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft); timeScale->SetSecondsPerLine(timePerFFT); @@ -481,30 +326,45 @@ WaterfallDisplayPlot::PlotNewData(const double* dataPoints, ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT); ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp); - d_spectrogram->invalidateCache(); - d_spectrogram->itemChanged(); + for(int i = 0; i < _nplots; i++) { + d_spectrogram[i]->invalidateCache(); + d_spectrogram[i]->itemChanged(); + } replot(); - - _lastReplot = gruel::high_res_timer_now(); } } } void +WaterfallDisplayPlot::PlotNewData(const double* dataPoints, + const int64_t numDataPoints, + const double timePerFFT, + const gruel::high_res_timer_type timestamp, + const int droppedFrames) +{ + std::vector<double*> vecDataPoints; + vecDataPoints.push_back((double*)dataPoints); + PlotNewData(vecDataPoints, numDataPoints, timePerFFT, + timestamp, droppedFrames); +} + +void WaterfallDisplayPlot::SetIntensityRange(const double minIntensity, const double maxIntensity) { + for(int i = 0; i < _nplots; i++) { #if QWT_VERSION < 0x060000 - d_data->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); + d_data[i]->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); #else - d_data->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); + d_data[i]->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); #endif - emit UpdatedLowerIntensityLevel(minIntensity); - emit UpdatedUpperIntensityLevel(maxIntensity); + emit UpdatedLowerIntensityLevel(minIntensity); + emit UpdatedUpperIntensityLevel(maxIntensity); - _UpdateIntensityRangeDisplay(); + _UpdateIntensityRangeDisplay(); + } } void @@ -513,8 +373,8 @@ WaterfallDisplayPlot::replot() QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft); timeScale->initiateUpdate(); - WaterfallFreqDisplayScaleDraw* freqScale = \ - (WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom); + FreqDisplayScaleDraw* freqScale = \ + (FreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom); freqScale->initiateUpdate(); // Update the time axis display @@ -534,76 +394,71 @@ WaterfallDisplayPlot::replot() QwtPlot::replot(); } -void -WaterfallDisplayPlot::resizeSlot( QSize *s ) -{ - resize(s->width(), s->height()); -} - int -WaterfallDisplayPlot::GetIntensityColorMapType() const +WaterfallDisplayPlot::GetIntensityColorMapType(int which) const { - return _intensityColorMapType; + return _intensityColorMapType[which]; } void -WaterfallDisplayPlot::SetIntensityColorMapType(const int newType, +WaterfallDisplayPlot::SetIntensityColorMapType(const int which, + const int newType, const QColor lowColor, const QColor highColor) { - if((_intensityColorMapType != newType) || + if((_intensityColorMapType[which] != newType) || ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && (lowColor.isValid() && highColor.isValid()))){ switch(newType){ case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{ - _intensityColorMapType = newType; + _intensityColorMapType[which] = newType; #if QWT_VERSION < 0x060000 ColorMap_MultiColor colorMap; - d_spectrogram->setColorMap(colorMap); + d_spectrogram[which]->setColorMap(colorMap); #else - d_spectrogram->setColorMap(new ColorMap_MultiColor()); + d_spectrogram[which]->setColorMap(new ColorMap_MultiColor()); #endif break; } case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{ - _intensityColorMapType = newType; + _intensityColorMapType[which] = newType; #if QWT_VERSION < 0x060000 ColorMap_WhiteHot colorMap; - d_spectrogram->setColorMap(colorMap); + d_spectrogram[which]->setColorMap(colorMap); #else - d_spectrogram->setColorMap(new ColorMap_WhiteHot()); + d_spectrogram[which]->setColorMap(new ColorMap_WhiteHot()); #endif break; } case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{ - _intensityColorMapType = newType; + _intensityColorMapType[which] = newType; #if QWT_VERSION < 0x060000 ColorMap_BlackHot colorMap; - d_spectrogram->setColorMap(colorMap); + d_spectrogram[which]->setColorMap(colorMap); #else - d_spectrogram->setColorMap(new ColorMap_BlackHot()); + d_spectrogram[which]->setColorMap(new ColorMap_BlackHot()); #endif break; } case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{ - _intensityColorMapType = newType; + _intensityColorMapType[which] = newType; #if QWT_VERSION < 0x060000 ColorMap_Incandescent colorMap; - d_spectrogram->setColorMap(colorMap); + d_spectrogram[which]->setColorMap(colorMap); #else - d_spectrogram->setColorMap(new ColorMap_Incandescent()); + d_spectrogram[which]->setColorMap(new ColorMap_Incandescent()); #endif break; } case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{ _userDefinedLowIntensityColor = lowColor; _userDefinedHighIntensityColor = highColor; - _intensityColorMapType = newType; + _intensityColorMapType[which] = newType; #if QWT_VERSION < 0x060000 ColorMap_UserDefined colorMap(lowColor, highColor); - d_spectrogram->setColorMap(colorMap); + d_spectrogram[which]->setColorMap(colorMap); #else - d_spectrogram->setColorMap(new ColorMap_UserDefined(lowColor, highColor)); + d_spectrogram[which]->setColorMap(new ColorMap_UserDefined(lowColor, highColor)); #endif break; } @@ -633,39 +488,41 @@ WaterfallDisplayPlot::_UpdateIntensityRangeDisplay() rightAxis->setTitle("Intensity (dB)"); rightAxis->setColorBarEnabled(true); + for(int i = 0; i < _nplots; i++) { #if QWT_VERSION < 0x060000 - rightAxis->setColorMap(d_spectrogram->data()->range(), - d_spectrogram->colorMap()); - setAxisScale(QwtPlot::yRight, - d_spectrogram->data()->range().minValue(), - d_spectrogram->data()->range().maxValue()); + rightAxis->setColorMap(d_spectrogram[i]->data()->range(), + d_spectrogram[i]->colorMap()); + setAxisScale(QwtPlot::yRight, + d_spectrogram[i]->data()->range().minValue(), + d_spectrogram[i]->data()->range().maxValue()); #else - QwtInterval intv = d_spectrogram->interval(Qt::ZAxis); - switch(_intensityColorMapType) { - case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR: - rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; - case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT: - rightAxis->setColorMap(intv, new ColorMap_WhiteHot()); break; - case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT: - rightAxis->setColorMap(intv, new ColorMap_BlackHot()); break; - case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT: - rightAxis->setColorMap(intv, new ColorMap_Incandescent()); break; - case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED: - rightAxis->setColorMap(intv, new ColorMap_UserDefined(_userDefinedLowIntensityColor, - _userDefinedHighIntensityColor)); break; - default: - rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; - } - setAxisScale(QwtPlot::yRight, intv.minValue(), intv.maxValue()); + QwtInterval intv = d_spectrogram[i]->interval(Qt::ZAxis); + switch(_intensityColorMapType[i]) { + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR: + rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT: + rightAxis->setColorMap(intv, new ColorMap_WhiteHot()); break; + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT: + rightAxis->setColorMap(intv, new ColorMap_BlackHot()); break; + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT: + rightAxis->setColorMap(intv, new ColorMap_Incandescent()); break; + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED: + rightAxis->setColorMap(intv, new ColorMap_UserDefined(_userDefinedLowIntensityColor, + _userDefinedHighIntensityColor)); break; + default: + rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; + } + setAxisScale(QwtPlot::yRight, intv.minValue(), intv.maxValue()); #endif - enableAxis(QwtPlot::yRight); + enableAxis(QwtPlot::yRight); - plotLayout()->setAlignCanvasToScales(true); + plotLayout()->setAlignCanvasToScales(true); - // Tell the display to redraw everything - d_spectrogram->invalidateCache(); - d_spectrogram->itemChanged(); + // Tell the display to redraw everything + d_spectrogram[i]->invalidateCache(); + d_spectrogram[i]->itemChanged(); + } // Draw again replot(); @@ -674,22 +531,4 @@ WaterfallDisplayPlot::_UpdateIntensityRangeDisplay() _lastReplot = gruel::high_res_timer_now(); } -void -WaterfallDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) -{ - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - point.setX(point.x() * _xAxisMultiplier); - emit plotPointSelected(point); -} - -void -WaterfallDisplayPlot::OnPickerPointSelected6(const QPointF & p) -{ - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - point.setX(point.x() * _xAxisMultiplier); - emit plotPointSelected(point); -} - #endif /* WATERFALL_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.h b/gr-qtgui/lib/WaterfallDisplayPlot.h index d189ca2cbd..adf5278059 100644 --- a/gr-qtgui/lib/WaterfallDisplayPlot.h +++ b/gr-qtgui/lib/WaterfallDisplayPlot.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -20,18 +20,15 @@ * Boston, MA 02110-1301, USA. */ -#ifndef WATERFALL_DISPLAY_PLOT_HPP -#define WATERFALL_DISPLAY_PLOT_HPP +#ifndef WATERFALL_DISPLAY_PLOT_H +#define WATERFALL_DISPLAY_PLOT_H #include <stdint.h> #include <cstdio> -#include <qwt_plot.h> +#include <vector> #include <qwt_plot_spectrogram.h> -#include <qwt_plot_zoomer.h> -#include <qwt_plot_panner.h> -#include <qtgui_util.h> -#include <waterfallGlobalData.h> - +#include "DisplayPlot.h" +#include "waterfallGlobalData.h" #include <gruel/high_res_timer.h> #if QWT_VERSION < 0x060000 @@ -40,11 +37,12 @@ #include <qwt_compat.h> #endif -class WaterfallDisplayPlot:public QwtPlot{ +class WaterfallDisplayPlot: public DisplayPlot +{ Q_OBJECT public: - WaterfallDisplayPlot(QWidget*); + WaterfallDisplayPlot(int nplots, QWidget*); virtual ~WaterfallDisplayPlot(); void Reset(); @@ -53,45 +51,34 @@ public: const double, const bool, const double units=1000.0, const std::string &strunits = "kHz"); - double GetStartFrequency()const; - double GetStopFrequency()const; + double GetStartFrequency() const; + double GetStopFrequency() const; - void PlotNewData(const double* dataPoints, const int64_t numDataPoints, - const double timePerFFT, const gruel::high_res_timer_type timestamp, + void PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, + const double timePerFFT, + const gruel::high_res_timer_type timestamp, const int droppedFrames); - void SetIntensityRange(const double minIntensity, const double maxIntensity); - - virtual void replot(void); - - int GetIntensityColorMapType()const; - void SetIntensityColorMapType( const int, const QColor, const QColor ); - const QColor GetUserDefinedLowIntensityColor()const; - const QColor GetUserDefinedHighIntensityColor()const; + // to be removed + void PlotNewData(const double* dataPoints, + const int64_t numDataPoints, + const double timePerFFT, + const gruel::high_res_timer_type timestamp, + const int droppedFrames); - enum{ - INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR = 0, - INTENSITY_COLOR_MAP_TYPE_WHITE_HOT = 1, - INTENSITY_COLOR_MAP_TYPE_BLACK_HOT = 2, - INTENSITY_COLOR_MAP_TYPE_INCANDESCENT = 3, - INTENSITY_COLOR_MAP_TYPE_USER_DEFINED = 4 - }; + void SetIntensityRange(const double minIntensity, const double maxIntensity); -public slots: - void resizeSlot( QSize *s ); + void replot(void); - // Because of the preprocessing of slots in QT, these are no - // easily separated by the version check. Make one for each - // version until it's worked out. - void OnPickerPointSelected(const QwtDoublePoint & p); - void OnPickerPointSelected6(const QPointF & p); + int GetIntensityColorMapType(int) const; + void SetIntensityColorMapType(const int, const int, const QColor, const QColor); + const QColor GetUserDefinedLowIntensityColor() const; + const QColor GetUserDefinedHighIntensityColor() const; signals: void UpdatedLowerIntensityLevel(const double); void UpdatedUpperIntensityLevel(const double); - void plotPointSelected(const QPointF p); - -protected: private: void _UpdateIntensityRangeDisplay(); @@ -100,28 +87,21 @@ private: double _stopFrequency; int _xAxisMultiplier; - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - - QwtDblClickPlotPicker *_picker; - - WaterfallData *d_data; + std::vector<WaterfallData*> d_data; #if QWT_VERSION < 0x060000 - PlotWaterfall *d_spectrogram; + std::vector<PlotWaterfall*> d_spectrogram; #else - QwtPlotSpectrogram *d_spectrogram; + std::vector<QwtPlotSpectrogram*> d_spectrogram; #endif gruel::high_res_timer_type _lastReplot; bool _useCenterFrequencyFlag; - int64_t _numPoints; - - int _intensityColorMapType; + std::vector<int> _intensityColorMapType; QColor _userDefinedLowIntensityColor; QColor _userDefinedHighIntensityColor; }; -#endif /* WATERFALL_DISPLAY_PLOT_HPP */ +#endif /* WATERFALL_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/const_sink_c_impl.cc b/gr-qtgui/lib/const_sink_c_impl.cc new file mode 100644 index 0000000000..b504f7121d --- /dev/null +++ b/gr-qtgui/lib/const_sink_c_impl.cc @@ -0,0 +1,257 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "const_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> +#include <fft/fft.h> + +namespace gr { + namespace qtgui { + + const_sink_c::sptr + const_sink_c::make(int size, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new const_sink_c_impl(size, name, nconnections, parent)); + } + + const_sink_c_impl::const_sink_c_impl(int size, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("const_sink_c", + gr_make_io_signature(nconnections, nconnections, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_size(size), d_name(name), + d_nconnections(nconnections), d_parent(parent) + { + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs_real.push_back(fft::malloc_double(d_size)); + d_residbufs_imag.push_back(fft::malloc_double(d_size)); + memset(d_residbufs_real[i], 0, d_size*sizeof(double)); + memset(d_residbufs_imag[i], 0, d_size*sizeof(double)); + } + + // Set alignment properties for VOLK + const int alignment_multiple = + volk_get_alignment() / sizeof(gr_complex); + set_alignment(std::max(1,alignment_multiple)); + + initialize(); + } + + const_sink_c_impl::~const_sink_c_impl() + { + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs_real[i]); + fft::free(d_residbufs_imag[i]); + } + } + + void + const_sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new ConstellationDisplayForm(d_nconnections, d_parent); + d_main_gui->SetNPoints(d_size); + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + const_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + const_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + const_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + const_sink_c_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + const_sink_c_impl::set_title(int which, const std::string &title) + { + d_main_gui->setTitle(which, title.c_str()); + } + + void + const_sink_c_impl::set_color(int which, const std::string &color) + { + d_main_gui->setColor(which, color.c_str()); + } + + void + const_sink_c_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + const_sink_c_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + const_sink_c_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + const_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + const_sink_c_impl::set_nsamps(const int newsize) + { + gruel::scoped_lock lock(d_mutex); + + if(newsize != d_size) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs_real[i]); + fft::free(d_residbufs_imag[i]); + d_residbufs_real[i] = fft::malloc_double(newsize); + d_residbufs_imag[i] = fft::malloc_double(newsize); + + memset(d_residbufs_real[i], 0, newsize*sizeof(double)); + memset(d_residbufs_imag[i], 0, newsize*sizeof(double)); + } + + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) + d_size = newsize; + d_index = 0; + + d_main_gui->SetNPoints(d_size); + } + } + + void + const_sink_c_impl::npoints_resize() + { + int newsize = d_main_gui->GetNPoints(); + set_nsamps(newsize); + } + + int + const_sink_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const gr_complex *in = (const gr_complex*)input_items[idx]; + + npoints_resize(); + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[idx++]; + volk_32fc_deinterleave_64f_x2_u(&d_residbufs_real[n][d_index], + &d_residbufs_imag[n][d_index], + &in[j], resid); + } + + // Update the plot if its time + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new ConstUpdateEvent(d_residbufs_real, + d_residbufs_imag, + d_size)); + } + + d_index = 0; + j += resid; + } + + // Otherwise, copy what we received into the residbufs for next time + else { + for(n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[idx++]; + volk_32fc_deinterleave_64f_x2_u(&d_residbufs_real[n][d_index], + &d_residbufs_imag[n][d_index], + &in[j], datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/const_sink_c_impl.h b/gr-qtgui/lib/const_sink_c_impl.h new file mode 100644 index 0000000000..a17a8793b4 --- /dev/null +++ b/gr-qtgui/lib/const_sink_c_impl.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_CONST_SINK_C_IMPL_H +#define INCLUDED_QTGUI_CONST_SINK_C_IMPL_H + +#include <qtgui/const_sink_c.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <constellationdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API const_sink_c_impl : public const_sink_c + { + private: + void initialize(); + + gruel::mutex d_mutex; + + int d_size; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs_real; + std::vector<double*> d_residbufs_imag; + + QWidget *d_parent; + ConstellationDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void npoints_resize(); + + public: + const_sink_c_impl(int size, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~const_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, Qt::PenStyle style); + void set_line_marker(int which, QwtSymbol::Style marker); + void set_nsamps(const int size); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_CONST_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/constellationdisplayform.cc b/gr-qtgui/lib/constellationdisplayform.cc new file mode 100644 index 0000000000..98375ebaed --- /dev/null +++ b/gr-qtgui/lib/constellationdisplayform.cc @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <cmath> +#include <QMessageBox> +#include <constellationdisplayform.h> +#include <iostream> + +ConstellationDisplayForm::ConstellationDisplayForm(int nplots, QWidget* parent) + : DisplayForm(nplots, parent) +{ + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + + _layout = new QGridLayout(this); + _displayPlot = new ConstellationDisplayPlot(nplots, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + NPointsMenu *nptsmenu = new NPointsMenu(this); + _menu->addAction(nptsmenu); + connect(nptsmenu, SIGNAL(whichTrigger(int)), + this, SLOT(SetNPoints(const int))); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +ConstellationDisplayForm::~ConstellationDisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + delete _intValidator; +} + +ConstellationDisplayPlot* +ConstellationDisplayForm::getPlot() +{ + return ((ConstellationDisplayPlot*)_displayPlot); +} + +void +ConstellationDisplayForm::newData(const QEvent* updateEvent) +{ + ConstUpdateEvent *tevent = (ConstUpdateEvent*)updateEvent; + const std::vector<double*> realDataPoints = tevent->getRealPoints(); + const std::vector<double*> imagDataPoints = tevent->getImagPoints(); + const uint64_t numDataPoints = tevent->getNumDataPoints(); + + getPlot()->PlotNewData(realDataPoints, + imagDataPoints, + numDataPoints, + d_update_time); +} + +void +ConstellationDisplayForm::customEvent(QEvent * e) +{ + if(e->type() == ConstUpdateEvent::Type()) { + newData(e); + } +} + +int +ConstellationDisplayForm::GetNPoints() const +{ + return d_npoints; +} + +void +ConstellationDisplayForm::SetNPoints(const int npoints) +{ + d_npoints = npoints; +} diff --git a/gr-qtgui/lib/constellationdisplayform.h b/gr-qtgui/lib/constellationdisplayform.h new file mode 100644 index 0000000000..6832bd44cc --- /dev/null +++ b/gr-qtgui/lib/constellationdisplayform.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef CONSTELLATION_DISPLAY_FORM_H +#define CONSTELLATION_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <ConstellationDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> + +#include "displayform.h" + +class ConstellationDisplayForm : public DisplayForm +{ + Q_OBJECT + +public: + ConstellationDisplayForm(int nplots=1, QWidget* parent = 0); + ~ConstellationDisplayForm(); + + ConstellationDisplayPlot* getPlot(); + + int GetNPoints() const; + +public slots: + void customEvent(QEvent * e); + void SetNPoints(const int); + + // add set x/y axis + +private slots: + void newData(const QEvent*); + +private: + QIntValidator* _intValidator; + int d_npoints; +}; + +#endif /* CONSTELLATION_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/displayform.cc b/gr-qtgui/lib/displayform.cc new file mode 100644 index 0000000000..68e1a5c4fd --- /dev/null +++ b/gr-qtgui/lib/displayform.cc @@ -0,0 +1,260 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <displayform.h> +#include <iostream> + +DisplayForm::DisplayForm(int nplots, QWidget* parent) + : QWidget(parent), _nplots(nplots), _systemSpecifiedFlag(false) +{ + // Set the initial plot size + resize(QSize(800, 600)); + + // Set up a grid that can be turned on/off + _grid = new QwtPlotGrid(); + _grid->setPen(QPen(QColor(Qt::gray))); + + // Create a set of actions for the menu + _stop_act = new QAction("Stop", this); + _stop_act->setStatusTip(tr("Start/Stop")); + connect(_stop_act, SIGNAL(triggered()), this, SLOT(setStop())); + _stop_state = false; + + _grid_act = new QAction("Grid On", this); + _grid_act->setStatusTip(tr("Toggle Grid on/off")); + connect(_grid_act, SIGNAL(triggered()), this, SLOT(setGrid())); + _grid_state = false; + + // Create a pop-up menu for manipulating the figure + _menu = new QMenu(this); + _menu->addAction(_stop_act); + _menu->addAction(_grid_act); + + for(int i = 0; i < _nplots; i++) { + _line_title_act.push_back(new LineTitleAction(i, this)); + _line_color_menu.push_back(new LineColorMenu(i, this)); + _line_width_menu.push_back(new LineWidthMenu(i, this)); + _line_style_menu.push_back(new LineStyleMenu(i, this)); + _line_marker_menu.push_back(new LineMarkerMenu(i, this)); + + connect(_line_title_act[i], SIGNAL(whichTrigger(int, const QString&)), + this, SLOT(setTitle(int, const QString&))); + + for(int j = 0; j < _line_color_menu[i]->getNumActions(); j++) { + connect(_line_color_menu[i], SIGNAL(whichTrigger(int, const QString&)), + this, SLOT(setColor(int, const QString&))); + } + + for(int j = 0; j < _line_width_menu[i]->getNumActions(); j++) { + connect(_line_width_menu[i], SIGNAL(whichTrigger(int, int)), + this, SLOT(setLineWidth(int, int))); + } + + for(int j = 0; j < _line_style_menu[i]->getNumActions(); j++) { + connect(_line_style_menu[i], SIGNAL(whichTrigger(int, Qt::PenStyle)), + this, SLOT(setLineStyle(int, Qt::PenStyle))); + } + + for(int j = 0; j < _line_marker_menu[i]->getNumActions(); j++) { + connect(_line_marker_menu[i], SIGNAL(whichTrigger(int, QwtSymbol::Style)), + this, SLOT(setLineMarker(int, QwtSymbol::Style))); + } + + _lines_menu.push_back(new QMenu(tr(""), this)); + _lines_menu[i]->addAction(_line_title_act[i]); + _lines_menu[i]->addMenu(_line_color_menu[i]); + _lines_menu[i]->addMenu(_line_width_menu[i]); + _lines_menu[i]->addMenu(_line_style_menu[i]); + _lines_menu[i]->addMenu(_line_marker_menu[i]); + _menu->addMenu(_lines_menu[i]); + } + + Reset(); + + // Create a timer to update plots at the specified rate + d_displayTimer = new QTimer(this); + connect(d_displayTimer, SIGNAL(timeout()), this, SLOT(updateGuiTimer())); +} + +DisplayForm::~DisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + + d_displayTimer->stop(); + delete d_displayTimer; +} + +void +DisplayForm::resizeEvent( QResizeEvent *e ) +{ + QSize s = size(); + emit _displayPlot->resizeSlot(&s); +} + +void +DisplayForm::mousePressEvent( QMouseEvent * e) +{ + if(e->button() == Qt::RightButton) { + QwtPlotLayout *plt = _displayPlot->plotLayout(); + QRectF cvs = plt->canvasRect(); + + QRect plotrect; + plotrect.setLeft(cvs.x()-plt->spacing()-plt->canvasMargin(0)); + plotrect.setRight(cvs.x()+cvs.width()+plt->spacing()+plt->canvasMargin(0)); + plotrect.setBottom(cvs.y()-plt->spacing()-plt->canvasMargin(0)); + plotrect.setTop(cvs.y()+cvs.width()+plt->spacing()+plt->canvasMargin(0)); + + if(!plotrect.contains(e->pos())) { + if(_stop_state == false) + _stop_act->setText(tr("Stop")); + else + _stop_act->setText(tr("Start")); + + if(_grid_state == false) + _grid_act->setText(tr("Grid On")); + else + _grid_act->setText(tr("Grid Off")); + + // Update the line titles if changed externally + for(int i = 0; i < _nplots; i++) { + _lines_menu[i]->setTitle(_displayPlot->title(i)); + } + _menu->exec(e->globalPos()); + } + } +} + +void +DisplayForm::updateGuiTimer() +{ + _displayPlot->canvas()->update(); +} + +void +DisplayForm::onPlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 3); +} + +void +DisplayForm::Reset() +{ +} + + +void +DisplayForm::closeEvent( QCloseEvent *e ) +{ + qApp->processEvents(); + QWidget::closeEvent(e); +} + +void +DisplayForm::setUpdateTime(double t) +{ + d_update_time = t; + d_displayTimer->start(d_update_time); +} + +void +DisplayForm::setTitle(int which, const QString &title) +{ + _displayPlot->setTitle(which, title); +} + +void +DisplayForm::setColor(int which, const QString &color) +{ + _displayPlot->setColor(which, color); + _displayPlot->replot(); +} + +void +DisplayForm::setLineWidth(int which, int width) +{ + _displayPlot->setLineWidth(which, width); + _displayPlot->replot(); +} + +void +DisplayForm::setLineStyle(int which, Qt::PenStyle style) +{ + _displayPlot->setLineStyle(which, style); + _displayPlot->replot(); +} + +void +DisplayForm::setLineMarker(int which, QwtSymbol::Style marker) +{ + _displayPlot->setLineMarker(which, marker); + _displayPlot->replot(); +} + +void +DisplayForm::setStop(bool on) +{ + if(!on) { + // will auto-detach if already attached. + _displayPlot->setStop(false); + _stop_state = false; + } + else { + _displayPlot->setStop(true); + _stop_state = true; + } + _displayPlot->replot(); +} + +void +DisplayForm::setStop() +{ + if(_stop_state == false) + setStop(true); + else + setStop(false); +} + +void +DisplayForm::setGrid(bool on) +{ + if(on) { + // will auto-detach if already attached. + _grid->attach(_displayPlot); + _grid_state = true; + } + else { + _grid->detach(); + _grid_state = false; + } + _displayPlot->replot(); +} + +void +DisplayForm::setGrid() +{ + if(_grid_state == false) + setGrid(true); + else + setGrid(false); +} diff --git a/gr-qtgui/lib/displayform.h b/gr-qtgui/lib/displayform.h new file mode 100644 index 0000000000..82b28c745d --- /dev/null +++ b/gr-qtgui/lib/displayform.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef DISPLAY_FORM_H +#define DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <QtGui/QtGui> +#include <vector> + +#include <qwt_plot_grid.h> +#include <qwt_plot_layout.h> + +#include "DisplayPlot.h" +#include "form_menus.h" + +class DisplayForm : public QWidget +{ + Q_OBJECT + + public: + DisplayForm(int nplots=1, QWidget* parent = 0); + ~DisplayForm(); + + virtual DisplayPlot* getPlot() = 0; + void Reset(); + +public slots: + void resizeEvent( QResizeEvent * e ); + void mousePressEvent( QMouseEvent * e); + virtual void customEvent( QEvent * e ) = 0; + + void closeEvent( QCloseEvent * e ); + + void setUpdateTime(double t); + + void setTitle(int which, const QString &title); + void setColor(int which, const QString &color); + void setLineWidth(int which, int width); + void setLineStyle(int which, Qt::PenStyle style); + void setLineMarker(int which, QwtSymbol::Style style); + + void setStop(bool on); + void setStop(); + + void setGrid(bool on); + void setGrid(); + +private slots: + virtual void newData(const QEvent*) = 0; + void updateGuiTimer(); + + void onPlotPointSelected(const QPointF p); + +signals: + void plotPointSelected(const QPointF p, int type); + +protected: + int _nplots; + + QGridLayout *_layout; + DisplayPlot* _displayPlot; + bool _systemSpecifiedFlag; + + QwtPlotGrid *_grid; + + QMenu *_menu; + + QAction *_stop_act; + bool _stop_state; + QAction *_grid_act; + bool _grid_state; + + QList<QMenu*> _lines_menu; + QList<LineTitleAction*> _line_title_act; + QList<LineColorMenu*> _line_color_menu; + QList<LineWidthMenu*> _line_width_menu; + QList<LineStyleMenu*> _line_style_menu; + QList<LineMarkerMenu*> _line_marker_menu; + + QTimer *d_displayTimer; + double d_update_time; +}; + +#endif /* DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/form_menus.h b/gr-qtgui/lib/form_menus.h new file mode 100644 index 0000000000..01695f2c46 --- /dev/null +++ b/gr-qtgui/lib/form_menus.h @@ -0,0 +1,882 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef FORM_MENUS_H +#define FORM_MENUS_H + +#include <stdexcept> +#include <vector> +#include <QtGui/QtGui> +#include <qwt_symbol.h> +#include <filter/firdes.h> +#include "qtgui_types.h" + +class LineColorMenu: public QMenu +{ + Q_OBJECT + +public: + LineColorMenu(int which, QWidget *parent) + : QMenu("Line Color", parent), d_which(which) + { + d_act.push_back(new QAction("Blue", this)); + d_act.push_back(new QAction("Red", this)); + d_act.push_back(new QAction("Green", this)); + d_act.push_back(new QAction("Black", this)); + d_act.push_back(new QAction("Cyan", this)); + d_act.push_back(new QAction("Magenta", this)); + d_act.push_back(new QAction("Yellow", this)); + d_act.push_back(new QAction("Gray", this)); + d_act.push_back(new QAction("Dark Red", this)); + d_act.push_back(new QAction("Dark Green", this)); + d_act.push_back(new QAction("Dark Blue", this)); + d_act.push_back(new QAction("Dark Gray", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getBlue())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getRed())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getGreen())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getBlack())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getCyan())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(getMagenta())); + connect(d_act[6], SIGNAL(triggered()), this, SLOT(getYellow())); + connect(d_act[7], SIGNAL(triggered()), this, SLOT(getGray())); + connect(d_act[8], SIGNAL(triggered()), this, SLOT(getDarkRed())); + connect(d_act[9], SIGNAL(triggered()), this, SLOT(getDarkGreen())); + connect(d_act[10], SIGNAL(triggered()), this, SLOT(getDarkBlue())); + connect(d_act[11], SIGNAL(triggered()), this, SLOT(getDarkGray())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~LineColorMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("LineColorMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(int which, const QString &name); + +public slots: + void getBlue() { emit whichTrigger(d_which, "blue"); } + void getRed() { emit whichTrigger(d_which, "red"); } + void getGreen() { emit whichTrigger(d_which, "green"); } + void getBlack() { emit whichTrigger(d_which, "black"); } + void getCyan() { emit whichTrigger(d_which, "cyan"); } + void getMagenta() { emit whichTrigger(d_which, "magenta"); } + void getYellow() { emit whichTrigger(d_which, "yellow"); } + void getGray() { emit whichTrigger(d_which, "gray"); } + void getDarkRed() { emit whichTrigger(d_which, "darkred"); } + void getDarkGreen() { emit whichTrigger(d_which, "darkgreen"); } + void getDarkBlue() { emit whichTrigger(d_which, "darkblue"); } + void getDarkGray() { emit whichTrigger(d_which, "darkgray"); } + +private: + QList<QAction *> d_act; + int d_which; +}; + + +/********************************************************************/ + + +class LineWidthMenu: public QMenu +{ + Q_OBJECT + +public: + LineWidthMenu(int which, QWidget *parent) + : QMenu("Line Width", parent), d_which(which) + { + d_act.push_back(new QAction("1", this)); + d_act.push_back(new QAction("2", this)); + d_act.push_back(new QAction("3", this)); + d_act.push_back(new QAction("4", this)); + d_act.push_back(new QAction("5", this)); + d_act.push_back(new QAction("6", this)); + d_act.push_back(new QAction("7", this)); + d_act.push_back(new QAction("8", this)); + d_act.push_back(new QAction("9", this)); + d_act.push_back(new QAction("10", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getOne())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getTwo())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getThree())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getFour())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getFive())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(getSix())); + connect(d_act[6], SIGNAL(triggered()), this, SLOT(getSeven())); + connect(d_act[7], SIGNAL(triggered()), this, SLOT(getEight())); + connect(d_act[8], SIGNAL(triggered()), this, SLOT(getNine())); + connect(d_act[9], SIGNAL(triggered()), this, SLOT(getTen())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~LineWidthMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("LineWidthMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(int which, int width); + +public slots: + void getOne() { emit whichTrigger(d_which, 1); } + void getTwo() { emit whichTrigger(d_which, 2); } + void getThree() { emit whichTrigger(d_which, 3); } + void getFour() { emit whichTrigger(d_which, 4); } + void getFive() { emit whichTrigger(d_which, 5); } + void getSix() { emit whichTrigger(d_which, 6); } + void getSeven() { emit whichTrigger(d_which, 7); } + void getEight() { emit whichTrigger(d_which, 8); } + void getNine() { emit whichTrigger(d_which, 9); } + void getTen() { emit whichTrigger(d_which, 10); } + +private: + QList<QAction *> d_act; + int d_which; +}; + + +/********************************************************************/ + + +class LineStyleMenu: public QMenu +{ + Q_OBJECT + +public: + LineStyleMenu(int which, QWidget *parent) + : QMenu("Line Style", parent), d_which(which) + { + d_act.push_back(new QAction("None", this)); + d_act.push_back(new QAction("Solid", this)); + d_act.push_back(new QAction("Dash", this)); + d_act.push_back(new QAction("Dots", this)); + d_act.push_back(new QAction("Dash-Dot", this)); + d_act.push_back(new QAction("Dash-Dot-Dot", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getSolid())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getDash())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getDots())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getDashDot())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(getDashDotDot())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~LineStyleMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("LineStyleMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(int which, Qt::PenStyle); + +public slots: + void getNone() { emit whichTrigger(d_which, Qt::NoPen); } + void getSolid() { emit whichTrigger(d_which, Qt::SolidLine); } + void getDash() { emit whichTrigger(d_which, Qt::DashLine); } + void getDots() { emit whichTrigger(d_which, Qt::DotLine); } + void getDashDot() { emit whichTrigger(d_which, Qt::DashDotLine); } + void getDashDotDot() { emit whichTrigger(d_which, Qt::DashDotDotLine); } + +private: + QList<QAction *> d_act; + int d_which; +}; + + +/********************************************************************/ + + +class LineMarkerMenu: public QMenu +{ + Q_OBJECT + +public: + LineMarkerMenu(int which, QWidget *parent) + : QMenu("Line Marker", parent), d_which(which) + { + d_act.push_back(new QAction("None", this)); + d_act.push_back(new QAction("Circle", this)); + d_act.push_back(new QAction("Rectangle", this)); + d_act.push_back(new QAction("Diamond", this)); + d_act.push_back(new QAction("Triangle", this)); + d_act.push_back(new QAction("Down Triangle", this)); + d_act.push_back(new QAction("Left Triangle", this)); + d_act.push_back(new QAction("Right Triangle", this)); + d_act.push_back(new QAction("Cross", this)); + d_act.push_back(new QAction("X-Cross", this)); + d_act.push_back(new QAction("Horiz. Line", this)); + d_act.push_back(new QAction("Vert. Line", this)); + d_act.push_back(new QAction("Star 1", this)); + d_act.push_back(new QAction("Star 2", this)); + d_act.push_back(new QAction("Hexagon", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getCircle())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getRect())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getDiamond())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getTriangle())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(getDTriangle())); + connect(d_act[6], SIGNAL(triggered()), this, SLOT(getLTriangle())); + connect(d_act[7], SIGNAL(triggered()), this, SLOT(getRTriangle())); + connect(d_act[8], SIGNAL(triggered()), this, SLOT(getCross())); + connect(d_act[9], SIGNAL(triggered()), this, SLOT(getXCross())); + connect(d_act[10], SIGNAL(triggered()), this, SLOT(getHLine())); + connect(d_act[11], SIGNAL(triggered()), this, SLOT(getVLine())); + connect(d_act[12], SIGNAL(triggered()), this, SLOT(getStar1())); + connect(d_act[13], SIGNAL(triggered()), this, SLOT(getStar2())); + connect(d_act[14], SIGNAL(triggered()), this, SLOT(getHexagon())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~LineMarkerMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("LineMarkerMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(int which, QwtSymbol::Style); + +public slots: + void getNone() { emit whichTrigger(d_which, QwtSymbol::NoSymbol); } + void getCircle() { emit whichTrigger(d_which, QwtSymbol::Ellipse); } + void getRect() { emit whichTrigger(d_which, QwtSymbol::Rect); } + void getDiamond() { emit whichTrigger(d_which, QwtSymbol::Diamond); } + void getTriangle() { emit whichTrigger(d_which, QwtSymbol::Triangle); } + void getDTriangle() { emit whichTrigger(d_which, QwtSymbol::DTriangle); } + void getLTriangle() { emit whichTrigger(d_which, QwtSymbol::LTriangle); } + void getRTriangle() { emit whichTrigger(d_which, QwtSymbol::RTriangle); } + void getCross() { emit whichTrigger(d_which, QwtSymbol::Cross); } + void getXCross() { emit whichTrigger(d_which, QwtSymbol::XCross); } + void getHLine() { emit whichTrigger(d_which, QwtSymbol::HLine); } + void getVLine() { emit whichTrigger(d_which, QwtSymbol::VLine); } + void getStar1() { emit whichTrigger(d_which, QwtSymbol::Star1); } + void getStar2() { emit whichTrigger(d_which, QwtSymbol::Star2); } + void getHexagon() { emit whichTrigger(d_which, QwtSymbol::Hexagon); } + +private: + QList<QAction *> d_act; + int d_which; +}; + + +/********************************************************************/ + + +class LineTitleAction: public QAction +{ + Q_OBJECT + +public: + LineTitleAction(int which, QWidget *parent) + : QAction("Line Title", parent), d_which(which) + { + d_diag = new QDialog(parent); + d_diag->setModal(true); + + d_text = new QLineEdit(); + + QGridLayout *layout = new QGridLayout(d_diag); + QPushButton *btn_ok = new QPushButton(tr("OK")); + QPushButton *btn_cancel = new QPushButton(tr("Cancel")); + + layout->addWidget(d_text, 0, 0, 1, 2); + layout->addWidget(btn_ok, 1, 0); + layout->addWidget(btn_cancel, 1, 1); + + connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText())); + connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close())); + + connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag())); + } + + ~LineTitleAction() + {} + +signals: + void whichTrigger(int which, const QString &text); + +public slots: + void getTextDiag() + { + d_diag->exec(); + } + +private slots: + void getText() + { + emit whichTrigger(d_which, d_text->text()); + d_diag->accept(); + } + +private: + int d_which; + + QDialog *d_diag; + QLineEdit *d_text; +}; + + +/********************************************************************/ + + +class OtherAction: public QAction +{ + Q_OBJECT + +public: + OtherAction(QWidget *parent) + : QAction("Other", parent) + { + d_diag = new QDialog(parent); + d_diag->setModal(true); + + d_text = new QLineEdit(); + + QGridLayout *layout = new QGridLayout(d_diag); + QPushButton *btn_ok = new QPushButton(tr("OK")); + QPushButton *btn_cancel = new QPushButton(tr("Cancel")); + + layout->addWidget(d_text, 0, 0, 1, 2); + layout->addWidget(btn_ok, 1, 0); + layout->addWidget(btn_cancel, 1, 1); + + connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText())); + connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close())); + + connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag())); + } + + ~OtherAction() + {} + +signals: + void whichTrigger(const QString &text); + +public slots: + void getTextDiag() + { + d_diag->exec(); + } + +private slots: + void getText() + { + emit whichTrigger(d_text->text()); + d_diag->accept(); + } + +private: + QDialog *d_diag; + QLineEdit *d_text; +}; + +/********************************************************************/ + + +class OtherDualAction: public QAction +{ + Q_OBJECT + +public: + OtherDualAction(QString label0, QString label1, QWidget *parent) + : QAction("Other", parent) + { + d_diag = new QDialog(parent); + d_diag->setModal(true); + + d_text0 = new QLineEdit(); + d_text1 = new QLineEdit(); + + QLabel *_label0 = new QLabel(label0); + QLabel *_label1 = new QLabel(label1); + + QGridLayout *layout = new QGridLayout(d_diag); + QPushButton *btn_ok = new QPushButton(tr("OK")); + QPushButton *btn_cancel = new QPushButton(tr("Cancel")); + + layout->addWidget(_label0, 0, 0, 1, 2); + layout->addWidget(_label1, 1, 0, 1, 2); + + layout->addWidget(d_text0, 0, 1, 1, 2); + layout->addWidget(d_text1, 1, 1, 1, 2); + layout->addWidget(btn_ok, 2, 0); + layout->addWidget(btn_cancel, 2, 1); + + connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText())); + connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close())); + + connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag())); + } + + ~OtherDualAction() + {} + +signals: + void whichTrigger(const QString &text0, const QString &text1); + +public slots: + void getTextDiag() + { + d_diag->exec(); + } + +private slots: + void getText() + { + emit whichTrigger(d_text0->text(), d_text1->text()); + d_diag->accept(); + } + +private: + QDialog *d_diag; + QLineEdit *d_text0; + QLineEdit *d_text1; +}; + + +/********************************************************************/ + + +class FFTSizeMenu: public QMenu +{ + Q_OBJECT + +public: + FFTSizeMenu(QWidget *parent) + : QMenu("FFT Size", parent) + { + d_act.push_back(new QAction("32", this)); + d_act.push_back(new QAction("64", this)); + d_act.push_back(new QAction("128", this)); + d_act.push_back(new QAction("256", this)); + d_act.push_back(new QAction("512", this)); + d_act.push_back(new QAction("1024", this)); + d_act.push_back(new QAction("2048", this)); + d_act.push_back(new QAction("4096", this)); + d_act.push_back(new QAction("8192", this)); + d_act.push_back(new QAction("16384", this)); + d_act.push_back(new QAction("32768", this)); + d_act.push_back(new OtherAction(this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(get05())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(get06())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(get07())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(get08())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(get09())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(get10())); + connect(d_act[6], SIGNAL(triggered()), this, SLOT(get11())); + connect(d_act[7], SIGNAL(triggered()), this, SLOT(get12())); + connect(d_act[8], SIGNAL(triggered()), this, SLOT(get13())); + connect(d_act[9], SIGNAL(triggered()), this, SLOT(get14())); + connect(d_act[10], SIGNAL(triggered()), this, SLOT(get15())); + connect(d_act[11], SIGNAL(whichTrigger(const QString&)), + this, SLOT(getOther(const QString&))); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~FFTSizeMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("FFTSizeMenu::getAction: which out of range.\n"); + } + + signals: + void whichTrigger(int size); + + public slots: + void get05() { emit whichTrigger(32); } + void get06() { emit whichTrigger(64); } + void get07() { emit whichTrigger(128); } + void get08() { emit whichTrigger(256); } + void get09() { emit whichTrigger(512); } + void get10() { emit whichTrigger(1024); } + void get11() { emit whichTrigger(2048); } + void get12() { emit whichTrigger(4096); } + void get13() { emit whichTrigger(8192); } + void get14() { emit whichTrigger(16384); } + void get15() { emit whichTrigger(32768); } + void getOther(const QString &str) + { + int value = str.toInt(); + emit whichTrigger(value); + } + +private: + QList<QAction *> d_act; + OtherAction *d_other; +}; + + +/********************************************************************/ + + +class FFTAverageMenu: public QMenu +{ + Q_OBJECT + +public: + FFTAverageMenu(QWidget *parent) + : QMenu("FFT Average", parent) + { + d_act.push_back(new QAction("Off", this)); + d_act.push_back(new QAction("High", this)); + d_act.push_back(new QAction("Medium", this)); + d_act.push_back(new QAction("Low", this)); + d_act.push_back(new OtherAction(this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getOff())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getHigh())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getMedium())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getLow())); + connect(d_act[4], SIGNAL(whichTrigger(const QString&)), + this, SLOT(getOther(const QString&))); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~FFTAverageMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("FFTSizeMenu::getAction: which out of range.\n"); + } + + signals: + void whichTrigger(float alpha); + + public slots: + void getOff() { emit whichTrigger(1.0); } + void getHigh() { emit whichTrigger(0.05); } + void getMedium() { emit whichTrigger(0.1); } + void getLow() { emit whichTrigger(0.2); } + void getOther(const QString &str) + { + float value = str.toFloat(); + emit whichTrigger(value); + } + +private: + QList<QAction *> d_act; + OtherAction *d_other; +}; + + +/********************************************************************/ + + +class FFTWindowMenu: public QMenu +{ + Q_OBJECT + +public: + FFTWindowMenu(QWidget *parent) + : QMenu("FFT Window", parent) + { + d_act.push_back(new QAction("None", this)); + d_act.push_back(new QAction("Hamming", this)); + d_act.push_back(new QAction("Hann", this)); + d_act.push_back(new QAction("Blackman", this)); + d_act.push_back(new QAction("Blackman-harris", this)); + d_act.push_back(new QAction("Rectangular", this)); + d_act.push_back(new QAction("Kaiser", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getHamming())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getHann())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getBlackman())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getBlackmanharris())); + connect(d_act[5], SIGNAL(triggered()), this, SLOT(getRectangular())); + connect(d_act[6], SIGNAL(triggered()), this, SLOT(getKaiser())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~FFTWindowMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("FFTWindowMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(const gr::filter::firdes::win_type type); + +public slots: + void getNone() { emit whichTrigger(gr::filter::firdes::WIN_NONE); } + void getHamming() { emit whichTrigger(gr::filter::firdes::WIN_HAMMING); } + void getHann() { emit whichTrigger(gr::filter::firdes::WIN_HANN); } + void getBlackman() { emit whichTrigger(gr::filter::firdes::WIN_BLACKMAN); } + void getBlackmanharris() { emit whichTrigger(gr::filter::firdes::WIN_BLACKMAN_hARRIS); } + void getRectangular() { emit whichTrigger(gr::filter::firdes::WIN_RECTANGULAR); } + void getKaiser() { emit whichTrigger(gr::filter::firdes::WIN_KAISER); } + +private: + QList<QAction *> d_act; + int d_which; +}; + + +/********************************************************************/ + + +class NPointsMenu: public QAction +{ + Q_OBJECT + +public: + NPointsMenu(QWidget *parent) + : QAction("Number of Points", parent) + { + d_diag = new QDialog(parent); + d_diag->setModal(true); + + d_text = new QLineEdit(); + + QGridLayout *layout = new QGridLayout(d_diag); + QPushButton *btn_ok = new QPushButton(tr("OK")); + QPushButton *btn_cancel = new QPushButton(tr("Cancel")); + + layout->addWidget(d_text, 0, 0, 1, 2); + layout->addWidget(btn_ok, 1, 0); + layout->addWidget(btn_cancel, 1, 1); + + connect(btn_ok, SIGNAL(clicked()), this, SLOT(getText())); + connect(btn_cancel, SIGNAL(clicked()), d_diag, SLOT(close())); + + connect(this, SIGNAL(triggered()), this, SLOT(getTextDiag())); + } + + ~NPointsMenu() + {} + +signals: + void whichTrigger(const int npts); + +public slots: + void getTextDiag() + { + d_diag->show(); + } + +private slots: + void getText() + { + emit whichTrigger(d_text->text().toInt()); + d_diag->accept(); + } + +private: + QDialog *d_diag; + QLineEdit *d_text; +}; + + +/********************************************************************/ + + +class ColorMapMenu: public QMenu +{ + Q_OBJECT + +public: + ColorMapMenu(QWidget *parent) + : QMenu("Color Map", parent) + { + d_act.push_back(new QAction("Multi-Color", this)); + d_act.push_back(new QAction("White Hot", this)); + d_act.push_back(new QAction("Black Hot", this)); + d_act.push_back(new QAction("Incandescent", this)); + d_act.push_back(new QAction("Other", this)); + //d_act.push_back(new OtherDualAction("Min Intensity: ", "Max Intensity: ", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getMultiColor())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getWhiteHot())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getBlackHot())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getIncandescent())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getOther())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + + d_max_value = QColor("white"); + d_min_value = QColor("white"); + } + + ~ColorMapMenu() + {} + + int getNumActions() const + { + return d_act.size(); + } + + QAction * getAction(int which) + { + if(which < d_act.size()) + return d_act[which]; + else + throw std::runtime_error("ColorMapMenu::getAction: which out of range.\n"); + } + + signals: + void whichTrigger(const int type, const QColor &min_color=QColor(), + const QColor &max_color=QColor()); + + public slots: + void getMultiColor() { emit whichTrigger(INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR); } + void getWhiteHot() { emit whichTrigger(INTENSITY_COLOR_MAP_TYPE_WHITE_HOT); } + void getBlackHot() { emit whichTrigger(INTENSITY_COLOR_MAP_TYPE_BLACK_HOT); } + void getIncandescent() { emit whichTrigger(INTENSITY_COLOR_MAP_TYPE_INCANDESCENT); } + //void getOther(const QString &min_str, const QString &max_str) + void getOther() + { + QMessageBox::information(this, "Set low and high intensities", + "In the next windows, select the low and then the high intensity colors.", + QMessageBox::Ok); + d_min_value = QColorDialog::getColor(d_min_value, this); + d_max_value = QColorDialog::getColor(d_max_value, this); + + emit whichTrigger(INTENSITY_COLOR_MAP_TYPE_USER_DEFINED, + d_min_value, d_max_value); + } + +private: + QList<QAction *> d_act; + OtherDualAction *d_other; + QColor d_max_value, d_min_value; +}; + + +/********************************************************************/ + + +#endif /* FORM_MENUS_H */ diff --git a/gr-qtgui/lib/freq_sink_c_impl.cc b/gr-qtgui/lib/freq_sink_c_impl.cc new file mode 100644 index 0000000000..cf5c989c09 --- /dev/null +++ b/gr-qtgui/lib/freq_sink_c_impl.cc @@ -0,0 +1,376 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "freq_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + freq_sink_c::sptr + freq_sink_c::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new freq_sink_c_impl(fftsize, wintype, + fc, bw, name, + nconnections, + parent)); + } + + freq_sink_c_impl::freq_sink_c_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("freq_sink_c", + gr_make_io_signature(1, -1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_fftsize(fftsize), d_fftavg(1.0), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_nconnections(nconnections), d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex(d_fftsize, true); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_complex(d_fftsize)); + d_magbufs.push_back(fft::malloc_double(d_fftsize)); + + memset(d_residbufs[i], 0, d_fftsize*sizeof(gr_complex)); + memset(d_magbufs[i], 0, d_fftsize*sizeof(double)); + } + + buildwindow(); + + initialize(); + } + + freq_sink_c_impl::~freq_sink_c_impl() + { + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + } + delete d_fft; + fft::free(d_fbuf); + } + + void + freq_sink_c_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + freq_sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new FreqDisplayForm(d_nconnections, d_parent); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetFFTWindowType(d_wintype); + d_main_gui->SetFrequencyRange(d_center_freq, + d_center_freq - d_bandwidth/2.0, + d_center_freq + d_bandwidth/2.0); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + freq_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + freq_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + freq_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + freq_sink_c_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + freq_sink_c_impl::fft_size() const + { + return d_fftsize; + } + + void + freq_sink_c_impl::set_fft_average(const float fftavg) + { + d_fftavg = fftavg; + d_main_gui->SetFFTAverage(fftavg); + } + + float + freq_sink_c_impl::fft_average() const + { + return d_fftavg; + } + + void + freq_sink_c_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + freq_sink_c_impl::set_fft_power_db(double min, double max) + { + d_main_gui->SetFrequencyAxis(min, max); + } + + void + freq_sink_c_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + freq_sink_c_impl::set_title(int which, const std::string &title) + { + d_main_gui->setTitle(which, title.c_str()); + } + + void + freq_sink_c_impl::set_color(int which, const std::string &color) + { + d_main_gui->setColor(which, color.c_str()); + } + + void + freq_sink_c_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + freq_sink_c_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + freq_sink_c_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + freq_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + freq_sink_c_impl::fft(float *data_out, const gr_complex *data_in, int size) + { + if(d_window.size()) { + volk_32fc_32f_multiply_32fc_a(d_fft->get_inbuf(), data_in, + &d_window.front(), size); + } + else { + memcpy(d_fft->get_inbuf(), data_in, sizeof(gr_complex)*size); + } + + d_fft->execute(); // compute the fft + + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); + + // Perform shift operation + unsigned int len = (unsigned int)(floor(size/2.0)); + float *tmp = (float*)malloc(sizeof(float)*len); + memcpy(tmp, &data_out[0], sizeof(float)*len); + memcpy(&data_out[0], &data_out[len], sizeof(float)*(size - len)); + memcpy(&data_out[size - len], tmp, sizeof(float)*len); + free(tmp); + } + + void + freq_sink_c_impl::windowreset() + { + filter::firdes::win_type newwintype; + newwintype = d_main_gui->GetFFTWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + freq_sink_c_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != filter::firdes::WIN_NONE) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + freq_sink_c_impl::fftresize() + { + gruel::scoped_lock lock(d_mutex); + + int newfftsize = d_main_gui->GetFFTSize(); + d_fftavg = d_main_gui->GetFFTAverage(); + + if(newfftsize != d_fftsize) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + + d_residbufs[i] = fft::malloc_complex(newfftsize); + d_magbufs[i] = fft::malloc_double(newfftsize); + + memset(d_residbufs[i], 0, newfftsize*sizeof(gr_complex)); + memset(d_magbufs[i], 0, newfftsize*sizeof(double)); + } + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex(d_fftsize, true); + + fft::free(d_fbuf); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + } + } + + int + freq_sink_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const gr_complex *in = (const gr_complex*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + + for(int n = 0; n < d_nconnections; n++) { + // Fill up residbuf with d_fftsize number of items + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*resid); + + fft(d_fbuf, d_residbufs[n], d_fftsize); + for(int x = 0; x < d_fftsize; x++) { + d_magbufs[n][x] = (double)((1.0-d_fftavg)*d_magbufs[n][x] + (d_fftavg)*d_fbuf[x]); + } + //volk_32f_convert_64f_a(d_magbufs[n], d_fbuf, d_fftsize); + } + + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new FreqUpdateEvent(d_magbufs, d_fftsize)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbuf for next time + else { + for(int n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/freq_sink_c_impl.h b/gr-qtgui/lib/freq_sink_c_impl.h new file mode 100644 index 0000000000..106e50497d --- /dev/null +++ b/gr-qtgui/lib/freq_sink_c_impl.h @@ -0,0 +1,110 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_FREQ_SINK_C_IMPL_H +#define INCLUDED_QTGUI_FREQ_SINK_C_IMPL_H + +#include <qtgui/freq_sink_c.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <freqdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API freq_sink_c_impl : public freq_sink_c + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + gruel::mutex d_mutex; + + int d_fftsize; + float d_fftavg; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + std::vector<gr_complex*> d_residbufs; + std::vector<double*> d_magbufs; + float *d_fbuf; + + QWidget *d_parent; + FreqDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const gr_complex *data_in, int size); + + public: + freq_sink_c_impl(int size, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~freq_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_fft_power_db(double min, double max); + + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, Qt::PenStyle style); + void set_line_marker(int which, QwtSymbol::Style marker); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_FREQ_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/freq_sink_f_impl.cc b/gr-qtgui/lib/freq_sink_f_impl.cc new file mode 100644 index 0000000000..2cf43e9994 --- /dev/null +++ b/gr-qtgui/lib/freq_sink_f_impl.cc @@ -0,0 +1,380 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "freq_sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + freq_sink_f::sptr + freq_sink_f::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new freq_sink_f_impl(fftsize, wintype, + fc, bw, name, + nconnections, + parent)); + } + + freq_sink_f_impl::freq_sink_f_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("freq_sink_f", + gr_make_io_signature(1, -1, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_fftsize(fftsize), d_fftavg(1.0), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_nconnections(nconnections), d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex(d_fftsize, true); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_float(d_fftsize)); + d_magbufs.push_back(fft::malloc_double(d_fftsize)); + + memset(d_residbufs[i], 0, d_fftsize*sizeof(float)); + memset(d_magbufs[i], 0, d_fftsize*sizeof(double)); + } + + buildwindow(); + + initialize(); + } + + freq_sink_f_impl::~freq_sink_f_impl() + { + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + } + delete d_fft; + fft::free(d_fbuf); + } + + void + freq_sink_f_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + freq_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new FreqDisplayForm(d_nconnections, d_parent); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetFFTWindowType(d_wintype); + d_main_gui->SetFrequencyRange(d_center_freq, + d_center_freq - d_bandwidth/2.0, + d_center_freq + d_bandwidth/2.0); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + freq_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + freq_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + freq_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + freq_sink_f_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + freq_sink_f_impl::fft_size() const + { + return d_fftsize; + } + + void + freq_sink_f_impl::set_fft_average(const float fftavg) + { + d_fftavg = fftavg; + d_main_gui->SetFFTAverage(fftavg); + } + + float + freq_sink_f_impl::fft_average() const + { + return d_fftavg; + } + + void + freq_sink_f_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + freq_sink_f_impl::set_fft_power_db(double min, double max) + { + d_main_gui->SetFrequencyAxis(min, max); + } + + void + freq_sink_f_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + freq_sink_f_impl::set_title(int which, const std::string &title) + { + d_main_gui->setTitle(which, title.c_str()); + } + + void + freq_sink_f_impl::set_color(int which, const std::string &color) + { + d_main_gui->setColor(which, color.c_str()); + } + + void + freq_sink_f_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + freq_sink_f_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + freq_sink_f_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + + void + freq_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + freq_sink_f_impl::fft(float *data_out, const float *data_in, int size) + { + // float to complex conversion + gr_complex *dst = d_fft->get_inbuf(); + for (int i = 0; i < size; i++) + dst[i] = data_in[i]; + + if(d_window.size()) { + volk_32fc_32f_multiply_32fc_a(d_fft->get_inbuf(), dst, + &d_window.front(), size); + } + + d_fft->execute(); // compute the fft + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); + + // Perform shift operation + unsigned int len = (unsigned int)(floor(size/2.0)); + float *tmp = (float*)malloc(sizeof(float)*len); + memcpy(tmp, &data_out[0], sizeof(float)*len); + memcpy(&data_out[0], &data_out[len], sizeof(float)*(size - len)); + memcpy(&data_out[size - len], tmp, sizeof(float)*len); + free(tmp); + } + + void + freq_sink_f_impl::windowreset() + { + gruel::scoped_lock lock(d_mutex); + + filter::firdes::win_type newwintype; + newwintype = d_main_gui->GetFFTWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + freq_sink_f_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != filter::firdes::WIN_NONE) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + freq_sink_f_impl::fftresize() + { + gruel::scoped_lock lock(d_mutex); + + int newfftsize = d_main_gui->GetFFTSize(); + d_fftavg = d_main_gui->GetFFTAverage(); + + if(newfftsize != d_fftsize) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + + d_residbufs[i] = fft::malloc_float(newfftsize); + d_magbufs[i] = fft::malloc_double(newfftsize); + + memset(d_residbufs[i], 0, newfftsize*sizeof(float)); + memset(d_magbufs[i], 0, newfftsize*sizeof(double)); + } + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex(d_fftsize, true); + + fft::free(d_fbuf); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + } + } + + int + freq_sink_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const float *in = (const float*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + + for(int n = 0; n < d_nconnections; n++) { + // Fill up residbuf with d_fftsize number of items + in = (const float*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(float)*resid); + + fft(d_fbuf, d_residbufs[n], d_fftsize); + for(int x = 0; x < d_fftsize; x++) { + d_magbufs[n][x] = (double)((1.0-d_fftavg)*d_magbufs[n][x] + (d_fftavg)*d_fbuf[x]); + } + //volk_32f_convert_64f_a(d_magbufs[n], d_fbuf, d_fftsize); + } + + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new FreqUpdateEvent(d_magbufs, d_fftsize)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbuf for next time + else { + for(int n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(float)*datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/freq_sink_f_impl.h b/gr-qtgui/lib/freq_sink_f_impl.h new file mode 100644 index 0000000000..a358953629 --- /dev/null +++ b/gr-qtgui/lib/freq_sink_f_impl.h @@ -0,0 +1,110 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_FREQ_SINK_F_IMPL_H +#define INCLUDED_QTGUI_FREQ_SINK_F_IMPL_H + +#include <qtgui/freq_sink_f.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <freqdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API freq_sink_f_impl : public freq_sink_f + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + gruel::mutex d_mutex; + + int d_fftsize; + float d_fftavg; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + std::vector<float*> d_residbufs; + std::vector<double*> d_magbufs; + float *d_fbuf; + + QWidget *d_parent; + FreqDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const float *data_in, int size); + + public: + freq_sink_f_impl(int size, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~freq_sink_f_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_fft_power_db(double min, double max); + + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, Qt::PenStyle style); + void set_line_marker(int which, QwtSymbol::Style marker); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_FREQ_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/freqdisplayform.cc b/gr-qtgui/lib/freqdisplayform.cc new file mode 100644 index 0000000000..6f12fe5090 --- /dev/null +++ b/gr-qtgui/lib/freqdisplayform.cc @@ -0,0 +1,162 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <cmath> +#include <QMessageBox> +#include <freqdisplayform.h> +#include <iostream> + +FreqDisplayForm::FreqDisplayForm(int nplots, QWidget* parent) + : DisplayForm(nplots, parent) +{ + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + + _layout = new QGridLayout(this); + _displayPlot = new FrequencyDisplayPlot(nplots, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + _numRealDataPoints = 1024; + _fftsize = 1024; + _fftavg = 1.0; + + FFTSizeMenu *sizemenu = new FFTSizeMenu(this); + FFTAverageMenu *avgmenu = new FFTAverageMenu(this); + FFTWindowMenu *winmenu = new FFTWindowMenu(this); + _menu->addMenu(sizemenu); + _menu->addMenu(avgmenu); + _menu->addMenu(winmenu); + connect(sizemenu, SIGNAL(whichTrigger(int)), + this, SLOT(SetFFTSize(const int))); + connect(avgmenu, SIGNAL(whichTrigger(float)), + this, SLOT(SetFFTAverage(const float))); + connect(winmenu, SIGNAL(whichTrigger(gr::filter::firdes::win_type)), + this, SLOT(SetFFTWindowType(const gr::filter::firdes::win_type))); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +FreqDisplayForm::~FreqDisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + delete _intValidator; +} + +FrequencyDisplayPlot* +FreqDisplayForm::getPlot() +{ + return ((FrequencyDisplayPlot*)_displayPlot); +} + +void +FreqDisplayForm::newData(const QEvent *updateEvent) +{ + FreqUpdateEvent *fevent = (FreqUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = fevent->getPoints(); + const uint64_t numDataPoints = fevent->getNumDataPoints(); + + getPlot()->PlotNewData(dataPoints, numDataPoints, + 0, 0, 0, d_update_time); +} + +void +FreqDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == FreqUpdateEvent::Type()) { + newData(e); + } +} + +int +FreqDisplayForm::GetFFTSize() const +{ + return _fftsize; +} + +float +FreqDisplayForm::GetFFTAverage() const +{ + return _fftavg; +} + +gr::filter::firdes::win_type +FreqDisplayForm::GetFFTWindowType() const +{ + return _fftwintype; +} + +void +FreqDisplayForm::SetFFTSize(const int newsize) +{ + _fftsize = newsize; +} + +void +FreqDisplayForm::SetFFTAverage(const float newavg) +{ + _fftavg = newavg; + getPlot()->replot(); +} + +void +FreqDisplayForm::SetFFTWindowType(const gr::filter::firdes::win_type newwin) +{ + _fftwintype = newwin; +} + +void +FreqDisplayForm::SetFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency) +{ + double fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency)); + + if(fdiff > 0) { + std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"}; + double units10 = floor(log10(fdiff)); + double units3 = std::max(floor(units10 / 3.0), 0.0); + double units = pow(10, (units10-fmod(units10, 3.0))); + int iunit = static_cast<int>(units3); + + _startFrequency = newStartFrequency; + _stopFrequency = newStopFrequency; + double centerFrequency = newCenterFrequency; + + getPlot()->SetFrequencyRange(_startFrequency, + _stopFrequency, + centerFrequency, + true, + units, strunits[iunit]); + } +} + +void +FreqDisplayForm::SetFrequencyAxis(double min, double max) +{ + getPlot()->set_yaxis(min, max); +} diff --git a/gr-qtgui/lib/freqdisplayform.h b/gr-qtgui/lib/freqdisplayform.h new file mode 100644 index 0000000000..ffce317c4f --- /dev/null +++ b/gr-qtgui/lib/freqdisplayform.h @@ -0,0 +1,75 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef FREQ_DISPLAY_FORM_H +#define FREQ_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <FrequencyDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> +#include <filter/firdes.h> + +#include "displayform.h" + +class FreqDisplayForm : public DisplayForm +{ + Q_OBJECT + + public: + FreqDisplayForm(int nplots=1, QWidget* parent = 0); + ~FreqDisplayForm(); + + FrequencyDisplayPlot* getPlot(); + + int GetFFTSize() const; + float GetFFTAverage() const; + gr::filter::firdes::win_type GetFFTWindowType() const; + +public slots: + void customEvent(QEvent *e); + + void SetFFTSize(const int); + void SetFFTAverage(const float); + void SetFFTWindowType(const gr::filter::firdes::win_type); + + void SetFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency); + void SetFrequencyAxis(double min, double max); + +private slots: + void newData(const QEvent *updateEvent); + +private: + uint64_t _numRealDataPoints; + QIntValidator* _intValidator; + + double _startFrequency; + double _stopFrequency; + + int _fftsize; + float _fftavg; + gr::filter::firdes::win_type _fftwintype; +}; + +#endif /* FREQ_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/qtgui_sink_c.cc b/gr-qtgui/lib/qtgui_sink_c.cc deleted file mode 100644 index de730a8710..0000000000 --- a/gr-qtgui/lib/qtgui_sink_c.cc +++ /dev/null @@ -1,310 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2010,2011 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <qtgui_sink_c.h> -#include <gr_io_signature.h> -#include <string.h> - -#include <QTimer> - -qtgui_sink_c_sptr -qtgui_make_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent) -{ - return gnuradio::get_initial_sptr(new qtgui_sink_c (fftsize, wintype, - fc, bw, name, - plotfreq, plotwaterfall, - plottime, plotconst, - parent)); -} - -qtgui_sink_c::qtgui_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent) - : gr_block ("sink_c", - gr_make_io_signature (1, -1, sizeof(gr_complex)), - gr_make_io_signature (0, 0, 0)), - d_fftsize(fftsize), - d_wintype((gr_firdes::win_type)(wintype)), - d_center_freq(fc), d_bandwidth(bw), d_name(name), - d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall), - d_plottime(plottime), d_plotconst(plotconst), - d_parent(parent) -{ - d_main_gui = NULL; - - // Perform fftshift operation; - // this is usually desired when plotting - d_shift = true; - - d_fft = new gri_fft_complex (d_fftsize, true); - - d_index = 0; - d_residbuf = new gr_complex[d_fftsize]; - - buildwindow(); - - initialize(); -} - -qtgui_sink_c::~qtgui_sink_c() -{ - delete d_main_gui; - delete [] d_residbuf; - delete d_fft; -} - -void -qtgui_sink_c::forecast(int noutput_items, gr_vector_int &ninput_items_required) -{ - unsigned int ninputs = ninput_items_required.size(); - for (unsigned int i = 0; i < ninputs; i++) { - ninput_items_required[i] = std::min(d_fftsize, 8191); - } -} - -void -qtgui_sink_c::initialize() -{ - if(qApp != NULL) { - d_qApplication = qApp; - } - else { - int argc=0; - char **argv = NULL; - d_qApplication = new QApplication(argc, argv); - } - - if(d_center_freq < 0) { - throw std::runtime_error("qtgui_sink_c: Received bad center frequency.\n"); - } - - uint64_t maxBufferSize = 32768; - d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, - d_center_freq, - -d_bandwidth/2.0, - d_bandwidth/2.0); - - d_main_gui->SetDisplayTitle(d_name); - d_main_gui->SetFFTSize(d_fftsize); - d_main_gui->SetWindowType((int)d_wintype); - - d_main_gui->OpenSpectrumWindow(d_parent, - d_plotfreq, d_plotwaterfall, - d_plottime, d_plotconst); - - // initialize update time to 10 times a second - set_update_time(0.5); - - d_last_update = gruel::high_res_timer_now(); - d_update_active = false; -} - - -void -qtgui_sink_c::exec_() -{ - d_qApplication->exec(); -} - -QWidget* -qtgui_sink_c::qwidget() -{ - return d_main_gui->qwidget(); -} - -PyObject* -qtgui_sink_c::pyqwidget() -{ - PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget()); - PyObject *retarg = Py_BuildValue("N", w); - return retarg; -} - -void -qtgui_sink_c::set_frequency_range(const double centerfreq, - const double bandwidth) -{ - d_center_freq = centerfreq; - d_bandwidth = bandwidth; - d_main_gui->SetFrequencyRange(d_center_freq, - -d_bandwidth/2.0, - d_bandwidth/2.0); -} - -void -qtgui_sink_c::set_time_domain_axis(double min, double max) -{ - d_main_gui->SetTimeDomainAxis(min, max); -} - -void -qtgui_sink_c::set_constellation_axis(double xmin, double xmax, - double ymin, double ymax) -{ - d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax); -} - -void -qtgui_sink_c::set_constellation_pen_size(int size) -{ - d_main_gui->SetConstellationPenSize(size); -} - - -void -qtgui_sink_c::set_frequency_axis(double min, double max) -{ - d_main_gui->SetFrequencyAxis(min, max); -} - -void -qtgui_sink_c::set_update_time(double t) -{ - d_update_time = t * gruel::high_res_timer_tps(); - d_main_gui->SetUpdateTime(t); -} - -void -qtgui_sink_c::fft(const gr_complex *data_in, int size) -{ - if (d_window.size()) { - gr_complex *dst = d_fft->get_inbuf(); - int i; - for (i = 0; i < size; i++) // apply window - dst[i] = data_in[i] * d_window[i]; - } - else { - memcpy (d_fft->get_inbuf(), data_in, sizeof(gr_complex)*size); - } - - d_fft->execute (); // compute the fft -} - -void -qtgui_sink_c::windowreset() -{ - gr_firdes::win_type newwintype = (gr_firdes::win_type)d_main_gui->GetWindowType(); - if(d_wintype != newwintype) { - d_wintype = newwintype; - buildwindow(); - } -} - -void -qtgui_sink_c::buildwindow() -{ - d_window.clear(); - if(d_wintype != 0) { - d_window = gr_firdes::window(d_wintype, d_fftsize, 6.76); - } -} - -void -qtgui_sink_c::fftresize() -{ - int newfftsize = d_main_gui->GetFFTSize(); - - if(newfftsize != d_fftsize) { - - // Resize residbuf and replace data - delete [] d_residbuf; - d_residbuf = new gr_complex[newfftsize]; - - // Set new fft size and reset buffer index - // (throws away any currently held data, but who cares?) - d_fftsize = newfftsize; - d_index = 0; - - // Reset window to reflect new size - buildwindow(); - - // Reset FFTW plan for new size - delete d_fft; - d_fft = new gri_fft_complex (d_fftsize, true); - } -} - - -int -qtgui_sink_c::general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - int j=0; - const gr_complex *in = (const gr_complex*)input_items[0]; - - // Update the FFT size from the application - fftresize(); - windowreset(); - - for(int i=0; i < noutput_items; i+=d_fftsize) { - unsigned int datasize = noutput_items - i; - unsigned int resid = d_fftsize-d_index; - - if (!d_update_active && (gruel::high_res_timer_now() - d_last_update) < d_update_time) { - consume_each(noutput_items); - return noutput_items; - } else { - d_last_update = gruel::high_res_timer_now(); - d_update_active = true; - } - - // If we have enough input for one full FFT, do it - if(datasize >= resid) { - const gruel::high_res_timer_type currentTime = gruel::high_res_timer_now(); - - // Fill up residbuf with d_fftsize number of items - memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*resid); - d_index = 0; - - j += resid; - fft(d_residbuf, d_fftsize); - - d_main_gui->UpdateWindow(true, d_fft->get_outbuf(), d_fftsize, - NULL, 0, (float*)d_residbuf, d_fftsize, - currentTime, true); - d_update_active = false; - } - // Otherwise, copy what we received into the residbuf for next time - else { - memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*datasize); - d_index += datasize; - j += datasize; - } - } - - consume_each(j); - return j; -} diff --git a/gr-qtgui/lib/qtgui_sink_f.cc b/gr-qtgui/lib/qtgui_sink_f.cc deleted file mode 100644 index a02f89d0ac..0000000000 --- a/gr-qtgui/lib/qtgui_sink_f.cc +++ /dev/null @@ -1,294 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2010,2011 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <qtgui_sink_f.h> -#include <gr_io_signature.h> -#include <string.h> - -#include <QTimer> - -qtgui_sink_f_sptr -qtgui_make_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent) -{ - return gnuradio::get_initial_sptr(new qtgui_sink_f (fftsize, wintype, - fc, bw, name, - plotfreq, plotwaterfall, - plottime, plotconst, - parent)); -} - -qtgui_sink_f::qtgui_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent) - : gr_block ("sink_f", - gr_make_io_signature (1, 1, sizeof(float)), - gr_make_io_signature (0, 0, 0)), - d_fftsize(fftsize), - d_wintype((gr_firdes::win_type)(wintype)), - d_center_freq(fc), d_bandwidth(bw), d_name(name), - d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall), - d_plottime(plottime), d_plotconst(plotconst), - d_parent(parent) -{ - d_main_gui = NULL; - - // Perform fftshift operation; - // this is usually desired when plotting - d_shift = true; - - d_fft = new gri_fft_complex (d_fftsize, true); - - d_index = 0; - d_residbuf = new float[d_fftsize]; - - buildwindow(); - - initialize(); -} - -qtgui_sink_f::~qtgui_sink_f() -{ - delete d_main_gui; - delete [] d_residbuf; - delete d_fft; -} - -void -qtgui_sink_f::forecast(int noutput_items, gr_vector_int &ninput_items_required) -{ - unsigned int ninputs = ninput_items_required.size(); - for (unsigned int i = 0; i < ninputs; i++) { - ninput_items_required[i] = std::min(d_fftsize, 8191); - } -} - -void -qtgui_sink_f::initialize() -{ - if(qApp != NULL) { - d_qApplication = qApp; - } - else { - int argc; - char **argv = NULL; - d_qApplication = new QApplication(argc, argv); - } - - - uint64_t maxBufferSize = 32768; - d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, - d_center_freq, - -d_bandwidth/2.0, - d_bandwidth/2.0); - d_main_gui->SetDisplayTitle(d_name); - d_main_gui->SetFFTSize(d_fftsize); - d_main_gui->SetWindowType((int)d_wintype); - - d_main_gui->OpenSpectrumWindow(d_parent, - d_plotfreq, d_plotwaterfall, - d_plottime, d_plotconst); - - // initialize update time to 10 times a second - set_update_time(0.1); -} - -void -qtgui_sink_f::exec_() -{ - d_qApplication->exec(); -} - -QWidget* -qtgui_sink_f::qwidget() -{ - return d_main_gui->qwidget(); -} - -PyObject* -qtgui_sink_f::pyqwidget() -{ - PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget()); - PyObject *retarg = Py_BuildValue("N", w); - return retarg; -} - -void -qtgui_sink_f::set_frequency_range(const double centerfreq, - const double bandwidth) -{ - d_center_freq = centerfreq; - d_bandwidth = bandwidth; - d_main_gui->SetFrequencyRange(d_center_freq, - -d_bandwidth/2.0, - d_bandwidth/2.0); -} - -void -qtgui_sink_f::set_time_domain_axis(double min, double max) -{ - d_main_gui->SetTimeDomainAxis(min, max); -} - -void -qtgui_sink_f::set_constellation_axis(double xmin, double xmax, - double ymin, double ymax) -{ - d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax); -} - -void -qtgui_sink_f::set_constellation_pen_size(int size) -{ - d_main_gui->SetConstellationPenSize(size); -} - - -void -qtgui_sink_f::set_frequency_axis(double min, double max) -{ - d_main_gui->SetFrequencyAxis(min, max); -} - -void -qtgui_sink_f::set_update_time(double t) -{ - d_update_time = t; - d_main_gui->SetUpdateTime(d_update_time); -} - -void -qtgui_sink_f::fft(const float *data_in, int size) -{ - if (d_window.size()) { - gr_complex *dst = d_fft->get_inbuf(); - for (int i = 0; i < size; i++) // apply window - dst[i] = data_in[i] * d_window[i]; - } - else { - gr_complex *dst = d_fft->get_inbuf(); - for (int i = 0; i < size; i++) // float to complex conversion - dst[i] = data_in[i]; - } - - d_fft->execute (); // compute the fft -} - -void -qtgui_sink_f::windowreset() -{ - gr_firdes::win_type newwintype = (gr_firdes::win_type)d_main_gui->GetWindowType(); - if(d_wintype != newwintype) { - d_wintype = newwintype; - buildwindow(); - } -} - -void -qtgui_sink_f::buildwindow() -{ - d_window.clear(); - if(d_wintype != 0) { - d_window = gr_firdes::window(d_wintype, d_fftsize, 6.76); - } -} - -void -qtgui_sink_f::fftresize() -{ - int newfftsize = d_main_gui->GetFFTSize(); - - if(newfftsize != d_fftsize) { - - // Resize residbuf and replace data - delete [] d_residbuf; - d_residbuf = new float[newfftsize]; - - // Set new fft size and reset buffer index - // (throws away any currently held data, but who cares?) - d_fftsize = newfftsize; - d_index = 0; - - // Reset window to reflect new size - buildwindow(); - - // Reset FFTW plan for new size - delete d_fft; - d_fft = new gri_fft_complex (d_fftsize, true); - } -} - - -int -qtgui_sink_f::general_work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - int j=0; - const float *in = (const float*)input_items[0]; - - // Update the FFT size from the application - fftresize(); - windowreset(); - - for(int i=0; i < noutput_items; i+=d_fftsize) { - unsigned int datasize = noutput_items - i; - unsigned int resid = d_fftsize-d_index; - - // If we have enough input for one full FFT, do it - if(datasize >= resid) { - const gruel::high_res_timer_type currentTime = gruel::high_res_timer_now(); - - // Fill up residbuf with d_fftsize number of items - memcpy(d_residbuf+d_index, &in[j], sizeof(float)*resid); - d_index = 0; - - j += resid; - fft(d_residbuf, d_fftsize); - - d_main_gui->UpdateWindow(true, d_fft->get_outbuf(), d_fftsize, - (float*)d_residbuf, d_fftsize, NULL, 0, - currentTime, true); - } - // Otherwise, copy what we received into the residbuf for next time - else { - memcpy(d_residbuf+d_index, &in[j], sizeof(float)*datasize); - d_index += datasize; - j += datasize; - } - } - - consume_each(j); - return j; -} diff --git a/gr-qtgui/lib/qtgui_time_sink_c.cc b/gr-qtgui/lib/qtgui_time_sink_c.cc deleted file mode 100644 index 574fd93ad6..0000000000 --- a/gr-qtgui/lib/qtgui_time_sink_c.cc +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <qtgui_time_sink_c.h> -#include <gr_io_signature.h> -#include <string.h> - -#include <QTimer> - -qtgui_time_sink_c_sptr -qtgui_make_time_sink_c (int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent) -{ - return gnuradio::get_initial_sptr(new qtgui_time_sink_c (size, bw, name, - nconnections, parent)); -} - -qtgui_time_sink_c::qtgui_time_sink_c (int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent) - : gr_sync_block ("time_sink_c", - gr_make_io_signature (nconnections, nconnections, sizeof(gr_complex)), - gr_make_io_signature (0, 0, 0)), - d_size(size), d_bandwidth(bw), d_name(name), - d_nconnections(2*nconnections), d_parent(parent) -{ - d_main_gui = NULL; - - d_index = 0; - - for(int i = 0; i < d_nconnections; i++) { - d_residbufs.push_back(new double[d_size]); - } - - initialize(); - set_output_multiple(d_size); -} - -qtgui_time_sink_c::~qtgui_time_sink_c() -{ - // d_main_gui is a qwidget destroyed with its parent - for(int i = 0; i < d_nconnections; i++) { - delete [] d_residbufs[i]; - } -} - -void -qtgui_time_sink_c::initialize() -{ - if(qApp != NULL) { - d_qApplication = qApp; - } - else { - int argc=0; - char **argv = NULL; - d_qApplication = new QApplication(argc, argv); - } - - d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); - - // initialize update time to 10 times a second - set_update_time(0.1); - d_last_time = 0; -} - - -void -qtgui_time_sink_c::exec_() -{ - d_qApplication->exec(); -} - -QWidget* -qtgui_time_sink_c::qwidget() -{ - return d_main_gui; -} - -PyObject* -qtgui_time_sink_c::pyqwidget() -{ - PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); - PyObject *retarg = Py_BuildValue("N", w); - return retarg; -} - -void -qtgui_time_sink_c::set_time_domain_axis(double min, double max) -{ - d_main_gui->setTimeDomainAxis(min, max); -} - -void -qtgui_time_sink_c::set_update_time(double t) -{ - d_update_time = t; - d_main_gui->setUpdateTime(d_update_time); -} - -void -qtgui_time_sink_c::set_title(int which, const std::string &title) -{ - d_main_gui->setTitle(which, title.c_str()); -} - -void -qtgui_time_sink_c::set_color(int which, const std::string &color) -{ - d_main_gui->setColor(which, color.c_str()); -} - -int -qtgui_time_sink_c::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - int n=0, j=0, idx=0; - const gr_complex *in = (const gr_complex*)input_items[idx]; - - for(int i=0; i < noutput_items; i+=d_size) { - unsigned int datasize = noutput_items - i; - unsigned int resid = d_size-d_index; - idx = 0; - - // If we have enough input for one full plot, do it - if(datasize >= resid) { - d_current_time = gruel::high_res_timer_now(); - - // Fill up residbufs with d_size number of items - for(n = 0; n < d_nconnections; n+=2) { - in = (const gr_complex*)input_items[idx++]; - for(unsigned int k = 0; k < resid; k++) { - d_residbufs[n][d_index+k] = in[j+k].real(); - d_residbufs[n+1][d_index+k] = in[j+k].imag(); - } - } - - // Update the plot if its time - if(gruel::high_res_timer_now() - d_last_time > d_update_time) { - d_last_time = d_current_time; - d_qApplication->postEvent(d_main_gui, - new TimeUpdateEvent(d_residbufs, d_size)); - } - - d_index = 0; - j += resid; - } - // Otherwise, copy what we received into the residbufs for next time - // because we set the output_multiple, this should never need to be called - else { - assert(0); - for(n = 0; n < d_nconnections; n+=2) { - in = (const gr_complex*)input_items[idx++]; - for(unsigned int k = 0; k < resid; k++) { - d_residbufs[n][d_index+k] = in[j+k].real(); - d_residbufs[n+1][d_index+k] = in[j+k].imag(); - } - } - d_index += datasize; - j += datasize; - } - } - - return noutput_items; -} diff --git a/gr-qtgui/lib/qtgui_time_sink_f.cc b/gr-qtgui/lib/qtgui_time_sink_f.cc deleted file mode 100644 index 09fdf0a92e..0000000000 --- a/gr-qtgui/lib/qtgui_time_sink_f.cc +++ /dev/null @@ -1,189 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <qtgui_time_sink_f.h> -#include <gr_io_signature.h> -#include <string.h> - -#include <QTimer> - -qtgui_time_sink_f_sptr -qtgui_make_time_sink_f (int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent) -{ - return gnuradio::get_initial_sptr(new qtgui_time_sink_f (size, bw, name, - nconnections, parent)); -} - -qtgui_time_sink_f::qtgui_time_sink_f (int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent) - : gr_sync_block ("time_sink_f", - gr_make_io_signature (nconnections, nconnections, sizeof(float)), - gr_make_io_signature (0, 0, 0)), - d_size(size), d_bandwidth(bw), d_name(name), - d_nconnections(nconnections), d_parent(parent) -{ - d_main_gui = NULL; - - d_index = 0; - - for(int i = 0; i < d_nconnections; i++) { - d_residbufs.push_back(new double[d_size]); - } - - initialize(); - set_output_multiple(d_size); -} - -qtgui_time_sink_f::~qtgui_time_sink_f() -{ - // d_main_gui is a qwidget destroyed with its parent - for(int i = 0; i < d_nconnections; i++) { - delete [] d_residbufs[i]; - } -} - -void -qtgui_time_sink_f::initialize() -{ - if(qApp != NULL) { - d_qApplication = qApp; - } - else { - int argc=0; - char **argv = NULL; - d_qApplication = new QApplication(argc, argv); - } - - d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); - - // initialize update time to 10 times a second - set_update_time(0.1); - d_last_time = 0; -} - - -void -qtgui_time_sink_f::exec_() -{ - d_qApplication->exec(); -} - -QWidget* -qtgui_time_sink_f::qwidget() -{ - return d_main_gui; -} - -PyObject* -qtgui_time_sink_f::pyqwidget() -{ - PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); - PyObject *retarg = Py_BuildValue("N", w); - return retarg; -} - -void -qtgui_time_sink_f::set_time_domain_axis(double min, double max) -{ - d_main_gui->setTimeDomainAxis(min, max); -} - -void -qtgui_time_sink_f::set_update_time(double t) -{ - d_update_time = t; - d_main_gui->setUpdateTime(d_update_time); -} - -void -qtgui_time_sink_f::set_title(int which, const std::string &title) -{ - d_main_gui->setTitle(which, title.c_str()); -} - -void -qtgui_time_sink_f::set_color(int which, const std::string &color) -{ - d_main_gui->setColor(which, color.c_str()); -} - -int -qtgui_time_sink_f::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - int n=0, j=0, idx=0; - const float *in = (const float*)input_items[idx]; - - for(int i=0; i < noutput_items; i+=d_size) { - unsigned int datasize = noutput_items - i; - unsigned int resid = d_size-d_index; - idx = 0; - - // If we have enough input for one full plot, do it - if(datasize >= resid) { - d_current_time = gruel::high_res_timer_now(); - - // Fill up residbufs with d_size number of items - for(n = 0; n < d_nconnections; n++) { - in = (const float*)input_items[idx++]; - for(unsigned int k = 0; k < resid; k++) { - d_residbufs[n][d_index+k] = in[j+k]; - } - } - - // Update the plot if its time - if(gruel::high_res_timer_now() - d_last_time > d_update_time) { - d_last_time = d_current_time; - d_qApplication->postEvent(d_main_gui, - new TimeUpdateEvent(d_residbufs, d_size)); - } - - d_index = 0; - j += resid; - } - // Otherwise, copy what we received into the residbufs for next time - // because we set the output_multiple, this should never need to be called - else { - assert(0); - for(n = 0; n < d_nconnections; n++) { - in = (const float*)input_items[idx++]; - for(unsigned int k = 0; k < resid; k++) { - d_residbufs[n][d_index+k] = in[j+k]; - } - } - d_index += datasize; - j += datasize; - } - } - - return noutput_items; -} diff --git a/gr-qtgui/lib/qtgui_types.h b/gr-qtgui/lib/qtgui_types.h new file mode 100644 index 0000000000..fd5dd6295f --- /dev/null +++ b/gr-qtgui/lib/qtgui_types.h @@ -0,0 +1,196 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef QTGUI_TYPES_H +#define QTGUI_TYPES_H + +#include <qwt_color_map.h> +#include <qwt_scale_draw.h> +#include <gruel/high_res_timer.h> + +class FreqOffsetAndPrecisionClass +{ +public: + FreqOffsetAndPrecisionClass(const int freqPrecision) + { + _frequencyPrecision = freqPrecision; + _centerFrequency = 0; + } + + virtual ~FreqOffsetAndPrecisionClass() + { + } + + virtual unsigned int GetFrequencyPrecision() const + { + return _frequencyPrecision; + } + + virtual void SetFrequencyPrecision(const unsigned int newPrecision) + { + _frequencyPrecision = newPrecision; + } + + virtual double GetCenterFrequency() const + { + return _centerFrequency; + } + + virtual void SetCenterFrequency(const double newFreq) + { + _centerFrequency = newFreq; + } + +protected: + +private: + unsigned int _frequencyPrecision; + double _centerFrequency; +}; + +class TimeScaleData +{ +public: + TimeScaleData() + { + _zeroTime = 0; + _secondsPerLine = 1.0; + } + + virtual ~TimeScaleData() + { + } + + virtual gruel::high_res_timer_type GetZeroTime() const + { + return _zeroTime; + } + + virtual void SetZeroTime(const gruel::high_res_timer_type newTime) + { + _zeroTime = newTime - gruel::high_res_timer_epoch(); + } + + virtual void SetSecondsPerLine(const double newTime) + { + _secondsPerLine = newTime; + } + + virtual double GetSecondsPerLine() const + { + return _secondsPerLine; + } + + +protected: + +private: + gruel::high_res_timer_type _zeroTime; + double _secondsPerLine; + +}; + +/*********************************************************************** + * Text scale widget to provide X (freq) axis text + **********************************************************************/ +class FreqDisplayScaleDraw: public QwtScaleDraw, FreqOffsetAndPrecisionClass +{ +public: + FreqDisplayScaleDraw(const unsigned int precision) + : QwtScaleDraw(), FreqOffsetAndPrecisionClass(precision) + { + } + + virtual QwtText label(double value) const + { + return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision()); + } + + virtual void initiateUpdate(void) + { + invalidateCache(); + } + +protected: + +private: + +}; + +enum{ + INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR = 0, + INTENSITY_COLOR_MAP_TYPE_WHITE_HOT = 1, + INTENSITY_COLOR_MAP_TYPE_BLACK_HOT = 2, + INTENSITY_COLOR_MAP_TYPE_INCANDESCENT = 3, + INTENSITY_COLOR_MAP_TYPE_USER_DEFINED = 4 +}; + +class ColorMap_MultiColor: public QwtLinearColorMap +{ +public: + ColorMap_MultiColor(): + QwtLinearColorMap(Qt::darkCyan, Qt::white) + { + addColorStop(0.25, Qt::cyan); + addColorStop(0.5, Qt::yellow); + addColorStop(0.75, Qt::red); + } +}; + +class ColorMap_WhiteHot: public QwtLinearColorMap +{ +public: + ColorMap_WhiteHot(): + QwtLinearColorMap(Qt::black, Qt::white) + { + } +}; + +class ColorMap_BlackHot: public QwtLinearColorMap +{ +public: + ColorMap_BlackHot(): + QwtLinearColorMap(Qt::white, Qt::black) + { + } +}; + +class ColorMap_Incandescent: public QwtLinearColorMap +{ +public: + ColorMap_Incandescent(): + QwtLinearColorMap(Qt::black, Qt::white) + { + addColorStop(0.5, Qt::darkRed); + } +}; + +class ColorMap_UserDefined: public QwtLinearColorMap +{ +public: + ColorMap_UserDefined(QColor low, QColor high): + QwtLinearColorMap(low, high) + { + } +}; + +#endif //QTGUI_TYPES_H diff --git a/gr-qtgui/lib/qtgui_util.cc b/gr-qtgui/lib/qtgui_util.cc index 543ce1b1c7..70dcb483fb 100644 --- a/gr-qtgui/lib/qtgui_util.cc +++ b/gr-qtgui/lib/qtgui_util.cc @@ -20,53 +20,32 @@ * Boston, MA 02110-1301, USA. */ -#include <qtgui_util.h> +#include <qtgui/utils.h> +#include <QDebug> -#if QWT_VERSION < 0x060000 QwtPickerDblClickPointMachine::QwtPickerDblClickPointMachine() +#if QWT_VERSION < 0x060000 : QwtPickerMachine () -{ -} #else -QwtPickerDblClickPointMachine::QwtPickerDblClickPointMachine() : QwtPickerMachine (PointSelection) +#endif { } -#endif QwtPickerDblClickPointMachine::~QwtPickerDblClickPointMachine() { - } #if QWT_VERSION < 0x060000 -QwtPickerMachine::CommandList -QwtPickerDblClickPointMachine::transition(const QwtEventPattern &eventPattern, - const QEvent *e) -{ - QwtPickerMachine::CommandList cmdList; - switch(e->type()) { - case QEvent::MouseButtonDblClick: - if ( eventPattern.mouseMatch(QwtEventPattern::MouseSelect1, - (const QMouseEvent *)e) ) { - cmdList += QwtPickerMachine::Begin; - cmdList += QwtPickerMachine::Append; - cmdList += QwtPickerMachine::End; - } - break; - default: - break; - } - return cmdList; -} - +#define CMDLIST_TYPE QwtPickerMachine::CommandList #else - -QList<QwtPickerMachine::Command> +#define CMDLIST_TYPE QList<QwtPickerMachine::Command> +#endif +CMDLIST_TYPE QwtPickerDblClickPointMachine::transition(const QwtEventPattern &eventPattern, const QEvent *e) { - QList<QwtPickerMachine::Command> cmdList; + CMDLIST_TYPE cmdList; switch(e->type()) { case QEvent::MouseButtonDblClick: if ( eventPattern.mouseMatch(QwtEventPattern::MouseSelect1, @@ -81,7 +60,6 @@ QwtPickerDblClickPointMachine::transition(const QwtEventPattern &eventPattern, } return cmdList; } -#endif QwtDblClickPlotPicker::QwtDblClickPlotPicker(QwtPlotCanvas* canvas) : QwtPlotPicker(canvas) diff --git a/gr-qtgui/lib/sink_c_impl.cc b/gr-qtgui/lib/sink_c_impl.cc new file mode 100644 index 0000000000..29f56a5f39 --- /dev/null +++ b/gr-qtgui/lib/sink_c_impl.cc @@ -0,0 +1,335 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008-2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + sink_c::sptr + sink_c::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new sink_c_impl(fftsize, wintype, + fc, bw, name, + plotfreq, plotwaterfall, + plottime, plotconst, + parent)); + } + + sink_c_impl::sink_c_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent) + : gr_block("sink_c", + gr_make_io_signature(1, -1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_fftsize(fftsize), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall), + d_plottime(plottime), d_plotconst(plotconst), + d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex (d_fftsize, true); + + d_index = 0; + d_residbuf = new gr_complex[d_fftsize]; + d_magbuf = new float[d_fftsize]; + + buildwindow(); + + initialize(); + } + + sink_c_impl::~sink_c_impl() + { + delete d_main_gui; + delete [] d_residbuf; + delete [] d_magbuf; + delete d_fft; + } + + void + sink_c_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for(unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + if(d_center_freq < 0) { + throw std::runtime_error("sink_c_impl: Received bad center frequency.\n"); + } + + uint64_t maxBufferSize = 32768; + d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, + d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + + d_main_gui->SetDisplayTitle(d_name); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetWindowType((int)d_wintype); + + d_main_gui->OpenSpectrumWindow(d_parent, + d_plotfreq, d_plotwaterfall, + d_plottime, d_plotconst); + + // initialize update time to 10 times a second + set_update_time(0.5); + + d_last_update = gruel::high_res_timer_now(); + d_update_active = false; + } + + void + sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + sink_c_impl::qwidget() + { + return d_main_gui->qwidget(); + } + + PyObject* + sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget()); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + sink_c_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + sink_c_impl::fft_size() const + { + return d_fftsize; + } + + void + sink_c_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + sink_c_impl::set_fft_power_db(double min, double max) + { + d_main_gui->SetFrequencyAxis(min, max); + } + + /* + void + sink_c_impl::set_time_domain_axis(double min, double max) + { + d_main_gui->SetTimeDomainAxis(min, max); + } + + void + sink_c_impl::set_constellation_axis(double xmin, double xmax, + double ymin, double ymax) + { + d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax); + } + + void + sink_c_impl::set_constellation_pen_size(int size) + { + d_main_gui->SetConstellationPenSize(size); + } + */ + + void + sink_c_impl::set_update_time(double t) + { + d_update_time = t * gruel::high_res_timer_tps(); + d_main_gui->SetUpdateTime(t); + } + + void + sink_c_impl::fft(float *data_out, const gr_complex *data_in, int size) + { + if (d_window.size()) { + volk_32fc_32f_multiply_32fc_a(d_fft->get_inbuf(), data_in, + &d_window.front(), size); + } + else { + memcpy (d_fft->get_inbuf(), data_in, sizeof(gr_complex)*size); + } + + d_fft->execute (); // compute the fft + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); +} + + void + sink_c_impl::windowreset() + { + filter::firdes::win_type newwintype; + newwintype = (filter::firdes::win_type)d_main_gui->GetWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + sink_c_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != 0) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + sink_c_impl::fftresize() + { + int newfftsize = d_main_gui->GetFFTSize(); + + if(newfftsize != d_fftsize) { + + // Resize residbuf and replace data + delete [] d_residbuf; + d_residbuf = new gr_complex[newfftsize]; + + delete [] d_magbuf; + d_magbuf = new float[newfftsize]; + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex (d_fftsize, true); + } + } + + int + sink_c_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const gr_complex *in = (const gr_complex*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + if (!d_update_active && (gruel::high_res_timer_now() - d_last_update) < d_update_time) { + consume_each(noutput_items); + return noutput_items; + } + else { + d_last_update = gruel::high_res_timer_now(); + d_update_active = true; + } + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + const gruel::high_res_timer_type currentTime = gruel::high_res_timer_now(); + + // Fill up residbuf with d_fftsize number of items + memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*resid); + d_index = 0; + + j += resid; + fft(d_magbuf, d_residbuf, d_fftsize); + + d_main_gui->UpdateWindow(true, d_magbuf, d_fftsize, + NULL, 0, (float*)d_residbuf, d_fftsize, + currentTime, true); + d_update_active = false; + } + // Otherwise, copy what we received into the residbuf for next time + else { + memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*datasize); + d_index += datasize; + j += datasize; + } + } + + consume_each(j); + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/sink_c_impl.h b/gr-qtgui/lib/sink_c_impl.h new file mode 100644 index 0000000000..b201bcd5a2 --- /dev/null +++ b/gr-qtgui/lib/sink_c_impl.h @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_SINK_C_IMPL_H +#define INCLUDED_QTGUI_SINK_C_IMPL_H + +#include <qtgui/sink_c.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <SpectrumGUIClass.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API sink_c_impl : public sink_c + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + gruel::high_res_timer_type d_last_update; + bool d_update_active; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + gr_complex *d_residbuf; + float *d_magbuf; + + bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst; + + gruel::high_res_timer_type d_update_time; + + QWidget *d_parent; + SpectrumGUIClass *d_main_gui; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const gr_complex *data_in, int size); + + public: + sink_c_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + ~sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + + void set_frequency_range(const double centerfreq, + const double bandwidth); + void set_fft_power_db(double min, double max); + + //void set_time_domain_axis(double min, double max); + //void set_constellation_axis(double xmin, double xmax, + // double ymin, double ymax); + //void set_constellation_pen_size(int size); + + void set_update_time(double t); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/sink_f_impl.cc b/gr-qtgui/lib/sink_f_impl.cc new file mode 100644 index 0000000000..580c774c0b --- /dev/null +++ b/gr-qtgui/lib/sink_f_impl.cc @@ -0,0 +1,320 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008-2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + sink_f::sptr + sink_f::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new sink_f_impl(fftsize, wintype, + fc, bw, name, + plotfreq, plotwaterfall, + plottime, plotconst, + parent)); + } + + sink_f_impl::sink_f_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent) + : gr_block("sink_f", + gr_make_io_signature(1, 1, sizeof(float)), + gr_make_io_signature (0, 0, 0)), + d_fftsize(fftsize), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall), + d_plottime(plottime), d_plotconst(plotconst), + d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex (d_fftsize, true); + + d_index = 0; + d_residbuf = new float[d_fftsize]; + d_magbuf = new float[d_fftsize]; + + buildwindow(); + + initialize(); + } + + sink_f_impl::~sink_f_impl() + { + delete d_main_gui; + delete [] d_residbuf; + delete [] d_magbuf; + delete d_fft; + } + + void + sink_f_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + uint64_t maxBufferSize = 32768; + d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, + d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + d_main_gui->SetDisplayTitle(d_name); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetWindowType((int)d_wintype); + + d_main_gui->OpenSpectrumWindow(d_parent, + d_plotfreq, d_plotwaterfall, + d_plottime, d_plotconst); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + void + sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + sink_f_impl::qwidget() + { + return d_main_gui->qwidget(); + } + + PyObject* + sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget()); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + sink_f_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + sink_f_impl::fft_size() const + { + return d_fftsize; + } + + void + sink_f_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + sink_f_impl::set_fft_power_db(double min, double max) + { + d_main_gui->SetFrequencyAxis(min, max); + } + + /* + void + sink_f_impl::set_time_domain_axis(double min, double max) + { + d_main_gui->SetTimeDomainAxis(min, max); + } + + void + sink_f_impl::set_constellation_axis(double xmin, double xmax, + double ymin, double ymax) + { + d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax); + } + + void + sink_f_impl::set_constellation_pen_size(int size) + { + d_main_gui->SetConstellationPenSize(size); + } + */ + + void + sink_f_impl::set_update_time(double t) + { + d_update_time = t; + d_main_gui->SetUpdateTime(d_update_time); + } + + void + sink_f_impl::fft(float *data_out, const float *data_in, int size) + { + if (d_window.size()) { + gr_complex *dst = d_fft->get_inbuf(); + for (int i = 0; i < size; i++) // apply window + dst[i] = data_in[i] * d_window[i]; + } + else { + gr_complex *dst = d_fft->get_inbuf(); + for (int i = 0; i < size; i++) // float to complex conversion + dst[i] = data_in[i]; + } + + d_fft->execute (); // compute the fft + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); + } + + void + sink_f_impl::windowreset() + { + filter::firdes::win_type newwintype; + newwintype = (filter::firdes::win_type)d_main_gui->GetWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + sink_f_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != 0) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + sink_f_impl::fftresize() + { + int newfftsize = d_main_gui->GetFFTSize(); + + if(newfftsize != d_fftsize) { + + // Resize residbuf and replace data + delete [] d_residbuf; + d_residbuf = new float[newfftsize]; + + delete [] d_magbuf; + d_magbuf = new float[newfftsize]; + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex (d_fftsize, true); + } + } + + int + sink_f_impl::general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const float *in = (const float*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + const gruel::high_res_timer_type currentTime = gruel::high_res_timer_now(); + + // Fill up residbuf with d_fftsize number of items + memcpy(d_residbuf+d_index, &in[j], sizeof(float)*resid); + d_index = 0; + + j += resid; + fft(d_magbuf, d_residbuf, d_fftsize); + + d_main_gui->UpdateWindow(true, d_magbuf, d_fftsize, + (float*)d_residbuf, d_fftsize, NULL, 0, + currentTime, true); + } + // Otherwise, copy what we received into the residbuf for next time + else { + memcpy(d_residbuf+d_index, &in[j], sizeof(float)*datasize); + d_index += datasize; + j += datasize; + } + } + + consume_each(j); + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/sink_f_impl.h b/gr-qtgui/lib/sink_f_impl.h new file mode 100644 index 0000000000..93dc46e85f --- /dev/null +++ b/gr-qtgui/lib/sink_f_impl.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_SINK_F_IMPL_H +#define INCLUDED_QTGUI_SINK_F_IMPL_H + +#include <qtgui/sink_f.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <SpectrumGUIClass.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API sink_f_impl : public sink_f + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + float *d_residbuf; + float *d_magbuf; + + bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst; + + double d_update_time; + + QWidget *d_parent; + SpectrumGUIClass *d_main_gui; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const float *data_in, int size); + + public: + sink_f_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + ~sink_f_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + + void set_frequency_range(const double centerfreq, + const double bandwidth); + void set_fft_power_db(double min, double max); + + //void set_time_domain_axis(double min, double max); + //void set_constellation_axis(double xmin, double xmax, + // double ymin, double ymax); + //void set_constellation_pen_size(int size); + + void set_update_time(double t); + + int general_work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/spectrumUpdateEvents.cc b/gr-qtgui/lib/spectrumUpdateEvents.cc index bec39747b8..92dd807147 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.cc +++ b/gr-qtgui/lib/spectrumUpdateEvents.cc @@ -3,7 +3,7 @@ #include <spectrumUpdateEvents.h> -SpectrumUpdateEvent::SpectrumUpdateEvent(const std::complex<float>* fftPoints, +SpectrumUpdateEvent::SpectrumUpdateEvent(const float* fftPoints, const uint64_t numFFTDataPoints, const double* realTimeDomainPoints, const double* imagTimeDomainPoints, @@ -13,7 +13,7 @@ SpectrumUpdateEvent::SpectrumUpdateEvent(const std::complex<float>* fftPoints, const bool lastOfMultipleUpdateFlag, const gruel::high_res_timer_type generatedTimestamp, const int droppedFFTFrames) - : QEvent(QEvent::Type(10005)) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) { if(numFFTDataPoints < 1) { _numFFTDataPoints = 1; @@ -29,9 +29,9 @@ SpectrumUpdateEvent::SpectrumUpdateEvent(const std::complex<float>* fftPoints, _numTimeDomainDataPoints = numTimeDomainDataPoints; } - _fftPoints = new std::complex<float>[_numFFTDataPoints]; - _fftPoints[0] = std::complex<float>(0,0); - memcpy(_fftPoints, fftPoints, numFFTDataPoints*sizeof(std::complex<float>)); + _fftPoints = new float[_numFFTDataPoints]; + _fftPoints[0] = 0; + memcpy(_fftPoints, fftPoints, numFFTDataPoints*sizeof(float)); _realDataTimeDomainPoints = new double[_numTimeDomainDataPoints]; memset(_realDataTimeDomainPoints, 0x0, _numTimeDomainDataPoints*sizeof(double)); @@ -60,7 +60,7 @@ SpectrumUpdateEvent::~SpectrumUpdateEvent() delete[] _imagDataTimeDomainPoints; } -const std::complex<float>* +const float* SpectrumUpdateEvent::getFFTPoints() const { return _fftPoints; @@ -121,7 +121,7 @@ SpectrumUpdateEvent::getDroppedFFTFrames() const } SpectrumWindowCaptionEvent::SpectrumWindowCaptionEvent(const QString& newLbl) - : QEvent(QEvent::Type(10008)) + : QEvent(QEvent::Type(SpectrumWindowCaptionEventType)) { _labelString = newLbl; } @@ -137,7 +137,7 @@ SpectrumWindowCaptionEvent::getLabel() } SpectrumWindowResetEvent::SpectrumWindowResetEvent() - : QEvent(QEvent::Type(10009)) + : QEvent(QEvent::Type(SpectrumWindowResetEventType)) { } @@ -148,7 +148,7 @@ SpectrumWindowResetEvent::~SpectrumWindowResetEvent() SpectrumFrequencyRangeEvent::SpectrumFrequencyRangeEvent(const double centerFreq, const double startFreq, const double stopFreq) - : QEvent(QEvent::Type(10010)) + : QEvent(QEvent::Type(SpectrumFrequencyRangeEventType)) { _centerFrequency = centerFreq; _startFrequency = startFreq; @@ -179,10 +179,11 @@ SpectrumFrequencyRangeEvent::GetStopFrequency() const /***************************************************************************/ -#include <iostream> + + TimeUpdateEvent::TimeUpdateEvent(const std::vector<double*> timeDomainPoints, const uint64_t numTimeDomainDataPoints) - : QEvent(QEvent::Type(10005)) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) { if(numTimeDomainDataPoints < 1) { _numTimeDomainDataPoints = 1; @@ -220,4 +221,156 @@ TimeUpdateEvent::getNumTimeDomainDataPoints() const return _numTimeDomainDataPoints; } + +/***************************************************************************/ + + +FreqUpdateEvent::FreqUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) +{ + if(numDataPoints < 1) { + _numDataPoints = 1; + } + else { + _numDataPoints = numDataPoints; + } + + _nplots = dataPoints.size(); + for(size_t i = 0; i < _nplots; i++) { + _dataPoints.push_back(new double[_numDataPoints]); + if(numDataPoints > 0) { + memcpy(_dataPoints[i], dataPoints[i], + _numDataPoints*sizeof(double)); + } + } +} + +FreqUpdateEvent::~FreqUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + } +} + +const std::vector<double*> +FreqUpdateEvent::getPoints() const +{ + return _dataPoints; +} + +uint64_t +FreqUpdateEvent::getNumDataPoints() const +{ + return _numDataPoints; +} + + +/***************************************************************************/ + + +ConstUpdateEvent::ConstUpdateEvent(const std::vector<double*> realDataPoints, + const std::vector<double*> imagDataPoints, + const uint64_t numDataPoints) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) +{ + if(numDataPoints < 1) { + _numDataPoints = 1; + } + else { + _numDataPoints = numDataPoints; + } + + _nplots = realDataPoints.size(); + for(size_t i = 0; i < _nplots; i++) { + _realDataPoints.push_back(new double[_numDataPoints]); + _imagDataPoints.push_back(new double[_numDataPoints]); + if(numDataPoints > 0) { + memcpy(_realDataPoints[i], realDataPoints[i], + _numDataPoints*sizeof(double)); + memcpy(_imagDataPoints[i], imagDataPoints[i], + _numDataPoints*sizeof(double)); + } + } +} + +ConstUpdateEvent::~ConstUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _realDataPoints[i]; + delete[] _imagDataPoints[i]; + } +} + +const std::vector<double*> +ConstUpdateEvent::getRealPoints() const +{ + return _realDataPoints; +} + +const std::vector<double*> +ConstUpdateEvent::getImagPoints() const +{ + return _imagDataPoints; +} + +uint64_t +ConstUpdateEvent::getNumDataPoints() const +{ + return _numDataPoints; +} + + +/***************************************************************************/ + + +WaterfallUpdateEvent::WaterfallUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints, + const gruel::high_res_timer_type dataTimestamp) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) +{ + if(numDataPoints < 1) { + _numDataPoints = 1; + } + else { + _numDataPoints = numDataPoints; + } + + _nplots = dataPoints.size(); + for(size_t i = 0; i < _nplots; i++) { + _dataPoints.push_back(new double[_numDataPoints]); + if(numDataPoints > 0) { + memcpy(_dataPoints[i], dataPoints[i], + _numDataPoints*sizeof(double)); + } + } + + _dataTimestamp = dataTimestamp; +} + +WaterfallUpdateEvent::~WaterfallUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + } +} + +const std::vector<double*> +WaterfallUpdateEvent::getPoints() const +{ + return _dataPoints; +} + +uint64_t +WaterfallUpdateEvent::getNumDataPoints() const +{ + return _numDataPoints; +} + +gruel::high_res_timer_type +WaterfallUpdateEvent::getDataTimestamp() const +{ + return _dataTimestamp; +} + #endif /* SPECTRUM_UPDATE_EVENTS_C */ diff --git a/gr-qtgui/lib/spectrumUpdateEvents.h b/gr-qtgui/lib/spectrumUpdateEvents.h index faef0f0875..e663980bc6 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.h +++ b/gr-qtgui/lib/spectrumUpdateEvents.h @@ -8,10 +8,15 @@ #include <vector> #include <gruel/high_res_timer.h> +static const int SpectrumUpdateEventType = 10005; +static const int SpectrumWindowCaptionEventType = 10008; +static const int SpectrumWindowResetEventType = 10009; +static const int SpectrumFrequencyRangeEventType = 10010; + class SpectrumUpdateEvent:public QEvent{ public: - SpectrumUpdateEvent(const std::complex<float>* fftPoints, + SpectrumUpdateEvent(const float* fftPoints, const uint64_t numFFTDataPoints, const double* realTimeDomainPoints, const double* imagTimeDomainPoints, @@ -24,7 +29,7 @@ public: ~SpectrumUpdateEvent(); - const std::complex<float>* getFFTPoints() const; + const float* getFFTPoints() const; const double* getRealTimeDomainPoints() const; const double* getImagTimeDomainPoints() const; uint64_t getNumFFTDataPoints() const; @@ -38,7 +43,7 @@ public: protected: private: - std::complex<float>* _fftPoints; + float* _fftPoints; double* _realDataTimeDomainPoints; double* _imagDataTimeDomainPoints; uint64_t _numFFTDataPoints; @@ -103,6 +108,9 @@ public: uint64_t getNumTimeDomainDataPoints() const; bool getRepeatDataFlag() const; + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + protected: private: @@ -112,4 +120,96 @@ private: }; +/********************************************************************/ + + +class FreqUpdateEvent: public QEvent +{ +public: + FreqUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints); + + ~FreqUpdateEvent(); + + int which() const; + const std::vector<double*> getPoints() const; + uint64_t getNumDataPoints() const; + bool getRepeatDataFlag() const; + + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + +protected: + +private: + size_t _nplots; + std::vector<double*> _dataPoints; + uint64_t _numDataPoints; +}; + + +/********************************************************************/ + + +class ConstUpdateEvent: public QEvent +{ +public: + ConstUpdateEvent(const std::vector<double*> realDataPoints, + const std::vector<double*> imagDataPoints, + const uint64_t numDataPoints); + + ~ConstUpdateEvent(); + + int which() const; + const std::vector<double*> getRealPoints() const; + const std::vector<double*> getImagPoints() const; + uint64_t getNumDataPoints() const; + bool getRepeatDataFlag() const; + + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + +protected: + +private: + size_t _nplots; + std::vector<double*> _realDataPoints; + std::vector<double*> _imagDataPoints; + uint64_t _numDataPoints; +}; + + +/********************************************************************/ + + +class WaterfallUpdateEvent: public QEvent +{ +public: + WaterfallUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints, + const gruel::high_res_timer_type dataTimestamp); + + ~WaterfallUpdateEvent(); + + int which() const; + const std::vector<double*> getPoints() const; + uint64_t getNumDataPoints() const; + bool getRepeatDataFlag() const; + + gruel::high_res_timer_type getDataTimestamp() const; + + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + +protected: + +private: + size_t _nplots; + std::vector<double*> _dataPoints; + uint64_t _numDataPoints; + + gruel::high_res_timer_type _dataTimestamp; +}; + + #endif /* SPECTRUM_UPDATE_EVENTS_H */ diff --git a/gr-qtgui/lib/spectrumdisplayform.cc b/gr-qtgui/lib/spectrumdisplayform.cc index dd9011dbdd..6de6caa1b1 100644 --- a/gr-qtgui/lib/spectrumdisplayform.cc +++ b/gr-qtgui/lib/spectrumdisplayform.cc @@ -24,6 +24,7 @@ #include <QColorDialog> #include <QMessageBox> #include <spectrumdisplayform.h> +#include "qtgui_types.h" SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) : QWidget(parent) @@ -33,10 +34,10 @@ SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) _systemSpecifiedFlag = false; _intValidator = new QIntValidator(this); _intValidator->setBottom(0); - _frequencyDisplayPlot = new FrequencyDisplayPlot(FrequencyPlotDisplayFrame); - _waterfallDisplayPlot = new WaterfallDisplayPlot(WaterfallPlotDisplayFrame); + _frequencyDisplayPlot = new FrequencyDisplayPlot(1, FrequencyPlotDisplayFrame); + _waterfallDisplayPlot = new WaterfallDisplayPlot(1, WaterfallPlotDisplayFrame); _timeDomainDisplayPlot = new TimeDomainDisplayPlot(2, TimeDomainDisplayFrame); - _constellationDisplayPlot = new ConstellationDisplayPlot(ConstellationDisplayFrame); + _constellationDisplayPlot = new ConstellationDisplayPlot(1, ConstellationDisplayFrame); _numRealDataPoints = 1024; _realFFTDataPoints = new double[_numRealDataPoints]; _averagedValues = new double[_numRealDataPoints]; @@ -48,13 +49,11 @@ SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) AvgLineEdit->setRange(0, 500); // Set range of Average box value from 0 to 500 MinHoldCheckBox_toggled( false ); MaxHoldCheckBox_toggled( false ); - - WaterfallMaximumIntensityWheel->setRange(-200, 0); - WaterfallMaximumIntensityWheel->setTickCnt(50); - WaterfallMinimumIntensityWheel->setRange(-200, 0); - WaterfallMinimumIntensityWheel->setTickCnt(50); - WaterfallMinimumIntensityWheel->setValue(-200); - + + WaterfallMaximumIntensitySlider->setRange(-200, 0); + WaterfallMinimumIntensitySlider->setRange(-200, 0); + WaterfallMinimumIntensitySlider->setValue(-200); + _peakFrequency = 0; _peakAmplitude = -HUGE_VAL; @@ -137,11 +136,57 @@ SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem, } } +/*********************************************************************** + * This is kind of gross because we're combining three operations: + * Conversion from float to double (which is what the plotter wants) + * Finding the peak and mean + * Doing the "FFT shift" to put 0Hz at the center of the plot + * I feel like this might want to be part of the sink block + **********************************************************************/ +static void fftshift_and_sum(double *outFFT, const float *inFFT, uint64_t num_points, double &sum_mean, double &peak_ampl, int &peak_bin) { + const float* inptr = inFFT+num_points/2; + double* outptr = outFFT; + + sum_mean = 0; + peak_ampl = -HUGE_VAL; + peak_bin = 0; + + // Run this twice to perform the fftshift operation on the data here as well + for(uint64_t point = 0; point < num_points/2; point++){ + float pt = (*inptr); + *outptr = pt; + if(*outptr > peak_ampl) { + peak_bin = point; + peak_ampl = *outptr; + } + sum_mean += *outptr; + + inptr++; + outptr++; + } + + // This loop takes the first half of the input data and puts it in the + // second half of the plotted data + inptr = inFFT; + for(uint64_t point = 0; point < num_points/2; point++){ + float pt = (*inptr); + *outptr = pt; + if(*outptr > peak_ampl) { + peak_bin = point; + peak_ampl = *outptr; + } + sum_mean += *outptr; + + inptr++; + outptr++; + } +} + void SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdateEvent) { //_lastSpectrumEvent = (SpectrumUpdateEvent)(*spectrumUpdateEvent); - const std::complex<float>* complexDataPoints = spectrumUpdateEvent->getFFTPoints(); + const float* fftMagDataPoints = spectrumUpdateEvent->getFFTPoints(); const uint64_t numFFTDataPoints = spectrumUpdateEvent->getNumFFTDataPoints(); const uint64_t numTimeDomainDataPoints = spectrumUpdateEvent->getNumTimeDomainDataPoints(); const gruel::high_res_timer_type dataTimestamp = spectrumUpdateEvent->getDataTimestamp(); @@ -158,68 +203,34 @@ SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdate // REMEMBER: The dataTimestamp is NOT valid when the repeat data flag is true... ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); - // Calculate the Magnitude of the complex point - const std::complex<float>* complexDataPointsPtr = complexDataPoints+numFFTDataPoints/2; - double* realFFTDataPointsPtr = _realFFTDataPoints; - - double sumMean = 0.0; - double localPeakAmplitude = -HUGE_VAL; - double localPeakFrequency = 0.0; const double fftBinSize = (_stopFrequency-_startFrequency) / static_cast<double>(numFFTDataPoints); - // Run this twice to perform the fftshift operation on the data here as well - std::complex<float> scaleFactor = std::complex<float>((float)numFFTDataPoints); - for(uint64_t point = 0; point < numFFTDataPoints/2; point++){ - std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor; - *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20); - - if(*realFFTDataPointsPtr > localPeakAmplitude) { - localPeakFrequency = static_cast<float>(point) * fftBinSize; - localPeakAmplitude = *realFFTDataPointsPtr; - } - sumMean += *realFFTDataPointsPtr; - - complexDataPointsPtr++; - realFFTDataPointsPtr++; - } - - // This loop takes the first half of the input data and puts it in the - // second half of the plotted data - complexDataPointsPtr = complexDataPoints; - for(uint64_t point = 0; point < numFFTDataPoints/2; point++){ - std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor; - *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20); - - if(*realFFTDataPointsPtr > localPeakAmplitude) { - localPeakFrequency = static_cast<float>(point) * fftBinSize; - localPeakAmplitude = *realFFTDataPointsPtr; - } - sumMean += *realFFTDataPointsPtr; - - complexDataPointsPtr++; - realFFTDataPointsPtr++; - } + //this does the fftshift, conversion to double, and calculation of sum, peak amplitude, peak freq. + double sum_mean, peak_ampl; + int peak_bin; + fftshift_and_sum(_realFFTDataPoints, fftMagDataPoints, numFFTDataPoints, sum_mean, peak_ampl, peak_bin); + double peak_freq = peak_bin * fftBinSize; // Don't update the averaging history if this is repeated data if(!repeatDataFlag){ _AverageHistory(_realFFTDataPoints); // Only use the local info if we are not repeating data - _peakAmplitude = localPeakAmplitude; - _peakFrequency = localPeakFrequency; + _peakAmplitude = peak_ampl; + _peakFrequency = peak_freq; // calculate the spectral mean // +20 because for the comparison below we only want to throw out bins // that are significantly higher (and would, thus, affect the mean more) - const double meanAmplitude = (sumMean / numFFTDataPoints) + 20.0; + const double meanAmplitude = (sum_mean / numFFTDataPoints) + 20.0; // now throw out any bins higher than the mean - sumMean = 0.0; + sum_mean = 0.0; uint64_t newNumDataPoints = numFFTDataPoints; for(uint64_t number = 0; number < numFFTDataPoints; number++){ if (_realFFTDataPoints[number] <= meanAmplitude) - sumMean += _realFFTDataPoints[number]; + sum_mean += _realFFTDataPoints[number]; else newNumDataPoints--; } @@ -227,7 +238,7 @@ SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdate if (newNumDataPoints == 0) // in the odd case that all _noiseFloorAmplitude = meanAmplitude; // amplitudes are equal! else - _noiseFloorAmplitude = sumMean / newNumDataPoints; + _noiseFloorAmplitude = sum_mean / newNumDataPoints; } if(lastOfMultipleUpdatesFlag){ @@ -296,26 +307,26 @@ SpectrumDisplayForm::customEvent( QEvent * e) FFTSizeComboBox->setCurrentIndex(_system->GetFFTSizeIndex()); } - waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensityWheel->value()); - waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensityWheel->value()); + waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensitySlider->value()); + waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensitySlider->value()); // Clear any previous display Reset(); } - else if(e->type() == 10005){ + else if(e->type() == SpectrumUpdateEventType){ SpectrumUpdateEvent* spectrumUpdateEvent = (SpectrumUpdateEvent*)e; newFrequencyData(spectrumUpdateEvent); } - else if(e->type() == 10008){ + else if(e->type() == SpectrumWindowCaptionEventType){ setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel()); } - else if(e->type() == 10009){ + else if(e->type() == SpectrumWindowResetEventType){ Reset(); if(_systemSpecifiedFlag){ _system->ResetPendingGUIUpdateEvents(); } } - else if(e->type() == 10010){ + else if(e->type() == SpectrumFrequencyRangeEventType){ _startFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStartFrequency(); _stopFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStopFrequency(); _centerFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetCenterFrequency(); @@ -535,7 +546,7 @@ SpectrumDisplayForm::closeEvent( QCloseEvent *e ) qApp->processEvents(); - QWidget::closeEvent(e); + QWidget::closeEvent(e); //equivalent to e->accept() } @@ -558,30 +569,30 @@ SpectrumDisplayForm::UseRFFrequenciesCB( bool useRFFlag ) void SpectrumDisplayForm::waterfallMaximumIntensityChangedCB( double newValue ) { - if(newValue > WaterfallMinimumIntensityWheel->value()){ + if(newValue > WaterfallMinimumIntensitySlider->value()){ WaterfallMaximumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); } else{ - WaterfallMaximumIntensityWheel->setValue(WaterfallMinimumIntensityWheel->value()); + WaterfallMinimumIntensitySlider->setValue(newValue - 2); } - _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(), - WaterfallMaximumIntensityWheel->value()); + _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensitySlider->value(), + WaterfallMaximumIntensitySlider->value()); } void SpectrumDisplayForm::waterfallMinimumIntensityChangedCB( double newValue ) { - if(newValue < WaterfallMaximumIntensityWheel->value()){ + if(newValue < WaterfallMaximumIntensitySlider->value()){ WaterfallMinimumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); } else{ - WaterfallMinimumIntensityWheel->setValue(WaterfallMaximumIntensityWheel->value()); + WaterfallMaximumIntensitySlider->setValue(newValue + 2); } - _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(), - WaterfallMaximumIntensityWheel->value()); + _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensitySlider->value(), + WaterfallMaximumIntensitySlider->value()); } void @@ -597,15 +608,15 @@ void SpectrumDisplayForm::WaterfallAutoScaleBtnCB() { double minimumIntensity = _noiseFloorAmplitude - 5; - if(minimumIntensity < WaterfallMinimumIntensityWheel->minValue()){ - minimumIntensity = WaterfallMinimumIntensityWheel->minValue(); + if(minimumIntensity < WaterfallMinimumIntensitySlider->minValue()){ + minimumIntensity = WaterfallMinimumIntensitySlider->minValue(); } - WaterfallMinimumIntensityWheel->setValue(minimumIntensity); + WaterfallMinimumIntensitySlider->setValue(minimumIntensity); double maximumIntensity = _peakAmplitude + 10; - if(maximumIntensity > WaterfallMaximumIntensityWheel->maxValue()){ - maximumIntensity = WaterfallMaximumIntensityWheel->maxValue(); + if(maximumIntensity > WaterfallMaximumIntensitySlider->maxValue()){ + maximumIntensity = WaterfallMaximumIntensitySlider->maxValue(); } - WaterfallMaximumIntensityWheel->setValue(maximumIntensity); + WaterfallMaximumIntensitySlider->setValue(maximumIntensity); waterfallMaximumIntensityChangedCB(maximumIntensity); } @@ -614,7 +625,7 @@ SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) { QColor lowIntensityColor; QColor highIntensityColor; - if(newType == WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED){ + if(newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED){ // Select the Low Intensity Color lowIntensityColor = _waterfallDisplayPlot->GetUserDefinedLowIntensityColor(); if(!lowIntensityColor.isValid()){ @@ -632,7 +643,7 @@ SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) highIntensityColor = QColorDialog::getColor(highIntensityColor, this); } - _waterfallDisplayPlot->SetIntensityColorMapType(newType, lowIntensityColor, highIntensityColor); + _waterfallDisplayPlot->SetIntensityColorMapType(0, newType, lowIntensityColor, highIntensityColor); } void diff --git a/gr-qtgui/lib/spectrumdisplayform.ui b/gr-qtgui/lib/spectrumdisplayform.ui index 049d4ffeb4..9b06f44f0d 100644 --- a/gr-qtgui/lib/spectrumdisplayform.ui +++ b/gr-qtgui/lib/spectrumdisplayform.ui @@ -177,13 +177,13 @@ <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> - <verstretch>0</verstretch> + <verstretch>1</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> - <width>617</width> - <height>400</height> + <width>320</width> + <height>200</height> </size> </property> <property name="sizeIncrement"> @@ -283,7 +283,7 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>200</width> + <width>100</width> <height>20</height> </size> </property> @@ -304,7 +304,7 @@ <widget class="QLabel" name="textLabel1"> <property name="maximumSize"> <size> - <width>100</width> + <width>130</width> <height>16777215</height> </size> </property> @@ -317,10 +317,10 @@ </widget> </item> <item row="0" column="2"> - <widget class="QwtWheel" name="WaterfallMaximumIntensityWheel"> + <widget class="QwtSlider" name="WaterfallMaximumIntensitySlider"> <property name="minimumSize"> <size> - <width>200</width> + <width>50</width> <height>0</height> </size> </property> @@ -328,20 +328,11 @@ <bool>true</bool> </property> <property name="focusPolicy"> - <enum>Qt::WheelFocus</enum> + <enum>Qt::ClickFocus</enum> </property> <property name="valid"> <bool>true</bool> </property> - <property name="totalAngle"> - <double>200.000000000000000</double> - </property> - <property name="viewAngle"> - <double>20.000000000000000</double> - </property> - <property name="mass"> - <double>0.000000000000000</double> - </property> </widget> </item> <item row="0" column="3"> @@ -364,8 +355,8 @@ <widget class="QFrame" name="WaterfallPlotDisplayFrame"> <property name="minimumSize"> <size> - <width>617</width> - <height>338</height> + <width>320</width> + <height>200</height> </size> </property> <property name="frameShape"> @@ -377,24 +368,21 @@ </widget> </item> <item row="2" column="2"> - <widget class="QwtWheel" name="WaterfallMinimumIntensityWheel"> + <widget class="QwtSlider" name="WaterfallMinimumIntensitySlider"> <property name="minimumSize"> <size> - <width>200</width> + <width>50</width> <height>0</height> </size> </property> <property name="valid"> <bool>true</bool> </property> - <property name="totalAngle"> - <double>200.000000000000000</double> - </property> - <property name="viewAngle"> - <double>20.000000000000000</double> + <property name="mouseTracking"> + <bool>true</bool> </property> - <property name="mass"> - <double>0.000000000000000</double> + <property name="focusPolicy"> + <enum>Qt::ClickFocus</enum> </property> </widget> </item> @@ -476,8 +464,8 @@ <widget class="QFrame" name="TimeDomainDisplayFrame"> <property name="minimumSize"> <size> - <width>617</width> - <height>404</height> + <width>320</width> + <height>200</height> </size> </property> <property name="frameShape"> @@ -499,8 +487,8 @@ <widget class="QFrame" name="ConstellationDisplayFrame"> <property name="minimumSize"> <size> - <width>617</width> - <height>406</height> + <width>320</width> + <height>200</height> </size> </property> <property name="frameShape"> @@ -520,18 +508,18 @@ <layoutdefault spacing="6" margin="11"/> <pixmapfunction>qPixmapFromMimeSource</pixmapfunction> <customwidgets> - <customwidget> - <class>QwtWheel</class> + <customwidget> + <class>QwtSlider</class> <extends>QWidget</extends> - <header>qwt_wheel.h</header> - </customwidget> + <header>qwt_slider.h</header> + </customwidget> </customwidgets> <tabstops> <tabstop>SpectrumTypeTab</tabstop> <tabstop>UseRFFrequenciesCheckBox</tabstop> <tabstop>FFTSizeComboBox</tabstop> - <tabstop>WaterfallMaximumIntensityWheel</tabstop> - <tabstop>WaterfallMinimumIntensityWheel</tabstop> + <tabstop>WaterfallMaximumIntensitySlider</tabstop> + <tabstop>WaterfallMinimumIntensitySlider</tabstop> </tabstops> <includes> <include location="global">SpectrumGUIClass.h</include> @@ -540,7 +528,7 @@ <include location="global">TimeDomainDisplayPlot.h</include> <include location="global">qvalidator.h</include> <include location="global">vector</include> - <include location="local">qwt_wheel.h</include> + <include location="local">qwt_slider.h</include> </includes> <resources/> <connections> @@ -641,7 +629,7 @@ </hints> </connection> <connection> - <sender>WaterfallMaximumIntensityWheel</sender> + <sender>WaterfallMaximumIntensitySlider</sender> <signal>valueChanged(double)</signal> <receiver>SpectrumDisplayForm</receiver> <slot>waterfallMaximumIntensityChangedCB(double)</slot> @@ -657,7 +645,7 @@ </hints> </connection> <connection> - <sender>WaterfallMinimumIntensityWheel</sender> + <sender>WaterfallMinimumIntensitySlider</sender> <signal>valueChanged(double)</signal> <receiver>SpectrumDisplayForm</receiver> <slot>waterfallMinimumIntensityChangedCB(double)</slot> diff --git a/gr-qtgui/lib/time_sink_c_impl.cc b/gr-qtgui/lib/time_sink_c_impl.cc new file mode 100644 index 0000000000..83ab76eb6e --- /dev/null +++ b/gr-qtgui/lib/time_sink_c_impl.cc @@ -0,0 +1,256 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> +#include <fft/fft.h> + +namespace gr { + namespace qtgui { + + time_sink_c::sptr + time_sink_c::make(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_sink_c_impl(size, bw, name, nconnections, parent)); + } + + time_sink_c_impl::time_sink_c_impl(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_sink_c", + gr_make_io_signature(nconnections, nconnections, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_size(size), d_bandwidth(bw), d_name(name), + d_nconnections(2*nconnections), d_parent(parent) + { + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_size)); + memset(d_residbufs[i], 0, d_size*sizeof(double)); + } + + // Set alignment properties for VOLK + const int alignment_multiple = + volk_get_alignment() / sizeof(gr_complex); + set_alignment(std::max(1,alignment_multiple)); + + initialize(); + } + + time_sink_c_impl::~time_sink_c_impl() + { + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + void + time_sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); + d_main_gui->SetNPoints(d_size); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_sink_c_impl::set_time_domain_axis(double min, double max) + { + d_main_gui->setTimeDomainAxis(min, max); + } + + void + time_sink_c_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + time_sink_c_impl::set_title(int which, const std::string &title) + { + d_main_gui->setTitle(which, title.c_str()); + } + + void + time_sink_c_impl::set_color(int which, const std::string &color) + { + d_main_gui->setColor(which, color.c_str()); + } + + void + time_sink_c_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + time_sink_c_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + time_sink_c_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + time_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_sink_c_impl::set_nsamps(const int newsize) + { + gruel::scoped_lock lock(d_mutex); + + if(newsize != d_size) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(newsize); + + memset(d_residbufs[i], 0, newsize*sizeof(double)); + } + + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) + d_size = newsize; + d_index = 0; + + d_main_gui->SetNPoints(d_size); + } + } + + void + time_sink_c_impl::npoints_resize() + { + int newsize = d_main_gui->GetNPoints(); + set_nsamps(newsize); + } + + int + time_sink_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const gr_complex *in = (const gr_complex*)input_items[idx]; + + npoints_resize(); + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n+=2) { + in = (const gr_complex*)input_items[idx++]; + volk_32fc_deinterleave_64f_x2_u(&d_residbufs[n][d_index], + &d_residbufs[n+1][d_index], + &in[j], resid); + } + + // Update the plot if its time + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new TimeUpdateEvent(d_residbufs, d_size)); + } + + d_index = 0; + j += resid; + } + + // Otherwise, copy what we received into the residbufs for next time + else { + for(n = 0; n < d_nconnections; n+=2) { + in = (const gr_complex*)input_items[idx++]; + volk_32fc_deinterleave_64f_x2_u(&d_residbufs[n][d_index], + &d_residbufs[n+1][d_index], + &in[j], datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_sink_c_impl.h b/gr-qtgui/lib/time_sink_c_impl.h new file mode 100644 index 0000000000..0cace14890 --- /dev/null +++ b/gr-qtgui/lib/time_sink_c_impl.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_C_IMPL_H +#define INCLUDED_QTGUI_TIME_SINK_C_IMPL_H + +#include <qtgui/time_sink_c.h> +#include <timedisplayform.h> +#include <gruel/thread.h> +#include <gruel/high_res_timer.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_sink_c_impl : public time_sink_c + { + private: + void initialize(); + + gruel::mutex d_mutex; + + int d_size; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + QWidget *d_parent; + TimeDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void npoints_resize(); + + public: + time_sink_c_impl(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_time_domain_axis(double min, double max); + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, Qt::PenStyle style); + void set_line_marker(int which, QwtSymbol::Style marker); + void set_nsamps(const int size); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/time_sink_f_impl.cc b/gr-qtgui/lib/time_sink_f_impl.cc new file mode 100644 index 0000000000..4b965b648c --- /dev/null +++ b/gr-qtgui/lib/time_sink_f_impl.cc @@ -0,0 +1,254 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> +#include <fft/fft.h> + +namespace gr { + namespace qtgui { + + time_sink_f::sptr + time_sink_f::make(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_sink_f_impl(size, bw, name, nconnections, parent)); + } + + time_sink_f_impl::time_sink_f_impl(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_sink_f", + gr_make_io_signature(nconnections, nconnections, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_size(size), d_bandwidth(bw), d_name(name), + d_nconnections(nconnections), d_parent(parent) + { + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_size)); + memset(d_residbufs[i], 0, d_size*sizeof(double)); + } + + // Set alignment properties for VOLK + const int alignment_multiple = + volk_get_alignment() / sizeof(gr_complex); + set_alignment(std::max(1,alignment_multiple)); + + initialize(); + } + + time_sink_f_impl::~time_sink_f_impl() + { + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + void + time_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); + d_main_gui->SetNPoints(d_size); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_sink_f_impl::set_time_domain_axis(double min, double max) + { + d_main_gui->setTimeDomainAxis(min, max); + } + + void + time_sink_f_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + time_sink_f_impl::set_title(int which, const std::string &title) + { + d_main_gui->setTitle(which, title.c_str()); + } + + void + time_sink_f_impl::set_color(int which, const std::string &color) + { + d_main_gui->setColor(which, color.c_str()); + } + + void + time_sink_f_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + time_sink_f_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + time_sink_f_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + time_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_sink_f_impl::set_nsamps(const int newsize) + { + gruel::scoped_lock lock(d_mutex); + + if(newsize != d_size) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(newsize); + + memset(d_residbufs[i], 0, newsize*sizeof(double)); + } + + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) + d_size = newsize; + d_index = 0; + + d_main_gui->SetNPoints(d_size); + } + } + + void + time_sink_f_impl::npoints_resize() + { + int newsize = d_main_gui->GetNPoints(); + set_nsamps(newsize); + } + + int + time_sink_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const float *in = (const float*)input_items[idx]; + + npoints_resize(); + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + &in[j], resid); + } + + // Update the plot if its time + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new TimeUpdateEvent(d_residbufs, d_size)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + // because we set the output_multiple, this should never need to be called + else { + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + &in[j], datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_sink_f_impl.h b/gr-qtgui/lib/time_sink_f_impl.h new file mode 100644 index 0000000000..69c07fbdab --- /dev/null +++ b/gr-qtgui/lib/time_sink_f_impl.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011,2012 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. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_F_IMPL_H +#define INCLUDED_QTGUI_TIME_SINK_F_IMPL_H + +#include <qtgui/time_sink_f.h> +#include <timedisplayform.h> +#include <gruel/thread.h> +#include <gruel/high_res_timer.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_sink_f_impl : public time_sink_f + { + private: + void initialize(); + + gruel::mutex d_mutex; + + int d_size; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + QWidget *d_parent; + TimeDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void npoints_resize(); + + public: + time_sink_f_impl(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_sink_f_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_time_domain_axis(double min, double max); + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, Qt::PenStyle style); + void set_line_marker(int which, QwtSymbol::Style marker); + void set_nsamps(const int newsize); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/timedisplayform.cc b/gr-qtgui/lib/timedisplayform.cc index c650cd3eb0..3d94d20094 100644 --- a/gr-qtgui/lib/timedisplayform.cc +++ b/gr-qtgui/lib/timedisplayform.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2011 Free Software Foundation, Inc. + * Copyright 2011,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -21,34 +21,30 @@ */ #include <cmath> -#include <QColorDialog> #include <QMessageBox> #include <timedisplayform.h> #include <iostream> TimeDisplayForm::TimeDisplayForm(int nplots, QWidget* parent) - : QWidget(parent) + : DisplayForm(nplots, parent) { - _systemSpecifiedFlag = false; _intValidator = new QIntValidator(this); _intValidator->setBottom(0); _layout = new QGridLayout(this); - _timeDomainDisplayPlot = new TimeDomainDisplayPlot(nplots, this); - _layout->addWidget(_timeDomainDisplayPlot, 0, 0); - - _numRealDataPoints = 1024; - + _displayPlot = new TimeDomainDisplayPlot(nplots, this); + _layout->addWidget(_displayPlot, 0, 0); setLayout(_layout); - Reset(); + NPointsMenu *nptsmenu = new NPointsMenu(this); + _menu->addAction(nptsmenu); + connect(nptsmenu, SIGNAL(whichTrigger(int)), + this, SLOT(SetNPoints(const int))); - // Create a timer to update plots at the specified rate - displayTimer = new QTimer(this); - connect(displayTimer, SIGNAL(timeout()), this, SLOT(updateGuiTimer())); + Reset(); - connect(_timeDomainDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), - this, SLOT(onTimePlotPointSelected(const QPointF))); + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); } TimeDisplayForm::~TimeDisplayForm() @@ -57,57 +53,32 @@ TimeDisplayForm::~TimeDisplayForm() // Don't worry about deleting Display Plots - they are deleted when parents are deleted delete _intValidator; - - displayTimer->stop(); - delete displayTimer; } -void -TimeDisplayForm::newData( const TimeUpdateEvent* spectrumUpdateEvent) +TimeDomainDisplayPlot* +TimeDisplayForm::getPlot() { - const std::vector<double*> timeDomainDataPoints = spectrumUpdateEvent->getTimeDomainPoints(); - const uint64_t numTimeDomainDataPoints = spectrumUpdateEvent->getNumTimeDomainDataPoints(); - - _timeDomainDisplayPlot->PlotNewData(timeDomainDataPoints, - numTimeDomainDataPoints, - d_update_time); + return ((TimeDomainDisplayPlot*)_displayPlot); } void -TimeDisplayForm::resizeEvent( QResizeEvent *e ) +TimeDisplayForm::newData(const QEvent* updateEvent) { - QSize s = size(); - emit _timeDomainDisplayPlot->resizeSlot(&s); -} + TimeUpdateEvent *tevent = (TimeUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = tevent->getTimeDomainPoints(); + const uint64_t numDataPoints = tevent->getNumTimeDomainDataPoints(); -void -TimeDisplayForm::customEvent( QEvent * e) -{ - if(e->type() == 10005) { - TimeUpdateEvent* timeUpdateEvent = (TimeUpdateEvent*)e; - newData(timeUpdateEvent); - } - //else if(e->type() == 10008){ - //setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel()); - //} - //else if(e->type() == 10009){ - //Reset(); - //if(_systemSpecifiedFlag){ - // _system->ResetPendingGUIUpdateEvents(); - //} - //} -} - -void -TimeDisplayForm::updateGuiTimer() -{ - _timeDomainDisplayPlot->canvas()->update(); + getPlot()->PlotNewData(dataPoints, + numDataPoints, + d_update_time); } void -TimeDisplayForm::onTimePlotPointSelected(const QPointF p) +TimeDisplayForm::customEvent(QEvent * e) { - emit plotPointSelected(p, 3); + if(e->type() == TimeUpdateEvent::Type()) { + newData(e); + } } void @@ -126,52 +97,26 @@ TimeDisplayForm::setFrequencyRange(const double newCenterFrequency, _startFrequency = newStartFrequency; _stopFrequency = newStopFrequency; - - _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency, - units, strtime[iunit]); + + getPlot()->SetSampleRate(_stopFrequency - _startFrequency, + units, strtime[iunit]); } } void -TimeDisplayForm::Reset() -{ -} - - -void -TimeDisplayForm::closeEvent( QCloseEvent *e ) -{ - //if(_systemSpecifiedFlag){ - // _system->SetWindowOpenFlag(false); - //} - - qApp->processEvents(); - - QWidget::closeEvent(e); -} - -void TimeDisplayForm::setTimeDomainAxis(double min, double max) { - _timeDomainDisplayPlot->setYaxis(min, max); -} - -void -TimeDisplayForm::setUpdateTime(double t) -{ - d_update_time = t; - // QTimer class takes millisecond input - displayTimer->start(d_update_time*1000); + getPlot()->setYaxis(min, max); } -void -TimeDisplayForm::setTitle(int which, QString title) +int +TimeDisplayForm::GetNPoints() const { - _timeDomainDisplayPlot->setTitle(which, title); + return d_npoints; } void -TimeDisplayForm::setColor(int which, QString color) +TimeDisplayForm::SetNPoints(const int npoints) { - _timeDomainDisplayPlot->setColor(which, color); + d_npoints = npoints; } diff --git a/gr-qtgui/lib/timedisplayform.h b/gr-qtgui/lib/timedisplayform.h index dd3f62a83f..806fc4053f 100644 --- a/gr-qtgui/lib/timedisplayform.h +++ b/gr-qtgui/lib/timedisplayform.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2011 Free Software Foundation, Inc. + * Copyright 2011,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -24,17 +24,13 @@ #define TIME_DISPLAY_FORM_H #include <spectrumUpdateEvents.h> -#include <FrequencyDisplayPlot.h> -#include <WaterfallDisplayPlot.h> #include <TimeDomainDisplayPlot.h> -#include <ConstellationDisplayPlot.h> -#include <QtGui/QApplication> -#include <QtGui/QGridLayout> -#include <QValidator> -#include <QTimer> +#include <QtGui/QtGui> #include <vector> -class TimeDisplayForm : public QWidget +#include "displayform.h" + +class TimeDisplayForm : public DisplayForm { Q_OBJECT @@ -42,44 +38,29 @@ class TimeDisplayForm : public QWidget TimeDisplayForm(int nplots=1, QWidget* parent = 0); ~TimeDisplayForm(); - void Reset(); + TimeDomainDisplayPlot* getPlot(); + + int GetNPoints() const; public slots: - void resizeEvent( QResizeEvent * e ); - void customEvent( QEvent * e ); - void setFrequencyRange( const double newCenterFrequency, - const double newStartFrequency, - const double newStopFrequency ); - void closeEvent( QCloseEvent * e ); + void customEvent(QEvent * e); + void setFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency); void setTimeDomainAxis(double min, double max); - - void setUpdateTime(double t); - - void setTitle(int which, QString title); - void setColor(int which, QString color); + void SetNPoints(const int); private slots: - void newData( const TimeUpdateEvent* ); - void updateGuiTimer(); - - void onTimePlotPointSelected(const QPointF p); - -signals: - void plotPointSelected(const QPointF p, int type); + void newData(const QEvent*); private: - uint64_t _numRealDataPoints; QIntValidator* _intValidator; - QGridLayout *_layout; - TimeDomainDisplayPlot* _timeDomainDisplayPlot; - bool _systemSpecifiedFlag; double _startFrequency; double _stopFrequency; - QTimer *displayTimer; - double d_update_time; + int d_npoints; }; #endif /* TIME_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/waterfall_sink_c_impl.cc b/gr-qtgui/lib/waterfall_sink_c_impl.cc new file mode 100644 index 0000000000..0611d37974 --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_c_impl.cc @@ -0,0 +1,366 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "waterfall_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + waterfall_sink_c::sptr + waterfall_sink_c::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new waterfall_sink_c_impl(fftsize, wintype, + fc, bw, name, + parent)); + } + + waterfall_sink_c_impl::waterfall_sink_c_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent) + : gr_sync_block("waterfall_sink_c", + gr_make_io_signature(1, -1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_fftsize(fftsize), d_fftavg(1.0), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_nconnections(1), d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex(d_fftsize, true); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_complex(d_fftsize)); + d_magbufs.push_back(fft::malloc_double(d_fftsize)); + memset(d_residbufs[i], 0, d_fftsize*sizeof(float)); + memset(d_magbufs[i], 0, d_fftsize*sizeof(double)); + } + + buildwindow(); + + initialize(); + } + + waterfall_sink_c_impl::~waterfall_sink_c_impl() + { + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + } + delete d_fft; + fft::free(d_fbuf); + } + + void + waterfall_sink_c_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + waterfall_sink_c_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new WaterfallDisplayForm(d_nconnections, d_parent); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetFFTWindowType(d_wintype); + d_main_gui->SetFrequencyRange(d_center_freq, + d_center_freq - d_bandwidth/2.0, + d_center_freq + d_bandwidth/2.0); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + waterfall_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + waterfall_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + waterfall_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + waterfall_sink_c_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + waterfall_sink_c_impl::fft_size() const + { + return d_fftsize; + } + + void + waterfall_sink_c_impl::set_fft_average(const float fftavg) + { + d_fftavg = fftavg; + d_main_gui->SetFFTAverage(fftavg); + } + + float + waterfall_sink_c_impl::fft_average() const + { + return d_fftavg; + } + + void + waterfall_sink_c_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + waterfall_sink_c_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + waterfall_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(0, title.c_str()); + } + + void + waterfall_sink_c_impl::set_color(const std::string &color) + { + d_main_gui->setColor(0, color.c_str()); + } + + void + waterfall_sink_c_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + waterfall_sink_c_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + waterfall_sink_c_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + waterfall_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + waterfall_sink_c_impl::fft(float *data_out, const gr_complex *data_in, int size) + { + if(d_window.size()) { + volk_32fc_32f_multiply_32fc_a(d_fft->get_inbuf(), data_in, &d_window.front(), size); + } + else { + memcpy(d_fft->get_inbuf(), data_in, sizeof(gr_complex)*size); + } + + d_fft->execute(); // compute the fft + + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); + + // Perform shift operation + unsigned int len = (unsigned int)(floor(size/2.0)); + float *tmp = (float*)malloc(sizeof(float)*len); + memcpy(tmp, &data_out[0], sizeof(float)*len); + memcpy(&data_out[0], &data_out[len], sizeof(float)*(size - len)); + memcpy(&data_out[size - len], tmp, sizeof(float)*len); + free(tmp); + } + + void + waterfall_sink_c_impl::windowreset() + { + filter::firdes::win_type newwintype; + newwintype = d_main_gui->GetFFTWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + waterfall_sink_c_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != filter::firdes::WIN_NONE) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + waterfall_sink_c_impl::fftresize() + { + int newfftsize = d_main_gui->GetFFTSize(); + d_fftavg = d_main_gui->GetFFTAverage(); + + if(newfftsize != d_fftsize) { + + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + + d_residbufs[i] = fft::malloc_complex(newfftsize); + d_magbufs[i] = fft::malloc_double(newfftsize); + + memset(d_residbufs[i], 0, newfftsize*sizeof(gr_complex)); + memset(d_magbufs[i], 0, newfftsize*sizeof(double)); + } + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex(d_fftsize, true); + + fft::free(d_fbuf); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + } + } + + int + waterfall_sink_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const gr_complex *in = (const gr_complex*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + + for(int n = 0; n < d_nconnections; n++) { + // Fill up residbuf with d_fftsize number of items + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*resid); + + fft(d_fbuf, d_residbufs[n], d_fftsize); + for(int x = 0; x < d_fftsize; x++) { + d_magbufs[n][x] = (double)((1.0-d_fftavg)*d_magbufs[n][x] + (d_fftavg)*d_fbuf[x]); + } + //volk_32f_convert_64f_a(d_magbufs[n], d_fbuf, d_fftsize); + } + + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new WaterfallUpdateEvent(d_magbufs, + d_fftsize, + d_last_time)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbuf for next time + else { + for(int n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(gr_complex)*datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/waterfall_sink_c_impl.h b/gr-qtgui/lib/waterfall_sink_c_impl.h new file mode 100644 index 0000000000..b51b1e66eb --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_c_impl.h @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_WATERFALL_SINK_C_IMPL_H +#define INCLUDED_QTGUI_WATERFALL_SINK_C_IMPL_H + +#include <qtgui/waterfall_sink_c.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <waterfalldisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API waterfall_sink_c_impl : public waterfall_sink_c + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + float d_fftavg; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + std::vector<gr_complex*> d_residbufs; + std::vector<double*> d_magbufs; + float *d_fbuf; + + QWidget *d_parent; + WaterfallDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const gr_complex *data_in, int size); + + public: + waterfall_sink_c_impl(int size, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent=NULL); + ~waterfall_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + + void set_frequency_range(const double centerfreq, const double bandwidth); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_color(const std::string &color); + void set_line_width(int width); + void set_line_style(Qt::PenStyle style); + void set_line_marker(QwtSymbol::Style marker); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_WATERFALL_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/waterfall_sink_f_impl.cc b/gr-qtgui/lib/waterfall_sink_f_impl.cc new file mode 100644 index 0000000000..3fdb2b795a --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_f_impl.cc @@ -0,0 +1,368 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "waterfall_sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + waterfall_sink_f::sptr + waterfall_sink_f::make(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new waterfall_sink_f_impl(fftsize, wintype, + fc, bw, name, + parent)); + } + + waterfall_sink_f_impl::waterfall_sink_f_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent) + : gr_sync_block("waterfall_sink_f", + gr_make_io_signature(1, -1, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_fftsize(fftsize), d_fftavg(1.0), + d_wintype((filter::firdes::win_type)(wintype)), + d_center_freq(fc), d_bandwidth(bw), d_name(name), + d_nconnections(1), d_parent(parent) + { + d_main_gui = NULL; + + // Perform fftshift operation; + // this is usually desired when plotting + d_shift = true; + + d_fft = new fft::fft_complex(d_fftsize, true); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_float(d_fftsize)); + d_magbufs.push_back(fft::malloc_double(d_fftsize)); + memset(d_residbufs[i], 0, d_fftsize*sizeof(float)); + memset(d_magbufs[i], 0, d_fftsize*sizeof(double)); + } + + buildwindow(); + + initialize(); + } + + waterfall_sink_f_impl::~waterfall_sink_f_impl() + { + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + } + delete d_fft; + fft::free(d_fbuf); + } + + void + waterfall_sink_f_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required) + { + unsigned int ninputs = ninput_items_required.size(); + for (unsigned int i = 0; i < ninputs; i++) { + ninput_items_required[i] = std::min(d_fftsize, 8191); + } + } + + void + waterfall_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new WaterfallDisplayForm(d_nconnections, d_parent); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetFFTWindowType(d_wintype); + d_main_gui->SetFrequencyRange(d_center_freq, + d_center_freq - d_bandwidth/2.0, + d_center_freq + d_bandwidth/2.0); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + waterfall_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + waterfall_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + waterfall_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + waterfall_sink_f_impl::set_fft_size(const int fftsize) + { + d_fftsize = fftsize; + d_main_gui->SetFFTSize(fftsize); + } + + int + waterfall_sink_f_impl::fft_size() const + { + return d_fftsize; + } + + void + waterfall_sink_f_impl::set_fft_average(const float fftavg) + { + d_fftavg = fftavg; + d_main_gui->SetFFTAverage(fftavg); + } + + float + waterfall_sink_f_impl::fft_average() const + { + return d_fftavg; + } + + void + waterfall_sink_f_impl::set_frequency_range(const double centerfreq, + const double bandwidth) + { + d_center_freq = centerfreq; + d_bandwidth = bandwidth; + d_main_gui->SetFrequencyRange(d_center_freq, + -d_bandwidth/2.0, + d_bandwidth/2.0); + } + + void + waterfall_sink_f_impl::set_update_time(double t) + { + //convert update time to ticks + gruel::high_res_timer_type tps = gruel::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + } + + void + waterfall_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(0, title.c_str()); + } + + void + waterfall_sink_f_impl::set_color(const std::string &color) + { + d_main_gui->setColor(0, color.c_str()); + } + + void + waterfall_sink_f_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + waterfall_sink_f_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + waterfall_sink_f_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + waterfall_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + waterfall_sink_f_impl::fft(float *data_out, const float *data_in, int size) + { + // float to complex conversion + gr_complex *dst = d_fft->get_inbuf(); + for (int i = 0; i < size; i++) + dst[i] = data_in[i]; + + if(d_window.size()) { + volk_32fc_32f_multiply_32fc_a(d_fft->get_inbuf(), dst, + &d_window.front(), size); + } + + d_fft->execute(); // compute the fft + + volk_32fc_s32f_x2_power_spectral_density_32f_a(data_out, d_fft->get_outbuf(), + size, 1.0, size); + + // Perform shift operation + unsigned int len = (unsigned int)(floor(size/2.0)); + float *tmp = (float*)malloc(sizeof(float)*len); + memcpy(tmp, &data_out[0], sizeof(float)*len); + memcpy(&data_out[0], &data_out[len], sizeof(float)*(size - len)); + memcpy(&data_out[size - len], tmp, sizeof(float)*len); + free(tmp); + } + + void + waterfall_sink_f_impl::windowreset() + { + filter::firdes::win_type newwintype; + newwintype = d_main_gui->GetFFTWindowType(); + if(d_wintype != newwintype) { + d_wintype = newwintype; + buildwindow(); + } + } + + void + waterfall_sink_f_impl::buildwindow() + { + d_window.clear(); + if(d_wintype != filter::firdes::WIN_NONE) { + d_window = filter::firdes::window(d_wintype, d_fftsize, 6.76); + } + } + + void + waterfall_sink_f_impl::fftresize() + { + int newfftsize = d_fftsize; + + if(newfftsize != d_fftsize) { + + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + fft::free(d_magbufs[i]); + + d_residbufs[i] = fft::malloc_float(newfftsize); + d_magbufs[i] = fft::malloc_double(newfftsize); + + memset(d_residbufs[i], 0, newfftsize*sizeof(float)); + memset(d_magbufs[i], 0, newfftsize*sizeof(double)); + } + + // Set new fft size and reset buffer index + // (throws away any currently held data, but who cares?) + d_fftsize = newfftsize; + d_index = 0; + + // Reset window to reflect new size + buildwindow(); + + // Reset FFTW plan for new size + delete d_fft; + d_fft = new fft::fft_complex(d_fftsize, true); + + fft::free(d_fbuf); + d_fbuf = fft::malloc_float(d_fftsize); + memset(d_fbuf, 0, d_fftsize*sizeof(float)); + } + } + + int + waterfall_sink_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int j=0; + const float *in = (const float*)input_items[0]; + + // Update the FFT size from the application + fftresize(); + windowreset(); + + for(int i=0; i < noutput_items; i+=d_fftsize) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_fftsize-d_index; + + // If we have enough input for one full FFT, do it + if(datasize >= resid) { + + for(int n = 0; n < d_nconnections; n++) { + // Fill up residbuf with d_fftsize number of items + in = (const float*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(float)*resid); + + fft(d_fbuf, d_residbufs[n], d_fftsize); + for(int x = 0; x < d_fftsize; x++) { + d_magbufs[n][x] = (double)((1.0-d_fftavg)*d_magbufs[n][x] + (d_fftavg)*d_fbuf[x]); + } + //volk_32f_convert_64f_a(d_magbufs[n], d_fbuf, d_fftsize); + } + + if(gruel::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gruel::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new WaterfallUpdateEvent(d_magbufs, + d_fftsize, + d_last_time)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbuf for next time + else { + for(int n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[n]; + memcpy(d_residbufs[n]+d_index, &in[j], sizeof(float)*datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/waterfall_sink_f_impl.h b/gr-qtgui/lib/waterfall_sink_f_impl.h new file mode 100644 index 0000000000..684e1dfb03 --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_f_impl.h @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef INCLUDED_QTGUI_WATERFALL_SINK_F_IMPL_H +#define INCLUDED_QTGUI_WATERFALL_SINK_F_IMPL_H + + +#include <qtgui/waterfall_sink_f.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <waterfalldisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API waterfall_sink_f_impl : public waterfall_sink_f + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + float d_fftavg; + filter::firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + bool d_shift; + fft::fft_complex *d_fft; + + int d_index; + std::vector<float*> d_residbufs; + std::vector<double*> d_magbufs; + float *d_fbuf; + + QWidget *d_parent; + WaterfallDisplayForm *d_main_gui; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(float *data_out, const float *data_in, int size); + + public: + waterfall_sink_f_impl(int size, int wintype, + double fc, double bw, + const std::string &name, + QWidget *parent=NULL); + ~waterfall_sink_f_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + + void set_frequency_range(const double centerfreq, const double bandwidth); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_color(const std::string &color); + void set_line_width(int width); + void set_line_style(Qt::PenStyle style); + void set_line_marker(QwtSymbol::Style marker); + + void set_size(int width, int height); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_WATERFALL_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/waterfalldisplayform.cc b/gr-qtgui/lib/waterfalldisplayform.cc new file mode 100644 index 0000000000..1a69e97d10 --- /dev/null +++ b/gr-qtgui/lib/waterfalldisplayform.cc @@ -0,0 +1,205 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <cmath> +#include <QColorDialog> +#include <QMessageBox> +#include <waterfalldisplayform.h> +#include <iostream> + +WaterfallDisplayForm::WaterfallDisplayForm(int nplots, QWidget* parent) + : DisplayForm(nplots, parent) +{ + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + + _layout = new QGridLayout(this); + _displayPlot = new WaterfallDisplayPlot(nplots, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + _numRealDataPoints = 1024; + _fftsize = 1024; + _fftavg = 1.0; + + _min_val = 1000; + _max_val = -1000; + + QAction *autoscale_act = new QAction("Auto Scale", this); + autoscale_act->setStatusTip(tr("Autoscale intensity range")); + connect(autoscale_act, SIGNAL(triggered()), this, SLOT(AutoScale())); + + FFTSizeMenu *sizemenu = new FFTSizeMenu(this); + FFTAverageMenu *avgmenu = new FFTAverageMenu(this); + FFTWindowMenu *winmenu = new FFTWindowMenu(this); + ColorMapMenu *colormenu = new ColorMapMenu(this); + _menu->addMenu(sizemenu); + _menu->addMenu(avgmenu); + _menu->addMenu(winmenu); + _menu->addMenu(colormenu); + _menu->addAction(autoscale_act); + connect(sizemenu, SIGNAL(whichTrigger(int)), + this, SLOT(SetFFTSize(const int))); + connect(avgmenu, SIGNAL(whichTrigger(float)), + this, SLOT(SetFFTAverage(const float))); + connect(winmenu, SIGNAL(whichTrigger(gr::filter::firdes::win_type)), + this, SLOT(SetFFTWindowType(const gr::filter::firdes::win_type))); + connect(colormenu, SIGNAL(whichTrigger(const int, const QColor&, const QColor&)), + this, SLOT(SetColorMap(const int, const QColor&, const QColor&))); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +WaterfallDisplayForm::~WaterfallDisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + delete _intValidator; +} + +WaterfallDisplayPlot* +WaterfallDisplayForm::getPlot() +{ + return ((WaterfallDisplayPlot*)_displayPlot); +} + +void +WaterfallDisplayForm::newData(const QEvent *updateEvent) +{ + WaterfallUpdateEvent *event = (WaterfallUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = event->getPoints(); + const uint64_t numDataPoints = event->getNumDataPoints(); + const gruel::high_res_timer_type dataTimestamp = event->getDataTimestamp(); + + _min_val = 1000; + _max_val = -1000; + for(size_t i=0; i < dataPoints.size(); i++) { + double *min_val = std::min_element(&dataPoints[i][0], &dataPoints[i][numDataPoints-1]); + double *max_val = std::max_element(&dataPoints[i][0], &dataPoints[i][numDataPoints-1]); + if(*min_val < _min_val) + _min_val = *min_val; + if(*max_val > _max_val) + _max_val = *max_val; + } + + getPlot()->PlotNewData(dataPoints, numDataPoints, + d_update_time, dataTimestamp, 0); +} + +void +WaterfallDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == WaterfallUpdateEvent::Type()) { + newData(e); + } +} + +int +WaterfallDisplayForm::GetFFTSize() const +{ + return _fftsize; +} + +float +WaterfallDisplayForm::GetFFTAverage() const +{ + return _fftavg; +} + +gr::filter::firdes::win_type +WaterfallDisplayForm::GetFFTWindowType() const +{ + return _fftwintype; +} + +void +WaterfallDisplayForm::SetFFTSize(const int newsize) +{ + _fftsize = newsize; +} + +void +WaterfallDisplayForm::SetFFTAverage(const float newavg) +{ + _fftavg = newavg; +} + +void +WaterfallDisplayForm::SetFFTWindowType(const gr::filter::firdes::win_type newwin) +{ + _fftwintype = newwin; +} + +void +WaterfallDisplayForm::SetFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency) +{ + double fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency)); + + if(fdiff > 0) { + std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"}; + double units10 = floor(log10(fdiff)); + double units3 = std::max(floor(units10 / 3.0), 0.0); + double units = pow(10, (units10-fmod(units10, 3.0))); + int iunit = static_cast<int>(units3); + + _startFrequency = newStartFrequency; + _stopFrequency = newStopFrequency; + double centerFrequency = newCenterFrequency; + + getPlot()->SetFrequencyRange(_startFrequency, + _stopFrequency, + centerFrequency, + true, + units, strunits[iunit]); + } +} + +void +WaterfallDisplayForm::SetColorMap(const int newType, + const QColor lowColor, + const QColor highColor) +{ + getPlot()->SetIntensityColorMapType(0, newType, + lowColor, highColor); +} + +void +WaterfallDisplayForm::SetIntensityRange(const double minIntensity, + const double maxIntensity) +{ + getPlot()->SetIntensityRange(minIntensity, maxIntensity); +} + +void +WaterfallDisplayForm::AutoScale() +{ + double min_int = _min_val - 5; + double max_int = _max_val + 10; + + getPlot()->SetIntensityRange(min_int, max_int); +} diff --git a/gr-qtgui/lib/waterfalldisplayform.h b/gr-qtgui/lib/waterfalldisplayform.h new file mode 100644 index 0000000000..46e0d815e0 --- /dev/null +++ b/gr-qtgui/lib/waterfalldisplayform.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifndef WATERFALL_DISPLAY_FORM_H +#define WATERFALL_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <WaterfallDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> +#include <filter/firdes.h> + +#include "displayform.h" + +class WaterfallDisplayForm : public DisplayForm +{ + Q_OBJECT + + public: + WaterfallDisplayForm(int nplots=1, QWidget* parent = 0); + ~WaterfallDisplayForm(); + + WaterfallDisplayPlot* getPlot(); + + int GetFFTSize() const; + float GetFFTAverage() const; + gr::filter::firdes::win_type GetFFTWindowType() const; + +public slots: + void customEvent(QEvent *e); + + void SetFFTSize(const int); + void SetFFTAverage(const float); + void SetFFTWindowType(const gr::filter::firdes::win_type); + + void SetFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency); + + void SetIntensityRange(const double minIntensity, + const double maxIntensity); + + void SetColorMap(const int newType, + const QColor lowColor, + const QColor highColor); + + void AutoScale(); + +private slots: + void newData(const QEvent *updateEvent); + +private: + uint64_t _numRealDataPoints; + QIntValidator* _intValidator; + + double _startFrequency; + double _stopFrequency; + + int _fftsize; + float _fftavg; + gr::filter::firdes::win_type _fftwintype; + + double _min_val; + double _max_val; +}; + +#endif /* WATERFALL_DISPLAY_FORM_H */ diff --git a/gr-qtgui/python/__init__.py b/gr-qtgui/python/__init__.py index c7024e4f72..82e36597be 100644 --- a/gr-qtgui/python/__init__.py +++ b/gr-qtgui/python/__init__.py @@ -27,4 +27,4 @@ sinks. # The presence of this file turns this directory into a Python package from qtgui_swig import * -import qtgui_swig as qtgui # to preserve the old interface +#import qtgui_swig as qtgui # to preserve the old interface diff --git a/gr-qtgui/python/qa_qtgui.py b/gr-qtgui/python/qa_qtgui.py index 562706701a..eb402a2084 100755 --- a/gr-qtgui/python/qa_qtgui.py +++ b/gr-qtgui/python/qa_qtgui.py @@ -21,7 +21,7 @@ # from gnuradio import gr, gr_unittest -import qtgui_swig +import qtgui_swig as qtgui class test_qtgui(gr_unittest.TestCase): @@ -31,18 +31,41 @@ class test_qtgui(gr_unittest.TestCase): def tearDown (self): self.tb = None - def test01 (self): - # Test to make sure we can instantiate the sink - self.qtsnk = qtgui_swig.sink_c(1024, gr.firdes.WIN_BLACKMAN_hARRIS, - 0, 1, "Test", - True, True, True, True) + # Tests to make sure we can instantiate the sink + def test01(self): + self.qtsnk = qtgui.sink_c(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test", + True, True, True, True) - def test02 (self): - # Test to make sure we can instantiate the sink - self.qtsnk = qtgui_swig.sink_f(1024, gr.firdes.WIN_BLACKMAN_hARRIS, - 0, 1, "Test", - True, True, True, True) + def test02(self): + self.qtsnk = qtgui.sink_f(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test", + True, True, True, True) + def test03(self): + self.qtsnk = qtgui.time_sink_c(1024, 1, "Test", 1) + + def test04(self): + self.qtsnk = qtgui.time_sink_f(1024, 1, "Test", 1) + + def test05(self): + self.qtsnk = qtgui.freq_sink_c(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test", 1) + + def test06(self): + self.qtsnk = qtgui.freq_sink_f(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test", 1) + + def test07(self): + self.qtsnk = qtgui.waterfall_sink_c(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test") + + def test08(self): + self.qtsnk = qtgui.waterfall_sink_f(1024, gr.firdes.WIN_BLACKMAN_hARRIS, + 0, 1, "Test") + + def test09(self): + self.qtsnk = qtgui.const_sink_c(1024, "Test", 1) if __name__ == '__main__': gr_unittest.run(test_qtgui, "test_qtgui.xml") diff --git a/gr-qtgui/swig/CMakeLists.txt b/gr-qtgui/swig/CMakeLists.txt index e84035b9f7..74545f4cfe 100644 --- a/gr-qtgui/swig/CMakeLists.txt +++ b/gr-qtgui/swig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2010-2011 Free Software Foundation, Inc. +# Copyright 2010-2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -28,6 +28,7 @@ set(GR_SWIG_TARGET_DEPS core_swig) set(GR_SWIG_INCLUDE_DIRS ${GNURADIO_CORE_SWIG_INCLUDE_DIRS} ${GR_QTGUI_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} ${QWT_INCLUDE_DIRS} ) @@ -46,10 +47,6 @@ GR_SWIG_INSTALL( install(FILES qtgui_swig.i - qtgui_sink_c.i - qtgui_sink_f.i - qtgui_time_sink_c.i - qtgui_time_sink_f.i ${CMAKE_CURRENT_BINARY_DIR}/qtgui_swig_doc.i DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig COMPONENT "qtgui_swig" diff --git a/gr-qtgui/swig/__init__.py b/gr-qtgui/swig/__init__.py index e52e326cb3..8f3fca0f4a 100644 --- a/gr-qtgui/swig/__init__.py +++ b/gr-qtgui/swig/__init__.py @@ -21,4 +21,8 @@ # The presence of this file turns this directory into a Python package +''' +Provides a GUI interface using the Qt backend. +''' + from qtgui_swig import * diff --git a/gr-qtgui/swig/qtgui_sink_c.i b/gr-qtgui/swig/qtgui_sink_c.i deleted file mode 100644 index 65e7d1c82b..0000000000 --- a/gr-qtgui/swig/qtgui_sink_c.i +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2011 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. - */ - -%include "gnuradio.i" - -%{ -#include <qtgui_sink_c.h> -%} - -GR_SWIG_BLOCK_MAGIC(qtgui,sink_c) - -qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, - double fc=0, double bw=1.0, - const std::string &name="Display", - bool plotfreq=true, bool plotwaterfall=true, - bool plottime=true, bool plotconst=true, - QWidget *parent=NULL); - -class qtgui_sink_c : public gr_block -{ -private: - friend qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - qtgui_sink_c (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - -public: - void exec_(); - PyObject* pyqwidget(); - - void set_frequency_range(const double centerfreq, - const double bandwidth); - void set_time_domain_axis(double min, double max); - void set_constellation_axis(double xmin, double xmax, - double ymin, double ymax); - void set_frequency_axis(double min, double max); - void set_constellation_pen_size(int size); - void set_update_time(double t); -}; diff --git a/gr-qtgui/swig/qtgui_sink_f.i b/gr-qtgui/swig/qtgui_sink_f.i deleted file mode 100644 index b07eaa9ec8..0000000000 --- a/gr-qtgui/swig/qtgui_sink_f.i +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008,2009,2011 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. - */ - -%include "gnuradio.i" - -%{ -#include <qtgui_sink_f.h> -%} - -GR_SWIG_BLOCK_MAGIC(qtgui,sink_f) - -qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, - double fc=0, double bw=0.0, - const std::string &name="Display", - bool plotfreq=true, bool plotwaterfall=true, - bool plottime=true, bool plotconst=false, - QWidget *parent=NULL); - -class qtgui_sink_f : public gr_block -{ -private: - friend qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - qtgui_sink_f (int fftsize, int wintype, - double fc, double bw, - const std::string &name, - bool plotfreq, bool plotwaterfall, - bool plottime, bool plotconst, - QWidget *parent); - -public: - void exec_(); - PyObject* pyqwidget(); - - void set_frequency_range(const double centerfreq, - const double bandwidth); - void set_time_domain_axis(double min, double max); - void set_constellation_axis(double xmin, double xmax, - double ymin, double ymax); - void set_frequency_axis(double min, double max); - void set_constellation_pen_size(int size); - void set_update_time(double t); -}; diff --git a/gr-qtgui/swig/qtgui_swig.i b/gr-qtgui/swig/qtgui_swig.i index 0d77e22da8..9821ccf440 100644 --- a/gr-qtgui/swig/qtgui_swig.i +++ b/gr-qtgui/swig/qtgui_swig.i @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2011 Free Software Foundation, Inc. + * Copyright 2008,2009,2011,2012 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -20,19 +20,41 @@ * Boston, MA 02110-1301, USA. */ +#define QTGUI_API + %include "gnuradio.i" //load generated python docstrings %include "qtgui_swig_doc.i" %{ -#include "qtgui_sink_c.h" -#include "qtgui_sink_f.h" -#include "qtgui_time_sink_c.h" -#include "qtgui_time_sink_f.h" +#include "qtgui/sink_c.h" +#include "qtgui/sink_f.h" +#include "qtgui/time_sink_c.h" +#include "qtgui/time_sink_f.h" +#include "qtgui/freq_sink_c.h" +#include "qtgui/freq_sink_f.h" +#include "qtgui/const_sink_c.h" +#include "qtgui/waterfall_sink_c.h" +#include "qtgui/waterfall_sink_f.h" %} -%include "qtgui_sink_c.i" -%include "qtgui_sink_f.i" -%include "qtgui_time_sink_c.i" -%include "qtgui_time_sink_f.i" +%include "qtgui/sink_c.h" +%include "qtgui/sink_f.h" +%include "qtgui/time_sink_c.h" +%include "qtgui/time_sink_f.h" +%include "qtgui/freq_sink_c.h" +%include "qtgui/freq_sink_f.h" +%include "qtgui/const_sink_c.h" +%include "qtgui/waterfall_sink_c.h" +%include "qtgui/waterfall_sink_f.h" + +GR_SWIG_BLOCK_MAGIC2(qtgui, sink_c); +GR_SWIG_BLOCK_MAGIC2(qtgui, sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, time_sink_c); +GR_SWIG_BLOCK_MAGIC2(qtgui, time_sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, freq_sink_c); +GR_SWIG_BLOCK_MAGIC2(qtgui, freq_sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, const_sink_c); +GR_SWIG_BLOCK_MAGIC2(qtgui, waterfall_sink_c); +GR_SWIG_BLOCK_MAGIC2(qtgui, waterfall_sink_f); diff --git a/gr-qtgui/swig/qtgui_time_sink_c.i b/gr-qtgui/swig/qtgui_time_sink_c.i deleted file mode 100644 index b78ca5386b..0000000000 --- a/gr-qtgui/swig/qtgui_time_sink_c.i +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -%include "gnuradio.i" - -%{ -#include <qtgui_time_sink_c.h> -%} - -GR_SWIG_BLOCK_MAGIC(qtgui,time_sink_c) - -qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, - const std::string &name, - int nconnections=1, - QWidget *parent=NULL); - -class qtgui_time_sink_c : public gr_sync_block -{ -private: - friend qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent); - qtgui_time_sink_c(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent=NULL); - -public: - void exec_(); - PyObject* pyqwidget(); - - void set_time_domain_axis(double min, double max); - void set_update_time(double t); - void set_title(int which, const std::string &title); - void set_color(int which, const std::string &color); -}; diff --git a/gr-qtgui/swig/qtgui_time_sink_f.i b/gr-qtgui/swig/qtgui_time_sink_f.i deleted file mode 100644 index 9d59f93643..0000000000 --- a/gr-qtgui/swig/qtgui_time_sink_f.i +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2011 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. - */ - -%include "gnuradio.i" - -%{ -#include <qtgui_time_sink_f.h> -%} - -GR_SWIG_BLOCK_MAGIC(qtgui,time_sink_f) - -qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, - const std::string &name, - int nconnections=1, - QWidget *parent=NULL); - -class qtgui_time_sink_f : public gr_sync_block -{ -private: - friend qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent); - qtgui_time_sink_f(int size, double bw, - const std::string &name, - int nconnections, - QWidget *parent=NULL); - -public: - void exec_(); - PyObject* pyqwidget(); - - void set_time_domain_axis(double min, double max); - void set_update_time(double t); - void set_title(int which, const std::string &title); - void set_color(int which, const std::string &color); -}; |