summaryrefslogtreecommitdiff
path: root/gr-qtgui/lib
diff options
context:
space:
mode:
authorTom Rondeau2011-04-10 19:39:58 -0400
committerTom Rondeau2011-04-10 19:39:58 -0400
commit4d6f2a34a6380270e6ac6c5ea37f1dbac7acb9f6 (patch)
treec339967250fe6f19dccfce6b911c3cb51ed4285c /gr-qtgui/lib
parentec0edceb88e9603aedd4f6dd7a8a73702ce59547 (diff)
parent11e91cdd442f9232382986f9d974b230554fabe2 (diff)
downloadgnuradio-4d6f2a34a6380270e6ac6c5ea37f1dbac7acb9f6.tar.gz
gnuradio-4d6f2a34a6380270e6ac6c5ea37f1dbac7acb9f6.tar.bz2
gnuradio-4d6f2a34a6380270e6ac6c5ea37f1dbac7acb9f6.zip
Merge branch 'qtgui'
Diffstat (limited to 'gr-qtgui/lib')
-rw-r--r--gr-qtgui/lib/.gitignore15
-rw-r--r--gr-qtgui/lib/ConstellationDisplayPlot.cc189
-rw-r--r--gr-qtgui/lib/ConstellationDisplayPlot.h61
-rw-r--r--gr-qtgui/lib/FrequencyDisplayPlot.cc437
-rw-r--r--gr-qtgui/lib/FrequencyDisplayPlot.h92
-rw-r--r--gr-qtgui/lib/Makefile.am108
-rw-r--r--gr-qtgui/lib/SpectrumGUIClass.cc452
-rw-r--r--gr-qtgui/lib/SpectrumGUIClass.h108
-rw-r--r--gr-qtgui/lib/TimeDomainDisplayPlot.cc272
-rw-r--r--gr-qtgui/lib/TimeDomainDisplayPlot.h65
-rw-r--r--gr-qtgui/lib/WaterfallDisplayPlot.cc547
-rw-r--r--gr-qtgui/lib/WaterfallDisplayPlot.h84
-rw-r--r--gr-qtgui/lib/highResTimeFunctions.h299
-rw-r--r--gr-qtgui/lib/plot_waterfall.cc325
-rw-r--r--gr-qtgui/lib/plot_waterfall.h52
-rw-r--r--gr-qtgui/lib/qtgui_sink_c.cc298
-rw-r--r--gr-qtgui/lib/qtgui_sink_c.h116
-rw-r--r--gr-qtgui/lib/qtgui_sink_f.cc294
-rw-r--r--gr-qtgui/lib/qtgui_sink_f.h114
-rw-r--r--gr-qtgui/lib/spectrumUpdateEvents.cc180
-rw-r--r--gr-qtgui/lib/spectrumUpdateEvents.h92
-rw-r--r--gr-qtgui/lib/spectrumdisplayform.cc691
-rw-r--r--gr-qtgui/lib/spectrumdisplayform.h111
-rw-r--r--gr-qtgui/lib/spectrumdisplayform.ui756
-rw-r--r--gr-qtgui/lib/waterfallGlobalData.cc164
-rw-r--r--gr-qtgui/lib/waterfallGlobalData.h47
26 files changed, 5969 insertions, 0 deletions
diff --git a/gr-qtgui/lib/.gitignore b/gr-qtgui/lib/.gitignore
new file mode 100644
index 000000000..fc4f8cdfe
--- /dev/null
+++ b/gr-qtgui/lib/.gitignore
@@ -0,0 +1,15 @@
+/Makefile
+/Makefile.in
+/.libs
+/.deps
+/qtgui.cc
+/qtgui.py
+/WaterfallDisplayPlot.moc.cc
+/TimeDomainDisplayPlot.moc.cc
+/spectrumdisplayform.moc.cc
+/spectrumdisplayform.ui.h
+/FrequencyDisplayPlot.moc.cc
+/ConstellationDisplayPlot.moc.cc
+/gnuradio
+/guile
+/python
diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.cc b/gr-qtgui/lib/ConstellationDisplayPlot.cc
new file mode 100644
index 000000000..71933cece
--- /dev/null
+++ b/gr-qtgui/lib/ConstellationDisplayPlot.cc
@@ -0,0 +1,189 @@
+#ifndef CONSTELLATION_DISPLAY_PLOT_C
+#define CONSTELLATION_DISPLAY_PLOT_C
+
+#include <ConstellationDisplayPlot.h>
+
+#include <qwt_scale_draw.h>
+#include <qwt_legend.h>
+
+
+class ConstellationDisplayZoomer: public QwtPlotZoomer
+{
+public:
+ ConstellationDisplayZoomer(QwtPlotCanvas* canvas):QwtPlotZoomer(canvas)
+ {
+ setTrackerMode(QwtPicker::AlwaysOn);
+ }
+
+ virtual ~ConstellationDisplayZoomer(){
+
+ }
+
+ virtual void updateTrackerText(){
+ updateDisplay();
+ }
+
+protected:
+ using QwtPlotZoomer::trackerText;
+ virtual QwtText trackerText( const QwtDoublePoint& p ) const
+ {
+ QwtText t(QString("(%1, %2)").arg(p.x(), 0, 'f', 4).
+ arg(p.y(), 0, 'f', 4));
+ return t;
+ }
+};
+
+ConstellationDisplayPlot::ConstellationDisplayPlot(QWidget* parent)
+ : QwtPlot(parent)
+{
+ timespec_reset(&_lastReplot);
+
+ resize(parent->width(), parent->height());
+
+ _numPoints = 1024;
+ _penSize = 5;
+ _realDataPoints = new double[_numPoints];
+ _imagDataPoints = 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);
+ set_xaxis(-2.0, 2.0);
+ setAxisTitle(QwtPlot::xBottom, "In-phase");
+
+ setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
+ set_yaxis(-2.0, 2.0);
+ setAxisTitle(QwtPlot::yLeft, "Quadrature");
+
+ // Automatically deleted when parent is deleted
+ _plot_curve = new QwtPlotCurve("Constellation Points");
+ _plot_curve->attach(this);
+ _plot_curve->setPen(QPen(Qt::blue, _penSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+ _plot_curve->setStyle(QwtPlotCurve::Dots);
+ _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints);
+
+ memset(_realDataPoints, 0x0, _numPoints*sizeof(double));
+ memset(_imagDataPoints, 0x0, _numPoints*sizeof(double));
+
+ _zoomer = new ConstellationDisplayZoomer(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);
+
+ connect(this, SIGNAL( legendChecked(QwtPlotItem *, bool ) ),
+ this, SLOT( LegendEntryChecked(QwtPlotItem *, bool ) ));
+}
+
+ConstellationDisplayPlot::~ConstellationDisplayPlot()
+{
+ delete[] _realDataPoints;
+ delete[] _imagDataPoints;
+
+ // _fft_plot_curves deleted when parent deleted
+ // _zoomer and _panner deleted when parent deleted
+}
+
+void
+ConstellationDisplayPlot::set_pen_size(int size)
+{
+ if(size > 0 && size < 30){
+ _penSize = size;
+ _plot_curve->setPen(QPen(Qt::blue, _penSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
+ }
+}
+
+
+void
+ConstellationDisplayPlot::set_xaxis(double min, double max)
+{
+ setAxisScale(QwtPlot::xBottom, min, max);
+}
+
+void
+ConstellationDisplayPlot::set_yaxis(double min, double max)
+{
+ setAxisScale(QwtPlot::yLeft, min, max);
+}
+
+void
+ConstellationDisplayPlot::set_axis(double xmin, double xmax,
+ double ymin, double ymax)
+{
+ set_xaxis(xmin, xmax);
+ set_yaxis(ymin, ymax);
+}
+
+void ConstellationDisplayPlot::replot()
+{
+ QwtPlot::replot();
+}
+
+void
+ConstellationDisplayPlot::resizeSlot( QSize *s )
+{
+ resize(s->width(), s->height());
+}
+
+void ConstellationDisplayPlot::PlotNewData(const double* realDataPoints,
+ const double* imagDataPoints,
+ const int64_t numDataPoints,
+ const double timeInterval)
+{
+ if((numDataPoints > 0) &&
+ (diff_timespec(get_highres_clock(), _lastReplot) > timeInterval)) {
+
+ if(numDataPoints != _numPoints){
+ _numPoints = numDataPoints;
+
+ delete[] _realDataPoints;
+ delete[] _imagDataPoints;
+ _realDataPoints = new double[_numPoints];
+ _imagDataPoints = new double[_numPoints];
+
+ _plot_curve->setRawData(_realDataPoints, _imagDataPoints, _numPoints);
+ }
+
+ memcpy(_realDataPoints, realDataPoints, numDataPoints*sizeof(double));
+ memcpy(_imagDataPoints, imagDataPoints, numDataPoints*sizeof(double));
+
+ _lastReplot = get_highres_clock();
+ }
+}
+
+void
+ConstellationDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on)
+{
+ plotItem->setVisible(!on);
+}
+
+#endif /* CONSTELLATION_DISPLAY_PLOT_C */
diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.h b/gr-qtgui/lib/ConstellationDisplayPlot.h
new file mode 100644
index 000000000..a441a8bfe
--- /dev/null
+++ b/gr-qtgui/lib/ConstellationDisplayPlot.h
@@ -0,0 +1,61 @@
+#ifndef CONSTELLATION_DISPLAY_PLOT_HPP
+#define CONSTELLATION_DISPLAY_PLOT_HPP
+
+#include <cstdio>
+#include <qwt_plot.h>
+#include <qwt_painter.h>
+#include <qwt_plot_canvas.h>
+#include <qwt_plot_curve.h>
+#include <qwt_scale_engine.h>
+#include <qwt_scale_widget.h>
+#include <qwt_plot_zoomer.h>
+#include <qwt_plot_panner.h>
+#include <qwt_plot_marker.h>
+#include <highResTimeFunctions.h>
+#include <qwt_symbol.h>
+
+class ConstellationDisplayPlot : public QwtPlot
+{
+ Q_OBJECT
+
+public:
+ ConstellationDisplayPlot(QWidget*);
+ virtual ~ConstellationDisplayPlot();
+
+ void PlotNewData(const double* realDataPoints,
+ const double* imagDataPoints,
+ const int64_t numDataPoints,
+ const double timeInterval);
+
+ virtual void replot();
+
+ void set_xaxis(double min, double max);
+ void set_yaxis(double min, double max);
+ void set_axis(double xmin, double xmax,
+ double ymin, double ymax);
+ void set_pen_size(int size);
+
+public slots:
+ void resizeSlot( QSize *s );
+
+protected slots:
+ void LegendEntryChecked(QwtPlotItem *plotItem, bool on);
+
+protected:
+
+private:
+ QwtPlotCurve* _plot_curve;
+
+ QwtPlotPanner* _panner;
+ QwtPlotZoomer* _zoomer;
+
+ double* _realDataPoints;
+ double* _imagDataPoints;
+
+ timespec _lastReplot;
+
+ int64_t _numPoints;
+ int64_t _penSize;
+};
+
+#endif /* CONSTELLATION_DISPLAY_PLOT_HPP */
diff --git a/gr-qtgui/lib/FrequencyDisplayPlot.cc b/gr-qtgui/lib/FrequencyDisplayPlot.cc
new file mode 100644
index 000000000..45e2e6c16
--- /dev/null
+++ b/gr-qtgui/lib/FrequencyDisplayPlot.cc
@@ -0,0 +1,437 @@
+#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();
+ }
+
+ void SetUnitType(const std::string &type)
+ {
+ _unitType = type;
+ }
+
+protected:
+ using QwtPlotZoomer::trackerText;
+ virtual QwtText trackerText( const QwtDoublePoint& p ) const
+ {
+ QwtText t(QString("%1 %2, %3 dB").
+ arg(p.x(), 0, 'f', GetFrequencyPrecision()).
+ arg(_unitType.c_str()).arg(p.y(), 0, 'f', 2));
+ return t;
+ }
+
+private:
+ std::string _unitType;
+};
+
+FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent)
+ : QwtPlot(parent)
+{
+ _startFrequency = 0;
+ _stopFrequency = 4000;
+
+ timespec_reset(&_lastReplot);
+
+ resize(parent->width(), parent->height());
+
+ _useCenterFrequencyFlag = false;
+
+ _numPoints = 1024;
+ _dataPoints = new double[_numPoints];
+ _minFFTPoints = new double[_numPoints];
+ _maxFFTPoints = new double[_numPoints];
+ _xAxisPoints = new double[_numPoints];
+
+ // Disable polygon clipping
+ 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);
+
+ setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)");
+ setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0));
+
+ _minYAxis = -120;
+ _maxYAxis = 10;
+ setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
+ setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis);
+ setAxisTitle(QwtPlot::yLeft, "Power (dB)");
+
+ // Automatically deleted when parent is deleted
+ _fft_plot_curve = new QwtPlotCurve("Power Spectrum");
+ _fft_plot_curve->attach(this);
+ _fft_plot_curve->setPen(QPen(Qt::blue));
+ _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, 0, Qt::DotLine));
+ _upper_intensity_marker->attach(this);
+
+ memset(_dataPoints, 0x0, _numPoints*sizeof(double));
+ memset(_xAxisPoints, 0x0, _numPoints*sizeof(double));
+
+ for(int64_t number = 0; number < _numPoints; number++){
+ _minFFTPoints[number] = 200.0;
+ _maxFFTPoints[number] = -280.0;
+ }
+
+ // set up peak marker
+ QwtSymbol symbol;
+
+ _markerPeakAmplitude = new QwtPlotMarker();
+ _markerPeakAmplitude->setLinePen(QPen(Qt::yellow));
+ symbol.setStyle(QwtSymbol::Diamond);
+ symbol.setSize(8);
+ symbol.setPen(QPen(Qt::yellow));
+ symbol.setBrush(QBrush(Qt::yellow));
+ _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);
+
+ // Do this after the zoomer has been built
+ _resetXAxisPoints();
+}
+
+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::set_yaxis(double min, double max)
+{
+ // Get the new max/min values for the plot
+ _minYAxis = min;
+ _maxYAxis = max;
+
+ // Set the axis max/min to the new values
+ setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis);
+
+ // Reset the base zoom level to the new axis scale set here
+ _zoomer->setZoomBase();
+}
+
+void
+FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq,
+ const double constStopFreq,
+ const double constCenterFreq,
+ const bool useCenterFrequencyFlag,
+ const double units, const std::string &strunits)
+{
+ double startFreq = constStartFreq / units;
+ double stopFreq = constStopFreq / units;
+ double centerFreq = constCenterFreq / units;
+
+ _useCenterFrequencyFlag = useCenterFrequencyFlag;
+
+ if(_useCenterFrequencyFlag){
+ startFreq = (startFreq + centerFreq);
+ stopFreq = (stopFreq + centerFreq);
+ }
+
+ bool reset = false;
+ if((startFreq != _startFrequency) || (stopFreq != _stopFrequency))
+ reset = true;
+
+ if(stopFreq > startFreq) {
+ _startFrequency = startFreq;
+ _stopFrequency = stopFreq;
+
+ if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){
+ double display_units = ceil(log10(units)/2.0);
+ setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(display_units));
+ setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str()));
+
+ if(reset)
+ _resetXAxisPoints();
+
+ ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(display_units);
+ ((FreqDisplayZoomer*)_zoomer)->SetUnitType(strunits);
+ }
+ }
+}
+
+
+double
+FrequencyDisplayPlot::GetStartFrequency() const
+{
+ return _startFrequency;
+}
+
+double
+FrequencyDisplayPlot::GetStopFrequency() const
+{
+ return _stopFrequency;
+}
+
+void
+FrequencyDisplayPlot::replot()
+{
+ _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();
+}
+
+void
+FrequencyDisplayPlot::resizeSlot( QSize *s )
+{
+ resize(s->width(), s->height());
+}
+
+void
+FrequencyDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints,
+ const double noiseFloorAmplitude, const double peakFrequency,
+ const double peakAmplitude, const double timeInterval)
+{
+ // Only update plot if there is data and if the time interval has elapsed
+ if((numDataPoints > 0) &&
+ (diff_timespec(get_highres_clock(), _lastReplot) > timeInterval)) {
+
+ 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;
+
+ SetUpperIntensityLevel(_peakAmplitude);
+
+ _lastReplot = get_highres_clock();
+ }
+}
+
+void
+FrequencyDisplayPlot::ClearMaxData()
+{
+ for(int64_t number = 0; number < _numPoints; number++){
+ _maxFFTPoints[number] = _minYAxis;
+ }
+}
+
+void
+FrequencyDisplayPlot::ClearMinData()
+{
+ for(int64_t number = 0; number < _numPoints; number++){
+ _minFFTPoints[number] = _maxYAxis;
+ }
+}
+
+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;
+ }
+
+ setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
+
+ // Set up zoomer base for maximum unzoom x-axis
+ // and reset to maximum unzoom level
+ QwtDoubleRect zbase = _zoomer->zoomBase();
+ zbase.setLeft(_startFrequency);
+ zbase.setRight(_stopFrequency);
+ _zoomer->zoom(zbase);
+ _zoomer->setZoomBase(zbase);
+ _zoomer->zoom(0);
+}
+
+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/lib/FrequencyDisplayPlot.h b/gr-qtgui/lib/FrequencyDisplayPlot.h
new file mode 100644
index 000000000..3c22c1397
--- /dev/null
+++ b/gr-qtgui/lib/FrequencyDisplayPlot.h
@@ -0,0 +1,92 @@
+#ifndef FREQUENCY_DISPLAY_PLOT_HPP
+#define FREQUENCY_DISPLAY_PLOT_HPP
+
+#include <stdint.h>
+#include <cstdio>
+#include <qwt_plot.h>
+#include <qwt_painter.h>
+#include <qwt_plot_canvas.h>
+#include <qwt_plot_curve.h>
+#include <qwt_scale_engine.h>
+#include <qwt_scale_widget.h>
+#include <qwt_plot_zoomer.h>
+#include <qwt_plot_panner.h>
+#include <qwt_plot_marker.h>
+#include <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,
+ const double units=1000.0,
+ const std::string &strunits = "kHz");
+ double GetStartFrequency()const;
+ double GetStopFrequency()const;
+
+ void PlotNewData(const double* dataPoints, const int64_t numDataPoints,
+ const double noiseFloorAmplitude, const double peakFrequency,
+ const double peakAmplitude, const double timeInterval);
+
+ void ClearMaxData();
+ void ClearMinData();
+
+ void SetMaxFFTVisible(const bool);
+ void SetMinFFTVisible(const bool);
+
+ virtual void replot();
+
+ void set_yaxis(double min, double max);
+
+public slots:
+ void resizeSlot( QSize *e );
+ void SetLowerIntensityLevel(const double);
+ void SetUpperIntensityLevel(const double);
+
+protected:
+
+private:
+
+ void _resetXAxisPoints();
+
+ double _startFrequency;
+ double _stopFrequency;
+ double _maxYAxis;
+ double _minYAxis;
+
+ QwtPlotCurve* _fft_plot_curve;
+ QwtPlotCurve* _min_fft_plot_curve;
+ QwtPlotCurve* _max_fft_plot_curve;
+
+ QwtPlotMarker* _lower_intensity_marker;
+ QwtPlotMarker* _upper_intensity_marker;
+
+ QwtPlotPanner* _panner;
+ QwtPlotZoomer* _zoomer;
+
+ QwtPlotMarker *_markerPeakAmplitude;
+ QwtPlotMarker *_markerNoiseFloorAmplitude;
+
+ double* _dataPoints;
+ double* _xAxisPoints;
+
+ double* _minFFTPoints;
+ double* _maxFFTPoints;
+ int64_t _numPoints;
+
+ double _peakFrequency;
+ double _peakAmplitude;
+
+ double _noiseFloorAmplitude;
+
+ timespec _lastReplot;
+
+ bool _useCenterFrequencyFlag;
+};
+
+#endif /* FREQUENCY_DISPLAY_PLOT_HPP */
diff --git a/gr-qtgui/lib/Makefile.am b/gr-qtgui/lib/Makefile.am
new file mode 100644
index 000000000..f9b5dd03e
--- /dev/null
+++ b/gr-qtgui/lib/Makefile.am
@@ -0,0 +1,108 @@
+#
+# Copyright 2008-2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+EXTRA_DIST += spectrumdisplayform.ui
+
+AM_CPPFLAGS = -I. $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) \
+ $(QT_INCLUDES) $(BOOST_CPPFLAGS) $(WITH_INCLUDES)
+
+# Only include these files in the build if qtgui passes configure checks
+# This is mostly to help make distcheck pass
+QMAKE_SOURCES = \
+ spectrumdisplayform.moc.cc \
+ FrequencyDisplayPlot.moc.cc \
+ TimeDomainDisplayPlot.moc.cc \
+ WaterfallDisplayPlot.moc.cc \
+ ConstellationDisplayPlot.moc.cc \
+ spectrumdisplayform.ui.h
+
+BUILT_SOURCES += $(QMAKE_SOURCES)
+
+# Build the normal library for C++ apps to link against
+lib_LTLIBRARIES = libgnuradio-qtgui.la
+
+# These are the source files that go into the shared library
+libgnuradio_qtgui_la_SOURCES = \
+ FrequencyDisplayPlot.cc \
+ TimeDomainDisplayPlot.cc \
+ WaterfallDisplayPlot.cc \
+ waterfallGlobalData.cc \
+ ConstellationDisplayPlot.cc \
+ spectrumdisplayform.cc \
+ SpectrumGUIClass.cc \
+ spectrumUpdateEvents.cc \
+ plot_waterfall.cc \
+ qtgui_sink_c.cc \
+ qtgui_sink_f.cc
+
+nodist_libgnuradio_qtgui_la_SOURCES=$(QMAKE_SOURCES)
+
+# These headers get installed in ${prefix}/include/gnuradio
+grinclude_HEADERS = \
+ FrequencyDisplayPlot.h \
+ TimeDomainDisplayPlot.h \
+ WaterfallDisplayPlot.h \
+ waterfallGlobalData.h \
+ ConstellationDisplayPlot.h \
+ highResTimeFunctions.h \
+ plot_waterfall.h \
+ spectrumdisplayform.h \
+ SpectrumGUIClass.h \
+ spectrumUpdateEvents.h \
+ qtgui_sink_c.h \
+ qtgui_sink_f.h
+
+#QT_MOC_FLAGS=-DQT_SHARED -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_GUI_LIB -DQT_CORE_LIB
+QT_MOC_FLAGS=-DQT_SHARED -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB
+%.moc.cc : %.h
+ $(QT_MOC_EXEC) $(QT_MOC_FLAGS) -p $(srcdir) $< -o $@
+
+%.ui.h : %.ui
+ $(QT_UIC_EXEC) $< -o $@
+
+# magic flags
+libgnuradio_qtgui_la_LDFLAGS = $(NO_UNDEFINED) $(BOOST_LDFLAGS) $(LTVERSIONFLAGS)
+
+libgnuradio_qtgui_la_LIBADD = \
+ $(GNURADIO_CORE_LA) \
+ $(BOOST_THREAD_LIB) \
+ $(BOOST_DATE_TIME_LIB) \
+ -lstdc++ \
+ $(QT_LIBS)
+
+##############################
+# SWIG interfaces and libraries
+
+TOP_SWIG_IFILES = \
+ qtgui.i
+
+# Install so that they end up available as:
+# import gnuradio.qtgui
+# This ends up at:
+# ${prefix}/lib/python${python_version}/site-packages/gnuradio
+qtgui_pythondir_category = \
+ gnuradio/qtgui
+
+# additional libraries for linking with the SWIG-generated library
+qtgui_la_swig_libadd = \
+ libgnuradio-qtgui.la
diff --git a/gr-qtgui/lib/SpectrumGUIClass.cc b/gr-qtgui/lib/SpectrumGUIClass.cc
new file mode 100644
index 000000000..1a519591d
--- /dev/null
+++ b/gr-qtgui/lib/SpectrumGUIClass.cc
@@ -0,0 +1,452 @@
+#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 = 32768;
+const long SpectrumGUIClass::MIN_FFT_SIZE = 1024;
+
+SpectrumGUIClass::SpectrumGUIClass(const uint64_t maxDataSize,
+ const uint64_t fftSize,
+ const double newCenterFrequency,
+ const double newStartFrequency,
+ const double newStopFrequency)
+{
+ _dataPoints = maxDataSize;
+ if(_dataPoints < 2){
+ _dataPoints = 2;
+ }
+ _lastDataPointCount = _dataPoints;
+
+ _fftSize = fftSize;
+
+ _pendingGUIUpdateEventsCount = 0;
+ _droppedEntriesCount = 0;
+
+ _centerFrequency = newCenterFrequency;
+ _startFrequency = newStartFrequency;
+ _stopFrequency = newStopFrequency;
+
+ _windowType = 5;
+
+ timespec_reset(&_lastGUIUpdateTime);
+
+ _windowOpennedFlag = false;
+ _fftBuffersCreatedFlag = false;
+
+ _powerValue = 1;
+}
+
+SpectrumGUIClass::~SpectrumGUIClass()
+{
+ // We don't need to delete this since as a QWidget, it is supposed to be destroyed
+ // with it's parent. Deleting it causes a segmentation fault, and not deleting it
+ // does not leave any extra memory.
+ //if(GetWindowOpenFlag()){
+ //delete _spectrumDisplayForm;
+ //}
+
+ if(_fftBuffersCreatedFlag){
+ delete[] _fftPoints;
+ delete[] _realTimeDomainPoints;
+ delete[] _imagTimeDomainPoints;
+ }
+}
+
+void
+SpectrumGUIClass::OpenSpectrumWindow(QWidget* parent,
+ const bool frequency, const bool waterfall,
+ const bool time, const bool constellation)
+{
+ d_mutex.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);
+
+ // Toggle Windows on/off
+ _spectrumDisplayForm->ToggleTabFrequency(frequency);
+ _spectrumDisplayForm->ToggleTabWaterfall(waterfall);
+ _spectrumDisplayForm->ToggleTabTime(time);
+ _spectrumDisplayForm->ToggleTabConstellation(constellation);
+
+ _windowOpennedFlag = true;
+
+ _spectrumDisplayForm->setSystem(this, _dataPoints, _fftSize);
+
+ qApp->processEvents();
+ }
+ d_mutex.unlock();
+
+
+ SetDisplayTitle(_title);
+ Reset();
+
+ qApp->postEvent(_spectrumDisplayForm,
+ new QEvent(QEvent::Type(QEvent::User+3)));
+
+ timespec_reset(&_lastGUIUpdateTime);
+
+ // Draw Blank Display
+ UpdateWindow(false, NULL, 0, NULL, 0, NULL, 0, get_highres_clock(), true);
+
+ // Set up the initial frequency axis settings
+ SetFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency);
+
+ // 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()
+{
+ gruel::scoped_lock lock(d_mutex);
+ bool returnFlag = false;
+ returnFlag = _windowOpennedFlag;
+ return returnFlag;
+}
+
+
+void
+SpectrumGUIClass::SetWindowOpenFlag(const bool newFlag)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _windowOpennedFlag = newFlag;
+}
+
+void
+SpectrumGUIClass::SetFrequencyRange(const double centerFreq,
+ const double startFreq,
+ const double stopFreq)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _centerFrequency = centerFreq;
+ _startFrequency = startFreq;
+ _stopFrequency = stopFreq;
+
+ _spectrumDisplayForm->SetFrequencyRange(_centerFrequency,
+ _startFrequency,
+ _stopFrequency);
+}
+
+double
+SpectrumGUIClass::GetStartFrequency()
+{
+ gruel::scoped_lock lock(d_mutex);
+ double returnValue = 0.0;
+ returnValue = _startFrequency;
+ return returnValue;
+}
+
+double
+SpectrumGUIClass::GetStopFrequency()
+{
+ gruel::scoped_lock lock(d_mutex);
+ double returnValue = 0.0;
+ returnValue = _stopFrequency;
+ return returnValue;
+}
+
+double
+SpectrumGUIClass::GetCenterFrequency()
+{
+ gruel::scoped_lock lock(d_mutex);
+ double returnValue = 0.0;
+ returnValue = _centerFrequency;
+ 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 timespec timestamp,
+ const bool lastOfMultipleFFTUpdateFlag)
+{
+ //gruel::scoped_lock lock(d_mutex);
+ 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*_updateTime)) &&
+ (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,
+ 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;
+ }
+ }
+}
+
+float
+SpectrumGUIClass::GetPowerValue()
+{
+ gruel::scoped_lock lock(d_mutex);
+ float returnValue = 0;
+ returnValue = _powerValue;
+ return returnValue;
+}
+
+void
+SpectrumGUIClass::SetPowerValue(const float value)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _powerValue = value;
+}
+
+int
+SpectrumGUIClass::GetWindowType()
+{
+ gruel::scoped_lock lock(d_mutex);
+ int returnValue = 0;
+ returnValue = _windowType;
+ return returnValue;
+}
+
+void
+SpectrumGUIClass::SetWindowType(const int newType)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _windowType = newType;
+}
+
+int
+SpectrumGUIClass::GetFFTSize()
+{
+ int returnValue = 0;
+ returnValue = _fftSize;
+ return returnValue;
+}
+
+int
+SpectrumGUIClass::GetFFTSizeIndex()
+{
+ gruel::scoped_lock lock(d_mutex);
+ 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)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _fftSize = newSize;
+}
+
+timespec
+SpectrumGUIClass::GetLastGUIUpdateTime()
+{
+ gruel::scoped_lock lock(d_mutex);
+ timespec returnValue;
+ returnValue = _lastGUIUpdateTime;
+ return returnValue;
+}
+
+void
+SpectrumGUIClass::SetLastGUIUpdateTime(const timespec newTime)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _lastGUIUpdateTime = newTime;
+}
+
+unsigned int
+SpectrumGUIClass::GetPendingGUIUpdateEvents()
+{
+ gruel::scoped_lock lock(d_mutex);
+ unsigned int returnValue = 0;
+ returnValue = _pendingGUIUpdateEventsCount;
+ return returnValue;
+}
+
+void
+SpectrumGUIClass::IncrementPendingGUIUpdateEvents()
+{
+ gruel::scoped_lock lock(d_mutex);
+ _pendingGUIUpdateEventsCount++;
+}
+
+void
+SpectrumGUIClass::DecrementPendingGUIUpdateEvents()
+{
+ gruel::scoped_lock lock(d_mutex);
+ if(_pendingGUIUpdateEventsCount > 0){
+ _pendingGUIUpdateEventsCount--;
+ }
+}
+
+void
+SpectrumGUIClass::ResetPendingGUIUpdateEvents()
+{
+ gruel::scoped_lock lock(d_mutex);
+ _pendingGUIUpdateEventsCount = 0;
+}
+
+
+QWidget*
+SpectrumGUIClass::qwidget()
+{
+ gruel::scoped_lock lock(d_mutex);
+ return (QWidget*)_spectrumDisplayForm;
+}
+
+void
+SpectrumGUIClass::SetTimeDomainAxis(double min, double max)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _spectrumDisplayForm->SetTimeDomainAxis(min, max);
+}
+
+void
+SpectrumGUIClass::SetConstellationAxis(double xmin, double xmax,
+ double ymin, double ymax)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _spectrumDisplayForm->SetConstellationAxis(xmin, xmax, ymin, ymax);
+}
+
+void
+SpectrumGUIClass::SetConstellationPenSize(int size)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _spectrumDisplayForm->SetConstellationPenSize(size);
+}
+
+
+void
+SpectrumGUIClass::SetFrequencyAxis(double min, double max)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _spectrumDisplayForm->SetFrequencyAxis(min, max);
+}
+
+void
+SpectrumGUIClass::SetUpdateTime(double t)
+{
+ gruel::scoped_lock lock(d_mutex);
+ _updateTime = t;
+ _spectrumDisplayForm->SetUpdateTime(_updateTime);
+}
+
+
+#endif /* SPECTRUM_GUI_CLASS_CPP */
diff --git a/gr-qtgui/lib/SpectrumGUIClass.h b/gr-qtgui/lib/SpectrumGUIClass.h
new file mode 100644
index 000000000..48f45a0f5
--- /dev/null
+++ b/gr-qtgui/lib/SpectrumGUIClass.h
@@ -0,0 +1,108 @@
+#ifndef SPECTRUM_GUI_CLASS_HPP
+#define SPECTRUM_GUI_CLASS_HPP
+
+#include <gruel/thread.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qlabel.h>
+#include <qslider.h>
+#include <spectrumUpdateEvents.h>
+
+class SpectrumDisplayForm;
+#include <spectrumdisplayform.h>
+
+#include <cmath>
+
+#include <complex>
+#include <vector>
+#include <string>
+
+class SpectrumGUIClass
+{
+public:
+ SpectrumGUIClass(const uint64_t maxDataSize, const uint64_t fftSize,
+ const double newCenterFrequency,
+ const double newStartFrequency,
+ const double newStopFrequency);
+ ~SpectrumGUIClass();
+ void Reset();
+
+ void OpenSpectrumWindow(QWidget*,
+ const bool frequency=true, const bool waterfall=true,
+ const bool time=true, const bool constellation=true);
+ void SetDisplayTitle(const std::string);
+
+ bool GetWindowOpenFlag();
+ void SetWindowOpenFlag(const bool);
+
+ void SetFrequencyRange(const double, const double, const double);
+ double GetStartFrequency();
+ double GetStopFrequency();
+ double GetCenterFrequency();
+
+ void UpdateWindow(const bool, const std::complex<float>*,
+ const uint64_t, const float*,
+ const uint64_t, const float*,
+ const uint64_t,
+ const timespec, const bool);
+
+ float GetPowerValue();
+ void SetPowerValue(const float);
+
+ int GetWindowType();
+ void SetWindowType(const int);
+
+ int GetFFTSize();
+ int GetFFTSizeIndex();
+ void SetFFTSize(const int);
+
+ timespec GetLastGUIUpdateTime();
+ void SetLastGUIUpdateTime(const timespec);
+
+ unsigned int GetPendingGUIUpdateEvents();
+ void IncrementPendingGUIUpdateEvents();
+ void DecrementPendingGUIUpdateEvents();
+ void ResetPendingGUIUpdateEvents();
+
+ static const long MAX_FFT_SIZE;
+ static const long MIN_FFT_SIZE;
+
+ QWidget* qwidget();
+
+ void SetTimeDomainAxis(double min, double max);
+ void SetConstellationAxis(double xmin, double xmax,
+ double ymin, double ymax);
+ void SetConstellationPenSize(int size);
+ void SetFrequencyAxis(double min, double max);
+
+ void SetUpdateTime(double t);
+
+protected:
+
+private:
+
+ gruel::mutex d_mutex;
+ 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;
+ double _updateTime;
+
+ SpectrumDisplayForm* _spectrumDisplayForm;
+
+ std::complex<float>* _fftPoints;
+ double* _realTimeDomainPoints;
+ double* _imagTimeDomainPoints;
+};
+
+#endif /* SPECTRUM_GUI_CLASS_HPP */
diff --git a/gr-qtgui/lib/TimeDomainDisplayPlot.cc b/gr-qtgui/lib/TimeDomainDisplayPlot.cc
new file mode 100644
index 000000000..da28d0304
--- /dev/null
+++ b/gr-qtgui/lib/TimeDomainDisplayPlot.cc
@@ -0,0 +1,272 @@
+#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 TimePrecisionClass
+{
+public:
+ TimePrecisionClass(const int timePrecision)
+ {
+ _timePrecision = timePrecision;
+ }
+
+ virtual ~TimePrecisionClass()
+ {
+ }
+
+ virtual unsigned int GetTimePrecision() const
+ {
+ return _timePrecision;
+ }
+
+ virtual void SetTimePrecision(const unsigned int newPrecision)
+ {
+ _timePrecision = newPrecision;
+ }
+protected:
+ unsigned int _timePrecision;
+};
+
+
+class TimeDomainDisplayZoomer: public QwtPlotZoomer, public TimePrecisionClass
+{
+public:
+ TimeDomainDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int timePrecision)
+ : QwtPlotZoomer(canvas),TimePrecisionClass(timePrecision)
+ {
+ setTrackerMode(QwtPicker::AlwaysOn);
+ }
+
+ virtual ~TimeDomainDisplayZoomer(){
+
+ }
+
+ virtual void updateTrackerText(){
+ updateDisplay();
+ }
+
+ void SetUnitType(const std::string &type)
+ {
+ _unitType = type;
+ }
+
+protected:
+ using QwtPlotZoomer::trackerText;
+ virtual QwtText trackerText( const QwtDoublePoint& p ) const
+ {
+ QwtText t(QString("%1 %2, %3 V").arg(p.x(), 0, 'f', GetTimePrecision()).
+ arg(_unitType.c_str()).
+ arg(p.y(), 0, 'f', 4));
+
+ return t;
+ }
+
+private:
+ std::string _unitType;
+};
+
+TimeDomainDisplayPlot::TimeDomainDisplayPlot(QWidget* parent):QwtPlot(parent)
+{
+ timespec_reset(&_lastReplot);
+
+ resize(parent->width(), parent->height());
+
+ _numPoints = 1024;
+ _realDataPoints = new double[_numPoints];
+ _imagDataPoints = new double[_numPoints];
+ _xAxisPoints = new double[_numPoints];
+
+ _zoomer = new TimeDomainDisplayZoomer(canvas(), 0);
+
+ // 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);
+ set_xaxis(0, _numPoints);
+ setAxisTitle(QwtPlot::xBottom, "Time (sec)");
+
+ setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
+ set_yaxis(-2.0, 2.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));
+
+ _sampleRate = 1;
+ _resetXAxisPoints();
+
+#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::set_yaxis(double min, double max)
+{
+ setAxisScale(QwtPlot::yLeft, min, max);
+ _zoomer->setZoomBase();
+}
+
+void
+TimeDomainDisplayPlot::set_xaxis(double min, double max)
+{
+ setAxisScale(QwtPlot::xBottom, min, max);
+ _zoomer->setZoomBase();
+}
+
+
+void TimeDomainDisplayPlot::replot()
+{
+ QwtPlot::replot();
+}
+
+void
+TimeDomainDisplayPlot::resizeSlot( QSize *s )
+{
+ resize(s->width(), s->height());
+}
+
+void TimeDomainDisplayPlot::PlotNewData(const double* realDataPoints,
+ const double* imagDataPoints,
+ const int64_t numDataPoints,
+ const double timeInterval)
+{
+ if((numDataPoints > 0) &&
+ (diff_timespec(get_highres_clock(), _lastReplot) > timeInterval)) {
+
+ 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);
+
+ set_xaxis(0, numDataPoints);
+
+ _resetXAxisPoints();
+ }
+
+ memcpy(_realDataPoints, realDataPoints, numDataPoints*sizeof(double));
+ memcpy(_imagDataPoints, imagDataPoints, numDataPoints*sizeof(double));
+
+ _lastReplot = get_highres_clock();
+ }
+}
+
+void TimeDomainDisplayPlot::SetImaginaryDataVisible(const bool visibleFlag)
+{
+ _imag_plot_curve->setVisible(visibleFlag);
+}
+
+void TimeDomainDisplayPlot::_resetXAxisPoints()
+{
+ double delt = 1.0/_sampleRate;
+ for(long loc = 0; loc < _numPoints; loc++){
+ _xAxisPoints[loc] = loc*delt;
+ }
+ setAxisScale(QwtPlot::xBottom, 0, _numPoints*delt);
+
+ // Set up zoomer base for maximum unzoom x-axis
+ // and reset to maximum unzoom level
+ QwtDoubleRect zbase = _zoomer->zoomBase();
+ zbase.setLeft(0);
+ zbase.setRight(_numPoints*delt);
+ _zoomer->zoom(zbase);
+ _zoomer->setZoomBase(zbase);
+ _zoomer->zoom(0);
+}
+
+void TimeDomainDisplayPlot::LegendEntryChecked(QwtPlotItem* plotItem, bool on)
+{
+ plotItem->setVisible(!on);
+}
+
+void
+TimeDomainDisplayPlot::SetSampleRate(double sr, double units,
+ const std::string &strunits)
+{
+ double newsr = sr/units;
+ if(newsr != _sampleRate) {
+ _sampleRate = sr/units;
+ _resetXAxisPoints();
+
+ // While we could change the displayed sigfigs based on the unit being
+ // displayed, I think it looks better by just setting it to 4 regardless.
+ //double display_units = ceil(log10(units)/2.0);
+ double display_units = 4;
+ setAxisTitle(QwtPlot::xBottom, QString("Time (%1)").arg(strunits.c_str()));
+ ((TimeDomainDisplayZoomer*)_zoomer)->SetTimePrecision(display_units);
+ ((TimeDomainDisplayZoomer*)_zoomer)->SetUnitType(strunits);
+ }
+}
+
+#endif /* TIME_DOMAIN_DISPLAY_PLOT_C */
diff --git a/gr-qtgui/lib/TimeDomainDisplayPlot.h b/gr-qtgui/lib/TimeDomainDisplayPlot.h
new file mode 100644
index 000000000..952b5c8cf
--- /dev/null
+++ b/gr-qtgui/lib/TimeDomainDisplayPlot.h
@@ -0,0 +1,65 @@
+#ifndef TIME_DOMAIN_DISPLAY_PLOT_HPP
+#define TIME_DOMAIN_DISPLAY_PLOT_HPP
+
+#include <stdint.h>
+#include <cstdio>
+#include <qwt_plot.h>
+#include <qwt_painter.h>
+#include <qwt_plot_canvas.h>
+#include <qwt_plot_curve.h>
+#include <qwt_scale_engine.h>
+#include <qwt_scale_widget.h>
+#include <qwt_plot_zoomer.h>
+#include <qwt_plot_panner.h>
+#include <qwt_plot_marker.h>
+#include <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, const double timeInterval);
+
+ void SetImaginaryDataVisible(const bool);
+
+ virtual void replot();
+
+ void set_yaxis(double min, double max);
+ void set_xaxis(double min, double max);
+
+public slots:
+ void resizeSlot( QSize *s );
+ void SetSampleRate(double sr, double units,
+ const std::string &strunits);
+
+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;
+
+ double _sampleRate;
+
+ timespec _lastReplot;
+
+ int64_t _numPoints;
+};
+
+#endif /* TIME_DOMAIN_DISPLAY_PLOT_HPP */
diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.cc b/gr-qtgui/lib/WaterfallDisplayPlot.cc
new file mode 100644
index 000000000..08a71c023
--- /dev/null
+++ b/gr-qtgui/lib/WaterfallDisplayPlot.cc
@@ -0,0 +1,547 @@
+#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>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+namespace pt = boost::posix_time;
+
+class FreqOffsetAndPrecisionClass
+{
+public:
+ FreqOffsetAndPrecisionClass(const int freqPrecision)
+ {
+ _frequencyPrecision = freqPrecision;
+ _centerFrequency = 0;
+ }
+
+ virtual ~FreqOffsetAndPrecisionClass()
+ {
+ }
+
+ virtual unsigned int GetFrequencyPrecision() const
+ {
+ return _frequencyPrecision;
+ }
+
+ virtual void SetFrequencyPrecision(const unsigned int newPrecision)
+ {
+ _frequencyPrecision = newPrecision;
+ }
+
+ virtual double GetCenterFrequency() const
+ {
+ return _centerFrequency;
+ }
+
+ virtual void SetCenterFrequency(const double newFreq)
+ {
+ _centerFrequency = newFreq;
+ }
+
+protected:
+ unsigned int _frequencyPrecision;
+ double _centerFrequency;
+
+private:
+
+};
+
+class WaterfallFreqDisplayScaleDraw: public QwtScaleDraw, public FreqOffsetAndPrecisionClass{
+public:
+ WaterfallFreqDisplayScaleDraw(const unsigned int precision)
+ : QwtScaleDraw(), FreqOffsetAndPrecisionClass(precision)
+ {
+ }
+
+ virtual ~WaterfallFreqDisplayScaleDraw()
+ {
+ }
+
+ QwtText label(double value) const
+ {
+ return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision());
+ }
+
+ virtual void initiateUpdate()
+ {
+ invalidateCache();
+ }
+
+protected:
+
+private:
+
+};
+
+class TimeScaleData
+{
+public:
+ TimeScaleData()
+ {
+ 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
+ {
+ timespec lineTime = timespec_add(GetZeroTime(), (-value) * GetSecondsPerLine());
+ std::string time_str = pt::to_simple_string(pt::from_time_t(lineTime.tv_sec));
+
+ // lops off the YYYY-mmm-DD part of the string
+ size_t ind = time_str.find(" ");
+ if(ind != std::string::npos)
+ time_str = time_str.substr(ind);
+ return QwtText(QString("").sprintf("%s.%03ld", time_str.c_str(), lineTime.tv_nsec/1000000));
+ }
+
+ 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();
+ }
+
+ void SetUnitType(const std::string &type)
+ {
+ _unitType = type;
+ }
+
+protected:
+ using QwtPlotZoomer::trackerText;
+ virtual QwtText trackerText( const QwtDoublePoint& p ) const
+ {
+ timespec lineTime = timespec_add(GetZeroTime(), (-p.y()) * GetSecondsPerLine());
+ std::string time_str = pt::to_simple_string(pt::from_time_t(lineTime.tv_sec));
+
+ // lops off the YYYY-mmm-DD part of the string
+ size_t ind = time_str.find(" ");
+ if(ind != std::string::npos)
+ time_str = time_str.substr(ind);
+ QString yLabel(QString("").sprintf("%s.%03ld", time_str.c_str(), lineTime.tv_nsec/1000000));
+
+ QwtText t(QString("%1 %2, %3").
+ arg(p.x(), 0, 'f', GetFrequencyPrecision()).
+ arg(_unitType.c_str()).arg(yLabel));
+ return t;
+ }
+
+private:
+ std::string _unitType;
+};
+
+
+WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent)
+ : QwtPlot(parent)
+{
+ _zoomer = NULL;
+ _startFrequency = 0;
+ _stopFrequency = 4000;
+
+ resize(parent->width(), parent->height());
+ _numPoints = 1024;
+
+ _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;
+ delete d_spectrogram;
+}
+
+void
+WaterfallDisplayPlot::Reset()
+{
+ _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints);
+ _waterfallData->Reset();
+
+ setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
+
+ // Load up the new base zoom settings
+ QwtDoubleRect newSize = _zoomer->zoomBase();
+ newSize.setLeft(_startFrequency);
+ newSize.setWidth(_stopFrequency-_startFrequency);
+ _zoomer->zoom(newSize);
+ _zoomer->setZoomBase(newSize);
+ _zoomer->zoom(0);
+}
+
+void
+WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq,
+ const double constStopFreq,
+ const double constCenterFreq,
+ const bool useCenterFrequencyFlag,
+ const double units, const std::string &strunits)
+{
+ double startFreq = constStartFreq / units;
+ double stopFreq = constStopFreq / units;
+ double centerFreq = constCenterFreq / units;
+
+ _useCenterFrequencyFlag = useCenterFrequencyFlag;
+
+ if(_useCenterFrequencyFlag){
+ startFreq = (startFreq + centerFreq);
+ stopFreq = (stopFreq + centerFreq);
+ }
+
+ bool reset = false;
+ if((startFreq != _startFrequency) || (stopFreq != _stopFrequency))
+ reset = true;
+
+ if(stopFreq > startFreq) {
+ _startFrequency = startFreq;
+ _stopFrequency = stopFreq;
+
+ if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){
+ double display_units = ceil(log10(units)/2.0);
+ setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(display_units));
+ setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str()));
+
+ if(reset) {
+ Reset();
+ }
+
+ ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision(display_units);
+ ((WaterfallZoomer*)_zoomer)->SetUnitType(strunits);
+ }
+ }
+}
+
+
+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();
+ }
+
+ if(diff_timespec(get_highres_clock(), _lastReplot) > timePerFFT) {
+ //FIXME: We may want to average the data between these updates to smooth display
+ _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);
+
+ d_spectrogram->invalidateCache();
+ d_spectrogram->itemChanged();
+
+ 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()
+{
+ 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();
+}
+
+void
+WaterfallDisplayPlot::resizeSlot( QSize *s )
+{
+ resize(s->width(), s->height());
+}
+
+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/lib/WaterfallDisplayPlot.h b/gr-qtgui/lib/WaterfallDisplayPlot.h
new file mode 100644
index 000000000..6b4e978bb
--- /dev/null
+++ b/gr-qtgui/lib/WaterfallDisplayPlot.h
@@ -0,0 +1,84 @@
+#ifndef WATERFALL_DISPLAY_PLOT_HPP
+#define WATERFALL_DISPLAY_PLOT_HPP
+
+#include <stdint.h>
+#include <cstdio>
+#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,
+ const double units=1000.0,
+ const std::string &strunits = "kHz");
+ 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;
+
+ enum{
+ INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR = 0,
+ INTENSITY_COLOR_MAP_TYPE_WHITE_HOT = 1,
+ INTENSITY_COLOR_MAP_TYPE_BLACK_HOT = 2,
+ INTENSITY_COLOR_MAP_TYPE_INCANDESCENT = 3,
+ INTENSITY_COLOR_MAP_TYPE_USER_DEFINED = 4
+ };
+
+public slots:
+ void resizeSlot( QSize *s );
+
+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;
+
+ bool _useCenterFrequencyFlag;
+
+ int64_t _numPoints;
+
+ int _intensityColorMapType;
+ QColor _userDefinedLowIntensityColor;
+ QColor _userDefinedHighIntensityColor;
+};
+
+#endif /* WATERFALL_DISPLAY_PLOT_HPP */
diff --git a/gr-qtgui/lib/highResTimeFunctions.h b/gr-qtgui/lib/highResTimeFunctions.h
new file mode 100644
index 000000000..251bbad8b
--- /dev/null
+++ b/gr-qtgui/lib/highResTimeFunctions.h
@@ -0,0 +1,299 @@
+#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);
+}
+
+
+#ifdef CLOCK_REALTIME
+// If we can use clock_gettime, use it;
+// otherwise, use gettimeofday
+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;
+ }
+}
+
+#else
+
+// Trick timer functions into thinking it has an nsec timer
+// but only use the low resolution (usec) timer.
+static inline void
+get_highres_clock(struct timespec* ret)
+{
+ timeval lowResTime;
+ gettimeofday(&lowResTime, NULL);
+ ret->tv_sec = lowResTime.tv_sec;
+ ret->tv_nsec = lowResTime.tv_usec*1000;
+}
+#endif
+
+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/lib/plot_waterfall.cc b/gr-qtgui/lib/plot_waterfall.cc
new file mode 100644
index 000000000..2b1447e03
--- /dev/null
+++ b/gr-qtgui/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/lib/plot_waterfall.h b/gr-qtgui/lib/plot_waterfall.h
new file mode 100644
index 000000000..a11461611
--- /dev/null
+++ b/gr-qtgui/lib/plot_waterfall.h
@@ -0,0 +1,52 @@
+#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.
+
+ \sa QwtRasterData, QwtColorMap
+*/
+
+class 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/lib/qtgui_sink_c.cc b/gr-qtgui/lib/qtgui_sink_c.cc
new file mode 100644
index 000000000..7b40dd01d
--- /dev/null
+++ b/gr-qtgui/lib/qtgui_sink_c.cc
@@ -0,0 +1,298 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qtgui_sink_c.h>
+#include <gr_io_signature.h>
+#include <string.h>
+
+#include <QTimer>
+
+qtgui_sink_c_sptr
+qtgui_make_sink_c (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent)
+{
+ return gnuradio::get_initial_sptr(new qtgui_sink_c (fftsize, wintype,
+ fc, bw, name,
+ plotfreq, plotwaterfall,
+ plottime, plotconst,
+ parent));
+}
+
+qtgui_sink_c::qtgui_sink_c (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent)
+ : gr_block ("sink_c",
+ gr_make_io_signature (1, -1, sizeof(gr_complex)),
+ gr_make_io_signature (0, 0, 0)),
+ d_fftsize(fftsize),
+ d_wintype((gr_firdes::win_type)(wintype)),
+ d_center_freq(fc), d_bandwidth(bw), d_name(name),
+ d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall),
+ d_plottime(plottime), d_plotconst(plotconst),
+ d_parent(parent)
+{
+ d_main_gui = NULL;
+
+ // Perform fftshift operation;
+ // this is usually desired when plotting
+ d_shift = true;
+
+ d_fft = new gri_fft_complex (d_fftsize, true);
+
+ d_index = 0;
+ d_residbuf = new gr_complex[d_fftsize];
+
+ buildwindow();
+
+ initialize();
+}
+
+qtgui_sink_c::~qtgui_sink_c()
+{
+ delete d_main_gui;
+ delete [] d_residbuf;
+ delete d_fft;
+}
+
+void
+qtgui_sink_c::forecast(int noutput_items, gr_vector_int &ninput_items_required)
+{
+ unsigned int ninputs = ninput_items_required.size();
+ for (unsigned int i = 0; i < ninputs; i++) {
+ ninput_items_required[i] = std::min(d_fftsize, 8191);
+ }
+}
+
+void
+qtgui_sink_c::initialize()
+{
+ if(qApp != NULL) {
+ d_qApplication = qApp;
+ }
+ else {
+ int argc=0;
+ char **argv = NULL;
+ d_qApplication = new QApplication(argc, argv);
+ }
+
+ if(d_center_freq < 0) {
+ throw std::runtime_error("qtgui_sink_c: Received bad center frequency.\n");
+ }
+
+ uint64_t maxBufferSize = 32768;
+ d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize,
+ d_center_freq,
+ -d_bandwidth/2.0,
+ d_bandwidth/2.0);
+
+ d_main_gui->SetDisplayTitle(d_name);
+ d_main_gui->SetFFTSize(d_fftsize);
+ d_main_gui->SetWindowType((int)d_wintype);
+
+ d_main_gui->OpenSpectrumWindow(d_parent,
+ d_plotfreq, d_plotwaterfall,
+ d_plottime, d_plotconst);
+
+ // initialize update time to 10 times a second
+ set_update_time(0.1);
+}
+
+
+void
+qtgui_sink_c::exec_()
+{
+ d_qApplication->exec();
+}
+
+QWidget*
+qtgui_sink_c::qwidget()
+{
+ return d_main_gui->qwidget();
+}
+
+PyObject*
+qtgui_sink_c::pyqwidget()
+{
+ PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget());
+ PyObject *retarg = Py_BuildValue("N", w);
+ return retarg;
+}
+
+void
+qtgui_sink_c::set_frequency_range(const double centerfreq,
+ const double bandwidth)
+{
+ d_center_freq = centerfreq;
+ d_bandwidth = bandwidth;
+ d_main_gui->SetFrequencyRange(d_center_freq,
+ -d_bandwidth/2.0,
+ d_bandwidth/2.0);
+}
+
+void
+qtgui_sink_c::set_time_domain_axis(double min, double max)
+{
+ d_main_gui->SetTimeDomainAxis(min, max);
+}
+
+void
+qtgui_sink_c::set_constellation_axis(double xmin, double xmax,
+ double ymin, double ymax)
+{
+ d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax);
+}
+
+void
+qtgui_sink_c::set_constellation_pen_size(int size)
+{
+ d_main_gui->SetConstellationPenSize(size);
+}
+
+
+void
+qtgui_sink_c::set_frequency_axis(double min, double max)
+{
+ d_main_gui->SetFrequencyAxis(min, max);
+}
+
+void
+qtgui_sink_c::set_update_time(double t)
+{
+ d_update_time = t;
+ d_main_gui->SetUpdateTime(d_update_time);
+}
+
+void
+qtgui_sink_c::fft(const gr_complex *data_in, int size)
+{
+ if (d_window.size()) {
+ gr_complex *dst = d_fft->get_inbuf();
+ int i;
+ for (i = 0; i < size; i++) // apply window
+ dst[i] = data_in[i] * d_window[i];
+ }
+ else {
+ memcpy (d_fft->get_inbuf(), data_in, sizeof(gr_complex)*size);
+ }
+
+ d_fft->execute (); // compute the fft
+}
+
+void
+qtgui_sink_c::windowreset()
+{
+ gr_firdes::win_type newwintype = (gr_firdes::win_type)d_main_gui->GetWindowType();
+ if(d_wintype != newwintype) {
+ d_wintype = newwintype;
+ buildwindow();
+ }
+}
+
+void
+qtgui_sink_c::buildwindow()
+{
+ d_window.clear();
+ if(d_wintype != 0) {
+ d_window = gr_firdes::window(d_wintype, d_fftsize, 6.76);
+ }
+}
+
+void
+qtgui_sink_c::fftresize()
+{
+ int newfftsize = d_main_gui->GetFFTSize();
+
+ if(newfftsize != d_fftsize) {
+
+ // Resize residbuf and replace data
+ delete [] d_residbuf;
+ d_residbuf = new gr_complex[newfftsize];
+
+ // Set new fft size and reset buffer index
+ // (throws away any currently held data, but who cares?)
+ d_fftsize = newfftsize;
+ d_index = 0;
+
+ // Reset window to reflect new size
+ buildwindow();
+
+ // Reset FFTW plan for new size
+ delete d_fft;
+ d_fft = new gri_fft_complex (d_fftsize, true);
+ }
+}
+
+
+int
+qtgui_sink_c::general_work (int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int j=0;
+ const gr_complex *in = (const gr_complex*)input_items[0];
+
+ // Update the FFT size from the application
+ fftresize();
+ windowreset();
+
+ for(int i=0; i < noutput_items; i+=d_fftsize) {
+ unsigned int datasize = noutput_items - i;
+ unsigned int resid = d_fftsize-d_index;
+
+ // If we have enough input for one full FFT, do it
+ if(datasize >= resid) {
+ const timespec currentTime = get_highres_clock();
+
+ // Fill up residbuf with d_fftsize number of items
+ memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*resid);
+ d_index = 0;
+
+ j += resid;
+ fft(d_residbuf, d_fftsize);
+
+ d_main_gui->UpdateWindow(true, d_fft->get_outbuf(), d_fftsize,
+ NULL, 0, (float*)d_residbuf, d_fftsize,
+ currentTime, true);
+ }
+ // Otherwise, copy what we received into the residbuf for next time
+ else {
+ memcpy(d_residbuf+d_index, &in[j], sizeof(gr_complex)*datasize);
+ d_index += datasize;
+ j += datasize;
+ }
+ }
+
+ consume_each(j);
+ return j;
+}
diff --git a/gr-qtgui/lib/qtgui_sink_c.h b/gr-qtgui/lib/qtgui_sink_c.h
new file mode 100644
index 000000000..1c9561704
--- /dev/null
+++ b/gr-qtgui/lib/qtgui_sink_c.h
@@ -0,0 +1,116 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2008,2009,2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_QTGUI_SINK_C_H
+#define INCLUDED_QTGUI_SINK_C_H
+
+#include <Python.h>
+#include <gr_block.h>
+#include <gr_firdes.h>
+#include <gri_fft.h>
+#include <qapplication.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,
+ double fc=0, double bandwidth=1.0,
+ const std::string &name="Spectrum Display",
+ bool plotfreq=true, bool plotwaterfall=true,
+ bool plottime=true, bool plotconst=true,
+ QWidget *parent=NULL);
+
+class qtgui_sink_c : public gr_block
+{
+private:
+ friend qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent);
+ qtgui_sink_c (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent);
+
+ void forecast(int noutput_items, gr_vector_int &ninput_items_required);
+
+ void initialize();
+
+ int d_fftsize;
+ gr_firdes::win_type d_wintype;
+ std::vector<float> d_window;
+ double d_center_freq;
+ double d_bandwidth;
+ std::string d_name;
+
+ //gruel::mutex d_mutex;
+
+ bool d_shift;
+ gri_fft_complex *d_fft;
+
+ int d_index;
+ gr_complex *d_residbuf;
+
+ bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst;
+
+ double d_update_time;
+
+ QWidget *d_parent;
+ SpectrumGUIClass *d_main_gui;
+
+ void windowreset();
+ void buildwindow();
+ void fftresize();
+ void fft(const gr_complex *data_in, int size);
+
+public:
+ ~qtgui_sink_c();
+ void exec_();
+ QWidget* qwidget();
+ PyObject* pyqwidget();
+
+ void set_frequency_range(const double centerfreq,
+ const double bandwidth);
+
+ void set_time_domain_axis(double min, double max);
+ void set_constellation_axis(double xmin, double xmax,
+ double ymin, double ymax);
+ void set_constellation_pen_size(int size);
+ void set_frequency_axis(double min, double max);
+
+ void set_update_time(double t);
+
+ QApplication *d_qApplication;
+ //qtgui_obj *d_object;
+
+ 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/lib/qtgui_sink_f.cc b/gr-qtgui/lib/qtgui_sink_f.cc
new file mode 100644
index 000000000..3312b9006
--- /dev/null
+++ b/gr-qtgui/lib/qtgui_sink_f.cc
@@ -0,0 +1,294 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2008,2009,2010,2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qtgui_sink_f.h>
+#include <gr_io_signature.h>
+#include <string.h>
+
+#include <QTimer>
+
+qtgui_sink_f_sptr
+qtgui_make_sink_f (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent)
+{
+ return gnuradio::get_initial_sptr(new qtgui_sink_f (fftsize, wintype,
+ fc, bw, name,
+ plotfreq, plotwaterfall,
+ plottime, plotconst,
+ parent));
+}
+
+qtgui_sink_f::qtgui_sink_f (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent)
+ : gr_block ("sink_f",
+ gr_make_io_signature (1, 1, sizeof(float)),
+ gr_make_io_signature (0, 0, 0)),
+ d_fftsize(fftsize),
+ d_wintype((gr_firdes::win_type)(wintype)),
+ d_center_freq(fc), d_bandwidth(bw), d_name(name),
+ d_plotfreq(plotfreq), d_plotwaterfall(plotwaterfall),
+ d_plottime(plottime), d_plotconst(plotconst),
+ d_parent(parent)
+{
+ d_main_gui = NULL;
+
+ // Perform fftshift operation;
+ // this is usually desired when plotting
+ d_shift = true;
+
+ d_fft = new gri_fft_complex (d_fftsize, true);
+
+ d_index = 0;
+ d_residbuf = new float[d_fftsize];
+
+ buildwindow();
+
+ initialize();
+}
+
+qtgui_sink_f::~qtgui_sink_f()
+{
+ delete d_main_gui;
+ delete [] d_residbuf;
+ delete d_fft;
+}
+
+void
+qtgui_sink_f::forecast(int noutput_items, gr_vector_int &ninput_items_required)
+{
+ unsigned int ninputs = ninput_items_required.size();
+ for (unsigned int i = 0; i < ninputs; i++) {
+ ninput_items_required[i] = std::min(d_fftsize, 8191);
+ }
+}
+
+void
+qtgui_sink_f::initialize()
+{
+ if(qApp != NULL) {
+ d_qApplication = qApp;
+ }
+ else {
+ int argc;
+ char **argv = NULL;
+ d_qApplication = new QApplication(argc, argv);
+ }
+
+
+ uint64_t maxBufferSize = 32768;
+ d_main_gui = new SpectrumGUIClass(maxBufferSize, d_fftsize,
+ d_center_freq,
+ -d_bandwidth/2.0,
+ d_bandwidth/2.0);
+ d_main_gui->SetDisplayTitle(d_name);
+ d_main_gui->SetFFTSize(d_fftsize);
+ d_main_gui->SetWindowType((int)d_wintype);
+
+ d_main_gui->OpenSpectrumWindow(d_parent,
+ d_plotfreq, d_plotwaterfall,
+ d_plottime, d_plotconst);
+
+ // initialize update time to 10 times a second
+ set_update_time(0.1);
+}
+
+void
+qtgui_sink_f::exec_()
+{
+ d_qApplication->exec();
+}
+
+QWidget*
+qtgui_sink_f::qwidget()
+{
+ return d_main_gui->qwidget();
+}
+
+PyObject*
+qtgui_sink_f::pyqwidget()
+{
+ PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui->qwidget());
+ PyObject *retarg = Py_BuildValue("N", w);
+ return retarg;
+}
+
+void
+qtgui_sink_f::set_frequency_range(const double centerfreq,
+ const double bandwidth)
+{
+ d_center_freq = centerfreq;
+ d_bandwidth = bandwidth;
+ d_main_gui->SetFrequencyRange(d_center_freq,
+ -d_bandwidth/2.0,
+ d_bandwidth/2.0);
+}
+
+void
+qtgui_sink_f::set_time_domain_axis(double min, double max)
+{
+ d_main_gui->SetTimeDomainAxis(min, max);
+}
+
+void
+qtgui_sink_f::set_constellation_axis(double xmin, double xmax,
+ double ymin, double ymax)
+{
+ d_main_gui->SetConstellationAxis(xmin, xmax, ymin, ymax);
+}
+
+void
+qtgui_sink_f::set_constellation_pen_size(int size)
+{
+ d_main_gui->SetConstellationPenSize(size);
+}
+
+
+void
+qtgui_sink_f::set_frequency_axis(double min, double max)
+{
+ d_main_gui->SetFrequencyAxis(min, max);
+}
+
+void
+qtgui_sink_f::set_update_time(double t)
+{
+ d_update_time = t;
+ d_main_gui->SetUpdateTime(d_update_time);
+}
+
+void
+qtgui_sink_f::fft(const float *data_in, int size)
+{
+ if (d_window.size()) {
+ gr_complex *dst = d_fft->get_inbuf();
+ for (int i = 0; i < size; i++) // apply window
+ dst[i] = data_in[i] * d_window[i];
+ }
+ else {
+ gr_complex *dst = d_fft->get_inbuf();
+ for (int i = 0; i < size; i++) // float to complex conversion
+ dst[i] = data_in[i];
+ }
+
+ d_fft->execute (); // compute the fft
+}
+
+void
+qtgui_sink_f::windowreset()
+{
+ gr_firdes::win_type newwintype = (gr_firdes::win_type)d_main_gui->GetWindowType();
+ if(d_wintype != newwintype) {
+ d_wintype = newwintype;
+ buildwindow();
+ }
+}
+
+void
+qtgui_sink_f::buildwindow()
+{
+ d_window.clear();
+ if(d_wintype != 0) {
+ d_window = gr_firdes::window(d_wintype, d_fftsize, 6.76);
+ }
+}
+
+void
+qtgui_sink_f::fftresize()
+{
+ int newfftsize = d_main_gui->GetFFTSize();
+
+ if(newfftsize != d_fftsize) {
+
+ // Resize residbuf and replace data
+ delete [] d_residbuf;
+ d_residbuf = new float[newfftsize];
+
+ // Set new fft size and reset buffer index
+ // (throws away any currently held data, but who cares?)
+ d_fftsize = newfftsize;
+ d_index = 0;
+
+ // Reset window to reflect new size
+ buildwindow();
+
+ // Reset FFTW plan for new size
+ delete d_fft;
+ d_fft = new gri_fft_complex (d_fftsize, true);
+ }
+}
+
+
+int
+qtgui_sink_f::general_work (int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int j=0;
+ const float *in = (const float*)input_items[0];
+
+ // Update the FFT size from the application
+ fftresize();
+ windowreset();
+
+ for(int i=0; i < noutput_items; i+=d_fftsize) {
+ unsigned int datasize = noutput_items - i;
+ unsigned int resid = d_fftsize-d_index;
+
+ // If we have enough input for one full FFT, do it
+ if(datasize >= resid) {
+ const timespec currentTime = get_highres_clock();
+
+ // Fill up residbuf with d_fftsize number of items
+ memcpy(d_residbuf+d_index, &in[j], sizeof(float)*resid);
+ d_index = 0;
+
+ j += resid;
+ fft(d_residbuf, d_fftsize);
+
+ d_main_gui->UpdateWindow(true, d_fft->get_outbuf(), d_fftsize,
+ (float*)d_residbuf, d_fftsize, NULL, 0,
+ currentTime, true);
+ }
+ // Otherwise, copy what we received into the residbuf for next time
+ else {
+ memcpy(d_residbuf+d_index, &in[j], sizeof(float)*datasize);
+ d_index += datasize;
+ j += datasize;
+ }
+ }
+
+ consume_each(j);
+ return j;
+}
diff --git a/gr-qtgui/lib/qtgui_sink_f.h b/gr-qtgui/lib/qtgui_sink_f.h
new file mode 100644
index 000000000..518aa3846
--- /dev/null
+++ b/gr-qtgui/lib/qtgui_sink_f.h
@@ -0,0 +1,114 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2008,2009,2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_QTGUI_SINK_F_H
+#define INCLUDED_QTGUI_SINK_F_H
+
+#include <Python.h>
+#include <gr_block.h>
+#include <gr_firdes.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, int wintype,
+ double fc=0, double bw=1.0,
+ const std::string &name="Spectrum Display",
+ bool plotfreq=true, bool plotwaterfall=true,
+ bool plottime=true, bool plotconst=true,
+ QWidget *parent=NULL);
+
+class qtgui_sink_f : public gr_block
+{
+private:
+ friend qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent);
+ qtgui_sink_f (int fftsize, int wintype,
+ double fc, double bw,
+ const std::string &name,
+ bool plotfreq, bool plotwaterfall,
+ bool plottime, bool plotconst,
+ QWidget *parent);
+
+ void forecast(int noutput_items, gr_vector_int &ninput_items_required);
+
+ void initialize();
+
+ int d_fftsize;
+ gr_firdes::win_type d_wintype;
+ std::vector<float> d_window;
+ double d_center_freq;
+ double d_bandwidth;
+ std::string d_name;
+
+ bool d_shift;
+ gri_fft_complex *d_fft;
+
+ int d_index;
+ float *d_residbuf;
+
+ bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst;
+
+ double d_update_time;
+
+ QWidget *d_parent;
+ SpectrumGUIClass *d_main_gui;
+
+ void windowreset();
+ void buildwindow();
+ void fftresize();
+ void fft(const float *data_in, int size);
+
+public:
+ ~qtgui_sink_f();
+ void exec_();
+ QWidget* qwidget();
+ PyObject* pyqwidget();
+
+ void set_frequency_range(const double centerfreq,
+ const double bandwidth);
+
+ void set_time_domain_axis(double min, double max);
+ void set_constellation_axis(double xmin, double xmax,
+ double ymin, double ymax);
+ void set_constellation_pen_size(int size);
+ void set_frequency_axis(double min, double max);
+
+ void set_update_time(double t);
+
+ QApplication *d_qApplication;
+
+ int general_work (int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_QTGUI_SINK_F_H */
diff --git a/gr-qtgui/lib/spectrumUpdateEvents.cc b/gr-qtgui/lib/spectrumUpdateEvents.cc
new file mode 100644
index 000000000..53a205fb7
--- /dev/null
+++ b/gr-qtgui/lib/spectrumUpdateEvents.cc
@@ -0,0 +1,180 @@
+#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 timespec dataTimestamp,
+ const bool repeatDataFlag,
+ const bool lastOfMultipleUpdateFlag,
+ const timespec generatedTimestamp,
+ const int droppedFFTFrames)
+ : QEvent(QEvent::Type(10005))
+{
+ if(numFFTDataPoints < 1) {
+ _numFFTDataPoints = 1;
+ }
+ else {
+ _numFFTDataPoints = numFFTDataPoints;
+ }
+
+ if(numTimeDomainDataPoints < 1) {
+ _numTimeDomainDataPoints = 1;
+ }
+ else {
+ _numTimeDomainDataPoints = numTimeDomainDataPoints;
+ }
+
+ _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;
+ _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;
+}
+
+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/lib/spectrumUpdateEvents.h b/gr-qtgui/lib/spectrumUpdateEvents.h
new file mode 100644
index 000000000..ccc072c3e
--- /dev/null
+++ b/gr-qtgui/lib/spectrumUpdateEvents.h
@@ -0,0 +1,92 @@
+#ifndef SPECTRUM_UPDATE_EVENTS_H
+#define SPECTRUM_UPDATE_EVENTS_H
+
+#include <stdint.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 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;
+ 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;
+ 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/lib/spectrumdisplayform.cc b/gr-qtgui/lib/spectrumdisplayform.cc
new file mode 100644
index 000000000..804dbdd62
--- /dev/null
+++ b/gr-qtgui/lib/spectrumdisplayform.cc
@@ -0,0 +1,691 @@
+#include <cmath>
+#include <QColorDialog>
+#include <QMessageBox>
+#include <spectrumdisplayform.h>
+
+SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent)
+ : QWidget(parent)
+{
+ setupUi(this);
+
+ _systemSpecifiedFlag = false;
+ _intValidator = new QIntValidator(this);
+ _intValidator->setBottom(0);
+ _frequencyDisplayPlot = new FrequencyDisplayPlot(FrequencyPlotDisplayFrame);
+ _waterfallDisplayPlot = new WaterfallDisplayPlot(WaterfallPlotDisplayFrame);
+
+ _timeDomainDisplayPlot = new TimeDomainDisplayPlot(TimeDomainDisplayFrame);
+ _constellationDisplayPlot = new ConstellationDisplayPlot(ConstellationDisplayFrame);
+ _numRealDataPoints = 1024;
+ _realFFTDataPoints = new double[_numRealDataPoints];
+ _averagedValues = new double[_numRealDataPoints];
+ _historyVector = new std::vector<double*>;
+
+ AvgLineEdit->setRange(0, 500); // Set range of Average box value from 0 to 500
+ MinHoldCheckBox_toggled( false );
+ MaxHoldCheckBox_toggled( false );
+
+ WaterfallMaximumIntensityWheel->setRange(-200, 0);
+ WaterfallMaximumIntensityWheel->setTickCnt(50);
+ WaterfallMinimumIntensityWheel->setRange(-200, 0);
+ WaterfallMinimumIntensityWheel->setTickCnt(50);
+ WaterfallMinimumIntensityWheel->setValue(-200);
+
+ _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();
+
+ ToggleTabFrequency(false);
+ ToggleTabWaterfall(false);
+ ToggleTabTime(false);
+ ToggleTabConstellation(false);
+
+ _historyEntry = 0;
+ _historyEntryCount = 0;
+
+ // Create a timer to update plots at the specified rate
+ displayTimer = new QTimer(this);
+ connect(displayTimer, SIGNAL(timeout()), this, SLOT(UpdateGuiTimer()));
+}
+
+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;
+
+ displayTimer->stop();
+ delete displayTimer;
+}
+
+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)
+{
+ //_lastSpectrumEvent = (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 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+numFFTDataPoints/2;
+ double* realFFTDataPointsPtr = _realFFTDataPoints;
+
+ double sumMean = 0.0;
+ double localPeakAmplitude = -HUGE_VAL;
+ double localPeakFrequency = 0.0;
+ const double fftBinSize = (_stopFrequency-_startFrequency) /
+ static_cast<double>(numFFTDataPoints);
+
+ // Run this twice to perform the fftshift operation on the data here as well
+ std::complex<float> scaleFactor = std::complex<float>((float)numFFTDataPoints);
+ for(uint64_t point = 0; point < numFFTDataPoints/2; point++){
+ std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor;
+ *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20);
+
+ if(*realFFTDataPointsPtr > localPeakAmplitude) {
+ localPeakFrequency = static_cast<float>(point) * fftBinSize;
+ localPeakAmplitude = *realFFTDataPointsPtr;
+ }
+ sumMean += *realFFTDataPointsPtr;
+
+ complexDataPointsPtr++;
+ realFFTDataPointsPtr++;
+ }
+
+ // This loop takes the first half of the input data and puts it in the
+ // second half of the plotted data
+ complexDataPointsPtr = complexDataPoints;
+ for(uint64_t point = 0; point < numFFTDataPoints/2; point++){
+ std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor;
+ *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20);
+
+ if(*realFFTDataPointsPtr > localPeakAmplitude) {
+ localPeakFrequency = static_cast<float>(point) * fftBinSize;
+ localPeakAmplitude = *realFFTDataPointsPtr;
+ }
+ sumMean += *realFFTDataPointsPtr;
+
+ complexDataPointsPtr++;
+ realFFTDataPointsPtr++;
+ }
+
+ // Don't update the averaging history if this is repeated data
+ if(!repeatDataFlag){
+ _AverageHistory(_realFFTDataPoints);
+
+ // Only use the local info if we are not repeating data
+ _peakAmplitude = localPeakAmplitude;
+ _peakFrequency = localPeakFrequency;
+
+ // 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){
+ int tabindex = SpectrumTypeTab->currentIndex();
+ if(tabindex == d_plot_fft) {
+ _frequencyDisplayPlot->PlotNewData(_averagedValues, numFFTDataPoints,
+ _noiseFloorAmplitude, _peakFrequency,
+ _peakAmplitude, d_update_time);
+ }
+ if(tabindex == d_plot_time) {
+ _timeDomainDisplayPlot->PlotNewData(realTimeDomainDataPoints,
+ imagTimeDomainDataPoints,
+ numTimeDomainDataPoints,
+ d_update_time);
+ }
+ if(tabindex == d_plot_constellation) {
+ _constellationDisplayPlot->PlotNewData(realTimeDomainDataPoints,
+ imagTimeDomainDataPoints,
+ numTimeDomainDataPoints,
+ d_update_time);
+ }
+
+ // Don't update the repeated data for the waterfall
+ if(!repeatDataFlag){
+ if(tabindex == d_plot_waterfall) {
+ _waterfallDisplayPlot->PlotNewData(_realFFTDataPoints, numFFTDataPoints,
+ d_update_time, dataTimestamp,
+ spectrumUpdateEvent->getDroppedFFTFrames());
+ }
+ }
+
+
+ // Tell the system the GUI has been updated
+ if(_systemSpecifiedFlag){
+ _system->SetLastGUIUpdateTime(generatedTimestamp);
+ _system->DecrementPendingGUIUpdateEvents();
+ }
+ }
+}
+
+void
+SpectrumDisplayForm::resizeEvent( QResizeEvent *e )
+{
+ QSize s;
+ s.setWidth(FrequencyPlotDisplayFrame->width());
+ s.setHeight(FrequencyPlotDisplayFrame->height());
+ emit _frequencyDisplayPlot->resizeSlot(&s);
+
+ s.setWidth(TimeDomainDisplayFrame->width());
+ s.setHeight(TimeDomainDisplayFrame->height());
+ emit _timeDomainDisplayPlot->resizeSlot(&s);
+
+ s.setWidth(WaterfallPlotDisplayFrame->width());
+ s.setHeight(WaterfallPlotDisplayFrame->height());
+ emit _waterfallDisplayPlot->resizeSlot(&s);
+
+ s.setWidth(ConstellationDisplayFrame->width());
+ s.setHeight(ConstellationDisplayFrame->height());
+ emit _constellationDisplayPlot->resizeSlot(&s);
+}
+
+void
+SpectrumDisplayForm::customEvent( QEvent * e)
+{
+ if(e->type() == QEvent::User+3){
+ if(_systemSpecifiedFlag){
+ WindowComboBox->setCurrentIndex(_system->GetWindowType());
+ FFTSizeComboBox->setCurrentIndex(_system->GetFFTSizeIndex());
+ //FFTSizeComboBox->setCurrentIndex(1);
+ }
+
+ waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensityWheel->value());
+ waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensityWheel->value());
+
+ // Clear any previous display
+ Reset();
+ }
+ 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::UpdateGuiTimer()
+{
+ // This is called by the displayTimer and redraws the canvases of
+ // all of the plots.
+ _frequencyDisplayPlot->canvas()->update();
+ _waterfallDisplayPlot->canvas()->update();
+ _timeDomainDisplayPlot->canvas()->update();
+ _constellationDisplayPlot->canvas()->update();
+}
+
+
+void
+SpectrumDisplayForm::AvgLineEdit_valueChanged( int value )
+{
+ 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::TabChanged(int index)
+{
+ // This might be dangerous to call this with NULL
+ resizeEvent(NULL);
+}
+
+void
+SpectrumDisplayForm::SetFrequencyRange(const double newCenterFrequency,
+ const double newStartFrequency,
+ const double newStopFrequency)
+{
+ double fdiff;
+ if(UseRFFrequenciesCheckBox->isChecked()) {
+ fdiff = newCenterFrequency;
+ }
+ else {
+ fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency));
+ }
+
+ if(fdiff > 0) {
+ std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"};
+ std::string strtime[4] = {"sec", "ms", "us", "ns"};
+ double units10 = floor(log10(fdiff));
+ double units3 = std::max(floor(units10 / 3.0), 0.0);
+ double units = pow(10, (units10-fmod(units10, 3.0)));
+ int iunit = static_cast<int>(units3);
+
+ _startFrequency = newStartFrequency;
+ _stopFrequency = newStopFrequency;
+ _centerFrequency = newCenterFrequency;
+
+ _frequencyDisplayPlot->SetFrequencyRange(_startFrequency,
+ _stopFrequency,
+ _centerFrequency,
+ UseRFFrequenciesCheckBox->isChecked(),
+ units, strunits[iunit]);
+ _waterfallDisplayPlot->SetFrequencyRange(_startFrequency,
+ _stopFrequency,
+ _centerFrequency,
+ UseRFFrequenciesCheckBox->isChecked(),
+ units, strunits[iunit]);
+ _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency,
+ units, strtime[iunit]);
+ }
+}
+
+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();
+}
+
+
+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 )
+{
+ SetFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency);
+}
+
+
+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::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::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::ToggleTabFrequency(const bool state)
+{
+ if(state == true) {
+ if(d_plot_fft == -1) {
+ SpectrumTypeTab->addTab(FrequencyPage, "Frequency Display");
+ d_plot_fft = SpectrumTypeTab->count()-1;
+ }
+ }
+ else {
+ SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(FrequencyPage));
+ d_plot_fft = -1;
+ }
+}
+
+void
+SpectrumDisplayForm::ToggleTabWaterfall(const bool state)
+{
+ if(state == true) {
+ if(d_plot_waterfall == -1) {
+ SpectrumTypeTab->addTab(WaterfallPage, "Waterfall Display");
+ d_plot_waterfall = SpectrumTypeTab->count()-1;
+ }
+ }
+ else {
+ SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(WaterfallPage));
+ d_plot_waterfall = -1;
+ }
+}
+
+void
+SpectrumDisplayForm::ToggleTabTime(const bool state)
+{
+ if(state == true) {
+ if(d_plot_time == -1) {
+ SpectrumTypeTab->addTab(TimeDomainPage, "Time Domain Display");
+ d_plot_time = SpectrumTypeTab->count()-1;
+ }
+ }
+ else {
+ SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(TimeDomainPage));
+ d_plot_time = -1;
+ }
+}
+
+void
+SpectrumDisplayForm::ToggleTabConstellation(const bool state)
+{
+ if(state == true) {
+ if(d_plot_constellation == -1) {
+ SpectrumTypeTab->addTab(ConstellationPage, "Constellation Display");
+ d_plot_constellation = SpectrumTypeTab->count()-1;
+ }
+ }
+ else {
+ SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(ConstellationPage));
+ d_plot_constellation = -1;
+ }
+}
+
+
+void
+SpectrumDisplayForm::SetTimeDomainAxis(double min, double max)
+{
+ _timeDomainDisplayPlot->set_yaxis(min, max);
+}
+
+void
+SpectrumDisplayForm::SetConstellationAxis(double xmin, double xmax,
+ double ymin, double ymax)
+{
+ _constellationDisplayPlot->set_axis(xmin, xmax, ymin, ymax);
+}
+
+void
+SpectrumDisplayForm::SetConstellationPenSize(int size)
+{
+ _constellationDisplayPlot->set_pen_size( size );
+}
+
+void
+SpectrumDisplayForm::SetFrequencyAxis(double min, double max)
+{
+ _frequencyDisplayPlot->set_yaxis(min, max);
+}
+
+void
+SpectrumDisplayForm::SetUpdateTime(double t)
+{
+ d_update_time = t;
+ // QTimer class takes millisecond input
+ displayTimer->start(d_update_time*1000);
+}
diff --git a/gr-qtgui/lib/spectrumdisplayform.h b/gr-qtgui/lib/spectrumdisplayform.h
new file mode 100644
index 000000000..794a8c58e
--- /dev/null
+++ b/gr-qtgui/lib/spectrumdisplayform.h
@@ -0,0 +1,111 @@
+#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 <TimeDomainDisplayPlot.h>
+#include <ConstellationDisplayPlot.h>
+#include <QValidator>
+#include <QTimer>
+#include <vector>
+
+class SpectrumDisplayForm : public QWidget, 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_valueChanged( int valueString );
+ void MaxHoldCheckBox_toggled( bool newState );
+ void MinHoldCheckBox_toggled( bool newState );
+ void MinHoldResetBtn_clicked();
+ void MaxHoldResetBtn_clicked();
+ void TabChanged(int index);
+
+ void SetFrequencyRange( const double newCenterFrequency,
+ const double newStartFrequency,
+ const double newStopFrequency );
+ void closeEvent( QCloseEvent * e );
+ void WindowTypeChanged( int newItem );
+ void UseRFFrequenciesCB( bool useRFFlag );
+ void waterfallMaximumIntensityChangedCB(double);
+ void waterfallMinimumIntensityChangedCB(double);
+ void WaterfallIntensityColorTypeChanged(int);
+ void WaterfallAutoScaleBtnCB();
+ void FFTComboBoxSelectedCB(const QString&);
+
+ void ToggleTabFrequency(const bool state);
+ void ToggleTabWaterfall(const bool state);
+ void ToggleTabTime(const bool state);
+ void ToggleTabConstellation(const bool state);
+
+ void SetTimeDomainAxis(double min, double max);
+ void SetConstellationAxis(double xmin, double xmax,
+ double ymin, double ymax);
+ void SetConstellationPenSize(int size);
+ void SetFrequencyAxis(double min, double max);
+ void SetUpdateTime(double t);
+
+private slots:
+ void newFrequencyData( const SpectrumUpdateEvent* );
+ void UpdateGuiTimer();
+
+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;
+ TimeDomainDisplayPlot* _timeDomainDisplayPlot;
+ ConstellationDisplayPlot* _constellationDisplayPlot;
+ SpectrumGUIClass* _system;
+ bool _systemSpecifiedFlag;
+ double _centerFrequency;
+ double _startFrequency;
+ double _noiseFloorAmplitude;
+ double _peakFrequency;
+ double _peakAmplitude;
+ double _stopFrequency;
+
+ //SpectrumUpdateEvent _lastSpectrumEvent;
+
+ // whether or not to use a particular display
+ int d_plot_fft;
+ int d_plot_waterfall;
+ int d_plot_time;
+ int d_plot_constellation;
+
+ QTimer *displayTimer;
+ double d_update_time;
+};
+
+#endif /* SPECTRUM_DISPLAY_FORM_H */
diff --git a/gr-qtgui/lib/spectrumdisplayform.ui b/gr-qtgui/lib/spectrumdisplayform.ui
new file mode 100644
index 000000000..049d4ffeb
--- /dev/null
+++ b/gr-qtgui/lib/spectrumdisplayform.ui
@@ -0,0 +1,756 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SpectrumDisplayForm</class>
+ <widget class="QWidget" name="SpectrumDisplayForm">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>712</width>
+ <height>560</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Spectrum Display</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="3">
+ <widget class="QComboBox" name="FFTSizeComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </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>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="FFTSizeLabel">
+ <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>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="UseRFFrequenciesCheckBox">
+ <property name="text">
+ <string>Display RF Frequencies</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="WindowLbl">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </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>
+ </item>
+ <item>
+ <widget class="QComboBox" name="WindowComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </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>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0" colspan="4">
+ <widget class="QTabWidget" name="SpectrumTypeTab">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="FrequencyPage">
+ <attribute name="title">
+ <string>Frequency Display</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QFrame" name="FrequencyPlotDisplayFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>617</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="MaxHoldCheckBox">
+ <property name="text">
+ <string>Max Hold</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="MinHoldCheckBox">
+ <property name="text">
+ <string>Min Hold</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="MaxHoldResetBtn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="AvgLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>62</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Average</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPushButton" name="MinHoldResetBtn">
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QSpinBox" name="AvgLineEdit"/>
+ </item>
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>200</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="WaterfallPage">
+ <attribute name="title">
+ <string>Waterfall Display</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel1">
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Intensity Display:</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QwtWheel" name="WaterfallMaximumIntensityWheel">
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>0</height>
+ </size>
+ </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="totalAngle">
+ <double>200.000000000000000</double>
+ </property>
+ <property name="viewAngle">
+ <double>20.000000000000000</double>
+ </property>
+ <property name="mass">
+ <double>0.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="WaterfallMaximumIntensityLabel">
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>100 dB</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="4">
+ <widget class="QFrame" name="WaterfallPlotDisplayFrame">
+ <property name="minimumSize">
+ <size>
+ <width>617</width>
+ <height>338</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QwtWheel" name="WaterfallMinimumIntensityWheel">
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="valid">
+ <bool>true</bool>
+ </property>
+ <property name="totalAngle">
+ <double>200.000000000000000</double>
+ </property>
+ <property name="viewAngle">
+ <double>20.000000000000000</double>
+ </property>
+ <property name="mass">
+ <double>0.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="WaterfallMinimumIntensityLabel">
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>-100 dB</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QPushButton" name="WaterfallAutoScaleBtn">
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Scales the Intensity to the current data extremes.</string>
+ </property>
+ <property name="text">
+ <string>Auto Scale</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="WaterfallIntensityComboBox">
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </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>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="TimeDomainPage">
+ <attribute name="title">
+ <string>Time Domain Display</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QFrame" name="TimeDomainDisplayFrame">
+ <property name="minimumSize">
+ <size>
+ <width>617</width>
+ <height>404</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="ConstellationPage">
+ <attribute name="title">
+ <string>Constellation Display</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QFrame" name="ConstellationDisplayFrame">
+ <property name="minimumSize">
+ <size>
+ <width>617</width>
+ <height>406</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </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>UseRFFrequenciesCheckBox</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">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>22</x>
+ <y>324</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>107</x>
+ <y>324</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>22</x>
+ <y>349</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>107</x>
+ <y>349</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>217</x>
+ <y>44</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>217</x>
+ <y>349</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>22</x>
+ <y>349</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>92</x>
+ <y>44</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>SpectrumTypeTab</sender>
+ <signal>currentChanged(int)</signal>
+ <receiver>SpectrumDisplayForm</receiver>
+ <slot>TabChanged(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>314</x>
+ <y>189</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>316</x>
+ <y>217</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>AvgLineEdit</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>SpectrumDisplayForm</receiver>
+ <slot>AvgLineEdit_valueChanged(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>604</x>
+ <y>421</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>328</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gr-qtgui/lib/waterfallGlobalData.cc b/gr-qtgui/lib/waterfallGlobalData.cc
new file mode 100644
index 000000000..1ba153f0d
--- /dev/null
+++ b/gr-qtgui/lib/waterfallGlobalData.cc
@@ -0,0 +1,164 @@
+#ifndef WATERFALL_GLOBAL_DATA_CPP
+#define WATERFALL_GLOBAL_DATA_CPP
+
+#include <waterfallGlobalData.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++;
+}
+
+#endif /* WATERFALL_GLOBAL_DATA_CPP */
diff --git a/gr-qtgui/lib/waterfallGlobalData.h b/gr-qtgui/lib/waterfallGlobalData.h
new file mode 100644
index 000000000..51f65064c
--- /dev/null
+++ b/gr-qtgui/lib/waterfallGlobalData.h
@@ -0,0 +1,47 @@
+#ifndef WATERFALL_GLOBAL_DATA_HPP
+#define WATERFALL_GLOBAL_DATA_HPP
+
+#include <qwt_raster_data.h>
+#include <inttypes.h>
+
+
+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:
+
+};
+
+#endif /* WATERFALL_GLOBAL_DATA_HPP */