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