From: trondeau Date: Sat, 25 Oct 2008 23:29:43 +0000 (+0000) Subject: Merging qtgui branch-r9068:9837: this ads a qtgui_sink_c and qtgui_sink_f that displa... X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=144fa44ed2e0378e2ec585c3991108926d9449be;p=debian%2Fgnuradio Merging qtgui branch-r9068:9837: this ads a qtgui_sink_c and qtgui_sink_f that displays the time, PSD, and spectrogram plots of a signal put into it. It requires qt4, qwt, and qwtplot3d and has not been tested on OSX. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@9853 221aa14e-8319-0410-a670-987f0aec2ac5 --- diff --git a/config/gr_qwt.m4 b/config/gr_qwt.m4 index bcdb7d98..16ec6381 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 ], - [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 00000000..758dfbc3 --- /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 479c15a3..4a717c65 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 4b581a16..7e06bdbf 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 3eaa9c8d..8efeb343 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 0ae34fed..de775bd0 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 d77c15ab..d49434ad 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 00000000..d53f96c1 --- /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 00000000..7230d609 --- /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 00000000..9fb9253b --- /dev/null +++ b/gr-qtgui/src/lib/FrequencyDisplayPlot.cc @@ -0,0 +1,386 @@ +#ifndef FREQUENCY_DISPLAY_PLOT_C +#define FREQUENCY_DISPLAY_PLOT_C + +#include + +#include + +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(_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 00000000..fb647d96 --- /dev/null +++ b/gr-qtgui/src/lib/FrequencyDisplayPlot.h @@ -0,0 +1,81 @@ +#ifndef FREQUENCY_DISPLAY_PLOT_HPP +#define FREQUENCY_DISPLAY_PLOT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 00000000..928d960d --- /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 00000000..a57395a6 --- /dev/null +++ b/gr-qtgui/src/lib/SpectrumGUIClass.cc @@ -0,0 +1,341 @@ +#ifndef SPECTRUM_GUI_CLASS_CPP +#define SPECTRUM_GUI_CLASS_CPP + +#include +//Added by qt3to4: +#include +#include + +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[_dataPoints]; + _realTimeDomainPoints = new double[_dataPoints]; + _imagTimeDomainPoints = new double[_dataPoints]; + _fftBuffersCreatedFlag = true; + + + memset(_fftPoints, 0x0, _dataPoints*sizeof(std::complex)); + 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* 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)); + } + + // 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 00000000..4f8fb978 --- /dev/null +++ b/gr-qtgui/src/lib/SpectrumGUIClass.h @@ -0,0 +1,89 @@ +#ifndef SPECTRUM_GUI_CLASS_HPP +#define SPECTRUM_GUI_CLASS_HPP + +//#include +#include +#include +#include +#include +#include + +//#include + +class SpectrumDisplayForm; +#include + +#include + +#include +#include +#include + +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*, 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* _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 00000000..d8abffaf --- /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 + +#include +#include + + +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 00000000..0a10be17 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 00000000..21011ed6 --- /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 + +#include +#include + +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(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(((_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 00000000..dcdbf380 --- /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 + +#include +#include + +#include +#include + +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; + + private: + double _floor; + + public: + explicit IntensityScale(const double newFloor):_floor(newFloor){ } + virtual ~IntensityScale(){} + + virtual QString ticLabel(unsigned int idx) const{ + if (idx; + friend class Waterfall3DDisplayPlot; + + private: + Waterfall3DDisplayPlot* _plot; + + public: + TimeScale(Waterfall3DDisplayPlot* plot ):_plot(plot){ + } + virtual ~TimeScale(){ + } + + virtual QString ticLabel(unsigned int idx) const{ + if (idx_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; + 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 + +#include +#include +#include +#include +#include +#include + +#include + +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 00000000..71fb76aa --- /dev/null +++ b/gr-qtgui/src/lib/WaterfallDisplayPlot.h @@ -0,0 +1,72 @@ +#ifndef WATERFALL_DISPLAY_PLOT_HPP +#define WATERFALL_DISPLAY_PLOT_HPP + +#include +#include +#include + +#include + +#include + +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 00000000..b85b1aca --- /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 +#include +#include +/* 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(modf(timeValue, &seconds) * static_cast(NSEC_PER_SEC)); + time_t sec = static_cast(seconds); + + set_normalized_timespec(&ret, sec, nsec); + + return ret; +} + +static inline double convert_from_timespec(const timespec actual){ + return (static_cast(actual.tv_sec) + (static_cast(actual.tv_nsec) / static_cast(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 00000000..2b1447e0 --- /dev/null +++ b/gr-qtgui/src/lib/plot_waterfall.cc @@ -0,0 +1,325 @@ +#include +#include +#include +#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 QwtColorTable; +#else +typedef QVector 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 00000000..2be677b1 --- /dev/null +++ b/gr-qtgui/src/lib/plot_waterfall.h @@ -0,0 +1,54 @@ +#ifndef PLOT_WATERFALL_H +#define PLOT_WATERFALL_H + +#include +#include + +#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 00000000..6edbca12 --- /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 +#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 00000000..52156054 --- /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 +%} + +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 &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 &window, + float fmin, float fmax, const std::string &name); + qtgui_sink_fy (int fftsize, const std::vector &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 00000000..eaef58d6 --- /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 +#endif + +#include +#include +#include + +#include + +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 00000000..a6bd59f2 --- /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 +#include +#include +#include +#include +#include "SpectrumGUIClass.h" + + +class qtgui_sink_c; +typedef boost::shared_ptr 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 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 00000000..b27718bd --- /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 +#endif + +#include +#include +#include + +#include + +qtgui_sink_f_sptr +qtgui_make_sink_f (int fftsize, const std::vector &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 &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 00000000..3f0e785c --- /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 +#include +#include +#include +#include "SpectrumGUIClass.h" + + +class qtgui_sink_f; +typedef boost::shared_ptr qtgui_sink_f_sptr; + +qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, const std::vector &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 &window, + float fmin, float fmax, const std::string &name); + qtgui_sink_f (int fftsize, const std::vector &window, + float fmin, float fmax, const std::string &name); + + int d_fftsize; + std::vector 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 00000000..f705e047 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumUpdateEvents.cc @@ -0,0 +1,131 @@ +#ifndef SPECTRUM_UPDATE_EVENTS_C +#define SPECTRUM_UPDATE_EVENTS_C + +#include + +SpectrumUpdateEvent::SpectrumUpdateEvent(const std::complex* 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[_numFFTDataPoints]; + _fftPoints[0] = std::complex(0,0); + memcpy(_fftPoints, fftPoints, numFFTDataPoints*sizeof(std::complex)); + + _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* 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 00000000..a758d884 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumUpdateEvents.h @@ -0,0 +1,82 @@ +#ifndef SPECTRUM_UPDATE_EVENTS_H +#define SPECTRUM_UPDATE_EVENTS_H + +#include +#include +#include +#include + +class SpectrumUpdateEvent:public QEvent{ + +public: + SpectrumUpdateEvent(const std::complex* 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* 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* _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 00000000..481a5526 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumdisplayform.cc @@ -0,0 +1,619 @@ +#include +#include +#include +#include + +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; + + 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* 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* 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(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(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", "
The system's video card hardware or current drivers do not support direct hardware rendering of the OpenGL modules.

Software rendering is VERY processor intensive.

Do you want to use software rendering?
", 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(_historyVector->size())){ + std::vector::iterator pos; + while(newCount < static_cast(_historyVector->size())){ + pos = _historyVector->begin(); + delete[] (*pos); + _historyVector->erase(pos); + } + + while(newCount > static_cast(_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(_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(_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 00000000..97b14a6c --- /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 + +#include +#include +#include +#include +#include +#include +#include + +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* _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 00000000..f75022f3 --- /dev/null +++ b/gr-qtgui/src/lib/spectrumdisplayform.ui @@ -0,0 +1,986 @@ + + SpectrumDisplayForm + + + + 0 + 0 + 637 + 436 + + + + Spectrum Display + + + + + 10 + 385 + 180 + 20 + + + + Display RF Frequencies + + + + + + 105 + 410 + 170 + 20 + + + + + 9 + + + + + Hamming + + + + + Hann + + + + + Blackman + + + + + Rectangular + + + + + Kaiser + + + + + Blackman-harris + + + + + + + 10 + 410 + 90 + 17 + + + + Window: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + + + 405 + 385 + 116 + 20 + + + + FFT Size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + + + 525 + 385 + 100 + 20 + + + + + 1024 + + + + + 2048 + + + + + 4096 + + + + + 8192 + + + + + 16384 + + + + + 32768 + + + + + + + 0 + 0 + 630 + 380 + + + + + Frequency Display + + + + + 480 + 320 + 60 + 20 + + + + 1 + + + + + + 10 + 325 + 95 + 20 + + + + Min Hold + + + false + + + + + + 545 + 300 + 72 + 20 + + + + Average + + + Qt::AlignCenter + + + false + + + + + + 105 + 325 + 61 + 20 + + + + Reset + + + + + + 550 + 320 + 60 + 20 + + + + 0 + + + + + + 475 + 300 + 72 + 20 + + + + Power + + + Qt::AlignCenter + + + false + + + + + + 105 + 300 + 61 + 20 + + + + Reset + + + + + + 10 + 300 + 95 + 20 + + + + Max Hold + + + false + + + + + + 5 + 5 + 620 + 290 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + Waterfall Display + + + + + 5 + 0 + 85 + 21 + + + + <font size="-2">Intensity Display:</font> + + + false + + + + + + 90 + 0 + 121 + 25 + + + + + Color + + + + + White Hot + + + + + Black Hot + + + + + Incandescent + + + + + User Defined + + + + + + + 215 + 0 + 335 + 24 + + + + true + + + Qt::WheelFocus + + + true + + + Qt::Horizontal + + + 200.000000000000000 + + + 20.000000000000000 + + + 0.000000000000000 + + + + + + 563 + 3 + 55 + 21 + + + + 100 dB + + + false + + + + + + 5 + 30 + 615 + 295 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + 215 + 325 + 335 + 24 + + + + true + + + Qt::Horizontal + + + 200.000000000000000 + + + 20.000000000000000 + + + 0.000000000000000 + + + + + + 565 + 325 + 55 + 20 + + + + -100 dB + + + false + + + + + + 0 + 325 + 135 + 21 + + + + Scales the Intensity to the current data extremes. + + + Auto Scale + + + + + + 3D Waterfall Display + + + + + 5 + 0 + 85 + 21 + + + + <font size="-2">Intensity Display:</font> + + + false + + + + + + 563 + 3 + 55 + 21 + + + + 100 dB + + + false + + + + + + 0 + 325 + 135 + 21 + + + + Scales the Intensity to the current data extremes. + + + Auto Scale + + + + + + 215 + 325 + 335 + 24 + + + + true + + + Qt::Horizontal + + + 200.000000000000000 + + + 20.000000000000000 + + + 0.000000000000000 + + + + + + 565 + 325 + 55 + 20 + + + + -100 dB + + + false + + + + + + 5 + 30 + 615 + 295 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + 90 + 0 + 121 + 25 + + + + + Color + + + + + White Hot + + + + + Black Hot + + + + + Incandescent + + + + + User Defined + + + + + + + 215 + 0 + 335 + 24 + + + + true + + + Qt::WheelFocus + + + true + + + Qt::Horizontal + + + 200.000000000000000 + + + 20.000000000000000 + + + 0.000000000000000 + + + + + + Time Domain Display + + + + + 5 + 5 + 620 + 340 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + qPixmapFromMimeSource + + + QwtWheel + QWidget +
qwt_wheel.h
+
+
+ + SpectrumTypeTab + MaxHoldCheckBox + MaxHoldResetBtn + MinHoldCheckBox + MinHoldResetBtn + PowerLineEdit + AvgLineEdit + UseRFFrequenciesCheckBox + WindowComboBox + FFTSizeComboBox + WaterfallMaximumIntensityWheel + WaterfallMinimumIntensityWheel + + + SpectrumGUIClass.h + FrequencyDisplayPlot.h + WaterfallDisplayPlot.h + Waterfall3DDisplayPlot.h + TimeDomainDisplayPlot.h + qvalidator.h + vector + qwt_wheel.h + + + + + MaxHoldCheckBox + toggled(bool) + SpectrumDisplayForm + MaxHoldCheckBox_toggled(bool) + + + 20 + 20 + + + 20 + 20 + + + + + MaxHoldResetBtn + clicked() + SpectrumDisplayForm + MaxHoldResetBtn_clicked() + + + 20 + 20 + + + 20 + 20 + + + + + MinHoldCheckBox + toggled(bool) + SpectrumDisplayForm + MinHoldCheckBox_toggled(bool) + + + 20 + 20 + + + 20 + 20 + + + + + MinHoldResetBtn + clicked() + SpectrumDisplayForm + MinHoldResetBtn_clicked() + + + 20 + 20 + + + 20 + 20 + + + + + AvgLineEdit + textChanged(QString) + SpectrumDisplayForm + AvgLineEdit_textChanged(QString) + + + 20 + 20 + + + 20 + 20 + + + + + PowerLineEdit + textChanged(QString) + SpectrumDisplayForm + PowerLineEdit_textChanged(QString) + + + 20 + 20 + + + 20 + 20 + + + + + WindowComboBox + activated(int) + SpectrumDisplayForm + WindowTypeChanged(int) + + + 20 + 20 + + + 20 + 20 + + + + + UseRFFrequenciesCheckBox + toggled(bool) + SpectrumDisplayForm + UseRFFrequenciesCB(bool) + + + 20 + 20 + + + 20 + 20 + + + + + WaterfallMaximumIntensityWheel + valueChanged(double) + SpectrumDisplayForm + waterfallMaximumIntensityChangedCB(double) + + + 20 + 20 + + + 20 + 20 + + + + + WaterfallMinimumIntensityWheel + valueChanged(double) + SpectrumDisplayForm + waterfallMinimumIntensityChangedCB(double) + + + 20 + 20 + + + 20 + 20 + + + + + Waterfall3DMaximumIntensityWheel + valueChanged(double) + SpectrumDisplayForm + waterfall3DMaximumIntensityChangedCB(double) + + + 20 + 20 + + + 20 + 20 + + + + + Waterfall3DMinimumIntensityWheel + valueChanged(double) + SpectrumDisplayForm + waterfall3DMinimumIntensityChangedCB(double) + + + 20 + 20 + + + 20 + 20 + + + + + FFTSizeComboBox + activated(QString) + SpectrumDisplayForm + FFTComboBoxSelectedCB(QString) + + + 20 + 20 + + + 20 + 20 + + + + + WaterfallAutoScaleBtn + clicked() + SpectrumDisplayForm + WaterfallAutoScaleBtnCB() + + + 20 + 20 + + + 20 + 20 + + + + + Waterfall3DAutoScaleBtn + clicked() + SpectrumDisplayForm + Waterfall3DAutoScaleBtnCB() + + + 20 + 20 + + + 20 + 20 + + + + + WaterfallIntensityComboBox + activated(int) + SpectrumDisplayForm + WaterfallIntensityColorTypeChanged(int) + + + 20 + 20 + + + 20 + 20 + + + + + Waterfall3DIntensityComboBox + activated(int) + SpectrumDisplayForm + Waterfall3DIntensityColorTypeChanged(int) + + + 20 + 20 + + + 20 + 20 + + + + +
diff --git a/gr-qtgui/src/lib/waterfallGlobalData.cc b/gr-qtgui/src/lib/waterfallGlobalData.cc new file mode 100644 index 00000000..0cbef4ec --- /dev/null +++ b/gr-qtgui/src/lib/waterfallGlobalData.cc @@ -0,0 +1,258 @@ +#ifndef WATERFALL_GLOBAL_DATA_CPP +#define WATERFALL_GLOBAL_DATA_CPP + +#include + +#include + +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(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((1.0 - (y/boundingRect().height())) * + static_cast(_historyLength - 1)); + const unsigned int intX = static_cast((((x - boundingRect().left()) / boundingRect().width()) * + static_cast(_fftPoints-1)) + 0.5); + + const int location = (intY * _fftPoints) + intX; + if((location > -1) && (location < static_cast(_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(_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(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(boundingRect().width()/20.0), _historyLength); + setDomain( boundingRect().left(), static_cast(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 00000000..6e34ce5b --- /dev/null +++ b/gr-qtgui/src/lib/waterfallGlobalData.h @@ -0,0 +1,78 @@ +#ifndef WATERFALL_GLOBAL_DATA_HPP +#define WATERFALL_GLOBAL_DATA_HPP + +#include +#include + +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 00000000..ce862c2b --- /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 00000000..e69de29b diff --git a/gr-qtgui/src/python/qttest_c.py b/gr-qtgui/src/python/qttest_c.py new file mode 100755 index 00000000..40d1f42b --- /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 00000000..a950b35b --- /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(); +