diff options
Diffstat (limited to 'gr-qtgui')
158 files changed, 19984 insertions, 3634 deletions
diff --git a/gr-qtgui/CMakeLists.txt b/gr-qtgui/CMakeLists.txt index d7d889e93a..a2485b8921 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} ) @@ -104,7 +108,7 @@ CPACK_COMPONENT("qtgui_swig" ######################################################################## # Add subdirectories ######################################################################## -add_subdirectory(include) +add_subdirectory(include/qtgui) add_subdirectory(lib) add_subdirectory(doc) if(ENABLE_PYTHON) @@ -112,6 +116,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..05accc64d2 --- /dev/null +++ b/gr-qtgui/apps/CMakeLists.txt @@ -0,0 +1,61 @@ +# 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_base.py + plot_psd_base.py + plot_spectrogram_base.py + plot_time_base.py + plot_time_raster_base.py + plot_form.py + plot_constellation_form.py + plot_psd_form.py + plot_spectrogram_form.py + plot_time_form.py + plot_time_raster_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_time_raster_f + gr_time_raster_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..0687443218 --- /dev/null +++ b/gr-qtgui/apps/gr_constellation_plot @@ -0,0 +1,187 @@ +#!/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_constellation_form import * + from gnuradio.qtgui.plot_base import * +except ImportError: + from plot_constellation_form import * + from plot_base import * + +class my_top_block(gr.top_block): + def __init__(self, filelist, start, nsamples, max_nsamples): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = 0 + self._center_freq = 0 + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._nsamps = nsamples + self._auto_scale = True + + self._y_min = -20 + self._y_max = 20 + self._y_range = 4 + self._y_value = 2 + self.gui_y_axis = None + + 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() + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for f in filelist: + data,_min,_max = read_samples_c(f, self._start, self._nsamps) + self.srcs.append(gr.vector_source_c(data)) + + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_line_label(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.enable_menu(False) + + # 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,_min,_max = read_samples_c(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 set_y_axis(self, y_min, y_max): + y_min = -y_max + self.gui_snk.set_y_axis(y_min, y_max) + self.gui_snk.set_x_axis(y_min, y_max) + return y_min, y_max + + def auto_scale(self, state): + if(state > 0): + self.set_y_axis(self._data_min, self._data_max) + self._auto_scale = True + self._y_value = self._data_max + self._y_range = self._data_max - self._data_min + self._y_min = 10*self._data_min + self._y_max = 10*self._data_max + + if(self.gui_y_axis): + self.gui_y_axis(self._data_min, self._data_max) + else: + self._auto_scale = False + + +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=1000000, + help="Set the number of samples to display [default=%default]") + 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 = plot_constellation_form(tb, 'GNU Radio Constellation Plot', 10000.0) + for n in xrange(tb._nsigs): + main_box._style_edit[n].setCurrentIndex(0) + 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..44ea997e6e --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_b @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_psd_base as plot_base +except ImportError: + import plot_psd_base as plot_base + +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 psd_plot_b(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0, auto_scale=True): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_b + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_chars_to_float + self.gui_snk = qtgui.freq_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of bytes." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = psd_plot_b(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average) + + main_box = plot_base.plot_psd_form(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..f2789ef34b --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_c @@ -0,0 +1,79 @@ +#!/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 import filter +import scipy + +try: + import gnuradio.qtgui.plot_psd_base as plot_base +except ImportError: + import plot_psd_base as plot_base + +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 psd_plot_c(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_c + self.dsize = gr.sizeof_gr_complex + self.src_type = gr.vector_source_c + self.gui_snk = qtgui.freq_sink_c(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of complex floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = psd_plot_c(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average) + + main_box = plot_base.plot_psd_form(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..c6b70baf27 --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_f @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_psd_base as plot_base +except ImportError: + import plot_psd_base as plot_base + +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 psd_plot_f(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0, auto_scale=True): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_f + self.dsize = gr.sizeof_float + self.src_type = gr.vector_source_f + self.gui_snk = qtgui.freq_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = psd_plot_f(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average) + + main_box = plot_base.plot_psd_form(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..b2ae5f7103 --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_i @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_psd_base as plot_base +except ImportError: + import plot_psd_base as plot_base + +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 psd_plot_i(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0, auto_scale=True): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_i + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_ints_to_float + self.gui_snk = qtgui.freq_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of integers." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = psd_plot_i(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average) + + main_box = plot_base.plot_psd_form(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..07a573b56a --- /dev/null +++ b/gr-qtgui/apps/gr_psd_plot_s @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_psd_base as plot_base +except ImportError: + import plot_psd_base as plot_base + +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 psd_plot_s(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0, auto_scale=True): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_s + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_shorts_to_float + self.gui_snk = qtgui.freq_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio PSD Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the PSDs of a list of files. Files are a binary list of shorts." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = psd_plot_s(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average) + + main_box = plot_base.plot_psd_form(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..82a443038e --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_b @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_spectrogram_base as plot_base +except ImportError: + import plot_spectrogram_base as plot_base + +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 spectrogram_plot_b(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_b + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_chars_to_float + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of chars." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = spectrogram_plot_b(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average); + + main_box = plot_base.plot_spectrogram_form(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_spectrogram_plot_c b/gr-qtgui/apps/gr_spectrogram_plot_c new file mode 100755 index 0000000000..059f65cd99 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_c @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_spectrogram_base as plot_base +except ImportError: + import plot_spectrogram_base as plot_base + +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 spectrogram_plot_c(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_c + self.dsize = gr.sizeof_gr_complex + self.src_type = gr.vector_source_c + self.gui_snk = qtgui.waterfall_sink_c(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of complex floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = spectrogram_plot_c(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average); + + main_box = plot_base.plot_spectrogram_form(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..be9c981532 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_f @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_spectrogram_base as plot_base +except ImportError: + import plot_spectrogram_base as plot_base + +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 spectrogram_plot_f(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_f + self.dsize = gr.sizeof_float + self.src_type = gr.vector_source_f + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = spectrogram_plot_f(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average); + + main_box = plot_base.plot_spectrogram_form(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_spectrogram_plot_i b/gr-qtgui/apps/gr_spectrogram_plot_i new file mode 100755 index 0000000000..27a1bebd45 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_i @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_spectrogram_base as plot_base +except ImportError: + import plot_spectrogram_base as plot_base + +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 spectrogram_plot_i(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_i + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_ints_to_float + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of ints." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = spectrogram_plot_i(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average); + + main_box = plot_base.plot_spectrogram_form(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_spectrogram_plot_s b/gr-qtgui/apps/gr_spectrogram_plot_s new file mode 100755 index 0000000000..b41b650a64 --- /dev/null +++ b/gr-qtgui/apps/gr_spectrogram_plot_s @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import filter +import scipy + +try: + import gnuradio.qtgui.plot_spectrogram_base as plot_base +except ImportError: + import plot_spectrogram_base as plot_base + +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 spectrogram_plot_s(plot_base.plot_base): + def __init__(self, filelist, fc, samp_rate, psdsize, start, + nsamples, max_nsamples, avg=1.0): + plot_base.plot_base.__init__(self, filelist, fc, samp_rate, + psdsize, start, nsamples, + max_nsamples, avg) + + self.read_samples = plot_base.read_samples_s + self.dsize = gr.sizeof_float + self.src_type = plot_base.source_shorts_to_float + self.gui_snk = qtgui.waterfall_sink_f(self._psd_size, filter.firdes.WIN_BLACKMAN_hARRIS, + self._center_freq, self._samp_rate, + "GNU Radio Spectrogram Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots the spectrogram (waterfall) of a list of files. Files are a binary list of shorts." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = spectrogram_plot_s(filelist, + options.center_frequency, options.sample_rate, + options.psd_size, + options.start, options.nsamples, max_nsamples, + options.average); + + main_box = plot_base.plot_spectrogram_form(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_b b/gr-qtgui/apps/gr_time_plot_b new file mode 100755 index 0000000000..20522095de --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_b @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_base as plot_base +except ImportError: + import plot_time_base as plot_base + +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 plot_time_b(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self.read_samples = plot_base.read_samples_b + self.dsize = gr.sizeof_float # already converted + self.src_type = plot_base.source_chars_to_float + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of chars." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_b(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + not options.no_auto_scale) + + main_box = plot_base.plot_time_form(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..a7c39a24b9 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_c @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_base as plot_base +except ImportError: + import plot_time_base as plot_base + +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 plot_time_c(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self.read_samples = plot_base.read_samples_c + self.dsize = gr.sizeof_gr_complex + self.src_type = gr.vector_source_c + self.gui_snk = qtgui.time_sink_c(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + self._nsigs *= 2 # complex plots have real/imag + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of complex floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_c(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + not options.no_auto_scale) + + main_box = plot_base.plot_time_form(tb, 'GNU Radio Time Plot', 10000.0) + 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..9b29709b10 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_f @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_base as plot_base +except ImportError: + import plot_time_base as plot_base + +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 plot_time_f(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self.read_samples = plot_base.read_samples_f + self.dsize = gr.sizeof_float + self.src_type = gr.vector_source_f + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_f(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + not options.no_auto_scale) + + main_box = plot_base.plot_time_form(tb, 'GNU Radio Time Plot', 10000.0) + 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..04229de0cd --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_i @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_base as plot_base +except ImportError: + import plot_time_base as plot_base + +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 plot_time_i(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self.read_samples = plot_base.read_samples_i + self.dsize = gr.sizeof_float # already converted + self.src_type = plot_base.source_ints_to_float + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of integers." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_i(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + not options.no_auto_scale) + + main_box = plot_base.plot_time_form(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..cc4a8a7812 --- /dev/null +++ b/gr-qtgui/apps/gr_time_plot_s @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_base as plot_base +except ImportError: + import plot_time_base as plot_base + +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 plot_time_s(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self.read_samples = plot_base.read_samples_s + self.dsize = gr.sizeof_float # already converted + self.src_type = plot_base.source_shorts_to_float + self.gui_snk = qtgui.time_sink_f(self._nsamps, self._samp_rate, + "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of shorts." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_s(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + not options.no_auto_scale) + + main_box = plot_base.plot_time_form(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_raster_b b/gr-qtgui/apps/gr_time_raster_b new file mode 100755 index 0000000000..a326a126c4 --- /dev/null +++ b/gr-qtgui/apps/gr_time_raster_b @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_raster_base as plot_base +except ImportError: + import plot_time_raster_base as plot_base + +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 plot_time_raster_b(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + nrows, ncols, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self._nrows = nrows + self._ncols = ncols + self.read_samples = plot_base.read_samples_b + self.dsize = gr.sizeof_char + self.src_type = gr.vector_source_b + self.gui_snk = qtgui.time_raster_sink_b(self._samp_rate, self._nrows, self._ncols, + [], [], "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of chars." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_raster_b(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + options.nrows, options.ncols, + not options.no_auto_scale) + + main_box = plot_base.plot_time_raster_form(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_raster_f b/gr-qtgui/apps/gr_time_raster_f new file mode 100755 index 0000000000..a6cb897c0b --- /dev/null +++ b/gr-qtgui/apps/gr_time_raster_f @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +import scipy + +try: + import gnuradio.qtgui.plot_time_raster_base as plot_base +except ImportError: + import plot_time_raster_base as plot_base + +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 plot_time_raster_f(plot_base.plot_base): + def __init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + nrows, ncols, + auto_scale): + plot_base.plot_base.__init__(self, filelist, samp_rate, + start, nsamples, max_nsamples, + auto_scale) + self._nrows = nrows + self._ncols = ncols + self.read_samples = plot_base.read_samples_f + self.dsize = gr.sizeof_float + self.src_type = gr.vector_source_f + self.gui_snk = qtgui.time_raster_sink_f(self._samp_rate, self._nrows, self._ncols, + [], [], "GNU Radio Time Plot", self._nsigs) + self.setup() + +def main(): + description = "Plots a list of files on a scope plot. Files are a binary list of floats." + (options, args) = plot_base.setup_options(description) + + filelist = list(args) + max_nsamples = plot_base.find_max_nsamples(filelist) + + tb = plot_time_raster_f(filelist, options.sample_rate, + options.start, options.nsamples, max_nsamples, + options.nrows, options.ncols, + not options.no_auto_scale) + + main_box = plot_base.plot_time_raster_form(tb, 'GNU Radio Time Plot', 10000.0) + main_box.show() + + tb.run() + tb.qapp.exec_() + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass diff --git a/gr-qtgui/apps/grc_qt_example.grc b/gr-qtgui/apps/grc_qt_example.grc index 170cd546ad..320320edcc 100644 --- a/gr-qtgui/apps/grc_qt_example.grc +++ b/gr-qtgui/apps/grc_qt_example.grc @@ -1,6 +1,6 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Sun Apr 10 16:49:13 2011</timestamp> + <timestamp>Sat Nov 10 14:58:46 2012</timestamp> <block> <key>options</key> <param> @@ -44,6 +44,10 @@ <value>True</value> </param> <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> <key>realtime_scheduling</key> <value></value> </param> @@ -80,37 +84,6 @@ </param> </block> <block> - <key>gr_throttle</key> - <param> - <key>id</key> - <value>gr_throttle_0</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>type</key> - <value>complex</value> - </param> - <param> - <key>samples_per_second</key> - <value>samp_rate</value> - </param> - <param> - <key>vlen</key> - <value>1</value> - </param> - <param> - <key>_coordinate</key> - <value>(511, 96)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> <key>variable_qtgui_range</key> <param> <key>id</key> @@ -166,49 +139,6 @@ </param> </block> <block> - <key>gr_sig_source_x</key> - <param> - <key>id</key> - <value>gr_sig_source_x_0</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>type</key> - <value>complex</value> - </param> - <param> - <key>samp_rate</key> - <value>samp_rate</value> - </param> - <param> - <key>waveform</key> - <value>gr.GR_COS_WAVE</value> - </param> - <param> - <key>freq</key> - <value>freq</value> - </param> - <param> - <key>amp</key> - <value>amp</value> - </param> - <param> - <key>offset</key> - <value>0</value> - </param> - <param> - <key>_coordinate</key> - <value>(295, 64)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> <key>variable_qtgui_range</key> <param> <key>id</key> @@ -319,10 +249,41 @@ </param> </block> <block> - <key>gr_channel_model</key> + <key>blocks_throttle</key> <param> <key>id</key> - <value>gr_channel_model_0</value> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(511, 96)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_channel_model</key> + <param> + <key>id</key> + <value>channels_channel_model_0</value> </param> <param> <key>_enabled</key> @@ -346,11 +307,11 @@ </param> <param> <key>seed</key> - <value>42</value> + <value>-42</value> </param> <param> <key>_coordinate</key> - <value>(727, 64)</value> + <value>(724, 64)</value> </param> <param> <key>_rotation</key> @@ -392,6 +353,10 @@ <value>samp_rate</value> </param> <param> + <key>rate</key> + <value>10</value> + </param> + <param> <key>plotfreq</key> <value>True</value> </param> @@ -412,8 +377,55 @@ <value></value> </param> <param> + <key>freqchangevar</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(958, 64)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> + </param> + <param> + <key>freq</key> + <value>freq</value> + </param> + <param> + <key>amp</key> + <value>amp</value> + </param> + <param> + <key>offset</key> + <value>0</value> + </param> + <param> <key>_coordinate</key> - <value>(958, 72)</value> + <value>(297, 64)</value> </param> <param> <key>_rotation</key> @@ -421,20 +433,20 @@ </param> </block> <connection> - <source_block_id>gr_sig_source_x_0</source_block_id> - <sink_block_id>gr_throttle_0</sink_block_id> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>gr_throttle_0</source_block_id> - <sink_block_id>gr_channel_model_0</sink_block_id> + <source_block_id>channels_channel_model_0</source_block_id> + <sink_block_id>qtgui_sink_x_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>gr_channel_model_0</source_block_id> - <sink_block_id>qtgui_sink_x_0</sink_block_id> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_channel_model_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> diff --git a/gr-qtgui/apps/plot_base.py b/gr-qtgui/apps/plot_base.py new file mode 100644 index 0000000000..52c888cff3 --- /dev/null +++ b/gr-qtgui/apps/plot_base.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blocks +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_constellation_form import * + from gnuradio.qtgui.plot_psd_form import * + from gnuradio.qtgui.plot_spectrogram_form import * + from gnuradio.qtgui.plot_time_form import * + from gnuradio.qtgui.plot_time_raster_form import * +except ImportError: + from plot_constellation_form import * + from plot_psd_form import * + from plot_spectrogram_form import * + from plot_time_form import * + from plot_time_raster_form import * + +def read_samples(filename, start, in_size, min_size, dtype, dtype_size): + # Read in_size number of samples from file + fhandle = open(filename, 'r') + fhandle.seek(start*dtype_size, 0) + data = scipy.fromfile(fhandle, dtype=dtype, count=in_size) + data_min = 1.1*data.min() + data_max = 1.1*data.max() + data = data.tolist() + fhandle.close() + + if(min_size > 0): + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + else: + # If we have to, append 0's to create min_size samples of data + if(len(data) < min_size): + data += (min_size - len(data)) * [dtype(0)] + + + return data, data_min, data_max + +def read_samples_f(filename, start, in_size, min_size=0): + return read_samples(filename, start, in_size, min_size, + scipy.float32, gr.sizeof_float) + +def read_samples_i(filename, start, in_size, min_size=0): + return read_samples(filename, start, in_size, min_size, + scipy.int32, gr.sizeof_int) + +def read_samples_s(filename, start, in_size, min_size=0): + return read_samples(filename, start, in_size, min_size, + scipy.int16, gr.sizeof_short) + +def read_samples_b(filename, start, in_size, min_size=0): + d,mn,mx = read_samples(filename, start, in_size, min_size, + scipy.int8, gr.sizeof_char) + + # Bit of a hack since we want to read the data as signed ints, but + # the gr.vector_source_b will only accept unsigned. We read in as + # signed, do our min/max and things on that, then convert here. + d = scipy.array(d, dtype=scipy.uint8).tolist() + return d,mn,mx + +def read_samples_c(filename, start, in_size, min_size=0): + # Complex samples are handled differently + fhandle = open(filename, 'r') + fhandle.seek(start*gr.sizeof_gr_complex, 0) + data = scipy.fromfile(fhandle, dtype=scipy.complex64, count=in_size) + data_min = 1.1*float(min(data.real.min(), data.imag.min())) + data_max = 1.1*float(max(data.real.max(), data.imag.max())) + data = data.tolist() + fhandle.close() + + if(min_size > 0): + if(len(data) < in_size): + print "Warning: read in {0} samples but asked for {1} samples.".format( + len(data), in_size) + else: + # 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, data_min, data_max + +class source_ints_to_float(gr.hier_block2): + def __init__(self, data): + gr.hier_block2.__init__(self, "ints_to_floats", + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, gr.sizeof_float)) + self.src = gr.vector_source_i(data) + self.cvt = blocks.int_to_float() + self.connect(self.src, self.cvt, self) + + def set_data(self, data): + self.src.set_data(data) + +class source_shorts_to_float(gr.hier_block2): + def __init__(self, data): + gr.hier_block2.__init__(self, "shorts_to_floats", + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, gr.sizeof_float)) + self.src = gr.vector_source_s(data) + self.cvt = blocks.short_to_float() + self.connect(self.src, self.cvt, self) + + def set_data(self, data): + self.src.set_data(data) + +class source_chars_to_float(gr.hier_block2): + def __init__(self, data): + gr.hier_block2.__init__(self, "chars_to_floats", + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, gr.sizeof_float)) + self.src = gr.vector_source_b(data) + self.cvt = blocks.char_to_float() + self.connect(self.src, self.cvt, self) + + def set_data(self, data): + self.src.set_data(data) + +def find_max_nsamples(filelist): + # 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) + return max_nsamples diff --git a/gr-qtgui/apps/plot_constellation_form.py b/gr-qtgui/apps/plot_constellation_form.py new file mode 100644 index 0000000000..d5e37bb33c --- /dev/null +++ b/gr-qtgui/apps/plot_constellation_form.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import filter + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_from import plot_form +except ImportError: + from plot_form import plot_form + +class plot_constellation_form(plot_form): + def __init__(self, top_block, title='', scale=1): + plot_form.__init__(self, top_block, title, scale) + + self.right_col_layout = QtGui.QVBoxLayout() + self.right_col_form = QtGui.QFormLayout() + self.right_col_layout.addLayout(self.right_col_form) + self.layout.addLayout(self.right_col_layout, 1,4,1,1) + + # Constellation resizing scales x and y together. + # Set the bar to go from 0.001 to max + self.ybar.setMinimum(1) + self.ybar.setMaximum(self._pos_scale*self.top_block._y_max) + self.ybar.setSingleStep(self._pos_scale*(max(self.top_block._y_range/10, 0.010))) + self.ybar.setPageStep(self._pos_scale*(max(self.top_block._y_range/2, 0.010))) + + self.auto_scale = QtGui.QCheckBox("Auto Scale", self) + if(self.top_block._auto_scale): + self.auto_scale.setChecked(self.top_block._auto_scale) + self.set_auto_scale(self.top_block._auto_scale) + self.connect(self.auto_scale, QtCore.SIGNAL("stateChanged(int)"), + self.set_auto_scale) + self.right_col_layout.addWidget(self.auto_scale) + + self.ybar.setValue(1000*self.top_block._y_value) + + self.add_line_control(self.right_col_layout) + + def set_auto_scale(self, state): + if(state): + self.top_block.auto_scale(True) + else: + self.top_block.auto_scale(False) + + def update_samp_rate(self): + pass + diff --git a/gr-qtgui/apps/plot_form.py b/gr-qtgui/apps/plot_form.py new file mode 100644 index 0000000000..2e1a6c5f77 --- /dev/null +++ b/gr-qtgui/apps/plot_form.py @@ -0,0 +1,401 @@ +#!/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) + +import numpy + +class plot_form(QtGui.QWidget): + def __init__(self, top_block, title='', scale=1): + QtGui.QWidget.__init__(self, None) + + self._start = 0 + self._end = 0 + self._y_min = 0 + self._y_max = 0 + self._pos_scale = scale + + self.top_block = top_block + self.top_block.gui_y_axis = self.gui_y_axis + + self.setWindowTitle(title) + + self.layout = QtGui.QGridLayout(self) + self.layout.addWidget(top_block.get_gui(), 1,2,1,2) + + # Create a save action + self.save_act = QtGui.QAction("Save", self) + self.save_act.setShortcut(QtGui.QKeySequence.Save) + self.connect(self.save_act, QtCore.SIGNAL("triggered()"), + self.save_figure) + + # Create an exit action + self.exit_act = QtGui.QAction("Exit", self) + self.exit_act.setShortcut(QtGui.QKeySequence.Close) + self.connect(self.exit_act, QtCore.SIGNAL("triggered()"), + self.close) + + # Create a menu for the window + self.menu = QtGui.QToolBar("Menu", self) + self.menu.addAction(self.save_act) + self.menu.addAction(self.exit_act) + + self.layout.addWidget(self.menu, 0,0,1,4) + + self.left_col_form = QtGui.QFormLayout() + self.layout.addLayout(self.left_col_form, 1,0,1,1) + self.layout.setColumnStretch(0, 0) + self.layout.setColumnStretch(2, 1) + + # Create Edit boxes for X-axis start/stop + self.size_val = QtGui.QIntValidator(0, top_block._max_nsamps, self) + + self.start_edit = QtGui.QLineEdit(self) + self.start_edit.setMinimumWidth(100) + self.start_edit.setMaximumWidth(100) + self.start_edit.setText(QtCore.QString("%1").arg(top_block._start)) + self.start_edit.setValidator(self.size_val) + self.left_col_form.addRow("Start:", self.start_edit) + self.connect(self.start_edit, QtCore.SIGNAL("returnPressed()"), + self.update_xaxis_pos) + + end = top_block._start + top_block._nsamps + self.end_edit = QtGui.QLineEdit(self) + self.end_edit.setMinimumWidth(100) + self.end_edit.setMaximumWidth(100) + self.end_edit.setText(QtCore.QString("%1").arg(end)) + self.end_edit.setValidator(self.size_val) + self.left_col_form.addRow("End:", self.end_edit) + self.connect(self.end_edit, QtCore.SIGNAL("returnPressed()"), + self.update_xaxis_pos) + + # Create a slider to move the position in the file + self.posbar = QtGui.QSlider(QtCore.Qt.Horizontal, self) + self.posbar.setMaximum(self.top_block._max_nsamps) + self.posbar.setPageStep(self.top_block._nsamps) + self.connect(self.posbar, QtCore.SIGNAL("valueChanged(int)"), + self.update_xaxis_slider) + self.layout.addWidget(self.posbar, 2,2,1,1) + + # Create Edit boxes for Y-axis min/max + self.y_max_edit = QtGui.QLineEdit(self) + self.y_max_edit.setMinimumWidth(100) + self.y_max_edit.setMaximumWidth(100) + self.left_col_form.addRow("Y Max:", self.y_max_edit) + self.connect(self.y_max_edit, QtCore.SIGNAL("editingFinished()"), + self.update_yaxis_pos) + + self.y_min_edit = QtGui.QLineEdit(self) + self.y_min_edit.setMinimumWidth(100) + self.y_min_edit.setMaximumWidth(100) + self.left_col_form.addRow("Y Min:", self.y_min_edit) + self.connect(self.y_min_edit, QtCore.SIGNAL("editingFinished()"), + self.update_yaxis_pos) + + self.grid_check = QtGui.QCheckBox("Grid", self) + self.connect(self.grid_check, QtCore.SIGNAL("stateChanged(int)"), + self.set_grid_check) + self.left_col_form.addWidget(self.grid_check) + + # Create a slider to move the plot's y-axis offset + _ymax = numpy.int32(min(numpy.iinfo(numpy.int32).max, self.top_block._y_max)) + _ymin = numpy.int32(max(numpy.iinfo(numpy.int32).min, self.top_block._y_min)) + _yrng = numpy.int32(min(numpy.iinfo(numpy.int32).max, self.top_block._y_range)) + _yval = numpy.int32(min(numpy.iinfo(numpy.int32).max, self.top_block._y_value)) + self.ybar = QtGui.QSlider(QtCore.Qt.Vertical, self) + self.ybar.setMinimum(self._pos_scale*_ymin) + self.ybar.setMaximum(self._pos_scale*_ymax) + self.ybar.setSingleStep(self._pos_scale*(_yrng/10)) + self.ybar.setPageStep(self._pos_scale*(_yrng/2)) + self.ybar.setValue(self._pos_scale*_ymax) + self.connect(self.ybar, QtCore.SIGNAL("valueChanged(int)"), + self.update_yaxis_slider) + self.layout.addWidget(self.ybar, 1,1,1,1) + + self.gui_y_axis(top_block._y_value-top_block._y_range, top_block._y_value) + + # Create an edit box for the Sample Rate + sr = top_block._samp_rate + self.samp_rate_edit = QtGui.QLineEdit(self) + self.samp_rate_edit.setMinimumWidth(100) + self.samp_rate_edit.setMaximumWidth(100) + self.samp_rate_edit.setText(QtCore.QString("%1").arg(sr)) + self.left_col_form.addRow("Sample Rate:", self.samp_rate_edit) + self.connect(self.samp_rate_edit, QtCore.SIGNAL("returnPressed()"), + self.update_samp_rate) + + # Create an edit box for the center frequency + freq = top_block._center_freq + self.freq_edit = QtGui.QLineEdit(self) + self.freq_edit.setMinimumWidth(100) + self.freq_edit.setMaximumWidth(100) + self.freq_edit.setText(QtCore.QString("%1").arg(freq)) + self.left_col_form.addRow("Frequency:", self.freq_edit) + self.connect(self.freq_edit, QtCore.SIGNAL("returnPressed()"), + self.update_samp_rate) + + self.resize(1000, 500) + + def add_line_control(self, layout): + self._line_tabs = QtGui.QTabWidget() + + self._line_pages = [] + self._line_forms = [] + self._label_edit = [] + self._size_edit = [] + self._color_edit = [] + self._style_edit = [] + self._marker_edit = [] + self._alpha_edit = [] + for n in xrange(self.top_block._nsigs): + self._line_pages.append(QtGui.QDialog()) + self._line_forms.append(QtGui.QFormLayout(self._line_pages[-1])) + + label = self.top_block.gui_snk.line_label(n) + self._label_edit.append(QtGui.QLineEdit(self)) + self._label_edit[-1].setMinimumWidth(125) + self._label_edit[-1].setMaximumWidth(125) + self._label_edit[-1].setText(QtCore.QString("%1").arg(label)) + self._line_forms[-1].addRow("Label:", self._label_edit[-1]) + self.connect(self._label_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_label) + + width_val = QtGui.QIntValidator(1, 20, self) + self._size_edit.append(QtGui.QLineEdit(self)) + self._size_edit[-1].setValidator(width_val) + self._size_edit[-1].setMinimumWidth(100) + self._size_edit[-1].setMaximumWidth(100) + self._size_edit[-1].setText(QtCore.QString("%1").arg(1)) + self._line_forms[-1].addRow("Width:", self._size_edit[-1]) + self.connect(self._size_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_size) + + color = self.top_block.gui_snk.line_color(n) + self._color_edit.append(QtGui.QLineEdit(self)) + self._color_edit[-1].setMinimumWidth(100) + self._color_edit[-1].setMaximumWidth(100) + self._color_edit[-1].setText(QtCore.QString("%1").arg(color)) + self._line_forms[-1].addRow("Color:", self._color_edit[-1]) + self.connect(self._color_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_color) + + self._qtstyles = {"None": QtCore.Qt.NoPen, + "Solid": QtCore.Qt.SolidLine, + "Dash": QtCore.Qt.DashLine, + "Dot": QtCore.Qt.DotLine, + "DashDot": QtCore.Qt.DashDotLine, + "DashDotDot": QtCore.Qt.DashDotDotLine} + self._style_edit.append(QtGui.QComboBox(self)) + self._style_edit[-1].addItems(["None", "Solid", "Dash", + "Dot", "DashDot", "DashDotDot"]) + self._style_edit[-1].setCurrentIndex(1) + self._line_forms[-1].addRow("Style:", self._style_edit[-1]) + self.connect(self._style_edit[-1], + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_line_style) + + # A bit dangerous, this. If QWT ever changes the lineup, + # we will have to adjust this, too. But we also can't + # really rely on PyQwt right now. + self._qwtmarkers = {"None": -1, + "Circle": 0, + "Rectangle": 1, + "Diamond": 2, + "Triangle": 3, + "Down Triangle": 4, + "Left Triangle": 6, + "Right Triangle": 7, + "Cross": 8, + "X-Cross": 9, + "Horiz. Line": 10, + "Vert. Line": 11, + "Star 1": 12, + "Star 2": 13, + "Hexagon": 14} + self._marker_edit.append(QtGui.QComboBox(self)) + self._marker_edit[-1].addItems(["None", "Circle", "Rectangle", "Diamond", + "Triangle", "Down Triangle", "Left Triangle", + "Right Triangle", "Cross", "X-Cross", + "Horiz. Line", "Vert. Line", "Star 1", + "Star 2", "Hexagon"]) + self._line_forms[-1].addRow("Marker:", self._marker_edit[-1]) + self.connect(self._marker_edit[-1], + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_line_marker) + + alpha_val = QtGui.QDoubleValidator(0, 1.0, 2, self) + alpha_val.setTop(1.0) + alpha = self.top_block.gui_snk.line_alpha(n) + self._alpha_edit.append(QtGui.QLineEdit(self)) + self._alpha_edit[-1].setMinimumWidth(50) + self._alpha_edit[-1].setMaximumWidth(100) + self._alpha_edit[-1].setText(QtCore.QString("%1").arg(alpha)) + self._alpha_edit[-1].setValidator(alpha_val) + self._line_forms[-1].addRow("Alpha:", self._alpha_edit[-1]) + self.connect(self._alpha_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_alpha) + + self._line_tabs.addTab(self._line_pages[-1], "{0}".format(label)) + + layout.addWidget(self._line_tabs) + + def update_line_label(self): + index = self._line_tabs.currentIndex() + label = self._label_edit[index].text() + self._line_tabs.setTabText(index, label) + self.top_block.gui_snk.set_line_label(index, str(label)) + + def update_line_size(self): + index = self._line_tabs.currentIndex() + width = self._size_edit[index].text().toUInt()[0] + self.top_block.gui_snk.set_line_width(index, width) + self.update_line_alpha() + + def update_line_color(self): + index = self._line_tabs.currentIndex() + color = str(self._color_edit[index].text()) + self.top_block.gui_snk.set_line_color(index, color) + self.update_line_alpha() + + def update_line_style(self, s_index): + index = self._line_tabs.currentIndex() + style_str = self._style_edit[index].itemText(s_index) + style = self._qtstyles[str(style_str)] + self.top_block.gui_snk.set_line_style(index, int(style)) + + def update_line_marker(self, m_index): + index = self._line_tabs.currentIndex() + marker_str = self._marker_edit[index].itemText(m_index) + marker = self._qwtmarkers[str(marker_str)] + self.top_block.gui_snk.set_line_marker(index, int(marker)) + + def update_line_alpha(self): + index = self._line_tabs.currentIndex() + alpha = self._alpha_edit[index].text().toDouble()[0] + self.top_block.gui_snk.set_line_alpha(index, alpha) + + def update_xaxis_pos(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 + self.posbar.setPageStep(self.top_block._nsamps) + self.posbar.setValue(self._start) + + def update_xaxis_slider(self, value): + self._start = value + self._end = value + self.posbar.pageStep() + + self.start_edit.setText("{0}".format(self._start)) + self.end_edit.setText("{0}".format(self._end)) + + if(value != self.top_block._max_nsamps): + self.top_block.reset(self._start, self.posbar.pageStep()) + + def update_yaxis_pos(self): + if(not self.top_block._auto_scale): + newmin = self.y_min_edit.text().toDouble()[0] + newmax = self.y_max_edit.text().toDouble()[0] + if(newmin != self._y_min or newmax != self._y_max): + self._y_min = newmin + self._y_max = newmax + self.top_block._y_range = newmax - newmin + self.top_block.set_y_axis(self._y_min, self._y_max) + self.ybar.setValue(self._y_max*self._pos_scale) + else: + self.y_min_edit.setText("{0:.2f}".format(self._y_min)) + self.y_max_edit.setText("{0:.2f}".format(self._y_max)) + + def update_yaxis_slider(self, value): + if(not self.top_block._auto_scale): + value = value/self._pos_scale + self.top_block._y_value = value + self._y_min = value - self.top_block._y_range + self._y_max = value + + ret = self.top_block.set_y_axis(self._y_min, self._y_max) + if(ret): + self._y_min = ret[0] + self._y_max = ret[1] + + self.gui_y_axis(self._y_min, self._y_max) + else: + self.ybar.setValue(self._y_max*self._pos_scale) + + def update_samp_rate(self): + sr = self.samp_rate_edit.text().toDouble()[0] + fr = self.freq_edit.text().toDouble()[0] + self.top_block.gui_snk.set_frequency_range(fr, sr) + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def gui_y_axis(self, ymin, ymax): + self.y_min_edit.setText("{0:.2f}".format(ymin)) + self.y_max_edit.setText("{0:.2f}".format(ymax)) + self._y_min = ymin + self._y_max = ymax + self.ybar.setValue(self._pos_scale*ymax) + + def set_grid_check(self, state): + if(state): + self.top_block.gui_snk.enable_grid(True) + else: + self.top_block.gui_snk.enable_grid(False) + + def save_figure(self): + qpix = QtGui.QPixmap.grabWidget(self.top_block.pyWin) + types = "JPEG file (*.jpg);;" + \ + "Portable Network Graphics file (*.png);;" + \ + "Bitmap file (*.bmp);;" + \ + "TIFF file (*.tiff)" + filebox = QtGui.QFileDialog(self, "Save Image", "./", types) + filebox.setViewMode(QtGui.QFileDialog.Detail) + if(filebox.exec_()): + filename = filebox.selectedFiles()[0] + filetype = filebox.selectedNameFilter() + else: + return + + if(filetype.contains(".jpg")): + qpix.save(filename, "JPEG"); + elif(filetype.contains(".png")): + qpix.save(filename, "PNG"); + elif(filetype.contains(".bmp")): + qpix.save(filename, "BMP"); + elif(filetype.contains(".tiff")): + qpix.save(filename, "TIFF"); + else: + qpix.save(filename, "JPEG"); diff --git a/gr-qtgui/apps/plot_psd_base.py b/gr-qtgui/apps/plot_psd_base.py new file mode 100644 index 0000000000..a4a6cb871a --- /dev/null +++ b/gr-qtgui/apps/plot_psd_base.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blocks +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 * + from gnuradio.qtgui.plot_base import * +except ImportError: + from plot_form import * + from plot_base import * + +class plot_base(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 + self._nsamps = nsamples + self._auto_scale = False + + self._y_min = -200 + self._y_max = 400 + self._y_range = 130 + self._y_value = 10 + + self._is_setup = False + + self.qapp = QtGui.QApplication(sys.argv) + + def setup(self): + self.skip = gr.skiphead(self.dsize, self._start) + + n = 0 + self.srcs = list() + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for f in self._filelist: + data,_min,_max = self.read_samples(f, self._start, + self._nsamps, self._psd_size) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + self.srcs.append(self.src_type(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_line_label(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.enable_menu(False) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self._is_setup = True + + def is_setup(self): + return self._is_setup + + def set_y_axis(self, y_min, y_max): + self.gui_snk.set_y_axis(y_min, y_max) + return y_min, y_max + + def get_gui(self): + if(self.is_setup()): + return self.pyWin + else: + return None + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for s,f in zip(self.srcs, self._filelist): + data,_min,_max = self.read_samples(f, self._start, newnsamps, self._psd_size) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + s.set_data(data) + + self.start() + +def setup_options(desc): + parser = OptionParser(option_class=eng_option, description=desc, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=1000000, + 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) + return (options,args) diff --git a/gr-qtgui/apps/plot_psd_form.py b/gr-qtgui/apps/plot_psd_form.py new file mode 100644 index 0000000000..262734bd67 --- /dev/null +++ b/gr-qtgui/apps/plot_psd_form.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import filter + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_from import plot_form +except ImportError: + from plot_form import plot_form + +class plot_psd_form(plot_form): + def __init__(self, top_block, title=''): + plot_form.__init__(self, top_block, title) + + self.right_col_layout = QtGui.QVBoxLayout() + self.right_col_form = QtGui.QFormLayout() + self.right_col_layout.addLayout(self.right_col_form) + self.layout.addLayout(self.right_col_layout, 1,4,1,1) + + self.psd_size_val = QtGui.QIntValidator(0, 2**18, self) + self.psd_size_edit = QtGui.QLineEdit(self) + self.psd_size_edit.setMinimumWidth(50) + self.psd_size_edit.setMaximumWidth(100) + self.psd_size_edit.setText(QtCore.QString("%1").arg(top_block._psd_size)) + self.psd_size_edit.setValidator(self.psd_size_val) + self.right_col_form.addRow("FFT:", self.psd_size_edit) + self.connect(self.psd_size_edit, QtCore.SIGNAL("returnPressed()"), + self.update_psd_size) + + self.psd_win_combo = QtGui.QComboBox(self) + self.psd_win_combo.addItems(["None", "Hamming", "Hann", "Blackman", + "Rectangular", "Kaiser", "Blackman-harris"]) + self.psd_win_combo.setCurrentIndex(self.top_block.gui_snk.fft_window()+1) + self.right_col_form.addRow("Window:", self.psd_win_combo) + self.connect(self.psd_win_combo, + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_psd_win) + + self.psd_avg_val = QtGui.QDoubleValidator(0, 1.0, 4, self) + self.psd_avg_edit = QtGui.QLineEdit(self) + self.psd_avg_edit.setMinimumWidth(50) + self.psd_avg_edit.setMaximumWidth(100) + self.psd_avg_edit.setText(QtCore.QString("%1").arg(top_block._avg)) + self.psd_avg_edit.setValidator(self.psd_avg_val) + self.right_col_form.addRow("Average:", self.psd_avg_edit) + self.connect(self.psd_avg_edit, QtCore.SIGNAL("returnPressed()"), + self.update_psd_avg) + + self.add_line_control(self.right_col_layout) + + def update_psd_size(self): + newpsdsize = self.psd_size_edit.text().toInt()[0] + if(newpsdsize != self.top_block._psd_size): + self.top_block.gui_snk.set_fft_size(newpsdsize) + self.top_block._psd_size = newpsdsize + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def update_psd_win(self, index): + self.top_block.gui_snk.set_fft_window(index-1) + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def update_psd_avg(self): + newpsdavg = self.psd_avg_edit.text().toDouble()[0] + if(newpsdavg != self.top_block._avg): + self.top_block.gui_snk.set_fft_average(newpsdavg) + self.top_block._avg = newpsdavg + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + diff --git a/gr-qtgui/apps/plot_spectrogram_base.py b/gr-qtgui/apps/plot_spectrogram_base.py new file mode 100644 index 0000000000..adfbfd032f --- /dev/null +++ b/gr-qtgui/apps/plot_spectrogram_base.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blocks +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 * + from gnuradio.qtgui.plot_base import * +except ImportError: + from plot_form import * + from plot_base import * + +class plot_base(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 + self._nsamps = nsamples + self._auto_scale = False + + self._y_min = -200 + self._y_max = 400 + self._y_range = 130 + self._y_value = 10 + + self._is_setup = False + + self.qapp = QtGui.QApplication(sys.argv) + + def setup(self): + self.skip = gr.skiphead(self.dsize, self._start) + + n = 0 + self.srcs = list() + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for f in self._filelist: + data,_min,_max = self.read_samples(f, self._start, + self._nsamps, self._psd_size) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + self.srcs.append(self.src_type(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + self.gui_snk.set_line_label(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.enable_menu(False) + self.gui_snk.set_fft_average(self._avg) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self._is_setup = True + + def is_setup(self): + return self._is_setup + + def set_y_axis(self, y_min, y_max): + self.gui_snk.set_intensity_range(y_min, y_max) + return y_min, y_max + + def get_gui(self): + if(self.is_setup()): + return self.pyWin + else: + return None + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + self.gui_snk.clear_data() + + self._start = newstart + self._nsamps = newnsamps + + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for s,f in zip(self.srcs, self._filelist): + data,_min,_max = self.read_samples(f, self._start, newnsamps, self._psd_size) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + s.set_data(data) + + self.start() + +def setup_options(desc): + parser = OptionParser(option_class=eng_option, description=desc, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=1000000, + help="Set the number of samples to display [default=%default]") + 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) + + return (options, args) + diff --git a/gr-qtgui/apps/plot_spectrogram_form.py b/gr-qtgui/apps/plot_spectrogram_form.py new file mode 100644 index 0000000000..7f8361e6b1 --- /dev/null +++ b/gr-qtgui/apps/plot_spectrogram_form.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import filter + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_from import plot_form +except ImportError: + from plot_form import plot_form + +class plot_spectrogram_form(plot_form): + def __init__(self, top_block, title=''): + plot_form.__init__(self, top_block, title) + + self.right_col_layout = QtGui.QVBoxLayout() + self.right_col_form = QtGui.QFormLayout() + self.right_col_layout.addLayout(self.right_col_form) + self.layout.addLayout(self.right_col_layout, 1,4,1,1) + + self.psd_size_val = QtGui.QIntValidator(0, 2**18, self) + self.psd_size_edit = QtGui.QLineEdit(self) + self.psd_size_edit.setMinimumWidth(50) + self.psd_size_edit.setMaximumWidth(100) + self.psd_size_edit.setText(QtCore.QString("%1").arg(top_block._psd_size)) + self.psd_size_edit.setValidator(self.psd_size_val) + self.right_col_form.addRow("FFT Size:", self.psd_size_edit) + self.connect(self.psd_size_edit, QtCore.SIGNAL("returnPressed()"), + self.update_psd_size) + + self.psd_win_combo = QtGui.QComboBox(self) + self.psd_win_combo.addItems(["None", "Hamming", "Hann", "Blackman", + "Rectangular", "Kaiser", "Blackman-harris"]) + self.psd_win_combo.setCurrentIndex(self.top_block.gui_snk.fft_window()+1) + self.right_col_form.addRow("Window:", self.psd_win_combo) + self.connect(self.psd_win_combo, + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_psd_win) + + self.psd_avg_val = QtGui.QDoubleValidator(0, 1.0, 4, self) + self.psd_avg_edit = QtGui.QLineEdit(self) + self.psd_avg_edit.setMinimumWidth(50) + self.psd_avg_edit.setMaximumWidth(100) + self.psd_avg_edit.setText(QtCore.QString("%1").arg(top_block._avg)) + self.psd_avg_edit.setValidator(self.psd_avg_val) + self.right_col_form.addRow("Average:", self.psd_avg_edit) + self.connect(self.psd_avg_edit, QtCore.SIGNAL("returnPressed()"), + self.update_psd_avg) + + self.autoscale_button = QtGui.QPushButton("Auto Scale", self) + self.autoscale_button.setMaximumWidth(100) + self.right_col_layout.addWidget(self.autoscale_button) + self.connect(self.autoscale_button, QtCore.SIGNAL("clicked()"), + self.spectrogram_auto_scale) + + self.add_spectrogram_control(self.right_col_layout) + + def update_psd_size(self): + newpsdsize = self.psd_size_edit.text().toInt()[0] + if(newpsdsize != self.top_block._psd_size): + self.top_block.gui_snk.set_fft_size(newpsdsize) + self.top_block._psd_size = newpsdsize + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def update_psd_win(self, index): + self.top_block.gui_snk.set_fft_window(index-1) + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def update_psd_avg(self): + newpsdavg = self.psd_avg_edit.text().toDouble()[0] + if(newpsdavg != self.top_block._avg): + self.top_block.gui_snk.set_fft_average(newpsdavg) + self.top_block._avg = newpsdavg + self.top_block.reset(self.top_block._start, + self.top_block._nsamps) + + def add_spectrogram_control(self, layout): + self._line_tabs = QtGui.QTabWidget() + + self._line_pages = [] + self._line_forms = [] + self._label_edit = [] + self._size_edit = [] + self._color_edit = [] + self._style_edit = [] + self._marker_edit = [] + self._alpha_edit = [] + for n in xrange(self.top_block._nsigs): + self._line_pages.append(QtGui.QDialog()) + self._line_forms.append(QtGui.QFormLayout(self._line_pages[-1])) + + label = self.top_block.gui_snk.line_label(n) + self._label_edit.append(QtGui.QLineEdit(self)) + self._label_edit[-1].setMinimumWidth(125) + self._label_edit[-1].setMaximumWidth(125) + self._label_edit[-1].setText(QtCore.QString("%1").arg(label)) + self._line_forms[-1].addRow("Label:", self._label_edit[-1]) + self.connect(self._label_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_label) + + self._qtcolormaps = ["Multi Color", "White Hot", + "Black Hot", "Incandescent"] + self._color_edit.append(QtGui.QComboBox(self)) + self._color_edit[-1].addItems(self._qtcolormaps) + self._line_forms[-1].addRow("Color Map:", self._color_edit[-1]) + self.connect(self._color_edit[-1], + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_color_map) + + alpha_val = QtGui.QDoubleValidator(0, 1.0, 2, self) + alpha_val.setTop(1.0) + alpha = self.top_block.gui_snk.line_alpha(n) + self._alpha_edit.append(QtGui.QLineEdit(self)) + self._alpha_edit[-1].setMinimumWidth(50) + self._alpha_edit[-1].setMaximumWidth(100) + self._alpha_edit[-1].setText(QtCore.QString("%1").arg(alpha)) + self._alpha_edit[-1].setValidator(alpha_val) + self._line_forms[-1].addRow("Alpha:", self._alpha_edit[-1]) + self.connect(self._alpha_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_alpha) + + self._line_tabs.addTab(self._line_pages[-1], "{0}".format(label)) + + layout.addWidget(self._line_tabs) + + def update_color_map(self, c_index): + index = self._line_tabs.currentIndex() + self.top_block.gui_snk.set_color_map(index, c_index) + self.update_line_alpha() + + def spectrogram_auto_scale(self): + self.top_block.gui_snk.auto_scale() + _min = self.top_block.gui_snk.min_intensity(0) + _max = self.top_block.gui_snk.max_intensity(0) + if(self.gui_y_axis): + self.gui_y_axis(_min, _max) diff --git a/gr-qtgui/apps/plot_time_base.py b/gr-qtgui/apps/plot_time_base.py new file mode 100644 index 0000000000..c80bace1b6 --- /dev/null +++ b/gr-qtgui/apps/plot_time_base.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blocks +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 * + from gnuradio.qtgui.plot_base import * +except ImportError: + from plot_form import * + from plot_base import * + +class plot_base(gr.top_block): + def __init__(self, filelist, samp_rate, start, + nsamples, max_nsamples, + auto_scale): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._center_freq = 0 + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._auto_scale = auto_scale + self._nsamps = nsamples + self._is_setup = False + + self._y_min = -20 + self._y_max = 20 + self._y_range = 2 + self._y_value = 1 + self.gui_y_axis = None + + self.qapp = QtGui.QApplication(sys.argv) + + def setup(self): + self.skip = gr.skiphead(self.dsize, self._start) + + n = 0 + self.srcs = list() + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for f in self._filelist: + data,_min,_max = self.read_samples(f, self._start, self._nsamps) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + self.srcs.append(self.src_type(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + if(type(self.gui_snk) == qtgui.time_sink_c_sptr): + self.gui_snk.set_line_label(n, "Re{{{0}}}".format(fname)) + self.gui_snk.set_line_label(n+1, "Im{{{0}}}".format(fname)) + n += 2 + else: + self.gui_snk.set_line_label(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.enable_menu(False) + self.auto_scale(self._auto_scale) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self._is_setup = True + + def is_setup(self): + return self._is_setup + + def set_y_axis(self, y_min, y_max): + self.gui_snk.set_y_axis(y_min, y_max) + return y_min, y_max + + def get_gui(self): + if(self.is_setup()): + return self.pyWin + else: + return None + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for s,f in zip(self.srcs, self._filelist): + data,_min,_max = self.read_samples(f, self._start, newnsamps) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self.auto_scale(self._auto_scale) + + self._nsamps = newnsamps + self.gui_snk.set_nsamps(self._nsamps) + + self.start() + + def auto_scale(self, state): + if(state > 0): + self.gui_snk.set_y_axis(self._data_min, self._data_max) + self._auto_scale = True + self._y_value = self._data_max + self._y_range = self._data_max - self._data_min + self._y_min = 10*self._data_min + self._y_max = 10*self._data_max + + if(self.gui_y_axis): + self.gui_y_axis(self._data_min, self._data_max) + else: + self._auto_scale = False + +def setup_options(desc): + parser = OptionParser(option_class=eng_option, description=desc, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=1000000, + help="Set the number of samples to display [default=%default]") + 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("", "--no-auto-scale", action="store_true", default=False, + help="Do not auto-scale the plot [default=%default]") + (options,args) = parser.parse_args() + if(len(args) < 1): + parser.print_help() + sys.exit(0) + return (options,args) + diff --git a/gr-qtgui/apps/plot_time_form.py b/gr-qtgui/apps/plot_time_form.py new file mode 100644 index 0000000000..30701c8d37 --- /dev/null +++ b/gr-qtgui/apps/plot_time_form.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import filter + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_from import plot_form +except ImportError: + from plot_form import plot_form + +class plot_time_form(plot_form): + def __init__(self, top_block, title='', scale=1): + plot_form.__init__(self, top_block, title, scale) + + self.right_col_layout = QtGui.QVBoxLayout() + self.right_col_form = QtGui.QFormLayout() + self.right_col_layout.addLayout(self.right_col_form) + self.layout.addLayout(self.right_col_layout, 1,4,1,1) + + self.auto_scale = QtGui.QCheckBox("Auto Scale", self) + if(self.top_block._auto_scale): + self.auto_scale.setChecked(self.top_block._auto_scale) + self.connect(self.auto_scale, QtCore.SIGNAL("stateChanged(int)"), + self.set_auto_scale) + self.right_col_layout.addWidget(self.auto_scale) + + self.stem = QtGui.QCheckBox("Stem", self) + self.connect(self.stem, QtCore.SIGNAL("stateChanged(int)"), + self.enable_stem) + self.right_col_layout.addWidget(self.stem) + + self.add_line_control(self.right_col_layout) + + def set_auto_scale(self, state): + if(state): + self.top_block.auto_scale(True) + else: + self.top_block.auto_scale(False) + + def enable_stem(self, state): + self.top_block.gui_snk.enable_stem_plot(state) + if(state): + index = self._qwtmarkers['Circle']+1 + else: + index = self._qwtmarkers['None']+1 + for n in xrange(self.top_block._nsigs): + self._marker_edit[n].setCurrentIndex(index) + + def update_samp_rate(self): + sr = self.samp_rate_edit.text().toDouble()[0] + self.top_block.gui_snk.set_samp_rate(sr) + diff --git a/gr-qtgui/apps/plot_time_raster_base.py b/gr-qtgui/apps/plot_time_raster_base.py new file mode 100644 index 0000000000..ff3cc2869d --- /dev/null +++ b/gr-qtgui/apps/plot_time_raster_base.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, blocks +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 * + from gnuradio.qtgui.plot_base import * +except ImportError: + from plot_form import * + from plot_base import * + +class plot_base(gr.top_block): + def __init__(self, filelist, samp_rate, start, + nsamples, max_nsamples, + auto_scale): + gr.top_block.__init__(self) + + self._filelist = filelist + self._samp_rate = samp_rate + self._center_freq = 0 + self._start = start + self._max_nsamps = max_nsamples + self._nsigs = len(self._filelist) + self._auto_scale = auto_scale + self._nsamps = nsamples + self._is_setup = False + + self._y_min = -20 + self._y_max = 20 + self._y_range = 2 + self._y_value = 1 + self.gui_y_axis = None + + self.qapp = QtGui.QApplication(sys.argv) + + def setup(self): + self.skip = gr.skiphead(self.dsize, self._start) + + n = 0 + self.srcs = list() + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for f in self._filelist: + data,_min,_max = self.read_samples(f, self._start, self._nsamps) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + self.srcs.append(self.src_type(data)) + + # Set default labels based on file names + fname = f.split("/")[-1] + #self.gui_snk.set_line_label(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.enable_menu(False) + self.auto_scale(self._auto_scale) + + # Get Python Qt references + pyQt = self.gui_snk.pyqwidget() + self.pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) + + self._is_setup = True + + def is_setup(self): + return self._is_setup + + def set_y_axis(self, y_min, y_max): + if(not self._auto_scale): + self.gui_snk.set_intensity_range(y_min, y_max) + return y_min, y_max + else: + return None + + def get_gui(self): + if(self.is_setup()): + return self.pyWin + else: + return None + + def reset(self, newstart, newnsamps): + self.stop() + self.wait() + + self._start = newstart + + self._data_min = sys.maxint + self._data_max = -sys.maxint - 1 + for s,f in zip(self.srcs, self._filelist): + data,_min,_max = self.read_samples(f, self._start, newnsamps) + if(_min < self._data_min): + self._data_min = _min + if(_max > self._data_max): + self._data_max = _max + + s.set_data(data) + if(len(data) < newnsamps): + newnsamps = len(data) + + self.auto_scale(self._auto_scale) + self._nsamps = newnsamps + self.start() + + def auto_scale(self, state): + if(state > 0): + self.gui_snk.set_intensity_range(self._data_min, self._data_max) + self._auto_scale = True + self._y_value = self._data_max + self._y_range = self._data_max - self._data_min + self._y_min = 10*self._data_min + self._y_max = 10*self._data_max + + if(self.gui_y_axis): + self.gui_y_axis(self._data_min, self._data_max) + else: + self._auto_scale = False + +def setup_options(desc): + parser = OptionParser(option_class=eng_option, description=desc, + conflict_handler="resolve") + parser.add_option("-N", "--nsamples", type="int", default=1000000, + help="Set the number of samples to display [default=%default]") + parser.add_option("-S", "--start", type="int", default=0, + help="Starting sample number [default=%default]") + parser.add_option("-C", "--ncols", type="int", default=100, + help="Number of columns [default=%default]") + parser.add_option("-R", "--nrows", type="int", default=100, + help="Number of rows [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("", "--no-auto-scale", action="store_true", default=False, + help="Do not auto-scale the plot [default=%default]") + (options,args) = parser.parse_args() + if(len(args) < 1): + parser.print_help() + sys.exit(0) + return (options,args) + diff --git a/gr-qtgui/apps/plot_time_raster_form.py b/gr-qtgui/apps/plot_time_raster_form.py new file mode 100644 index 0000000000..32d93744d5 --- /dev/null +++ b/gr-qtgui/apps/plot_time_raster_form.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys, math +from gnuradio import filter + +try: + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + from gnuradio.qtgui.plot_from import plot_form +except ImportError: + from plot_form import plot_form + +class plot_time_raster_form(plot_form): + def __init__(self, top_block, title='', scale=1): + plot_form.__init__(self, top_block, title, scale) + + self.right_col_layout = QtGui.QVBoxLayout() + self.right_col_form = QtGui.QFormLayout() + self.right_col_layout.addLayout(self.right_col_form) + self.layout.addLayout(self.right_col_layout, 1,4,1,1) + + self.auto_scale = QtGui.QCheckBox("Auto Scale", self) + if(self.top_block._auto_scale): + self.auto_scale.setChecked(self.top_block._auto_scale) + self.connect(self.auto_scale, QtCore.SIGNAL("stateChanged(int)"), + self.set_auto_scale) + self.right_col_layout.addWidget(self.auto_scale) + + self.ncols_edit = QtGui.QLineEdit(self) + self.ncols_edit.setMinimumWidth(100) + self.ncols_edit.setMaximumWidth(100) + self.ncols_edit.setText(QtCore.QString("%1").arg(top_block._ncols)) + self.right_col_form.addRow("Num. Cols.", self.ncols_edit) + self.connect(self.ncols_edit, QtCore.SIGNAL("returnPressed()"), + self.ncols_update) + + self.nrows_edit = QtGui.QLineEdit(self) + self.nrows_edit.setMinimumWidth(100) + self.nrows_edit.setMaximumWidth(100) + self.nrows_edit.setText(QtCore.QString("%1").arg(top_block._nrows)) + self.right_col_form.addRow("Num. Rows.", self.nrows_edit) + self.connect(self.nrows_edit, QtCore.SIGNAL("returnPressed()"), + self.nrows_update) + + + self.add_raster_control(self.right_col_layout) + + def add_raster_control(self, layout): + self._line_tabs = QtGui.QTabWidget() + + self._line_pages = [] + self._line_forms = [] + self._label_edit = [] + self._size_edit = [] + self._color_edit = [] + self._style_edit = [] + self._marker_edit = [] + self._alpha_edit = [] + for n in xrange(self.top_block._nsigs): + self._line_pages.append(QtGui.QDialog()) + self._line_forms.append(QtGui.QFormLayout(self._line_pages[-1])) + + label = self.top_block.gui_snk.line_label(n) + self._label_edit.append(QtGui.QLineEdit(self)) + self._label_edit[-1].setMinimumWidth(125) + self._label_edit[-1].setMaximumWidth(125) + self._label_edit[-1].setText(QtCore.QString("%1").arg(label)) + self._line_forms[-1].addRow("Label:", self._label_edit[-1]) + self.connect(self._label_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_label) + + self._qtcolormaps = ["Multi Color", "White Hot", + "Black Hot", "Incandescent"] + self._color_edit.append(QtGui.QComboBox(self)) + self._color_edit[-1].addItems(self._qtcolormaps) + self._color_edit[-1].setCurrentIndex(1) + self._line_forms[-1].addRow("Color Map:", self._color_edit[-1]) + self.connect(self._color_edit[-1], + QtCore.SIGNAL("currentIndexChanged(int)"), + self.update_color_map) + + alpha_val = QtGui.QDoubleValidator(0, 1.0, 2, self) + alpha_val.setTop(1.0) + alpha = self.top_block.gui_snk.line_alpha(n) + self._alpha_edit.append(QtGui.QLineEdit(self)) + self._alpha_edit[-1].setMinimumWidth(50) + self._alpha_edit[-1].setMaximumWidth(100) + self._alpha_edit[-1].setText(QtCore.QString("%1").arg(alpha)) + self._alpha_edit[-1].setValidator(alpha_val) + self._line_forms[-1].addRow("Alpha:", self._alpha_edit[-1]) + self.connect(self._alpha_edit[-1], QtCore.SIGNAL("returnPressed()"), + self.update_line_alpha) + + self._line_tabs.addTab(self._line_pages[-1], "{0}".format(label)) + + layout.addWidget(self._line_tabs) + + def update_color_map(self, c_index): + index = self._line_tabs.currentIndex() + self.top_block.gui_snk.set_color_map(index, c_index) + self.update_line_alpha() + + def set_auto_scale(self, state): + if(state): + self.top_block.auto_scale(True) + else: + self.top_block.auto_scale(False) + + def update_samp_rate(self): + sr = self.samp_rate_edit.text().toDouble()[0] + self.top_block.gui_snk.set_samp_rate(sr) + + nsamps = int(math.ceil((self.top_block._nrows+1)*self.top_block._ncols)) + self.top_block.reset(self._start, nsamps) + + def ncols_update(self): + n = self.ncols_edit.text().toDouble()[0] + self.top_block.gui_snk.set_num_cols(n) + self.top_block._ncols = n + + nsamps = int(math.ceil((self.top_block._nrows+1)*n)) + self.top_block.reset(self._start, nsamps) + + def nrows_update(self): + n = self.nrows_edit.text().toInt()[0] + self.top_block.gui_snk.set_num_rows(n) + self.top_block._nrows = n + + nsamps = int(math.ceil(self.top_block._ncols*(n+1))) + self.top_block.reset(self._start, nsamps) + + diff --git a/gr-qtgui/apps/qt_digital.py b/gr-qtgui/apps/qt_digital.py index 2bc039a31f..445d6d4fc1 100755 --- a/gr-qtgui/apps/qt_digital.py +++ b/gr-qtgui/apps/qt_digital.py @@ -20,7 +20,10 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr, blks2 +from gnuradio import gr, digital +from gnuradio import blocks +from gnuradio import filter +from gnuradio import channels from gnuradio import eng_notation import sys @@ -60,7 +63,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 +149,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,16 +177,19 @@ 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) + self.rrctaps = filter.firdes.root_raised_cosine(1, self.sps, 1, self.excess_bw, 21) + self.rx_rrc = filter.fir_filter_ccf(1, self.rrctaps) # Set up the carrier & clock recovery parameters @@ -194,31 +200,30 @@ 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 noise = self.get_noise_voltage(self.snr_dB) self.fo = 100/self.sample_rate() self.to = 1.0 - self.channel = gr.channel_model(noise, self.fo, self.to) + self.channel = channels.channel_model(noise, self.fo, self.to) - self.thr = gr.throttle(gr.sizeof_char, self._sample_rate) - self.snk_tx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, + self.thr = blocks.throttle(gr.sizeof_char, self._sample_rate) + self.snk_tx = qtgui.sink_c(fftsize, filter.firdes.WIN_BLACKMAN_hARRIS, 0, self._sample_rate*self.sps, "Tx", True, True, True, True) - self.snk_rx = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, + self.snk_rx = qtgui.sink_c(fftsize, filter.firdes.WIN_BLACKMAN_hARRIS, 0, self._sample_rate, "Rx", True, True, True, True) @@ -288,18 +293,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..0e0c8a177d 100755 --- a/gr-qtgui/apps/uhd_display.py +++ b/gr-qtgui/apps/uhd_display.py @@ -21,15 +21,16 @@ # from gnuradio import gr +from gnuradio import filter +from gnuradio import blocks 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: @@ -193,20 +194,20 @@ class my_top_block(gr.top_block): self._fftsize = options.fft_size self.snk = qtgui.sink_c(options.fft_size, - gr.firdes.WIN_BLACKMAN_hARRIS, + filter.firdes.WIN_BLACKMAN_hARRIS, self._freq, self._bandwidth, "UHD Display", True, True, True, False) # Set up internal amplifier - self.amp = gr.multiply_const_cc(0.0) + self.amp = blocks.multiply_const_cc(0.0) self.set_amplifier_gain(100) # Create a single-pole IIR filter to remove DC # but don't connect it yet self.dc_gain = 0.001 - self.dc = gr.single_pole_iir_filter_cc(self.dc_gain) - self.dc_sub = gr.sub_cc() + self.dc = filter.single_pole_iir_filter_cc(self.dc_gain) + self.dc_sub = blocks.sub_cc() self.connect(self.u, self.amp, self.snk) @@ -234,7 +235,7 @@ class my_top_block(gr.top_block): self.lock() # Add file sink to save data - self.file_sink = gr.file_sink(gr.sizeof_gr_complex, name) + self.file_sink = blocks.file_sink(gr.sizeof_gr_complex, name) self.connect(self.amp, self.file_sink) self.unlock() diff --git a/gr-qtgui/doc/qtgui.dox b/gr-qtgui/doc/qtgui.dox index 8664af163d..c3ba512107 100644 --- a/gr-qtgui/doc/qtgui.dox +++ b/gr-qtgui/doc/qtgui.dox @@ -22,6 +22,40 @@ by using: help(qtgui) \endcode +\subsection Blocks + +There are a number of available QTGUI blocks for different plotting +purposes. These include: + +\li Time Domain: x-axis is time, y-axis is amplitude. +\li Frequency Domain or PSD: x-axis is frequency, y-axis is magnitude +in dB. +\li Waterfall or spectrogram: x-axis is frequency, y-axis is time, +z-axis is intensity related to magnitude in dB. +\li Constellation: polar plot of real vs. imaginary. +\li Time Raster: time vs. time with the z-axis being intensity based +on value of the sample. +\li Combined Sink: combines time, frequency, waterfall, and +constellation plots into one widget. + +The time domain, frequency domain, and waterfall have both a complex +and a floating point block. The constellation plot only makes sense +with complex inputs. The time raster plots accept bits and floats. + +Because the time raster plots are designed to show structure over time +in a signal, frame, packet, etc., they never drop samples. This is a +fairly taxing job and performance can be an issue. Since it is +expected that this block will work on a frame or packet structure, we +tend to be at the lowest possible rate at this point, so that will +help. Expect performance issues at high data rates. + +Note: There seem to be extra performance issue with the raster +plotters in QWT version 5 that were fixed with QWT version 6. As such, +the time raster plots have incredibly poor performance with QWT5 to +the point of almost being unusable. In the future, we may restrict +compilation and installation of these plots only if QWT6 or higher is +discovered. For now, just be aware of this limitation. + \section Dependencies @@ -51,7 +85,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): @@ -70,8 +104,15 @@ def main(): tb.start() qapp.exec_() tb.stop() - - \endcode +There are graphical controls in all but the combined plotting +tools. In the margins of the GUIs (that is, not on the canvas showing +the signal itself), right-clicking the mouse will pull up a drop-down +menu that will allow you to change difference parameters of the +plots. These include things like the look of the lines (width, color, +style, markers, etc.), the ability to start and stop the display, the +ability to save to a file, and other plot-specific controls (FFT size +for the frequency and waterfall plots, etc.). + */ diff --git a/gr-qtgui/examples/dark.qss b/gr-qtgui/examples/dark.qss new file mode 100644 index 0000000000..77a3756733 --- /dev/null +++ b/gr-qtgui/examples/dark.qss @@ -0,0 +1,115 @@ +QWidget +{ + color: white; + background-color: black; +} + +QwtPlot +{ + padding: 10px; + font-size: 18px; +} + +DisplayPlot { + qproperty-zoomer_color: green; + qproperty-line_color1: green; + qproperty-line_color2: green; + qproperty-line_color3: green; + qproperty-line_style1: SolidLine; + qproperty-line_style2: DashLine; + qproperty-line_style3: DotLine; + qproperty-axes_label_font_size: 18; +} + +WaterfallDisplayPlot { + qproperty-intensity_color_map_type1: 1; + qproperty-low_intensity_color: black; + qproperty-high_intensity_color: green; +} + +FrequencyDisplayPlot { + qproperty-max_fft_color: palegreen; + qproperty-min_fft_color: palegreen; + qproperty-marker_lower_intensity_color: white; + qproperty-marker_upper_intensity_color: red; + qproperty-marker_lower_intensity_visible: false; + qproperty-marker_upper_intensity_visible: false; + qproperty-marker_noise_floor_amplitude_color: red; + qproperty-marker_noise_floor_amplitude_visible: false; +} + +QwtPlotCanvas +{ + border: 1px solid White; + border-radius: 10px; +} + +QwtScaleWidget +{ + font-size: 14px; +} + +QwtLegend +{ + padding: 3px; + margin: 3px; + font-size: 14px; +} + +QwtLegendItem +{ + padding: 2px; + margin: 2px; +} + +QLineEdit, +QPlainTextEdit, +QComboBox, +QMenu { + selection-color: black; + selection-background-color: lightgrey; + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 1 rgb(128, 128, 128)); + border: 1px solid gray; + padding: 2px; + border-radius: 3px; +} + +QPushButton, +QToolButton { + selection-color: black; + selection-background-color: white; + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 black, stop: 1 rgb(128, 128, 128)); + border: 1px solid gray; + padding: 2px; + border-radius: 3px; +} + +QTabWidget::pane { + border: 1px solid white; +} + +QTabBar { + margin: 20px; + padding: 20px; +} + +QTabBar::tab { + background-color: rgb(100, 100, 100); + border: 1px solid white; + padding: 5px; +} + +QTabBar::tab:first { + border-top-left-radius: 4px; +} + +QTabBar::tab:last { + border-top-right-radius: 4px; +} + +QTabBar::tab:selected { + background-color: rgb(200, 200, 200); + color: black; +} + + diff --git a/gr-qtgui/examples/pyqt_const_c.py b/gr-qtgui/examples/pyqt_const_c.py new file mode 100755 index 0000000000..3a43bf9a92 --- /dev/null +++ b/gr-qtgui/examples/pyqt_const_c.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# +# 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. +# + +from gnuradio import gr, filter +from gnuradio import blocks +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + sys.exit(1) + +try: + from gnuradio import channels +except ImportError: + sys.stderr.write("Error: Program requires gr-channels.\n") + 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 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f1, 0.5, 0) + src2 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f2, 0.5, 0) + src = blocks.add_cc() + channel = channels.channel_model(0.001) + thr = blocks.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_example_c.py b/gr-qtgui/examples/pyqt_example_c.py index 0b43fce0e5..cb708360f1 100755 --- a/gr-qtgui/examples/pyqt_example_c.py +++ b/gr-qtgui/examples/pyqt_example_c.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 Free Software Foundation, Inc. +# Copyright 2011,2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -20,7 +20,8 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr +from gnuradio import gr, filter +from gnuradio import blocks import sys try: @@ -28,7 +29,19 @@ try: from PyQt4 import QtGui, QtCore import sip except ImportError: - print "Error: Program requires PyQt4 and gr-qtgui." + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + sys.exit(1) + +try: + from gnuradio import channels +except ImportError: + sys.stderr.write("Error: Program requires gr-channels.\n") sys.exit(1) class dialog_box(QtGui.QWidget): @@ -138,13 +151,17 @@ class my_top_block(gr.top_block): fftsize = 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.001) - thr = gr.throttle(gr.sizeof_gr_complex, 100*fftsize) - self.snk1 = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, + ss = open('dark.qss') + sstext = ss.read() + ss.close() + self.qapp.setStyleSheet(sstext) + + src1 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_cc() + channel = channels.channel_model(0.001) + thr = blocks.throttle(gr.sizeof_gr_complex, 100*fftsize) + self.snk1 = qtgui.sink_c(fftsize, filter.firdes.WIN_BLACKMAN_hARRIS, 0, Rs, "Complex Signal Example", True, True, True, False) diff --git a/gr-qtgui/examples/pyqt_example_f.py b/gr-qtgui/examples/pyqt_example_f.py index d00011f403..5bd582d1e1 100755 --- a/gr-qtgui/examples/pyqt_example_f.py +++ b/gr-qtgui/examples/pyqt_example_f.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 Free Software Foundation, Inc. +# Copyright 2011,2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -20,7 +20,8 @@ # Boston, MA 02110-1301, USA. # -from gnuradio import gr +from gnuradio import gr, filter +from gnuradio import blocks import sys try: @@ -28,7 +29,13 @@ try: from PyQt4 import QtGui, QtCore import sip except ImportError: - print "Error: Program requires PyQt4 and gr-qtgui." + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") sys.exit(1) class dialog_box(QtGui.QWidget): @@ -139,13 +146,13 @@ class my_top_block(gr.top_block): 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*fftsize) - noise = gr.noise_source_f(gr.GR_GAUSSIAN, 0.001) - add = gr.add_ff() - self.snk1 = qtgui.sink_f(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, + src1 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_ff() + thr = blocks.throttle(gr.sizeof_float, 100*fftsize) + noise = analog.noise_source_f(analog.GR_GAUSSIAN, 0.001) + add = blocks.add_ff() + self.snk1 = qtgui.sink_f(fftsize, filter.firdes.WIN_BLACKMAN_hARRIS, 0, Rs, "Float Signal Example", True, True, True, False) diff --git a/gr-qtgui/examples/pyqt_freq_c.py b/gr-qtgui/examples/pyqt_freq_c.py new file mode 100755 index 0000000000..7632176878 --- /dev/null +++ b/gr-qtgui/examples/pyqt_freq_c.py @@ -0,0 +1,194 @@ +#!/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, filter +from gnuradio import blocks +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + sys.exit(1) + +try: + from gnuradio import channels +except ImportError: + sys.stderr.write("Error: Program requires gr-channels.\n") + 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) + ss = open('dark.qss') + sstext = ss.read() + ss.close() + self.qapp.setStyleSheet(sstext) + + src1 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_cc() + channel = channels.channel_model(0.01) + thr = blocks.throttle(gr.sizeof_gr_complex, 100*npts) + self.snk1 = qtgui.freq_sink_c(npts, filter.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..668b54d249 --- /dev/null +++ b/gr-qtgui/examples/pyqt_freq_f.py @@ -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, filter +from gnuradio import blocks +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + 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 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_ff() + thr = blocks.throttle(gr.sizeof_float, 100*npts) + self.snk1 = qtgui.freq_sink_f(npts, filter.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..ae8ebfc257 100755 --- a/gr-qtgui/examples/pyqt_time_c.py +++ b/gr-qtgui/examples/pyqt_time_c.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 Free Software Foundation, Inc. +# Copyright 2011,2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -21,6 +21,7 @@ # from gnuradio import gr +from gnuradio import blocks import sys try: @@ -28,7 +29,19 @@ try: from PyQt4 import QtGui, QtCore import sip except ImportError: - print "Error: Program requires PyQt4 and gr-qtgui." + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + sys.exit(1) + +try: + from gnuradio import channels +except ImportError: + sys.stderr.write("Error: Program requires gr-channels.\n") sys.exit(1) class dialog_box(QtGui.QWidget): @@ -138,20 +151,24 @@ class my_top_block(gr.top_block): 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) + ss = open('dark.qss') + sstext = ss.read() + ss.close() + self.qapp.setStyleSheet(sstext) + + src1 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_cc() + channel = channels.channel_model(0.01) + thr = blocks.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) @@ -165,18 +182,20 @@ class my_top_block(gr.top_block): pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) # Example of using signal/slot to set the title of a curve - pyWin.connect(pyWin, QtCore.SIGNAL("setTitle(int, QString)"), - 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}") + pyWin.connect(pyWin, QtCore.SIGNAL("setLineLabel(int, QString)"), + pyWin, QtCore.SLOT("setLineLabel(int, QString)")) + pyWin.emit(QtCore.SIGNAL("setLineLabel(int, QString)"), 0, "Re{sum}") + self.snk1.set_line_label(1, "Im{Sum}") + #self.snk1.set_line_label(2, "Re{src1}") + #self.snk1.set_line_label(3, "Im{src1}") + #self.snk1.set_line_label(4, "Re{src2}") + #self.snk1.set_line_label(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_time_f.py b/gr-qtgui/examples/pyqt_time_f.py index 464d8939b0..b733a5a809 100755 --- a/gr-qtgui/examples/pyqt_time_f.py +++ b/gr-qtgui/examples/pyqt_time_f.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 Free Software Foundation, Inc. +# Copyright 2011,2012 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -21,6 +21,7 @@ # from gnuradio import gr +from gnuradio import blocks import sys try: @@ -28,7 +29,13 @@ try: from PyQt4 import QtGui, QtCore import sip except ImportError: - print "Error: Program requires PyQt4 and gr-qtgui." + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") sys.exit(1) class dialog_box(QtGui.QWidget): @@ -139,12 +146,12 @@ class my_top_block(gr.top_block): 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) - noise = gr.noise_source_f(gr.GR_GAUSSIAN, 0.001) - add = gr.add_ff() + src1 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_ff() + thr = blocks.throttle(gr.sizeof_float, 100*npts) + noise = analog.noise_source_f(analog.GR_GAUSSIAN, 0.001) + add = blocks.add_ff() self.snk1 = qtgui.time_sink_f(npts, Rs, "Complex Time Example", 3) @@ -168,11 +175,11 @@ class my_top_block(gr.top_block): pyWin = sip.wrapinstance(pyQt, QtGui.QWidget) # Example of using signal/slot to set the title of a curve - pyWin.connect(pyWin, QtCore.SIGNAL("setTitle(int, QString)"), - pyWin, QtCore.SLOT("setTitle(int, QString)")) - pyWin.emit(QtCore.SIGNAL("setTitle(int, QString)"), 0, "sum") - self.snk1.set_title(1, "src1") - self.snk1.set_title(2, "src2") + pyWin.connect(pyWin, QtCore.SIGNAL("setLineLabel(int, QString)"), + pyWin, QtCore.SLOT("setLineLabel(int, QString)")) + pyWin.emit(QtCore.SIGNAL("setLineLabel(int, QString)"), 0, "sum") + self.snk1.set_line_label(1, "src1") + self.snk1.set_line_label(2, "src2") # Can also set the color of a curve #self.snk1.set_color(5, "blue") diff --git a/gr-qtgui/examples/pyqt_time_raster_b.py b/gr-qtgui/examples/pyqt_time_raster_b.py new file mode 100755 index 0000000000..b29403819d --- /dev/null +++ b/gr-qtgui/examples/pyqt_time_raster_b.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import blocks +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): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + + self.resize(800, 500) + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self.qapp = QtGui.QApplication(sys.argv) + + data0 = 10*[0,] + 40*[1,0] + 10*[0,] + data0 += 10*[0,] + 40*[0,1] + 10*[0,] + data1 = 20*[0,] + [0,0,0,1,1,1,0,0,0,0] + 70*[0,] + + # Adjust these to change the layout of the plot. + # Can be set to fractions. + ncols = 100.25 + nrows = 100 + + fs = 200 + src0 = gr.vector_source_b(data0, True) + src1 = gr.vector_source_b(data1, True) + thr = blocks.throttle(gr.sizeof_char, 50000) + head = gr.head(gr.sizeof_char, 10000000) + self.snk1 = qtgui.time_raster_sink_b(fs, nrows, ncols, [], [], + "Time Raster Example", 2) + + self.connect(src0, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + + # 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.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_raster_f.py b/gr-qtgui/examples/pyqt_time_raster_f.py new file mode 100755 index 0000000000..957e694b38 --- /dev/null +++ b/gr-qtgui/examples/pyqt_time_raster_f.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# Copyright 2012,2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr +from gnuradio import blocks +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): + QtGui.QWidget.__init__(self, None) + self.setWindowTitle('PyQt Test GUI') + + self.boxlayout = QtGui.QBoxLayout(QtGui.QBoxLayout.LeftToRight, self) + self.boxlayout.addWidget(display, 1) + + self.resize(800, 500) + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + self.qapp = QtGui.QApplication(sys.argv) + + data0 = 10*[0,] + 40*[1,0] + 10*[0,] + data0 += 10*[0,] + 40*[0,1] + 10*[0,] + data1 = 20*[0,] + [0,0,0,1,1,1,0,0,0,0] + 70*[0,] + + # Adjust these to change the layout of the plot. + # Can be set to fractions. + ncols = 100.25 + nrows = 100 + + fs = 200 + src0 = gr.vector_source_f(data0, True) + src1 = gr.vector_source_f(data1, True) + thr = blocks.throttle(gr.sizeof_float, 50000) + hed = gr.head(gr.sizeof_float, 10000000) + self.snk1 = qtgui.time_raster_sink_f(fs, nrows, ncols, [], [], + "Float Time Raster Example", 2) + + self.connect(src0, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + + # 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.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_c.py b/gr-qtgui/examples/pyqt_waterfall_c.py new file mode 100755 index 0000000000..118a3df660 --- /dev/null +++ b/gr-qtgui/examples/pyqt_waterfall_c.py @@ -0,0 +1,196 @@ +#!/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, filter +from gnuradio import blocks +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + sys.exit(1) + +try: + from gnuradio import channels +except ImportError: + sys.stderr.write("Error: Program requires gr-channels.\n") + 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 = 2000 + + npts = 2048 + + taps = filter.firdes.complex_band_pass_2(1, Rs, 1500, 2500, 100, 60) + + self.qapp = QtGui.QApplication(sys.argv) + ss = open('dark.qss') + sstext = ss.read() + ss.close() + self.qapp.setStyleSheet(sstext) + + src1 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_c(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_cc() + channel = channels.channel_model(0.01) + thr = blocks.throttle(gr.sizeof_gr_complex, 100*npts) + filt = filter.fft_filter_ccc(1, taps) + self.snk1 = qtgui.waterfall_sink_c(npts, filter.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Complex Waterfall Example", 2) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, channel, thr, (self.snk1, 0)) + self.connect(thr, filt, (self.snk1, 1)) + + 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..f9680c28cf --- /dev/null +++ b/gr-qtgui/examples/pyqt_waterfall_f.py @@ -0,0 +1,182 @@ +#!/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, filter +from gnuradio import blocks +import sys + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + sys.stderr.write("Error: Program requires PyQt4 and gr-qtgui.\n") + sys.exit(1) + +try: + from gnuradio import analog +except ImportError: + sys.stderr.write("Error: Program requires gr-analog.\n") + 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 = 2000 + + npts = 2048 + + self.qapp = QtGui.QApplication(sys.argv) + + src1 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f1, 0.1, 0) + src2 = analog.sig_source_f(Rs, analog.GR_SIN_WAVE, f2, 0.1, 0) + src = blocks.add_ff() + thr = blocks.throttle(gr.sizeof_float, 100*npts) + self.snk1 = qtgui.waterfall_sink_f(npts, filter.firdes.WIN_BLACKMAN_hARRIS, + 0, Rs, + "Real Waterfall Example", 2) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, thr, (self.snk1, 0)) + self.connect(src1, (self.snk1, 1)) + + 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..3eaaf49295 --- /dev/null +++ b/gr-qtgui/grc/qtgui_block_tree.xml @@ -0,0 +1,49 @@ +<?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> + <block>qtgui_time_raster_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..c751c89437 --- /dev/null +++ b/gr-qtgui/grc/qtgui_const_sink_x.xml @@ -0,0 +1,107 @@ +<?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).set_update_time($update_time) +self.$(id).set_y_axis($ymin, $ymax) +self.$(id).set_x_axis($xmin, $xmax) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_resize($width, $height)</callback> + <callback>set_update_time($update_time)</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>Y min</name> + <key>ymin</key> + <value>-2</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>Y max</name> + <key>ymax</key> + <value>2</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>X min</name> + <key>xmin</key> + <value>-2</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>X max</name> + <key>xmax</key> + <value>2</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>real</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..35edf61ed8 --- /dev/null +++ b/gr-qtgui/grc/qtgui_freq_sink_x.xml @@ -0,0 +1,139 @@ +<?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.filter 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).set_update_time($update_time) +self.$(id).set_y_axis($ymin, $ymax) +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($update_time)</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>Y min</name> + <key>ymin</key> + <value>-140</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>Y max</name> + <key>ymax</key> + <value>10</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>real</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 6488ac04cd..6e13481397 100644 --- a/gr-qtgui/grc/qtgui_sink_x.xml +++ b/gr-qtgui/grc/qtgui_sink_x.xml @@ -7,10 +7,9 @@ <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.gr import firdes</import> + <import>from gnuradio import qtgui</import> + <import>from gnuradio.filter import firdes</import> <import>import sip</import> <make>#set $win = 'self._%s_win'%$id qtgui.$(type.fcn)( @@ -24,9 +23,18 @@ qtgui.$(type.fcn)( $plottime, \#plottime $plotconst, \#plotconst ) -self.$(id).set_update_time(1.0 / $rate) +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> @@ -92,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> @@ -135,10 +149,11 @@ $(gui_hint()($win))</make> <hide>part</hide> </param> <param> - <name>Update Rate</name> - <key>rate</key> - <value>10</value> - <type>real</type> + <name>Clicked freq variable</name> + <key>freqchangevar</key> + <value>None</value> + <type>raw</type> + <hide>part</hide> </param> <sink> <name>in</name> 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_raster_x.xml b/gr-qtgui/grc/qtgui_time_raster_x.xml new file mode 100644 index 0000000000..7d880ab084 --- /dev/null +++ b/gr-qtgui/grc/qtgui_time_raster_x.xml @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Sink +################################################### + --> +<block> + <name>QT GUI Time Raster Sink</name> + <key>qtgui_time_raster_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)( + $samp_rate, + $nrows, + $ncols, + $mult, + $offset, + $name, + $nconnections, + ) +self.$(id).set_update_time($update_time) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_num_rows($nrows)</callback> + <callback>set_num_cols($ncols)</callback> + <callback>set_multiplier($mult)</callback> + <callback>set_offset($offset)</callback> + <callback>set_update_time($update_time)</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>Byte</name><key>byte</key><opt>fcn:time_raster_sink_b</opt></option> + <option><name>Float</name><key>float</key><opt>fcn:time_raster_sink_f</opt></option> + </param> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>Sample Rate</name> + <key>samp_rate</key> + <value>samp_rate</value> + <type>real</type> + </param> + <param> + <name>Num. Rows</name> + <key>nrows</key> + <type>int</type> + </param> + <param> + <name>Num. Cols</name> + <key>ncols</key> + <type>int</type> + </param> + <param> + <name>Multiplier</name> + <key>mult</key> + <value>[]</value> + <type>real_vector</type> + <hide>part</hide> + </param> + <param> + <name>Offset</name> + <key>offset</key> + <value>[]</value> + <type>real_vector</type> + <hide>part</hide> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>real</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_time_sink_x.xml b/gr-qtgui/grc/qtgui_time_sink_x.xml index 9c8da6fbcf..d64ce2e237 100644 --- a/gr-qtgui/grc/qtgui_time_sink_x.xml +++ b/gr-qtgui/grc/qtgui_time_sink_x.xml @@ -7,24 +7,27 @@ <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.gr import firdes</import> + <import>from gnuradio import qtgui</import> + <import>from gnuradio.filter import firdes</import> <import>import sip</import> <make>#set $win = 'self._%s_win'%$id qtgui.$(type.fcn)( $size, \#size - $bw, \#bw + $srate, \#samp_rate $name, \#name $nconnections \#number of inputs ) +self.$(id).set_update_time($update_time) +self.$(id).set_y_axis($ymin, $ymax) self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) $(gui_hint()($win))</make> <callback>set_time_domain_axis($min, $max)</callback> - <callback>set_update_time($t)</callback> + <callback>set_update_time($update_time)</callback> <callback>set_title($which, $title)</callback> <callback>set_color($which, $color)</callback> + <callback>set_y_axis($ymin, $ymax)</callback> + <callback>set_samp_rate($srate)</callback> <param> <name>Type</name> <key>type</key> @@ -46,12 +49,26 @@ $(gui_hint()($win))</make> <type>int</type> </param> <param> - <name>Bandwidth (Hz)</name> - <key>bw</key> + <name>Sample Rate</name> + <key>srate</key> <value>samp_rate</value> <type>real</type> </param> <param> + <name>Y min</name> + <key>ymin</key> + <value>-1</value> + <type>real</type> + <hide>part</hide> + </param> + <param> + <name>Y max</name> + <key>ymax</key> + <value>1</value> + <type>real</type> + <hide>part</hide> + </param> + <param> <name>Number of Inputs</name> <key>nconnections</key> <value>1</value> @@ -59,6 +76,13 @@ $(gui_hint()($win))</make> <hide>part</hide> </param> <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>real</type> + <hide>part</hide> + </param> + <param> <name>GUI Hint</name> <key>gui_hint</key> <value></value> 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..176a907e9e --- /dev/null +++ b/gr-qtgui/grc/qtgui_waterfall_sink_x.xml @@ -0,0 +1,124 @@ +<?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.filter 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).set_update_time($update_time) +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($update_time)</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>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>real</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/include/CMakeLists.txt b/gr-qtgui/include/qtgui/CMakeLists.txt index ef4cb11752..f3ef6808e8 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,19 @@ # 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 + time_raster_sink_b.h + time_raster_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..e0c358bcad --- /dev/null +++ b/gr-qtgui/include/qtgui/const_sink_c.h @@ -0,0 +1,97 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <filter/firdes.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=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_y_axis(double min, double max) = 0; + virtual void set_x_axis(double min, double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &label) = 0; + virtual void set_line_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, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_autoscale(bool en) = 0; + virtual int nsamps() const = 0; + virtual void reset() = 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..79d3f2e8f0 --- /dev/null +++ b/gr-qtgui/include/qtgui/freq_sink_c.h @@ -0,0 +1,112 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <filter/firdes.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=1, + 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_fft_window(const gr::filter::firdes::win_type win) = 0; + virtual gr::filter::firdes::win_type fft_window() = 0; + + virtual void set_frequency_range(const double centerfreq, const double bandwidth) = 0; + virtual void set_y_axis(double min, double max) = 0; + + virtual void set_update_time(double t) = 0; + + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &label) = 0; + virtual void set_line_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, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 0; + virtual void enable_autoscale(bool en=true) = 0; + virtual void reset() = 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..131801f531 --- /dev/null +++ b/gr-qtgui/include/qtgui/freq_sink_f.h @@ -0,0 +1,111 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <filter/firdes.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=1, + 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_fft_window(const gr::filter::firdes::win_type win) = 0; + virtual gr::filter::firdes::win_type fft_window() = 0; + + virtual void set_frequency_range(const double centerfreq, const double bandwidth) = 0; + virtual void set_y_axis(double min, double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &label) = 0; + virtual void set_line_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, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 0; + virtual void enable_autoscale(bool en=true) = 0; + virtual void reset() = 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..45b980686c --- /dev/null +++ b/gr-qtgui/include/qtgui/sink_c.h @@ -0,0 +1,97 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_block.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..c2bed902d9 --- /dev/null +++ b/gr-qtgui/include/qtgui/sink_f.h @@ -0,0 +1,97 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_block.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_raster_sink_b.h b/gr-qtgui/include/qtgui/time_raster_sink_b.h new file mode 100644 index 0000000000..132ab9715c --- /dev/null +++ b/gr-qtgui/include/qtgui/time_raster_sink_b.h @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_B_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_B_H + +#include <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * time_raster plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink that takes in byte + * streams and plots a time_raster (spectrogram) plot. + * + * Input stream: This expects a bit stream (0, 1 in the LSB of a + * byte). It will display packed bytes but the display will have + * to be autoscaled. + */ + class QTGUI_API time_raster_sink_b : virtual public gr_sync_block + { + public: + // gr::qtgui::time_raster_sink_b::sptr + typedef boost::shared_ptr<time_raster_sink_b> sptr; + + /*! + * \brief Build a bit time raster sink. + * + * \param samp_rate sample rate of signal + * \param cols number of cols to plot + * \param rows number of rows to plot + * \param mult vector of floats as a scaling multiplier for each input stream + * \param offset vector of floats as an offset for each input stream + * \param name title for the plot + * \param nconnections number of streams connected + * \param parent a QWidget parent object, if any + */ + static sptr make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &lable) = 0; + virtual void set_line_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_line_alpha(int which, double alpha) = 0; + virtual void set_color_map(int which, const int color) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + virtual int color_map(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void set_samp_rate(const double samp_rate) = 0; + virtual void set_num_rows(double rows) = 0; + virtual void set_num_cols(double cols) = 0; + + virtual double num_rows() = 0; + virtual double num_cols() = 0; + + virtual void set_multiplier(const std::vector<float> &mult) = 0; + virtual void set_offset(const std::vector<float> &offset) = 0; + + virtual void set_intensity_range(float min, float max) = 0; + + virtual void enable_menu(bool en) = 0; + virtual void enable_grid(bool en) = 0; + virtual void enable_autoscale(bool en) = 0; + virtual void reset() = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_SINK_B_H */ diff --git a/gr-qtgui/include/qtgui/time_raster_sink_f.h b/gr-qtgui/include/qtgui/time_raster_sink_f.h new file mode 100644 index 0000000000..7215373bec --- /dev/null +++ b/gr-qtgui/include/qtgui/time_raster_sink_f.h @@ -0,0 +1,117 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_F_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_F_H + +#include <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display multiple signals on a + * time_raster plot. + * \ingroup qtgui_blk + * + * This is a QT-based graphical sink that takes set of a floating + * point streams and plots a time_raster (spectrogram) plot. + * + */ + class QTGUI_API time_raster_sink_f : virtual public gr_sync_block + { + public: + // gr::qtgui::time_raster_sink_f::sptr + typedef boost::shared_ptr<time_raster_sink_f> sptr; + + /*! + * \brief Build a floating point time raster sink. + * + * \param samp_rate sample rate of signal + * \param cols number of cols to plot + * \param rows number of rows to plot + * \param mult vector of floats as a scaling multiplier for each input stream + * \param offset vector of floats as an offset for each input stream + * \param name title for the plot + * \param nconnections number of streams connected + * \param parent a QWidget parent object, if any + */ + static sptr make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &lable) = 0; + virtual void set_line_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_line_alpha(int which, double alpha) = 0; + virtual void set_color_map(int which, const int color) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + virtual int color_map(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void set_samp_rate(const double samp_rate) = 0; + virtual void set_num_rows(double rows) = 0; + virtual void set_num_cols(double cols) = 0; + + virtual double num_rows() = 0; + virtual double num_cols() = 0; + + virtual void set_multiplier(const std::vector<float> &mult) = 0; + virtual void set_offset(const std::vector<float> &offset) = 0; + + virtual void set_intensity_range(float min, float max) = 0; + + virtual void enable_menu(bool en) = 0; + virtual void enable_grid(bool en) = 0; + virtual void enable_autoscale(bool en) = 0; + virtual void reset() = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_TIME_RASTER_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..82ab27d443 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_sink_c.h @@ -0,0 +1,105 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.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 samp_rate sample rate (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 samp_rate, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_y_axis(double min, double max) = 0; + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &label) = 0; + virtual void set_line_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, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + virtual void set_samp_rate(const double samp_rate) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 0; + virtual void enable_autoscale(bool en=true) = 0; + virtual void enable_stem_plot(bool en=true) = 0; + virtual void enable_semilogx(bool en=true) = 0; + virtual void enable_semilogy(bool en=true) = 0; + virtual int nsamps() const = 0; + virtual void reset() = 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..6c0788f715 --- /dev/null +++ b/gr-qtgui/include/qtgui/time_sink_f.h @@ -0,0 +1,103 @@ +/* -*- 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 <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.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 samp_rate sample rate (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 samp_rate, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_y_axis(double min, double max) = 0; + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &line) = 0; + virtual void set_line_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, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_nsamps(const int newsize) = 0; + virtual void set_samp_rate(const double samp_rate) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 0; + virtual void enable_autoscale(bool en=true) = 0; + virtual void enable_stem_plot(bool en=true) = 0; + virtual void enable_semilogx(bool en=true) = 0; + virtual void enable_semilogy(bool en=true) = 0; + virtual int nsamps() const = 0; + virtual void reset() = 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..5bc921ccb3 --- /dev/null +++ b/gr-qtgui/include/qtgui/waterfall_sink_c.h @@ -0,0 +1,118 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <filter/firdes.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 nconnections number of signals to be connected to the sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, int wintype, + double fc, double bw, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void clear_data() = 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_fft_window(const gr::filter::firdes::win_type win) = 0; + virtual gr::filter::firdes::win_type fft_window() = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + virtual void set_intensity_range(const double min, + const double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &line) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + virtual void set_color_map(int which, const int color) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual double line_alpha(int which) = 0; + virtual int color_map(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void auto_scale() = 0; + virtual double min_intensity(int which) = 0; + virtual double max_intensity(int which) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 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..98a434e668 --- /dev/null +++ b/gr-qtgui/include/qtgui/waterfall_sink_f.h @@ -0,0 +1,117 @@ +/* -*- 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 <Python.h> +#include <qtgui/api.h> +#include <gr_sync_block.h> +#include <qapplication.h> +#include <filter/firdes.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 nconnections number of signals to be connected to the sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, int wintype, + double fc, double bw, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void clear_data() = 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_fft_window(const gr::filter::firdes::win_type win) = 0; + virtual gr::filter::firdes::win_type fft_window() = 0; + + virtual void set_frequency_range(const double centerfreq, + const double bandwidth) = 0; + virtual void set_intensity_range(const double min, + const double max) = 0; + + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &line) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + virtual void set_color_map(int which, const int color) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual double line_alpha(int which) = 0; + virtual int color_map(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void auto_scale() = 0; + virtual double min_intensity(int which) = 0; + virtual double max_intensity(int which) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 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 955db49e72..ff1e64ce31 100644 --- a/gr-qtgui/lib/CMakeLists.txt +++ b/gr-qtgui/lib/CMakeLists.txt @@ -22,9 +22,17 @@ ######################################################################## set(qtgui_moc_hdrs spectrumdisplayform.h + displayform.h timedisplayform.h + timerasterdisplayform.h + freqdisplayform.h + constellationdisplayform.h + waterfalldisplayform.h + form_menus.h + DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h + TimeRasterDisplayPlot.h WaterfallDisplayPlot.h ConstellationDisplayPlot.h ) @@ -41,20 +49,36 @@ endif(NOT EXISTS ${spectrum_ui_hdr}) set(qtgui_srcs ${qtgui_moc_srcs} ${qtgui_ui_hdrs} + DisplayPlot.cc FrequencyDisplayPlot.cc TimeDomainDisplayPlot.cc + TimeRasterDisplayPlot.cc + timeRasterGlobalData.cc WaterfallDisplayPlot.cc waterfallGlobalData.cc ConstellationDisplayPlot.cc spectrumdisplayform.cc + displayform.cc timedisplayform.cc + timerasterdisplayform.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 + plot_raster.cc + sink_c_impl.cc + sink_f_impl.cc + time_sink_c_impl.cc + time_sink_f_impl.cc + time_raster_sink_b_impl.cc + time_raster_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 ) @@ -77,31 +101,45 @@ endif(MSVC) ######################################################################## include_directories( ${GR_QTGUI_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} ${GNURADIO_CORE_INCLUDE_DIRS} ${GRUEL_INCLUDE_DIRS} + ${VOLK_INCLUDE_DIRS} ${QWT_INCLUDE_DIRS} ${QT_INCLUDE_DIRS} + ${FFTW3F_INCLUDE_DIRS} ${LOG4CPP_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ) link_directories( - ${QWT_LIBRARY_DIRS} - ${LOG4CPP_LIBRARY_DIRS} - ${Boost_LIBRARY_DIRS} + ${QWT_LIBRARY_DIRS} + ${FFTW3F_LIBRARY_DIRS} + ${LOG4CPP_LIBRARY_DIRS} + ${Boost_LIBRARY_DIRS} ) include_directories(${PYTHON_INCLUDE_PATH}) #deprecated for dirs (cmake 2.6) +if(ENABLE_GR_CTRLPORT) + ADD_DEFINITIONS(-DGR_CTRLPORT) + include_directories(${ICE_INCLUDE_DIR}) +endif(ENABLE_GR_CTRLPORT) + ######################################################################## # Setup library ######################################################################## list(APPEND qtgui_libs gnuradio-core + gnuradio-fft + gnuradio-filter + volk ${QWT_LIBRARIES} ${QT_LIBRARIES} ${PYTHON_LIBRARIES} + ${FFTW3F_LIBRARIES} ${LOG4CPP_LIBRARIES} ) @@ -114,14 +152,24 @@ GR_LIBRARY_FOO(gnuradio-qtgui RUNTIME_COMPONENT "qtgui_runtime" DEVEL_COMPONENT # Install the header files ######################################################################## install(FILES + DisplayPlot.h FrequencyDisplayPlot.h TimeDomainDisplayPlot.h + TimeRasterDisplayPlot.h + timeRasterGlobalData.h WaterfallDisplayPlot.h waterfallGlobalData.h ConstellationDisplayPlot.h plot_waterfall.h + plot_raster.h spectrumdisplayform.h + displayform.h timedisplayform.h + timerasterdisplayform.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..f3951534a9 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,65 +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); - setAxisTitle(QwtPlot::xBottom, "In-phase"); - - setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); - 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()); @@ -118,65 +76,73 @@ 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()); + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + set_xaxis(-2.0, 2.0); + setAxisTitle(QwtPlot::xBottom, "In-phase"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + set_yaxis(-2.0, 2.0); + setAxisTitle(QwtPlot::yLeft, "Quadrature"); + updateAxes(); + + 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); + setXaxis(min, max); } void ConstellationDisplayPlot::set_yaxis(double min, double max) { - setAxisScale(QwtPlot::yLeft, min, max); + setYaxis(min, max); } void @@ -187,69 +153,104 @@ 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)); + } + + if(_autoscale_state) { + double bottom=1e20, top=-1e20; + for(int n = 0; n < _nplots; n++) { + for(int64_t point = 0; point < numDataPoints; point++) { + double b = std::min(realDataPoints[n][point], imagDataPoints[n][point]); + double t = std::max(realDataPoints[n][point], imagDataPoints[n][point]); + if(b < bottom) { + bottom = b; + } + if(t > top) { + top = t; + } + } + } + _autoScale(bottom, top); + } + + replot(); - replot(); - - _lastReplot = gruel::high_res_timer_now(); + } } } void -ConstellationDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on) +ConstellationDisplayPlot::plotNewData(const double* realDataPoints, + const double* imagDataPoints, + const int64_t numDataPoints, + const double timeInterval) { - plotItem->setVisible(!on); + std::vector<double*> vecRealDataPoints; + std::vector<double*> vecImagDataPoints; + vecRealDataPoints.push_back((double*)realDataPoints); + vecImagDataPoints.push_back((double*)imagDataPoints); + plotNewData(vecRealDataPoints, vecImagDataPoints, + numDataPoints, timeInterval); } void -ConstellationDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +ConstellationDisplayPlot::_autoScale(double bottom, double top) { - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); + // Auto scale the x- and y-axis with a margin of 20% + double b = bottom - fabs(bottom)*0.20; + double t = top + fabs(top)*0.20; + set_axis(b, t, b, t); } void -ConstellationDisplayPlot::OnPickerPointSelected6(const QPointF & p) +ConstellationDisplayPlot::setAutoScale(bool state) { - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); + _autoscale_state = state; } #endif /* CONSTELLATION_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.h b/gr-qtgui/lib/ConstellationDisplayPlot.h index 9e0f6f26a8..b68b01f6d8 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 double* realDataPoints, + 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,15 @@ 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: + void setAutoScale(bool state); private: - QwtPlotCurve* _plot_curve; - - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - - QwtDblClickPlotPicker *_picker; - - double* _realDataPoints; - double* _imagDataPoints; + void _autoScale(double bottom, double top); - 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..1c9de3d0ae --- /dev/null +++ b/gr-qtgui/lib/DisplayPlot.cc @@ -0,0 +1,416 @@ +/* -*- 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> +#include <QDebug> + +DisplayPlot::DisplayPlot(int nplots, QWidget* parent) + : QwtPlot(parent), _nplots(nplots), _stop(false) +{ + qRegisterMetaType<QColorList>("QColorList"); + resize(parent->width(), parent->height()); + + _autoscale_state = false; + + // 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 + + QColor default_palette_color = QColor("white"); + setPaletteColor(default_palette_color); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton, Qt::ControlModifier); + + // 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); + if(!_autoscale_state) + _zoomer->setZoomBase(); +} + +void +DisplayPlot::setXaxis(double min, double max) +{ + setAxisScale(QwtPlot::xBottom, min, max); + _zoomer->setZoomBase(); +} + +void +DisplayPlot::setLineLabel(int which, QString label) +{ + _plot_curve[which]->setTitle(label); +} + +QString +DisplayPlot::getLineLabel(int which) +{ + return _plot_curve[which]->title().text(); +} + +void +DisplayPlot::setLineColor(int which, QColor color) +{ + if (which < _nplots) { + // 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(); + if(sym) { + sym->setColor(color); + sym->setPen(pen); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +QColor +DisplayPlot::getLineColor(int which) const { + // If that plot doesn't exist then return black. + if (which < _nplots) + return _plot_curve[which]->pen().color(); + return QColor("black"); +} + +// Use a preprocessor macro to create a bunch of hooks for Q_PROPERTY and hence the stylesheet. +#define SETUPLINE(i, im1) \ + void DisplayPlot::setLineColor ## i (QColor c) {setLineColor(im1, c);} \ + const QColor DisplayPlot::getLineColor ## i () const {return getLineColor(im1);} \ + void DisplayPlot::setLineWidth ## i (int width) {setLineWidth(im1, width);} \ + int DisplayPlot::getLineWidth ## i () const {return getLineWidth(im1);} \ + void DisplayPlot::setLineStyle ## i (Qt::PenStyle ps) {setLineStyle(im1, ps);} \ + const Qt::PenStyle DisplayPlot::getLineStyle ## i () const {return getLineStyle(im1);} \ + void DisplayPlot::setLineMarker ## i (QwtSymbol::Style ms) {setLineMarker(im1, ms);} \ + const QwtSymbol::Style DisplayPlot::getLineMarker ## i () const {return getLineMarker(im1);} \ + void DisplayPlot::setMarkerAlpha ## i (int alpha) {setMarkerAlpha(im1, alpha);} \ + int DisplayPlot::getMarkerAlpha ## i () const {return getMarkerAlpha(im1);} +SETUPLINE(1, 0) +SETUPLINE(2, 1) +SETUPLINE(3, 2) +SETUPLINE(4, 3) +SETUPLINE(5, 4) +SETUPLINE(6, 5) +SETUPLINE(7, 6) +SETUPLINE(8, 7) +SETUPLINE(9, 8) + +void +DisplayPlot::setZoomerColor(QColor c) { + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); +} + +QColor +DisplayPlot::getZoomerColor() const { + return _zoomer->rubberBandPen().color(); +} + +void +DisplayPlot::setPaletteColor(QColor c) { + QPalette palette; + palette.setColor(canvas()->backgroundRole(), c); + canvas()->setPalette(palette); +} + +QColor +DisplayPlot::getPaletteColor() const { + return canvas()->palette().color(canvas()->backgroundRole()); +} + +void +DisplayPlot::setAxisLabelFontSize(int axisId, int fs) { + QwtText axis_title = QwtText(axisWidget(axisId)->title()); + QFont font = QFont(axis_title.font()); + font.setPointSize(fs); + axis_title.setFont(font); + axisWidget(axisId)->setTitle(axis_title); +} + +int +DisplayPlot::getAxisLabelFontSize(int axisId) const { + return axisWidget(axisId)->title().font().pointSize(); +} + +void +DisplayPlot::setYaxisLabelFontSize(int fs) { + setAxisLabelFontSize(QwtPlot::yLeft, fs); +} + +int +DisplayPlot::getYaxisLabelFontSize() const { + int fs = getAxisLabelFontSize(QwtPlot::yLeft); + return fs; +} + +void +DisplayPlot::setXaxisLabelFontSize(int fs) { + setAxisLabelFontSize(QwtPlot::xBottom, fs); +} + +int +DisplayPlot::getXaxisLabelFontSize() const { + int fs = getAxisLabelFontSize(QwtPlot::xBottom); + return fs; +} + +void +DisplayPlot::setAxesLabelFontSize(int fs) { + setAxisLabelFontSize(QwtPlot::yLeft, fs); + setAxisLabelFontSize(QwtPlot::xBottom, fs); +} + +int +DisplayPlot::getAxesLabelFontSize() const { + // Returns 0 if all axes do not have the same font size. + int fs = getAxisLabelFontSize(QwtPlot::yLeft); + if (getAxisLabelFontSize(QwtPlot::xBottom) != fs) + return 0; + return fs; +} + +void +DisplayPlot::setLineWidth(int which, int width) +{ + if (which < _nplots) { + // 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(1.0*width), 7+10*log10(1.0*width)); + _plot_curve[which]->setSymbol(sym); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + if(sym) { + sym->setSize(7+10*log10(1.0*width), 7+10*log10(1.0*width)); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +int +DisplayPlot::getLineWidth(int which) const { + if (which < _nplots) { + return _plot_curve[which]->pen().width(); + } else { + return 0; + } +} + +void +DisplayPlot::setLineStyle(int which, Qt::PenStyle style) +{ + if (which < _nplots) { + QPen pen(_plot_curve[which]->pen()); + pen.setStyle(style); + _plot_curve[which]->setPen(pen); + } +} + +const Qt::PenStyle +DisplayPlot::getLineStyle(int which) const +{ + if (which < _nplots) { + return _plot_curve[which]->pen().style(); + } else { + return Qt::SolidLine; + } +} + +void +DisplayPlot::setLineMarker(int which, QwtSymbol::Style marker) +{ + if (which < _nplots) { +#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(); + if(sym) { + sym->setStyle(marker); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +const QwtSymbol::Style +DisplayPlot::getLineMarker(int which) const +{ + if(which < _nplots) { +#if QWT_VERSION < 0x060000 + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + return sym.style(); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + return sym->style(); +#endif + } + else { + return QwtSymbol::NoSymbol; + } +} + +void +DisplayPlot::setMarkerAlpha(int which, int alpha) +{ + if (which < _nplots) { + // Get the pen color + QPen pen(_plot_curve[which]->pen()); + QColor color = pen.color(); + + // Set new alpha and update pen + color.setAlpha(alpha); + pen.setColor(color); + _plot_curve[which]->setPen(pen); + + // And set the new color for the markers +#if QWT_VERSION < 0x060000 + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + setLineMarker(which, sym.style()); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + if(sym) { + sym->setColor(color); + sym->setPen(pen); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +int +DisplayPlot::getMarkerAlpha(int which) const +{ + if (which < _nplots) { + return _plot_curve[which]->pen().color().alpha(); + } else { + return 0; + } +} + +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..d9ee16e087 --- /dev/null +++ b/gr-qtgui/lib/DisplayPlot.h @@ -0,0 +1,287 @@ +/* -*- 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. + */ + +#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 + +typedef QList<QColor> QColorList; +Q_DECLARE_METATYPE ( QColorList ) + +class DisplayPlot:public QwtPlot{ + Q_OBJECT + + Q_PROPERTY ( QColor line_color1 READ getLineColor1 WRITE setLineColor1 ) + Q_PROPERTY ( QColor line_color2 READ getLineColor2 WRITE setLineColor2 ) + Q_PROPERTY ( QColor line_color3 READ getLineColor3 WRITE setLineColor3 ) + Q_PROPERTY ( QColor line_color4 READ getLineColor4 WRITE setLineColor4 ) + Q_PROPERTY ( QColor line_color5 READ getLineColor5 WRITE setLineColor5 ) + Q_PROPERTY ( QColor line_color6 READ getLineColor6 WRITE setLineColor6 ) + Q_PROPERTY ( QColor line_color7 READ getLineColor7 WRITE setLineColor7 ) + Q_PROPERTY ( QColor line_color8 READ getLineColor8 WRITE setLineColor8 ) + Q_PROPERTY ( QColor line_color9 READ getLineColor9 WRITE setLineColor9 ) + + Q_PROPERTY ( int line_width1 READ getLineWidth1 WRITE setLineWidth1 ) + Q_PROPERTY ( int line_width2 READ getLineWidth2 WRITE setLineWidth2 ) + Q_PROPERTY ( int line_width3 READ getLineWidth3 WRITE setLineWidth3 ) + Q_PROPERTY ( int line_width4 READ getLineWidth4 WRITE setLineWidth4 ) + Q_PROPERTY ( int line_width5 READ getLineWidth5 WRITE setLineWidth5 ) + Q_PROPERTY ( int line_width6 READ getLineWidth6 WRITE setLineWidth6 ) + Q_PROPERTY ( int line_width7 READ getLineWidth7 WRITE setLineWidth7 ) + Q_PROPERTY ( int line_width8 READ getLineWidth8 WRITE setLineWidth8 ) + Q_PROPERTY ( int line_width9 READ getLineWidth9 WRITE setLineWidth9 ) + + Q_PROPERTY ( Qt::PenStyle line_style1 READ getLineStyle1 WRITE setLineStyle1 ) + Q_PROPERTY ( Qt::PenStyle line_style2 READ getLineStyle2 WRITE setLineStyle2 ) + Q_PROPERTY ( Qt::PenStyle line_style3 READ getLineStyle3 WRITE setLineStyle3 ) + Q_PROPERTY ( Qt::PenStyle line_style4 READ getLineStyle4 WRITE setLineStyle4 ) + Q_PROPERTY ( Qt::PenStyle line_style5 READ getLineStyle5 WRITE setLineStyle5 ) + Q_PROPERTY ( Qt::PenStyle line_style6 READ getLineStyle6 WRITE setLineStyle6 ) + Q_PROPERTY ( Qt::PenStyle line_style7 READ getLineStyle7 WRITE setLineStyle7 ) + Q_PROPERTY ( Qt::PenStyle line_style8 READ getLineStyle8 WRITE setLineStyle8 ) + Q_PROPERTY ( Qt::PenStyle line_style9 READ getLineStyle9 WRITE setLineStyle9 ) + + typedef QwtSymbol::Style QwtSymbolStyle; + + Q_ENUMS ( QwtSymbolStyle ) + Q_PROPERTY ( QwtSymbolStyle line_marker1 READ getLineMarker1 WRITE setLineMarker1 ) + Q_PROPERTY ( QwtSymbolStyle line_marker2 READ getLineMarker2 WRITE setLineMarker2 ) + Q_PROPERTY ( QwtSymbolStyle line_marker3 READ getLineMarker3 WRITE setLineMarker3 ) + Q_PROPERTY ( QwtSymbolStyle line_marker4 READ getLineMarker4 WRITE setLineMarker4 ) + Q_PROPERTY ( QwtSymbolStyle line_marker5 READ getLineMarker5 WRITE setLineMarker5 ) + Q_PROPERTY ( QwtSymbolStyle line_marker6 READ getLineMarker6 WRITE setLineMarker6 ) + Q_PROPERTY ( QwtSymbolStyle line_marker7 READ getLineMarker7 WRITE setLineMarker7 ) + Q_PROPERTY ( QwtSymbolStyle line_marker8 READ getLineMarker8 WRITE setLineMarker8 ) + Q_PROPERTY ( QwtSymbolStyle line_marker9 READ getLineMarker9 WRITE setLineMarker9 ) + + Q_PROPERTY ( int marker_alpha1 READ getMarkerAlpha1 WRITE setMarkerAlpha1 ) + Q_PROPERTY ( int marker_alpha2 READ getMarkerAlpha2 WRITE setMarkerAlpha2 ) + Q_PROPERTY ( int marker_alpha3 READ getMarkerAlpha3 WRITE setMarkerAlpha3 ) + Q_PROPERTY ( int marker_alpha4 READ getMarkerAlpha4 WRITE setMarkerAlpha4 ) + Q_PROPERTY ( int marker_alpha5 READ getMarkerAlpha5 WRITE setMarkerAlpha5 ) + Q_PROPERTY ( int marker_alpha6 READ getMarkerAlpha6 WRITE setMarkerAlpha6 ) + Q_PROPERTY ( int marker_alpha7 READ getMarkerAlpha7 WRITE setMarkerAlpha7 ) + Q_PROPERTY ( int marker_alpha8 READ getMarkerAlpha8 WRITE setMarkerAlpha8 ) + Q_PROPERTY ( int marker_alpha9 READ getMarkerAlpha9 WRITE setMarkerAlpha9 ) + + Q_PROPERTY ( QColor zoomer_color READ getZoomerColor WRITE setZoomerColor ) + Q_PROPERTY ( QColor palette_color READ getPaletteColor WRITE setPaletteColor ) + Q_PROPERTY ( int yaxis_label_font_size READ getYaxisLabelFontSize WRITE setYaxisLabelFontSize ) + Q_PROPERTY ( int xaxis_label_font_size READ getXaxisLabelFontSize WRITE setXaxisLabelFontSize ) + Q_PROPERTY ( int axes_label_font_size READ getAxesLabelFontSize WRITE setAxesLabelFontSize ) + +public: + DisplayPlot(int nplots, QWidget*); + virtual ~DisplayPlot(); + + virtual void replot() = 0; + + const QColor getLineColor1 () const; + const QColor getLineColor2 () const; + const QColor getLineColor3 () const; + const QColor getLineColor4 () const; + const QColor getLineColor5 () const; + const QColor getLineColor6 () const; + const QColor getLineColor7 () const; + const QColor getLineColor8 () const; + const QColor getLineColor9 () const; + + int getLineWidth1 () const; + int getLineWidth2 () const; + int getLineWidth3 () const; + int getLineWidth4 () const; + int getLineWidth5 () const; + int getLineWidth6 () const; + int getLineWidth7 () const; + int getLineWidth8 () const; + int getLineWidth9 () const; + + const Qt::PenStyle getLineStyle1 () const; + const Qt::PenStyle getLineStyle2 () const; + const Qt::PenStyle getLineStyle3 () const; + const Qt::PenStyle getLineStyle4 () const; + const Qt::PenStyle getLineStyle5 () const; + const Qt::PenStyle getLineStyle6 () const; + const Qt::PenStyle getLineStyle7 () const; + const Qt::PenStyle getLineStyle8 () const; + const Qt::PenStyle getLineStyle9 () const; + + const QwtSymbol::Style getLineMarker1 () const; + const QwtSymbol::Style getLineMarker2 () const; + const QwtSymbol::Style getLineMarker3 () const; + const QwtSymbol::Style getLineMarker4 () const; + const QwtSymbol::Style getLineMarker5 () const; + const QwtSymbol::Style getLineMarker6 () const; + const QwtSymbol::Style getLineMarker7 () const; + const QwtSymbol::Style getLineMarker8 () const; + const QwtSymbol::Style getLineMarker9 () const; + + int getMarkerAlpha1 () const; + int getMarkerAlpha2 () const; + int getMarkerAlpha3 () const; + int getMarkerAlpha4 () const; + int getMarkerAlpha5 () const; + int getMarkerAlpha6 () const; + int getMarkerAlpha7 () const; + int getMarkerAlpha8 () const; + int getMarkerAlpha9 () const; + + QColor getZoomerColor() const; + QColor getPaletteColor() const; + int getAxisLabelFontSize(int axisId) const; + int getYaxisLabelFontSize() const; + int getXaxisLabelFontSize() const; + int getAxesLabelFontSize() const; + + // 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 setLineLabel(int which, QString label); + QString getLineLabel(int which); + void setLineColor(int which, QColor color); + QColor getLineColor(int which) const; + void setLineWidth(int which, int width); + int getLineWidth(int which) const; + void setLineStyle(int which, Qt::PenStyle style); + const Qt::PenStyle getLineStyle(int which) const; + void setLineMarker(int which, QwtSymbol::Style marker); + const QwtSymbol::Style getLineMarker(int which) const; + void setMarkerAlpha(int which, int alpha); + int getMarkerAlpha(int which) const; + // Need a function for each curve for setting via stylesheet. + // Can't use preprocessor directives because we're inside a Q_OBJECT. + void setLineColor1 (QColor); + void setLineColor2 (QColor); + void setLineColor3 (QColor); + void setLineColor4 (QColor); + void setLineColor5 (QColor); + void setLineColor6 (QColor); + void setLineColor7 (QColor); + void setLineColor8 (QColor); + void setLineColor9 (QColor); + + void setLineWidth1 (int); + void setLineWidth2 (int); + void setLineWidth3 (int); + void setLineWidth4 (int); + void setLineWidth5 (int); + void setLineWidth6 (int); + void setLineWidth7 (int); + void setLineWidth8 (int); + void setLineWidth9 (int); + + void setLineStyle1 (Qt::PenStyle); + void setLineStyle2 (Qt::PenStyle); + void setLineStyle3 (Qt::PenStyle); + void setLineStyle4 (Qt::PenStyle); + void setLineStyle5 (Qt::PenStyle); + void setLineStyle6 (Qt::PenStyle); + void setLineStyle7 (Qt::PenStyle); + void setLineStyle8 (Qt::PenStyle); + void setLineStyle9 (Qt::PenStyle); + + void setLineMarker1 (QwtSymbol::Style); + void setLineMarker2 (QwtSymbol::Style); + void setLineMarker3 (QwtSymbol::Style); + void setLineMarker4 (QwtSymbol::Style); + void setLineMarker5 (QwtSymbol::Style); + void setLineMarker6 (QwtSymbol::Style); + void setLineMarker7 (QwtSymbol::Style); + void setLineMarker8 (QwtSymbol::Style); + void setLineMarker9 (QwtSymbol::Style); + + void setMarkerAlpha1 (int); + void setMarkerAlpha2 (int); + void setMarkerAlpha3 (int); + void setMarkerAlpha4 (int); + void setMarkerAlpha5 (int); + void setMarkerAlpha6 (int); + void setMarkerAlpha7 (int); + void setMarkerAlpha8 (int); + void setMarkerAlpha9 (int); + + void setZoomerColor(QColor c); + void setPaletteColor(QColor c); + void setAxisLabelFontSize(int axisId, int fs); + void setYaxisLabelFontSize(int fs); + void setXaxisLabelFontSize(int fs); + void setAxesLabelFontSize(int fs); + + void setStop(bool on); + + 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; + + QList<QColor> _trace_colors; + + bool _autoscale_state; +}; + +#endif /* DOMAIN_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.cc b/gr-qtgui/lib/FrequencyDisplayPlot.cc index b74d460150..e279155544 100644 --- a/gr-qtgui/lib/FrequencyDisplayPlot.cc +++ b/gr-qtgui/lib/FrequencyDisplayPlot.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,88 +25,44 @@ #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(); } - void SetUnitType(const std::string &type) + void setUnitType(const std::string &type) { _unitType = type; } 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,21 @@ private: std::string _unitType; }; -FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) - : QwtPlot(parent) -{ - _startFrequency = 0; - _stopFrequency = 4000; - - _lastReplot = 0; - - resize(parent->width(), parent->height()); - _useCenterFrequencyFlag = false; +/*********************************************************************** + * Main frequency display plotter widget + **********************************************************************/ +FrequencyDisplayPlot::FrequencyDisplayPlot(int nplots, QWidget* parent) + : DisplayPlot(nplots, parent) +{ + _startFrequency = -1; + _stopFrequency = 1; _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,52 +94,67 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis); setAxisTitle(QwtPlot::yLeft, "Power (dB)"); + QList<QColor> default_colors; + default_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 - _fft_plot_curve = new QwtPlotCurve("Power Spectrum"); - _fft_plot_curve->attach(this); - _fft_plot_curve->setPen(QPen(Qt::blue)); + 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); + + QwtSymbol *symbol = new QwtSymbol(QwtSymbol::NoSymbol, QBrush(default_colors[i]), + QPen(default_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 - + setLineColor(i, default_colors[i]); + } + _min_fft_plot_curve = new QwtPlotCurve("Minimum Power"); _min_fft_plot_curve->attach(this); - _min_fft_plot_curve->setPen(QPen(Qt::magenta)); - + const QColor _default_min_fft_color = Qt::magenta; + setMinFFTColor(_default_min_fft_color); #if QWT_VERSION < 0x060000 _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); #else _min_fft_plot_curve->setRawSamples(_xAxisPoints, _minFFTPoints, _numPoints); #endif - _min_fft_plot_curve->setVisible(false); - + _max_fft_plot_curve = new QwtPlotCurve("Maximum Power"); _max_fft_plot_curve->attach(this); - _max_fft_plot_curve->setPen(QPen(Qt::darkYellow)); - + QColor _default_max_fft_color = Qt::darkYellow; + setMaxFFTColor(_default_max_fft_color); #if QWT_VERSION < 0x060000 _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); #else _max_fft_plot_curve->setRawSamples(_xAxisPoints, _maxFFTPoints, _numPoints); #endif - _max_fft_plot_curve->setVisible(false); - + _lower_intensity_marker= new QwtPlotMarker(); _lower_intensity_marker->setLineStyle(QwtPlotMarker::HLine); - _lower_intensity_marker->setLinePen(QPen(Qt::cyan)); + QColor _default_marker_lower_intensity_color = Qt::cyan; + setMarkerLowerIntensityColor(_default_marker_lower_intensity_color); _lower_intensity_marker->attach(this); - + _upper_intensity_marker = new QwtPlotMarker(); _upper_intensity_marker->setLineStyle(QwtPlotMarker::HLine); - _upper_intensity_marker->setLinePen(QPen(Qt::green, 0, Qt::DotLine)); + QColor _default_marker_upper_intensity_color = Qt::green; + setMarkerUpperIntensityColor(_default_marker_upper_intensity_color); _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++){ @@ -211,33 +162,22 @@ FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent) _maxFFTPoints[number] = -280.0; } - // set up peak marker - QwtSymbol symbol; - _markerPeakAmplitude = new QwtPlotMarker(); - _markerPeakAmplitude->setLinePen(QPen(Qt::yellow)); - symbol.setStyle(QwtSymbol::Diamond); - symbol.setSize(8); - symbol.setPen(QPen(Qt::yellow)); - symbol.setBrush(QBrush(Qt::yellow)); - -#if QWT_VERSION < 0x060000 - _markerPeakAmplitude->setSymbol(symbol); -#else - _markerPeakAmplitude->setSymbol(&symbol); -#endif - + QColor _default_marker_peak_amplitude_color = Qt::yellow; + setMarkerPeakAmplitudeColor(_default_marker_peak_amplitude_color); /// THIS CAUSES A PROBLEM! //_markerPeakAmplitude->attach(this); _markerNoiseFloorAmplitude = new QwtPlotMarker(); _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::HLine); - _markerNoiseFloorAmplitude->setLinePen(QPen(Qt::darkRed, 0, Qt::DotLine)); + QColor _default_marker_noise_floor_amplitude_color = Qt::darkRed; + setMarkerNoiseFloorAmplitudeColor(_default_marker_noise_floor_amplitude_color); _markerNoiseFloorAmplitude->attach(this); _markerCF= new QwtPlotMarker(); _markerCF->setLineStyle(QwtPlotMarker::VLine); - _markerCF->setLinePen(QPen(Qt::lightGray, 0, Qt::DotLine)); + QColor _default_marker_CF_color = Qt::lightGray; + setMarkerCFColor(_default_marker_CF_color); _markerCF->attach(this); _markerCF->hide(); @@ -248,21 +188,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,38 +199,33 @@ 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); - _zoomer->setTrackerPen(c); + const QColor default_zoomer_color(Qt::darkRed); + setZoomerColor(default_zoomer_color); // 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 -FrequencyDisplayPlot::set_yaxis(double min, double max) +FrequencyDisplayPlot::setYaxis(double min, double max) { // Get the new max/min values for the plot _minYAxis = min; @@ -314,28 +234,21 @@ FrequencyDisplayPlot::set_yaxis(double min, double max) // Set the axis max/min to the new values setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis); - // Reset the base zoom level to the new axis scale set here - _zoomer->setZoomBase(); + // Reset the base zoom level to the new axis scale set here. + // But don't do it if we set the axis due to auto scaling. + if(!_autoscale_state) + _zoomer->setZoomBase(); } void -FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq, - const double constStopFreq, - const double constCenterFreq, - const bool useCenterFrequencyFlag, +FrequencyDisplayPlot::setFrequencyRange(const double centerfreq, + const double bandwidth, const double units, const std::string &strunits) { - double startFreq = constStartFreq / units; - double stopFreq = constStopFreq / units; - double centerFreq = constCenterFreq / units; + double startFreq = (centerfreq - bandwidth/2.0f) / units; + double stopFreq = (centerfreq + bandwidth/2.0f) / units; _xAxisMultiplier = units; - _useCenterFrequencyFlag = useCenterFrequencyFlag; - - if(_useCenterFrequencyFlag){ - startFreq = (startFreq + centerFreq); - stopFreq = (stopFreq + centerFreq); - } bool reset = false; if((startFreq != _startFrequency) || (stopFreq != _stopFrequency)) @@ -345,7 +258,7 @@ FrequencyDisplayPlot::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 FreqDisplayScaleDraw(display_units)); setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str())); @@ -353,21 +266,21 @@ FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq, if(reset) _resetXAxisPoints(); - ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(display_units); - ((FreqDisplayZoomer*)_zoomer)->SetUnitType(strunits); + ((FreqDisplayZoomer*)_zoomer)->setFrequencyPrecision(display_units); + ((FreqDisplayZoomer*)_zoomer)->setUnitType(strunits); } } } double -FrequencyDisplayPlot::GetStartFrequency() const +FrequencyDisplayPlot::getStartFrequency() const { return _startFrequency; } double -FrequencyDisplayPlot::GetStopFrequency() const +FrequencyDisplayPlot::getStopFrequency() const { return _stopFrequency; } @@ -376,117 +289,169 @@ void FrequencyDisplayPlot::replot() { _markerNoiseFloorAmplitude->setYValue(_noiseFloorAmplitude); + _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency); // Make sure to take into account the start frequency - if(_useCenterFrequencyFlag){ - _markerPeakAmplitude->setXValue((_peakFrequency/1000.0) + _startFrequency); - } - else{ - _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency); - } +// if(_useCenterFrequencyFlag){ +// _markerPeakAmplitude->setXValue((_peakFrequency/1000.0) + _startFrequency); +// } +// else{ +// _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency); +// } _markerPeakAmplitude->setYValue(_peakAmplitude); QwtPlot::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); #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); #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]; + } +#if QWT_VERSION < 0x060000 + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); +#else + _min_fft_plot_curve->setRawSamples(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawSamples(_xAxisPoints, _maxFFTPoints, _numPoints); +#endif + _resetXAxisPoints(); + clearMaxData(); + clearMinData(); + } + + for(int i = 0; i < _nplots; i++) { + memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); } - if(dataPoints[point] > _maxFFTPoints[point]){ - _maxFFTPoints[point] = dataPoints[point]; + + double bottom=1e20, top=-1e20; + for(int n = 0; n < _nplots; n++) { + for(int64_t point = 0; point < numDataPoints; point++) { + if(dataPoints[n][point] < _minFFTPoints[point]) { + _minFFTPoints[point] = dataPoints[n][point]; + } + if(dataPoints[n][point] > _maxFFTPoints[point]) { + _maxFFTPoints[point] = dataPoints[n][point]; + } + + // Find overall top and bottom values in plot. + // Used for autoscaling y-axis. + if(dataPoints[n][point] < bottom) { + bottom = dataPoints[n][point]; + } + if(dataPoints[n][point] > top) { + top = dataPoints[n][point]; + } + } } + + if(_autoscale_state) + _autoScale(bottom, top); + + _noiseFloorAmplitude = noiseFloorAmplitude; + _peakFrequency = peakFrequency; + _peakAmplitude = peakAmplitude; + + setUpperIntensityLevel(_peakAmplitude); + + replot(); } - - _noiseFloorAmplitude = noiseFloorAmplitude; - _peakFrequency = peakFrequency; - _peakAmplitude = peakAmplitude; - - SetUpperIntensityLevel(_peakAmplitude); - - replot(); - - _lastReplot = gruel::high_res_timer_now(); } } void -FrequencyDisplayPlot::ClearMaxData() +FrequencyDisplayPlot::plotNewData(const double* dataPoints, + const int64_t numDataPoints, + const double noiseFloorAmplitude, const double peakFrequency, + const double peakAmplitude, const double timeInterval) { - for(int64_t number = 0; number < _numPoints; number++){ + 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++) { _maxFFTPoints[number] = _minYAxis; } } void -FrequencyDisplayPlot::ClearMinData() +FrequencyDisplayPlot::clearMinData() { - for(int64_t number = 0; number < _numPoints; number++){ + for(int64_t number = 0; number < _numPoints; number++) { _minFFTPoints[number] = _maxYAxis; } } void -FrequencyDisplayPlot::SetMaxFFTVisible(const bool visibleFlag) +FrequencyDisplayPlot::_autoScale(double bottom, double top) +{ + // Auto scale the y-axis with a margin of 10 dB on either side. + setYaxis(bottom - 10, top + 10); +} + +void +FrequencyDisplayPlot::setAutoScale(bool state) +{ + _autoscale_state = state; +} + +void +FrequencyDisplayPlot::setMaxFFTVisible(const bool visibleFlag) { + _max_fft_visible = visibleFlag; _max_fft_plot_curve->setVisible(visibleFlag); } +const bool FrequencyDisplayPlot::getMaxFFTVisible() const +{ + return _max_fft_visible; +} void -FrequencyDisplayPlot::SetMinFFTVisible(const bool visibleFlag) +FrequencyDisplayPlot::setMinFFTVisible(const bool visibleFlag) { + _min_fft_visible = visibleFlag; _min_fft_plot_curve->setVisible(visibleFlag); } +const bool FrequencyDisplayPlot::getMinFFTVisible() const +{ + return _min_fft_visible; +} 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; } @@ -500,29 +465,30 @@ FrequencyDisplayPlot::_resetXAxisPoints() zbase.setRight(_stopFrequency); _zoomer->zoom(zbase); _zoomer->setZoomBase(zbase); + _zoomer->setZoomBase(true); _zoomer->zoom(0); } void -FrequencyDisplayPlot::SetLowerIntensityLevel(const double lowerIntensityLevel) +FrequencyDisplayPlot::setLowerIntensityLevel(const double lowerIntensityLevel) { - _lower_intensity_marker->setYValue( lowerIntensityLevel ); + _lower_intensity_marker->setYValue(lowerIntensityLevel); } void -FrequencyDisplayPlot::SetUpperIntensityLevel(const double upperIntensityLevel) +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 +496,7 @@ FrequencyDisplayPlot::SetBGColour (QColor c) } void -FrequencyDisplayPlot::ShowCFMarker (const bool show) +FrequencyDisplayPlot::showCFMarker(const bool show) { if (show) _markerCF->show(); @@ -540,21 +506,128 @@ FrequencyDisplayPlot::ShowCFMarker (const bool show) void -FrequencyDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +FrequencyDisplayPlot::onPickerPointSelected(const QwtDoublePoint & p) { QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); + //fprintf(stderr,"onPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); point.setX(point.x() * _xAxisMultiplier); emit plotPointSelected(point); } void -FrequencyDisplayPlot::OnPickerPointSelected6(const QPointF & p) +FrequencyDisplayPlot::onPickerPointSelected6(const QPointF & p) { QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); + //fprintf(stderr,"onPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); point.setX(point.x() * _xAxisMultiplier); emit plotPointSelected(point); } +void +FrequencyDisplayPlot::setMinFFTColor (QColor c) +{ + _min_fft_color = c; + _min_fft_plot_curve->setPen(QPen(c)); +} +const QColor +FrequencyDisplayPlot::getMinFFTColor() const {return _min_fft_color;} + +void +FrequencyDisplayPlot::setMaxFFTColor (QColor c) +{ + _max_fft_color = c; + _max_fft_plot_curve->setPen(QPen(c)); +} +const QColor +FrequencyDisplayPlot::getMaxFFTColor() const {return _max_fft_color;} + +void +FrequencyDisplayPlot::setMarkerLowerIntensityColor (QColor c) +{ + _marker_lower_intensity_color = c; + _lower_intensity_marker->setLinePen(QPen(c)); +} +const QColor +FrequencyDisplayPlot::getMarkerLowerIntensityColor () const {return _marker_lower_intensity_color;} +void +FrequencyDisplayPlot::setMarkerLowerIntensityVisible (bool visible) +{ + _marker_lower_intensity_visible = visible; + if (visible) + _lower_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + else + _lower_intensity_marker->setLineStyle(QwtPlotMarker::NoLine); +} +const bool +FrequencyDisplayPlot::getMarkerLowerIntensityVisible () const {return _marker_lower_intensity_visible;} + +void +FrequencyDisplayPlot::setMarkerUpperIntensityColor (QColor c) +{ + _marker_upper_intensity_color = c; + _upper_intensity_marker->setLinePen(QPen(c, 0, Qt::DotLine)); +} +const QColor +FrequencyDisplayPlot::getMarkerUpperIntensityColor () const {return _marker_upper_intensity_color;} +void +FrequencyDisplayPlot::setMarkerUpperIntensityVisible (bool visible) +{ + _marker_upper_intensity_visible = visible; + if (visible) + _upper_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + else + _upper_intensity_marker->setLineStyle(QwtPlotMarker::NoLine); +} +const bool +FrequencyDisplayPlot::getMarkerUpperIntensityVisible () const {return _marker_upper_intensity_visible;} + +void +FrequencyDisplayPlot::setMarkerPeakAmplitudeColor (QColor c) +{ + _marker_peak_amplitude_color = c; + _markerPeakAmplitude->setLinePen(QPen(c)); + QwtSymbol symbol; + symbol.setStyle(QwtSymbol::Diamond); + symbol.setSize(8); + symbol.setPen(QPen(c)); + symbol.setBrush(QBrush(c)); +#if QWT_VERSION < 0x060000 + _markerPeakAmplitude->setSymbol(symbol); +#else + _markerPeakAmplitude->setSymbol(&symbol); +#endif +} +const QColor +FrequencyDisplayPlot::getMarkerPeakAmplitudeColor () const {return _marker_peak_amplitude_color;} + +void +FrequencyDisplayPlot::setMarkerNoiseFloorAmplitudeColor (QColor c) +{ + _marker_noise_floor_amplitude_color = c; + _markerNoiseFloorAmplitude->setLinePen(QPen(c, 0, Qt::DotLine)); +} +const QColor +FrequencyDisplayPlot::getMarkerNoiseFloorAmplitudeColor () const {return _marker_noise_floor_amplitude_color;} + +void +FrequencyDisplayPlot::setMarkerNoiseFloorAmplitudeVisible (bool visible) +{ + _marker_noise_floor_amplitude_visible = visible; + if (visible) + _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::HLine); + else + _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::NoLine); +} +const bool +FrequencyDisplayPlot::getMarkerNoiseFloorAmplitudeVisible () const {return _marker_noise_floor_amplitude_visible;} + +void +FrequencyDisplayPlot::setMarkerCFColor (QColor c) +{ + _marker_CF_color = c; + _markerCF->setLinePen(QPen(c, 0, Qt::DotLine)); +} +const QColor +FrequencyDisplayPlot::getMarkerCFColor () const {return _marker_CF_color;} + #endif /* FREQUENCY_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.h b/gr-qtgui/lib/FrequencyDisplayPlot.h index 5c3ea708c3..22d2be0055 100644 --- a/gr-qtgui/lib/FrequencyDisplayPlot.h +++ b/gr-qtgui/lib/FrequencyDisplayPlot.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,115 +25,136 @@ #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 + Q_PROPERTY ( QColor min_fft_color READ getMinFFTColor WRITE setMinFFTColor ) + Q_PROPERTY ( QColor max_fft_color READ getMaxFFTColor WRITE setMaxFFTColor ) + Q_PROPERTY ( bool min_fft_visible READ getMinFFTVisible WRITE setMinFFTVisible ) + Q_PROPERTY ( bool max_fft_visible READ getMaxFFTVisible WRITE setMaxFFTVisible ) + Q_PROPERTY ( QColor marker_lower_intensity_color READ getMarkerLowerIntensityColor WRITE setMarkerLowerIntensityColor ) + Q_PROPERTY ( bool marker_lower_intensity_visible READ getMarkerLowerIntensityVisible WRITE setMarkerLowerIntensityVisible ) + Q_PROPERTY ( QColor marker_upper_intensity_color READ getMarkerUpperIntensityColor WRITE setMarkerUpperIntensityColor ) + Q_PROPERTY ( bool marker_upper_intensity_visible READ getMarkerUpperIntensityVisible WRITE setMarkerUpperIntensityVisible ) + Q_PROPERTY ( QColor marker_peak_amplitude_color READ getMarkerPeakAmplitudeColor WRITE setMarkerPeakAmplitudeColor ) + Q_PROPERTY ( QColor marker_noise_floor_amplitude_color READ getMarkerNoiseFloorAmplitudeColor WRITE setMarkerNoiseFloorAmplitudeColor ) + Q_PROPERTY ( bool marker_noise_floor_amplitude_visible READ getMarkerNoiseFloorAmplitudeVisible WRITE setMarkerNoiseFloorAmplitudeVisible ) + Q_PROPERTY ( QColor marker_CF_color READ getMarkerCFColor WRITE setMarkerCFColor ) + public: - FrequencyDisplayPlot(QWidget*); + FrequencyDisplayPlot(int nplots, QWidget*); virtual ~FrequencyDisplayPlot(); - void SetFrequencyRange(const double, const double, - const double, const bool, + void setFrequencyRange(const double, const double, 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, + void plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude, const double timeInterval); - void ClearMaxData(); - void ClearMinData(); - - void SetMaxFFTVisible(const bool); - void SetMinFFTVisible(const bool); + // 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); - virtual void replot(); + void clearMaxData(); + void clearMinData(); - void set_yaxis(double min, double max); + void replot(); - void SetTraceColour (QColor); - void SetBGColour (QColor c); - void ShowCFMarker (const bool); + void setYaxis(double min, double max); -public slots: - void resizeSlot( QSize *e ); - void SetLowerIntensityLevel(const double); - void SetUpperIntensityLevel(const double); + void setTraceColour (QColor); + void setBGColour (QColor c); + void showCFMarker (const bool); - // 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); + const bool getMaxFFTVisible() const; + const bool getMinFFTVisible() const; + const QColor getMinFFTColor() const; + const QColor getMaxFFTColor() const; + const QColor getMarkerLowerIntensityColor () const; + const bool getMarkerLowerIntensityVisible () const; + const QColor getMarkerUpperIntensityColor () const; + const bool getMarkerUpperIntensityVisible () const; + const QColor getMarkerPeakAmplitudeColor () const; + const bool getMarkerNoiseFloorAmplitudeVisible () const; + const QColor getMarkerNoiseFloorAmplitudeColor () const; + const QColor getMarkerCFColor () const; -signals: - void plotPointSelected(const QPointF p); - -protected: +public slots: + void setMaxFFTVisible(const bool); + void setMinFFTVisible(const bool); + void setMinFFTColor (QColor c); + void setMaxFFTColor (QColor c); + void setMarkerLowerIntensityColor (QColor c); + void setMarkerLowerIntensityVisible (bool visible); + void setMarkerUpperIntensityColor (QColor c); + void setMarkerUpperIntensityVisible (bool visible); + void setMarkerPeakAmplitudeColor (QColor c); + void setMarkerNoiseFloorAmplitudeVisible (bool visible); + void setMarkerNoiseFloorAmplitudeColor (QColor c); + void setMarkerCFColor (QColor c); + + void setLowerIntensityLevel(const double); + void setUpperIntensityLevel(const double); + + void onPickerPointSelected(const QwtDoublePoint & p); + void onPickerPointSelected6(const QPointF & p); + + void setAutoScale(bool state); private: - void _resetXAxisPoints(); + void _autoScale(double bottom, double top); + + std::vector<double*> _dataPoints; + + QwtPlotCurve* _min_fft_plot_curve; + QwtPlotCurve* _max_fft_plot_curve; + QColor _min_fft_color; + bool _min_fft_visible; + QColor _max_fft_color; + bool _max_fft_visible; + QColor _marker_lower_intensity_color; + bool _marker_lower_intensity_visible; + QColor _marker_upper_intensity_color; + bool _marker_upper_intensity_visible; + QColor _marker_peak_amplitude_color; + QColor _marker_noise_floor_amplitude_color; + bool _marker_noise_floor_amplitude_visible; + QColor _marker_CF_color; 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; }; #endif /* FREQUENCY_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/lib/SpectrumGUIClass.cc b/gr-qtgui/lib/SpectrumGUIClass.cc index d2dbc77723..40c7fb02b6 100644 --- a/gr-qtgui/lib/SpectrumGUIClass.cc +++ b/gr-qtgui/lib/SpectrumGUIClass.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -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; @@ -67,7 +68,7 @@ SpectrumGUIClass::~SpectrumGUIClass() // We don't need to delete this since as a QWidget, it is supposed to be destroyed // with it's parent. Deleting it causes a segmentation fault, and not deleting it // does not leave any extra memory. - //if(GetWindowOpenFlag()){ + //if(getWindowOpenFlag()){ //delete _spectrumDisplayForm; //} @@ -79,22 +80,21 @@ SpectrumGUIClass::~SpectrumGUIClass() } void -SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent, +SpectrumGUIClass::openSpectrumWindow(QWidget* parent, const bool frequency, const bool waterfall, const bool time, const bool constellation) { d_mutex.lock(); - if(!_windowOpennedFlag){ + if(!_windowOpennedFlag) { - if(!_fftBuffersCreatedFlag){ - _fftPoints = new std::complex<float>[_dataPoints]; + if(!_fftBuffersCreatedFlag) { + _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)); } @@ -103,10 +103,10 @@ SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent, _spectrumDisplayForm = new SpectrumDisplayForm(parent); // Toggle Windows on/off - _spectrumDisplayForm->ToggleTabFrequency(frequency); - _spectrumDisplayForm->ToggleTabWaterfall(waterfall); - _spectrumDisplayForm->ToggleTabTime(time); - _spectrumDisplayForm->ToggleTabConstellation(constellation); + _spectrumDisplayForm->toggleTabFrequency(frequency); + _spectrumDisplayForm->toggleTabWaterfall(waterfall); + _spectrumDisplayForm->toggleTabTime(time); + _spectrumDisplayForm->toggleTabConstellation(constellation); _windowOpennedFlag = true; @@ -117,8 +117,8 @@ SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent, d_mutex.unlock(); - SetDisplayTitle(_title); - Reset(); + setDisplayTitle(_title); + reset(); qApp->postEvent(_spectrumDisplayForm, new QEvent(QEvent::Type(QEvent::User+3))); @@ -126,19 +126,19 @@ SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent, _lastGUIUpdateTime = 0; // Draw Blank Display - UpdateWindow(false, NULL, 0, NULL, 0, NULL, 0, gruel::high_res_timer_now(), true); + updateWindow(false, NULL, 0, NULL, 0, NULL, 0, gruel::high_res_timer_now(), true); // Set up the initial frequency axis settings - SetFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency); + setFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency); // GUI Thread only qApp->processEvents(); } void -SpectrumGUIClass::Reset() +SpectrumGUIClass::reset() { - if(GetWindowOpenFlag()) { + if(getWindowOpenFlag()) { qApp->postEvent(_spectrumDisplayForm, new SpectrumFrequencyRangeEvent(_centerFrequency, _startFrequency, @@ -151,18 +151,18 @@ SpectrumGUIClass::Reset() } void -SpectrumGUIClass::SetDisplayTitle(const std::string newString) +SpectrumGUIClass::setDisplayTitle(const std::string newString) { _title.assign(newString); - if(GetWindowOpenFlag()){ + if(getWindowOpenFlag()){ qApp->postEvent(_spectrumDisplayForm, new SpectrumWindowCaptionEvent(_title.c_str())); } } bool -SpectrumGUIClass::GetWindowOpenFlag() +SpectrumGUIClass::getWindowOpenFlag() { gruel::scoped_lock lock(d_mutex); bool returnFlag = false; @@ -172,14 +172,14 @@ SpectrumGUIClass::GetWindowOpenFlag() void -SpectrumGUIClass::SetWindowOpenFlag(const bool newFlag) +SpectrumGUIClass::setWindowOpenFlag(const bool newFlag) { gruel::scoped_lock lock(d_mutex); _windowOpennedFlag = newFlag; } void -SpectrumGUIClass::SetFrequencyRange(const double centerFreq, +SpectrumGUIClass::setFrequencyRange(const double centerFreq, const double startFreq, const double stopFreq) { @@ -188,13 +188,13 @@ SpectrumGUIClass::SetFrequencyRange(const double centerFreq, _startFrequency = startFreq; _stopFrequency = stopFreq; - _spectrumDisplayForm->SetFrequencyRange(_centerFrequency, + _spectrumDisplayForm->setFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency); } double -SpectrumGUIClass::GetStartFrequency() +SpectrumGUIClass::getStartFrequency() { gruel::scoped_lock lock(d_mutex); double returnValue = 0.0; @@ -203,7 +203,7 @@ SpectrumGUIClass::GetStartFrequency() } double -SpectrumGUIClass::GetStopFrequency() +SpectrumGUIClass::getStopFrequency() { gruel::scoped_lock lock(d_mutex); double returnValue = 0.0; @@ -212,7 +212,7 @@ SpectrumGUIClass::GetStopFrequency() } double -SpectrumGUIClass::GetCenterFrequency() +SpectrumGUIClass::getCenterFrequency() { gruel::scoped_lock lock(d_mutex); double returnValue = 0.0; @@ -222,8 +222,8 @@ SpectrumGUIClass::GetCenterFrequency() void -SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, - const std::complex<float>* fftBuffer, +SpectrumGUIClass::updateWindow(const bool updateDisplayFlag, + const float* fftBuffer, const uint64_t inputBufferSize, const float* realTimeDomainData, const uint64_t realTimeDomainDataSize, @@ -240,11 +240,13 @@ SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, } int64_t timeDomainBufferSize = 0; - if(updateDisplayFlag){ - if((fftBuffer != NULL) && (bufferSize > 0)){ - memcpy(_fftPoints, fftBuffer, bufferSize * sizeof(std::complex<float>)); + if(updateDisplayFlag) { + if((fftBuffer != NULL) && (bufferSize > 0)) { + 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,48 +254,42 @@ 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; - + if((complexTimeDomainData != NULL) && (complexTimeDomainDataSize > 0)) { + 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++; - } } } // If bufferSize is zero, then just update the display by sending over the old data - if(bufferSize < 1){ + if(bufferSize < 1) { bufferSize = _lastDataPointCount; repeatDataFlag = true; } - else{ + else { // Since there is data this time, update the count _lastDataPointCount = bufferSize; } const gruel::high_res_timer_type currentTime = gruel::high_res_timer_now(); - const gruel::high_res_timer_type lastUpdateGUITime = GetLastGUIUpdateTime(); + const gruel::high_res_timer_type lastUpdateGUITime = getLastGUIUpdateTime(); if((currentTime - lastUpdateGUITime > (4*_updateTime)*gruel::high_res_timer_tps()) && - (GetPendingGUIUpdateEvents() > 0) && lastUpdateGUITime != 0) { + (getPendingGUIUpdateEvents() > 0) && lastUpdateGUITime != 0) { // Do not update the display if too much data is pending to be displayed _droppedEntriesCount++; } - else{ + else { // Draw the Data - IncrementPendingGUIUpdateEvents(); + incrementPendingGUIUpdateEvents(); qApp->postEvent(_spectrumDisplayForm, new SpectrumUpdateEvent(_fftPoints, bufferSize, _realTimeDomainPoints, @@ -307,14 +303,14 @@ SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, // Only reset the dropped entries counter if this is not // repeat data since repeat data is dropped by the display systems - if(!repeatDataFlag){ + if(!repeatDataFlag) { _droppedEntriesCount = 0; } } } float -SpectrumGUIClass::GetPowerValue() +SpectrumGUIClass::getPowerValue() { gruel::scoped_lock lock(d_mutex); float returnValue = 0; @@ -323,14 +319,14 @@ SpectrumGUIClass::GetPowerValue() } void -SpectrumGUIClass::SetPowerValue(const float value) +SpectrumGUIClass::setPowerValue(const float value) { gruel::scoped_lock lock(d_mutex); _powerValue = value; } int -SpectrumGUIClass::GetWindowType() +SpectrumGUIClass::getWindowType() { gruel::scoped_lock lock(d_mutex); int returnValue = 0; @@ -339,14 +335,14 @@ SpectrumGUIClass::GetWindowType() } void -SpectrumGUIClass::SetWindowType(const int newType) +SpectrumGUIClass::setWindowType(const int newType) { gruel::scoped_lock lock(d_mutex); _windowType = newType; } int -SpectrumGUIClass::GetFFTSize() +SpectrumGUIClass::getFFTSize() { int returnValue = 0; returnValue = _fftSize; @@ -354,10 +350,10 @@ SpectrumGUIClass::GetFFTSize() } int -SpectrumGUIClass::GetFFTSizeIndex() +SpectrumGUIClass::getFFTSizeIndex() { gruel::scoped_lock lock(d_mutex); - int fftsize = GetFFTSize(); + int fftsize = getFFTSize(); switch(fftsize) { case(1024): return 0; break; case(2048): return 1; break; @@ -370,14 +366,14 @@ SpectrumGUIClass::GetFFTSizeIndex() } void -SpectrumGUIClass::SetFFTSize(const int newSize) +SpectrumGUIClass::setFFTSize(const int newSize) { gruel::scoped_lock lock(d_mutex); _fftSize = newSize; } gruel::high_res_timer_type -SpectrumGUIClass::GetLastGUIUpdateTime() +SpectrumGUIClass::getLastGUIUpdateTime() { gruel::scoped_lock lock(d_mutex); gruel::high_res_timer_type returnValue; @@ -386,14 +382,14 @@ SpectrumGUIClass::GetLastGUIUpdateTime() } void -SpectrumGUIClass::SetLastGUIUpdateTime(const gruel::high_res_timer_type newTime) +SpectrumGUIClass::setLastGUIUpdateTime(const gruel::high_res_timer_type newTime) { gruel::scoped_lock lock(d_mutex); _lastGUIUpdateTime = newTime; } unsigned int -SpectrumGUIClass::GetPendingGUIUpdateEvents() +SpectrumGUIClass::getPendingGUIUpdateEvents() { gruel::scoped_lock lock(d_mutex); unsigned int returnValue = 0; @@ -402,14 +398,14 @@ SpectrumGUIClass::GetPendingGUIUpdateEvents() } void -SpectrumGUIClass::IncrementPendingGUIUpdateEvents() +SpectrumGUIClass::incrementPendingGUIUpdateEvents() { gruel::scoped_lock lock(d_mutex); _pendingGUIUpdateEventsCount++; } void -SpectrumGUIClass::DecrementPendingGUIUpdateEvents() +SpectrumGUIClass::decrementPendingGUIUpdateEvents() { gruel::scoped_lock lock(d_mutex); if(_pendingGUIUpdateEventsCount > 0){ @@ -418,7 +414,7 @@ SpectrumGUIClass::DecrementPendingGUIUpdateEvents() } void -SpectrumGUIClass::ResetPendingGUIUpdateEvents() +SpectrumGUIClass::resetPendingGUIUpdateEvents() { gruel::scoped_lock lock(d_mutex); _pendingGUIUpdateEventsCount = 0; @@ -433,41 +429,41 @@ SpectrumGUIClass::qwidget() } void -SpectrumGUIClass::SetTimeDomainAxis(double min, double max) +SpectrumGUIClass::setTimeDomainAxis(double min, double max) { gruel::scoped_lock lock(d_mutex); - _spectrumDisplayForm->SetTimeDomainAxis(min, max); + _spectrumDisplayForm->setTimeDomainAxis(min, max); } void -SpectrumGUIClass::SetConstellationAxis(double xmin, double xmax, +SpectrumGUIClass::setConstellationAxis(double xmin, double xmax, double ymin, double ymax) { gruel::scoped_lock lock(d_mutex); - _spectrumDisplayForm->SetConstellationAxis(xmin, xmax, ymin, ymax); + _spectrumDisplayForm->setConstellationAxis(xmin, xmax, ymin, ymax); } void -SpectrumGUIClass::SetConstellationPenSize(int size) +SpectrumGUIClass::setConstellationPenSize(int size) { gruel::scoped_lock lock(d_mutex); - _spectrumDisplayForm->SetConstellationPenSize(size); + _spectrumDisplayForm->setConstellationPenSize(size); } void -SpectrumGUIClass::SetFrequencyAxis(double min, double max) +SpectrumGUIClass::setFrequencyAxis(double min, double max) { gruel::scoped_lock lock(d_mutex); - _spectrumDisplayForm->SetFrequencyAxis(min, max); + _spectrumDisplayForm->setFrequencyAxis(min, max); } void -SpectrumGUIClass::SetUpdateTime(double t) +SpectrumGUIClass::setUpdateTime(double t) { gruel::scoped_lock lock(d_mutex); _updateTime = t; - _spectrumDisplayForm->SetUpdateTime(_updateTime); + _spectrumDisplayForm->setUpdateTime(_updateTime); } diff --git a/gr-qtgui/lib/SpectrumGUIClass.h b/gr-qtgui/lib/SpectrumGUIClass.h index e0612413b0..9a2233d168 100644 --- a/gr-qtgui/lib/SpectrumGUIClass.h +++ b/gr-qtgui/lib/SpectrumGUIClass.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -47,57 +47,57 @@ public: const double newStartFrequency, const double newStopFrequency); ~SpectrumGUIClass(); - void Reset(); + void reset(); - void OpenSpectrumWindow(QWidget*, + void openSpectrumWindow(QWidget*, const bool frequency=true, const bool waterfall=true, const bool time=true, const bool constellation=true); - void SetDisplayTitle(const std::string); + void setDisplayTitle(const std::string); - bool GetWindowOpenFlag(); - void SetWindowOpenFlag(const bool); + bool getWindowOpenFlag(); + void setWindowOpenFlag(const bool); - void SetFrequencyRange(const double, const double, const double); - double GetStartFrequency(); - double GetStopFrequency(); - double GetCenterFrequency(); + void setFrequencyRange(const double, const double, const double); + double getStartFrequency(); + 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, const gruel::high_res_timer_type, const bool); - float GetPowerValue(); - void SetPowerValue(const float); + float getPowerValue(); + void setPowerValue(const float); - int GetWindowType(); - void SetWindowType(const int); + int getWindowType(); + void setWindowType(const int); - int GetFFTSize(); - int GetFFTSizeIndex(); - void SetFFTSize(const int); + int getFFTSize(); + int getFFTSizeIndex(); + void setFFTSize(const int); - gruel::high_res_timer_type GetLastGUIUpdateTime(); - void SetLastGUIUpdateTime(const gruel::high_res_timer_type); + gruel::high_res_timer_type getLastGUIUpdateTime(); + void setLastGUIUpdateTime(const gruel::high_res_timer_type); - unsigned int GetPendingGUIUpdateEvents(); - void IncrementPendingGUIUpdateEvents(); - void DecrementPendingGUIUpdateEvents(); - void ResetPendingGUIUpdateEvents(); + unsigned int getPendingGUIUpdateEvents(); + void incrementPendingGUIUpdateEvents(); + void decrementPendingGUIUpdateEvents(); + void resetPendingGUIUpdateEvents(); static const long MAX_FFT_SIZE; static const long MIN_FFT_SIZE; QWidget* qwidget(); - void SetTimeDomainAxis(double min, double max); - void SetConstellationAxis(double xmin, double xmax, + void setTimeDomainAxis(double min, double max); + void setConstellationAxis(double xmin, double xmax, double ymin, double ymax); - void SetConstellationPenSize(int size); - void SetFrequencyAxis(double min, double max); + void setConstellationPenSize(int size); + void setFrequencyAxis(double min, double max); - void SetUpdateTime(double t); + void setUpdateTime(double t); protected: @@ -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..032da2547d 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,7 +28,9 @@ #include <qwt_scale_draw.h> #include <qwt_legend.h> #include <QColor> +#include <cmath> #include <iostream> +#include <volk/volk.h> class TimePrecisionClass { @@ -42,12 +44,12 @@ public: { } - virtual unsigned int GetTimePrecision() const + virtual unsigned int getTimePrecision() const { return _timePrecision; } - virtual void SetTimePrecision(const unsigned int newPrecision) + virtual void setTimePrecision(const unsigned int newPrecision) { _timePrecision = newPrecision; } @@ -73,18 +75,29 @@ public: updateDisplay(); } - void SetUnitType(const std::string &type) + void setUnitType(const std::string &type) { _unitType = type; } 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()). - arg(_unitType.c_str()). - arg(p.y(), 0, 'f', 4)); + QwtText t; + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + if((dp.y() > 0.0001) && (dp.y() < 10000)) { + t.setText(QString("%1 %2, %3 V"). + arg(dp.x(), 0, 'f', getTimePrecision()). + arg(_unitType.c_str()). + arg(dp.y(), 0, 'f', 4)); + } + else { + t.setText(QString("%1 %2, %3 V"). + arg(dp.x(), 0, 'f', getTimePrecision()). + arg(_unitType.c_str()). + arg(dp.y(), 0, 'e', 4)); + } return t; } @@ -93,11 +106,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 +123,17 @@ 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 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); -#if QWT_VERSION < 0x060000 - // We don't need the cache here - canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); - canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); -#endif + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); - QPalette palette; - palette.setColor(canvas()->backgroundRole(), QColor("white")); - canvas()->setPalette(palette); + d_semilogx = false; + d_semilogy = false; setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); setXaxis(0, _numPoints); @@ -132,7 +142,7 @@ TimeDomainDisplayPlot::TimeDomainDisplayPlot(int nplots, QWidget* parent) setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); setYaxis(-2.0, 2.0); setAxisTitle(QwtPlot::yLeft, "Amplitude"); - + QList<QColor> colors; colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) << QColor(Qt::black) << QColor(Qt::cyan) << QColor(Qt::magenta) @@ -148,58 +158,22 @@ TimeDomainDisplayPlot::TimeDomainDisplayPlot(int nplots, QWidget* parent) _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); _plot_curve[i]->attach(this); _plot_curve[i]->setPen(QPen(colors[i])); + _plot_curve[i]->setRenderHint(QwtPlotItem::RenderAntialiased); + 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,102 +186,123 @@ 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 + } + + _resetXAxisPoints(); } - setXaxis(0, numDataPoints); - _resetXAxisPoints(); - } + for(int i = 0; i < _nplots; i++) { + if(d_semilogy) { + for(int n = 0; n < numDataPoints; n++) + _dataPoints[i][n] = fabs(dataPoints[i][n]); + } + else { + memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); + } + } - for(int i = 0; i < _nplots; i++) { - memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); + if(_autoscale_state) { + double bottom=1e20, top=-1e20; + for(int n = 0; n < _nplots; n++) { + for(int64_t point = 0; point < numDataPoints; point++) { + if(_dataPoints[n][point] < bottom) { + bottom = _dataPoints[n][point]; + } + if(_dataPoints[n][point] > top) { + top = _dataPoints[n][point]; + } + } + } + _autoScale(bottom, top); + } + + replot(); } - - replot(); } } -void TimeDomainDisplayPlot::_resetXAxisPoints() +void +TimeDomainDisplayPlot::_resetXAxisPoints() { double delt = 1.0/_sampleRate; for(long loc = 0; loc < _numPoints; loc++){ _xAxisPoints[loc] = loc*delt; } - setAxisScale(QwtPlot::xBottom, 0, _numPoints*delt); // Set up zoomer base for maximum unzoom x-axis // and reset to maximum unzoom level QwtDoubleRect zbase = _zoomer->zoomBase(); - zbase.setLeft(0); + + if(d_semilogx) { + setAxisScale(QwtPlot::xBottom, 1e-1, _numPoints*delt); + zbase.setLeft(1e-1); + } + else { + setAxisScale(QwtPlot::xBottom, 0, _numPoints*delt); + zbase.setLeft(0); + } + zbase.setRight(_numPoints*delt); _zoomer->zoom(zbase); _zoomer->setZoomBase(zbase); _zoomer->zoom(0); + } -void TimeDomainDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on) +void +TimeDomainDisplayPlot::_autoScale(double bottom, double top) { - plotItem->setVisible(!on); + // Auto scale the y-axis with a margin of 20% (10 dB for log scale) + double _bot = bottom - fabs(bottom)*0.20; + double _top = top + fabs(top)*0.20; + if(d_semilogy) { + if(bottom > 0) { + setYaxis(_bot-10, _top+10); + } + else { + setYaxis(1e-3, _top+10); + } + } + else { + setYaxis(_bot, _top); + } +} + +void +TimeDomainDisplayPlot::setAutoScale(bool state) +{ + _autoscale_state = state; } void -TimeDomainDisplayPlot::SetSampleRate(double sr, double units, +TimeDomainDisplayPlot::setSampleRate(double sr, double units, const std::string &strunits) { double newsr = sr/units; @@ -320,26 +315,56 @@ TimeDomainDisplayPlot::SetSampleRate(double sr, double units, //double display_units = ceil(log10(units)/2.0); double display_units = 4; setAxisTitle(QwtPlot::xBottom, QString("Time (%1)").arg(strunits.c_str())); - ((TimeDomainDisplayZoomer*)_zoomer)->SetTimePrecision(display_units); - ((TimeDomainDisplayZoomer*)_zoomer)->SetUnitType(strunits); + ((TimeDomainDisplayZoomer*)_zoomer)->setTimePrecision(display_units); + ((TimeDomainDisplayZoomer*)_zoomer)->setUnitType(strunits); } } +void +TimeDomainDisplayPlot::stemPlot(bool en) +{ + if(en) { + for(int i = 0; i < _nplots; i++) { + _plot_curve[i]->setStyle(QwtPlotCurve::Sticks); + setLineMarker(i, QwtSymbol::Ellipse); + } + } + else { + for(int i = 0; i < _nplots; i++) { + _plot_curve[i]->setStyle(QwtPlotCurve::Lines); + setLineMarker(i, QwtSymbol::NoSymbol); + } + } +} void -TimeDomainDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +TimeDomainDisplayPlot::setSemilogx(bool en) { - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); + d_semilogx = en; + if(!d_semilogx) { + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + } + else { + setAxisScaleEngine(QwtPlot::xBottom, new QwtLog10ScaleEngine); + } + _resetXAxisPoints(); } void -TimeDomainDisplayPlot::OnPickerPointSelected6(const QPointF & p) +TimeDomainDisplayPlot::setSemilogy(bool en) { - QPointF point = p; - //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); - emit plotPointSelected(point); + if(d_semilogy != en) { + d_semilogy = en; + double max = axisScaleDiv(QwtPlot::yLeft)->upperBound(); + if(!d_semilogy) { + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setYaxis(-pow(10.0, max/10.0), pow(10.0, max/10.0)); + } + else { + setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine); + setYaxis(1e-10, 10.0*log10(max)); + } + } } #endif /* TIME_DOMAIN_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/TimeDomainDisplayPlot.h b/gr-qtgui/lib/TimeDomainDisplayPlot.h index 356da25ad4..9996f84fc3 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,83 +20,48 @@ * 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: TimeDomainDisplayPlot(int nplots, QWidget*); virtual ~TimeDomainDisplayPlot(); - void PlotNewData(const std::vector<double*> dataPoints, + 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 stemPlot(bool en); - void resizeSlot( QSize *s ); - void SetSampleRate(double sr, double units, +public slots: + 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: + void setAutoScale(bool state); + void setSemilogx(bool en); + void setSemilogy(bool en); private: void _resetXAxisPoints(); - - int _nplots; - std::vector<QwtPlotCurve*> _plot_curve; - - QwtPlotPanner* _panner; - QwtPlotZoomer* _zoomer; - - QwtDblClickPlotPicker *_picker; - QwtPlotMagnifier *_magnifier; + void _autoScale(double bottom, double top); std::vector<double*> _dataPoints; double* _xAxisPoints; double _sampleRate; - int64_t _numPoints; + bool d_semilogx; + bool d_semilogy; }; -#endif /* TIME_DOMAIN_DISPLAY_PLOT_HPP */ +#endif /* TIME_DOMAIN_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/TimeRasterDisplayPlot.cc b/gr-qtgui/lib/TimeRasterDisplayPlot.cc new file mode 100644 index 0000000000..48fb7f7b4a --- /dev/null +++ b/gr-qtgui/lib/TimeRasterDisplayPlot.cc @@ -0,0 +1,570 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_PLOT_C +#define TIMERASTER_DISPLAY_PLOT_C + +#include <TimeRasterDisplayPlot.h> + +#include "qtgui_types.h" +#include <qwt_color_map.h> +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <qwt_legend_item.h> +#include <qwt_plot_layout.h> +#include <QColor> +#include <iostream> + +#include <boost/date_time/posix_time/posix_time.hpp> +namespace pt = boost::posix_time; + +#include <QDebug> + +/*********************************************************************** + * Text scale widget to provide X (time) axis text + **********************************************************************/ +class QwtXScaleDraw: public QwtScaleDraw, public TimeScaleData +{ +public: + QwtXScaleDraw():QwtScaleDraw(),TimeScaleData() { } + + virtual ~QwtXScaleDraw() { } + + virtual QwtText label(double value) const + { + double secs = double(value * getSecondsPerLine()); + return QwtText(QString("").sprintf("%.2f", secs)); + } + + virtual void initiateUpdate() + { + // Do this in one call rather than when zeroTime and secondsPerLine + // updates is to prevent the display from being updated too often... + invalidateCache(); + } +}; + +/*********************************************************************** + * Text scale widget to provide Y axis text + **********************************************************************/ +class QwtYScaleDraw: public QwtScaleDraw +{ +public: + QwtYScaleDraw(): QwtScaleDraw(), _rows(0) { } + + virtual ~QwtYScaleDraw() { } + + virtual QwtText label(double value) const + { + if(_rows > 0) + value = _rows - value; + return QwtText(QString("").sprintf("%.0f", value)); + } + + virtual void initiateUpdate() + { + // Do this in one call rather than when zeroTime and secondsPerLine + // updates is to prevent the display from being updated too often... + invalidateCache(); + } + + void setRows(double rows) { rows>0 ? _rows = rows : _rows = 0; } + +private: + double _rows; +}; + +class TimePrecisionClass +{ +public: + TimePrecisionClass(const int timePrecision) + { + _timePrecision = timePrecision; + } + + virtual ~TimePrecisionClass() + { + } + + virtual unsigned int getTimePrecision() const + { + return _timePrecision; + } + + virtual void setTimePrecision(const unsigned int newPrecision) + { + _timePrecision = newPrecision; + } +protected: + unsigned int _timePrecision; +}; + +/*********************************************************************** + * Widget to provide mouse pointer coordinate text + **********************************************************************/ +class TimeRasterZoomer: public QwtPlotZoomer, public TimePrecisionClass, + public TimeScaleData +{ +public: + TimeRasterZoomer(QwtPlotCanvas* canvas, double rows, double cols, + const unsigned int timePrecision) + : QwtPlotZoomer(canvas), TimePrecisionClass(timePrecision), TimeScaleData(), + d_rows(static_cast<double>(rows)), d_cols(static_cast<double>(cols)) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~TimeRasterZoomer() + { + } + + virtual void updateTrackerText() + { + updateDisplay(); + } + + void setUnitType(const std::string &type) + { + _unitType = type; + } + + void setColumns(const double cols) + { + d_cols = cols; + } + + void setRows(const double rows) + { + d_rows = rows; + } + +protected: + using QwtPlotZoomer::trackerText; + virtual QwtText trackerText( QPoint const &p ) const + { + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + double x = dp.x() * getSecondsPerLine(); + //double y = dp.y() * getSecondsPerLine() * d_cols; + double y = floor(d_rows - dp.y()); + QwtText t(QString("%1 %2, %3") + .arg(x, 0, 'f', getTimePrecision()) + .arg(_unitType.c_str()) + .arg(y, 0, 'f', 0)); + return t; + } + +private: + std::string _unitType; + double d_rows, d_cols; +}; + +/********************************************************************* +* Main time raster plot widget +*********************************************************************/ +TimeRasterDisplayPlot::TimeRasterDisplayPlot(int nplots, + double samp_rate, + double rows, double cols, + QWidget* parent) + : DisplayPlot(nplots, parent) +{ + _zoomer = NULL; // need this for proper init + + resize(parent->width(), parent->height()); + + d_samp_rate = samp_rate; + d_cols = cols; + d_rows = rows; + _numPoints = d_cols; + + setAxisScaleDraw(QwtPlot::xBottom, new QwtXScaleDraw()); + setAxisScaleDraw(QwtPlot::yLeft, new QwtYScaleDraw()); + + for(int i = 0; i < _nplots; i++) { + d_data.push_back(new TimeRasterData(d_rows, d_cols)); + d_raster.push_back(new PlotTimeRaster("Raster")); + d_raster[i]->setData(d_data[i]); + + // a hack around the fact that we aren't using plot curves for the + // raster plots. + _plot_curve.push_back(new QwtPlotCurve(QString("Data"))); + + d_raster[i]->attach(this); + + d_color_map_type.push_back(INTENSITY_COLOR_MAP_TYPE_BLACK_HOT); + setAlpha(i, 255/_nplots); + } + + // Set bottom plot with no transparency as a base + setAlpha(0, 255); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + _zoomer = new TimeRasterZoomer(canvas(), d_rows, d_cols, 0); +#if QWT_VERSION < 0x060000 + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); +#endif + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + const QColor c(Qt::red); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + // Set intensity color now (needed _zoomer before we could do this). + // We've made sure the old type is different than here so we'll + // force and update. + for(int i = 0; i < _nplots; i++) { + setIntensityColorMapType(i, INTENSITY_COLOR_MAP_TYPE_WHITE_HOT, + QColor("white"), QColor("white")); + } + + _updateIntensityRangeDisplay(); + + reset(); +} + +TimeRasterDisplayPlot::~TimeRasterDisplayPlot() +{ +} + +void +TimeRasterDisplayPlot::reset() +{ + for(int i = 0; i < _nplots; i++) { + d_data[i]->resizeData(d_rows, d_cols); + d_data[i]->reset(); + } + + // Update zoomer/picker text units + std::string strunits[4] = {"sec", "ms", "us", "ns"}; + double units10 = floor(log10(d_samp_rate)); + double units3 = std::max(floor(units10/3), 0.0); + double units = pow(10, (units10-fmod(units10, 3.0))); + int iunit = static_cast<int>(units3); + + double sec_per_samp = units/d_samp_rate; + + QwtYScaleDraw* yScale = (QwtYScaleDraw*)axisScaleDraw(QwtPlot::yLeft); + yScale->setRows(d_rows); + + QwtXScaleDraw* xScale = (QwtXScaleDraw*)axisScaleDraw(QwtPlot::xBottom); + xScale->setSecondsPerLine(sec_per_samp); + setAxisTitle(QwtPlot::xBottom, QString("Time (%1)") + .arg(strunits[iunit].c_str())); + xScale->initiateUpdate(); + + // Load up the new base zoom settings + if(_zoomer) { + double display_units = 4; + ((TimeRasterZoomer*)_zoomer)->setColumns(d_cols); + ((TimeRasterZoomer*)_zoomer)->setRows(d_rows); + ((TimeRasterZoomer*)_zoomer)->setSecondsPerLine(sec_per_samp); + ((TimeRasterZoomer*)_zoomer)->setTimePrecision(display_units); + ((TimeRasterZoomer*)_zoomer)->setUnitType(strunits[iunit]); + + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(0); + newSize.setWidth(d_cols); + newSize.setBottom(0); + newSize.setHeight(d_rows); + + _zoomer->zoom(newSize); + _zoomer->setZoomBase(newSize); + _zoomer->zoom(0); + } +} + +void +TimeRasterDisplayPlot::setNumRows(double rows) +{ + d_rows = rows; + reset(); +} + +void +TimeRasterDisplayPlot::setNumCols(double cols) +{ + d_cols = cols; + reset(); +} + +void +TimeRasterDisplayPlot::setAlpha(int which, int alpha) +{ + d_raster[which]->setAlpha(alpha); +} + +void +TimeRasterDisplayPlot::setSampleRate(double samprate) +{ + d_samp_rate = samprate; + reset(); +} + +double +TimeRasterDisplayPlot::numRows() const +{ + return d_rows; +} + +double +TimeRasterDisplayPlot::numCols() const +{ + return d_cols; +} + +int +TimeRasterDisplayPlot::getAlpha(int which) +{ + return d_raster[which]->alpha(); +} + +void +TimeRasterDisplayPlot::setPlotDimensions(const double rows, const double cols, + const double units, const std::string &strunits) +{ + bool rst = false; + if((rows != d_rows) || (cols != d_cols)) + rst = true; + + d_rows = rows; + d_cols = cols; + + if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)) { + if(rst) { + reset(); + } + } +} + +void +TimeRasterDisplayPlot::plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints) +{ + if(!_stop) { + if(numDataPoints > 0) { + for(int i = 0; i < _nplots; i++) { + d_data[i]->addData(dataPoints[i], numDataPoints); + d_raster[i]->invalidateCache(); + d_raster[i]->itemChanged(); + } + + replot(); + } + } +} + +void +TimeRasterDisplayPlot::plotNewData(const double* dataPoints, + const int64_t numDataPoints) +{ + std::vector<double*> vecDataPoints; + vecDataPoints.push_back((double*)dataPoints); + plotNewData(vecDataPoints, numDataPoints); +} + +void +TimeRasterDisplayPlot::setIntensityRange(const double minIntensity, + const double maxIntensity) +{ + for(int i = 0; i < _nplots; i++) { +#if QWT_VERSION < 0x060000 + d_data[i]->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); +#else + d_data[i]->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); +#endif + + emit updatedLowerIntensityLevel(minIntensity); + emit updatedUpperIntensityLevel(maxIntensity); + + _updateIntensityRangeDisplay(); + } +} + +double +TimeRasterDisplayPlot::getMinIntensity(int which) const +{ +#if QWT_VERSION < 0x060000 + QwtDoubleInterval r = d_data[which]->range(); +#else + QwtInterval r = d_data[which]->interval(Qt::ZAxis); +#endif + + return r.minValue(); +} + +double +TimeRasterDisplayPlot::getMaxIntensity(int which) const +{ +#if QWT_VERSION < 0x060000 + QwtDoubleInterval r = d_data[which]->range(); +#else + QwtInterval r = d_data[which]->interval(Qt::ZAxis); +#endif + + return r.maxValue(); +} + +void +TimeRasterDisplayPlot::replot() +{ + // Update the x-axis display + if(axisWidget(QwtPlot::yLeft) != NULL) { + axisWidget(QwtPlot::yLeft)->update(); + } + + // Update the y-axis display + if(axisWidget(QwtPlot::xBottom) != NULL) { + axisWidget(QwtPlot::xBottom)->update(); + } + + if(_zoomer != NULL) { + ((TimeRasterZoomer*)_zoomer)->updateTrackerText(); + } + + QwtPlot::replot(); +} + +int +TimeRasterDisplayPlot::getIntensityColorMapType(int which) const +{ + if(which >= (int)d_color_map_type.size()) + throw std::runtime_error("TimerasterDisplayPlot::GetIntesityColorMap: invalid which.\n"); + + return d_color_map_type[which]; +} + +void +TimeRasterDisplayPlot::setIntensityColorMapType(const int which, + const int newType, + const QColor lowColor, + const QColor highColor) +{ + if(which >= (int)d_color_map_type.size()) + throw std::runtime_error("TimerasterDisplayPlot::setIntesityColorMap: invalid which.\n"); + + if((d_color_map_type[which] != newType) || + ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && + (lowColor.isValid() && highColor.isValid()))) { + switch(newType) { + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR: { + d_color_map_type[which] = newType; + + d_raster[which]->setColorMap(new ColorMap_MultiColor()); + if(_zoomer) + _zoomer->setTrackerPen(QColor(Qt::black)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_WhiteHot()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_BlackHot()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT: { + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_Incandescent()); + break; + } + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED: { + d_low_intensity = lowColor; + d_high_intensity = highColor; + d_color_map_type[which] = newType; + d_raster[which]->setColorMap(new ColorMap_UserDefined(lowColor, highColor)); + break; + } + default: break; + } + + _updateIntensityRangeDisplay(); + } +} + +const QColor +TimeRasterDisplayPlot::getUserDefinedLowIntensityColor() const +{ + return d_low_intensity; +} + +const QColor +TimeRasterDisplayPlot::getUserDefinedHighIntensityColor() const +{ + return d_high_intensity; +} + +void +TimeRasterDisplayPlot::_updateIntensityRangeDisplay() +{ + QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight); + rightAxis->setTitle("Intensity"); + rightAxis->setColorBarEnabled(true); + + for(int i = 0; i < _nplots; i++) { +#if QWT_VERSION < 0x060000 + rightAxis->setColorMap(d_raster[i]->data()->range(), + d_raster[i]->colorMap()); + setAxisScale(QwtPlot::yRight, + d_raster[i]->data()->range().minValue(), + d_raster[i]->data()->range().maxValue()); +#else + QwtInterval intv = d_raster[i]->interval(Qt::ZAxis); + switch(d_color_map_type[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(d_low_intensity, + d_high_intensity)); + break; + default: + rightAxis->setColorMap(intv, new ColorMap_MultiColor()); break; + } + setAxisScale(QwtPlot::yRight, intv.minValue(), intv.maxValue()); +#endif + + enableAxis(QwtPlot::yRight); + + plotLayout()->setAlignCanvasToScales(true); + + // Tell the display to redraw everything + d_raster[i]->invalidateCache(); + d_raster[i]->itemChanged(); + } + + // Draw again + replot(); +} + +#endif /* TIMERASTER_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/TimeRasterDisplayPlot.h b/gr-qtgui/lib/TimeRasterDisplayPlot.h new file mode 100644 index 0000000000..e4e292a54d --- /dev/null +++ b/gr-qtgui/lib/TimeRasterDisplayPlot.h @@ -0,0 +1,104 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_PLOT_H +#define TIMERASTER_DISPLAY_PLOT_H + +#include <stdint.h> +#include <cstdio> +#include <vector> +#include <qwt_plot_rasteritem.h> +#include "DisplayPlot.h" +#include "timeRasterGlobalData.h" +#include "plot_raster.h" +#include <gruel/high_res_timer.h> + +#if QWT_VERSION < 0x060000 +#include <plot_waterfall.h> +#else +#include <qwt_compat.h> +#endif + +class TimeRasterDisplayPlot: public DisplayPlot +{ + Q_OBJECT + +public: + TimeRasterDisplayPlot(int nplots, + double samp_rate, + double rows, double cols, + QWidget*); + virtual ~TimeRasterDisplayPlot(); + + void reset(); + + void setNumRows(double rows); + void setNumCols(double cols); + void setAlpha(int which, int alpha); + void setSampleRate(double samprate); + + double numRows() const; + double numCols() const; + + int getAlpha(int which); + + void setPlotDimensions(const double rows, const double cols, + const double units, const std::string &strunits); + + void plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints); + + void plotNewData(const double* dataPoints, + const int64_t numDataPoints); + + void setIntensityRange(const double minIntensity, + const double maxIntensity); + + void replot(void); + + int getIntensityColorMapType(int) const; + void setIntensityColorMapType(const int, const int, const QColor, const QColor); + const QColor getUserDefinedLowIntensityColor() const; + const QColor getUserDefinedHighIntensityColor() const; + + double getMinIntensity(int which) const; + double getMaxIntensity(int which) const; + +signals: + void updatedLowerIntensityLevel(const double); + void updatedUpperIntensityLevel(const double); + +private: + void _updateIntensityRangeDisplay(); + + std::vector<TimeRasterData*> d_data; + std::vector<PlotTimeRaster*> d_raster; + + double d_samp_rate; + double d_rows, d_cols; + + std::vector<int> d_color_map_type; + QColor d_low_intensity; + QColor d_high_intensity; +}; + +#endif /* TIMERASTER_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.cc b/gr-qtgui/lib/WaterfallDisplayPlot.cc index 63eb57ffe8..05c89e3ecb 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,141 +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: - -}; - -static QString -make_time_label(double secs) -{ - std::string time_str = pt::to_simple_string(pt::from_time_t(time_t(secs))); - - // lops off the YYYY-mmm-DD part of the string - size_t ind = time_str.find(" "); - if(ind != std::string::npos) - time_str = time_str.substr(ind); - - return QString("").sprintf("%s.%03ld", time_str.c_str(), long(std::fmod(secs*1000, 1000))); -} +#include <QDebug> +/*********************************************************************** + * Text scale widget to provide Y (time) axis text + **********************************************************************/ class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData { public: @@ -173,8 +55,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 +72,10 @@ private: }; -class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, +/*********************************************************************** + * Widget to provide mouse pointer coordinate text + **********************************************************************/ +class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass { public: @@ -210,19 +95,21 @@ public: updateDisplay(); } - void SetUnitType(const std::string &type) + void setUnitType(const std::string &type) { _unitType = type; } 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 +117,56 @@ 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; - _startFrequency = 0; - _stopFrequency = 4000; + _zoomer = NULL; // need this for proper init + _startFrequency = -1; + _stopFrequency = 1; 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; - - 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(INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR); + setIntensityColorMapType(i, _intensityColorMapType[i], + QColor("white"), QColor("white")); + + setAlpha(i, 255/_nplots); + } + + // Set bottom plot with no transparency as a base + setAlpha(0, 255); // LeftButton for the zooming // MidButton for the panning @@ -338,79 +181,49 @@ 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); - _UpdateIntensityRangeDisplay(); + _updateIntensityRangeDisplay(); _xAxisMultiplier = 1; } WaterfallDisplayPlot::~WaterfallDisplayPlot() { - delete d_data; - delete d_spectrogram; } void -WaterfallDisplayPlot::Reset() +WaterfallDisplayPlot::resetAxis() { - 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); // Load up the new base zoom settings - QwtDoubleRect newSize = _zoomer->zoomBase(); - newSize.setLeft(_startFrequency); - newSize.setWidth(_stopFrequency-_startFrequency); - _zoomer->zoom(newSize); - _zoomer->setZoomBase(newSize); + QwtDoubleRect zbase = _zoomer->zoomBase(); + zbase.setLeft(_startFrequency); + zbase.setRight(_stopFrequency); + _zoomer->zoom(zbase); + _zoomer->setZoomBase(zbase); + _zoomer->setZoomBase(true); _zoomer->zoom(0); } void -WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq, - const double constStopFreq, - const double constCenterFreq, - const bool useCenterFrequencyFlag, +WaterfallDisplayPlot::setFrequencyRange(const double centerfreq, + const double bandwidth, const double units, const std::string &strunits) { - double startFreq = constStartFreq / units; - double stopFreq = constStopFreq / units; - double centerFreq = constCenterFreq / units; + double startFreq = (centerfreq - bandwidth/2.0f) / units; + double stopFreq = (centerfreq + bandwidth/2.0f) / units; _xAxisMultiplier = units; - _useCenterFrequencyFlag = useCenterFrequencyFlag; - - if(_useCenterFrequencyFlag){ - startFreq = (startFreq + centerFreq); - stopFreq = (stopFreq + centerFreq); - } - bool reset = false; if((startFreq != _startFrequency) || (stopFreq != _stopFrequency)) reset = true; @@ -419,92 +232,135 @@ 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) { - Reset(); + resetAxis(); } - ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision(display_units); - ((WaterfallZoomer*)_zoomer)->SetUnitType(strunits); + ((WaterfallZoomer*)_zoomer)->setFrequencyPrecision(display_units); + ((WaterfallZoomer*)_zoomer)->setUnitType(strunits); } } } double -WaterfallDisplayPlot::GetStartFrequency() const +WaterfallDisplayPlot::getStartFrequency() const { return _startFrequency; } double -WaterfallDisplayPlot::GetStopFrequency() const +WaterfallDisplayPlot::getStopFrequency() const { return _stopFrequency; } 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(); + resetAxis(); - d_spectrogram->invalidateCache(); - d_spectrogram->itemChanged(); + for(int i = 0; i < _nplots; i++) { + d_spectrogram[i]->invalidateCache(); + d_spectrogram[i]->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); - timeScale->SetZeroTime(timestamp); + timeScale->setSecondsPerLine(timePerFFT); + timeScale->setZeroTime(timestamp); - ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT); - ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp); + ((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::SetIntensityRange(const double minIntensity, +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[i]->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); +#else + d_data[i]->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); +#endif + + emit updatedLowerIntensityLevel(minIntensity); + emit updatedUpperIntensityLevel(maxIntensity); + + _updateIntensityRangeDisplay(); + } +} + +double +WaterfallDisplayPlot::getMinIntensity(int which) const +{ #if QWT_VERSION < 0x060000 - d_data->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); + QwtDoubleInterval r = d_data[which]->range(); #else - d_data->setInterval(Qt::ZAxis, QwtInterval(minIntensity, maxIntensity)); + QwtInterval r = d_data[which]->interval(Qt::ZAxis); #endif - emit UpdatedLowerIntensityLevel(minIntensity); - emit UpdatedUpperIntensityLevel(maxIntensity); + return r.minValue(); +} - _UpdateIntensityRangeDisplay(); +double +WaterfallDisplayPlot::getMaxIntensity(int which) const +{ +#if QWT_VERSION < 0x060000 + QwtDoubleInterval r = d_data[which]->range(); +#else + QwtInterval r = d_data[which]->interval(Qt::ZAxis); +#endif + + return r.maxValue(); } void @@ -513,8 +369,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 @@ -535,137 +391,179 @@ WaterfallDisplayPlot::replot() } void -WaterfallDisplayPlot::resizeSlot( QSize *s ) +WaterfallDisplayPlot::clearData() { - resize(s->width(), s->height()); + for(int i = 0; i < _nplots; i++) { + d_data[i]->reset(); + } } + 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; } default: break; } - _UpdateIntensityRangeDisplay(); + _updateIntensityRangeDisplay(); } } +void +WaterfallDisplayPlot::setIntensityColorMapType1(int newType) +{ + setIntensityColorMapType(0, newType, _userDefinedLowIntensityColor, _userDefinedHighIntensityColor); +} + +int +WaterfallDisplayPlot::getIntensityColorMapType1() const +{ + return getIntensityColorMapType(0); +} + +void +WaterfallDisplayPlot::setUserDefinedLowIntensityColor(QColor c) +{ + _userDefinedLowIntensityColor = c; +} + const QColor -WaterfallDisplayPlot::GetUserDefinedLowIntensityColor() const +WaterfallDisplayPlot::getUserDefinedLowIntensityColor() const { return _userDefinedLowIntensityColor; } +void +WaterfallDisplayPlot::setUserDefinedHighIntensityColor(QColor c) +{ + _userDefinedHighIntensityColor = c; +} + const QColor -WaterfallDisplayPlot::GetUserDefinedHighIntensityColor() const +WaterfallDisplayPlot::getUserDefinedHighIntensityColor() const { return _userDefinedHighIntensityColor; } +int +WaterfallDisplayPlot::getAlpha(int which) +{ + return d_spectrogram[which]->alpha(); +} + +void +WaterfallDisplayPlot::setAlpha(int which, int alpha) +{ + d_spectrogram[which]->setAlpha(alpha); +} + void -WaterfallDisplayPlot::_UpdateIntensityRangeDisplay() +WaterfallDisplayPlot::_updateIntensityRangeDisplay() { QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight); 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 +572,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..6637adc594 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,88 +37,85 @@ #include <qwt_compat.h> #endif -class WaterfallDisplayPlot:public QwtPlot{ +class WaterfallDisplayPlot: public DisplayPlot +{ Q_OBJECT + Q_PROPERTY ( int intensity_color_map_type1 READ getIntensityColorMapType1 WRITE setIntensityColorMapType1 ) + Q_PROPERTY ( QColor low_intensity_color READ getUserDefinedLowIntensityColor WRITE setUserDefinedLowIntensityColor ) + Q_PROPERTY ( QColor high_intensity_color READ getUserDefinedHighIntensityColor WRITE setUserDefinedHighIntensityColor ) + + public: - WaterfallDisplayPlot(QWidget*); + WaterfallDisplayPlot(int nplots, QWidget*); virtual ~WaterfallDisplayPlot(); - void Reset(); + void resetAxis(); - void SetFrequencyRange(const double, const double, - const double, const bool, + void setFrequencyRange(const double, const double, 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); + // 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); - virtual void replot(void); + void setIntensityRange(const double minIntensity, const double maxIntensity); + double getMinIntensity(int which) const; + double getMaxIntensity(int which) const; - int GetIntensityColorMapType()const; - void SetIntensityColorMapType( const int, const QColor, const QColor ); - const QColor GetUserDefinedLowIntensityColor()const; - const QColor GetUserDefinedHighIntensityColor()const; + void replot(void); + void clearData(); - 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 - }; + int getIntensityColorMapType(int) const; + int getIntensityColorMapType1() const; + const QColor getUserDefinedLowIntensityColor() const; + const QColor getUserDefinedHighIntensityColor() const; -public slots: - void resizeSlot( QSize *s ); + int getAlpha(int which); + void setAlpha(int which, int alpha); - // 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); +public slots: + void setIntensityColorMapType(const int, const int, const QColor, const QColor); + void setIntensityColorMapType1(int); + void setUserDefinedLowIntensityColor(QColor); + void setUserDefinedHighIntensityColor(QColor); signals: - void UpdatedLowerIntensityLevel(const double); - void UpdatedUpperIntensityLevel(const double); - void plotPointSelected(const QPointF p); - -protected: + void updatedLowerIntensityLevel(const double); + void updatedUpperIntensityLevel(const double); private: - void _UpdateIntensityRangeDisplay(); + void _updateIntensityRangeDisplay(); double _startFrequency; 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..b623dc2175 --- /dev/null +++ b/gr-qtgui/lib/const_sink_c_impl.cc @@ -0,0 +1,357 @@ +/* -*- 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> +#include <qwt_symbol.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() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + // 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]); + } + } + + bool + const_sink_c_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + } + + 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_y_axis(double min, double max) + { + d_main_gui->setYaxis(min, max); + } + + void + const_sink_c_impl::set_x_axis(double min, double max) + { + d_main_gui->setXaxis(min, max); + } + + 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); + d_last_time = 0; + } + + void + const_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + const_sink_c_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + const_sink_c_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(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, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + const_sink_c_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + const_sink_c_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + const_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + const_sink_c_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + const_sink_c_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + const_sink_c_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + const_sink_c_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + const_sink_c_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + const_sink_c_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + const_sink_c_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + 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::nsamps() const + { + return d_size; + } + + void + const_sink_c_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + const_sink_c_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + const_sink_c_impl::reset() + { + d_index = 0; + } + + 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..81cdb9881f --- /dev/null +++ b/gr-qtgui/lib/const_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_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(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_y_axis(double min, double max); + void set_x_axis(double min, double max); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_nsamps(const int size); + void set_line_alpha(int which, double alpha); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + int nsamps() const; + void enable_menu(bool en); + void enable_autoscale(bool en); + void reset(); + + 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..e3c65ec41d --- /dev/null +++ b/gr-qtgui/lib/constellationdisplayform.cc @@ -0,0 +1,122 @@ +/* -*- 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; +} + +void +ConstellationDisplayForm::setYaxis(double min, double max) +{ + getPlot()->set_yaxis(min, max); +} + +void +ConstellationDisplayForm::setXaxis(double min, double max) +{ + getPlot()->set_xaxis(min, max); +} + +void +ConstellationDisplayForm::autoScale(bool en) +{ + _autoscale_state = en; + _autoscale_act->setChecked(en); + getPlot()->setAutoScale(_autoscale_state); + getPlot()->replot(); +} + +void +ConstellationDisplayForm::setSampleRate(const QString &samprate) +{ +} diff --git a/gr-qtgui/lib/constellationdisplayform.h b/gr-qtgui/lib/constellationdisplayform.h new file mode 100644 index 0000000000..06a8e7ed5f --- /dev/null +++ b/gr-qtgui/lib/constellationdisplayform.h @@ -0,0 +1,62 @@ +/* -*- 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); + + void setSampleRate(const QString &samprate); + void setYaxis(double min, double max); + void setXaxis(double min, double max); + void autoScale(bool en); + +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..276539cdde --- /dev/null +++ b/gr-qtgui/lib/displayform.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. + */ + +#include <displayform.h> +#include <iostream> +#include <QPixmap> +#include <QFileDialog> + +DisplayForm::DisplayForm(int nplots, QWidget* parent) + : QWidget(parent), _nplots(nplots), _systemSpecifiedFlag(false) +{ + _isclosed = 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", this); + _grid_act->setCheckable(true); + _grid_act->setStatusTip(tr("Toggle Grid on/off")); + connect(_grid_act, SIGNAL(triggered(bool)), + this, SLOT(setGrid(bool))); + _grid_state = false; + + // Create a pop-up menu for manipulating the figure + _menu_on = true; + _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)); + _marker_alpha_menu.push_back(new MarkerAlphaMenu(i, this)); + + connect(_line_title_act[i], SIGNAL(whichTrigger(int, const QString&)), + this, SLOT(setLineLabel(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(setLineColor(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))); + } + + for(int j = 0; j < _marker_alpha_menu[i]->getNumActions(); j++) { + connect(_marker_alpha_menu[i], SIGNAL(whichTrigger(int, int)), + this, SLOT(setMarkerAlpha(int, int))); + } + + _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]); + _lines_menu[i]->addMenu(_marker_alpha_menu[i]); + _menu->addMenu(_lines_menu[i]); + } + + _samp_rate_act = new PopupMenu("Sample Rate", this); + _samp_rate_act->setStatusTip(tr("Set Sample Rate")); + connect(_samp_rate_act, SIGNAL(whichTrigger(QString)), + this, SLOT(setSampleRate(QString))); + _menu->addAction(_samp_rate_act); + + _autoscale_act = new QAction("Auto Scale", this); + _autoscale_act->setStatusTip(tr("Autoscale Plot")); + _autoscale_act->setCheckable(true); + connect(_autoscale_act, SIGNAL(triggered(bool)), + this, SLOT(autoScale(bool))); + _autoscale_state = false; + _menu->addAction(_autoscale_act); + + _save_act = new QAction("Save", this); + _save_act->setStatusTip(tr("Save Figure")); + connect(_save_act, SIGNAL(triggered()), this, SLOT(saveFigure())); + _menu->addAction(_save_act); + + 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() +{ + _isclosed = true; + + // 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) +{ + bool ctrloff = Qt::ControlModifier != QApplication::keyboardModifiers(); + if((e->button() == Qt::MiddleButton) && ctrloff && (_menu_on)) { + if(_stop_state == false) + _stop_act->setText(tr("Stop")); + else + _stop_act->setText(tr("Start")); + + // Update the line titles if changed externally + for(int i = 0; i < _nplots; i++) { + _lines_menu[i]->setTitle(_displayPlot->getLineLabel(i)); + } + _menu->exec(e->globalPos()); + } +} + +void +DisplayForm::updateGuiTimer() +{ + _displayPlot->canvas()->update(); +} + +void +DisplayForm::onPlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 3); +} + +void +DisplayForm::Reset() +{ +} + +bool +DisplayForm::isClosed() const +{ + return _isclosed; +} + +void +DisplayForm::enableMenu(bool en) +{ + _menu_on = en; +} + +void +DisplayForm::closeEvent(QCloseEvent *e) +{ + _isclosed = true; + qApp->processEvents(); + QWidget::closeEvent(e); +} + +void +DisplayForm::setUpdateTime(double t) +{ + d_update_time = t; + d_displayTimer->start(d_update_time); +} + +void +DisplayForm::setTitle(const QString &title) +{ + _displayPlot->setTitle(title); +} + +void +DisplayForm::setLineLabel(int which, const QString &label) +{ + _displayPlot->setLineLabel(which, label); +} + +void +DisplayForm::setLineColor(int which, const QString &color) +{ + QColor c = QColor(color); + _displayPlot->setLineColor(which, c); + _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::setMarkerAlpha(int which, int alpha) +{ + _displayPlot->setMarkerAlpha(which, alpha); + _displayPlot->replot(); +} + +QString +DisplayForm::title() +{ + return _displayPlot->title().text(); +} + +QString +DisplayForm::lineLabel(int which) +{ + return _displayPlot->getLineLabel(which); +} + +QString +DisplayForm::lineColor(int which) +{ + return _displayPlot->getLineColor(which).name(); +} + +int +DisplayForm::lineWidth(int which) +{ + return _displayPlot->getLineWidth(which); +} + +Qt::PenStyle +DisplayForm::lineStyle(int which) +{ + return _displayPlot->getLineStyle(which); +} + +QwtSymbol::Style +DisplayForm::lineMarker(int which) +{ + return _displayPlot->getLineMarker(which); +} + +int +DisplayForm::markerAlpha(int which) +{ + return _displayPlot->getMarkerAlpha(which); +} + +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::saveFigure() +{ + QPixmap qpix = QPixmap::grabWidget(this); + + QString types = QString(tr("JPEG file (*.jpg);;Portable Network Graphics file (*.png);;Bitmap file (*.bmp);;TIFF file (*.tiff)")); + + QString filename, filetype; + QFileDialog *filebox = new QFileDialog(0, "Save Image", "./", types); + filebox->setViewMode(QFileDialog::Detail); + if(filebox->exec()) { + filename = filebox->selectedFiles()[0]; + filetype = filebox->selectedNameFilter(); + } + else { + return; + } + + if(filetype.contains(".jpg")) { + qpix.save(filename, "JPEG"); + } + else if(filetype.contains(".png")) { + qpix.save(filename, "PNG"); + } + else if(filetype.contains(".bmp")) { + qpix.save(filename, "BMP"); + } + else if(filetype.contains(".tiff")) { + qpix.save(filename, "TIFF"); + } + else { + qpix.save(filename, "JPEG"); + } + + delete filebox; +} diff --git a/gr-qtgui/lib/displayform.h b/gr-qtgui/lib/displayform.h new file mode 100644 index 0000000000..31dc757bb6 --- /dev/null +++ b/gr-qtgui/lib/displayform.h @@ -0,0 +1,131 @@ +/* -*- 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(); + bool isClosed() const; + + void enableMenu(bool en=true); + +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(const QString &title); + void setLineLabel(int which, const QString &label); + void setLineColor(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 setMarkerAlpha(int which, int alpha); + + QString title(); + QString lineLabel(int which); + QString lineColor(int which); + int lineWidth(int which); + Qt::PenStyle lineStyle(int which); + QwtSymbol::Style lineMarker(int which); + int markerAlpha(int which); + + virtual void setSampleRate(const QString &rate) = 0; + + void setStop(bool on); + void setStop(); + + void setGrid(bool on); + + void saveFigure(); + +private slots: + virtual void newData(const QEvent*) = 0; + virtual void autoScale(bool) = 0; + void updateGuiTimer(); + + void onPlotPointSelected(const QPointF p); + +signals: + void plotPointSelected(const QPointF p, int type); + +protected: + bool _isclosed; + + int _nplots; + + QGridLayout *_layout; + DisplayPlot* _displayPlot; + bool _systemSpecifiedFlag; + + QwtPlotGrid *_grid; + + bool _menu_on; + QMenu *_menu; + + QAction *_stop_act; + bool _stop_state; + QAction *_grid_act; + bool _grid_state; + + QAction *_autoscale_act; + bool _autoscale_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; + QList<MarkerAlphaMenu*> _marker_alpha_menu; + + PopupMenu *_samp_rate_act; + QAction *_save_act; + + 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..63ab35ed95 --- /dev/null +++ b/gr-qtgui/lib/form_menus.h @@ -0,0 +1,1001 @@ +/* -*- 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 MarkerAlphaMenu: public QMenu +{ + Q_OBJECT + +public: + MarkerAlphaMenu(int which, QWidget *parent) + : QMenu("Line Transparency", parent), d_which(which) + { + d_act.push_back(new QAction("None", this)); + d_act.push_back(new QAction("Low", this)); + d_act.push_back(new QAction("Medium", this)); + d_act.push_back(new QAction("High", this)); + d_act.push_back(new QAction("Off", this)); + + connect(d_act[0], SIGNAL(triggered()), this, SLOT(getNone())); + connect(d_act[1], SIGNAL(triggered()), this, SLOT(getLow())); + connect(d_act[2], SIGNAL(triggered()), this, SLOT(getMedium())); + connect(d_act[3], SIGNAL(triggered()), this, SLOT(getHigh())); + connect(d_act[4], SIGNAL(triggered()), this, SLOT(getOff())); + + QListIterator<QAction*> i(d_act); + while(i.hasNext()) { + QAction *a = i.next(); + addAction(a); + } + } + + ~MarkerAlphaMenu() + {} + + 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("MarkerAlphaMenu::getAction: which out of range.\n"); + } + +signals: + void whichTrigger(int which, int); + +public slots: + void getNone() { emit whichTrigger(d_which, 255); } + void getLow() { emit whichTrigger(d_which, 200); } + void getMedium() { emit whichTrigger(d_which, 125); } + void getHigh() { emit whichTrigger(d_which, 50); } + void getOff() { emit whichTrigger(d_which, 0); } + +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(int which, QWidget *parent) + : QMenu("Color Map", parent), d_which(which) + { + 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(int which, const int type, + const QColor &min_color=QColor(), + const QColor &max_color=QColor()); + + public slots: + void getMultiColor() { emit whichTrigger(d_which, INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR); } + void getWhiteHot() { emit whichTrigger(d_which, INTENSITY_COLOR_MAP_TYPE_WHITE_HOT); } + void getBlackHot() { emit whichTrigger(d_which, INTENSITY_COLOR_MAP_TYPE_BLACK_HOT); } + void getIncandescent() { emit whichTrigger(d_which, INTENSITY_COLOR_MAP_TYPE_INCANDESCENT); } + //void getOther(d_which, 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(d_which, 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; + int d_which; +}; + + +/********************************************************************/ + + +class PopupMenu: public QAction +{ + Q_OBJECT + +public: + PopupMenu(QString desc, QWidget *parent) + : QAction(desc, 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())); + } + + ~PopupMenu() + {} + +signals: + void whichTrigger(const QString data); + +public slots: + void getTextDiag() + { + d_diag->show(); + } + +private slots: + void getText() + { + emit whichTrigger(d_text->text()); + d_diag->accept(); + } + +private: + QDialog *d_diag; + QLineEdit *d_text; +}; + + +/********************************************************************/ + + +#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..b1f9a2b1a1 --- /dev/null +++ b/gr-qtgui/lib/freq_sink_c_impl.cc @@ -0,0 +1,470 @@ +/* -*- 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> +#include <qwt_symbol.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() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + 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); + } + + bool + freq_sink_c_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + set_fft_window(d_wintype); + set_fft_size(d_fftsize); + set_frequency_range(d_center_freq, d_bandwidth); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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_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_main_gui->setFFTAverage(fftavg); + } + + float + freq_sink_c_impl::fft_average() const + { + return d_fftavg; + } + + void + freq_sink_c_impl::set_fft_window(const filter::firdes::win_type win) + { + d_main_gui->setFFTWindowType(win); + } + + filter::firdes::win_type + freq_sink_c_impl::fft_window() + { + return d_wintype; + } + + 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); + } + + void + freq_sink_c_impl::set_y_axis(double min, double max) + { + d_main_gui->setYaxis(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); + d_last_time = 0; + } + + void + freq_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + freq_sink_c_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + freq_sink_c_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(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, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + freq_sink_c_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + freq_sink_c_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + freq_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + freq_sink_c_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + freq_sink_c_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + freq_sink_c_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + freq_sink_c_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + freq_sink_c_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + freq_sink_c_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + freq_sink_c_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + void + freq_sink_c_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + freq_sink_c_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + freq_sink_c_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + freq_sink_c_impl::reset() + { + d_index = 0; + } + + 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..0567b914c8 --- /dev/null +++ b/gr-qtgui/lib/freq_sink_c_impl.h @@ -0,0 +1,130 @@ +/* -*- 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(); + + bool check_topology(int ninputs, int noutputs); + + 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_fft_window(const filter::firdes::win_type win); + filter::firdes::win_type fft_window(); + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_y_axis(double min, double max); + + void set_update_time(double t); + + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_line_alpha(int which, double alpha); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void reset(); + + 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..f357ae10be --- /dev/null +++ b/gr-qtgui/lib/freq_sink_f_impl.cc @@ -0,0 +1,473 @@ +/* -*- 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> +#include <qwt_symbol.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() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + 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); + } + + bool + freq_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + set_fft_window(d_wintype); + set_fft_size(d_fftsize); + set_frequency_range(d_center_freq, d_bandwidth/2.0); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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_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_main_gui->setFFTAverage(fftavg); + } + + float + freq_sink_f_impl::fft_average() const + { + return d_fftavg; + } + + void + freq_sink_f_impl::set_fft_window(const filter::firdes::win_type win) + { + d_main_gui->setFFTWindowType(win); + } + + filter::firdes::win_type + freq_sink_f_impl::fft_window() + { + return d_wintype; + } + + 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); + } + + void + freq_sink_f_impl::set_y_axis(double min, double max) + { + d_main_gui->setYaxis(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); + d_last_time = 0; + } + + void + freq_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + freq_sink_f_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + freq_sink_f_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(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, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + freq_sink_f_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + freq_sink_f_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + freq_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + freq_sink_f_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + freq_sink_f_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + freq_sink_f_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + freq_sink_f_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + freq_sink_f_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + freq_sink_f_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + freq_sink_f_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + void + freq_sink_f_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + freq_sink_f_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + freq_sink_f_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + freq_sink_f_impl::reset() + { + d_index = 0; + } + + 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..b79127f3dd --- /dev/null +++ b/gr-qtgui/lib/freq_sink_f_impl.h @@ -0,0 +1,129 @@ +/* -*- 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(); + + bool check_topology(int ninputs, int noutputs); + + 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_fft_window(const filter::firdes::win_type win); + filter::firdes::win_type fft_window(); + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_y_axis(double min, double max); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_line_alpha(int which, double alpha); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void reset(); + + 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..c90e8b7961 --- /dev/null +++ b/gr-qtgui/lib/freqdisplayform.cc @@ -0,0 +1,175 @@ +/* -*- 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::setSampleRate(const QString &samprate) +{ + setFrequencyRange(_center_freq, samprate.toDouble()); +} + +void +FreqDisplayForm::setFFTSize(const int newsize) +{ + _fftsize = newsize; + getPlot()->replot(); +} + +void +FreqDisplayForm::setFFTAverage(const float newavg) +{ + _fftavg = newavg; + getPlot()->replot(); +} + +void +FreqDisplayForm::setFFTWindowType(const gr::filter::firdes::win_type newwin) +{ + _fftwintype = newwin; + getPlot()->replot(); +} + +void +FreqDisplayForm::setFrequencyRange(const double centerfreq, + const double bandwidth) +{ + std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"}; + double units10 = floor(log10(bandwidth)); + 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); + + _center_freq = centerfreq; + _samp_rate = bandwidth; + + getPlot()->setFrequencyRange(centerfreq, bandwidth, + units, strunits[iunit]); +} + +void +FreqDisplayForm::setYaxis(double min, double max) +{ + getPlot()->setYaxis(min, max); +} + +void +FreqDisplayForm::autoScale(bool en) +{ + if(en) { + _autoscale_state = true; + } + else { + _autoscale_state = false; + } + + getPlot()->setAutoScale(_autoscale_state); + getPlot()->replot(); +} diff --git a/gr-qtgui/lib/freqdisplayform.h b/gr-qtgui/lib/freqdisplayform.h new file mode 100644 index 0000000000..b22cd93c51 --- /dev/null +++ b/gr-qtgui/lib/freqdisplayform.h @@ -0,0 +1,74 @@ +/* -*- 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 setSampleRate(const QString &samprate); + void setFFTSize(const int); + void setFFTAverage(const float); + void setFFTWindowType(const gr::filter::firdes::win_type); + + void setFrequencyRange(const double centerfreq, + const double bandwidth); + void setYaxis(double min, double max); + void autoScale(bool en); + +private slots: + void newData(const QEvent *updateEvent); + +private: + uint64_t _numRealDataPoints; + QIntValidator* _intValidator; + + double _samp_rate, _center_freq; + int _fftsize; + float _fftavg; + gr::filter::firdes::win_type _fftwintype; +}; + +#endif /* FREQ_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/plot_raster.cc b/gr-qtgui/lib/plot_raster.cc new file mode 100644 index 0000000000..76bff473e9 --- /dev/null +++ b/gr-qtgui/lib/plot_raster.cc @@ -0,0 +1,350 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <iostream> +#include <qimage.h> +#include <qpen.h> +#include <qpainter.h> +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include "qwt_color_map.h" +#include "plot_raster.h" + +#if QWT_VERSION < 0x060000 +#include "qwt_double_interval.h" +#endif + +typedef QVector<QRgb> QwtColorTable; + +class PlotTimeRasterImage: public QImage +{ + // This class hides some Qt3/Qt4 API differences +public: + PlotTimeRasterImage(const QSize &size, QwtColorMap::Format format): + QImage(size, format == QwtColorMap::RGB + ? QImage::Format_ARGB32 : QImage::Format_Indexed8 ) + { + } + + PlotTimeRasterImage(const QImage &other): + QImage(other) + { + } + + void initColorTable(const QImage& other) + { + setColorTable(other.colorTable()); + } +}; + +class PlotTimeRaster::PrivateData +{ +public: + PrivateData() + { + data = NULL; + colorMap = new QwtLinearColorMap(); + } + + ~PrivateData() + { + delete colorMap; + } + + TimeRasterData *data; + QwtColorMap *colorMap; +}; + +/*! + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + The z value is initialized to 8.0. + + \param title Title + + \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ() +*/ +PlotTimeRaster::PlotTimeRaster(const QString &title) + : QwtPlotRasterItem(title) +{ + d_data = new PrivateData(); + + setItemAttribute(QwtPlotItem::AutoScale, true); + setItemAttribute(QwtPlotItem::Legend, false); + + setZ(1.0); +} + +//! Destructor +PlotTimeRaster::~PlotTimeRaster() +{ + delete d_data; +} + +const TimeRasterData* +PlotTimeRaster::data()const +{ + return d_data->data; +} + +void +PlotTimeRaster::setData(TimeRasterData *data) +{ + d_data->data = data; +} + +//! \return QwtPlotItem::Rtti_PlotSpectrogram +int +PlotTimeRaster::rtti() const +{ + return QwtPlotItem::Rtti_PlotGrid; +} + +/*! + Change the color map + + Often it is useful to display the mapping between intensities and + colors as an additional plot axis, showing a color bar. + + \param map Color Map + + \sa colorMap(), QwtScaleWidget::setColorBarEnabled(), + QwtScaleWidget::setColorMap() +*/ +void +PlotTimeRaster::setColorMap(const QwtColorMap *map) +{ + delete d_data->colorMap; + d_data->colorMap = (QwtColorMap*)map; + + invalidateCache(); + itemChanged(); +} + +/*! + \return Color Map used for mapping the intensity values to colors + \sa setColorMap() +*/ +const QwtColorMap &PlotTimeRaster::colorMap() const +{ + return *d_data->colorMap; +} + +/*! + \return Bounding rect of the data + \sa QwtRasterData::boundingRect +*/ +#if QWT_VERSION < 0x060000 +QwtDoubleRect PlotTimeRaster::boundingRect() const +{ + return d_data->data->boundingRect(); +} +#endif + +/*! + \brief Returns the recommended raster for a given rect. + + F.e the raster hint is used to limit the resolution of + the image that is rendered. + + \param rect Rect for the raster hint + \return data().rasterHint(rect) +*/ +#if QWT_VERSION < 0x060000 +QSize PlotTimeRaster::rasterHint(const QwtDoubleRect &rect) const +{ + return d_data->data->rasterHint(rect); +} +#endif + +/*! + \brief Render an image from the data and color map. + + The area is translated into a rect of the paint device. + For each pixel of this rect the intensity is mapped + into a color. + + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param area Area that should be rendered in scale coordinates. + + \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending + on the color map. + + \sa QwtRasterData::intensity(), QwtColorMap::rgb(), + QwtColorMap::colorIndex() +*/ +#if QWT_VERSION < 0x060000 +QImage PlotTimeRaster::renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QwtDoubleRect &area) const +#else +QImage PlotTimeRaster::renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRectF &area, + const QSize &size) const +#endif +{ + if(area.isEmpty()) + return QImage(); + +#if QWT_VERSION < 0x060000 + QRect rect = transform(xMap, yMap, area); + const QSize res = d_data->data->rasterHint(area); +#else + QRect rect = QwtScaleMap::transform(xMap, yMap, area).toRect(); + const QSize res(-1,-1); + +#endif + + QwtScaleMap xxMap = xMap; + QwtScaleMap yyMap = yMap; + + if(res.isValid()) { + /* + It is useless to render an image with a higher resolution + than the data offers. Of course someone will have to + scale this image later into the size of the given rect, but f.e. + in case of postscript this will done on the printer. + */ + rect.setSize(rect.size().boundedTo(res)); + + int px1 = rect.x(); + int px2 = rect.x() + rect.width(); + if(xMap.p1() > xMap.p2()) + qSwap(px1, px2); + + double sx1 = area.x(); + double sx2 = area.x() + area.width(); + if(xMap.s1() > xMap.s2()) + qSwap(sx1, sx2); + + int py1 = rect.y(); + int py2 = rect.y() + rect.height(); + if(yMap.p1() > yMap.p2()) + qSwap(py1, py2); + + double sy1 = area.y(); + double sy2 = area.y() + area.height(); + if(yMap.s1() > yMap.s2()) + qSwap(sy1, sy2); + + xxMap.setPaintInterval(px1, px2); + xxMap.setScaleInterval(sx1, sx2); + yyMap.setPaintInterval(py1, py2); + yyMap.setScaleInterval(sy1, sy2); + } + + PlotTimeRasterImage image(rect.size(), d_data->colorMap->format()); + +#if QWT_VERSION < 0x060000 + const QwtDoubleInterval intensityRange = d_data->data->range(); +#else + const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis); +#endif + if(!intensityRange.isValid()) + return image; + + d_data->data->initRaster(area, rect.size()); + + if(d_data->colorMap->format() == QwtColorMap::RGB) { + for(int y = rect.top(); y <= rect.bottom(); y++) { + const double ty = yyMap.invTransform(y); + + QRgb *line = (QRgb *)image.scanLine(y - rect.top()); + + for(int x = rect.left(); x <= rect.right(); x++) { + const double tx = xxMap.invTransform(x); + + *line++ = d_data->colorMap->rgb(intensityRange, + d_data->data->value(tx, ty)); + } + } + d_data->data->incrementResidual(); + } + else if(d_data->colorMap->format() == QwtColorMap::Indexed) { + image.setColorTable(d_data->colorMap->colorTable(intensityRange)); + + for(int y = rect.top(); y <= rect.bottom(); y++) { + const double ty = yyMap.invTransform(y); + + unsigned char *line = image.scanLine(y - rect.top()); + for(int x = rect.left(); x <= rect.right(); x++) { + const double tx = xxMap.invTransform(x); + + *line++ = d_data->colorMap->colorIndex(intensityRange, + d_data->data->value(tx, ty)); + } + } + } + + d_data->data->discardRaster(); + + // Mirror the image in case of inverted maps + + const bool hInvert = xxMap.p1() > xxMap.p2(); + const bool vInvert = yyMap.p1() > yyMap.p2(); + if(hInvert || vInvert) { + image = image.mirrored(hInvert, vInvert); + } + + return image; +} + +#if QWT_VERSION < 0x060000 +QwtDoubleInterval +PlotTimeRaster::interval(Qt::Axis ax) const +{ + return d_data->data->range(); +} + +#else + +QwtInterval +PlotTimeRaster::interval(Qt::Axis ax) const +{ + return d_data->data->interval(ax); +} +#endif + +/*! + \brief Draw the raster + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas in painter coordinates + + \sa setDisplayMode, renderImage, + QwtPlotRasterItem::draw, drawContourLines +*/ + +void PlotTimeRaster::draw(QPainter *painter, + const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRect &canvasRect) const +{ + QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect); +} + diff --git a/gr-qtgui/lib/plot_raster.h b/gr-qtgui/lib/plot_raster.h new file mode 100644 index 0000000000..7bea0a4d57 --- /dev/null +++ b/gr-qtgui/lib/plot_raster.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PLOT_TIMERASTER_H +#define PLOT_TIMERASTER_H + +#include <qglobal.h> +#include <timeRasterGlobalData.h> +#include <qwt_plot_rasteritem.h> + +#if QWT_VERSION >= 0x060000 +#include <qwt_point_3d.h> // doesn't seem necessary, but is... +#include <qwt_compat.h> +#endif + +class QwtColorMap; + +/*! + \brief A plot item, which displays a time raster. + + A time raster displays threedimenional data, where the 3rd dimension + ( the intensity ) is displayed using colors. The colors are calculated + from the values using a color map. + + \sa QwtRasterData, QwtColorMap +*/ + +class PlotTimeRaster: public QwtPlotRasterItem +{ +public: + explicit PlotTimeRaster(const QString &title = QString::null); + virtual ~PlotTimeRaster(); + + const TimeRasterData* data()const; + + void setData(TimeRasterData *data); + + void setColorMap(const QwtColorMap *map); + + const QwtColorMap &colorMap() const; + +#if QWT_VERSION < 0x060000 + virtual QwtDoubleRect boundingRect() const; + virtual QSize rasterHint(const QwtDoubleRect &) const; + virtual QwtDoubleInterval interval(Qt::Axis ax) const; +#else + virtual QwtInterval interval(Qt::Axis ax) const; +#endif + + virtual int rtti() const; + + virtual void draw(QPainter *p, + const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRect &rect) const; + +protected: +#if QWT_VERSION < 0x060000 + QImage renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QwtDoubleRect &rect) const; +#else + QImage renderImage(const QwtScaleMap &xMap, + const QwtScaleMap &yMap, + const QRectF &rect, + const QSize &size=QSize(0,0)) const; +#endif + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/gr-qtgui/lib/plot_waterfall.cc b/gr-qtgui/lib/plot_waterfall.cc index 517ec0275a..84915e3f31 100644 --- a/gr-qtgui/lib/plot_waterfall.cc +++ b/gr-qtgui/lib/plot_waterfall.cc @@ -1,3 +1,25 @@ +/* -*- 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. + */ + #include <qimage.h> #include <qpen.h> #include <qpainter.h> diff --git a/gr-qtgui/lib/plot_waterfall.h b/gr-qtgui/lib/plot_waterfall.h index df2537b57a..40ee4eccb3 100644 --- a/gr-qtgui/lib/plot_waterfall.h +++ b/gr-qtgui/lib/plot_waterfall.h @@ -1,3 +1,25 @@ +/* -*- 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. + */ + #ifndef PLOT_WATERFALL_H #define PLOT_WATERFALL_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 7731994622..0000000000 --- a/gr-qtgui/lib/qtgui_time_sink_c.cc +++ /dev/null @@ -1,192 +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); - d_main_gui->setFrequencyRange(0, 0, d_bandwidth); - - // 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 1a23023b5e..0000000000 --- a/gr-qtgui/lib/qtgui_time_sink_f.cc +++ /dev/null @@ -1,190 +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); - d_main_gui->setFrequencyRange(0, 0, d_bandwidth); - - // 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..f4b16dfef2 --- /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..a8a0526e77 --- /dev/null +++ b/gr-qtgui/lib/sink_c_impl.cc @@ -0,0 +1,341 @@ +/* -*- 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; + } + + bool + sink_c_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == 1; + } + + 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->setWindowType((int)d_wintype); + set_fft_size(d_fftsize); + + 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..ddfb0865d5 --- /dev/null +++ b/gr-qtgui/lib/sink_c_impl.h @@ -0,0 +1,108 @@ +/* -*- 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(); + + bool check_topology(int ninputs, int noutputs); + + 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..5cb14db672 --- /dev/null +++ b/gr-qtgui/lib/sink_f_impl.cc @@ -0,0 +1,326 @@ +/* -*- 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; + } + + bool + sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == 1; + } + + 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->setWindowType((int)d_wintype); + set_fft_size(d_fftsize); + + 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..394d5c3428 --- /dev/null +++ b/gr-qtgui/lib/sink_f_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_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(); + + bool check_topology(int ninputs, int noutputs); + + 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..bda89651b6 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.cc +++ b/gr-qtgui/lib/spectrumUpdateEvents.cc @@ -1,9 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + #ifndef SPECTRUM_UPDATE_EVENTS_C #define SPECTRUM_UPDATE_EVENTS_C #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 +35,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 +51,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 +82,7 @@ SpectrumUpdateEvent::~SpectrumUpdateEvent() delete[] _imagDataTimeDomainPoints; } -const std::complex<float>* +const float* SpectrumUpdateEvent::getFFTPoints() const { return _fftPoints; @@ -121,7 +143,7 @@ SpectrumUpdateEvent::getDroppedFFTFrames() const } SpectrumWindowCaptionEvent::SpectrumWindowCaptionEvent(const QString& newLbl) - : QEvent(QEvent::Type(10008)) + : QEvent(QEvent::Type(SpectrumWindowCaptionEventType)) { _labelString = newLbl; } @@ -137,7 +159,7 @@ SpectrumWindowCaptionEvent::getLabel() } SpectrumWindowResetEvent::SpectrumWindowResetEvent() - : QEvent(QEvent::Type(10009)) + : QEvent(QEvent::Type(SpectrumWindowResetEventType)) { } @@ -148,7 +170,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 +201,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 +243,200 @@ 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; +} + + +/***************************************************************************/ + + +TimeRasterUpdateEvent::TimeRasterUpdateEvent(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)); + } + } +} + +TimeRasterUpdateEvent::~TimeRasterUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + } +} + +const std::vector<double*> +TimeRasterUpdateEvent::getPoints() const +{ + return _dataPoints; +} + +uint64_t +TimeRasterUpdateEvent::getNumDataPoints() const +{ + return _numDataPoints; +} + #endif /* SPECTRUM_UPDATE_EVENTS_C */ diff --git a/gr-qtgui/lib/spectrumUpdateEvents.h b/gr-qtgui/lib/spectrumUpdateEvents.h index faef0f0875..97d9c90146 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.h +++ b/gr-qtgui/lib/spectrumUpdateEvents.h @@ -1,3 +1,25 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008-2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + #ifndef SPECTRUM_UPDATE_EVENTS_H #define SPECTRUM_UPDATE_EVENTS_H @@ -8,10 +30,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 +51,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 +65,7 @@ public: protected: private: - std::complex<float>* _fftPoints; + float* _fftPoints; double* _realDataTimeDomainPoints; double* _imagDataTimeDomainPoints; uint64_t _numFFTDataPoints; @@ -103,6 +130,9 @@ public: uint64_t getNumTimeDomainDataPoints() const; bool getRepeatDataFlag() const; + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + protected: private: @@ -112,4 +142,123 @@ 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; +}; + + +/********************************************************************/ + + +class TimeRasterUpdateEvent: public QEvent +{ +public: + TimeRasterUpdateEvent(const std::vector<double*> dataPoints, + const uint64_t numDataPoints); + ~TimeRasterUpdateEvent(); + + 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; +}; + + #endif /* SPECTRUM_UPDATE_EVENTS_H */ diff --git a/gr-qtgui/lib/spectrumdisplayform.cc b/gr-qtgui/lib/spectrumdisplayform.cc index dd9011dbdd..8d7caebda8 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,59 +34,57 @@ 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]; _historyVector = new std::vector<double*>; - _timeDomainDisplayPlot->setTitle(0, "real"); - _timeDomainDisplayPlot->setTitle(1, "imag"); + _timeDomainDisplayPlot->setLineLabel(0, "real"); + _timeDomainDisplayPlot->setLineLabel(1, "imag"); 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); - + minHoldCheckBox_toggled( false ); + maxHoldCheckBox_toggled( false ); + + WaterfallMaximumIntensitySlider->setRange(-200, 0); + WaterfallMinimumIntensitySlider->setRange(-200, 0); + WaterfallMinimumIntensitySlider->setValue(-200); + _peakFrequency = 0; _peakAmplitude = -HUGE_VAL; _noiseFloorAmplitude = -HUGE_VAL; - connect(_waterfallDisplayPlot, SIGNAL(UpdatedLowerIntensityLevel(const double)), - _frequencyDisplayPlot, SLOT(SetLowerIntensityLevel(const double))); - connect(_waterfallDisplayPlot, SIGNAL(UpdatedUpperIntensityLevel(const double)), - _frequencyDisplayPlot, SLOT(SetUpperIntensityLevel(const double))); + connect(_waterfallDisplayPlot, SIGNAL(updatedLowerIntensityLevel(const double)), + _frequencyDisplayPlot, SLOT(setLowerIntensityLevel(const double))); + connect(_waterfallDisplayPlot, SIGNAL(updatedUpperIntensityLevel(const double)), + _frequencyDisplayPlot, SLOT(setUpperIntensityLevel(const double))); - _frequencyDisplayPlot->SetLowerIntensityLevel(-200); - _frequencyDisplayPlot->SetUpperIntensityLevel(-200); + _frequencyDisplayPlot->setLowerIntensityLevel(-200); + _frequencyDisplayPlot->setUpperIntensityLevel(-200); // Load up the acceptable FFT sizes... FFTSizeComboBox->clear(); for(long fftSize = SpectrumGUIClass::MIN_FFT_SIZE; fftSize <= SpectrumGUIClass::MAX_FFT_SIZE; fftSize *= 2){ FFTSizeComboBox->insertItem(FFTSizeComboBox->count(), QString("%1").arg(fftSize)); } - Reset(); + reset(); - ToggleTabFrequency(false); - ToggleTabWaterfall(false); - ToggleTabTime(false); - ToggleTabConstellation(false); + toggleTabFrequency(false); + toggleTabWaterfall(false); + toggleTabTime(false); + toggleTabConstellation(false); _historyEntry = 0; _historyEntryCount = 0; // Create a timer to update plots at the specified rate displayTimer = new QTimer(this); - connect(displayTimer, SIGNAL(timeout()), this, SLOT(UpdateGuiTimer())); + connect(displayTimer, SIGNAL(timeout()), this, SLOT(updateGuiTimer())); // Connect double click signals up connect(_frequencyDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), @@ -122,26 +121,75 @@ SpectrumDisplayForm::~SpectrumDisplayForm() } void -SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem, - const uint64_t numFFTDataPoints, - const uint64_t numTimeDomainDataPoints ) +SpectrumDisplayForm::setSystem(SpectrumGUIClass * newSystem, + const uint64_t numFFTDataPoints, + const uint64_t numTimeDomainDataPoints) { - ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); + resizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); - if(newSystem != NULL){ + if(newSystem != NULL) { _system = newSystem; _systemSpecifiedFlag = true; } - else{ + else { _systemSpecifiedFlag = false; } } +/*********************************************************************** + * 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(); @@ -156,70 +204,36 @@ SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdate timeDomainDataPoints.push_back(imagTimeDomainDataPoints); // REMEMBER: The dataTimestamp is NOT valid when the repeat data flag is true... - ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); + 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); + _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,23 +241,23 @@ 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){ int tabindex = SpectrumTypeTab->currentIndex(); if(tabindex == d_plot_fft) { - _frequencyDisplayPlot->PlotNewData(_averagedValues, numFFTDataPoints, + _frequencyDisplayPlot->plotNewData(_averagedValues, numFFTDataPoints, _noiseFloorAmplitude, _peakFrequency, _peakAmplitude, d_update_time); } if(tabindex == d_plot_time) { - _timeDomainDisplayPlot->PlotNewData(timeDomainDataPoints, + _timeDomainDisplayPlot->plotNewData(timeDomainDataPoints, numTimeDomainDataPoints, d_update_time); } if(tabindex == d_plot_constellation) { - _constellationDisplayPlot->PlotNewData(realTimeDomainDataPoints, + _constellationDisplayPlot->plotNewData(realTimeDomainDataPoints, imagTimeDomainDataPoints, numTimeDomainDataPoints, d_update_time); @@ -252,7 +266,7 @@ SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdate // Don't update the repeated data for the waterfall if(!repeatDataFlag){ if(tabindex == d_plot_waterfall) { - _waterfallDisplayPlot->PlotNewData(_realFFTDataPoints, numFFTDataPoints, + _waterfallDisplayPlot->plotNewData(_realFFTDataPoints, numFFTDataPoints, d_update_time, dataTimestamp, spectrumUpdateEvent->getDroppedFFTFrames()); } @@ -260,8 +274,8 @@ SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdate // Tell the system the GUI has been updated if(_systemSpecifiedFlag){ - _system->SetLastGUIUpdateTime(generatedTimestamp); - _system->DecrementPendingGUIUpdateEvents(); + _system->setLastGUIUpdateTime(generatedTimestamp); + _system->decrementPendingGUIUpdateEvents(); } } } @@ -292,40 +306,40 @@ SpectrumDisplayForm::customEvent( QEvent * e) { if(e->type() == QEvent::User+3){ if(_systemSpecifiedFlag){ - WindowComboBox->setCurrentIndex(_system->GetWindowType()); - FFTSizeComboBox->setCurrentIndex(_system->GetFFTSizeIndex()); + WindowComboBox->setCurrentIndex(_system->getWindowType()); + FFTSizeComboBox->setCurrentIndex(_system->getFFTSizeIndex()); } - waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensityWheel->value()); - waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensityWheel->value()); + waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensitySlider->value()); + waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensitySlider->value()); // Clear any previous display - Reset(); + 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){ - Reset(); + else if(e->type() == SpectrumWindowResetEventType) { + reset(); if(_systemSpecifiedFlag){ - _system->ResetPendingGUIUpdateEvents(); + _system->resetPendingGUIUpdateEvents(); } } - else if(e->type() == 10010){ + else if(e->type() == SpectrumFrequencyRangeEventType) { _startFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStartFrequency(); _stopFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStopFrequency(); _centerFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetCenterFrequency(); - UseRFFrequenciesCB(UseRFFrequenciesCheckBox->isChecked()); + useRFFrequenciesCB(UseRFFrequenciesCheckBox->isChecked()); } } void -SpectrumDisplayForm::UpdateGuiTimer() +SpectrumDisplayForm::updateGuiTimer() { // This is called by the displayTimer and redraws the canvases of // all of the plots. @@ -337,55 +351,55 @@ SpectrumDisplayForm::UpdateGuiTimer() void -SpectrumDisplayForm::AvgLineEdit_valueChanged( int value ) +SpectrumDisplayForm::avgLineEdit_valueChanged( int value ) { - SetAverageCount(value); + setAverageCount(value); } void -SpectrumDisplayForm::MaxHoldCheckBox_toggled( bool newState ) +SpectrumDisplayForm::maxHoldCheckBox_toggled( bool newState ) { MaxHoldResetBtn->setEnabled(newState); - _frequencyDisplayPlot->SetMaxFFTVisible(newState); - MaxHoldResetBtn_clicked(); + _frequencyDisplayPlot->setMaxFFTVisible(newState); + maxHoldResetBtn_clicked(); } void -SpectrumDisplayForm::MinHoldCheckBox_toggled( bool newState ) +SpectrumDisplayForm::minHoldCheckBox_toggled( bool newState ) { MinHoldResetBtn->setEnabled(newState); - _frequencyDisplayPlot->SetMinFFTVisible(newState); - MinHoldResetBtn_clicked(); + _frequencyDisplayPlot->setMinFFTVisible(newState); + minHoldResetBtn_clicked(); } void -SpectrumDisplayForm::MinHoldResetBtn_clicked() +SpectrumDisplayForm::minHoldResetBtn_clicked() { - _frequencyDisplayPlot->ClearMinData(); + _frequencyDisplayPlot->clearMinData(); _frequencyDisplayPlot->replot(); } void -SpectrumDisplayForm::MaxHoldResetBtn_clicked() +SpectrumDisplayForm::maxHoldResetBtn_clicked() { - _frequencyDisplayPlot->ClearMaxData(); + _frequencyDisplayPlot->clearMaxData(); _frequencyDisplayPlot->replot(); } void -SpectrumDisplayForm::TabChanged(int index) +SpectrumDisplayForm::tabChanged(int index) { // This might be dangerous to call this with NULL resizeEvent(NULL); } void -SpectrumDisplayForm::SetFrequencyRange(const double newCenterFrequency, +SpectrumDisplayForm::setFrequencyRange(const double newCenterFrequency, const double newStartFrequency, const double newStopFrequency) { @@ -409,58 +423,52 @@ SpectrumDisplayForm::SetFrequencyRange(const double newCenterFrequency, _stopFrequency = newStopFrequency; _centerFrequency = newCenterFrequency; - _frequencyDisplayPlot->SetFrequencyRange(_startFrequency, - _stopFrequency, - _centerFrequency, - UseRFFrequenciesCheckBox->isChecked(), + _frequencyDisplayPlot->setFrequencyRange(_centerFrequency, fdiff, units, strunits[iunit]); - _waterfallDisplayPlot->SetFrequencyRange(_startFrequency, - _stopFrequency, - _centerFrequency, - UseRFFrequenciesCheckBox->isChecked(), + _waterfallDisplayPlot->setFrequencyRange(_centerFrequency, fdiff, units, strunits[iunit]); - _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency, + _timeDomainDisplayPlot->setSampleRate(_stopFrequency - _startFrequency, units, strtime[iunit]); } } int -SpectrumDisplayForm::GetAverageCount() +SpectrumDisplayForm::getAverageCount() { return _historyVector->size(); } void -SpectrumDisplayForm::SetAverageCount(const int newCount) +SpectrumDisplayForm::setAverageCount(const int newCount) { - if(newCount > -1){ - if(newCount != static_cast<int>(_historyVector->size())){ + if(newCount > -1) { + if(newCount != static_cast<int>(_historyVector->size())) { std::vector<double*>::iterator pos; - while(newCount < static_cast<int>(_historyVector->size())){ + while(newCount < static_cast<int>(_historyVector->size())) { pos = _historyVector->begin(); delete[] (*pos); _historyVector->erase(pos); } - while(newCount > static_cast<int>(_historyVector->size())){ + while(newCount > static_cast<int>(_historyVector->size())) { _historyVector->push_back(new double[_numRealDataPoints]); } - AverageDataReset(); + averageDataReset(); } } } void -SpectrumDisplayForm::_AverageHistory(const double* newBuffer) +SpectrumDisplayForm::_averageHistory(const double* newBuffer) { - if(_numRealDataPoints > 0){ - if(_historyVector->size() > 0){ + if(_numRealDataPoints > 0) { + if(_historyVector->size() > 0) { memcpy(_historyVector->operator[](_historyEntry), newBuffer, _numRealDataPoints*sizeof(double)); // Increment the next location to store data _historyEntryCount++; - if(_historyEntryCount > static_cast<int>(_historyVector->size())){ + if(_historyEntryCount > static_cast<int>(_historyVector->size())) { _historyEntryCount = _historyVector->size(); } _historyEntry += 1; @@ -468,26 +476,26 @@ SpectrumDisplayForm::_AverageHistory(const double* newBuffer) // Total up and then average the values double sum; - for(uint64_t location = 0; location < _numRealDataPoints; location++){ + for(uint64_t location = 0; location < _numRealDataPoints; location++) { sum = 0; - for(int number = 0; number < _historyEntryCount; number++){ + for(int number = 0; number < _historyEntryCount; number++) { sum += _historyVector->operator[](number)[location]; } _averagedValues[location] = sum/static_cast<double>(_historyEntryCount); } } - else{ + else { memcpy(_averagedValues, newBuffer, _numRealDataPoints*sizeof(double)); } } } void -SpectrumDisplayForm::ResizeBuffers( const uint64_t numFFTDataPoints, - const uint64_t /*numTimeDomainDataPoints*/ ) +SpectrumDisplayForm::resizeBuffers(const uint64_t numFFTDataPoints, + const uint64_t /*numTimeDomainDataPoints*/) { // Convert from Complex to Real for certain Displays - if(_numRealDataPoints != numFFTDataPoints){ + if(_numRealDataPoints != numFFTDataPoints) { _numRealDataPoints = numFFTDataPoints; delete[] _realFFTDataPoints; delete[] _averagedValues; @@ -497,126 +505,126 @@ SpectrumDisplayForm::ResizeBuffers( const uint64_t numFFTDataPoints, memset(_realFFTDataPoints, 0x0, _numRealDataPoints*sizeof(double)); const int historySize = _historyVector->size(); - SetAverageCount(0); // Clear the existing history - SetAverageCount(historySize); + setAverageCount(0); // Clear the existing history + setAverageCount(historySize); - Reset(); + reset(); } } void -SpectrumDisplayForm::Reset() +SpectrumDisplayForm::reset() { - AverageDataReset(); + averageDataReset(); - _waterfallDisplayPlot->Reset(); + _waterfallDisplayPlot->resetAxis(); } void -SpectrumDisplayForm::AverageDataReset() +SpectrumDisplayForm::averageDataReset() { _historyEntry = 0; _historyEntryCount = 0; memset(_averagedValues, 0x0, _numRealDataPoints*sizeof(double)); - MaxHoldResetBtn_clicked(); - MinHoldResetBtn_clicked(); + maxHoldResetBtn_clicked(); + minHoldResetBtn_clicked(); } void -SpectrumDisplayForm::closeEvent( QCloseEvent *e ) +SpectrumDisplayForm::closeEvent(QCloseEvent *e) { if(_systemSpecifiedFlag){ - _system->SetWindowOpenFlag(false); + _system->setWindowOpenFlag(false); } qApp->processEvents(); - QWidget::closeEvent(e); + QWidget::closeEvent(e); //equivalent to e->accept() } void -SpectrumDisplayForm::WindowTypeChanged( int newItem ) +SpectrumDisplayForm::windowTypeChanged(int newItem) { - if(_systemSpecifiedFlag){ - _system->SetWindowType(newItem); + if(_systemSpecifiedFlag) { + _system->setWindowType(newItem); } } void -SpectrumDisplayForm::UseRFFrequenciesCB( bool useRFFlag ) +SpectrumDisplayForm::useRFFrequenciesCB(bool useRFFlag) { - SetFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency); + setFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency); } void -SpectrumDisplayForm::waterfallMaximumIntensityChangedCB( double newValue ) +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 -SpectrumDisplayForm::FFTComboBoxSelectedCB( const QString &fftSizeString ) +SpectrumDisplayForm::fftComboBoxSelectedCB(const QString &fftSizeString) { if(_systemSpecifiedFlag){ - _system->SetFFTSize(fftSizeString.toLong()); + _system->setFFTSize(fftSizeString.toLong()); } } void -SpectrumDisplayForm::WaterfallAutoScaleBtnCB() +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); } void -SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) +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(); + lowIntensityColor = _waterfallDisplayPlot->getUserDefinedLowIntensityColor(); if(!lowIntensityColor.isValid()){ lowIntensityColor = Qt::black; } @@ -624,7 +632,7 @@ SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) lowIntensityColor = QColorDialog::getColor(lowIntensityColor, this); // Select the High Intensity Color - highIntensityColor = _waterfallDisplayPlot->GetUserDefinedHighIntensityColor(); + highIntensityColor = _waterfallDisplayPlot->getUserDefinedHighIntensityColor(); if(!highIntensityColor.isValid()){ highIntensityColor = Qt::white; } @@ -632,11 +640,11 @@ SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) highIntensityColor = QColorDialog::getColor(highIntensityColor, this); } - _waterfallDisplayPlot->SetIntensityColorMapType(newType, lowIntensityColor, highIntensityColor); + _waterfallDisplayPlot->setIntensityColorMapType(0, newType, lowIntensityColor, highIntensityColor); } void -SpectrumDisplayForm::ToggleTabFrequency(const bool state) +SpectrumDisplayForm::toggleTabFrequency(const bool state) { if(state == true) { if(d_plot_fft == -1) { @@ -651,7 +659,7 @@ SpectrumDisplayForm::ToggleTabFrequency(const bool state) } void -SpectrumDisplayForm::ToggleTabWaterfall(const bool state) +SpectrumDisplayForm::toggleTabWaterfall(const bool state) { if(state == true) { if(d_plot_waterfall == -1) { @@ -666,7 +674,7 @@ SpectrumDisplayForm::ToggleTabWaterfall(const bool state) } void -SpectrumDisplayForm::ToggleTabTime(const bool state) +SpectrumDisplayForm::toggleTabTime(const bool state) { if(state == true) { if(d_plot_time == -1) { @@ -681,7 +689,7 @@ SpectrumDisplayForm::ToggleTabTime(const bool state) } void -SpectrumDisplayForm::ToggleTabConstellation(const bool state) +SpectrumDisplayForm::toggleTabConstellation(const bool state) { if(state == true) { if(d_plot_constellation == -1) { @@ -697,32 +705,32 @@ SpectrumDisplayForm::ToggleTabConstellation(const bool state) void -SpectrumDisplayForm::SetTimeDomainAxis(double min, double max) +SpectrumDisplayForm::setTimeDomainAxis(double min, double max) { _timeDomainDisplayPlot->setYaxis(min, max); } void -SpectrumDisplayForm::SetConstellationAxis(double xmin, double xmax, +SpectrumDisplayForm::setConstellationAxis(double xmin, double xmax, double ymin, double ymax) { _constellationDisplayPlot->set_axis(xmin, xmax, ymin, ymax); } void -SpectrumDisplayForm::SetConstellationPenSize(int size) +SpectrumDisplayForm::setConstellationPenSize(int size) { _constellationDisplayPlot->set_pen_size( size ); } void -SpectrumDisplayForm::SetFrequencyAxis(double min, double max) +SpectrumDisplayForm::setFrequencyAxis(double min, double max) { - _frequencyDisplayPlot->set_yaxis(min, max); + _frequencyDisplayPlot->setYaxis(min, max); } void -SpectrumDisplayForm::SetUpdateTime(double t) +SpectrumDisplayForm::setUpdateTime(double t) { d_update_time = t; // QTimer class takes millisecond input diff --git a/gr-qtgui/lib/spectrumdisplayform.h b/gr-qtgui/lib/spectrumdisplayform.h index 63dd304d52..c5b714e73f 100644 --- a/gr-qtgui/lib/spectrumdisplayform.h +++ b/gr-qtgui/lib/spectrumdisplayform.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2008-2011 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -45,53 +45,53 @@ class SpectrumDisplayForm : public QWidget, public Ui::SpectrumDisplayForm SpectrumDisplayForm(QWidget* parent = 0); ~SpectrumDisplayForm(); - void setSystem( SpectrumGUIClass * newSystem, const uint64_t numFFTDataPoints, - const uint64_t numTimeDomainDataPoints ); + void setSystem(SpectrumGUIClass * newSystem, const uint64_t numFFTDataPoints, + const uint64_t numTimeDomainDataPoints); - int GetAverageCount(); - void SetAverageCount( const int newCount ); - void Reset(); - void AverageDataReset(); - void ResizeBuffers( const uint64_t numFFTDataPoints, - const uint64_t numTimeDomainDataPoints ); + int getAverageCount(); + void setAverageCount(const int newCount); + void reset(); + void averageDataReset(); + void resizeBuffers(const uint64_t numFFTDataPoints, + const uint64_t numTimeDomainDataPoints); public slots: - void resizeEvent( QResizeEvent * e ); - void customEvent( QEvent * e ); - void AvgLineEdit_valueChanged( int valueString ); - void MaxHoldCheckBox_toggled( bool newState ); - void MinHoldCheckBox_toggled( bool newState ); - void MinHoldResetBtn_clicked(); - void MaxHoldResetBtn_clicked(); - void TabChanged(int index); - - void SetFrequencyRange( const double newCenterFrequency, - const double newStartFrequency, - const double newStopFrequency ); - void closeEvent( QCloseEvent * e ); - void WindowTypeChanged( int newItem ); - void UseRFFrequenciesCB( bool useRFFlag ); + void resizeEvent(QResizeEvent * e); + void customEvent(QEvent * e); + void avgLineEdit_valueChanged(int valueString); + void maxHoldCheckBox_toggled(bool newState); + void minHoldCheckBox_toggled(bool newState); + void minHoldResetBtn_clicked(); + void maxHoldResetBtn_clicked(); + void tabChanged(int index); + + void setFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency); + void closeEvent(QCloseEvent * e); + void windowTypeChanged(int newItem); + void useRFFrequenciesCB(bool useRFFlag); void waterfallMaximumIntensityChangedCB(double); void waterfallMinimumIntensityChangedCB(double); - void WaterfallIntensityColorTypeChanged(int); - void WaterfallAutoScaleBtnCB(); - void FFTComboBoxSelectedCB(const QString&); + void waterfallIntensityColorTypeChanged(int); + void waterfallAutoScaleBtnCB(); + void fftComboBoxSelectedCB(const QString&); - void ToggleTabFrequency(const bool state); - void ToggleTabWaterfall(const bool state); - void ToggleTabTime(const bool state); - void ToggleTabConstellation(const bool state); + void toggleTabFrequency(const bool state); + void toggleTabWaterfall(const bool state); + void toggleTabTime(const bool state); + void toggleTabConstellation(const bool state); - void SetTimeDomainAxis(double min, double max); - void SetConstellationAxis(double xmin, double xmax, + void setTimeDomainAxis(double min, double max); + void setConstellationAxis(double xmin, double xmax, double ymin, double ymax); - void SetConstellationPenSize(int size); - void SetFrequencyAxis(double min, double max); - void SetUpdateTime(double t); + void setConstellationPenSize(int size); + void setFrequencyAxis(double min, double max); + void setUpdateTime(double t); private slots: - void newFrequencyData( const SpectrumUpdateEvent* ); - void UpdateGuiTimer(); + void newFrequencyData(const SpectrumUpdateEvent*); + void updateGuiTimer(); void onFFTPlotPointSelected(const QPointF p); void onWFallPlotPointSelected(const QPointF p); @@ -102,7 +102,7 @@ signals: void plotPointSelected(const QPointF p, int type); private: - void _AverageHistory( const double * newBuffer ); + void _averageHistory(const double * newBuffer); int _historyEntryCount; int _historyEntry; diff --git a/gr-qtgui/lib/spectrumdisplayform.ui b/gr-qtgui/lib/spectrumdisplayform.ui index 049d4ffeb4..f1e1f08f98 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> @@ -548,7 +536,7 @@ <sender>MaxHoldCheckBox</sender> <signal>toggled(bool)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>MaxHoldCheckBox_toggled(bool)</slot> + <slot>maxHoldCheckBox_toggled(bool)</slot> <hints> <hint type="sourcelabel"> <x>22</x> @@ -564,7 +552,7 @@ <sender>MaxHoldResetBtn</sender> <signal>clicked()</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>MaxHoldResetBtn_clicked()</slot> + <slot>maxHoldResetBtn_clicked()</slot> <hints> <hint type="sourcelabel"> <x>107</x> @@ -580,7 +568,7 @@ <sender>MinHoldCheckBox</sender> <signal>toggled(bool)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>MinHoldCheckBox_toggled(bool)</slot> + <slot>minHoldCheckBox_toggled(bool)</slot> <hints> <hint type="sourcelabel"> <x>22</x> @@ -596,7 +584,7 @@ <sender>MinHoldResetBtn</sender> <signal>clicked()</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>MinHoldResetBtn_clicked()</slot> + <slot>minHoldResetBtn_clicked()</slot> <hints> <hint type="sourcelabel"> <x>107</x> @@ -612,7 +600,7 @@ <sender>WindowComboBox</sender> <signal>activated(int)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>WindowTypeChanged(int)</slot> + <slot>windowTypeChanged(int)</slot> <hints> <hint type="sourcelabel"> <x>20</x> @@ -628,7 +616,7 @@ <sender>UseRFFrequenciesCheckBox</sender> <signal>toggled(bool)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>UseRFFrequenciesCB(bool)</slot> + <slot>useRFFrequenciesCB(bool)</slot> <hints> <hint type="sourcelabel"> <x>20</x> @@ -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> @@ -676,7 +664,7 @@ <sender>FFTSizeComboBox</sender> <signal>activated(QString)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>FFTComboBoxSelectedCB(QString)</slot> + <slot>fftComboBoxSelectedCB(QString)</slot> <hints> <hint type="sourcelabel"> <x>20</x> @@ -692,7 +680,7 @@ <sender>WaterfallAutoScaleBtn</sender> <signal>clicked()</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>WaterfallAutoScaleBtnCB()</slot> + <slot>waterfallAutoScaleBtnCB()</slot> <hints> <hint type="sourcelabel"> <x>22</x> @@ -708,7 +696,7 @@ <sender>WaterfallIntensityComboBox</sender> <signal>activated(int)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>WaterfallIntensityColorTypeChanged(int)</slot> + <slot>waterfallIntensityColorTypeChanged(int)</slot> <hints> <hint type="sourcelabel"> <x>92</x> @@ -724,7 +712,7 @@ <sender>SpectrumTypeTab</sender> <signal>currentChanged(int)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>TabChanged(int)</slot> + <slot>tabChanged(int)</slot> <hints> <hint type="sourcelabel"> <x>314</x> @@ -740,7 +728,7 @@ <sender>AvgLineEdit</sender> <signal>valueChanged(int)</signal> <receiver>SpectrumDisplayForm</receiver> - <slot>AvgLineEdit_valueChanged(int)</slot> + <slot>avgLineEdit_valueChanged(int)</slot> <hints> <hint type="sourcelabel"> <x>604</x> diff --git a/gr-qtgui/lib/timeRasterGlobalData.cc b/gr-qtgui/lib/timeRasterGlobalData.cc new file mode 100644 index 0000000000..556196f644 --- /dev/null +++ b/gr-qtgui/lib/timeRasterGlobalData.cc @@ -0,0 +1,240 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_GLOBAL_DATA_CPP +#define TIMERASTER_GLOBAL_DATA_CPP + +#include <timeRasterGlobalData.h> +#include <cstdio> +#include <cmath> +#include <iostream> + +TimeRasterData::TimeRasterData(const double rows, const double cols) +#if QWT_VERSION < 0x060000 + : QwtRasterData(QwtDoubleRect(0, 0, rows, cols)) +#else + : QwtRasterData() +#endif +{ + d_nitems = 0; + d_rows = rows; + d_cols = cols; + d_resid = 0; + + // We add 1 here so we always have the next row already started + // (helps when d_cols is fractional and we have to slide). + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + + d_intensityRange = QwtDoubleInterval(0.0, 10.0); + d_data = new double[d_data_size]; + +#if QWT_VERSION >= 0x060000 + setInterval(Qt::XAxis, QwtInterval(0, cols)); + setInterval(Qt::YAxis, QwtInterval(0, rows)); + setInterval(Qt::ZAxis, QwtInterval(0.0, 10.0)); +#endif + + reset(); +} + +TimeRasterData::~TimeRasterData() +{ + delete [] d_data; +} + +void TimeRasterData::reset() +{ + d_resid = 0; + d_nitems = 0; + memset(d_data, 0x0, d_data_size*sizeof(double)); +} + +void TimeRasterData::copy(const TimeRasterData* rhs) +{ +#if QWT_VERSION < 0x060000 + if((d_cols != rhs->getNumCols()) || + (boundingRect() != rhs->boundingRect()) ){ + d_cols = rhs->getNumCols(); + d_rows = rhs->getNumRows(); + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + setBoundingRect(rhs->boundingRect()); + delete [] d_data; + d_data = new double[d_data_size]; + } +#else + if((d_cols != rhs->getNumCols()) || (d_rows != rhs->getNumRows())) { + d_cols = rhs->getNumCols(); + d_rows = rhs->getNumRows(); + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + delete [] d_data; + d_data = new double[d_data_size]; + } +#endif + + reset(); + +#if QWT_VERSION < 0x060000 + setRange(rhs->range()); +#else + setInterval(Qt::XAxis, rhs->interval(Qt::XAxis)); + setInterval(Qt::YAxis, rhs->interval(Qt::YAxis)); + setInterval(Qt::ZAxis, rhs->interval(Qt::ZAxis)); +#endif +} + +void TimeRasterData::resizeData(const double rows, const double cols) +{ +#if QWT_VERSION < 0x060000 + if((cols != getNumCols()) || (boundingRect().width() != cols) || + (rows != getNumRows()) || (boundingRect().height() != cols)) { + setBoundingRect(QwtDoubleRect(0, 0, cols, rows)); + d_cols = cols; + d_rows = rows; + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + delete [] d_data; + d_data = new double[d_data_size]; + } + +#else + if((cols != getNumCols()) || (interval(Qt::XAxis).width() != cols) || + (rows != getNumRows()) || (interval(Qt::YAxis).width() != rows)) { + + setInterval(Qt::XAxis, QwtInterval(0, cols)); + setInterval(Qt::YAxis, QwtInterval(0, rows)); + + d_cols = cols; + d_rows = rows; + d_totalitems = static_cast<int>((d_rows+1)*floor(d_cols)); + d_data_size = d_totalitems + static_cast<int>(floor(d_cols)); + + delete [] d_data; + d_data = new double[d_data_size]; + } +#endif + + reset(); +} + +QwtRasterData *TimeRasterData::copy() const +{ +#if QWT_VERSION < 0x060000 + TimeRasterData* returnData = \ + new TimeRasterData(d_cols, d_rows); +#else + TimeRasterData* returnData = \ + new TimeRasterData(d_cols, d_rows); +#endif + + returnData->copy(this); + return returnData; +} + + +#if QWT_VERSION < 0x060000 +QwtDoubleInterval TimeRasterData::range() const +{ + return d_intensityRange; +} + +void TimeRasterData::setRange(const QwtDoubleInterval& newRange) +{ + d_intensityRange = newRange; +} + +#endif + + +double +TimeRasterData::value(double x, double y) const +{ + double returnValue = 0.0; + +#if QWT_VERSION < 0x060000 + double top = boundingRect().top(); + double bottom = top - boundingRect().height(); + double left = boundingRect().left(); + double right = left - boundingRect().width(); +#else + double top = interval(Qt::YAxis).maxValue(); + double bottom = interval(Qt::YAxis).minValue(); + double left = interval(Qt::XAxis).minValue(); + double right = interval(Qt::XAxis).maxValue(); +#endif + + if((x < left) || (x > right) || (y < bottom) || (y > top)) + return 0.0; + + double _y = floor(top - y); + double _loc = _y*(d_cols) + x + d_resid; + int location = static_cast<int>(_loc); + + if((location > -1) && (location < d_data_size)) { + returnValue = d_data[location]; + } + + return returnValue; +} + +void +TimeRasterData::incrementResidual() +{ + // After a full set of rows are drawn, we want to add up the + // residual due to any fractional value of d_cols to appropriately + // shift the next data in. + double icols = floor(d_cols); + d_resid = fmod(d_resid + (d_cols - icols) * d_rows, icols); +} + +double +TimeRasterData::getNumCols() const +{ + return d_cols; +} + +double +TimeRasterData::getNumRows() const +{ + return d_rows; +} + +void +TimeRasterData::addData(const double* data, + const int dataSize) +{ + int cols = static_cast<int>(d_cols); + if(dataSize == cols) { + if(d_nitems < d_totalitems) { + memcpy(&d_data[d_nitems], data, cols*sizeof(double)); + d_nitems += cols; + } + else { + memcpy(&d_data[d_nitems], data, cols*sizeof(double)); + memmove(d_data, &d_data[cols], d_totalitems*sizeof(double)); + } + } +} + +#endif /* TIMERASTER_GLOBAL_DATA_CPP */ diff --git a/gr-qtgui/lib/timeRasterGlobalData.h b/gr-qtgui/lib/timeRasterGlobalData.h new file mode 100644 index 0000000000..0a414432d1 --- /dev/null +++ b/gr-qtgui/lib/timeRasterGlobalData.h @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_GLOBAL_DATA_HPP +#define TIMERASTER_GLOBAL_DATA_HPP + +#include <qwt_raster_data.h> +#include <inttypes.h> + +#if QWT_VERSION >= 0x060000 +#include <qwt_point_3d.h> // doesn't seem necessary, but is... +#include <qwt_compat.h> +#endif + +class TimeRasterData: public QwtRasterData +{ +public: + TimeRasterData(const double rows, const double cols); + virtual ~TimeRasterData(); + + virtual void reset(); + virtual void copy(const TimeRasterData*); + + virtual void resizeData(const double rows, const double cols); + + virtual QwtRasterData *copy() const; + +#if QWT_VERSION < 0x060000 + virtual QwtDoubleInterval range() const; + virtual void setRange(const QwtDoubleInterval&); +#endif + + virtual double value(double x, double y) const; + + virtual double getNumCols()const; + virtual double getNumRows()const; + + virtual void addData(const double*, const int); + + void incrementResidual(); + +protected: + + double* d_data; + double d_rows, d_cols; + double d_resid; + int d_nitems, d_totalitems, d_data_size; + +#if QWT_VERSION < 0x060000 + QwtDoubleInterval d_intensityRange; +#else + QwtInterval d_intensityRange; +#endif + +private: + +}; + +#endif /* TIMERASTER_GLOBAL_DATA_HPP */ diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.cc b/gr-qtgui/lib/time_raster_sink_b_impl.cc new file mode 100644 index 0000000000..d90661b968 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_b_impl.cc @@ -0,0 +1,441 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_b_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_b::sptr + time_raster_sink_b::make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_b_impl(samp_rate, + rows, cols, + mult, offset, + name, + nconnections, + parent)); + } + + time_raster_sink_b_impl::time_raster_sink_b_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_raster_sink_b", + gr_make_io_signature(1, -1, sizeof(char)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(nconnections), d_parent(parent), + d_rows(rows), d_cols(cols), + d_mult(std::vector<float>(nconnections,1)), + d_offset(std::vector<float>(nconnections,0)), + d_samp_rate(samp_rate) + { + d_main_gui = NULL; + + d_index = 0; + + d_scale = 1.0f; + + d_icols = static_cast<int>(ceil(d_cols)); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_icols)); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + + set_multiplier(mult); + set_offset(offset); + + initialize(); + } + + time_raster_sink_b_impl::~time_raster_sink_b_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + fft::free(d_tmpflt); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_raster_sink_b_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + void + time_raster_sink_b_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc = 0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + // Create time raster plot; as a bit input, we expect to see 1's + // and 0's from each stream, so we set the maximum intensity + // (zmax) to the number of connections so after adding the + // streams, the max will the the max of 1's from all streams. + d_main_gui = new TimeRasterDisplayForm(d_nconnections, + d_samp_rate, + d_rows, d_cols, + 1, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + void + time_raster_sink_b_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_b_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_b_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_sink_b_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); + d_last_time = 0; + } + + void + time_raster_sink_b_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_b_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + time_raster_sink_b_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(which, color.c_str()); + } + + void + time_raster_sink_b_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + time_raster_sink_b_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + time_raster_sink_b_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + time_raster_sink_b_impl::set_color_map(int which, const int color) + { + d_main_gui->setColorMap(which, color); + } + + void + time_raster_sink_b_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setAlpha(which, (int)(255.0*alpha)); + } + + void + time_raster_sink_b_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_raster_sink_b_impl::set_samp_rate(const double samp_rate) + { + d_samp_rate = samp_rate; + d_main_gui->setSampleRate(d_samp_rate); + } + + void + time_raster_sink_b_impl::set_num_rows(double rows) + { + d_main_gui->setNumRows(rows); + } + + void + time_raster_sink_b_impl::set_num_cols(double cols) + { + d_main_gui->setNumCols(cols); + } + + std::string + time_raster_sink_b_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + time_raster_sink_b_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + time_raster_sink_b_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + time_raster_sink_b_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + time_raster_sink_b_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + time_raster_sink_b_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + int + time_raster_sink_b_impl::color_map(int which) + { + return d_main_gui->getColorMap(which); + } + + double + time_raster_sink_b_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + double + time_raster_sink_b_impl::num_rows() + { + return d_main_gui->numRows(); + } + + double + time_raster_sink_b_impl::num_cols() + { + return d_main_gui->numCols(); + } + + void + time_raster_sink_b_impl::set_multiplier(const std::vector<float> &mult) + { + if(mult.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = 1.0f; + } + } + else if(mult.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = mult[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_b_impl::set_multiplier incorrect dimensions.\n"); + } + } + + void + time_raster_sink_b_impl::set_offset(const std::vector<float> &offset) + { + if(offset.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = 0.0f; + } + } + else if(offset.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = offset[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_b_impl::set_offset incorrect dimensions.\n"); + } + } + + void + time_raster_sink_b_impl::set_intensity_range(float min, float max) + { + d_main_gui->setIntensityRange(min, max); + } + + void + time_raster_sink_b_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + time_raster_sink_b_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + time_raster_sink_b_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + time_raster_sink_b_impl::reset() + { + d_index = 0; + } + + int + time_raster_sink_b_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 int8_t *in = (const int8_t*)input_items[0]; + + unsigned int cols = d_main_gui->numCols(); + if(d_cols != cols) { + d_cols = cols; + d_index = 0; + d_icols = static_cast<int>(ceil(d_cols)); + fft::free(d_tmpflt); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(d_icols); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + } + + for(int i = 0; i < noutput_items; i += d_icols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_icols-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 int8_t*)input_items[idx++]; + volk_8i_s32f_convert_32f(d_tmpflt, &in[j], + d_scale, resid); + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, d_tmpflt, d_mult[n], resid); + for(unsigned int s = 0; s < resid; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + d_tmpflt, resid); + } + + 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 TimeRasterUpdateEvent(d_residbufs, d_icols)); + } + + 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 int8_t*)input_items[idx++]; + volk_8i_s32f_convert_32f(d_tmpflt, &in[j], + d_scale, datasize); + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, d_tmpflt, d_mult[n], datasize); + for(unsigned int s = 0; s < datasize; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f(&d_residbufs[n][d_index], + d_tmpflt, datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.h b/gr-qtgui/lib/time_raster_sink_b_impl.h new file mode 100644 index 0000000000..68dfa25aac --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_b_impl.h @@ -0,0 +1,124 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_B_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_B_IMPL_H + +#include <qtgui/time_raster_sink_b.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <timerasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_b_impl : public time_raster_sink_b + { + private: + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + float d_scale; + float *d_tmpflt; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + int d_icols; + double d_rows, d_cols; + std::vector<float> d_mult; + std::vector<float> d_offset; + double d_samp_rate; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_b_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_raster_sink_b_impl(); + + bool check_topology (int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_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_line_alpha(int which, double alpha); + void set_color_map(int which, const int color); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + int color_map(int which); + + void set_size(int width, int height); + + void set_samp_rate(const double samp_rate); + void set_num_rows(double rows); + void set_num_cols(double cols); + + double num_rows(); + double num_cols(); + + void set_multiplier(const std::vector<float> &mult); + void set_offset(const std::vector<float> &offset); + + void set_intensity_range(float min, float max); + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void reset(); + + 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_RASTER_SINK_B_IMPL_H */ diff --git a/gr-qtgui/lib/time_raster_sink_c_impl.cc b/gr-qtgui/lib/time_raster_sink_c_impl.cc new file mode 100644 index 0000000000..17098f0d93 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_c_impl.cc @@ -0,0 +1,222 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_c_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_c::sptr + time_raster_sink_c::make(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_c_impl(rows, cols, + name, parent)); + } + + time_raster_sink_c_impl::time_raster_sink_c_impl(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent) + : gr_sync_block("time_raster_sink_c", + gr_make_io_signature(1, -1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(1), d_parent(parent), + d_rows(rows), d_cols(cols) + { + d_main_gui = NULL; + + d_index = 0; + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_complex(rows)); + memset(d_residbufs[i], 0, rows*sizeof(float)); + } + + initialize(); + } + + time_raster_sink_c_impl::~time_raster_sink_c_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + void + time_raster_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_rows, 8191); + } + } + + void + time_raster_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 TimeRasterDisplayForm(d_nconnections, d_rows, d_cols, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + d_last_time = 0; + } + + void + time_raster_sink_c_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_c_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_c_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_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_raster_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_c_impl::set_line_label(const std::string &label) + { + d_main_gui->setLineLabel(0, label.c_str()); + } + + void + time_raster_sink_c_impl::set_line_color(const std::string &color) + { + d_main_gui->setLineColor(0, color.c_str()); + } + + void + time_raster_sink_c_impl::set_line_width(int width) + { + d_main_gui->setLineWidth(0, width); + } + + void + time_raster_sink_c_impl::set_line_style(Qt::PenStyle style) + { + d_main_gui->setLineStyle(0, style); + } + + void + time_raster_sink_c_impl::set_line_marker(QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(0, marker); + } + + void + time_raster_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + int + time_raster_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]; + + for(int i=0; i < noutput_items; i+=d_cols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_cols-d_index; + + // If we have enough input for one full column + if(datasize >= resid) { + + 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)*resid); + } + + 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 TimeRasterUpdateEvent(d_residbufs, + 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/time_raster_sink_c_impl.h b/gr-qtgui/lib/time_raster_sink_c_impl.h new file mode 100644 index 0000000000..f3594d58a5 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_c_impl.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_C_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_C_IMPL_H + +#include <qtgui/time_raster_sink_c.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <time_rasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_c_impl : public time_raster_sink_c + { + private: + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<gr_complex*> d_residbufs; + float *d_fbuf; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + unsigned int d_rows, d_cols; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_c_impl(unsignedint rows, + unsigned int cols, + const std::string &name, + QWidget *parent=NULL); + ~time_raster_sink_c_impl(); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(const std::string &label); + void set_line_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_TIME_RASTER_SINK_C_IMPL_H */ diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.cc b/gr-qtgui/lib/time_raster_sink_f_impl.cc new file mode 100644 index 0000000000..39237e2912 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_f_impl.cc @@ -0,0 +1,436 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "time_raster_sink_f_impl.h" +#include <gr_io_signature.h> +#include <string.h> +#include <volk/volk.h> + +namespace gr { + namespace qtgui { + + time_raster_sink_f::sptr + time_raster_sink_f::make(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_raster_sink_f_impl(samp_rate, + rows, cols, + mult, offset, + name, + nconnections, + parent)); + } + + time_raster_sink_f_impl::time_raster_sink_f_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("time_raster_sink_f", + gr_make_io_signature(1, -1, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_name(name), d_nconnections(nconnections), d_parent(parent), + d_rows(rows), d_cols(cols), + d_mult(std::vector<float>(nconnections,1)), + d_offset(std::vector<float>(nconnections,0)), + d_samp_rate(samp_rate) + { + d_main_gui = NULL; + + d_index = 0; + + d_icols = static_cast<int>(ceil(d_cols)); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_icols)); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + + set_multiplier(mult); + set_offset(offset); + + initialize(); + } + + time_raster_sink_f_impl::~time_raster_sink_f_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + fft::free(d_tmpflt); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_raster_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + void + time_raster_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc = 0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + // Create time raster plot; as a bit input, we expect to see 1's + // and 0's from each stream, so we set the maximum intensity + // (zmax) to the number of connections so after adding the + // streams, the max will the the max of 1's from all streams. + d_main_gui = new TimeRasterDisplayForm(d_nconnections, + d_samp_rate, + d_rows, d_cols, + 1, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + void + time_raster_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + time_raster_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + time_raster_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + time_raster_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); + d_last_time = 0; + } + + void + time_raster_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_raster_sink_f_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + time_raster_sink_f_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(which, color.c_str()); + } + + void + time_raster_sink_f_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + time_raster_sink_f_impl::set_line_style(int which, Qt::PenStyle style) + { + d_main_gui->setLineStyle(which, style); + } + + void + time_raster_sink_f_impl::set_line_marker(int which, QwtSymbol::Style marker) + { + d_main_gui->setLineMarker(which, marker); + } + + void + time_raster_sink_f_impl::set_color_map(int which, const int color) + { + d_main_gui->setColorMap(which, color); + } + + void + time_raster_sink_f_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setAlpha(which, (int)(255.0*alpha)); + } + + void + time_raster_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + void + time_raster_sink_f_impl::set_samp_rate(const double samp_rate) + { + d_samp_rate = samp_rate; + d_main_gui->setSampleRate(d_samp_rate); + } + + void + time_raster_sink_f_impl::set_num_rows(double rows) + { + d_main_gui->setNumRows(rows); + } + + void + time_raster_sink_f_impl::set_num_cols(double cols) + { + d_main_gui->setNumCols(cols); + } + + std::string + time_raster_sink_f_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + time_raster_sink_f_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + time_raster_sink_f_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + time_raster_sink_f_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + time_raster_sink_f_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + time_raster_sink_f_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + int + time_raster_sink_f_impl::color_map(int which) + { + return d_main_gui->getColorMap(which); + } + + double + time_raster_sink_f_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + double + time_raster_sink_f_impl::num_rows() + { + return d_main_gui->numRows(); + } + + double + time_raster_sink_f_impl::num_cols() + { + return d_main_gui->numCols(); + } + + void + time_raster_sink_f_impl::set_multiplier(const std::vector<float> &mult) + { + if(mult.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = 1.0f; + } + } + else if(mult.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_mult[i] = mult[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_f_impl::set_multiplier incorrect dimensions.\n"); + } + } + + void + time_raster_sink_f_impl::set_offset(const std::vector<float> &offset) + { + if(offset.size() == 0) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = 0.0f; + } + } + else if(offset.size() == (size_t)d_nconnections) { + for(int i = 0; i < d_nconnections; i++) { + d_offset[i] = offset[i]; + } + } + else { + throw std::runtime_error("time_raster_sink_f_impl::set_offset incorrect dimensions.\n"); + } + } + + void + time_raster_sink_f_impl::set_intensity_range(float min, float max) + { + d_main_gui->setIntensityRange(min, max); + } + + void + time_raster_sink_f_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + time_raster_sink_f_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + time_raster_sink_f_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + time_raster_sink_f_impl::reset() + { + d_index = 0; + } + + int + time_raster_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[0]; + + double cols = d_main_gui->numCols(); + if(d_cols != cols) { + d_cols = cols; + d_index = 0; + d_icols = static_cast<int>(ceil(d_cols)); + fft::free(d_tmpflt); + d_tmpflt = fft::malloc_float(d_icols); + memset(d_tmpflt, 0, d_icols*sizeof(float)); + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(d_icols); + memset(d_residbufs[i], 0, d_icols*sizeof(double)); + } + } + + for(int i = 0; i < noutput_items; i += d_icols) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_icols-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++]; + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, &in[j], d_mult[n], resid); + for(unsigned int s = 0; s < resid; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + d_tmpflt, 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 TimeRasterUpdateEvent(d_residbufs, d_cols)); + } + + 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++]; + + // Scale and add offset + volk_32f_s32f_multiply_32f(d_tmpflt, &in[j], d_mult[n], datasize); + for(unsigned int s = 0; s < datasize; s++) + d_tmpflt[s] = d_tmpflt[s] + d_offset[n]; + + volk_32f_convert_64f(&d_residbufs[n][d_index], + d_tmpflt, datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.h b/gr-qtgui/lib/time_raster_sink_f_impl.h new file mode 100644 index 0000000000..1d7d791202 --- /dev/null +++ b/gr-qtgui/lib/time_raster_sink_f_impl.h @@ -0,0 +1,123 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_RASTER_SINK_F_IMPL_H +#define INCLUDED_QTGUI_TIME_RASTER_SINK_F_IMPL_H + +#include <qtgui/time_raster_sink_f.h> +#include <filter/firdes.h> +#include <fft/fft.h> +#include <gruel/high_res_timer.h> +#include <gruel/thread.h> +#include <timerasterdisplayform.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API time_raster_sink_f_impl : public time_raster_sink_f + { + private: + void initialize(); + + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + float *d_tmpflt; + + QWidget *d_parent; + TimeRasterDisplayForm *d_main_gui; + + int d_icols; + double d_rows, d_cols; + std::vector<float> d_mult; + std::vector<float> d_offset; + double d_samp_rate; + + gruel::high_res_timer_type d_update_time; + gruel::high_res_timer_type d_last_time; + + public: + time_raster_sink_f_impl(double samp_rate, + double rows, double cols, + const std::vector<float> &mult, + const std::vector<float> &offset, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_raster_sink_f_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_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_line_alpha(int which, double alpha); + void set_color_map(int which, const int color); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + int color_map(int which); + + void set_size(int width, int height); + + void set_samp_rate(const double samp_rate); + void set_num_rows(double rows); + void set_num_cols(double cols); + + double num_rows(); + double num_cols(); + + void set_multiplier(const std::vector<float> &mult); + void set_offset(const std::vector<float> &offset); + + void set_intensity_range(float min, float max); + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void reset(); + + 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_RASTER_SINK_F_IMPL_H */ 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..2edb83fa12 --- /dev/null +++ b/gr-qtgui/lib/time_sink_c_impl.cc @@ -0,0 +1,377 @@ +/* -*- 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> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + time_sink_c::sptr + time_sink_c::make(int size, double samp_rate, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_sink_c_impl(size, samp_rate, name, nconnections, parent)); + } + + time_sink_c_impl::time_sink_c_impl(int size, double samp_rate, + 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_samp_rate(samp_rate), 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() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_sink_c_impl::check_topology(int ninputs, int noutputs) + { + return 2*ninputs == d_nconnections; + } + + 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); + d_main_gui->setSampleRate(d_samp_rate); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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_y_axis(double min, double max) + { + d_main_gui->setYaxis(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); + d_last_time = 0; + } + + void + time_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_sink_c_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + time_sink_c_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(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, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + time_sink_c_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + time_sink_c_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + time_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + time_sink_c_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + time_sink_c_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + time_sink_c_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + time_sink_c_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + time_sink_c_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + time_sink_c_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + time_sink_c_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + 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::set_samp_rate(const double samp_rate) + { + gruel::scoped_lock lock(d_mutex); + d_samp_rate = samp_rate; + d_main_gui->setSampleRate(d_samp_rate); + } + + void + time_sink_c_impl::npoints_resize() + { + int newsize = d_main_gui->getNPoints(); + set_nsamps(newsize); + } + + int + time_sink_c_impl::nsamps() const + { + return d_size; + } + + void + time_sink_c_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + time_sink_c_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + time_sink_c_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + time_sink_c_impl::enable_stem_plot(bool en) + { + d_main_gui->setStem(en); + } + + void + time_sink_c_impl::enable_semilogx(bool en) + { + d_main_gui->setSemilogx(en); + } + + void + time_sink_c_impl::enable_semilogy(bool en) + { + d_main_gui->setSemilogy(en); + } + + void + time_sink_c_impl::reset() + { + d_index = 0; + } + + 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..96bdf937b1 --- /dev/null +++ b/gr-qtgui/lib/time_sink_c_impl.h @@ -0,0 +1,110 @@ +/* -*- 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_samp_rate; + 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 samp_rate, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_sink_c_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_y_axis(double min, double max); + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_nsamps(const int size); + void set_samp_rate(const double samp_rate); + void set_line_alpha(int which, double alpha); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + int nsamps() const; + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void enable_stem_plot(bool en); + void enable_semilogx(bool en); + void enable_semilogy(bool en); + void reset(); + + 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..39a62d3d6b --- /dev/null +++ b/gr-qtgui/lib/time_sink_f_impl.cc @@ -0,0 +1,375 @@ +/* -*- 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> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + time_sink_f::sptr + time_sink_f::make(int size, double samp_rate, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new time_sink_f_impl(size, samp_rate, name, nconnections, parent)); + } + + time_sink_f_impl::time_sink_f_impl(int size, double samp_rate, + 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_samp_rate(samp_rate), 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() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + time_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + d_main_gui->setSampleRate(d_samp_rate); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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_y_axis(double min, double max) + { + d_main_gui->setYaxis(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); + d_last_time = 0; + } + + void + time_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + time_sink_f_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + time_sink_f_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(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, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + time_sink_f_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + time_sink_f_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + time_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + time_sink_f_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + time_sink_f_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + time_sink_f_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + time_sink_f_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + time_sink_f_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + time_sink_f_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + time_sink_f_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + 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::set_samp_rate(const double samp_rate) + { + gruel::scoped_lock lock(d_mutex); + d_samp_rate = samp_rate; + d_main_gui->setSampleRate(d_samp_rate); + } + + int + time_sink_f_impl::nsamps() const + { + return d_size; + } + + void + time_sink_f_impl::npoints_resize() + { + int newsize = d_main_gui->getNPoints(); + set_nsamps(newsize); + } + + void + time_sink_f_impl::enable_stem_plot(bool en) + { + d_main_gui->setStem(en); + } + + void + time_sink_f_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + time_sink_f_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + time_sink_f_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + time_sink_f_impl::enable_semilogx(bool en) + { + d_main_gui->setSemilogx(en); + } + + void + time_sink_f_impl::enable_semilogy(bool en) + { + d_main_gui->setSemilogy(en); + } + + void + time_sink_f_impl::reset() + { + d_index = 0; + } + + 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..522511da01 --- /dev/null +++ b/gr-qtgui/lib/time_sink_f_impl.h @@ -0,0 +1,110 @@ +/* -*- 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_samp_rate; + 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 samp_rate, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~time_sink_f_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_y_axis(double min, double max); + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_nsamps(const int newsize); + void set_samp_rate(const double samp_rate); + void set_line_alpha(int which, double alpha); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + int nsamps() const; + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void enable_stem_plot(bool en); + void enable_semilogx(bool en); + void enable_semilogy(bool en); + void reset(); + + 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..5e1fbfb570 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,52 @@ */ #include <cmath> -#include <QColorDialog> #include <QMessageBox> #include <timedisplayform.h> #include <iostream> TimeDisplayForm::TimeDisplayForm(int nplots, QWidget* parent) - : QWidget(parent) + : DisplayForm(nplots, parent) { - _systemSpecifiedFlag = false; + d_stem = false; + d_semilogx = false; + d_semilogy = 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))); + + d_stemmenu = new QAction("Stem Plot", this); + d_stemmenu->setCheckable(true); + _menu->addAction(d_stemmenu); + connect(d_stemmenu, SIGNAL(triggered(bool)), + this, SLOT(setStem(bool))); + + d_semilogxmenu = new QAction("Semilog X", this); + d_semilogxmenu->setCheckable(true); + _menu->addAction(d_semilogxmenu); + connect(d_semilogxmenu, SIGNAL(triggered(bool)), + this, SLOT(setSemilogx(bool))); + + d_semilogymenu = new QAction("Semilog Y", this); + d_semilogymenu->setCheckable(true); + _menu->addAction(d_semilogymenu); + connect(d_semilogymenu, SIGNAL(triggered(bool)), + this, SLOT(setSemilogy(bool))); - // 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,121 +75,107 @@ 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(); - //} - //} + getPlot()->plotNewData(dataPoints, + numDataPoints, + d_update_time); } void -TimeDisplayForm::updateGuiTimer() +TimeDisplayForm::customEvent(QEvent * e) { - _timeDomainDisplayPlot->canvas()->update(); + if(e->type() == TimeUpdateEvent::Type()) { + newData(e); + } } void -TimeDisplayForm::onTimePlotPointSelected(const QPointF p) +TimeDisplayForm::setSampleRate(const QString &samprate) { - emit plotPointSelected(p, 3); + setSampleRate(samprate.toDouble()); } void -TimeDisplayForm::setFrequencyRange(const double newCenterFrequency, - const double newStartFrequency, - const double newStopFrequency) +TimeDisplayForm::setSampleRate(const double samprate) { - double fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency)); - - if(fdiff > 0) { + if(samprate > 0) { std::string strtime[4] = {"sec", "ms", "us", "ns"}; - double units10 = floor(log10(fdiff)); + double units10 = floor(log10(samprate)); 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; - - _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency, - units, strtime[iunit]); + getPlot()->setSampleRate(samprate, units, strtime[iunit]); + } + else { + throw std::runtime_error("TimeDisplayForm: samprate must be > 0.\n"); } } void -TimeDisplayForm::Reset() +TimeDisplayForm::setYaxis(double min, double max) { + getPlot()->setYaxis(min, max); } +int +TimeDisplayForm::getNPoints() const +{ + return d_npoints; +} void -TimeDisplayForm::closeEvent( QCloseEvent *e ) +TimeDisplayForm::setNPoints(const int npoints) { - //if(_systemSpecifiedFlag){ - // _system->SetWindowOpenFlag(false); - //} - - qApp->processEvents(); - - QWidget::closeEvent(e); + d_npoints = npoints; } void -TimeDisplayForm::setTimeDomainAxis(double min, double max) +TimeDisplayForm::setStem(bool en) { - _timeDomainDisplayPlot->setYaxis(min, max); + d_stem = en; + d_stemmenu->setChecked(en); + getPlot()->stemPlot(d_stem); + getPlot()->replot(); } void -TimeDisplayForm::setUpdateTime(double t) +TimeDisplayForm::autoScale(bool en) { - d_update_time = t; - // QTimer class takes millisecond input - displayTimer->start(d_update_time*1000); + _autoscale_state = en; + _autoscale_act->setChecked(en); + getPlot()->setAutoScale(_autoscale_state); + getPlot()->replot(); } void -TimeDisplayForm::setTitle(int which, QString title) +TimeDisplayForm::setSemilogx(bool en) { - _timeDomainDisplayPlot->setTitle(which, title); + d_semilogx = en; + d_semilogxmenu->setChecked(en); + getPlot()->setSemilogx(d_semilogx); + getPlot()->replot(); } void -TimeDisplayForm::setColor(int which, QString color) +TimeDisplayForm::setSemilogy(bool en) { - _timeDomainDisplayPlot->setColor(which, color); + d_semilogy = en; + d_semilogymenu->setChecked(en); + getPlot()->setSemilogy(d_semilogy); + getPlot()->replot(); } diff --git a/gr-qtgui/lib/timedisplayform.h b/gr-qtgui/lib/timedisplayform.h index dd3f62a83f..4bc99f622d 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,40 @@ class TimeDisplayForm : public QWidget TimeDisplayForm(int nplots=1, QWidget* parent = 0); ~TimeDisplayForm(); - void Reset(); - -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 ); + TimeDomainDisplayPlot* getPlot(); - void setTimeDomainAxis(double min, double max); + int getNPoints() const; - void setUpdateTime(double t); +public slots: + void customEvent(QEvent * e); - void setTitle(int which, QString title); - void setColor(int which, QString color); + void setSampleRate(const double samprate); + void setSampleRate(const QString &samprate); + void setYaxis(double min, double max); + void setNPoints(const int); + void setStem(bool en); + void autoScale(bool en); + void setSemilogx(bool en); + void setSemilogy(bool en); 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; + + bool d_stem; + bool d_semilogx; + bool d_semilogy; + + QAction *d_stemmenu; + QAction *d_semilogxmenu; + QAction *d_semilogymenu; }; #endif /* TIME_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/timerasterdisplayform.cc b/gr-qtgui/lib/timerasterdisplayform.cc new file mode 100644 index 0000000000..ed17cf45ce --- /dev/null +++ b/gr-qtgui/lib/timerasterdisplayform.cc @@ -0,0 +1,251 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <cmath> +#include <QColorDialog> +#include <QMessageBox> +#include <timerasterdisplayform.h> +#include <iostream> + +TimeRasterDisplayForm::TimeRasterDisplayForm(int nplots, + double samp_rate, + double rows, double cols, + double zmax, + QWidget* parent) + : DisplayForm(nplots, parent) +{ +#if QWT_VERSION < 0x060000 + std::cerr << "Warning: QWT5 has been found which has serious performance issues with raster plots." + << std::endl << " Consider updating to QWT version 6 to use the time raster GUIs." + << std::endl << std::endl; +#endif + + _layout = new QGridLayout(this); + _displayPlot = new TimeRasterDisplayPlot(nplots, samp_rate, rows, cols, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + _min_val = 10; + _max_val = -10; + + // We don't use the normal menus that are part of the displayform. + // Clear them out to get rid of their resources. + for(int i = 0; i < nplots; i++) { + _lines_menu[i]->clear(); + } + _line_title_act.clear(); + _line_color_menu.clear(); + _line_width_menu.clear(); + _line_style_menu.clear(); + _line_marker_menu.clear(); + _marker_alpha_menu.clear(); + + // Now create our own menus + for(int i = 0; i < nplots; i++) { + ColorMapMenu *colormap = new ColorMapMenu(i, this); + connect(colormap, SIGNAL(whichTrigger(int, const int, const QColor&, const QColor&)), + this, SLOT(setColorMap(int, const int, const QColor&, const QColor&))); + _lines_menu[i]->addMenu(colormap); + + _marker_alpha_menu.push_back(new MarkerAlphaMenu(i, this)); + connect(_marker_alpha_menu[i], SIGNAL(whichTrigger(int, int)), + this, SLOT(setAlpha(int, int))); + _lines_menu[i]->addMenu(_marker_alpha_menu[i]); + } + + // One scales once when clicked, so no on/off toggling + _autoscale_act->setText(tr("Auto Scale")); + _autoscale_act->setCheckable(false); + + PopupMenu *colsmenu = new PopupMenu("Num. Columns", this); + _menu->addAction(colsmenu); + connect(colsmenu, SIGNAL(whichTrigger(QString)), + this, SLOT(setNumCols(QString))); + + PopupMenu *rowsmenu = new PopupMenu("Num. Rows", this); + _menu->addAction(rowsmenu); + connect(rowsmenu, SIGNAL(whichTrigger(QString)), + this, SLOT(setNumRows(QString))); + + getPlot()->setIntensityRange(0, zmax); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +TimeRasterDisplayForm::~TimeRasterDisplayForm() +{ + // Don't worry about deleting Display Plots - they are deleted when + // parents are deleted +} + +TimeRasterDisplayPlot* +TimeRasterDisplayForm::getPlot() +{ + return ((TimeRasterDisplayPlot*)_displayPlot); +} + +double +TimeRasterDisplayForm::numRows() +{ + return getPlot()->numRows(); +} + +double +TimeRasterDisplayForm::numCols() +{ + return getPlot()->numCols(); +} + +int +TimeRasterDisplayForm::getColorMap(int which) +{ + return getPlot()->getIntensityColorMapType(which); +} + +int +TimeRasterDisplayForm::getAlpha(int which) +{ + return getPlot()->getAlpha(which); +} + +double +TimeRasterDisplayForm::getMinIntensity(int which) +{ + return getPlot()->getMinIntensity(which); +} + +double +TimeRasterDisplayForm::getMaxIntensity(int which) +{ + return getPlot()->getMaxIntensity(which); +} + + +void +TimeRasterDisplayForm::newData(const QEvent *updateEvent) +{ + TimeRasterUpdateEvent *event = (TimeRasterUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = event->getPoints(); + const uint64_t numDataPoints = event->getNumDataPoints(); + + _min_val = 10; + _max_val = -10; + 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); +} + +void +TimeRasterDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == TimeRasterUpdateEvent::Type()) { + newData(e); + } +} + +void +TimeRasterDisplayForm::setNumRows(double rows) +{ + getPlot()->setNumRows(rows); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setNumRows(QString rows) +{ + getPlot()->setNumRows(rows.toDouble()); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setNumCols(double cols) +{ + getPlot()->setNumCols(cols); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setNumCols(QString cols) +{ + getPlot()->setNumCols(cols.toDouble()); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setSampleRate(const double rate) +{ + getPlot()->setSampleRate(rate); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setSampleRate(const QString &rate) +{ + getPlot()->setSampleRate(rate.toDouble()); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setColorMap(int which, + const int newType, + const QColor lowColor, + const QColor highColor) +{ + getPlot()->setIntensityColorMapType(which, newType, + lowColor, highColor); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setAlpha(int which, int alpha) +{ + getPlot()->setAlpha(which, alpha); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::setIntensityRange(const double minIntensity, + const double maxIntensity) +{ + getPlot()->setIntensityRange(minIntensity, maxIntensity); + getPlot()->replot(); +} + +void +TimeRasterDisplayForm::autoScale(bool en) +{ + double min_int = _min_val; + double max_int = _max_val; + + getPlot()->setIntensityRange(min_int, max_int); + getPlot()->replot(); +} diff --git a/gr-qtgui/lib/timerasterdisplayform.h b/gr-qtgui/lib/timerasterdisplayform.h new file mode 100644 index 0000000000..f5341f5ecf --- /dev/null +++ b/gr-qtgui/lib/timerasterdisplayform.h @@ -0,0 +1,89 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMERASTER_DISPLAY_FORM_H +#define TIMERASTER_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <TimeRasterDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> +#include <filter/firdes.h> + +#include "displayform.h" + +class TimeRasterDisplayForm : public DisplayForm +{ + Q_OBJECT + + public: + TimeRasterDisplayForm(int nplots=1, + double samp_rate=1, + double rows=1, + double cols=1, + double zmax=1, + QWidget* parent = 0); + ~TimeRasterDisplayForm(); + + TimeRasterDisplayPlot* getPlot(); + + double numRows(); + double numCols(); + + int getColorMap(int which); + int getAlpha(int which); + double getMinIntensity(int which); + double getMaxIntensity(int which); + +public slots: + void customEvent(QEvent *e); + + void setNumRows(double rows); + void setNumCols(double cols); + + void setNumRows(QString rows); + void setNumCols(QString cols); + + void setSampleRate(const double samprate); + void setSampleRate(const QString &rate); + + void setIntensityRange(const double minIntensity, + const double maxIntensity); + + void setColorMap(int which, + const int newType, + const QColor lowColor=QColor("white"), + const QColor highColor=QColor("white")); + + void setAlpha(int which, int alpha); + + void autoScale(bool en=false); + +private slots: + void newData(const QEvent *updateEvent); + +private: + double _min_val; + double _max_val; +}; + +#endif /* TIMERASTER_DISPLAY_FORM_H */ diff --git a/gr-qtgui/lib/waterfallGlobalData.cc b/gr-qtgui/lib/waterfallGlobalData.cc index f64ed14f18..8eb175b2c9 100644 --- a/gr-qtgui/lib/waterfallGlobalData.cc +++ b/gr-qtgui/lib/waterfallGlobalData.cc @@ -1,3 +1,25 @@ +/* -*- 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_GLOBAL_DATA_CPP #define WATERFALL_GLOBAL_DATA_CPP @@ -29,7 +51,7 @@ WaterfallData::WaterfallData(const double minimumFrequency, setInterval(Qt::ZAxis, QwtInterval(-200, 0.0)); #endif - Reset(); + reset(); } WaterfallData::~WaterfallData() @@ -37,34 +59,34 @@ WaterfallData::~WaterfallData() delete[] _spectrumData; } -void WaterfallData::Reset() +void WaterfallData::reset() { memset(_spectrumData, 0x0, _fftPoints*_historyLength*sizeof(double)); _numLinesToUpdate = -1; } -void WaterfallData::Copy(const WaterfallData* rhs) +void WaterfallData::copy(const WaterfallData* rhs) { #if QWT_VERSION < 0x060000 - if((_fftPoints != rhs->GetNumFFTPoints()) || + if((_fftPoints != rhs->getNumFFTPoints()) || (boundingRect() != rhs->boundingRect()) ){ - _fftPoints = rhs->GetNumFFTPoints(); + _fftPoints = rhs->getNumFFTPoints(); setBoundingRect(rhs->boundingRect()); delete[] _spectrumData; _spectrumData = new double[_fftPoints * _historyLength]; } #else - if(_fftPoints != rhs->GetNumFFTPoints()) { - _fftPoints = rhs->GetNumFFTPoints(); + if(_fftPoints != rhs->getNumFFTPoints()) { + _fftPoints = rhs->getNumFFTPoints(); delete[] _spectrumData; _spectrumData = new double[_fftPoints * _historyLength]; } #endif - Reset(); - SetSpectrumDataBuffer(rhs->GetSpectrumDataBuffer()); - SetNumLinesToUpdate(rhs->GetNumLinesToUpdate()); + reset(); + setSpectrumDataBuffer(rhs->getSpectrumDataBuffer()); + setNumLinesToUpdate(rhs->getNumLinesToUpdate()); #if QWT_VERSION < 0x060000 setRange(rhs->range()); @@ -75,12 +97,12 @@ void WaterfallData::Copy(const WaterfallData* rhs) #endif } -void WaterfallData::ResizeData(const double startFreq, +void WaterfallData::resizeData(const double startFreq, const double stopFreq, const uint64_t fftPoints) { #if QWT_VERSION < 0x060000 - if((fftPoints != GetNumFFTPoints()) || + if((fftPoints != getNumFFTPoints()) || (boundingRect().width() != (stopFreq - startFreq)) || (boundingRect().left() != startFreq)){ @@ -93,7 +115,7 @@ void WaterfallData::ResizeData(const double startFreq, } #else - if((fftPoints != GetNumFFTPoints()) || + if((fftPoints != getNumFFTPoints()) || (interval(Qt::XAxis).width() != (stopFreq - startFreq)) || (interval(Qt::XAxis).minValue() != startFreq)){ @@ -105,7 +127,7 @@ void WaterfallData::ResizeData(const double startFreq, } #endif - Reset(); + reset(); } QwtRasterData *WaterfallData::copy() const @@ -120,7 +142,7 @@ QwtRasterData *WaterfallData::copy() const _fftPoints, _historyLength); #endif - returnData->Copy(this); + returnData->copy(this); return returnData; } @@ -166,7 +188,7 @@ double WaterfallData::value(double x, double y) const return returnValue; } -uint64_t WaterfallData::GetNumFFTPoints() const +uint64_t WaterfallData::getNumFFTPoints() const { return _fftPoints; } @@ -202,27 +224,27 @@ void WaterfallData::addFFTData(const double* fftData, } } -double* WaterfallData::GetSpectrumDataBuffer() const +double* WaterfallData::getSpectrumDataBuffer() const { return _spectrumData; } -void WaterfallData::SetSpectrumDataBuffer(const double* newData) +void WaterfallData::setSpectrumDataBuffer(const double* newData) { memcpy(_spectrumData, newData, _fftPoints * _historyLength * sizeof(double)); } -int WaterfallData::GetNumLinesToUpdate() const +int WaterfallData::getNumLinesToUpdate() const { return _numLinesToUpdate; } -void WaterfallData::SetNumLinesToUpdate(const int newNum) +void WaterfallData::setNumLinesToUpdate(const int newNum) { _numLinesToUpdate = newNum; } -void WaterfallData::IncrementNumLinesToUpdate() +void WaterfallData::incrementNumLinesToUpdate() { _numLinesToUpdate++; } diff --git a/gr-qtgui/lib/waterfallGlobalData.h b/gr-qtgui/lib/waterfallGlobalData.h index 928c3b1759..8ab9590d0b 100644 --- a/gr-qtgui/lib/waterfallGlobalData.h +++ b/gr-qtgui/lib/waterfallGlobalData.h @@ -1,3 +1,25 @@ +/* -*- 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_GLOBAL_DATA_HPP #define WATERFALL_GLOBAL_DATA_HPP @@ -15,10 +37,10 @@ public: WaterfallData(const double, const double, const uint64_t, const unsigned int); virtual ~WaterfallData(); - virtual void Reset(); - virtual void Copy(const WaterfallData*); + virtual void reset(); + virtual void copy(const WaterfallData*); - virtual void ResizeData(const double, const double, const uint64_t); + virtual void resizeData(const double, const double, const uint64_t); virtual QwtRasterData *copy() const; @@ -29,15 +51,15 @@ public: virtual double value(double x, double y) const; - virtual uint64_t GetNumFFTPoints()const; + virtual uint64_t getNumFFTPoints()const; virtual void addFFTData(const double*, const uint64_t, const int); - virtual double* GetSpectrumDataBuffer()const; - virtual void SetSpectrumDataBuffer(const double*); + virtual double* getSpectrumDataBuffer()const; + virtual void setSpectrumDataBuffer(const double*); - virtual int GetNumLinesToUpdate()const; - virtual void SetNumLinesToUpdate(const int); - virtual void IncrementNumLinesToUpdate(); + virtual int getNumLinesToUpdate()const; + virtual void setNumLinesToUpdate(const int); + virtual void incrementNumLinesToUpdate(); protected: 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..72908b51b9 --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_c_impl.cc @@ -0,0 +1,446 @@ +/* -*- 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> +#include <qwt_symbol.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, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new waterfall_sink_c_impl(fftsize, wintype, + fc, bw, name, + nconnections, + parent)); + } + + waterfall_sink_c_impl::waterfall_sink_c_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_sync_block("waterfall_sink_c", + gr_make_io_signature(1, nconnections, 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(float)); + memset(d_magbufs[i], 0, d_fftsize*sizeof(double)); + } + + buildwindow(); + + initialize(); + } + + waterfall_sink_c_impl::~waterfall_sink_c_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + 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); + } + + bool + waterfall_sink_c_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + set_fft_window(d_wintype); + set_fft_size(d_fftsize); + set_frequency_range(d_center_freq, d_bandwidth); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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::clear_data() + { + d_main_gui->clearData(); + } + + void + waterfall_sink_c_impl::set_fft_size(const int 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_main_gui->setFFTAverage(fftavg); + } + + float + waterfall_sink_c_impl::fft_average() const + { + return d_fftavg; + } + + void + waterfall_sink_c_impl::set_fft_window(const filter::firdes::win_type win) + { + d_main_gui->setFFTWindowType(win); + } + + filter::firdes::win_type + waterfall_sink_c_impl::fft_window() + { + return d_wintype; + } + + 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); + } + + void + waterfall_sink_c_impl::set_intensity_range(const double min, + const double max) + { + d_main_gui->setIntensityRange(min, max); + } + + 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); + d_last_time = 0; + } + + void + waterfall_sink_c_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + waterfall_sink_c_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + waterfall_sink_c_impl::set_color_map(int which, const int color) + { + d_main_gui->setColorMap(which, color); + } + + void + waterfall_sink_c_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setAlpha(which, (int)(255.0*alpha)); + } + + void + waterfall_sink_c_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + waterfall_sink_c_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + waterfall_sink_c_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + int + waterfall_sink_c_impl::color_map(int which) + { + return d_main_gui->getColorMap(which); + } + + double + waterfall_sink_c_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + void + waterfall_sink_c_impl::auto_scale() + { + d_main_gui->autoScale(); + } + + double + waterfall_sink_c_impl::min_intensity(int which) + { + return d_main_gui->getMinIntensity(which); + } + + double + waterfall_sink_c_impl::max_intensity(int which) + { + return d_main_gui->getMaxIntensity(which); + } + + void + waterfall_sink_c_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + waterfall_sink_c_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + 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..1d709c6c5f --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_c_impl.h @@ -0,0 +1,125 @@ +/* -*- 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, + int nconnections, + QWidget *parent=NULL); + ~waterfall_sink_c_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void clear_data(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + void set_fft_window(const gr::filter::firdes::win_type win); + gr::filter::firdes::win_type fft_window(); + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_intensity_range(const double min, const double max); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_alpha(int which, double alpha); + void set_color_map(int which, const int color); + + std::string title(); + std::string line_label(int which); + double line_alpha(int which); + int color_map(int which); + + void set_size(int width, int height); + + void auto_scale(); + double min_intensity(int which); + double max_intensity(int which); + + void enable_menu(bool en); + void enable_grid(bool en); + + 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..bdf1159e5a --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_f_impl.cc @@ -0,0 +1,447 @@ +/* -*- 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, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new waterfall_sink_f_impl(fftsize, wintype, + fc, bw, name, + nconnections, + parent)); + } + + waterfall_sink_f_impl::waterfall_sink_f_impl(int fftsize, int wintype, + double fc, double bw, + const std::string &name, + int nconnections, + 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(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(); + } + + waterfall_sink_f_impl::~waterfall_sink_f_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + 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); + } + + bool + waterfall_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + 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); + set_fft_window(d_wintype); + set_fft_size(d_fftsize); + set_frequency_range(d_center_freq, d_bandwidth); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + 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::clear_data() + { + d_main_gui->clearData(); + } + + void + waterfall_sink_f_impl::set_fft_size(const int 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_main_gui->setFFTAverage(fftavg); + } + + float + waterfall_sink_f_impl::fft_average() const + { + return d_fftavg; + } + + void + waterfall_sink_f_impl::set_fft_window(const filter::firdes::win_type win) + { + d_main_gui->setFFTWindowType(win); + } + + filter::firdes::win_type + waterfall_sink_f_impl::fft_window() + { + return d_wintype; + } + + 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); + } + + void + waterfall_sink_f_impl::set_intensity_range(const double min, + const double max) + { + d_main_gui->setIntensityRange(min, max); + } + + 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); + d_last_time = 0; + } + + void + waterfall_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + waterfall_sink_f_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + waterfall_sink_f_impl::set_color_map(int which, const int color) + { + d_main_gui->setColorMap(which, color); + } + + void + waterfall_sink_f_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setAlpha(which, (int)(255.0*alpha)); + } + + void + waterfall_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + waterfall_sink_f_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + waterfall_sink_f_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + int + waterfall_sink_f_impl::color_map(int which) + { + return d_main_gui->getColorMap(which); + } + + double + waterfall_sink_f_impl::line_alpha(int which) + { + return (double)(d_main_gui->getAlpha(which))/255.0; + } + + void + waterfall_sink_f_impl::auto_scale() + { + d_main_gui->autoScale(); + } + + double + waterfall_sink_f_impl::min_intensity(int which) + { + return d_main_gui->getMinIntensity(which); + } + + double + waterfall_sink_f_impl::max_intensity(int which) + { + return d_main_gui->getMaxIntensity(which); + } + + void + waterfall_sink_f_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + waterfall_sink_f_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + 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..6185a25397 --- /dev/null +++ b/gr-qtgui/lib/waterfall_sink_f_impl.h @@ -0,0 +1,126 @@ +/* -*- 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, + int nconnections, + QWidget *parent=NULL); + ~waterfall_sink_f_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void clear_data(); + + void set_fft_size(const int fftsize); + int fft_size() const; + void set_fft_average(const float fftavg); + float fft_average() const; + void set_fft_window(const gr::filter::firdes::win_type win); + gr::filter::firdes::win_type fft_window(); + + void set_frequency_range(const double centerfreq, const double bandwidth); + void set_intensity_range(const double min, const double max); + + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_alpha(int which, double alpha); + void set_color_map(int which, const int color); + + std::string title(); + std::string line_label(int which); + double line_alpha(int which); + int color_map(int which); + + void set_size(int width, int height); + + void auto_scale(); + double min_intensity(int which); + double max_intensity(int which); + + void enable_menu(bool en); + void enable_grid(bool en); + + 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..e42e63fbc4 --- /dev/null +++ b/gr-qtgui/lib/waterfalldisplayform.cc @@ -0,0 +1,268 @@ +/* -*- 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); + + _center_freq = 0; + _samp_rate = 0; + + _numRealDataPoints = 1024; + _fftsize = 1024; + _fftavg = 1.0; + + _min_val = 1000; + _max_val = -1000; + + // We don't use the normal menus that are part of the displayform. + // Clear them out to get rid of their resources. + for(int i = 0; i < nplots; i++) { + _lines_menu[i]->clear(); + } + _line_title_act.clear(); + _line_color_menu.clear(); + _line_width_menu.clear(); + _line_style_menu.clear(); + _line_marker_menu.clear(); + _marker_alpha_menu.clear(); + + // Now create our own menus + for(int i = 0; i < nplots; i++) { + ColorMapMenu *colormap = new ColorMapMenu(i, this); + connect(colormap, SIGNAL(whichTrigger(int, const int, const QColor&, const QColor&)), + this, SLOT(setColorMap(int, const int, const QColor&, const QColor&))); + _lines_menu[i]->addMenu(colormap); + + _marker_alpha_menu.push_back(new MarkerAlphaMenu(i, this)); + connect(_marker_alpha_menu[i], SIGNAL(whichTrigger(int, int)), + this, SLOT(setAlpha(int, int))); + _lines_menu[i]->addMenu(_marker_alpha_menu[i]); + } + + // One scales once when clicked, so no on/off toggling + _autoscale_act->setText(tr("Auto Scale")); + _autoscale_act->setCheckable(false); + + 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))); +} + +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, + _time_per_slice, 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; +} + +int +WaterfallDisplayForm::getColorMap(int which) +{ + return getPlot()->getIntensityColorMapType(which); +} + +int +WaterfallDisplayForm::getAlpha(int which) +{ + return getPlot()->getAlpha(which); +} + +double +WaterfallDisplayForm::getMinIntensity(int which) +{ + return getPlot()->getMinIntensity(which); +} + +double +WaterfallDisplayForm::getMaxIntensity(int which) +{ + return getPlot()->getMaxIntensity(which); +} + +void +WaterfallDisplayForm::setSampleRate(const QString &samprate) +{ + setFrequencyRange(_center_freq, samprate.toDouble()); +} + +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 centerfreq, + const double bandwidth) +{ + std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"}; + double units10 = floor(log10(bandwidth)); + 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); + + _center_freq = centerfreq; + _samp_rate = bandwidth; + _time_per_slice = (1.0/bandwidth)*_fftsize; + + getPlot()->setFrequencyRange(centerfreq, bandwidth, + units, strunits[iunit]); + getPlot()->replot(); +} + +void +WaterfallDisplayForm::setColorMap(int which, + const int newType, + const QColor lowColor, + const QColor highColor) +{ + getPlot()->setIntensityColorMapType(which, newType, + lowColor, highColor); + getPlot()->replot(); +} + +void +WaterfallDisplayForm::setAlpha(int which, int alpha) +{ + getPlot()->setAlpha(which, alpha); + getPlot()->replot(); +} + +void +WaterfallDisplayForm::setIntensityRange(const double minIntensity, + const double maxIntensity) +{ + getPlot()->setIntensityRange(minIntensity, maxIntensity); + getPlot()->replot(); +} + +void +WaterfallDisplayForm::autoScale(bool en) +{ + double min_int = _min_val - 5; + double max_int = _max_val + 10; + + getPlot()->setIntensityRange(min_int, max_int); + getPlot()->replot(); +} + +void +WaterfallDisplayForm::clearData() +{ + getPlot()->clearData(); +} diff --git a/gr-qtgui/lib/waterfalldisplayform.h b/gr-qtgui/lib/waterfalldisplayform.h new file mode 100644 index 0000000000..959f2fea68 --- /dev/null +++ b/gr-qtgui/lib/waterfalldisplayform.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 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; + + int getColorMap(int which); + int getAlpha(int which); + double getMinIntensity(int which); + double getMaxIntensity(int which); + + void clearData(); + +public slots: + void customEvent(QEvent *e); + + void setSampleRate(const QString &samprate); + void setFFTSize(const int); + void setFFTAverage(const float); + void setFFTWindowType(const gr::filter::firdes::win_type); + + void setFrequencyRange(const double centerfreq, + const double bandwidth); + + void setIntensityRange(const double minIntensity, + const double maxIntensity); + + void setAlpha(int which, int alpha); + + void setColorMap(int which, + const int newType, + const QColor lowColor=QColor("white"), + const QColor highColor=QColor("white")); + + void autoScale(bool en=false); + +private slots: + void newData(const QEvent *updateEvent); + +private: + uint64_t _numRealDataPoints; + QIntValidator* _intValidator; + + double _samp_rate, _center_freq; + double _time_per_slice; + 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..0761ed3be9 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,52 @@ 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. + # We use '5' in place of filter.firdes.WIN_BLACKMAN_hARRIS so we + # don't have to worry about importing filter just for this one + # constant. + def test01(self): + self.qtsnk = qtgui.sink_c(1024, 5, + 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, 5, + 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, 5, + 0, 1, "Test", 1) + + def test06(self): + self.qtsnk = qtgui.freq_sink_f(1024, 5, + 0, 1, "Test", 1) + + def test07(self): + self.qtsnk = qtgui.waterfall_sink_c(1024, 5, + 0, 1, "Test") + + def test08(self): + self.qtsnk = qtgui.waterfall_sink_f(1024, 5, + 0, 1, "Test") + + def test09(self): + self.qtsnk = qtgui.const_sink_c(1024, "Test", 1) + + def test10(self): + self.qtsnk = qtgui.time_raster_sink_b(1024, 100, 100.5, + [], [], "Test", 1) + + def test11(self): + self.qtsnk = qtgui.time_raster_sink_f(1024, 100, 100.5, + [], [], "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 a1f70240c3..d17130597e 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 # @@ -25,6 +25,8 @@ include(GrSwig) set(GR_SWIG_INCLUDE_DIRS ${GR_QTGUI_INCLUDE_DIRS} + ${GR_FFT_INCLUDE_DIRS} + ${GR_FILTER_INCLUDE_DIRS} ${GNURADIO_CORE_SWIG_INCLUDE_DIRS} ${GRUEL_INCLUDE_DIRS} ${QWT_INCLUDE_DIRS} @@ -47,10 +49,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..4e1d1969e2 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,51 @@ * Boston, MA 02110-1301, USA. */ +#define QTGUI_API +#define FILTER_API + %include "gnuradio.i" //load generated python docstrings %include "qtgui_swig_doc.i" +// So we understand the firdes window types +%include "filter/firdes.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/sink_c.h" +#include "qtgui/sink_f.h" +#include "qtgui/time_sink_c.h" +#include "qtgui/time_sink_f.h" +#include "qtgui/time_raster_sink_b.h" +#include "qtgui/time_raster_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/time_raster_sink_b.h" +%include "qtgui/time_raster_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, time_raster_sink_b); +GR_SWIG_BLOCK_MAGIC2(qtgui, time_raster_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); -}; |