diff options
40 files changed, 6347 insertions, 38 deletions
diff --git a/config/gr_qwt.m4 b/config/gr_qwt.m4 index bcdb7d98c..16ec63819 100644 --- a/config/gr_qwt.m4 +++ b/config/gr_qwt.m4 @@ -1,5 +1,5 @@ dnl -dnl Copyright 2007 Free Software Foundation, Inc. +dnl Copyright 2007,2008 Free Software Foundation, Inc. dnl dnl This file is part of GNU Radio dnl @@ -29,12 +29,17 @@ dnl AC_DEFUN([GR_QWT], [ + dnl QWT Library Version + QWT_LIBRARY1=-lqwt-qt4 + QWT_LIBRARY2=-lqwt + dnl Save the environment AC_LANG_PUSH(C++) qwt_save_CPPFLAGS="$CPPFLAGS" qwt_save_LIBS="$LIBS" libqwt_ok=yes + dnl QWT Info dnl Allow user to specify where QWT files are AC_ARG_WITH([qwt-libdir], [ --with-qwt-libdir=path Prefix where QWT library is installed (optional)], @@ -43,41 +48,89 @@ AC_DEFUN([GR_QWT], AC_ARG_WITH([qwt-incdir], [ --with-qwt-incdir=path Prefix where QWT include files are (optional)], [qwt_incdir="$withval"], [qwt_incdir=""]) + AC_ARG_WITH([qwt-lib], + [ --with-qwt-lib=library QWT library name (optional)], + [qwt_lib="$withval"], [qwt_lib=""]) + + dnl Check for presence of header files + dnl if not user-specified, try the first include dir (Ubuntu), then + dnl try the second include dir (Fedora) + CPPFLAGS="$CPPFLAGS $QTCORE_CFLAGS" - dnl Create QWT_CFLAGS based on user input - AC_MSG_CHECKING(QWT_CFLAGS) - if test "$qwt_incdir" != "" ; then + dnl if not set by user + if test "$qwt_incdir" = "" ; then + dnl check qwt/qwt.h (as in Fedora) + AC_CHECK_HEADER( + [qwt/qwt.h], + [qwt_qwt_h=yes], + [qwt_qwt_h=no] + ) + dnl If it was found, set the flags and move on + if test "$qwt_qwt_h" = "yes" ; then + QWT_CFLAGS="$QWT_CFLAGS -I/usr/include/qwt" + else + dnl otherwise, check qwt-qt4/qwt.h (as in Ubuntu) + AC_CHECK_HEADER( + [qwt-qt4/qwt.h], + [qwt_qt4_qwt_h=yes], + [qwt_qt4_qwt_h=no] + ) + dnl if it was found, set the flags and move on + if test "$qwt_qt4_qwt_h" = "yes" ; then + QWT_CFLAGS="$QWT_CFLAGS -I/usr/include/qwt-qt4" + else + dnl otherwise, qwt.h wasn't found, so set the flag to no + libqwt_ok=no + fi + fi + else + dnl Using the user-specified include directory QWT_CFLAGS="$QWT_CFLAGS -I$qwt_incdir" + AC_CHECK_HEADER( + [$qwt_incdir/qwt.h], + [], + [libqwt_ok=no]) fi - AC_MSG_RESULT($QWT_CFLAGS) - - dnl Set CPPFLAGS so C++ tests can operate - CPPFLAGS="$CPPFLAGS $QT_CFLAGS $QWT_CFLAGS" - dnl Check for presence of header files - AC_CHECK_HEADERS([qwt.h], - [], - [libqwt_ok=no;AC_MSG_RESULT([cannot find usable qwt headers])] - ) - - dnl Set QWT_LIBS based on user input - AC_MSG_CHECKING(QWT_LIBS) - QWT_LIBS="$QWT_LIBS -lqwt" - if test "$qwt_libdir" != "" ; then - QWT_LIBS="-L$qwt_libdir $QWT_LIBS" + dnl Don't bother going on if we can't find the headers + if test "$libqwt_ok" = "yes" ; then + + dnl Check for QWT library (qwt or qwt-qt4) + + dnl User-defined QWT library path + if test "$qwt_libdir" != "" ; then + QWT_LIBS="-L$qwt_libdir $QWT_LIBS" + fi + + dnl temporarily set these so the AC_CHECK_LIB works + CPPFLAGS="$CPPFLAGS $QWT_CFLAGS" + LIBS="$qwt_save_LIBS $QT_LIBS $QWT_LIBS -lqwt" + + dnl If the user specified a qwt library name, use it here + if test "$qwt_lib" != "" ; then + AC_CHECK_LIB([$qwt_lib], [main], [libqwt_ok=yes], [libqwt_ok=no]) + + else + dnl Check for 'main' in libqwt (Fedora) + AC_CHECK_LIB([qwt], [main], [libqwt_ok=yes], [libqwt_ok=no]) + + dnl If library found properly, set the flag and move on + if test "$libqwt_ok" = "yes" ; then + QWT_LIBS="$QWT_LIBS -lqwt" + else + dnl Otherwise, check for 'main' in libqwt-qt4 (Ubuntu) + LIBS="$qwt_save_LIBS $QT_LIBS $QWT_LIBS -lqwt-qt4" + AC_CHECK_LIB([qwt-qt4], [main], [libqwt_ok=yes], [libqwt_ok=no]) + if test "$libqwt_ok" = "yes" ; then + QWT_LIBS="$QWT_LIBS -lqwt-qt4" + else + AC_MSG_RESULT([Could not link to libqwt.so]) + fi + fi + fi + else + AC_MSG_RESULT([Could not find qwt headers]) fi - AC_MSG_RESULT($QWT_LIBS) - - dnl Set LIBS so C++ link test can operate - LIBS="$QWT_LIBS $QT_LIBS $LIBS" - - dnl Check that library files can be linked in - dnl This references an arbitrary static class method - AC_TRY_LINK([#include <qwt_text.h>], - [QwtTextEngine const *te = QwtText::textEngine(QwtText::AutoText)], - [], - [libqwt_ok=no;AC_MSG_RESULT([unable to link QWT library])] - ) dnl Restore saved variables LIBS="$qwt_save_LIBS" diff --git a/config/gr_qwtplot3d.m4 b/config/gr_qwtplot3d.m4 new file mode 100644 index 000000000..758dfbc3e --- /dev/null +++ b/config/gr_qwtplot3d.m4 @@ -0,0 +1,151 @@ +dnl +dnl Copyright 2008 Free Software Foundation, Inc. +dnl +dnl This file is part of GNU Radio +dnl +dnl GNU Radio is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3, or (at your option) +dnl any later version. +dnl +dnl GNU Radio is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU Radio; see the file COPYING. If not, write to +dnl the Free Software Foundation, Inc., 51 Franklin Street, +dnl Boston, MA 02110-1301, USA. +dnl + +dnl Configure paths for library qwtplot3d. +dnl +dnl GR_QWTPLOT3D([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl +dnl Test for library qwtplot3d, set QWTPLOT3D_CFLAGS and QWTPLOT3D_LIBS if found. Assumes +dnl QT_CFLAGS and QT_LIBS have already been correctly set. +dnl + +AC_DEFUN([GR_QWTPLOT3D], +[ + dnl QWTPLOT3D Library Version + + dnl Save the environment + AC_LANG_PUSH(C++) + qwtplot3d_save_CPPFLAGS="$CPPFLAGS" + qwtplot3d_save_LIBS="$LIBS" + libqwtplot3d_ok=yes + + dnl Allow user to specify where QWTPLOT3D files are + AC_ARG_WITH([qwtplot3d-libdir], + [ --with-qwtplot3d-libdir=path Prefix where QWTPLOT3D library is installed (optional)], + [qwtplot3d_libdir="$withval"], [qwtplot3d_libdir=""]) + + AC_ARG_WITH([qwtplot3d-incdir], + [ --with-qwtplot3d-incdir=path Prefix where QWTPLOT3D include files are (optional)], + [qwtplot3d_incdir="$withval"], [qwtplot3d_incdir=""]) + + AC_ARG_WITH([qwtplot3d-lib], + [ --with-qwtplot3d-lib=library QWT Plot3D library name (optional)], + [qwtplot3d_lib="$withval"], [qwtplot3d_lib=""]) + + + dnl Check for presence of header files + dnl if not user-specified, try the first include dir (Ubuntu), then + dnl try the second include dir (Fedora) + CPPFLAGS="$CPPFLAGS $QTCORE_CFLAGS $QTGUI_CFLAGS $QWT_CFLAGS" + + dnl if not set by user + if test "$qwtplot3d_incdir" = "" ; then + dnl check qwtplot3d/qwtplot3d.h (as in Fedora) + AC_CHECK_HEADER( + [qwtplot3d/qwt3d_plot.h], + [qwtplot3d_qwtplot3d_h=yes], + [qwtplot3d_qwtplot3d_h=no] + ) + dnl If it was found, set the flags and move on + if test "$qwtplot3d_qwtplot3d_h" = "yes" ; then + QWTPLOT3D_CFLAGS="$QWTPLOT3D_CFLAGS -I/usr/include/qwtplot3d" + else + dnl otherwise, check qwtplot3d-qt4/qwtplot3d.h (as in Ubuntu) + AC_CHECK_HEADER( + [qwtplot3d-qt4/qwt3d_plot.h], + [qwtplot3d_qt4_qwtplot3d_h=yes], + [qwtplot3d_qt4_qwtplot3d_h=no] + ) + dnl if it was found, set the flags and move on + if test "$qwtplot3d_qt4_qwtplot3d_h" = "yes" ; then + QWTPLOT3D_CFLAGS="$QWTPLOT3D_CFLAGS -I/usr/include/qwtplot3d-qt4" + else + dnl otherwise, qwtplot3d.h wasn't found, so set the flag to no + libqwtplot3d_ok=no + fi + fi + else + dnl Using the user-specified include directory + QWTPLOT3D_CFLAGS="$QWTPLOT3D_CFLAGS -I$qwtplot3d_incdir" + AC_CHECK_HEADER( + [$qwtplot3d_incdir/qwt3d_plot.h], + [], + [libqwtplot3d_ok=no]) + fi + + dnl Don't bother going on if we can't find the headers + if test "$libqwtplot3d_ok" = "yes" ; then + + dnl Check for QWTPLOT3D library (qwtplot3d or qwtplot3d-qt4) + + dnl User-defined QWTPLOT3D library path + if test "$qwtplot3d_libdir" != "" ; then + QWTPLOT3D_LIBS="-L$qwtplot3d_libdir $QWTPLOT3D_LIBS" + fi + + dnl temporarily set these so the AC_CHECK_LIB works + CPPFLAGS="$CPPFLAGS $QWTPLOT3D_CFLAGS" + LIBS="$qwtplot3d_save_LIBS $QT_LIBS $QWTPLOT3D_LIBS -lqwtplot3d-qt4" + + dnl If the user specified a qwtplot3d library name, use it here + if test "$qwtplot3d_lib" != "" ; then + AC_CHECK_LIB([$qwtplot3d_lib], [main], [libqwtplot3d_ok=yes], [libqwtplot3d_ok=no]) + + else + dnl Check for 'main' in libqwtplot3d-qt4 + AC_CHECK_LIB([qwtplot3d-qt4], [main], [libqwtplot3d_ok=yes], [libqwtplot3d_ok=no]) + + dnl If library found properly, set the flag and move on + if test "$libqwtplot3d_ok" = "yes" ; then + QWTPLOT3D_LIBS="$QWTPLOT3D_LIBS -lqwtplot3d-qt4" + else + dnl Otherwise, check for 'main' in libqwtplot3d + LIBS="$qwtplot3d_save_LIBS $QT_LIBS $QWTPLOT3D_LIBS -lqwtplot3d" + AC_CHECK_LIB([qwtplot3d], [main], [libqwtplot3d_ok=yes], [libqwtplot3d_ok=no]) + if test "$libqwtplot3d_ok" = "yes" ; then + QWTPLOT3D_LIBS="$QWTPLOT3D_LIBS -lqwtplot3d" + else + AC_MSG_RESULT([Could not link to libqwtplot3d.so]) + fi + fi + fi + else + AC_MSG_RESULT([Could not find qwtplot3d headers]) + fi + + dnl Restore saved variables + LIBS="$qwtplot3d_save_LIBS" + CPPFLAGS="$qwtplot3d_save_CPPFLAGS" + AC_LANG_POP + + dnl Execute user actions + if test "x$libqwtplot3d_ok" = "xyes" ; then + ifelse([$1], , :, [$1]) + else + QWTPLOT3D_CFLAGS="" + QWTPLOT3D_LIBDIRS="" + ifelse([$2], , :, [$2]) + fi + + dnl Export our variables + AC_SUBST(QWTPLOT3D_CFLAGS) + AC_SUBST(QWTPLOT3D_LIBS) +]) diff --git a/config/grc_gr_qtgui.m4 b/config/grc_gr_qtgui.m4 index 479c15a3f..4a717c658 100644 --- a/config/grc_gr_qtgui.m4 +++ b/config/grc_gr_qtgui.m4 @@ -27,20 +27,62 @@ AC_DEFUN([GRC_GR_QTGUI],[ dnl with : if the --with code didn't error out dnl yes : if the --enable code passed muster and all dependencies are met dnl no : otherwise - if test $passed = yes; then - dnl Check for package qt or qt-mt, set QT_CFLAGS and QT_LIBS - PKG_CHECK_MODULES(QT, qt >= 3.3, [], [ - PKG_CHECK_MODULES(QT, qt-mt >= 3.3, [], - [passed=no;AC_MSG_RESULT([gr-qtgui requires libqt or libqt-mt, neither found.])])]) + +# Check for: +# QtOpenGL +# QtGui +# QtCore +# qwt +# qwtplot3d +# qt4 + +# qt4-core, qt4-gui, qwt5-qt4, qwt5-qt4-dev, libqwtplot3d-qt4, libqwtplot3d-qt4-dev, qt4-dev-tools + + if test $passed = yes; then + dnl Check for package qt or qt-mt, set QT_CFLAGS and QT_LIBS + PKG_CHECK_MODULES(QTCORE, QtCore >= 4.3.4, [], + [passed=no; AC_MSG_RESULT([gr-qtgui requires libQtCore.])]) + PKG_CHECK_MODULES(QTGUI, QtGui >= 4.3.4, [], + [passed=no; AC_MSG_RESULT([gr-qtgui requires libQtGui.])]) + PKG_CHECK_MODULES(QTOPENGL, QtOpenGL >= 4.3.4, [], + [passed=no; AC_MSG_RESULT([gr-qtgui requires libQtOpenGL.])]) + dnl Fetch QWT variables GR_QWT([], [passed=no]) + + dnl Process QWT Plot3D only if QWT passed + if test "$passed" = "yes"; then + GR_QWTPLOT3D([], [passed=no]) + fi + + dnl Export the include dirs and libraries (note: QTOPENGL_LIBS includes links + dnl to QtCore and QtGui libraries) + QT_INCLUDES="$QWT_CFLAGS $QWTPLOT3D_CFLAGS $QTCORE_CFLAGS $QTGUI_CFLAGS" + QT_LIBS="$QWT_LIBS $QWTPLOT3D_LIBS $QTOPENGL_LIBS" + + dnl Build an includes variable specifically for running qmake by extracting + dnl all includes from the QWT and QWTPLOT3D, without the -I; + dnl qmake appends the -I when processing the project file INCLUDEPATH + for i in $QWT_CFLAGS $QWTPLOT3D_CFLAGS; do + QMAKE_INCLUDES="$QMAKE_INCLUDES ${i##-I}" + done + + QT_MOC_EXEC=`pkg-config --variable=moc_location QtCore` + QT_UIC_EXEC=`pkg-config --variable=uic_location QtCore` + + AC_SUBST(QMAKE_INCLUDES) + AC_SUBST(QT_INCLUDES) + AC_SUBST(QT_LIBS) + AC_SUBST(QT_MOC_EXEC) + AC_SUBST(QT_UIC_EXEC) fi AC_CONFIG_FILES([ \ gr-qtgui/Makefile \ gr-qtgui/src/Makefile \ gr-qtgui/src/lib/Makefile \ + gr-qtgui/src/python/Makefile \ ]) GRC_BUILD_CONDITIONAL(gr-qtgui) diff --git a/configure.ac b/configure.ac index 4b581a16b..7e06bdbfd 100644 --- a/configure.ac +++ b/configure.ac @@ -294,6 +294,7 @@ GRC_GR_RADIO_ASTRONOMY GRC_GR_TRELLIS GRC_GR_VIDEO_SDL GRC_GR_WXGUI +GRC_GR_QTGUI GRC_GR_SOUNDER dnl this must come after GRC_USRP GRC_GR_UTILS dnl this must come after GRC_GR_WXGUI GRC_GNURADIO_EXAMPLES dnl must come after all GRC_GR_* diff --git a/gnuradio-core/src/lib/general/gr_firdes.cc b/gnuradio-core/src/lib/general/gr_firdes.cc index 3eaa9c8d9..8efeb3438 100644 --- a/gnuradio-core/src/lib/general/gr_firdes.cc +++ b/gnuradio-core/src/lib/general/gr_firdes.cc @@ -739,6 +739,12 @@ gr_firdes::window (win_type type, int ntaps, double beta) taps[n] = 0.42 - 0.50 * cos ((2*M_PI * n) / (M-1)) - 0.08 * cos ((4*M_PI * n) / (M-1)); break; + case WIN_BLACKMAN_hARRIS: + for (int n = -ntaps/2; n < ntaps/2; n++) + taps[n+ntaps/2] = 0.35875 + 0.48829*cos((2*M_PI * n) / (float)M) + + 0.14128*cos((4*M_PI * n) / (float)M) + 0.01168*cos((6*M_PI * n) / (float)M); + break; + #if 0 case WIN_KAISER: for (int n = 0; n < ntaps; n++) diff --git a/gnuradio-core/src/lib/general/gr_firdes.h b/gnuradio-core/src/lib/general/gr_firdes.h index 0ae34fed4..de775bd07 100644 --- a/gnuradio-core/src/lib/general/gr_firdes.h +++ b/gnuradio-core/src/lib/general/gr_firdes.h @@ -40,7 +40,8 @@ class gr_firdes { WIN_HANN = 1, // max attenuation 44 dB WIN_BLACKMAN = 2, // max attenuation 74 dB WIN_RECTANGULAR = 3, - WIN_KAISER = 4 // max attenuation a function of beta, google it + WIN_KAISER = 4, // max attenuation a function of beta, google it + WIN_BLACKMAN_hARRIS = 5, }; diff --git a/gnuradio-core/src/lib/general/gr_firdes.i b/gnuradio-core/src/lib/general/gr_firdes.i index d77c15aba..d49434adf 100644 --- a/gnuradio-core/src/lib/general/gr_firdes.i +++ b/gnuradio-core/src/lib/general/gr_firdes.i @@ -34,7 +34,8 @@ class gr_firdes { WIN_HANN = 1, // max attenuation 44 dB WIN_BLACKMAN = 2, // max attenuation 74 dB WIN_RECTANGULAR = 3, - WIN_KAISER = 4 // max attenuation variable with beta, google it + WIN_KAISER = 4, // max attenuation variable with beta, google it + WIN_BLACKMAN_hARRIS = 5, }; // ... class methods ... diff --git a/gr-qtgui/Makefile.am b/gr-qtgui/Makefile.am new file mode 100644 index 000000000..d53f96c1f --- /dev/null +++ b/gr-qtgui/Makefile.am @@ -0,0 +1,25 @@ +# +# Copyright 2008 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 $(top_srcdir)/Makefile.common + +SUBDIRS = src +DIST_SUBDIRS = src diff --git a/gr-qtgui/src/Makefile.am b/gr-qtgui/src/Makefile.am new file mode 100644 index 000000000..7230d6096 --- /dev/null +++ b/gr-qtgui/src/Makefile.am @@ -0,0 +1,22 @@ +# +# Copyright 2008 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. +# + +SUBDIRS = lib python diff --git a/gr-qtgui/src/lib/FrequencyDisplayPlot.cc b/gr-qtgui/src/lib/FrequencyDisplayPlot.cc new file mode 100644 index 000000000..9fb9253b5 --- /dev/null +++ b/gr-qtgui/src/lib/FrequencyDisplayPlot.cc @@ -0,0 +1,386 @@ +#ifndef FREQUENCY_DISPLAY_PLOT_C +#define FREQUENCY_DISPLAY_PLOT_C + +#include <FrequencyDisplayPlot.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 +{ +public: + FreqDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision):QwtPlotZoomer(canvas),FreqPrecisionClass(freqPrecision) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~FreqDisplayZoomer(){ + + } + + virtual void updateTrackerText(){ + updateDisplay(); + } + +protected: + virtual QwtText trackerText( const QwtDoublePoint& p ) const + { + QwtText t(QString("%1 %2, %3 dB").arg(p.x(), 0, 'f', GetFrequencyPrecision()).arg( (GetFrequencyPrecision() == 0) ? "Hz" : "kHz").arg(p.y(), 0, 'f', 2)); + + return t; + } +}; + +FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent):QwtPlot(parent){ + _startFrequency = 0; + _stopFrequency = 4000; + + timespec_reset(&_lastReplot); + + resize(parent->width(), parent->height()); + + _displayIntervalTime = (1.0/10.0); // 1/10 of a second between updates + + _useCenterFrequencyFlag = false; + + _numPoints = 1024; + _dataPoints = new double[_numPoints]; + _minFFTPoints = new double[_numPoints]; + _maxFFTPoints = new double[_numPoints]; + _xAxisPoints = new double[_numPoints]; + + // Disable polygon clipping + QwtPainter::setDeviceClipping(false); + + // We don't need the cache here + canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); + canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); + setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency); + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::yLeft, -210, 5); + setAxisTitle(QwtPlot::yLeft, "Power (dB)"); + + // Automatically deleted when parent is deleted + _fft_plot_curve = new QwtPlotCurve("Power Spectrum"); + _fft_plot_curve->attach(this); + _fft_plot_curve->setPen(QPen(Qt::blue)); + _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); + + _min_fft_plot_curve = new QwtPlotCurve("Minimum Power"); + _min_fft_plot_curve->attach(this); + _min_fft_plot_curve->setPen(QPen(Qt::magenta)); + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _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)); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); + _max_fft_plot_curve->setVisible(false); + + _lower_intensity_marker = new QwtPlotMarker(); + _lower_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + _lower_intensity_marker->setLinePen(QPen(Qt::cyan)); + _lower_intensity_marker->attach(this); + + _upper_intensity_marker = new QwtPlotMarker(); + _upper_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + _upper_intensity_marker->setLinePen(QPen(Qt::green)); + _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++){ + _minFFTPoints[number] = 200.0; + _maxFFTPoints[number] = -280.0; + } + + _resetXAxisPoints(); + + + // 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)); + _markerPeakAmplitude->setSymbol(symbol); + _markerPeakAmplitude->attach(this); + + _markerNoiseFloorAmplitude = new QwtPlotMarker(); + _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::HLine); + _markerNoiseFloorAmplitude->setLinePen(QPen(Qt::darkRed, 0, Qt::DotLine)); + _markerNoiseFloorAmplitude->attach(this); + + _peakFrequency = 0; + _peakAmplitude = -HUGE_VAL; + + _noiseFloorAmplitude = -HUGE_VAL; + + replot(); + + _zoomer = new FreqDisplayZoomer(canvas(), 0); +#if QT_VERSION < 0x040000 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#else + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#endif + _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); + +} + +FrequencyDisplayPlot::~FrequencyDisplayPlot(){ + delete[] _dataPoints; + delete[] _maxFFTPoints; + delete[] _minFFTPoints; + delete[] _xAxisPoints; + + // _fft_plot_curves deleted when parent deleted + // _zoomer and _panner deleted when parent deleted +} + +void FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq, const double constStopFreq, const double centerFrequency, const bool useCenterFrequencyFlag){ + double startFreq = constStartFreq; + double stopFreq = constStopFreq; + + _useCenterFrequencyFlag = useCenterFrequencyFlag; + + if(_useCenterFrequencyFlag){ + startFreq = (startFreq + centerFrequency) / 1000.0; + stopFreq = (stopFreq + centerFrequency) / 1000.0; + } + + if((stopFreq > 0) && (stopFreq > startFreq)){ + _startFrequency = startFreq; + _stopFrequency = stopFreq; + _resetXAxisPoints(); + + // Load up the new base zoom settings + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(_startFrequency); + newSize.setWidth(_stopFrequency-_startFrequency); + _zoomer->setZoomBase(newSize); + + // Zooms back to the base and clears any other zoom levels + _zoomer->zoom(0); + + setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency); + } + + if(useCenterFrequencyFlag){ + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(3)); + setAxisTitle(QwtPlot::xBottom, "RF Frequency (kHz)"); + ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(3); + } + else{ + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(0); + } + + // Load up the new base zoom settings + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(_startFrequency); + newSize.setWidth(_stopFrequency-_startFrequency); + _zoomer->setZoomBase(newSize); + + // Zooms back to the base and clears any other zoom levels + _zoomer->zoom(0); +} + + +double FrequencyDisplayPlot::GetStartFrequency()const{ + return _startFrequency; +} + +double FrequencyDisplayPlot::GetStopFrequency()const{ + return _stopFrequency; +} + +void FrequencyDisplayPlot::replot(){ + + const timespec startTime = get_highres_clock(); + + _markerNoiseFloorAmplitude->setYValue(_noiseFloorAmplitude); + + // Make sure to take into account the start frequency + if(_useCenterFrequencyFlag){ + _markerPeakAmplitude->setXValue((_peakFrequency/1000.0) + _startFrequency); + } + else{ + _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency); + } + _markerPeakAmplitude->setYValue(_peakAmplitude); + + QwtPlot::replot(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + differenceTime *= 99.0; + // Require at least a 10% duty cycle + if(differenceTime > (1.0/10.0)){ + _displayIntervalTime = differenceTime; + } +} + +void FrequencyDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude){ + if(numDataPoints > 0){ + + 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]; + + _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); + + _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(dataPoints[point] > _maxFFTPoints[point]){ + _maxFFTPoints[point] = dataPoints[point]; + } + } + + _noiseFloorAmplitude = noiseFloorAmplitude; + _peakFrequency = peakFrequency; + _peakAmplitude = peakAmplitude; + + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only replot the screen if it is visible + if(isVisible()){ + replot(); + } + _lastReplot = get_highres_clock(); + } +} + +void FrequencyDisplayPlot::ClearMaxData(){ + for(int64_t number = 0; number < _numPoints; number++){ + _maxFFTPoints[number] = -280.0; + } +} + +void FrequencyDisplayPlot::ClearMinData(){ + for(int64_t number = 0; number < _numPoints; number++){ + _minFFTPoints[number] = 200.0; + } +} + +void FrequencyDisplayPlot::SetMaxFFTVisible(const bool visibleFlag){ + _max_fft_plot_curve->setVisible(visibleFlag); +} + +void FrequencyDisplayPlot::SetMinFFTVisible(const bool visibleFlag){ + _min_fft_plot_curve->setVisible(visibleFlag); +} + +void FrequencyDisplayPlot::_resetXAxisPoints(){ + double fft_bin_size = (_stopFrequency-_startFrequency) / static_cast<double>(_numPoints); + double freqValue = _startFrequency; + for(int64_t loc = 0; loc < _numPoints; loc++){ + _xAxisPoints[loc] = freqValue; + freqValue += fft_bin_size; + } +} + +void FrequencyDisplayPlot::SetLowerIntensityLevel(const double lowerIntensityLevel){ + _lower_intensity_marker->setYValue( lowerIntensityLevel ); +} + +void FrequencyDisplayPlot::SetUpperIntensityLevel(const double upperIntensityLevel){ + _upper_intensity_marker->setYValue( upperIntensityLevel ); +} + + +#endif /* FREQUENCY_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/src/lib/FrequencyDisplayPlot.h b/gr-qtgui/src/lib/FrequencyDisplayPlot.h new file mode 100644 index 000000000..fb647d964 --- /dev/null +++ b/gr-qtgui/src/lib/FrequencyDisplayPlot.h @@ -0,0 +1,81 @@ +#ifndef FREQUENCY_DISPLAY_PLOT_HPP +#define FREQUENCY_DISPLAY_PLOT_HPP + +#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 <highResTimeFunctions.h> +#include <qwt_symbol.h> + +class FrequencyDisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + FrequencyDisplayPlot(QWidget*); + virtual ~FrequencyDisplayPlot(); + + void SetFrequencyRange(const double, const double, const double, const bool); + double GetStartFrequency()const; + double GetStopFrequency()const; + + void PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude); + + void ClearMaxData(); + void ClearMinData(); + + void SetMaxFFTVisible(const bool); + void SetMinFFTVisible(const bool); + + virtual void replot(); + +public slots: + void SetLowerIntensityLevel(const double); + void SetUpperIntensityLevel(const double); + +protected: + +private: + void _resetXAxisPoints(); + + double _startFrequency; + double _stopFrequency; + + 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; + + double* _dataPoints; + double* _xAxisPoints; + + double* _minFFTPoints; + double* _maxFFTPoints; + int64_t _numPoints; + + double _peakFrequency; + double _peakAmplitude; + + double _noiseFloorAmplitude; + + timespec _lastReplot; + + bool _useCenterFrequencyFlag; + + double _displayIntervalTime; +}; + +#endif /* FREQUENCY_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/src/lib/Makefile.am b/gr-qtgui/src/lib/Makefile.am new file mode 100644 index 000000000..928d960dd --- /dev/null +++ b/gr-qtgui/src/lib/Makefile.am @@ -0,0 +1,152 @@ +# +# Copyright 2008 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 $(top_srcdir)/Makefile.common + +#SUBDIRS=qtspectrum +#DIST_SUBDIRS=qtspectrum + +# Install this stuff so that it ends up as the gnuradio.qtgui module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio/qtgui + +ourpythondir = $(grpythondir)/qtgui +ourlibdir = $(grpyexecdir)/qtgui + +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) \ + $(QT_INCLUDES) -I. $(WITH_INCLUDES) + +SWIGPYTHONARGS = $(SWIGPYTHONFLAGS) $(STD_DEFINES_AND_INCLUDES) \ + $(WITH_SWIG_INCLUDES) $(WITH_INCLUDES) + +ALL_IFILES = \ + $(LOCAL_IFILES) \ + $(NON_LOCAL_IFILES) + +NON_LOCAL_IFILES = \ + $(GNURADIO_I) + +LOCAL_IFILES = \ + $(top_srcdir)/gr-qtgui/src/lib/qtgui.i + +# These files are built by SWIG. The first is the C++ glue. +# The second is the python wrapper that loads the shared library +# and knows how to call our extensions. + +BUILT_SOURCES = \ + qtgui.cc \ + qtgui.py \ + spectrumdisplayform_moc.cc \ + FrequencyDisplayPlot_moc.cc \ + TimeDomainDisplayPlot_moc.cc \ + WaterfallDisplayPlot_moc.cc \ + Waterfall3DDisplayPlot_moc.cc \ + spectrumdisplayform_ui.h + +# This gets qtgui.py installed in the right place +ourpython_PYTHON = \ + qtgui.py + +# Build the library for Python module to link against +ourlib_LTLIBRARIES = _qtgui.la + +# Build the normal library for C++ apps to link against +lib_LTLIBRARIES = libqtgui.la + +# These are the source files that go into the shared library +libqtgui_la_SOURCES = \ + spectrumdisplayform.cc \ + FrequencyDisplayPlot.cc \ + TimeDomainDisplayPlot.cc \ + WaterfallDisplayPlot.cc \ + Waterfall3DDisplayPlot.cc \ + waterfallGlobalData.cc \ + SpectrumGUIClass.cc \ + spectrumUpdateEvents.cc \ + plot_waterfall.cc \ + spectrumdisplayform.ui \ + spectrumdisplayform_moc.cc \ + FrequencyDisplayPlot_moc.cc \ + TimeDomainDisplayPlot_moc.cc \ + WaterfallDisplayPlot_moc.cc \ + Waterfall3DDisplayPlot_moc.cc \ + qtgui_sink_c.cc \ + qtgui_sink_f.cc + +_qtgui_la_SOURCES = \ + $(libqtgui_la_SOURCES) \ + qtgui.cc + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + spectrumdisplayform.h \ + FrequencyDisplayPlot.h \ + TimeDomainDisplayPlot.h \ + WaterfallDisplayPlot.h \ + Waterfall3DDisplayPlot.h \ + SpectrumGUIClass.h \ + waterfallGlobalData.h \ + highResTimeFunctions.h \ + plot_waterfall.h \ + spectrumUpdateEvents.h \ + qtgui.h \ + qtgui_sink_c.h \ + qtgui_sink_f.h + +%_moc.cc : %.h + $(QT_MOC_EXEC) -DQT_SHARED -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_CORE_LIB $< -o $@ + +%_ui.h : %.ui + $(QT_UIC_EXEC) $< -o $@ + +# magic flags +_qtgui_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version +libqtgui_la_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 + +# link the library against the c++ standard library +_qtgui_la_LIBADD = \ + $(PYTHON_LDFLAGS) \ + $(GNURADIO_CORE_LA) \ + -lstdc++ \ + $(QT_LIBS) + + +libqtgui_la_LIBADD = \ + $(GNURADIO_CORE_LA) \ + -lstdc++ \ + $(QT_LIBS) + +qtgui.cc qtgui.py: qtgui.i $(ALL_IFILES) + $(SWIG) $(SWIGPYTHONARGS) -module qtgui -o qtgui.cc $(LOCAL_IFILES) + +# These swig headers get installed in ${prefix}/include/gnuradio/swig +swiginclude_HEADERS = \ + $(LOCAL_IFILES) + + +MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc + +#EXTRA_DIST=qtspectrum/GNUmakefile + +# Don't distribute output of swig +dist-hook: + @for file in $(BUILT_SOURCES); do echo $(RM) $(distdir)/$$file; done + @for file in $(BUILT_SOURCES); do $(RM) $(distdir)/$$file; done diff --git a/gr-qtgui/src/lib/SpectrumGUIClass.cc b/gr-qtgui/src/lib/SpectrumGUIClass.cc new file mode 100644 index 000000000..a57395a67 --- /dev/null +++ b/gr-qtgui/src/lib/SpectrumGUIClass.cc @@ -0,0 +1,341 @@ +#ifndef SPECTRUM_GUI_CLASS_CPP +#define SPECTRUM_GUI_CLASS_CPP + +#include <SpectrumGUIClass.h> +//Added by qt3to4: +#include <QEvent> +#include <QCustomEvent> + +const long SpectrumGUIClass::MAX_FFT_SIZE; +const long SpectrumGUIClass::MIN_FFT_SIZE; + +SpectrumGUIClass::SpectrumGUIClass(const uint64_t maxDataSize, const uint64_t fftSize, const double newStartFrequency, const double newStopFrequency){ + _dataPoints = maxDataSize; + if(_dataPoints < 2){ + _dataPoints = 2; + } + _lastDataPointCount = _dataPoints; + + _fftSize = fftSize; + + _pendingGUIUpdateEventsCount = 0; + _droppedEntriesCount = 0; + + _centerFrequency = 0; + _startFrequency = newStartFrequency; + _stopFrequency = newStopFrequency; + +#warning SPECIFY THIS LATER... + _windowType = 5; + + timespec_reset(&_lastGUIUpdateTime); + + _windowOpennedFlag = false; + _fftBuffersCreatedFlag = false; + + // Create Mutex Lock + //_windowStateLock = new MutexClass("_windowStateLock"); + + _powerValue = 1; +} + +SpectrumGUIClass::~SpectrumGUIClass(){ + if(GetWindowOpenFlag()){ + delete _spectrumDisplayForm; + } + + if(_fftBuffersCreatedFlag){ + delete[] _fftPoints; + delete[] _realTimeDomainPoints; + delete[] _imagTimeDomainPoints; + } + + //delete _windowStateLock; +} + +void SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent){ + //_windowStateLock->Lock(); + + if(!_windowOpennedFlag){ + + if(!_fftBuffersCreatedFlag){ + _fftPoints = new std::complex<float>[_dataPoints]; + _realTimeDomainPoints = new double[_dataPoints]; + _imagTimeDomainPoints = new double[_dataPoints]; + _fftBuffersCreatedFlag = true; + + + memset(_fftPoints, 0x0, _dataPoints*sizeof(std::complex<float>)); + memset(_realTimeDomainPoints, 0x0, _dataPoints*sizeof(double)); + memset(_imagTimeDomainPoints, 0x0, _dataPoints*sizeof(double)); + } + + // Called from the Event Thread + _spectrumDisplayForm = new SpectrumDisplayForm(parent); + + _windowOpennedFlag = true; + + _spectrumDisplayForm->setSystem(this, _dataPoints, _fftSize); + + qApp->processEvents(); + } + + //_windowStateLock->Unlock(); + + SetDisplayTitle(_title); + Reset(); + + qApp->postEvent(_spectrumDisplayForm, new QEvent(QEvent::Type(QEvent::User+3))); + + _spectrumDisplayForm->show(); + + qApp->processEvents(); + + timespec_reset(&_lastGUIUpdateTime); + + // Draw Blank Display + UpdateWindow(false, NULL, 0, NULL, 0, NULL, 0, 1.0, get_highres_clock(), true); + + // GUI Thread only + qApp->processEvents(); +} + +void SpectrumGUIClass::Reset(){ + if(GetWindowOpenFlag()){ + qApp->postEvent(_spectrumDisplayForm, new SpectrumFrequencyRangeEvent(_centerFrequency, _startFrequency, _stopFrequency)); + qApp->postEvent(_spectrumDisplayForm, new SpectrumWindowResetEvent()); + } + _droppedEntriesCount = 0; + // Call the following function the the Spectrum Window Reset Event window + // ResetPendingGUIUpdateEvents(); +} + +void SpectrumGUIClass::SetDisplayTitle(const std::string newString){ + _title.assign(newString); + + if(GetWindowOpenFlag()){ + qApp->postEvent(_spectrumDisplayForm, new SpectrumWindowCaptionEvent(_title.c_str())); + } + +} + +bool SpectrumGUIClass::GetWindowOpenFlag(){ + bool returnFlag = false; + //_windowStateLock->Lock(); + returnFlag = _windowOpennedFlag; + //_windowStateLock->Unlock(); + return returnFlag; +} + + +void SpectrumGUIClass::SetWindowOpenFlag(const bool newFlag){ + //_windowStateLock->Lock(); + _windowOpennedFlag = newFlag; + //_windowStateLock->Unlock(); +} + +void SpectrumGUIClass::SetFrequencyRange(const double centerFreq, const double startFreq, const double stopFreq){ + //_windowStateLock->Lock(); + _centerFrequency = centerFreq; + _startFrequency = startFreq; + _stopFrequency = stopFreq; + //_windowStateLock->Unlock(); +} + +double SpectrumGUIClass::GetStartFrequency()const{ + double returnValue = 0.0; + //_windowStateLock->Lock(); + returnValue = _startFrequency; + //_windowStateLock->Unlock(); + return returnValue; +} + +double SpectrumGUIClass::GetStopFrequency()const{ + double returnValue = 0.0; + //_windowStateLock->Lock(); + returnValue = _stopFrequency; + //_windowStateLock->Unlock(); + return returnValue; +} + +double SpectrumGUIClass::GetCenterFrequency()const{ + double returnValue = 0.0; + //_windowStateLock->Lock(); + returnValue = _centerFrequency; + //_windowStateLock->Unlock(); + return returnValue; +} + + +void SpectrumGUIClass::UpdateWindow(const bool updateDisplayFlag, const std::complex<float>* fftBuffer, const uint64_t inputBufferSize, const float* realTimeDomainData, const uint64_t realTimeDomainDataSize, const float* complexTimeDomainData, const uint64_t complexTimeDomainDataSize, const double timePerFFT, const timespec timestamp, const bool lastOfMultipleFFTUpdateFlag){ + + int64_t bufferSize = inputBufferSize; + bool repeatDataFlag = false; + if(bufferSize > _dataPoints){ + bufferSize = _dataPoints; + } + int64_t timeDomainBufferSize = 0; + + if( updateDisplayFlag){ + if((fftBuffer != NULL) && (bufferSize > 0)){ + memcpy(_fftPoints, fftBuffer, bufferSize * sizeof(std::complex<float>)); + } + + // Can't do a memcpy since ths is going from float to double data type + if((realTimeDomainData != NULL) && (realTimeDomainDataSize > 0)){ + const float* realTimeDomainDataPtr = realTimeDomainData; + + double* realTimeDomainPointsPtr = _realTimeDomainPoints; + timeDomainBufferSize = realTimeDomainDataSize; + + 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; + + 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){ + bufferSize = _lastDataPointCount; + repeatDataFlag = true; + } + else{ + // Since there is data this time, update the count + _lastDataPointCount = bufferSize; + } + + const timespec currentTime = get_highres_clock(); + const timespec lastUpdateGUITime = GetLastGUIUpdateTime(); + + if((diff_timespec(currentTime, lastUpdateGUITime) > (4*timePerFFT)) && (GetPendingGUIUpdateEvents() > 0) && !timespec_empty(&lastUpdateGUITime)){ + // Do not update the display if too much data is pending to be displayed + _droppedEntriesCount++; + } + else{ + // Draw the Data + IncrementPendingGUIUpdateEvents(); + qApp->postEvent(_spectrumDisplayForm, new SpectrumUpdateEvent(_fftPoints, bufferSize, _realTimeDomainPoints, _imagTimeDomainPoints, timeDomainBufferSize, timePerFFT, timestamp, repeatDataFlag, lastOfMultipleFFTUpdateFlag, currentTime, _droppedEntriesCount)); + + // Only reset the dropped entries counter if this is not repeat data since repeat data is dropped by the display systems + if(!repeatDataFlag){ + _droppedEntriesCount = 0; + } + + //qApp->wakeUpGuiThread(); + } +} + +float SpectrumGUIClass::GetPowerValue()const{ + float returnValue = 0; + //_windowStateLock->Lock(); + returnValue = _powerValue; + //_windowStateLock->Unlock(); + return returnValue; +} + +void SpectrumGUIClass::SetPowerValue(const float value){ + //_windowStateLock->Lock(); + _powerValue = value; + //_windowStateLock->Unlock(); +} + +int SpectrumGUIClass::GetWindowType()const{ + int returnValue = 0; + //_windowStateLock->Lock(); + returnValue = _windowType; + //_windowStateLock->Unlock(); + return returnValue; +} + +void SpectrumGUIClass::SetWindowType(const int newType){ + //_windowStateLock->Lock(); + _windowType = newType; + //_windowStateLock->Unlock(); +} + +int SpectrumGUIClass::GetFFTSize()const{ + int returnValue = 0; + //_windowStateLock->Lock(); + returnValue = _fftSize; + //_windowStateLock->Unlock(); + return returnValue; +} + +int SpectrumGUIClass::GetFFTSizeIndex()const{ + int fftsize = GetFFTSize(); + switch(fftsize) { + case(1024): return 0; break; + case(2048): return 1; break; + case(4096): return 2; break; + case(8192): return 3; break; + case(16384): return 3; break; + case(32768): return 3; break; + default: return 0; + } +} + +void SpectrumGUIClass::SetFFTSize(const int newSize){ + //_windowStateLock->Lock(); + _fftSize = newSize; + //_windowStateLock->Unlock(); +} + +timespec SpectrumGUIClass::GetLastGUIUpdateTime()const{ + timespec returnValue; + //_windowStateLock->Lock(); + returnValue = _lastGUIUpdateTime; + //_windowStateLock->Unlock(); + return returnValue; +} + +void SpectrumGUIClass::SetLastGUIUpdateTime(const timespec newTime){ + //_windowStateLock->Lock(); + _lastGUIUpdateTime = newTime; + //_windowStateLock->Unlock(); +} + +unsigned int SpectrumGUIClass::GetPendingGUIUpdateEvents()const{ + unsigned int returnValue = 0; + //_windowStateLock->Lock(); + returnValue = _pendingGUIUpdateEventsCount; + //_windowStateLock->Unlock(); + return returnValue; +} + +void SpectrumGUIClass::IncrementPendingGUIUpdateEvents(){ + //_windowStateLock->Lock(); + _pendingGUIUpdateEventsCount++; + //_windowStateLock->Unlock(); +} + +void SpectrumGUIClass::DecrementPendingGUIUpdateEvents(){ + //_windowStateLock->Lock(); + if(_pendingGUIUpdateEventsCount > 0){ + _pendingGUIUpdateEventsCount--; + } + //_windowStateLock->Unlock(); +} + +void SpectrumGUIClass::ResetPendingGUIUpdateEvents(){ + //_windowStateLock->Lock(); + _pendingGUIUpdateEventsCount = 0; + //_windowStateLock->Unlock(); +} + + +#endif /* SPECTRUM_GUI_CLASS_CPP */ diff --git a/gr-qtgui/src/lib/SpectrumGUIClass.h b/gr-qtgui/src/lib/SpectrumGUIClass.h new file mode 100644 index 000000000..4f8fb9782 --- /dev/null +++ b/gr-qtgui/src/lib/SpectrumGUIClass.h @@ -0,0 +1,89 @@ +#ifndef SPECTRUM_GUI_CLASS_HPP +#define SPECTRUM_GUI_CLASS_HPP + +//#include <mutexClass.hpp> +#include <qwidget.h> +#include <qapplication.h> +#include <qlabel.h> +#include <qslider.h> +#include <spectrumUpdateEvents.h> + +//#include <Windowing.hpp> + +class SpectrumDisplayForm; +#include <spectrumdisplayform.h> + +#include <cmath> + +#include <complex> +#include <vector> +#include <string> + +class SpectrumGUIClass{ +public: + SpectrumGUIClass(const uint64_t, const uint64_t, const double, const double); + ~SpectrumGUIClass(); + void Reset(); + + void OpenSpectrumWindow(QWidget*); + void SetDisplayTitle(const std::string); + + bool GetWindowOpenFlag(); + void SetWindowOpenFlag(const bool); + + void SetFrequencyRange(const double, const double, const double); + double GetStartFrequency()const; + double GetStopFrequency()const; + double GetCenterFrequency()const; + + void UpdateWindow(const bool, const std::complex<float>*, const uint64_t, const float*, const uint64_t, const float*, const uint64_t, const double, const timespec, const bool); + + float GetPowerValue()const; + void SetPowerValue(const float); + + int GetWindowType()const; + void SetWindowType(const int); + + int GetFFTSize()const; + int GetFFTSizeIndex()const; + void SetFFTSize(const int); + + timespec GetLastGUIUpdateTime()const; + void SetLastGUIUpdateTime(const timespec); + + unsigned int GetPendingGUIUpdateEvents()const; + void IncrementPendingGUIUpdateEvents(); + void DecrementPendingGUIUpdateEvents(); + void ResetPendingGUIUpdateEvents(); + + static const long MAX_FFT_SIZE = /*1048576*/32768; + static const long MIN_FFT_SIZE = 1024; + +protected: + +private: + + //MutexClass* _windowStateLock; + int64_t _dataPoints; + std::string _title; + double _centerFrequency; + double _startFrequency; + double _stopFrequency; + float _powerValue; + bool _windowOpennedFlag; + int _windowType; + int64_t _lastDataPointCount; + int _fftSize; + timespec _lastGUIUpdateTime; + unsigned int _pendingGUIUpdateEventsCount; + int _droppedEntriesCount; + bool _fftBuffersCreatedFlag; + + SpectrumDisplayForm* _spectrumDisplayForm; + + std::complex<float>* _fftPoints; + double* _realTimeDomainPoints; + double* _imagTimeDomainPoints; +}; + +#endif /* SPECTRUM_GUI_CLASS_HPP */ diff --git a/gr-qtgui/src/lib/TimeDomainDisplayPlot.cc b/gr-qtgui/src/lib/TimeDomainDisplayPlot.cc new file mode 100644 index 000000000..d8abffafa --- /dev/null +++ b/gr-qtgui/src/lib/TimeDomainDisplayPlot.cc @@ -0,0 +1,193 @@ +#ifndef TIME_DOMAIN_DISPLAY_PLOT_C +#define TIME_DOMAIN_DISPLAY_PLOT_C + +#include <TimeDomainDisplayPlot.h> + +#include <qwt_scale_draw.h> +#include <qwt_legend.h> + + +class TimeDomainDisplayZoomer: public QwtPlotZoomer +{ +public: + TimeDomainDisplayZoomer(QwtPlotCanvas* canvas):QwtPlotZoomer(canvas) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~TimeDomainDisplayZoomer(){ + + } + + virtual void updateTrackerText(){ + updateDisplay(); + } + +protected: + virtual QwtText trackerText( const QwtDoublePoint& p ) const + { + QwtText t(QString("Sample %1, %2 V").arg(p.x(), 0, 'f', 0).arg(p.y(), 0, 'f', 4)); + + return t; + } +}; + +TimeDomainDisplayPlot::TimeDomainDisplayPlot(QWidget* parent):QwtPlot(parent){ + timespec_reset(&_lastReplot); + + resize(parent->width(), parent->height()); + + _displayIntervalTime = (1.0/10.0); // 1/10 of a second between updates + + _numPoints = 1024; + _realDataPoints = new double[_numPoints]; + _imagDataPoints = new double[_numPoints]; + _xAxisPoints = new double[_numPoints]; + + // Disable polygon clipping + QwtPainter::setDeviceClipping(false); + + // We don't need the cache here + canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); + canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::xBottom, 0, _numPoints); + setAxisTitle(QwtPlot::xBottom, "Sample Number"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::yLeft, -1.0, 1.0); + setAxisTitle(QwtPlot::yLeft, "Normalized Voltage"); + + // Automatically deleted when parent is deleted + _real_plot_curve = new QwtPlotCurve("Real Data"); + _real_plot_curve->attach(this); + _real_plot_curve->setPen(QPen(Qt::blue)); + _real_plot_curve->setRawData(_xAxisPoints, _realDataPoints, _numPoints); + + _imag_plot_curve = new QwtPlotCurve("Imaginary Data"); + _imag_plot_curve->attach(this); + _imag_plot_curve->setPen(QPen(Qt::magenta)); + _imag_plot_curve->setRawData(_xAxisPoints, _imagDataPoints, _numPoints); + // _imag_plot_curve->setVisible(false); + + memset(_realDataPoints, 0x0, _numPoints*sizeof(double)); + memset(_imagDataPoints, 0x0, _numPoints*sizeof(double)); + memset(_xAxisPoints, 0x0, _numPoints*sizeof(double)); + + _resetXAxisPoints(); + + replot(); + + _zoomer = new TimeDomainDisplayZoomer(canvas()); +#if QT_VERSION < 0x040000 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#else + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#endif + _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); + + QwtLegend* legendDisplay = new QwtLegend(this); + legendDisplay->setItemMode(QwtLegend::CheckableItem); + insertLegend(legendDisplay); + + connect(this, SIGNAL( legendChecked(QwtPlotItem *, bool ) ), this, SLOT( LegendEntryChecked(QwtPlotItem *, bool ) )); +} + +TimeDomainDisplayPlot::~TimeDomainDisplayPlot(){ + delete[] _realDataPoints; + delete[] _imagDataPoints; + delete[] _xAxisPoints; + + // _fft_plot_curves deleted when parent deleted + // _zoomer and _panner deleted when parent deleted +} + + + +void TimeDomainDisplayPlot::replot(){ + + const timespec startTime = get_highres_clock(); + + QwtPlot::replot(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + differenceTime *= 99.0; + // Require at least a 10% duty cycle + if(differenceTime > (1.0/10.0)){ + _displayIntervalTime = differenceTime; + } +} + +void TimeDomainDisplayPlot::PlotNewData(const double* realDataPoints, const double* imagDataPoints, const int64_t numDataPoints){ + if(numDataPoints > 0){ + + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + delete[] _realDataPoints; + delete[] _imagDataPoints; + delete[] _xAxisPoints; + _realDataPoints = new double[_numPoints]; + _imagDataPoints = new double[_numPoints]; + _xAxisPoints = new double[_numPoints]; + + _real_plot_curve->setRawData(_xAxisPoints, _realDataPoints, _numPoints); + _imag_plot_curve->setRawData(_xAxisPoints, _imagDataPoints, _numPoints); + + _resetXAxisPoints(); + } + memcpy(_realDataPoints, realDataPoints, numDataPoints*sizeof(double)); + memcpy(_imagDataPoints, imagDataPoints, numDataPoints*sizeof(double)); + + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only replot the screen if it is visible + if(isVisible()){ + replot(); + } + _lastReplot = get_highres_clock(); + } +} + +void TimeDomainDisplayPlot::SetImaginaryDataVisible(const bool visibleFlag){ + _imag_plot_curve->setVisible(visibleFlag); +} + +void TimeDomainDisplayPlot::_resetXAxisPoints(){ + for(long loc = 0; loc < _numPoints; loc++){ + _xAxisPoints[loc] = loc; + } + setAxisScale(QwtPlot::xBottom, 0, _numPoints); +} + +void TimeDomainDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on){ + plotItem->setVisible(!on); +} + +#endif /* TIME_DOMAIN_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/src/lib/TimeDomainDisplayPlot.h b/gr-qtgui/src/lib/TimeDomainDisplayPlot.h new file mode 100644 index 000000000..0a10be174 --- /dev/null +++ b/gr-qtgui/src/lib/TimeDomainDisplayPlot.h @@ -0,0 +1,54 @@ +#ifndef TIME_DOMAIN_DISPLAY_PLOT_HPP +#define TIME_DOMAIN_DISPLAY_PLOT_HPP + +#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 <highResTimeFunctions.h> +#include <qwt_symbol.h> + +class TimeDomainDisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + TimeDomainDisplayPlot(QWidget*); + virtual ~TimeDomainDisplayPlot(); + + void PlotNewData(const double* realDataPoints, const double* imagDataPoints, const int64_t numDataPoints); + + void SetImaginaryDataVisible(const bool); + + virtual void replot(); + +protected slots: + void LegendEntryChecked(QwtPlotItem *plotItem, bool on); + +protected: + +private: + void _resetXAxisPoints(); + + QwtPlotCurve* _real_plot_curve; + QwtPlotCurve* _imag_plot_curve; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + double* _realDataPoints; + double* _imagDataPoints; + double* _xAxisPoints; + + timespec _lastReplot; + + int64_t _numPoints; + + double _displayIntervalTime; +}; + +#endif /* TIME_DOMAIN_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc new file mode 100644 index 000000000..21011ed68 --- /dev/null +++ b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc @@ -0,0 +1,351 @@ +#ifndef WATERFALL_3D_DISPLAY_PLOT_C +#define WATERFALL_3D_DISPLAY_PLOT_C + +#include <Waterfall3DDisplayPlot.h> + +#include <qwt3d_helper.h> +#include <qapplication.h> + +Waterfall3DColorMap::Waterfall3DColorMap(): Qwt3D::Color(), QwtLinearColorMap(){ + _interval.setInterval(0, 1.0); + +} + +Waterfall3DColorMap::~Waterfall3DColorMap(){ + +} + +Qwt3D::RGBA Waterfall3DColorMap::operator()(double, double, double z)const{ + return Qwt3D::RGBA(Qwt3D::Qt2GL(color(_interval, z))); +} + +void Waterfall3DColorMap::SetInterval(const double minValue, const double maxValue){ + _interval.setInterval(minValue, maxValue); +} + +Qwt3D::ColorVector& Waterfall3DColorMap::createVector(Qwt3D::ColorVector& vec) { + // Generate 100 interval values and then return those + Qwt3D::ColorVector colorVec; + for(unsigned int number = 0; number < 100; number++){ + double value = (_interval.width() * (static_cast<double>(number) / 100.0)) + _interval.minValue(); + colorVec.push_back(operator()(0,0,value)); + } + vec = colorVec; + return vec; +} + + +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED; + +Waterfall3DDisplayPlot::Waterfall3DDisplayPlot(QWidget* parent):Qwt3D::SurfacePlot(parent){ + _startFrequency = 0; + _stopFrequency = 4000; + + _createCoordinateSystemFlag = true; + + _initialized = false; + + _numPoints = 1024; + + _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates + + timespec_reset(&_lastReplot); + + _useCenterFrequencyFlag = false; + _centerFrequency = 0.0; + + _timePerFFT = 1.0; + timespec_reset(&_dataTimestamp); + + coordinates()->setAutoScale(false); + + _waterfallData = new Waterfall3DData(_startFrequency, _stopFrequency, _numPoints, 200); + _waterfallData->assign(this); + _waterfallData->create(); + + _intensityColorMapType = -1; + SetIntensityColorMapType(INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR, Qt::white, Qt::black, true, true); + + legend()->setScale(Qwt3D::LINEARSCALE); + legend()->setTitleString("Intensity"); + + enableMouse(true); +} + +Waterfall3DDisplayPlot::~Waterfall3DDisplayPlot(){ + delete _waterfallData; +} + +void Waterfall3DDisplayPlot::Init(){ + if(!_initialized && initializedGL()){ + resize(parentWidget()->width(), parentWidget()->height()); + + // Attempting to prevent valgrind uninitialized variable errors in QwtPlot3d::Drawable class + glDisable(GL_POLYGON_OFFSET_FILL); + + setPlotStyle(Qwt3D::FILLED); + + setCoordinateStyle(Qwt3D::FRAME); + setFloorStyle(Qwt3D::FLOORDATA); + setOrtho(true); + + _initialized = true; + } +} + +void Waterfall3DDisplayPlot::Reset(){ + _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints); + _waterfallData->Reset(); + + if(initializedGL()){ + setScale(1, static_cast<int>(((_stopFrequency - _startFrequency) / 200) ), 10); + } + + _createCoordinateSystemFlag = true; + + timespec_reset(&_dataTimestamp); + _timePerFFT = 1.0; +} + +void Waterfall3DDisplayPlot::SetFrequencyRange(const double startFreq, const double stopFreq, const double centerFreq, const bool useCenterFrequencyFlag){ + if((stopFreq > 0) && (stopFreq > startFreq)){ + _startFrequency = startFreq; + _stopFrequency = stopFreq; + + _useCenterFrequencyFlag = useCenterFrequencyFlag; + _centerFrequency = centerFreq; + + Reset(); + + // Only replot if screen is visible + if(isVisible()){ + replot(); + } + } +} + +bool Waterfall3DDisplayPlot::loadFromData(double** data, unsigned int columns, unsigned int rows + ,double minx, double maxx, double miny, double maxy){ + + Qwt3D::GridData* gridPtr = (Qwt3D::GridData*)actualData_p; + + gridPtr->setPeriodic(false,false); + gridPtr->setSize(columns,rows); + readIn(*gridPtr,data,columns,rows,minx,maxx,miny,maxy); + calcNormals(*gridPtr); + + updateData(); + updateNormals(); + + if( _createCoordinateSystemFlag ){ + createCoordinateSystem(); + + for (unsigned i=0; i!=coordinates()->axes.size(); ++i) + { + coordinates()->axes[i].setMajors(5); + coordinates()->axes[i].setMinors(3); + } + + coordinates()->axes[Qwt3D::Y1].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y2].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y3].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y4].setLabelString("Time"); + coordinates()->axes[Qwt3D::Z1].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z2].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z3].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z4].setLabelString("Intensity (dB)"); + + coordinates()->axes[Qwt3D::X1].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X2].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X3].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X4].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + + // The QwtPlot3D Interface takes ownership of these items, so there is no need to delete them... + coordinates()->axes[Qwt3D::X1].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X2].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X3].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X4].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + + coordinates()->axes[Qwt3D::Y1].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y2].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y3].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y4].setScale(new TimeScale(this)); + + coordinates()->axes[Qwt3D::Z1].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z2].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z3].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z4].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + + _createCoordinateSystemFlag = false; + } + + return true; +} + +double Waterfall3DDisplayPlot::GetStartFrequency()const{ + return _startFrequency; +} + +double Waterfall3DDisplayPlot::GetStopFrequency()const{ + return _stopFrequency; +} + +void Waterfall3DDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames){ + if(numDataPoints > 0){ + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + Reset(); + + if(isVisible()){ + replot(); + } + + _createCoordinateSystemFlag = true; + + _lastReplot = get_highres_clock(); + } + + _dataTimestamp = timestamp; + _timePerFFT = timePerFFT; + + _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames); + _waterfallData->IncrementNumLinesToUpdate(); + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only update when window is visible + if(isVisible()){ + replot(); + } + + _lastReplot = get_highres_clock(); + } +} + +void Waterfall3DDisplayPlot::SetIntensityRange(const double minIntensity, const double maxIntensity){ + _waterfallData->SetFloorValue(minIntensity); + _waterfallData->setMinZ(0); + _waterfallData->setMaxZ(maxIntensity-minIntensity); + + _createCoordinateSystemFlag = true; + + emit UpdatedLowerIntensityLevel(minIntensity); + emit UpdatedUpperIntensityLevel(maxIntensity); + + SetIntensityColorMapType(_intensityColorMapType, _userDefinedLowIntensityColor, _userDefinedLowIntensityColor, true); +} + +void Waterfall3DDisplayPlot::replot(){ + + if(!_initialized){ + Init(); + } + if(initializedGL()){ + const timespec startTime = get_highres_clock(); + + _waterfallData->create(); + + legend()->setMajors(4); + legend()->setMinors(5); + double start, stop; + coordinates()->axes[Qwt3D::Z1].limits(start,stop); + legend()->setLimits( _waterfallData->GetFloorValue(), _waterfallData->GetFloorValue() + stop - start ); + + coordinates()->axes[Qwt3D::X1].limits(start,stop); + + showColorLegend(true); + + updateGL(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + // Require at least a 20% duty cycle + differenceTime *= 4.0; + if(differenceTime > (1.0/5.0)){ + _displayIntervalTime = differenceTime; + } + } +} + +int Waterfall3DDisplayPlot::GetIntensityColorMapType()const{ + return _intensityColorMapType; +} + +void Waterfall3DDisplayPlot::SetIntensityColorMapType(const int newType, const QColor lowColor, const QColor highColor, const bool forceFlag, const bool noReplotFlag){ + if(((_intensityColorMapType != newType) || forceFlag) || + ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && + (lowColor.isValid() && highColor.isValid()))){ + + Waterfall3DColorMap* colorMap = new Waterfall3DColorMap(); + colorMap->SetInterval(_waterfallData->minZ(), _waterfallData->maxZ()); + + switch(newType){ + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::darkCyan, Qt::white); + colorMap->addColorStop(0.25, Qt::cyan); + colorMap->addColorStop(0.5, Qt::yellow); + colorMap->addColorStop(0.75, Qt::red); + setBackgroundColor(Qwt3D::Qt2GL(Qt::gray)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::black, Qt::white); + setBackgroundColor(Qwt3D::Qt2GL(Qt::blue)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::white, Qt::black); + setBackgroundColor(Qwt3D::Qt2GL(Qt::blue)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::black, Qt::white); + colorMap->addColorStop(0.5, Qt::darkRed); + setBackgroundColor(Qwt3D::Qt2GL(Qt::gray)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{ + _userDefinedLowIntensityColor = lowColor; + _userDefinedHighIntensityColor = highColor; + _intensityColorMapType = newType; + colorMap->setColorInterval(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor); + setBackgroundColor(Qwt3D::Qt2GL(Qt::white)); + break; + } + default: + colorMap->setColorInterval(Qt::black, Qt::white); + break; + } + + // Qwt3D takes over destruction of this object... + setDataColor(colorMap); + + if(!noReplotFlag){ + // Draw again + replot(); + + // Update the last replot timer + _lastReplot = get_highres_clock(); + } + } +} + +const QColor Waterfall3DDisplayPlot::GetUserDefinedLowIntensityColor()const{ + return _userDefinedLowIntensityColor; +} + +const QColor Waterfall3DDisplayPlot::GetUserDefinedHighIntensityColor()const{ + return _userDefinedHighIntensityColor; +} + +#endif /* WATERFALL_3D_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/src/lib/Waterfall3DDisplayPlot.h b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.h new file mode 100644 index 000000000..dcdbf380c --- /dev/null +++ b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.h @@ -0,0 +1,188 @@ +#ifndef WATERFALL_3D_DISPLAY_PLOT_HPP +#define WATERFALL_3D_DISPLAY_PLOT_HPP + +#include <highResTimeFunctions.h> + +#include <waterfallGlobalData.h> +#include <qwt3d_surfaceplot.h> + +#include <qwt3d_color.h> +#include <qwt_color_map.h> + +class Waterfall3DColorMap:public Qwt3D::Color, public QwtLinearColorMap{ +public: + Waterfall3DColorMap(); + virtual ~Waterfall3DColorMap(); + + virtual Qwt3D::RGBA operator()(double x, double y, double z)const; + virtual Qwt3D::ColorVector& createVector(Qwt3D::ColorVector& vec); + + virtual void SetInterval(const double minValue, const double maxValue); + +protected: + +private: + QwtDoubleInterval _interval; +}; + +class Waterfall3DDisplayPlot:public Qwt3D::SurfacePlot{ + Q_OBJECT + + protected: + class IntensityScale:public Qwt3D::LinearScale{ + + friend class Qwt3D::Axis; + friend class Qwt3D::qwt3d_ptr<Qwt3D::Scale>; + + private: + double _floor; + + public: + explicit IntensityScale(const double newFloor):_floor(newFloor){ } + virtual ~IntensityScale(){} + + virtual QString ticLabel(unsigned int idx) const{ + if (idx<majors_p.size()) + { + return QString("%1").arg( majors_p[idx] + GetFloorValue(), 0, 'f', 0 ); + } + return QString(""); + } + + virtual double GetFloorValue()const{ return _floor; } + virtual void SetFloorValue(const double newFloor){ _floor = newFloor; } + + //! Returns a new heap based object utilized from qwt3d_ptr + Scale* clone() const {return new IntensityScale(*this);} + }; + + class TimeScale:public Qwt3D::LinearScale{ + + friend class Qwt3D::Axis; + friend class Qwt3D::qwt3d_ptr<Qwt3D::Scale>; + friend class Waterfall3DDisplayPlot; + + private: + Waterfall3DDisplayPlot* _plot; + + public: + TimeScale(Waterfall3DDisplayPlot* plot ):_plot(plot){ + } + virtual ~TimeScale(){ + } + + virtual QString ticLabel(unsigned int idx) const{ + if (idx<majors_p.size()) + { + const timespec markerTime = timespec_add(_plot->_dataTimestamp, -(_plot->_timePerFFT) * majors_p[idx]); + struct tm timeTm; + gmtime_r(&markerTime.tv_sec, &timeTm); + + char* timeBuffer = new char[128]; + snprintf(timeBuffer, 128, "%02d:%02d:%02d.%03ld", timeTm.tm_hour, timeTm.tm_min, timeTm.tm_sec, (markerTime.tv_nsec / 1000000)); + QString returnBuffer(timeBuffer); + delete[] timeBuffer; + return returnBuffer; + } + return QString(""); + } + + //! Returns a new heap based object utilized from qwt3d_ptr + Scale* clone() const {return new TimeScale(*this);} + }; + + class FrequencyScale: public Qwt3D::LinearScale{ + + friend class Qwt3D::Axis; + friend class Qwt3D::qwt3d_ptr<Qwt3D::Scale>; + private: + double _centerFrequency; + bool _useCenterFrequencyFlag; + public: + FrequencyScale(bool useCenterFrequencyFlag, double centerFrequency):_centerFrequency(centerFrequency),_useCenterFrequencyFlag(useCenterFrequencyFlag){} + virtual ~FrequencyScale(){} + + virtual QString ticLabel(unsigned int idx) const{ + if (idx<majors_p.size()) + { + if(!_useCenterFrequencyFlag){ + return QString("%1").arg( majors_p[idx], 0, 'f', 0 ); + + } + else{ + return QString("%1").arg( (majors_p[idx] + _centerFrequency)/1000.0, 0, 'f', 3 ); + } + } + return QString(""); + } + + //! Returns a new heap based object utilized from qwt3d_ptr + Scale* clone() const {return new FrequencyScale(*this);} + }; + +public: + Waterfall3DDisplayPlot(QWidget*); + virtual ~Waterfall3DDisplayPlot(); + + void Init(); + void Reset(); + + bool loadFromData(double** data, unsigned int columns, unsigned int rows + ,double minx, double maxx, double miny, double maxy); + + void SetFrequencyRange(const double, const double, const double, const bool); + double GetStartFrequency()const; + double GetStopFrequency()const; + + void PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames); + + void SetIntensityRange(const double minIntensity, const double maxIntensity); + + virtual void replot(void); + + int GetIntensityColorMapType()const; + void SetIntensityColorMapType( const int, const QColor, const QColor, const bool forceFlag = false, const bool noReplotFlag = false ); + const QColor GetUserDefinedLowIntensityColor()const; + const QColor GetUserDefinedHighIntensityColor()const; + + static const int INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR = 0; + static const int INTENSITY_COLOR_MAP_TYPE_WHITE_HOT = 1; + static const int INTENSITY_COLOR_MAP_TYPE_BLACK_HOT = 2; + static const int INTENSITY_COLOR_MAP_TYPE_INCANDESCENT = 3; + static const int INTENSITY_COLOR_MAP_TYPE_USER_DEFINED = 4; + +signals: + void UpdatedLowerIntensityLevel(const double); + void UpdatedUpperIntensityLevel(const double); + +protected: + + double _startFrequency; + double _stopFrequency; + + Waterfall3DData* _waterfallData; + + timespec _lastReplot; + + int64_t _numPoints; + + double _displayIntervalTime; + + int _intensityColorMapType; + QColor _userDefinedLowIntensityColor; + QColor _userDefinedHighIntensityColor; + + bool _useCenterFrequencyFlag; + double _centerFrequency; + + timespec _dataTimestamp; + double _timePerFFT; + bool _initialized; + + bool _createCoordinateSystemFlag; + +private: + +}; + +#endif /* WATERFALL_3D_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/src/lib/WaterfallDisplayPlot.cc b/gr-qtgui/src/lib/WaterfallDisplayPlot.cc new file mode 100644 index 000000000..0f15d95fd --- /dev/null +++ b/gr-qtgui/src/lib/WaterfallDisplayPlot.cc @@ -0,0 +1,483 @@ +#ifndef WATERFALL_DISPLAY_PLOT_C +#define WATERFALL_DISPLAY_PLOT_C + +#include <WaterfallDisplayPlot.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_plot_layout.h> + +#include <qapplication.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: + 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 + GetCenterFrequency()) / ((GetFrequencyPrecision() == 0) ? 1.0 : 1000.0), 0, 'f', GetFrequencyPrecision()); + } + + virtual void initiateUpdate(){ + invalidateCache(); + } + +protected: + +private: + +}; + +class TimeScaleData +{ +public: + TimeScaleData(){ + timespec_reset(&_zeroTime); + _secondsPerLine = 1.0; + + } + + virtual ~TimeScaleData(){ + + } + + virtual timespec GetZeroTime()const{ + return _zeroTime; + } + + virtual void SetZeroTime(const timespec newTime){ + _zeroTime = newTime; + } + + virtual void SetSecondsPerLine(const double newTime){ + _secondsPerLine = newTime; + } + + virtual double GetSecondsPerLine()const{ + return _secondsPerLine; + } + + +protected: + timespec _zeroTime; + double _secondsPerLine; + +private: + +}; + +class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData +{ +public: + QwtTimeScaleDraw():QwtScaleDraw(),TimeScaleData(){ + + } + + virtual ~QwtTimeScaleDraw(){ + + } + + virtual QwtText label(double value)const{ + QwtText returnLabel(""); + + timespec lineTime = timespec_add(GetZeroTime(), (-value) * GetSecondsPerLine()); + struct tm timeTm; + gmtime_r(&lineTime.tv_sec, &timeTm); + returnLabel = (QString("").sprintf("%04d/%02d/%02d\n%02d:%02d:%02d.%03ld", timeTm.tm_year+1900, timeTm.tm_mon+1, timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min, timeTm.tm_sec, lineTime.tv_nsec/1000000)); + + return returnLabel; + } + + 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(); + } + +protected: + +private: + +}; + +class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass +{ +public: + WaterfallZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision):QwtPlotZoomer(canvas), TimeScaleData(), FreqOffsetAndPrecisionClass(freqPrecision) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~WaterfallZoomer(){ + + } + + virtual void updateTrackerText(){ + updateDisplay(); + } + +protected: + virtual QwtText trackerText( const QwtDoublePoint& p ) const + { + QString yLabel(""); + + timespec lineTime = timespec_add(GetZeroTime(), (-p.y()) * GetSecondsPerLine()); + struct tm timeTm; + gmtime_r(&lineTime.tv_sec, &timeTm); + yLabel = (QString("").sprintf("%04d/%02d/%02d %02d:%02d:%02d.%03ld", timeTm.tm_year+1900, timeTm.tm_mon+1, timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min, timeTm.tm_sec, lineTime.tv_nsec/1000000)); + + QwtText t(QString("%1 %2, %3").arg((p.x() + GetCenterFrequency()) / ((GetFrequencyPrecision() == 0) ? 1.0 : 1000.0), 0, 'f', GetFrequencyPrecision()).arg( (GetFrequencyPrecision() == 0) ? "Hz" : "kHz").arg(yLabel)); + + return t; + } +}; + + +const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR; +const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT; +const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT; +const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT; +const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED; + +WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent):QwtPlot(parent){ + _zoomer = NULL; + _startFrequency = 0; + _stopFrequency = 4000; + + resize(parent->width(), parent->height()); + _numPoints = 1024; + + _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates + + _waterfallData = new WaterfallData(_startFrequency, _stopFrequency, _numPoints, 200); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(0)); + + setAxisTitle(QwtPlot::yLeft, "Time"); + setAxisScaleDraw(QwtPlot::yLeft, new QwtTimeScaleDraw()); + + timespec_reset(&_lastReplot); + + d_spectrogram = new PlotWaterfall(_waterfallData, "Waterfall Display"); + + _intensityColorMapType = INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR; + + QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white); + colorMap.addColorStop(0.25, Qt::cyan); + colorMap.addColorStop(0.5, Qt::yellow); + colorMap.addColorStop(0.75, Qt::red); + + d_spectrogram->setColorMap(colorMap); + + d_spectrogram->attach(this); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + + _zoomer = new WaterfallZoomer(canvas(), 0); +#if QT_VERSION < 0x040000 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#else + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#endif + _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::white); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + _UpdateIntensityRangeDisplay(); +} + +WaterfallDisplayPlot::~WaterfallDisplayPlot(){ + delete _waterfallData; +} + +void WaterfallDisplayPlot::Reset(){ + _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints); + _waterfallData->Reset(); + + // Load up the new base zoom settings + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(_startFrequency); + newSize.setWidth(_stopFrequency-_startFrequency); + _zoomer->zoom(newSize); + _zoomer->setZoomBase(newSize); + _zoomer->zoom(0); +} + +void WaterfallDisplayPlot::SetFrequencyRange(const double startFreq, const double stopFreq, const double centerFreq, const bool useCenterFrequencyFlag){ + if((stopFreq > 0) && (stopFreq > startFreq)){ + _startFrequency = startFreq; + _stopFrequency = stopFreq; + + if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){ + WaterfallFreqDisplayScaleDraw* freqScale = ((WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom)); + freqScale->SetCenterFrequency(centerFreq); + ((WaterfallZoomer*)_zoomer)->SetCenterFrequency(centerFreq); + if(useCenterFrequencyFlag){ + freqScale->SetFrequencyPrecision( 3 ); + ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision( 3 ); + setAxisTitle(QwtPlot::xBottom, "Frequency (kHz)"); + } + else{ + freqScale->SetFrequencyPrecision( 0 ); + ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision( 0 ); + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + } + } + + Reset(); + + // Only replot if screen is visible + if(isVisible()){ + replot(); + } + } +} + + +double WaterfallDisplayPlot::GetStartFrequency()const{ + return _startFrequency; +} + +double WaterfallDisplayPlot::GetStopFrequency()const{ + return _stopFrequency; +} + +void WaterfallDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames){ + if(numDataPoints > 0){ + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + Reset(); + + d_spectrogram->invalidateCache(); + d_spectrogram->itemChanged(); + + if(isVisible()){ + replot(); + } + + _lastReplot = get_highres_clock(); + } + + _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames); + _waterfallData->IncrementNumLinesToUpdate(); + + QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft); + timeScale->SetSecondsPerLine(timePerFFT); + timeScale->SetZeroTime(timestamp); + + ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT); + ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp); + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + + d_spectrogram->invalidateCache(); + d_spectrogram->itemChanged(); + + // Only update when window is visible + if(isVisible()){ + replot(); + } + + _lastReplot = get_highres_clock(); + } +} + +void WaterfallDisplayPlot::SetIntensityRange(const double minIntensity, const double maxIntensity){ + _waterfallData->setRange(QwtDoubleInterval(minIntensity, maxIntensity)); + + emit UpdatedLowerIntensityLevel(minIntensity); + emit UpdatedUpperIntensityLevel(maxIntensity); + + _UpdateIntensityRangeDisplay(); +} + +void WaterfallDisplayPlot::replot(){ + const timespec startTime = get_highres_clock(); + + QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft); + timeScale->initiateUpdate(); + + WaterfallFreqDisplayScaleDraw* freqScale = (WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom); + freqScale->initiateUpdate(); + + // Update the time axis display + if(axisWidget(QwtPlot::yLeft) != NULL){ + axisWidget(QwtPlot::yLeft)->update(); + } + + // Update the Frequency Offset Display + if(axisWidget(QwtPlot::xBottom) != NULL){ + axisWidget(QwtPlot::xBottom)->update(); + } + + if(_zoomer != NULL){ + ((WaterfallZoomer*)_zoomer)->updateTrackerText(); + } + + QwtPlot::replot(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + // Require at least a 5% duty cycle + differenceTime *= 19.0; + if(differenceTime > (1.0/5.0)){ + _displayIntervalTime = differenceTime; + } +} + +int WaterfallDisplayPlot::GetIntensityColorMapType()const{ + return _intensityColorMapType; +} + +void WaterfallDisplayPlot::SetIntensityColorMapType(const int newType, const QColor lowColor, const QColor highColor){ + if((_intensityColorMapType != newType) || + ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && + (lowColor.isValid() && highColor.isValid()))){ + switch(newType){ + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{ + _intensityColorMapType = newType; + QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white); + colorMap.addColorStop(0.25, Qt::cyan); + colorMap.addColorStop(0.5, Qt::yellow); + colorMap.addColorStop(0.75, Qt::red); + d_spectrogram->setColorMap(colorMap); + break; + } + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{ + _intensityColorMapType = newType; + QwtLinearColorMap colorMap(Qt::black, Qt::white); + d_spectrogram->setColorMap(colorMap); + break; + } + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{ + _intensityColorMapType = newType; + QwtLinearColorMap colorMap(Qt::white, Qt::black); + d_spectrogram->setColorMap(colorMap); + break; + } + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{ + _intensityColorMapType = newType; + QwtLinearColorMap colorMap(Qt::black, Qt::white); + colorMap.addColorStop(0.5, Qt::darkRed); + d_spectrogram->setColorMap(colorMap); + break; + } + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{ + _userDefinedLowIntensityColor = lowColor; + _userDefinedHighIntensityColor = highColor; + _intensityColorMapType = newType; + QwtLinearColorMap colorMap(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor); + d_spectrogram->setColorMap(colorMap); + break; + } + default: break; + } + + _UpdateIntensityRangeDisplay(); + } +} + +const QColor WaterfallDisplayPlot::GetUserDefinedLowIntensityColor()const{ + return _userDefinedLowIntensityColor; +} + +const QColor WaterfallDisplayPlot::GetUserDefinedHighIntensityColor()const{ + return _userDefinedHighIntensityColor; +} + +void WaterfallDisplayPlot::_UpdateIntensityRangeDisplay(){ + QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight); + rightAxis->setTitle("Intensity (dB)"); + rightAxis->setColorBarEnabled(true); + rightAxis->setColorMap(d_spectrogram->data()->range(), + d_spectrogram->colorMap()); + + setAxisScale(QwtPlot::yRight, + d_spectrogram->data()->range().minValue(), + d_spectrogram->data()->range().maxValue() ); + enableAxis(QwtPlot::yRight); + + plotLayout()->setAlignCanvasToScales(true); + + // Tell the display to redraw everything + d_spectrogram->invalidateCache(); + d_spectrogram->itemChanged(); + + // Draw again + replot(); + + // Update the last replot timer + _lastReplot = get_highres_clock(); +} + +#endif /* WATERFALL_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/src/lib/WaterfallDisplayPlot.h b/gr-qtgui/src/lib/WaterfallDisplayPlot.h new file mode 100644 index 000000000..71fb76aaf --- /dev/null +++ b/gr-qtgui/src/lib/WaterfallDisplayPlot.h @@ -0,0 +1,72 @@ +#ifndef WATERFALL_DISPLAY_PLOT_HPP +#define WATERFALL_DISPLAY_PLOT_HPP + +#include <qwt_plot.h> +#include <qwt_plot_zoomer.h> +#include <qwt_plot_panner.h> + +#include <plot_waterfall.h> + +#include <highResTimeFunctions.h> + +class WaterfallDisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + WaterfallDisplayPlot(QWidget*); + virtual ~WaterfallDisplayPlot(); + + void Reset(); + + void SetFrequencyRange(const double, const double, const double, const bool); + double GetStartFrequency()const; + double GetStopFrequency()const; + + void PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames); + + void SetIntensityRange(const double minIntensity, const double maxIntensity); + + virtual void replot(void); + + int GetIntensityColorMapType()const; + void SetIntensityColorMapType( const int, const QColor, const QColor ); + const QColor GetUserDefinedLowIntensityColor()const; + const QColor GetUserDefinedHighIntensityColor()const; + + static const int INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR = 0; + static const int INTENSITY_COLOR_MAP_TYPE_WHITE_HOT = 1; + static const int INTENSITY_COLOR_MAP_TYPE_BLACK_HOT = 2; + static const int INTENSITY_COLOR_MAP_TYPE_INCANDESCENT = 3; + static const int INTENSITY_COLOR_MAP_TYPE_USER_DEFINED = 4; + +signals: + void UpdatedLowerIntensityLevel(const double); + void UpdatedUpperIntensityLevel(const double); + +protected: + +private: + void _UpdateIntensityRangeDisplay(); + + double _startFrequency; + double _stopFrequency; + + PlotWaterfall *d_spectrogram; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + WaterfallData* _waterfallData; + + timespec _lastReplot; + + int64_t _numPoints; + + double _displayIntervalTime; + + int _intensityColorMapType; + QColor _userDefinedLowIntensityColor; + QColor _userDefinedHighIntensityColor; +}; + +#endif /* WATERFALL_DISPLAY_PLOT_HPP */ diff --git a/gr-qtgui/src/lib/highResTimeFunctions.h b/gr-qtgui/src/lib/highResTimeFunctions.h new file mode 100644 index 000000000..b85b1acad --- /dev/null +++ b/gr-qtgui/src/lib/highResTimeFunctions.h @@ -0,0 +1,190 @@ +#ifndef HIGH_RES_TIME_FUNCTIONS_H +#define HIGH_RES_TIME_FUNCTIONS_H + +#include <ctime> +#include <sys/time.h> +#include <cmath> +/* Requires the librt and libm libraries */ + +static const long NSEC_PER_SEC = 1000000000L; + +static inline bool timespec_greater(const struct timespec* t1, const struct timespec* t0){ + return ((t1->tv_sec > t0->tv_sec) || ((t1->tv_sec == t0->tv_sec) && (t1->tv_nsec > t0->tv_nsec))); +} + +static inline bool timespec_greater(const struct timespec t1, const struct timespec t0){ + return ((t1.tv_sec > t0.tv_sec) || ((t1.tv_sec == t0.tv_sec) && (t1.tv_nsec > t0.tv_nsec))); +} + +static inline bool timespec_less(const struct timespec* t1, const struct timespec* t0){ + return ((t1->tv_sec < t0->tv_sec) || ((t1->tv_sec == t0->tv_sec) && (t1->tv_nsec < t0->tv_nsec))); +} + +static inline bool timespec_less(const struct timespec t1, const struct timespec t0){ + return ((t1.tv_sec < t0.tv_sec) || ((t1.tv_sec == t0.tv_sec) && (t1.tv_nsec < t0.tv_nsec))); +} + +static inline bool timespec_equal(const struct timespec* t1, const struct timespec* t0){ + return ((t1->tv_sec == t0->tv_sec) && (t1->tv_nsec == t0->tv_nsec)); +} + +static inline bool timespec_equal(const struct timespec t1, const struct timespec t0){ + return ((t1.tv_sec == t0.tv_sec) && (t1.tv_nsec == t0.tv_nsec)); +} + +static inline void timespec_reset(struct timespec* ret){ + ret->tv_sec = 0; + ret->tv_nsec = 0; +} + +static inline void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec){ + while (nsec > NSEC_PER_SEC){ + nsec -= NSEC_PER_SEC; + ++sec; + } + while(nsec < 0){ + nsec += NSEC_PER_SEC; + --sec; + } + ts->tv_sec = sec; + ts->tv_nsec = nsec; +} + +static inline struct timespec convert_to_timespec(const double timeValue){ + struct timespec ret; + double seconds = 0; + long nsec = static_cast<long>(modf(timeValue, &seconds) * static_cast<double>(NSEC_PER_SEC)); + time_t sec = static_cast<time_t>(seconds); + + set_normalized_timespec(&ret, sec, nsec); + + return ret; +} + +static inline double convert_from_timespec(const timespec actual){ + return (static_cast<double>(actual.tv_sec) + (static_cast<double>(actual.tv_nsec) / static_cast<double>(NSEC_PER_SEC))); +} + +static inline void timespec_add(struct timespec *ret, const struct timespec* t1, const struct timespec* t0){ + time_t sec = t1->tv_sec + t0->tv_sec; + long nsec = t1->tv_nsec + t0->tv_nsec; + + set_normalized_timespec(ret, sec, nsec); +} + +static inline void timespec_add(struct timespec *ret, const struct timespec t1, const struct timespec t0){ + return timespec_add(ret, &t1, &t0); +} + +static inline struct timespec timespec_add(const struct timespec t1, const struct timespec t0){ + struct timespec ret; + timespec_add(&ret, &t1, &t0); + return ret; +} + +static inline struct timespec timespec_add(const struct timespec t1, const double time0){ + struct timespec ret; + struct timespec t0; + t0 = convert_to_timespec(time0); + + timespec_add(&ret, &t1, &t0); + + return ret; +} + +static inline void timespec_subtract(struct timespec *ret, const struct timespec* t1, const struct timespec* t0){ + time_t sec = t1->tv_sec - t0->tv_sec; + long nsec = t1->tv_nsec - t0->tv_nsec; + + set_normalized_timespec(ret, sec, nsec); +} + +static inline void timespec_subtract(struct timespec *ret, const struct timespec t1, const struct timespec t0){ + return timespec_subtract(ret, &t1, &t0); +} + +static inline struct timespec timespec_subtract(const struct timespec t1, const struct timespec t0){ + struct timespec ret; + timespec_subtract(&ret, &t1, &t0); + return ret; +} + +static inline struct timespec timespec_subtract(const struct timespec t1, const double time0){ + struct timespec ret; + struct timespec t0; + t0 = convert_to_timespec(time0); + + timespec_subtract(&ret, &t1, &t0); + + return ret; +} + +static inline double diff_timespec(struct timespec* ret, const struct timespec *t1, const struct timespec* t0){ + struct timespec actual; + time_t sec = 0; + long nsec = 0; + + if(timespec_greater(t1, t0)){ + sec = t1->tv_sec - t0->tv_sec; + nsec = t1->tv_nsec - t0->tv_nsec; + + set_normalized_timespec(&actual, sec, nsec); + + if(ret != NULL){ + ret->tv_sec = actual.tv_sec; + ret->tv_nsec = actual.tv_nsec; + } + + return convert_from_timespec(actual); + } + else{ + sec = t0->tv_sec - t1->tv_sec; + nsec = t0->tv_nsec - t1->tv_nsec; + + // Do nothing with the ret value as the ret value would have to store a negative, which it can't. + + set_normalized_timespec(&actual, sec, nsec); + + return (-convert_from_timespec(actual)); + } +} + +static inline double diff_timespec(struct timespec* ret, const struct timespec t1, const struct timespec t0){ + return diff_timespec(ret, &t1, &t0); +} + +static inline double diff_timespec(const struct timespec t1, const struct timespec t0){ + return diff_timespec(NULL, &t1, &t0); +} + + +static inline double diff_timespec(const struct timespec* t1, const struct timespec* t0){ + return diff_timespec(NULL, t1, t0); +} + + +static inline void get_highres_clock(struct timespec* ret){ + if(clock_gettime(CLOCK_REALTIME, ret) != 0){ + // Unable to get high resolution time - fail over to low resolution time + timeval lowResTime; + gettimeofday(&lowResTime, NULL); + ret->tv_sec = lowResTime.tv_sec; + ret->tv_nsec = lowResTime.tv_usec*1000; + } +} + +static inline struct timespec get_highres_clock(){ + struct timespec ret; + get_highres_clock(&ret); + return ret; +} + +static inline bool timespec_empty(const struct timespec* ret){ + return ( (ret->tv_sec == 0 ) && (ret->tv_nsec == 0) ); +} + +static inline bool timespec_empty(const struct timespec ret){ + return timespec_empty(&ret); +} + +#endif /* HIGH_RES_TIME_FUNCTIONS_H */ diff --git a/gr-qtgui/src/lib/plot_waterfall.cc b/gr-qtgui/src/lib/plot_waterfall.cc new file mode 100644 index 000000000..2b1447e03 --- /dev/null +++ b/gr-qtgui/src/lib/plot_waterfall.cc @@ -0,0 +1,325 @@ +#include <qimage.h> +#include <qpen.h> +#include <qpainter.h> +#include "qwt_painter.h" +#include "qwt_double_interval.h" +#include "qwt_scale_map.h" +#include "qwt_color_map.h" +#include "plot_waterfall.h" + +#if QT_VERSION < 0x040000 +typedef Q3ValueVector<QRgb> QwtColorTable; +#else +typedef QVector<QRgb> QwtColorTable; +#endif + + +class PlotWaterfallImage: public QImage +{ + // This class hides some Qt3/Qt4 API differences +public: + PlotWaterfallImage(const QSize &size, QwtColorMap::Format format): +#if QT_VERSION < 0x040000 + QImage(size, format == QwtColorMap::RGB ? 32 : 8) +#else + QImage(size, format == QwtColorMap::RGB + ? QImage::Format_ARGB32 : QImage::Format_Indexed8 ) +#endif + { + } + + PlotWaterfallImage(const QImage &other): + QImage(other) + { + } + + void initColorTable(const QImage& other) + { +#if QT_VERSION < 0x040000 + const unsigned int numColors = other.numColors(); + + setNumColors(numColors); + for ( unsigned int i = 0; i < numColors; i++ ) + setColor(i, other.color(i)); +#else + setColorTable(other.colorTable()); +#endif + } + +#if QT_VERSION < 0x040000 + + void setColorTable(const QwtColorTable &colorTable) + { + setNumColors(colorTable.size()); + for ( unsigned int i = 0; i < colorTable.size(); i++ ) + setColor(i, colorTable[i]); + } + + QwtColorTable colorTable() const + { + QwtColorTable table(numColors()); + for ( int i = 0; i < numColors(); i++ ) + table[i] = color(i); + + return table; + } +#endif +}; + +class PlotWaterfall::PrivateData +{ +public: + PrivateData() + { + data = NULL; + colorMap = new QwtLinearColorMap(); + } + ~PrivateData() + { + delete colorMap; + } + + WaterfallData *data; + QwtColorMap *colorMap; +}; + +/*! + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + The z value is initialized by 8.0. + + \param title Title + + \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ() +*/ +PlotWaterfall::PlotWaterfall(WaterfallData* data, const QString &title): + QwtPlotRasterItem(title) +{ + d_data = new PrivateData(); + d_data->data = data; + +// setCachePolicy(QwtPlotRasterItem::PaintCache); + + setItemAttribute(QwtPlotItem::AutoScale, true); + setItemAttribute(QwtPlotItem::Legend, false); + + setZ(8.0); +} + +//! Destructor +PlotWaterfall::~PlotWaterfall() +{ + delete d_data; +} + +const WaterfallData* PlotWaterfall::data()const{ + return d_data->data; +} + +//! \return QwtPlotItem::Rtti_PlotSpectrogram +int PlotWaterfall::rtti() const +{ + return QwtPlotItem::Rtti_PlotSpectrogram; +} + +/*! + 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 colorMap Color Map + + \sa colorMap(), QwtScaleWidget::setColorBarEnabled(), + QwtScaleWidget::setColorMap() +*/ +void PlotWaterfall::setColorMap(const QwtColorMap &colorMap) +{ + delete d_data->colorMap; + d_data->colorMap = colorMap.copy(); + + invalidateCache(); + itemChanged(); +} + +/*! + \return Color Map used for mapping the intensity values to colors + \sa setColorMap() +*/ +const QwtColorMap &PlotWaterfall::colorMap() const +{ + return *d_data->colorMap; +} +/*! + \return Bounding rect of the data + \sa QwtRasterData::boundingRect +*/ +QwtDoubleRect PlotWaterfall::boundingRect() const +{ + return d_data->data->boundingRect(); +} + +/*! + \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) +*/ +QSize PlotWaterfall::rasterHint(const QwtDoubleRect &rect) const +{ + return d_data->data->rasterHint(rect); +} + +/*! + \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() +*/ +QImage PlotWaterfall::renderImage( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QwtDoubleRect &area) const +{ + if ( area.isEmpty() ) + return QImage(); + + QRect rect = transform(xMap, yMap, area); + + QwtScaleMap xxMap = xMap; + QwtScaleMap yyMap = yMap; + + const QSize res = d_data->data->rasterHint(area); + 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); + } + + PlotWaterfallImage image(rect.size(), d_data->colorMap->format()); + + const QwtDoubleInterval intensityRange = d_data->data->range(); + 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)); + } + } + } + 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 ) + { +#ifdef __GNUC__ +#endif +#if QT_VERSION < 0x040000 + image = image.mirror(hInvert, vInvert); +#else + image = image.mirrored(hInvert, vInvert); +#endif + } + + return image; +} + +/*! + \brief Draw the spectrogram + + \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 PlotWaterfall::draw(QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &canvasRect) const +{ + QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect); +} + diff --git a/gr-qtgui/src/lib/plot_waterfall.h b/gr-qtgui/src/lib/plot_waterfall.h new file mode 100644 index 000000000..2be677b10 --- /dev/null +++ b/gr-qtgui/src/lib/plot_waterfall.h @@ -0,0 +1,54 @@ +#ifndef PLOT_WATERFALL_H +#define PLOT_WATERFALL_H + +#include <qglobal.h> +#include <waterfallGlobalData.h> + +#include "qwt_valuelist.h" +#include "qwt_plot_rasteritem.h" + +class QwtColorMap; + +/*! + \brief A plot item, which displays a waterfall spectrogram + + A waterfall displays threedimenional data, where the 3rd dimension + ( the intensity ) is displayed using colors. The colors are calculated + from the values using a color map. + + \image html spectrogram3.png + + \sa QwtRasterData, QwtColorMap +*/ + +class QWT_EXPORT PlotWaterfall: public QwtPlotRasterItem +{ +public: + explicit PlotWaterfall(WaterfallData* data, const QString &title = QString::null); + virtual ~PlotWaterfall(); + + const WaterfallData* data()const; + + void setColorMap(const QwtColorMap &); + const QwtColorMap &colorMap() const; + + virtual QwtDoubleRect boundingRect() const; + virtual QSize rasterHint(const QwtDoubleRect &) const; + + virtual int rtti() const; + + virtual void draw(QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &rect) const; + +protected: + virtual QImage renderImage( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QwtDoubleRect &rect) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/gr-qtgui/src/lib/qtgui.h b/gr-qtgui/src/lib/qtgui.h new file mode 100644 index 000000000..6edbca12c --- /dev/null +++ b/gr-qtgui/src/lib/qtgui.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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_H +#define INCLUDED_QTGUI_H + +#include <qapplication.h> +#include "SpectrumGUIClass.h" + +class qtgui_event : public QEvent +{ +private: + pthread_mutex_t *pmutex; + +public: + qtgui_event(pthread_mutex_t *mut) + : QEvent((QEvent::Type)(QEvent::User+101)) + { + pmutex = mut; + } + + void lock() + { + pthread_mutex_lock(pmutex); + + } + + void unlock() + { + pthread_mutex_unlock(pmutex); + } +}; + +class qtgui_obj : public QObject +{ +public: + qtgui_obj(QObject *p) + : QObject(p) + { + } + + void customEvent(QEvent *e) + { + if(e->type() == (QEvent::Type)(QEvent::User+101)) { + qtgui_event *qt = (qtgui_event*)e; + qt->unlock(); + } + } +}; + +#endif /* INCLUDED_QTGUI_H */ diff --git a/gr-qtgui/src/lib/qtgui.i b/gr-qtgui/src/lib/qtgui.i new file mode 100644 index 000000000..52156054c --- /dev/null +++ b/gr-qtgui/src/lib/qtgui.i @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +%feature("autodoc","1"); +%include "exception.i" +%import "gnuradio.i" + +%{ +#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix +#include "qtgui_sink_c.h" +#include "qtgui_sink_f.h" +#include <stdexcept> +%} + +GR_SWIG_BLOCK_MAGIC(qtgui,sink_c) + + qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + float fmin=-0.5, float fmax=0.5, const std::string &name="Display"); + +class qtgui_sink_c : public gr_block +{ +private: + friend qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + float fmin, float fmax, const std::string &name); + qtgui_sink_c (int fftsize, int wintype, + float fmin, float fmax, const std::string &name); + +public: + void start_app(); + +}; + + + +/****************************************************************************************/ + + +GR_SWIG_BLOCK_MAGIC(qtgui,sink_f) + +qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name="Display"); + +class qtgui_sink_f : public gr_block +{ +private: + friend qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name); + qtgui_sink_fy (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name); + +public: + void start_app(); +}; + diff --git a/gr-qtgui/src/lib/qtgui_sink_c.cc b/gr-qtgui/src/lib/qtgui_sink_c.cc new file mode 100644 index 000000000..eaef58d6e --- /dev/null +++ b/gr-qtgui/src/lib/qtgui_sink_c.cc @@ -0,0 +1,233 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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, + float fmin, float fmax, const std::string &name) +{ + return qtgui_sink_c_sptr (new qtgui_sink_c (fftsize, wintype, fmin, fmax, name)); +} + +qtgui_sink_c::qtgui_sink_c (int fftsize, int wintype, + float fmin, float fmax, const std::string &name) + : 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_fmin(fmin), d_fmax(fmax), d_name(name) +{ + d_main_gui = NULL; + pthread_mutex_init(&d_pmutex, NULL); + lock(); + + d_shift = true; // Perform fftshift operation; this is usually desired when plotting + + d_fft = new gri_fft_complex (d_fftsize, true); + + d_fftdata = new gr_complex[d_fftsize]; + + d_index = 0; + d_residbuf = new gr_complex[d_fftsize]; + + buildwindow(); +} + +qtgui_sink_c::~qtgui_sink_c() +{ + delete [] d_fftdata; + delete [] d_residbuf; + delete d_main_gui; + delete d_fft; +} + +void qtgui_sink_c::lock() +{ + pthread_mutex_lock(&d_pmutex); +} + +void qtgui_sink_c::unlock() +{ + pthread_mutex_unlock(&d_pmutex); +} + +void +qtgui_sink_c::start_app() +{ + int argc; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + + uint64_t maxBufferSize = 32768; + d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, d_fmin, d_fmax); + d_main_gui->SetDisplayTitle(d_name); + d_main_gui->SetFFTSize(d_fftsize); + d_main_gui->SetWindowType((int)d_wintype); + d_main_gui->OpenSpectrumWindow(NULL); + + qtgui_obj object(d_qApplication); + qApp->postEvent(&object, new qtgui_event(&d_pmutex)); + + d_qApplication->exec(); +} + + +void +qtgui_sink_c::fft(const gr_complex *data_in, int size, gr_complex *data_out) +{ + 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 + + for(int i=0; i < size; i++) { + d_fft->get_outbuf()[i] /= size; + } + + // copy result to our output + if(d_shift) { // apply a fft shift on the data + unsigned int len = (unsigned int)(ceil(size/2.0)); + memcpy(&data_out[0], &d_fft->get_outbuf()[len], sizeof(gr_complex)*(size - len)); + memcpy(&data_out[size - len], &d_fft->get_outbuf()[0], sizeof(gr_complex)*len); + } + else { + memcpy(data_out, d_fft->get_outbuf(), sizeof(gr_complex)*size); + } +} + +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 the fftdata buffer; no need to preserve old data + delete [] d_fftdata; + d_fftdata = new gr_complex[newfftsize]; + + // 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 i=0, j=0; + const gr_complex *in = (const gr_complex*)input_items[0]; + + pthread_mutex_lock(&d_pmutex); + + // Update the FFT size from the application + fftresize(); + windowreset(); + + if(d_index) { + int filler = std::min(d_fftsize - d_index, noutput_items); + + memcpy(&d_residbuf[d_index], &in[0], sizeof(gr_complex)*filler); + d_index += filler; + i = filler; + j = filler; + } + + if(d_index == d_fftsize) { + d_index = 0; + fft(d_residbuf, d_fftsize, d_fftdata); + + d_main_gui->UpdateWindow(true, d_fftdata, d_fftsize, NULL, 0, (float*)d_residbuf, d_fftsize, + 1.0/4.0, convert_to_timespec(0.0), true); + } + + for(; i < noutput_items; i+=d_fftsize) { + if(noutput_items - i > d_fftsize) { + j += d_fftsize; + fft(&in[i], d_fftsize, d_fftdata); + + d_main_gui->UpdateWindow(true, d_fftdata, d_fftsize, NULL, 0, (float*)&in[i], d_fftsize, + 1.0/4.0, convert_to_timespec(0.0), true); + } + } + + if(noutput_items > j) { + d_index = noutput_items - j; + memcpy(d_residbuf, &in[j], sizeof(gr_complex)*d_index); + } + + pthread_mutex_unlock(&d_pmutex); + + consume_each(noutput_items); + return noutput_items; +} diff --git a/gr-qtgui/src/lib/qtgui_sink_c.h b/gr-qtgui/src/lib/qtgui_sink_c.h new file mode 100644 index 000000000..a6bd59f27 --- /dev/null +++ b/gr-qtgui/src/lib/qtgui_sink_c.h @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 <gr_block.h> +#include <gr_firdes.h> +#include <gri_fft.h> +#include <qapplication.h> +#include <qtgui.h> +#include "SpectrumGUIClass.h" + + +class qtgui_sink_c; +typedef boost::shared_ptr<qtgui_sink_c> qtgui_sink_c_sptr; + +qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + float fmin=-0.5, float fmax=0.5, const std::string &name="Display"); + +class qtgui_sink_c : public gr_block +{ +private: + friend qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + float fmin, float fmax, const std::string &name); + qtgui_sink_c (int fftsize, int wintype, + float fmin, float fmax, const std::string &name); + + int d_fftsize; + gr_firdes::win_type d_wintype; + std::vector<float> d_window; + float d_fmin; + float d_fmax; + std::string d_name; + + pthread_mutex_t d_pmutex; + + bool d_shift; + gri_fft_complex *d_fft; + gr_complex *d_fftdata; + + int d_index; + gr_complex *d_residbuf; + + SpectrumGUIClass *d_main_gui; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(const gr_complex *data_in, int size, gr_complex *data_out); + +public: + ~qtgui_sink_c(); + void start_app(); + void lock(); + void unlock(); + + 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/src/lib/qtgui_sink_f.cc b/gr-qtgui/src/lib/qtgui_sink_f.cc new file mode 100644 index 000000000..b27718bd7 --- /dev/null +++ b/gr-qtgui/src/lib/qtgui_sink_f.cc @@ -0,0 +1,175 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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, const std::vector<float> &window, + float fmin, float fmax, const std::string &name) +{ + return qtgui_sink_f_sptr (new qtgui_sink_f (fftsize, window, fmin, fmax, name)); +} + +qtgui_sink_f::qtgui_sink_f (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name) + : gr_block ("sink_f", + gr_make_io_signature (1, 1, sizeof(float)), + gr_make_io_signature (0, 0, 0)), + d_fftsize(fftsize), d_window(window), + d_fmin(fmin), d_fmax(fmax), d_name(name) +{ + d_main_gui = NULL; + pthread_mutex_init(&d_pmutex, NULL); + lock(); + + d_shift = true; // Perform fftshift operation; this is usually desired when plotting + + d_fft = new gri_fft_complex (d_fftsize, true); + + fftdata = new gr_complex[d_fftsize]; + + d_index = 0; + d_residbuf = new float[d_fftsize]; +} + +qtgui_sink_f::~qtgui_sink_f() +{ + delete [] fftdata; + delete [] d_residbuf; + delete d_main_gui; + delete d_fft; +} + +void qtgui_sink_f::lock() +{ + pthread_mutex_lock(&d_pmutex); +} + +void qtgui_sink_f::unlock() +{ + pthread_mutex_unlock(&d_pmutex); +} + +void +qtgui_sink_f::start_app() +{ + d_qApplication = new QApplication(0, NULL); + + uint64_t maxBufferSize = 32768; + d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize, d_fmin, d_fmax); + d_main_gui->SetDisplayTitle(d_name); + d_main_gui->OpenSpectrumWindow(NULL); + + qtgui_obj object(d_qApplication); + qApp->postEvent(&object, new qtgui_event(&d_pmutex)); + + d_qApplication->exec(); +} + + +void +qtgui_sink_f::fft(const float *data_in, int size, gr_complex *data_out) +{ + 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 (unsigned int i = 0; i < size; i++) // float to complex conversion + dst[i] = data_in[i]; + } + + d_fft->execute (); // compute the fft + + for(int i=0; i < size; i++) { + d_fft->get_outbuf()[i] /= size; + } + + // copy result to our output + if(d_shift) { // apply a fft shift on the data + unsigned int len = (unsigned int)(ceil(size/2.0)); + memcpy(&data_out[0], &d_fft->get_outbuf()[len], sizeof(gr_complex)*(size - len)); + memcpy(&data_out[size - len], &d_fft->get_outbuf()[0], sizeof(gr_complex)*len); + } + else { + memcpy(data_out, d_fft->get_outbuf(), sizeof(gr_complex)*size); + } +} + + +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 i=0, j=0; + const float *in = (const float*)input_items[0]; + + pthread_mutex_lock(&d_pmutex); + + if(d_index) { + int filler = std::min(d_fftsize - d_index, noutput_items); + memcpy(&d_residbuf[d_index], &in[0], sizeof(float)*filler); + d_index += filler; + i = filler; + j = filler; + } + + if(d_index == d_fftsize) { + d_index = 0; + fft(d_residbuf, d_fftsize, fftdata); + + d_main_gui->UpdateWindow(true, fftdata, d_fftsize, d_residbuf, d_fftsize, NULL, 0, + 1.0/4.0, convert_to_timespec(0.0), true); + } + + for(; i < noutput_items; i+=d_fftsize) { + if(noutput_items - i > d_fftsize) { + j += d_fftsize; + fft(&in[i], d_fftsize, fftdata); + + d_main_gui->UpdateWindow(true, fftdata, d_fftsize, &in[i], d_fftsize, NULL, 0, + 1.0/4.0, convert_to_timespec(0.0), true); + } + } + + if(noutput_items > j) { + d_index = noutput_items - j; + memcpy(d_residbuf, &in[j], sizeof(float)*d_index); + } + + pthread_mutex_unlock(&d_pmutex); + + consume_each(noutput_items); + return noutput_items; +} diff --git a/gr-qtgui/src/lib/qtgui_sink_f.h b/gr-qtgui/src/lib/qtgui_sink_f.h new file mode 100644 index 000000000..3f0e785c6 --- /dev/null +++ b/gr-qtgui/src/lib/qtgui_sink_f.h @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 <gr_block.h> +#include <gri_fft.h> +#include <qapplication.h> +#include <qtgui.h> +#include "SpectrumGUIClass.h" + + +class qtgui_sink_f; +typedef boost::shared_ptr<qtgui_sink_f> qtgui_sink_f_sptr; + +qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, const std::vector<float> &window, + float fmin=-0.5, float fmax=0.5, const std::string &name="Display"); + +class qtgui_sink_f : public gr_block +{ +private: + friend qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name); + qtgui_sink_f (int fftsize, const std::vector<float> &window, + float fmin, float fmax, const std::string &name); + + int d_fftsize; + std::vector<float> d_window; + float d_fmin; + float d_fmax; + std::string d_name; + + pthread_mutex_t d_pmutex; + + bool d_shift; + gri_fft_complex *d_fft; + gr_complex *fftdata; + + int d_index; + float *d_residbuf; + + SpectrumGUIClass *d_main_gui; + + void fft(const float *data_in, int size, gr_complex *data_out); + +public: + ~qtgui_sink_f(); + void start_app(); + void lock(); + void unlock(); + + 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/src/lib/spectrumUpdateEvents.cc b/gr-qtgui/src/lib/spectrumUpdateEvents.cc new file mode 100644 index 000000000..f705e0478 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumUpdateEvents.cc @@ -0,0 +1,131 @@ +#ifndef SPECTRUM_UPDATE_EVENTS_C +#define SPECTRUM_UPDATE_EVENTS_C + +#include <spectrumUpdateEvents.h> + +SpectrumUpdateEvent::SpectrumUpdateEvent(const std::complex<float>* fftPoints, const uint64_t numFFTDataPoints, const double* realTimeDomainPoints, const double* imagTimeDomainPoints, const uint64_t numTimeDomainDataPoints, const double timePerFFT, const timespec dataTimestamp, const bool repeatDataFlag, const bool lastOfMultipleUpdateFlag, const timespec generatedTimestamp, const int droppedFFTFrames):QEvent(QEvent::Type(10005)){ + + _numFFTDataPoints = numFFTDataPoints; + if(_numFFTDataPoints < 1){ + _numFFTDataPoints = 1; + } + + _numTimeDomainDataPoints = numTimeDomainDataPoints; + if(_numTimeDomainDataPoints < 1){ + _numTimeDomainDataPoints = 1; + } + + _fftPoints = new std::complex<float>[_numFFTDataPoints]; + _fftPoints[0] = std::complex<float>(0,0); + memcpy(_fftPoints, fftPoints, numFFTDataPoints*sizeof(std::complex<float>)); + + _realDataTimeDomainPoints = new double[_numTimeDomainDataPoints]; + memset(_realDataTimeDomainPoints, 0x0, _numTimeDomainDataPoints*sizeof(double)); + if(numTimeDomainDataPoints > 0){ + memcpy(_realDataTimeDomainPoints, realTimeDomainPoints, numTimeDomainDataPoints*sizeof(double)); + } + + _imagDataTimeDomainPoints = new double[_numTimeDomainDataPoints]; + memset(_imagDataTimeDomainPoints, 0x0, _numTimeDomainDataPoints*sizeof(double)); + if(numTimeDomainDataPoints > 0){ + memcpy(_imagDataTimeDomainPoints, imagTimeDomainPoints, numTimeDomainDataPoints*sizeof(double)); + } + _dataTimestamp = dataTimestamp; + _timePerFFT = timePerFFT; + _repeatDataFlag = repeatDataFlag; + _lastOfMultipleUpdateFlag = lastOfMultipleUpdateFlag; + _eventGeneratedTimestamp = generatedTimestamp; + _droppedFFTFrames = droppedFFTFrames; +} + +SpectrumUpdateEvent::~SpectrumUpdateEvent(){ + delete[] _fftPoints; + delete[] _realDataTimeDomainPoints; + delete[] _imagDataTimeDomainPoints; +} + +const std::complex<float>* SpectrumUpdateEvent::getFFTPoints()const{ + return _fftPoints; +} + +const double* SpectrumUpdateEvent::getRealTimeDomainPoints()const{ + return _realDataTimeDomainPoints; +} + +const double* SpectrumUpdateEvent::getImagTimeDomainPoints()const{ + return _imagDataTimeDomainPoints; +} + +uint64_t SpectrumUpdateEvent::getNumFFTDataPoints()const{ + return _numFFTDataPoints; +} + +uint64_t SpectrumUpdateEvent::getNumTimeDomainDataPoints()const{ + return _numTimeDomainDataPoints; +} + +double SpectrumUpdateEvent::getTimePerFFT()const{ + return _timePerFFT; +} + +timespec SpectrumUpdateEvent::getDataTimestamp()const{ + return _dataTimestamp; +} + +bool SpectrumUpdateEvent::getRepeatDataFlag()const{ + return _repeatDataFlag; +} + +bool SpectrumUpdateEvent::getLastOfMultipleUpdateFlag()const{ + return _lastOfMultipleUpdateFlag; +} + +timespec SpectrumUpdateEvent::getEventGeneratedTimestamp()const{ + return _eventGeneratedTimestamp; +} + +int SpectrumUpdateEvent::getDroppedFFTFrames()const{ + return _droppedFFTFrames; +} + +SpectrumWindowCaptionEvent::SpectrumWindowCaptionEvent(const QString& newLbl):QEvent(QEvent::Type(10008)){ + _labelString = newLbl; +} + +SpectrumWindowCaptionEvent::~SpectrumWindowCaptionEvent(){ +} + +QString SpectrumWindowCaptionEvent::getLabel(){ + return _labelString; +} + +SpectrumWindowResetEvent::SpectrumWindowResetEvent():QEvent(QEvent::Type(10009)){ +} + +SpectrumWindowResetEvent::~SpectrumWindowResetEvent(){ + +} + + SpectrumFrequencyRangeEvent::SpectrumFrequencyRangeEvent(const double centerFreq, const double startFreq, const double stopFreq):QEvent(QEvent::Type(10010)){ + _centerFrequency = centerFreq; + _startFrequency = startFreq; + _stopFrequency = stopFreq; +} + +SpectrumFrequencyRangeEvent::~SpectrumFrequencyRangeEvent(){ + +} + +double SpectrumFrequencyRangeEvent::GetCenterFrequency()const{ + return _centerFrequency; +} + +double SpectrumFrequencyRangeEvent::GetStartFrequency()const{ + return _startFrequency; +} + +double SpectrumFrequencyRangeEvent::GetStopFrequency()const{ + return _stopFrequency; +} + +#endif /* SPECTRUM_UPDATE_EVENTS_C */ diff --git a/gr-qtgui/src/lib/spectrumUpdateEvents.h b/gr-qtgui/src/lib/spectrumUpdateEvents.h new file mode 100644 index 000000000..a758d884c --- /dev/null +++ b/gr-qtgui/src/lib/spectrumUpdateEvents.h @@ -0,0 +1,82 @@ +#ifndef SPECTRUM_UPDATE_EVENTS_H +#define SPECTRUM_UPDATE_EVENTS_H + +#include <QEvent> +#include <QString> +#include <complex> +#include <highResTimeFunctions.h> + +class SpectrumUpdateEvent:public QEvent{ + +public: + SpectrumUpdateEvent(const std::complex<float>* fftPoints, const uint64_t numFFTDataPoints, const double* realTimeDomainPoints, const double* imagTimeDomainPoints, const uint64_t numTimeDomainDataPoints, const double timePerFFT, const timespec dataTimestamp, const bool repeatDataFlag, const bool lastOfMultipleUpdateFlag, const timespec generatedTimestamp, const int droppedFFTFrames); + ~SpectrumUpdateEvent(); + const std::complex<float>* getFFTPoints()const; + const double* getRealTimeDomainPoints()const; + const double* getImagTimeDomainPoints()const; + uint64_t getNumFFTDataPoints()const; + uint64_t getNumTimeDomainDataPoints()const; + double getTimePerFFT()const; + timespec getDataTimestamp()const; + bool getRepeatDataFlag()const; + bool getLastOfMultipleUpdateFlag()const; + timespec getEventGeneratedTimestamp()const; + int getDroppedFFTFrames()const; + +protected: + +private: + std::complex<float>* _fftPoints; + double* _realDataTimeDomainPoints; + double* _imagDataTimeDomainPoints; + uint64_t _numFFTDataPoints; + uint64_t _numTimeDomainDataPoints; + double _timePerFFT; + timespec _dataTimestamp; + bool _repeatDataFlag; + bool _lastOfMultipleUpdateFlag; + timespec _eventGeneratedTimestamp; + int _droppedFFTFrames; +}; + +class SpectrumWindowCaptionEvent:public QEvent{ +public: + SpectrumWindowCaptionEvent(const QString&); + ~SpectrumWindowCaptionEvent(); + QString getLabel(); + +protected: + +private: + QString _labelString; +}; + +class SpectrumWindowResetEvent:public QEvent{ +public: + SpectrumWindowResetEvent(); + ~SpectrumWindowResetEvent(); + +protected: + +private: + +}; + +class SpectrumFrequencyRangeEvent:public QEvent{ +public: + SpectrumFrequencyRangeEvent(const double, const double, const double); + ~SpectrumFrequencyRangeEvent(); + double GetCenterFrequency()const; + double GetStartFrequency()const; + double GetStopFrequency()const; + +protected: + +private: + double _centerFrequency; + double _startFrequency; + double _stopFrequency; +}; + + +#endif /* SPECTRUM_UPDATE_EVENTS_H */ diff --git a/gr-qtgui/src/lib/spectrumdisplayform.cc b/gr-qtgui/src/lib/spectrumdisplayform.cc new file mode 100644 index 000000000..481a55264 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumdisplayform.cc @@ -0,0 +1,619 @@ +#include <cmath> +#include <QColorDialog> +#include <QMessageBox> +#include <spectrumdisplayform.h> + +int SpectrumDisplayForm::_openGLWaterfall3DFlag = -1; + +SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) : QDialog(parent){ + setupUi(this); + + _systemSpecifiedFlag = false; + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + _frequencyDisplayPlot = new FrequencyDisplayPlot(Tab1PlotDisplayFrame); + _waterfallDisplayPlot = new WaterfallDisplayPlot(Tab2PlotDisplayFrame); + _waterfall3DDisplayPlot = new Waterfall3DDisplayPlot(Waterfall3DPlotDisplayFrame); + _timeDomainDisplayPlot = new TimeDomainDisplayPlot(TimeDomainDisplayFrame); + _numRealDataPoints = 1024; + _realFFTDataPoints = new double[_numRealDataPoints]; + _averagedValues = new double[_numRealDataPoints]; + _historyVector = new std::vector<double*>; + + AvgLineEdit->setValidator(_intValidator); + PowerLineEdit->setValidator(_intValidator); + MinHoldCheckBox_toggled( false ); + MaxHoldCheckBox_toggled( false ); + + WaterfallMaximumIntensityWheel->setRange(-200, 0); + WaterfallMaximumIntensityWheel->setTickCnt(50); + WaterfallMinimumIntensityWheel->setRange(-200, 0); + WaterfallMinimumIntensityWheel->setTickCnt(50); + WaterfallMinimumIntensityWheel->setValue(-200); + + Waterfall3DMaximumIntensityWheel->setRange(-200, 0); + Waterfall3DMaximumIntensityWheel->setTickCnt(50); + Waterfall3DMinimumIntensityWheel->setRange(-200, 0); + Waterfall3DMinimumIntensityWheel->setTickCnt(50); + Waterfall3DMinimumIntensityWheel->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))); + + _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(); +} + +SpectrumDisplayForm::~SpectrumDisplayForm(){ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + /* delete _intValidator; */ + + delete[] _realFFTDataPoints; + delete[] _averagedValues; + + for(unsigned int count = 0; count < _historyVector->size(); count++){ + delete[] _historyVector->operator[](count); + } + + delete _historyVector; +} + +void SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem, const uint64_t numFFTDataPoints, const uint64_t numTimeDomainDataPoints ) +{ + ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); + + if(newSystem != NULL){ + _system = newSystem; + _systemSpecifiedFlag = true; + } + else{ + _systemSpecifiedFlag = false; + } +} + +void SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdateEvent) +{ + const std::complex<float>* complexDataPoints = spectrumUpdateEvent->getFFTPoints(); + const uint64_t numFFTDataPoints = spectrumUpdateEvent->getNumFFTDataPoints(); + const double* realTimeDomainDataPoints = spectrumUpdateEvent->getRealTimeDomainPoints(); + const double* imagTimeDomainDataPoints = spectrumUpdateEvent->getImagTimeDomainPoints(); + const uint64_t numTimeDomainDataPoints = spectrumUpdateEvent->getNumTimeDomainDataPoints(); + const double timePerFFT = spectrumUpdateEvent->getTimePerFFT(); + const timespec dataTimestamp = spectrumUpdateEvent->getDataTimestamp();; + const bool repeatDataFlag = spectrumUpdateEvent->getRepeatDataFlag(); + const bool lastOfMultipleUpdatesFlag = spectrumUpdateEvent->getLastOfMultipleUpdateFlag(); + const timespec generatedTimestamp = spectrumUpdateEvent->getEventGeneratedTimestamp(); + + // REMEMBER: The dataTimestamp is NOT valid when the repeat data flag is true... + ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints); + + // Calculate the Magnitude of the complex point + const std::complex<float>* complexDataPointsPtr = complexDataPoints; + double* realFFTDataPointsPtr = _realFFTDataPoints; + for(uint64_t point = 0; point < numFFTDataPoints; point++){ + // Calculate dBm + // 50 ohm load assumption + // 10 * log10 (v^2 / (2 * 50.0 * .001)) = 10 * log10( v^2 * 10) + // 75 ohm load assumption + // 10 * log10 (v^2 / (2 * 75.0 * .001)) = 10 * log10( v^2 * 15) + + *realFFTDataPointsPtr = 10.0*log10((((*complexDataPointsPtr).real() * (*complexDataPointsPtr).real()) + ((*complexDataPointsPtr).imag()*(*complexDataPointsPtr).imag())) + 1e-20); + + complexDataPointsPtr++; + realFFTDataPointsPtr++; + } + + + // Don't update the averaging history if this is repeated data + if(!repeatDataFlag){ + _AverageHistory(_realFFTDataPoints); + + double sumMean; + const double fft_bin_size = (_stopFrequency-_startFrequency) / static_cast<double>(numFFTDataPoints); + + // find the peak, sum (for mean), etc + _peakAmplitude = -HUGE_VAL; + sumMean = 0.0; + for(uint64_t number = 0; number < numFFTDataPoints; number++){ + // find peak + if(_realFFTDataPoints[number] > _peakAmplitude){ + _peakFrequency = (static_cast<float>(number) * fft_bin_size); // Calculate the frequency relative to the local bw, adjust for _startFrequency later + _peakAmplitude = _realFFTDataPoints[number]; + // _peakBin = number; + } + // sum (for mean) + sumMean += _realFFTDataPoints[number]; + } + + // 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; + + // now throw out any bins higher than the mean + sumMean = 0.0; + uint64_t newNumDataPoints = numFFTDataPoints; + for(uint64_t number = 0; number < numFFTDataPoints; number++){ + if (_realFFTDataPoints[number] <= meanAmplitude) + sumMean += _realFFTDataPoints[number]; + else + newNumDataPoints--; + } + + if (newNumDataPoints == 0) // in the odd case that all + _noiseFloorAmplitude = meanAmplitude; // amplitudes are equal! + else + _noiseFloorAmplitude = sumMean / newNumDataPoints; + } + + if(lastOfMultipleUpdatesFlag){ + _frequencyDisplayPlot->PlotNewData(_averagedValues, numFFTDataPoints, _noiseFloorAmplitude, _peakFrequency, _peakAmplitude); + _timeDomainDisplayPlot->PlotNewData(realTimeDomainDataPoints, imagTimeDomainDataPoints, numTimeDomainDataPoints); + } + // Don't update the repeated data for the waterfall + if(!repeatDataFlag){ + _waterfallDisplayPlot->PlotNewData(_realFFTDataPoints, numFFTDataPoints, timePerFFT, dataTimestamp, spectrumUpdateEvent->getDroppedFFTFrames()); + if( _openGLWaterfall3DFlag == 1 ){ + _waterfall3DDisplayPlot->PlotNewData(_realFFTDataPoints, numFFTDataPoints, timePerFFT, dataTimestamp, spectrumUpdateEvent->getDroppedFFTFrames()); + } + } + + // Tell the system the GUI has been updated + if(_systemSpecifiedFlag){ + _system->SetLastGUIUpdateTime(generatedTimestamp); + _system->DecrementPendingGUIUpdateEvents(); + } +} + +void SpectrumDisplayForm::resizeEvent( QResizeEvent *e ) +{ + // Let the actual window resize its width, but not its height + QSize newSize(e->size().width(), e->oldSize().height()); + QResizeEvent et(newSize, e->oldSize()); + QWidget::resizeEvent(&et); + + // Tell the Tab Window to Resize + SpectrumTypeTab->resize( e->size().width(), SpectrumTypeTab->height()); + + // Tell the TabXFreqDisplay to resize + Tab1PlotDisplayFrame->resize(e->size().width()-4, Tab1PlotDisplayFrame->height()); + Tab2PlotDisplayFrame->resize(e->size().width()-4, Tab2PlotDisplayFrame->height()); + Waterfall3DPlotDisplayFrame->resize(e->size().width()-4, Waterfall3DPlotDisplayFrame->height()); + TimeDomainDisplayFrame->resize(e->size().width()-4, TimeDomainDisplayFrame->height()); + _frequencyDisplayPlot->resize( Tab1PlotDisplayFrame->width()-4, Tab1PlotDisplayFrame->height()); + _waterfallDisplayPlot->resize( Tab2PlotDisplayFrame->width()-4, Tab2PlotDisplayFrame->height()); + _waterfall3DDisplayPlot->resize( Waterfall3DPlotDisplayFrame->width()-4, Waterfall3DPlotDisplayFrame->height()); + _timeDomainDisplayPlot->resize( TimeDomainDisplayFrame->width()-4, TimeDomainDisplayFrame->height()); + + // Move the IntensityWheels and Labels + WaterfallMaximumIntensityLabel->move(width() - 5 - WaterfallMaximumIntensityLabel->width(), WaterfallMaximumIntensityLabel->y()); + WaterfallMinimumIntensityLabel->move(width() - 5 - WaterfallMinimumIntensityLabel->width(), WaterfallMinimumIntensityLabel->y()); + WaterfallMaximumIntensityWheel->resize(WaterfallMaximumIntensityLabel->x() - 5 - WaterfallMaximumIntensityWheel->x(), WaterfallMaximumIntensityWheel->height()); + WaterfallMinimumIntensityWheel->resize(WaterfallMinimumIntensityLabel->x() - 5 - WaterfallMinimumIntensityWheel->x(), WaterfallMinimumIntensityWheel->height()); + + Waterfall3DMaximumIntensityLabel->move(width() - 5 - Waterfall3DMaximumIntensityLabel->width(), Waterfall3DMaximumIntensityLabel->y()); + Waterfall3DMinimumIntensityLabel->move(width() - 5 - Waterfall3DMinimumIntensityLabel->width(), Waterfall3DMinimumIntensityLabel->y()); + Waterfall3DMaximumIntensityWheel->resize(Waterfall3DMaximumIntensityLabel->x() - 5 - Waterfall3DMaximumIntensityWheel->x(), Waterfall3DMaximumIntensityWheel->height()); + Waterfall3DMinimumIntensityWheel->resize(Waterfall3DMinimumIntensityLabel->x() - 5 - Waterfall3DMinimumIntensityWheel->x(), Waterfall3DMinimumIntensityWheel->height()); + + + // Move the Power Lbl + PowerLabel->move(e->size().width()-(415-324) - PowerLabel->width(), PowerLabel->y()); + + // Move the Power Line Edit + PowerLineEdit->move(e->size().width()-(415-318) - PowerLineEdit->width(), PowerLineEdit->y()); + + // Move the Avg Lbl + AvgLabel->move(e->size().width()-(415-406) - AvgLabel->width(), AvgLabel->y()); + + // Move the Avg Line Edit + AvgLineEdit->move(e->size().width()-(415-400) - AvgLineEdit->width(), AvgLineEdit->y()); + + // Move the FFT Size Combobox and label + FFTSizeComboBox->move(width() - 5 - FFTSizeComboBox->width(), FFTSizeComboBox->y()); + FFTSizeLabel->move(width() - 10 - FFTSizeComboBox->width() - FFTSizeLabel->width(), FFTSizeLabel->y()); +} + + +void SpectrumDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == QEvent::User+3){ + if(_systemSpecifiedFlag){ + WindowComboBox->setCurrentIndex(_system->GetWindowType()); + FFTSizeComboBox->setCurrentIndex(_system->GetFFTSizeIndex()); + //FFTSizeComboBox->setCurrentIndex(1); + PowerLineEdit_textChanged(PowerLineEdit->text()); + } + + waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensityWheel->value()); + waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensityWheel->value()); + + waterfall3DMinimumIntensityChangedCB(Waterfall3DMinimumIntensityWheel->value()); + waterfall3DMaximumIntensityChangedCB(Waterfall3DMaximumIntensityWheel->value()); + + // If the video card doesn't support OpenGL then don't display the 3D Waterfall + if(QGLFormat::hasOpenGL()){ + // Check for Hardware Acceleration of the OpenGL + if(!_waterfall3DDisplayPlot->format().directRendering()){ + // Only ask this once while the program is running... + if(_openGLWaterfall3DFlag == -1){ + _openGLWaterfall3DFlag = 0; + if(QMessageBox::warning(this, "OpenGL Direct Rendering NOT Supported", "<center>The system's video card hardware or current drivers do not support direct hardware rendering of the OpenGL modules.</center><br><center>Software rendering is VERY processor intensive.</center><br><center>Do you want to use software rendering?</center>", QMessageBox::Yes, QMessageBox::No | QMessageBox::Default | QMessageBox::Escape) == QMessageBox::Yes){ + _openGLWaterfall3DFlag = 1; + } + } + } + else{ + _openGLWaterfall3DFlag = 1; + } + } + + if(_openGLWaterfall3DFlag != 1){ + SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(Waterfall3DPage)); + } + + // Clear any previous display + Reset(); + + show(); + } + else if(e->type() == 10005){ + SpectrumUpdateEvent* spectrumUpdateEvent = (SpectrumUpdateEvent*)e; + newFrequencyData(spectrumUpdateEvent); + } + else if(e->type() == 10008){ + setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel()); + } + else if(e->type() == 10009){ + Reset(); + if(_systemSpecifiedFlag){ + _system->ResetPendingGUIUpdateEvents(); + } + } + else if(e->type() == 10010){ + _startFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStartFrequency(); + _stopFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStopFrequency(); + _centerFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetCenterFrequency(); + + UseRFFrequenciesCB(UseRFFrequenciesCheckBox->isChecked()); + } +} + +void SpectrumDisplayForm::AvgLineEdit_textChanged( const QString &valueString ) +{ + if(!valueString.isEmpty()){ + int value = valueString.toInt(); + if(value > 500){ + value = 500; + AvgLineEdit->setText("500"); + } + SetAverageCount(value); + } +} + + +void SpectrumDisplayForm::MaxHoldCheckBox_toggled( bool newState ) +{ + MaxHoldResetBtn->setEnabled(newState); + _frequencyDisplayPlot->SetMaxFFTVisible(newState); + MaxHoldResetBtn_clicked(); +} + + +void SpectrumDisplayForm::MinHoldCheckBox_toggled( bool newState ) +{ + MinHoldResetBtn->setEnabled(newState); + _frequencyDisplayPlot->SetMinFFTVisible(newState); + MinHoldResetBtn_clicked(); +} + + +void SpectrumDisplayForm::MinHoldResetBtn_clicked() +{ + _frequencyDisplayPlot->ClearMinData(); + _frequencyDisplayPlot->replot(); +} + + +void SpectrumDisplayForm::MaxHoldResetBtn_clicked() +{ + _frequencyDisplayPlot->ClearMaxData(); + _frequencyDisplayPlot->replot(); +} + + +void SpectrumDisplayForm::PowerLineEdit_textChanged( const QString &valueString ) +{ + if(_systemSpecifiedFlag){ + if(!valueString.isEmpty()){ + double value = valueString.toDouble(); + if(value < 1.0){ + value = 1.0; + PowerLineEdit->setText("1"); + } + _system->SetPowerValue(value); + } + + if(_system->GetPowerValue() > 1){ + UseRFFrequenciesCheckBox->setChecked(false); + UseRFFrequenciesCheckBox->setEnabled(false); + UseRFFrequenciesCB(false); + } + else{ + UseRFFrequenciesCheckBox->setEnabled(true); + } + } +} + +void SpectrumDisplayForm::SetFrequencyRange(const double newStartFrequency, const double newStopFrequency, const double newCenterFrequency){ + _frequencyDisplayPlot->SetFrequencyRange(newStartFrequency, newStopFrequency, newCenterFrequency, UseRFFrequenciesCheckBox->isChecked()); + _waterfallDisplayPlot->SetFrequencyRange(newStartFrequency, newStopFrequency, newCenterFrequency, UseRFFrequenciesCheckBox->isChecked()); + _waterfall3DDisplayPlot->SetFrequencyRange(newStartFrequency, newStopFrequency, newCenterFrequency, UseRFFrequenciesCheckBox->isChecked()); + +} + +int SpectrumDisplayForm::GetAverageCount(){ + return _historyVector->size(); +} + +void SpectrumDisplayForm::SetAverageCount(const int newCount){ + if(newCount > -1){ + if(newCount != static_cast<int>(_historyVector->size())){ + std::vector<double*>::iterator pos; + while(newCount < static_cast<int>(_historyVector->size())){ + pos = _historyVector->begin(); + delete[] (*pos); + _historyVector->erase(pos); + } + + while(newCount > static_cast<int>(_historyVector->size())){ + _historyVector->push_back(new double[_numRealDataPoints]); + } + AverageDataReset(); + } + } +} + +void SpectrumDisplayForm::_AverageHistory(const double* newBuffer){ + 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())){ + _historyEntryCount = _historyVector->size(); + } + _historyEntry = (++_historyEntry)%_historyVector->size(); + + // Total up and then average the values + double sum; + for(uint64_t location = 0; location < _numRealDataPoints; location++){ + sum = 0; + for(int number = 0; number < _historyEntryCount; number++){ + sum += _historyVector->operator[](number)[location]; + } + _averagedValues[location] = sum/static_cast<double>(_historyEntryCount); + } + } + else{ + memcpy(_averagedValues, newBuffer, _numRealDataPoints*sizeof(double)); + } + } +} + +void SpectrumDisplayForm::ResizeBuffers( const uint64_t numFFTDataPoints, const uint64_t /*numTimeDomainDataPoints*/ ){ + // Convert from Complex to Real for certain Displays + if(_numRealDataPoints != numFFTDataPoints){ + _numRealDataPoints = numFFTDataPoints; + delete[] _realFFTDataPoints; + delete[] _averagedValues; + + _realFFTDataPoints = new double[_numRealDataPoints]; + _averagedValues = new double[_numRealDataPoints]; + memset(_realFFTDataPoints, 0x0, _numRealDataPoints*sizeof(double)); + + const int historySize = _historyVector->size(); + SetAverageCount(0); // Clear the existing history + SetAverageCount(historySize); + + Reset(); + } +} + +void SpectrumDisplayForm::Reset(){ + AverageDataReset(); + + _waterfallDisplayPlot->Reset(); + _waterfall3DDisplayPlot->Reset(); +} + + +void SpectrumDisplayForm::AverageDataReset(){ + _historyEntry = 0; + _historyEntryCount = 0; + + memset(_averagedValues, 0x0, _numRealDataPoints*sizeof(double)); + + MaxHoldResetBtn_clicked(); + MinHoldResetBtn_clicked(); +} + + +void SpectrumDisplayForm::closeEvent( QCloseEvent *e ) +{ + if(_systemSpecifiedFlag){ + _system->SetWindowOpenFlag(false); + } + + qApp->processEvents(); + + QWidget::closeEvent(e); +} + + +void SpectrumDisplayForm::WindowTypeChanged( int newItem ) +{ + if(_systemSpecifiedFlag){ + _system->SetWindowType(newItem); + } +} + + +void SpectrumDisplayForm::UseRFFrequenciesCB( bool useRFFlag ) +{ + if(useRFFlag){ + SetFrequencyRange(_startFrequency, _stopFrequency, _centerFrequency); + } + else{ + SetFrequencyRange(_startFrequency, _stopFrequency, 0.0 ); + } +} + + +void SpectrumDisplayForm::waterfallMaximumIntensityChangedCB( double newValue ) +{ + if(newValue > WaterfallMinimumIntensityWheel->value()){ + WaterfallMaximumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); + } + else{ + WaterfallMaximumIntensityWheel->setValue(WaterfallMinimumIntensityWheel->value()); + } + _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(), WaterfallMaximumIntensityWheel->value()); +} + + +void SpectrumDisplayForm::waterfallMinimumIntensityChangedCB( double newValue ) +{ + if(newValue < WaterfallMaximumIntensityWheel->value()){ + WaterfallMinimumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); + } + else{ + WaterfallMinimumIntensityWheel->setValue(WaterfallMaximumIntensityWheel->value()); + } + _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(), WaterfallMaximumIntensityWheel->value()); +} + +void SpectrumDisplayForm::waterfall3DMaximumIntensityChangedCB( double newValue ) +{ + if(newValue > Waterfall3DMinimumIntensityWheel->value()){ + Waterfall3DMaximumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); + } + else{ + Waterfall3DMaximumIntensityWheel->setValue(Waterfall3DMinimumIntensityWheel->value()); + } + _waterfall3DDisplayPlot->SetIntensityRange(Waterfall3DMinimumIntensityWheel->value(), Waterfall3DMaximumIntensityWheel->value()); +} + + +void SpectrumDisplayForm::waterfall3DMinimumIntensityChangedCB( double newValue ) +{ + if(newValue < Waterfall3DMaximumIntensityWheel->value()){ + Waterfall3DMinimumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0)); + } + else{ + Waterfall3DMinimumIntensityWheel->setValue(Waterfall3DMaximumIntensityWheel->value()); + } + _waterfall3DDisplayPlot->SetIntensityRange(Waterfall3DMinimumIntensityWheel->value(), Waterfall3DMaximumIntensityWheel->value()); +} + + +void SpectrumDisplayForm::FFTComboBoxSelectedCB( const QString &fftSizeString ) +{ + if(_systemSpecifiedFlag){ + _system->SetFFTSize(fftSizeString.toLong()); + } +} + + +void SpectrumDisplayForm::WaterfallAutoScaleBtnCB() +{ + double minimumIntensity = _noiseFloorAmplitude - 5; + if(minimumIntensity < WaterfallMinimumIntensityWheel->minValue()){ + minimumIntensity = WaterfallMinimumIntensityWheel->minValue(); + } + WaterfallMinimumIntensityWheel->setValue(minimumIntensity); + double maximumIntensity = _peakAmplitude + 10; + if(maximumIntensity > WaterfallMaximumIntensityWheel->maxValue()){ + maximumIntensity = WaterfallMaximumIntensityWheel->maxValue(); + } + WaterfallMaximumIntensityWheel->setValue(maximumIntensity); + waterfallMaximumIntensityChangedCB(maximumIntensity); +} + +void SpectrumDisplayForm::Waterfall3DAutoScaleBtnCB() +{ + double minimumIntensity = _noiseFloorAmplitude - 5; + if(minimumIntensity < Waterfall3DMinimumIntensityWheel->minValue()){ + minimumIntensity = Waterfall3DMinimumIntensityWheel->minValue(); + } + Waterfall3DMinimumIntensityWheel->setValue(minimumIntensity); + double maximumIntensity = _peakAmplitude + 10; + if(maximumIntensity > Waterfall3DMaximumIntensityWheel->maxValue()){ + maximumIntensity = Waterfall3DMaximumIntensityWheel->maxValue(); + } + Waterfall3DMaximumIntensityWheel->setValue(maximumIntensity); + waterfallMaximumIntensityChangedCB(maximumIntensity); +} + +void SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType ) +{ + QColor lowIntensityColor; + QColor highIntensityColor; + if(newType == WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED){ + // Select the Low Intensity Color + lowIntensityColor = _waterfallDisplayPlot->GetUserDefinedLowIntensityColor(); + if(!lowIntensityColor.isValid()){ + lowIntensityColor = Qt::black; + } + QMessageBox::information(this, "Low Intensity Color Selection", "In the next window, select the low intensity color for the waterfall display", QMessageBox::Ok); + lowIntensityColor = QColorDialog::getColor(lowIntensityColor, this); + + // Select the High Intensity Color + highIntensityColor = _waterfallDisplayPlot->GetUserDefinedHighIntensityColor(); + if(!highIntensityColor.isValid()){ + highIntensityColor = Qt::white; + } + QMessageBox::information(this, "High Intensity Color Selection", "In the next window, select the high intensity color for the waterfall display", QMessageBox::Ok); + highIntensityColor = QColorDialog::getColor(highIntensityColor, this); + } + _waterfallDisplayPlot->SetIntensityColorMapType(newType, lowIntensityColor, highIntensityColor); +} + +void SpectrumDisplayForm::Waterfall3DIntensityColorTypeChanged( int newType ) +{ + QColor lowIntensityColor; + QColor highIntensityColor; + if(newType == Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED){ + // Select the Low Intensity Color + lowIntensityColor = _waterfallDisplayPlot->GetUserDefinedLowIntensityColor(); + if(!lowIntensityColor.isValid()){ + lowIntensityColor = Qt::black; + } + QMessageBox::information(this, "Low Intensity Color Selection", "In the next window, select the low intensity color for the waterfall display", QMessageBox::Ok); + lowIntensityColor = QColorDialog::getColor(lowIntensityColor, this); + + // Select the High Intensity Color + highIntensityColor = _waterfallDisplayPlot->GetUserDefinedHighIntensityColor(); + if(!highIntensityColor.isValid()){ + highIntensityColor = Qt::white; + } + QMessageBox::information(this, "High Intensity Color Selection", "In the next window, select the high intensity color for the waterfall display", QMessageBox::Ok); + highIntensityColor = QColorDialog::getColor(highIntensityColor, this); + } + _waterfall3DDisplayPlot->SetIntensityColorMapType(newType, lowIntensityColor, highIntensityColor); +} diff --git a/gr-qtgui/src/lib/spectrumdisplayform.h b/gr-qtgui/src/lib/spectrumdisplayform.h new file mode 100644 index 000000000..97b14a6c9 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumdisplayform.h @@ -0,0 +1,87 @@ +#ifndef SPECTRUM_DISPLAY_FORM_H +#define SPECTRUM_DISPLAY_FORM_H + +#include "spectrumdisplayform_ui.h" + +class SpectrumGUIClass; +#include <SpectrumGUIClass.h> + +#include <SpectrumGUIClass.h> +#include <FrequencyDisplayPlot.h> +#include <WaterfallDisplayPlot.h> +#include <Waterfall3DDisplayPlot.h> +#include <TimeDomainDisplayPlot.h> +#include <QValidator> +#include <vector> + +class SpectrumDisplayForm : public QDialog, public Ui::SpectrumDisplayForm +{ + Q_OBJECT + + public: + SpectrumDisplayForm(QWidget* parent = 0); + ~SpectrumDisplayForm(); + + 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 ); + +public slots: + void resizeEvent( QResizeEvent * e ); + void customEvent( QEvent * e ); + void AvgLineEdit_textChanged( const QString & valueString ); + void MaxHoldCheckBox_toggled( bool newState ); + void MinHoldCheckBox_toggled( bool newState ); + void MinHoldResetBtn_clicked(); + void MaxHoldResetBtn_clicked(); + void PowerLineEdit_textChanged( const QString& valueString ); + void SetFrequencyRange( const double newStartFrequency, const double newStopFrequency, const double newCenterFrequency ); + void closeEvent( QCloseEvent * e ); + void WindowTypeChanged( int newItem ); + void UseRFFrequenciesCB( bool useRFFlag ); + void waterfallMaximumIntensityChangedCB(double); + void waterfallMinimumIntensityChangedCB(double); + void WaterfallIntensityColorTypeChanged(int); + void WaterfallAutoScaleBtnCB(); + void waterfall3DMaximumIntensityChangedCB(double); + void waterfall3DMinimumIntensityChangedCB(double); + void Waterfall3DIntensityColorTypeChanged(int); + void Waterfall3DAutoScaleBtnCB(); + void FFTComboBoxSelectedCB(const QString&); + + +private slots: + void newFrequencyData( const SpectrumUpdateEvent* ); + +protected: + +private: + void _AverageHistory( const double * newBuffer ); + + int _historyEntryCount; + int _historyEntry; + std::vector<double*>* _historyVector; + double* _averagedValues; + uint64_t _numRealDataPoints; + double* _realFFTDataPoints; + QIntValidator* _intValidator; + FrequencyDisplayPlot* _frequencyDisplayPlot; + WaterfallDisplayPlot* _waterfallDisplayPlot; + Waterfall3DDisplayPlot* _waterfall3DDisplayPlot; + TimeDomainDisplayPlot* _timeDomainDisplayPlot; + SpectrumGUIClass* _system; + bool _systemSpecifiedFlag; + double _centerFrequency; + double _startFrequency; + double _noiseFloorAmplitude; + double _peakFrequency; + double _peakAmplitude; + static int _openGLWaterfall3DFlag; + double _stopFrequency; +}; + +#endif /* SPECTRUM_DISPLAY_FORM_H */ diff --git a/gr-qtgui/src/lib/spectrumdisplayform.ui b/gr-qtgui/src/lib/spectrumdisplayform.ui new file mode 100644 index 000000000..f75022f34 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumdisplayform.ui @@ -0,0 +1,986 @@ +<ui version="4.0" > + <class>SpectrumDisplayForm</class> + <widget class="QDialog" name="SpectrumDisplayForm" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>637</width> + <height>436</height> + </rect> + </property> + <property name="windowTitle" > + <string>Spectrum Display</string> + </property> + <widget class="QCheckBox" name="UseRFFrequenciesCheckBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>385</y> + <width>180</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Display RF Frequencies</string> + </property> + </widget> + <widget class="QComboBox" name="WindowComboBox" > + <property name="geometry" > + <rect> + <x>105</x> + <y>410</y> + <width>170</width> + <height>20</height> + </rect> + </property> + <property name="font" > + <font> + <pointsize>9</pointsize> + </font> + </property> + <item> + <property name="text" > + <string>Hamming</string> + </property> + </item> + <item> + <property name="text" > + <string>Hann</string> + </property> + </item> + <item> + <property name="text" > + <string>Blackman</string> + </property> + </item> + <item> + <property name="text" > + <string>Rectangular</string> + </property> + </item> + <item> + <property name="text" > + <string>Kaiser</string> + </property> + </item> + <item> + <property name="text" > + <string>Blackman-harris</string> + </property> + </item> + </widget> + <widget class="QLabel" name="WindowLbl" > + <property name="geometry" > + <rect> + <x>10</x> + <y>410</y> + <width>90</width> + <height>17</height> + </rect> + </property> + <property name="text" > + <string>Window:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" name="FFTSizeLabel" > + <property name="geometry" > + <rect> + <x>405</x> + <y>385</y> + <width>116</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>FFT Size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QComboBox" name="FFTSizeComboBox" > + <property name="geometry" > + <rect> + <x>525</x> + <y>385</y> + <width>100</width> + <height>20</height> + </rect> + </property> + <item> + <property name="text" > + <string>1024</string> + </property> + </item> + <item> + <property name="text" > + <string>2048</string> + </property> + </item> + <item> + <property name="text" > + <string>4096</string> + </property> + </item> + <item> + <property name="text" > + <string>8192</string> + </property> + </item> + <item> + <property name="text" > + <string>16384</string> + </property> + </item> + <item> + <property name="text" > + <string>32768</string> + </property> + </item> + </widget> + <widget class="QTabWidget" name="SpectrumTypeTab" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>630</width> + <height>380</height> + </rect> + </property> + <widget class="QWidget" name="FrequencyPage" > + <attribute name="title" > + <string>Frequency Display</string> + </attribute> + <widget class="QLineEdit" name="PowerLineEdit" > + <property name="geometry" > + <rect> + <x>480</x> + <y>320</y> + <width>60</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>1</string> + </property> + </widget> + <widget class="QCheckBox" name="MinHoldCheckBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>325</y> + <width>95</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Min Hold</string> + </property> + <property name="checked" > + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" name="AvgLabel" > + <property name="geometry" > + <rect> + <x>545</x> + <y>300</y> + <width>72</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Average</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QPushButton" name="MinHoldResetBtn" > + <property name="geometry" > + <rect> + <x>105</x> + <y>325</y> + <width>61</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Reset</string> + </property> + </widget> + <widget class="QLineEdit" name="AvgLineEdit" > + <property name="geometry" > + <rect> + <x>550</x> + <y>320</y> + <width>60</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>0</string> + </property> + </widget> + <widget class="QLabel" name="PowerLabel" > + <property name="geometry" > + <rect> + <x>475</x> + <y>300</y> + <width>72</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Power</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QPushButton" name="MaxHoldResetBtn" > + <property name="geometry" > + <rect> + <x>105</x> + <y>300</y> + <width>61</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Reset</string> + </property> + </widget> + <widget class="QCheckBox" name="MaxHoldCheckBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>300</y> + <width>95</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Max Hold</string> + </property> + <property name="checked" > + <bool>false</bool> + </property> + </widget> + <widget class="QFrame" name="Tab1PlotDisplayFrame" > + <property name="geometry" > + <rect> + <x>5</x> + <y>5</y> + <width>620</width> + <height>290</height> + </rect> + </property> + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + </widget> + </widget> + <widget class="QWidget" name="WaterfallPage" > + <attribute name="title" > + <string>Waterfall Display</string> + </attribute> + <widget class="QLabel" name="textLabel1" > + <property name="geometry" > + <rect> + <x>5</x> + <y>0</y> + <width>85</width> + <height>21</height> + </rect> + </property> + <property name="text" > + <string><font size="-2">Intensity Display:</font></string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QComboBox" name="WaterfallIntensityComboBox" > + <property name="geometry" > + <rect> + <x>90</x> + <y>0</y> + <width>121</width> + <height>25</height> + </rect> + </property> + <item> + <property name="text" > + <string>Color</string> + </property> + </item> + <item> + <property name="text" > + <string>White Hot</string> + </property> + </item> + <item> + <property name="text" > + <string>Black Hot</string> + </property> + </item> + <item> + <property name="text" > + <string>Incandescent</string> + </property> + </item> + <item> + <property name="text" > + <string>User Defined</string> + </property> + </item> + </widget> + <widget class="QwtWheel" name="WaterfallMaximumIntensityWheel" > + <property name="geometry" > + <rect> + <x>215</x> + <y>0</y> + <width>335</width> + <height>24</height> + </rect> + </property> + <property name="mouseTracking" > + <bool>true</bool> + </property> + <property name="focusPolicy" > + <enum>Qt::WheelFocus</enum> + </property> + <property name="valid" > + <bool>true</bool> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </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> + <widget class="QLabel" name="WaterfallMaximumIntensityLabel" > + <property name="geometry" > + <rect> + <x>563</x> + <y>3</y> + <width>55</width> + <height>21</height> + </rect> + </property> + <property name="text" > + <string>100 dB</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QFrame" name="Tab2PlotDisplayFrame" > + <property name="geometry" > + <rect> + <x>5</x> + <y>30</y> + <width>615</width> + <height>295</height> + </rect> + </property> + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + </widget> + <widget class="QwtWheel" name="WaterfallMinimumIntensityWheel" > + <property name="geometry" > + <rect> + <x>215</x> + <y>325</y> + <width>335</width> + <height>24</height> + </rect> + </property> + <property name="valid" > + <bool>true</bool> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </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> + <widget class="QLabel" name="WaterfallMinimumIntensityLabel" > + <property name="geometry" > + <rect> + <x>565</x> + <y>325</y> + <width>55</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>-100 dB</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QPushButton" name="WaterfallAutoScaleBtn" > + <property name="geometry" > + <rect> + <x>0</x> + <y>325</y> + <width>135</width> + <height>21</height> + </rect> + </property> + <property name="toolTip" > + <string>Scales the Intensity to the current data extremes.</string> + </property> + <property name="text" > + <string>Auto Scale</string> + </property> + </widget> + </widget> + <widget class="QWidget" name="Waterfall3DPage" > + <attribute name="title" > + <string>3D Waterfall Display</string> + </attribute> + <widget class="QLabel" name="textLabel1_2" > + <property name="geometry" > + <rect> + <x>5</x> + <y>0</y> + <width>85</width> + <height>21</height> + </rect> + </property> + <property name="text" > + <string><font size="-2">Intensity Display:</font></string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" name="Waterfall3DMaximumIntensityLabel" > + <property name="geometry" > + <rect> + <x>563</x> + <y>3</y> + <width>55</width> + <height>21</height> + </rect> + </property> + <property name="text" > + <string>100 dB</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QPushButton" name="Waterfall3DAutoScaleBtn" > + <property name="geometry" > + <rect> + <x>0</x> + <y>325</y> + <width>135</width> + <height>21</height> + </rect> + </property> + <property name="toolTip" > + <string>Scales the Intensity to the current data extremes.</string> + </property> + <property name="text" > + <string>Auto Scale</string> + </property> + </widget> + <widget class="QwtWheel" name="Waterfall3DMinimumIntensityWheel" > + <property name="geometry" > + <rect> + <x>215</x> + <y>325</y> + <width>335</width> + <height>24</height> + </rect> + </property> + <property name="valid" > + <bool>true</bool> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </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> + <widget class="QLabel" name="Waterfall3DMinimumIntensityLabel" > + <property name="geometry" > + <rect> + <x>565</x> + <y>325</y> + <width>55</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>-100 dB</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + <widget class="QFrame" name="Waterfall3DPlotDisplayFrame" > + <property name="geometry" > + <rect> + <x>5</x> + <y>30</y> + <width>615</width> + <height>295</height> + </rect> + </property> + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + </widget> + <widget class="QComboBox" name="Waterfall3DIntensityComboBox" > + <property name="geometry" > + <rect> + <x>90</x> + <y>0</y> + <width>121</width> + <height>25</height> + </rect> + </property> + <item> + <property name="text" > + <string>Color</string> + </property> + </item> + <item> + <property name="text" > + <string>White Hot</string> + </property> + </item> + <item> + <property name="text" > + <string>Black Hot</string> + </property> + </item> + <item> + <property name="text" > + <string>Incandescent</string> + </property> + </item> + <item> + <property name="text" > + <string>User Defined</string> + </property> + </item> + </widget> + <widget class="QwtWheel" name="Waterfall3DMaximumIntensityWheel" > + <property name="geometry" > + <rect> + <x>215</x> + <y>0</y> + <width>335</width> + <height>24</height> + </rect> + </property> + <property name="mouseTracking" > + <bool>true</bool> + </property> + <property name="focusPolicy" > + <enum>Qt::WheelFocus</enum> + </property> + <property name="valid" > + <bool>true</bool> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </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> + </widget> + <widget class="QWidget" name="TimeDomainPage" > + <attribute name="title" > + <string>Time Domain Display</string> + </attribute> + <widget class="QFrame" name="TimeDomainDisplayFrame" > + <property name="geometry" > + <rect> + <x>5</x> + <y>5</y> + <width>620</width> + <height>340</height> + </rect> + </property> + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + </widget> + </widget> + </widget> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction>qPixmapFromMimeSource</pixmapfunction> + <customwidgets> + <customwidget> + <class>QwtWheel</class> + <extends>QWidget</extends> + <header>qwt_wheel.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>SpectrumTypeTab</tabstop> + <tabstop>MaxHoldCheckBox</tabstop> + <tabstop>MaxHoldResetBtn</tabstop> + <tabstop>MinHoldCheckBox</tabstop> + <tabstop>MinHoldResetBtn</tabstop> + <tabstop>PowerLineEdit</tabstop> + <tabstop>AvgLineEdit</tabstop> + <tabstop>UseRFFrequenciesCheckBox</tabstop> + <tabstop>WindowComboBox</tabstop> + <tabstop>FFTSizeComboBox</tabstop> + <tabstop>WaterfallMaximumIntensityWheel</tabstop> + <tabstop>WaterfallMinimumIntensityWheel</tabstop> + </tabstops> + <includes> + <include location="global" >SpectrumGUIClass.h</include> + <include location="global" >FrequencyDisplayPlot.h</include> + <include location="global" >WaterfallDisplayPlot.h</include> + <include location="global" >Waterfall3DDisplayPlot.h</include> + <include location="global" >TimeDomainDisplayPlot.h</include> + <include location="global" >qvalidator.h</include> + <include location="global" >vector</include> + <include location="local" >qwt_wheel.h</include> + </includes> + <resources/> + <connections> + <connection> + <sender>MaxHoldCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>MaxHoldCheckBox_toggled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>MaxHoldResetBtn</sender> + <signal>clicked()</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>MaxHoldResetBtn_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>MinHoldCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>MinHoldCheckBox_toggled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>MinHoldResetBtn</sender> + <signal>clicked()</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>MinHoldResetBtn_clicked()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>AvgLineEdit</sender> + <signal>textChanged(QString)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>AvgLineEdit_textChanged(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>PowerLineEdit</sender> + <signal>textChanged(QString)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>PowerLineEdit_textChanged(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>WindowComboBox</sender> + <signal>activated(int)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>WindowTypeChanged(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>UseRFFrequenciesCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>UseRFFrequenciesCB(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>WaterfallMaximumIntensityWheel</sender> + <signal>valueChanged(double)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>waterfallMaximumIntensityChangedCB(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>WaterfallMinimumIntensityWheel</sender> + <signal>valueChanged(double)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>waterfallMinimumIntensityChangedCB(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>Waterfall3DMaximumIntensityWheel</sender> + <signal>valueChanged(double)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>waterfall3DMaximumIntensityChangedCB(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>Waterfall3DMinimumIntensityWheel</sender> + <signal>valueChanged(double)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>waterfall3DMinimumIntensityChangedCB(double)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>FFTSizeComboBox</sender> + <signal>activated(QString)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>FFTComboBoxSelectedCB(QString)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>WaterfallAutoScaleBtn</sender> + <signal>clicked()</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>WaterfallAutoScaleBtnCB()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>Waterfall3DAutoScaleBtn</sender> + <signal>clicked()</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>Waterfall3DAutoScaleBtnCB()</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>WaterfallIntensityComboBox</sender> + <signal>activated(int)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>WaterfallIntensityColorTypeChanged(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>Waterfall3DIntensityComboBox</sender> + <signal>activated(int)</signal> + <receiver>SpectrumDisplayForm</receiver> + <slot>Waterfall3DIntensityColorTypeChanged(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel" > + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/gr-qtgui/src/lib/waterfallGlobalData.cc b/gr-qtgui/src/lib/waterfallGlobalData.cc new file mode 100644 index 000000000..0cbef4ec3 --- /dev/null +++ b/gr-qtgui/src/lib/waterfallGlobalData.cc @@ -0,0 +1,258 @@ +#ifndef WATERFALL_GLOBAL_DATA_CPP +#define WATERFALL_GLOBAL_DATA_CPP + +#include <waterfallGlobalData.h> + +#include <Waterfall3DDisplayPlot.h> + +WaterfallData::WaterfallData(const double minimumFrequency, const double maximumFrequency, const uint64_t fftPoints, const unsigned int historyExtent): + QwtRasterData(QwtDoubleRect(minimumFrequency /* X START */, 0 /* Y START */, maximumFrequency - minimumFrequency /* WIDTH */, static_cast<double>(historyExtent)/* HEIGHT */)) + +{ + _intensityRange = QwtDoubleInterval(-200.0, 0.0); + + _fftPoints = fftPoints; + _historyLength = historyExtent; + + _spectrumData = new double[_fftPoints * _historyLength]; + + Reset(); +} + +WaterfallData::~WaterfallData(){ + delete[] _spectrumData; +} + +void WaterfallData::Reset(){ + memset(_spectrumData, 0x0, _fftPoints*_historyLength*sizeof(double)); + + _numLinesToUpdate = -1; +} + +void WaterfallData::Copy(const WaterfallData* rhs){ + if((_fftPoints != rhs->GetNumFFTPoints()) || + (boundingRect() != rhs->boundingRect()) ){ + _fftPoints = rhs->GetNumFFTPoints(); + setBoundingRect(rhs->boundingRect()); + delete[] _spectrumData; + _spectrumData = new double[_fftPoints * _historyLength]; + } + Reset(); + SetSpectrumDataBuffer(rhs->GetSpectrumDataBuffer()); + SetNumLinesToUpdate(rhs->GetNumLinesToUpdate()); + setRange(rhs->range()); +} + +void WaterfallData::ResizeData(const double startFreq, const double stopFreq, const uint64_t fftPoints){ + if((fftPoints != GetNumFFTPoints()) || + (boundingRect().width() != (stopFreq - startFreq)) || + (boundingRect().left() != startFreq)){ + + setBoundingRect(QwtDoubleRect(startFreq, 0, stopFreq-startFreq, boundingRect().height())); + _fftPoints = fftPoints; + delete[] _spectrumData; + _spectrumData = new double[_fftPoints * _historyLength]; + } + + Reset(); +} + +QwtRasterData *WaterfallData::copy() const{ + WaterfallData* returnData = new WaterfallData(boundingRect().left(), boundingRect().right(), _fftPoints, _historyLength); + returnData->Copy(this); + return returnData; +} + +QwtDoubleInterval WaterfallData::range() const{ + return _intensityRange; +} + +void WaterfallData::setRange(const QwtDoubleInterval& newRange){ + _intensityRange = newRange; +} + +double WaterfallData::value(double x, double y) const{ + double returnValue = 0.0; + + const unsigned int intY = static_cast<unsigned int>((1.0 - (y/boundingRect().height())) * + static_cast<double>(_historyLength - 1)); + const unsigned int intX = static_cast<unsigned int>((((x - boundingRect().left()) / boundingRect().width()) * + static_cast<double>(_fftPoints-1)) + 0.5); + + const int location = (intY * _fftPoints) + intX; + if((location > -1) && (location < static_cast<int64_t>(_fftPoints * _historyLength))){ + returnValue = _spectrumData[location]; + } + + return returnValue; +} + +uint64_t WaterfallData::GetNumFFTPoints()const{ + return _fftPoints; +} + +void WaterfallData::addFFTData(const double* fftData, const uint64_t fftDataSize, const int droppedFrames){ + if(fftDataSize == _fftPoints){ + int64_t heightOffset = _historyLength - 1 - droppedFrames; + uint64_t drawingDroppedFrames = droppedFrames; + + // Any valid data rolled off the display so just fill in zeros and write new data + if(heightOffset < 0){ + heightOffset = 0; + drawingDroppedFrames = static_cast<uint64_t>(_historyLength-1); + } + + // Copy the old data over if any available + if(heightOffset > 0){ + memmove( _spectrumData, &_spectrumData[(drawingDroppedFrames+1) * _fftPoints], heightOffset * _fftPoints * sizeof(double)) ; + } + + if(drawingDroppedFrames > 0){ + // Fill in zeros data for dropped data + memset(&_spectrumData[heightOffset * _fftPoints], 0x00, static_cast<int64_t>(drawingDroppedFrames) * _fftPoints * sizeof(double)); + } + + // add the new buffer + memcpy(&_spectrumData[(_historyLength - 1) * _fftPoints], fftData, _fftPoints*sizeof(double)); + + } +} + +double* WaterfallData::GetSpectrumDataBuffer()const{ + return _spectrumData; +} + +void WaterfallData::SetSpectrumDataBuffer(const double* newData){ + memcpy(_spectrumData, newData, _fftPoints * _historyLength * sizeof(double)); +} + +int WaterfallData::GetNumLinesToUpdate()const{ + return _numLinesToUpdate; +} + +void WaterfallData::SetNumLinesToUpdate(const int newNum){ + _numLinesToUpdate = newNum; +} + +void WaterfallData::IncrementNumLinesToUpdate(){ + _numLinesToUpdate++; +} + +Waterfall3DData::Waterfall3DData(const double minimumFrequency, const double maximumFrequency, const uint64_t fftPoints, const unsigned int historyExtent): + WaterfallData(minimumFrequency, maximumFrequency, fftPoints, historyExtent), Qwt3D::Function(){ + + _floorValue = 0.0; + setMinZ(0.0); + setMaxZ(200.0); + + // Create the dummy mesh data until _ResizeMesh is called + data = new double*[1]; + data[0] = new double[1]; + Qwt3D::Function::setMesh(1,1); + + _ResizeMesh(); +} + +Waterfall3DData::~Waterfall3DData(){ + for ( unsigned i = 0; i < umesh_p; i++){ + delete[] data[i]; + } + delete[] data; + +} + +void Waterfall3DData::ResizeData(const double startFreq, const double stopFreq, const uint64_t fftPoints){ + if((fftPoints != GetNumFFTPoints()) || + (boundingRect().width() != (stopFreq - startFreq)) || + (boundingRect().left() != startFreq)){ + WaterfallData::ResizeData(startFreq, stopFreq, fftPoints); + _ResizeMesh(); + } + + Reset(); +} + +bool Waterfall3DData::create() +{ + if ((umesh_p<=2) || (vmesh_p<=2) || !plotwidget_p) + return false; + + // Almost the same as the old create, except that here we store our own data buffer in the class rather than re-creating it each time... + + unsigned i,j; + + /* get the data */ + double dx = (maxu_p - minu_p) / (umesh_p - 1); + double dy = (maxv_p - minv_p) / (vmesh_p - 1); + + for (i = 0; i < umesh_p; ++i) + { + for (j = 0; j < vmesh_p; ++j) + { + data[i][j] = operator()(minu_p + i*dx, minv_p + j*dy); + + if (data[i][j] > range_p.maxVertex.z) + data[i][j] = range_p.maxVertex.z; + else if (data[i][j] < range_p.minVertex.z) + data[i][j] = range_p.minVertex.z; + } + } + + Q_ASSERT(plotwidget_p); + if (!plotwidget_p) + { + fprintf(stderr,"Function: no valid Plot3D Widget assigned"); + } + else + { + ((Waterfall3DDisplayPlot*)plotwidget_p)->loadFromData(data, umesh_p, vmesh_p, minu_p, maxu_p, minv_p, maxv_p); + } + + return true; +} + +double Waterfall3DData::operator()(double x, double y){ + return value(x,y) - _floorValue; +} + +double Waterfall3DData::GetFloorValue()const{ + return _floorValue; +} + +void Waterfall3DData::SetFloorValue(const double newValue){ + _floorValue = newValue; +} + +double Waterfall3DData::minZ()const{ + return range_p.minVertex.z; +} + +double Waterfall3DData::maxZ()const{ + return range_p.maxVertex.z; +} + +void Waterfall3DData::setMesh(unsigned int, unsigned int){ + // Do Nothing + printf("Should Not Reach this Function\n"); +} + +void Waterfall3DData::_ResizeMesh(){ + // Clear out the old mesh + for ( unsigned i = 0; i < umesh_p; i++){ + delete[] data[i]; + } + delete[] data; + + Qwt3D::Function::setMesh(static_cast<int>(boundingRect().width()/20.0), _historyLength); + setDomain( boundingRect().left(), static_cast<int>(boundingRect().right()), 0, _historyLength); + + /* allocate some space for the mesh */ + unsigned i; + data = new double* [umesh_p] ; + for ( i = 0; i < umesh_p; i++) + { + data[i] = new double [vmesh_p]; + } +} + +#endif /* WATERFALL_GLOBAL_DATA_CPP */ diff --git a/gr-qtgui/src/lib/waterfallGlobalData.h b/gr-qtgui/src/lib/waterfallGlobalData.h new file mode 100644 index 000000000..6e34ce5b1 --- /dev/null +++ b/gr-qtgui/src/lib/waterfallGlobalData.h @@ -0,0 +1,78 @@ +#ifndef WATERFALL_GLOBAL_DATA_HPP +#define WATERFALL_GLOBAL_DATA_HPP + +#include <qwt_raster_data.h> +#include <qwt3d_function.h> + +class Waterfall3DDisplayPlot; + +class WaterfallData: public QwtRasterData +{ +public: + WaterfallData(const double, const double, const uint64_t, const unsigned int); + virtual ~WaterfallData(); + + virtual void Reset(); + virtual void Copy(const WaterfallData*); + + virtual void ResizeData(const double, const double, const uint64_t); + + virtual QwtRasterData *copy() const; + virtual QwtDoubleInterval range() const; + virtual void setRange(const QwtDoubleInterval&); + + virtual double value(double x, double y) 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 int GetNumLinesToUpdate()const; + virtual void SetNumLinesToUpdate(const int); + virtual void IncrementNumLinesToUpdate(); + +protected: + + double* _spectrumData; + uint64_t _fftPoints; + uint64_t _historyLength; + int _numLinesToUpdate; + QwtDoubleInterval _intensityRange; + +private: + +}; + +class Waterfall3DData: public WaterfallData, public Qwt3D::Function +{ +public: + Waterfall3DData(const double, const double, const uint64_t, const unsigned int); + virtual ~Waterfall3DData(); + + virtual void ResizeData(const double, const double, const uint64_t); + + virtual bool create(); + virtual void setMesh(unsigned int columns, unsigned int rows); //!< Sets number of rows and columns. + + virtual double operator()(double x, double y); + + virtual double GetFloorValue()const; + virtual void SetFloorValue(const double); + + virtual double minZ()const; + virtual double maxZ()const; + +protected: + void _ResizeMesh(); + + double** data; + double _floorValue; + +private: + +}; + + +#endif /* WATERFALL_GLOBAL_DATA_HPP */ diff --git a/gr-qtgui/src/python/Makefile.am b/gr-qtgui/src/python/Makefile.am new file mode 100644 index 000000000..ce862c2b7 --- /dev/null +++ b/gr-qtgui/src/python/Makefile.am @@ -0,0 +1,33 @@ +# +# Copyright 2004 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 $(top_srcdir)/Makefile.common + +noinst_PYTHON = \ + qttest_f.py \ + qttest_c.py + +qtguipythondir = $(grpythondir)/qtgui + +qtguipython_PYTHON = \ + __init__.py + +CLEANFILES = *.pyc *.pyo diff --git a/gr-qtgui/src/python/__init__.py b/gr-qtgui/src/python/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/gr-qtgui/src/python/__init__.py diff --git a/gr-qtgui/src/python/qttest_c.py b/gr-qtgui/src/python/qttest_c.py new file mode 100755 index 000000000..40d1f42b7 --- /dev/null +++ b/gr-qtgui/src/python/qttest_c.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from gnuradio import gr +from gnuradio.qtgui import qtgui + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + fftsize = 2048 + + src1 = gr.sig_source_c(1, gr.GR_SIN_WAVE, 0.1, 0.01, 0) + src2 = gr.sig_source_c(1, gr.GR_SIN_WAVE, 0.015, 0.01, 0) + src = gr.add_cc() + thr = gr.throttle(gr.sizeof_gr_complex, 20*fftsize) + self.snk = qtgui.sink_c(fftsize, gr.firdes.WIN_BLACKMAN_hARRIS, -0.5, 0.5) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, thr, self.snk) + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.snk.start_app(); + #tb.wait(); + diff --git a/gr-qtgui/src/python/qttest_f.py b/gr-qtgui/src/python/qttest_f.py new file mode 100755 index 000000000..a950b35b1 --- /dev/null +++ b/gr-qtgui/src/python/qttest_f.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +from gnuradio import gr +from gnuradio.qtgui import qtgui + +class my_top_block(gr.top_block): + def __init__(self): + gr.top_block.__init__(self) + + fftsize = 8192 + + win = gr.firdes.window(gr.firdes.WIN_HANN, fftsize, 0) + + src1 = gr.sig_source_f(1, gr.GR_SIN_WAVE, 0.1, 0.1, 0) + src2 = gr.sig_source_f(1, gr.GR_SIN_WAVE, 0.015, 0.1, 0) + src = gr.add_ff() + thr = gr.throttle(gr.sizeof_float, 20*fftsize) + self.snk = qtgui.sink_f(fftsize, win, -0.5, 0.5) + + self.connect(src1, (src,0)) + self.connect(src2, (src,1)) + self.connect(src, thr, self.snk) + +if __name__ == "__main__": + tb = my_top_block(); + tb.start() + tb.snk.start_app(); + #tb.wait(); + |