diff options
Diffstat (limited to 'gr-qtgui/lib')
35 files changed, 7462 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..7ec2db820 --- /dev/null +++ b/gr-qtgui/lib/ConstellationDisplayPlot.cc @@ -0,0 +1,220 @@ +/* -*- 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. + */ + +#ifndef CONSTELLATION_DISPLAY_PLOT_C +#define CONSTELLATION_DISPLAY_PLOT_C + +#include <ConstellationDisplayPlot.h> + +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <iostream> + +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()); + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + // emit the position of clicks on widget + _picker = new QwtDblClickPlotPicker(canvas()); + connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), + this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); + + 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); +} + +void +ConstellationDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); + emit plotPointSelected(point); +} + +#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..3a85f3bc3 --- /dev/null +++ b/gr-qtgui/lib/ConstellationDisplayPlot.h @@ -0,0 +1,92 @@ +/* -*- 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. + */ + +#ifndef CONSTELLATION_DISPLAY_PLOT_HPP +#define CONSTELLATION_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> +#include <qtgui_util.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 ); + + void OnPickerPointSelected(const QwtDoublePoint & p); + +signals: + void plotPointSelected(const QPointF p); + +protected slots: + void LegendEntryChecked(QwtPlotItem *plotItem, bool on); + +protected: + +private: + QwtPlotCurve* _plot_curve; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + QwtDblClickPlotPicker *_picker; + + 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..4d60cd9be --- /dev/null +++ b/gr-qtgui/lib/FrequencyDisplayPlot.cc @@ -0,0 +1,502 @@ +/* -*- 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. + */ + +#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); + + _markerCF= new QwtPlotMarker(); + _markerCF->setLineStyle(QwtPlotMarker::VLine); + _markerCF->setLinePen(QPen(Qt::lightGray, 0, Qt::DotLine)); + _markerCF->attach(this); + _markerCF->hide(); + + _peakFrequency = 0; + _peakAmplitude = -HUGE_VAL; + + _noiseFloorAmplitude = -HUGE_VAL; + + replot(); + + // emit the position of clicks on widget + _picker = new QwtDblClickPlotPicker(canvas()); + connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), + this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); + + // Configure magnify on mouse wheel + _magnifier = new QwtPlotMagnifier(canvas()); + _magnifier->setAxisEnabled(QwtPlot::xBottom, false); + + _zoomer = new FreqDisplayZoomer(canvas(), 0); + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // 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; + + _xAxisMultiplier = 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 ); +} + +void +FrequencyDisplayPlot::SetTraceColour (QColor c) +{ + _fft_plot_curve->setPen(QPen(c)); +} + +void +FrequencyDisplayPlot::SetBGColour (QColor c) +{ + QPalette palette; + palette.setColor(canvas()->backgroundRole(), c); + canvas()->setPalette(palette); +} + +void +FrequencyDisplayPlot::ShowCFMarker (const bool show) +{ + if (show) + _markerCF->show(); + else + _markerCF->hide(); +} + +void +FrequencyDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); + point.setX(point.x() * _xAxisMultiplier); + emit plotPointSelected(point); +} + +#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..6689703bc --- /dev/null +++ b/gr-qtgui/lib/FrequencyDisplayPlot.h @@ -0,0 +1,130 @@ +/* -*- 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. + */ + +#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 <qwt_plot_magnifier.h> +#include <highResTimeFunctions.h> +#include <qwt_symbol.h> +#include <qtgui_util.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); + + void SetTraceColour (QColor); + void SetBGColour (QColor c); + void ShowCFMarker (const bool); + +public slots: + void resizeSlot( QSize *e ); + void SetLowerIntensityLevel(const double); + void SetUpperIntensityLevel(const double); + + void OnPickerPointSelected(const QwtDoublePoint & p); + +signals: + void plotPointSelected(const QPointF p); + +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; + QwtPlotMarker *_markerCF; + + QwtDblClickPlotPicker *_picker; + QwtPlotMagnifier *_magnifier; + + double* _dataPoints; + double* _xAxisPoints; + int _xAxisMultiplier; + + double* _minFFTPoints; + double* _maxFFTPoints; + int64_t _numPoints; + + double _peakFrequency; + double _peakAmplitude; + + double _noiseFloorAmplitude; + + 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..95b636d89 --- /dev/null +++ b/gr-qtgui/lib/Makefile.am @@ -0,0 +1,102 @@ +# +# 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) \ + -Dlibgnuradio_qtgui_EXPORTS + +# 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 \ + timedisplayform.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 \ + timedisplayform.cc \ + SpectrumGUIClass.cc \ + spectrumUpdateEvents.cc \ + plot_waterfall.cc \ + qtgui_sink_c.cc \ + qtgui_sink_f.cc \ + qtgui_time_sink_c.cc \ + qtgui_time_sink_f.cc \ + qtgui_util.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 \ + timedisplayform.h \ + SpectrumGUIClass.h \ + spectrumUpdateEvents.h \ + gr_qtgui_api.h \ + qtgui_sink_c.h \ + qtgui_sink_f.h \ + qtgui_time_sink_c.h \ + qtgui_time_sink_f.h \ + qtgui_util.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) diff --git a/gr-qtgui/lib/SpectrumGUIClass.cc b/gr-qtgui/lib/SpectrumGUIClass.cc new file mode 100644 index 000000000..b472470c6 --- /dev/null +++ b/gr-qtgui/lib/SpectrumGUIClass.cc @@ -0,0 +1,474 @@ +/* -*- 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. + */ + +#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..ad06674f5 --- /dev/null +++ b/gr-qtgui/lib/SpectrumGUIClass.h @@ -0,0 +1,130 @@ +/* -*- 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. + */ + +#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..f635a2b0c --- /dev/null +++ b/gr-qtgui/lib/TimeDomainDisplayPlot.cc @@ -0,0 +1,311 @@ +/* -*- 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. + */ + +#ifndef TIME_DOMAIN_DISPLAY_PLOT_C +#define TIME_DOMAIN_DISPLAY_PLOT_C + +#include <TimeDomainDisplayPlot.h> + +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <QColor> +#include <iostream> + +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(int nplots, QWidget* parent) + : QwtPlot(parent), _nplots(nplots) +{ + resize(parent->width(), parent->height()); + + _numPoints = 1024; + _xAxisPoints = new double[_numPoints]; + memset(_xAxisPoints, 0x0, _numPoints*sizeof(double)); + + _zoomer = new TimeDomainDisplayZoomer(canvas(), 0); + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); + + // 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); + setXaxis(0, _numPoints); + setAxisTitle(QwtPlot::xBottom, "Time (sec)"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setYaxis(-2.0, 2.0); + setAxisTitle(QwtPlot::yLeft, "Amplitude"); + + QList<QColor> colors; + colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) + << QColor(Qt::black) << QColor(Qt::cyan) << QColor(Qt::magenta) + << QColor(Qt::yellow) << QColor(Qt::gray) << QColor(Qt::darkRed) + << QColor(Qt::darkGreen) << QColor(Qt::darkBlue) << QColor(Qt::darkGray); + + int ncolors = colors.size(); + + // Setup dataPoints and plot vectors + // Automatically deleted when parent is deleted + for(int i = 0; i < _nplots; i++) { + _dataPoints.push_back(new double[_numPoints]); + memset(_dataPoints[i], 0x0, _numPoints*sizeof(double)); + + _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); + _plot_curve[i]->attach(this); + _plot_curve[i]->setPen(QPen(colors[i])); + _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + } + + _sampleRate = 1; + _resetXAxisPoints(); + + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // emit the position of clicks on widget + _picker = new QwtDblClickPlotPicker(canvas()); + connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), + this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); + + // Configure magnify on mouse wheel + _magnifier = new QwtPlotMagnifier(canvas()); + _magnifier->setAxisEnabled(QwtPlot::xBottom, false); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + QwtLegend* legendDisplay = new QwtLegend(this); + legendDisplay->setItemMode(QwtLegend::CheckableItem); + insertLegend(legendDisplay); + + connect(this, SIGNAL( legendChecked(QwtPlotItem *, bool ) ), + this, SLOT( LegendEntryChecked(QwtPlotItem *, bool ) )); +} + +TimeDomainDisplayPlot::~TimeDomainDisplayPlot() +{ + for(int i = 0; i < _nplots; i++) + delete [] _dataPoints[i]; + delete[] _xAxisPoints; + + // _zoomer and _panner deleted when parent deleted +} + +void +TimeDomainDisplayPlot::setYaxis(double min, double max) +{ + setAxisScale(QwtPlot::yLeft, min, max); + _zoomer->setZoomBase(); +} + +void +TimeDomainDisplayPlot::setXaxis(double min, double max) +{ + setAxisScale(QwtPlot::xBottom, min, max); + _zoomer->setZoomBase(); +} + +void +TimeDomainDisplayPlot::setTitle(int which, QString title) +{ + _plot_curve[which]->setTitle(title); +} + +void +TimeDomainDisplayPlot::setColor(int which, QString color) +{ + _plot_curve[which]->setPen(QPen(color)); +} + +void TimeDomainDisplayPlot::replot() +{ + QwtPlot::replot(); +} + +void +TimeDomainDisplayPlot::resizeSlot( QSize *s ) +{ + // -10 is to spare some room for the legend and x-axis label + resize(s->width()-10, s->height()-10); +} + +void TimeDomainDisplayPlot::PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, + const double timeInterval) +{ + if((numDataPoints > 0)) { + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + delete[] _xAxisPoints; + _xAxisPoints = new double[_numPoints]; + + for(int i = 0; i < _nplots; i++) { + delete[] _dataPoints[i]; + _dataPoints[i] = new double[_numPoints]; + _plot_curve[i]->setRawData(_xAxisPoints, _dataPoints[i], _numPoints); + } + + setXaxis(0, numDataPoints); + _resetXAxisPoints(); + } + + for(int i = 0; i < _nplots; i++) { + memcpy(_dataPoints[i], dataPoints[i], numDataPoints*sizeof(double)); + } + } +} + +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); + } +} + +void +TimeDomainDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f\n", point.x(), point.y()); + emit plotPointSelected(point); +} + +#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..4c7b1e319 --- /dev/null +++ b/gr-qtgui/lib/TimeDomainDisplayPlot.h @@ -0,0 +1,95 @@ +/* -*- 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. + */ + +#ifndef TIME_DOMAIN_DISPLAY_PLOT_HPP +#define TIME_DOMAIN_DISPLAY_PLOT_HPP + +#include <stdint.h> +#include <cstdio> +#include <vector> +#include <qwt_plot.h> +#include <qwt_painter.h> +#include <qwt_plot_canvas.h> +#include <qwt_plot_curve.h> +#include <qwt_scale_engine.h> +#include <qwt_scale_widget.h> +#include <qwt_plot_zoomer.h> +#include <qwt_plot_panner.h> +#include <qwt_plot_magnifier.h> +#include <qwt_plot_marker.h> +#include <highResTimeFunctions.h> +#include <qwt_symbol.h> +#include <qtgui_util.h> + +class TimeDomainDisplayPlot:public QwtPlot{ + Q_OBJECT + +public: + TimeDomainDisplayPlot(int nplots, QWidget*); + virtual ~TimeDomainDisplayPlot(); + + void PlotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, const double timeInterval); + + virtual void replot(); + +public slots: + void setYaxis(double min, double max); + void setXaxis(double min, double max); + void setTitle(int which, QString title); + void setColor(int which, QString color); + + void resizeSlot( QSize *s ); + void SetSampleRate(double sr, double units, + const std::string &strunits); + + void OnPickerPointSelected(const QwtDoublePoint & p); + +signals: + void plotPointSelected(const QPointF p); + +protected slots: + void LegendEntryChecked(QwtPlotItem *plotItem, bool on); + +protected: + +private: + void _resetXAxisPoints(); + + int _nplots; + std::vector<QwtPlotCurve*> _plot_curve; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + QwtDblClickPlotPicker *_picker; + QwtPlotMagnifier *_magnifier; + + std::vector<double*> _dataPoints; + double* _xAxisPoints; + + double _sampleRate; + + 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..52381a9f6 --- /dev/null +++ b/gr-qtgui/lib/WaterfallDisplayPlot.cc @@ -0,0 +1,583 @@ +/* -*- 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. + */ + +#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); + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // emit the position of clicks on widget + _picker = new QwtDblClickPlotPicker(canvas()); + connect(_picker, SIGNAL(selected(const QwtDoublePoint &)), + this, SLOT(OnPickerPointSelected(const QwtDoublePoint &))); + + // 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(); + + _xAxisMultiplier = 1; +} + +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; + + _xAxisMultiplier = 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(); +} + +void +WaterfallDisplayPlot::OnPickerPointSelected(const QwtDoublePoint & p) +{ + QPointF point = p; + //fprintf(stderr,"OnPickerPointSelected %f %f %d\n", point.x(), point.y(), _xAxisMultiplier); + point.setX(point.x() * _xAxisMultiplier); + emit plotPointSelected(point); +} + +#endif /* WATERFALL_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/WaterfallDisplayPlot.h b/gr-qtgui/lib/WaterfallDisplayPlot.h new file mode 100644 index 000000000..583bf9407 --- /dev/null +++ b/gr-qtgui/lib/WaterfallDisplayPlot.h @@ -0,0 +1,112 @@ +/* -*- 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. + */ + +#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 <qtgui_util.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 ); + void OnPickerPointSelected(const QwtDoublePoint & p); + +signals: + void UpdatedLowerIntensityLevel(const double); + void UpdatedUpperIntensityLevel(const double); + void plotPointSelected(const QPointF p); + +protected: + +private: + void _UpdateIntensityRangeDisplay(); + + double _startFrequency; + double _stopFrequency; + int _xAxisMultiplier; + + PlotWaterfall *d_spectrogram; + + QwtPlotPanner* _panner; + QwtPlotZoomer* _zoomer; + + QwtDblClickPlotPicker *_picker; + + 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/gr_qtgui_api.h b/gr-qtgui/lib/gr_qtgui_api.h new file mode 100644 index 000000000..65033a093 --- /dev/null +++ b/gr-qtgui/lib/gr_qtgui_api.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010 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_GR_QTGUI_API_H +#define INCLUDED_GR_QTGUI_API_H + +#include <gruel/attributes.h> + +#ifdef libgnuradio_qtgui_EXPORTS +# define GR_QTGUI_API __GR_ATTR_EXPORT +#else +# define GR_QTGUI_API __GR_ATTR_IMPORT +#endif + +#endif /* INCLUDED_GR_QTGUI_API_H */ 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..b0946885c --- /dev/null +++ b/gr-qtgui/lib/qtgui_sink_c.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_C_H +#define INCLUDED_QTGUI_SINK_C_H + +#include <Python.h> +#include <gr_qtgui_api.h> +#include <gr_block.h> +#include <gr_firdes.h> +#include <gri_fft.h> +#include <qapplication.h> +#include "SpectrumGUIClass.h" + +class qtgui_sink_c; +typedef boost::shared_ptr<qtgui_sink_c> qtgui_sink_c_sptr; + +GR_QTGUI_API qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + double fc=0, double bandwidth=1.0, + const std::string &name="Spectrum Display", + bool plotfreq=true, bool plotwaterfall=true, + bool plottime=true, bool plotconst=true, + QWidget *parent=NULL); + +class GR_QTGUI_API qtgui_sink_c : public gr_block +{ +private: + friend GR_QTGUI_API qtgui_sink_c_sptr qtgui_make_sink_c (int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + qtgui_sink_c (int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + gr_firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + + 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; + + 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..30db05eea --- /dev/null +++ b/gr-qtgui/lib/qtgui_sink_f.h @@ -0,0 +1,115 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_SINK_F_H +#define INCLUDED_QTGUI_SINK_F_H + +#include <Python.h> +#include <gr_qtgui_api.h> +#include <gr_block.h> +#include <gr_firdes.h> +#include <gri_fft.h> +#include <qapplication.h> +//#include <qtgui.h> +#include "SpectrumGUIClass.h" + +class qtgui_sink_f; +typedef boost::shared_ptr<qtgui_sink_f> qtgui_sink_f_sptr; + +GR_QTGUI_API qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, + double fc=0, double bw=1.0, + const std::string &name="Spectrum Display", + bool plotfreq=true, bool plotwaterfall=true, + bool plottime=true, bool plotconst=true, + QWidget *parent=NULL); + +class GR_QTGUI_API qtgui_sink_f : public gr_block +{ +private: + friend GR_QTGUI_API qtgui_sink_f_sptr qtgui_make_sink_f (int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + qtgui_sink_f (int fftsize, int wintype, + double fc, double bw, + const std::string &name, + bool plotfreq, bool plotwaterfall, + bool plottime, bool plotconst, + QWidget *parent); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_fftsize; + gr_firdes::win_type d_wintype; + std::vector<float> d_window; + double d_center_freq; + double d_bandwidth; + std::string d_name; + + bool d_shift; + gri_fft_complex *d_fft; + + int d_index; + float *d_residbuf; + + bool d_plotfreq, d_plotwaterfall, d_plottime, d_plotconst; + + double d_update_time; + + QWidget *d_parent; + SpectrumGUIClass *d_main_gui; + + void windowreset(); + void buildwindow(); + void fftresize(); + void fft(const float *data_in, int size); + +public: + ~qtgui_sink_f(); + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_frequency_range(const double centerfreq, + const double bandwidth); + + void set_time_domain_axis(double min, double max); + void set_constellation_axis(double xmin, double xmax, + double ymin, double ymax); + void set_constellation_pen_size(int size); + void set_frequency_axis(double min, double max); + + void set_update_time(double t); + + QApplication *d_qApplication; + + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_QTGUI_SINK_F_H */ diff --git a/gr-qtgui/lib/qtgui_time_sink_c.cc b/gr-qtgui/lib/qtgui_time_sink_c.cc new file mode 100644 index 000000000..6c4793ea1 --- /dev/null +++ b/gr-qtgui/lib/qtgui_time_sink_c.cc @@ -0,0 +1,199 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qtgui_time_sink_c.h> +#include <gr_io_signature.h> +#include <string.h> + +#include <QTimer> + +qtgui_time_sink_c_sptr +qtgui_make_time_sink_c (int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) +{ + return gnuradio::get_initial_sptr(new qtgui_time_sink_c (size, bw, name, + nconnections, parent)); +} + +qtgui_time_sink_c::qtgui_time_sink_c (int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_block ("time_sink_c", + gr_make_io_signature (nconnections, nconnections, sizeof(gr_complex)), + gr_make_io_signature (0, 0, 0)), + d_size(size), d_bandwidth(bw), d_name(name), + d_nconnections(2*nconnections), d_parent(parent) +{ + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(new double[d_size]); + } + + initialize(); +} + +qtgui_time_sink_c::~qtgui_time_sink_c() +{ + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + delete [] d_residbufs[i]; + } +} + +void +qtgui_time_sink_c::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_size, 8191); + } +} + +void +qtgui_time_sink_c::initialize() +{ + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + timespec_reset(&d_last_time); +} + + +void +qtgui_time_sink_c::exec_() +{ + d_qApplication->exec(); +} + +QWidget* +qtgui_time_sink_c::qwidget() +{ + return d_main_gui; +} + +PyObject* +qtgui_time_sink_c::pyqwidget() +{ + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; +} + +void +qtgui_time_sink_c::set_time_domain_axis(double min, double max) +{ + d_main_gui->setTimeDomainAxis(min, max); +} + +void +qtgui_time_sink_c::set_update_time(double t) +{ + d_update_time = t; + d_main_gui->setUpdateTime(d_update_time); +} + +void +qtgui_time_sink_c::set_title(int which, const std::string &title) +{ + d_main_gui->setTitle(which, title.c_str()); +} + +void +qtgui_time_sink_c::set_color(int which, const std::string &color) +{ + d_main_gui->setColor(which, color.c_str()); +} + +int +qtgui_time_sink_c::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int n=0, j=0, idx=0; + const gr_complex *in = (const gr_complex*)input_items[idx]; + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + d_current_time = get_highres_clock(); + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n+=2) { + in = (const gr_complex*)input_items[idx++]; + for(unsigned int k = 0; k < resid; k++) { + d_residbufs[n][d_index+k] = in[j+k].real(); + d_residbufs[n+1][d_index+k] = in[j+k].imag(); + } + } + + // Update the plot if its time + if(diff_timespec(d_current_time, d_last_time) > d_update_time) { + d_last_time = d_current_time; + d_qApplication->postEvent(d_main_gui, + new TimeUpdateEvent(d_residbufs, d_size)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + else { + for(n = 0; n < d_nconnections; n+=2) { + in = (const gr_complex*)input_items[idx++]; + for(unsigned int k = 0; k < resid; k++) { + d_residbufs[n][d_index+k] = in[j+k].real(); + d_residbufs[n+1][d_index+k] = in[j+k].imag(); + } + } + d_index += datasize; + j += datasize; + } + } + + consume_each(j); + return j; +} diff --git a/gr-qtgui/lib/qtgui_time_sink_c.h b/gr-qtgui/lib/qtgui_time_sink_c.h new file mode 100644 index 000000000..aa46ab3a7 --- /dev/null +++ b/gr-qtgui/lib/qtgui_time_sink_c.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_C_H +#define INCLUDED_QTGUI_TIME_SINK_C_H + +#include <Python.h> +#include <gr_qtgui_api.h> +#include <gr_block.h> +#include <gr_firdes.h> +#include <gri_fft.h> +#include <qapplication.h> +#include <timedisplayform.h> + +class qtgui_time_sink_c; +typedef boost::shared_ptr<qtgui_time_sink_c> qtgui_time_sink_c_sptr; + +GR_QTGUI_API qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, + const std::string &name, + int nconnectons=1, + QWidget *parent=NULL); + +class GR_QTGUI_API qtgui_time_sink_c : public gr_block +{ +private: + friend GR_QTGUI_API qtgui_time_sink_c_sptr qtgui_make_time_sink_c(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent); + qtgui_time_sink_c(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_size; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + double d_update_time; + + QWidget *d_parent; + TimeDisplayForm *d_main_gui; + + timespec d_current_time; + timespec d_last_time; + +public: + ~qtgui_time_sink_c(); + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_time_domain_axis(double min, double max); + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + + QApplication *d_qApplication; + + int 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_TIME_SINK_C_H */ diff --git a/gr-qtgui/lib/qtgui_time_sink_f.cc b/gr-qtgui/lib/qtgui_time_sink_f.cc new file mode 100644 index 000000000..28789c163 --- /dev/null +++ b/gr-qtgui/lib/qtgui_time_sink_f.cc @@ -0,0 +1,197 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qtgui_time_sink_f.h> +#include <gr_io_signature.h> +#include <string.h> + +#include <QTimer> + +qtgui_time_sink_f_sptr +qtgui_make_time_sink_f (int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) +{ + return gnuradio::get_initial_sptr(new qtgui_time_sink_f (size, bw, name, + nconnections, parent)); +} + +qtgui_time_sink_f::qtgui_time_sink_f (int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent) + : gr_block ("time_sink_f", + gr_make_io_signature (nconnections, nconnections, sizeof(float)), + gr_make_io_signature (0, 0, 0)), + d_size(size), d_bandwidth(bw), d_name(name), + d_nconnections(nconnections), d_parent(parent) +{ + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(new double[d_size]); + } + + initialize(); +} + +qtgui_time_sink_f::~qtgui_time_sink_f() +{ + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + delete [] d_residbufs[i]; + } +} + +void +qtgui_time_sink_f::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_size, 8191); + } +} + +void +qtgui_time_sink_f::initialize() +{ + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new TimeDisplayForm(d_nconnections, d_parent); + + // initialize update time to 10 times a second + set_update_time(0.1); + timespec_reset(&d_last_time); +} + + +void +qtgui_time_sink_f::exec_() +{ + d_qApplication->exec(); +} + +QWidget* +qtgui_time_sink_f::qwidget() +{ + return d_main_gui; +} + +PyObject* +qtgui_time_sink_f::pyqwidget() +{ + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; +} + +void +qtgui_time_sink_f::set_time_domain_axis(double min, double max) +{ + d_main_gui->setTimeDomainAxis(min, max); +} + +void +qtgui_time_sink_f::set_update_time(double t) +{ + d_update_time = t; + d_main_gui->setUpdateTime(d_update_time); +} + +void +qtgui_time_sink_f::set_title(int which, const std::string &title) +{ + d_main_gui->setTitle(which, title.c_str()); +} + +void +qtgui_time_sink_f::set_color(int which, const std::string &color) +{ + d_main_gui->setColor(which, color.c_str()); +} + +int +qtgui_time_sink_f::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int n=0, j=0, idx=0; + const float *in = (const float*)input_items[idx]; + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + d_current_time = get_highres_clock(); + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + for(unsigned int k = 0; k < resid; k++) { + d_residbufs[n][d_index+k] = in[j+k]; + } + } + + // Update the plot if its time + if(diff_timespec(d_current_time, d_last_time) > d_update_time) { + d_last_time = d_current_time; + d_qApplication->postEvent(d_main_gui, + new TimeUpdateEvent(d_residbufs, d_size)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + else { + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + for(unsigned int k = 0; k < resid; k++) { + d_residbufs[n][d_index+k] = in[j+k]; + } + } + d_index += datasize; + j += datasize; + } + } + + consume_each(j); + return j; +} diff --git a/gr-qtgui/lib/qtgui_time_sink_f.h b/gr-qtgui/lib/qtgui_time_sink_f.h new file mode 100644 index 000000000..29fca79ee --- /dev/null +++ b/gr-qtgui/lib/qtgui_time_sink_f.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_TIME_SINK_F_H +#define INCLUDED_QTGUI_TIME_SINK_F_H + +#include <Python.h> +#include <gr_qtgui_api.h> +#include <gr_block.h> +#include <gr_firdes.h> +#include <gri_fft.h> +#include <qapplication.h> +#include <timedisplayform.h> + +class qtgui_time_sink_f; +typedef boost::shared_ptr<qtgui_time_sink_f> qtgui_time_sink_f_sptr; + +GR_QTGUI_API qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, + const std::string &name, + int nconnectons=1, + QWidget *parent=NULL); + +class GR_QTGUI_API qtgui_time_sink_f : public gr_block +{ +private: + friend GR_QTGUI_API qtgui_time_sink_f_sptr qtgui_make_time_sink_f(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent); + qtgui_time_sink_f(int size, double bw, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + void initialize(); + + int d_size; + double d_bandwidth; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + double d_update_time; + + QWidget *d_parent; + TimeDisplayForm *d_main_gui; + + timespec d_current_time; + timespec d_last_time; + +public: + ~qtgui_time_sink_f(); + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_time_domain_axis(double min, double max); + void set_update_time(double t); + void set_title(int which, const std::string &title); + void set_color(int which, const std::string &color); + + QApplication *d_qApplication; + + int 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_TIME_SINK_F_H */ diff --git a/gr-qtgui/lib/qtgui_util.cc b/gr-qtgui/lib/qtgui_util.cc new file mode 100644 index 000000000..87b90997a --- /dev/null +++ b/gr-qtgui/lib/qtgui_util.cc @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 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 <qtgui_util.h> + +QwtPickerDblClickPointMachine::QwtPickerDblClickPointMachine() + : QwtPickerMachine () +{ + +} + +QwtPickerDblClickPointMachine::~QwtPickerDblClickPointMachine() +{ + +} + +QwtPickerMachine::CommandList +QwtPickerDblClickPointMachine::transition(const QwtEventPattern &eventPattern, + const QEvent *e) +{ + QwtPickerMachine::CommandList cmdList; + switch(e->type()) { + case QEvent::MouseButtonDblClick: + if ( eventPattern.mouseMatch(QwtEventPattern::MouseSelect1, + (const QMouseEvent *)e) ) { + cmdList += QwtPickerMachine::Begin; + cmdList += QwtPickerMachine::Append; + cmdList += QwtPickerMachine::End; + } + break; + default: + break; + } + return cmdList; +} + +QwtDblClickPlotPicker::QwtDblClickPlotPicker(QwtPlotCanvas* canvas) + : QwtPlotPicker(canvas) +{ + setSelectionFlags(QwtPicker::PointSelection); +} + +QwtDblClickPlotPicker::~QwtDblClickPlotPicker() +{ +} + +QwtPickerMachine* +QwtDblClickPlotPicker::stateMachine(int n) const +{ + return new QwtPickerDblClickPointMachine; +} + diff --git a/gr-qtgui/lib/qtgui_util.h b/gr-qtgui/lib/qtgui_util.h new file mode 100644 index 000000000..5b129c7b5 --- /dev/null +++ b/gr-qtgui/lib/qtgui_util.h @@ -0,0 +1,50 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_UTIL_H +#define INCLUDED_QTGUI_UTIL_H + +#include <qevent.h> +#include <gr_qtgui_api.h> +#include <qwt_plot_picker.h> +#include <qwt_picker_machine.h> + + +class GR_QTGUI_API QwtDblClickPlotPicker: public QwtPlotPicker +{ +public: + QwtDblClickPlotPicker(QwtPlotCanvas *); + ~QwtDblClickPlotPicker(); + + virtual QwtPickerMachine * stateMachine(int) const; +}; + +class GR_QTGUI_API QwtPickerDblClickPointMachine: public QwtPickerMachine +{ +public: + QwtPickerDblClickPointMachine(); + ~QwtPickerDblClickPointMachine(); + + virtual CommandList transition( const QwtEventPattern &eventPattern, const QEvent *e); +}; + +#endif /* INCLUDED_QTGUI_UTIL_H */ diff --git a/gr-qtgui/lib/spectrumUpdateEvents.cc b/gr-qtgui/lib/spectrumUpdateEvents.cc new file mode 100644 index 000000000..ae4caf31a --- /dev/null +++ b/gr-qtgui/lib/spectrumUpdateEvents.cc @@ -0,0 +1,223 @@ +#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; +} + + +/***************************************************************************/ +#include <iostream> +TimeUpdateEvent::TimeUpdateEvent(const std::vector<double*> timeDomainPoints, + const uint64_t numTimeDomainDataPoints) + : QEvent(QEvent::Type(10005)) +{ + if(numTimeDomainDataPoints < 1) { + _numTimeDomainDataPoints = 1; + } + else { + _numTimeDomainDataPoints = numTimeDomainDataPoints; + } + + _nplots = timeDomainPoints.size(); + for(size_t i = 0; i < _nplots; i++) { + _dataTimeDomainPoints.push_back(new double[_numTimeDomainDataPoints]); + if(numTimeDomainDataPoints > 0) { + memcpy(_dataTimeDomainPoints[i], timeDomainPoints[i], + _numTimeDomainDataPoints*sizeof(double)); + } + } +} + +TimeUpdateEvent::~TimeUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _dataTimeDomainPoints[i]; + } +} + +const std::vector<double*> +TimeUpdateEvent::getTimeDomainPoints() const +{ + return _dataTimeDomainPoints; +} + +uint64_t +TimeUpdateEvent::getNumTimeDomainDataPoints() const +{ + return _numTimeDomainDataPoints; +} + +#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..a5e359a4c --- /dev/null +++ b/gr-qtgui/lib/spectrumUpdateEvents.h @@ -0,0 +1,116 @@ +#ifndef SPECTRUM_UPDATE_EVENTS_H +#define SPECTRUM_UPDATE_EVENTS_H + +#include <stdint.h> +#include <QEvent> +#include <QString> +#include <complex> +#include <vector> +#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; +}; + + +class TimeUpdateEvent: public QEvent +{ +public: + TimeUpdateEvent(const std::vector<double*> timeDomainPoints, + const uint64_t numTimeDomainDataPoints); + + ~TimeUpdateEvent(); + + int which() const; + const std::vector<double*> getTimeDomainPoints() const; + uint64_t getNumTimeDomainDataPoints() const; + timespec getDataTimestamp() const; + bool getRepeatDataFlag() const; + +protected: + +private: + size_t _nplots; + std::vector<double*> _dataTimeDomainPoints; + uint64_t _numTimeDomainDataPoints; +}; + + +#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..991f51f47 --- /dev/null +++ b/gr-qtgui/lib/spectrumdisplayform.cc @@ -0,0 +1,756 @@ +/* -*- 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. + */ + +#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(2, TimeDomainDisplayFrame); + _constellationDisplayPlot = new ConstellationDisplayPlot(ConstellationDisplayFrame); + _numRealDataPoints = 1024; + _realFFTDataPoints = new double[_numRealDataPoints]; + _averagedValues = new double[_numRealDataPoints]; + _historyVector = new std::vector<double*>; + + _timeDomainDisplayPlot->setTitle(0, "real"); + _timeDomainDisplayPlot->setTitle(1, "imag"); + + AvgLineEdit->setRange(0, 500); // Set range of Average box value from 0 to 500 + MinHoldCheckBox_toggled( false ); + MaxHoldCheckBox_toggled( false ); + + WaterfallMaximumIntensityWheel->setRange(-200, 0); + WaterfallMaximumIntensityWheel->setTickCnt(50); + WaterfallMinimumIntensityWheel->setRange(-200, 0); + WaterfallMinimumIntensityWheel->setTickCnt(50); + WaterfallMinimumIntensityWheel->setValue(-200); + + _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())); + + // Connect double click signals up + connect(_frequencyDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onFFTPlotPointSelected(const QPointF))); + + connect(_waterfallDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onWFallPlotPointSelected(const QPointF))); + + connect(_timeDomainDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onTimePlotPointSelected(const QPointF))); + + connect(_constellationDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onConstPlotPointSelected(const QPointF))); +} + +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 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(); + double* realTimeDomainDataPoints = (double*)spectrumUpdateEvent->getRealTimeDomainPoints(); + double* imagTimeDomainDataPoints = (double*)spectrumUpdateEvent->getImagTimeDomainPoints(); + + std::vector<double*> timeDomainDataPoints; + timeDomainDataPoints.push_back(realTimeDomainDataPoints); + timeDomainDataPoints.push_back(imagTimeDomainDataPoints); + + // 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(timeDomainDataPoints, + 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->setYaxis(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); +} + +void +SpectrumDisplayForm::onFFTPlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 1); +} + +void +SpectrumDisplayForm::onWFallPlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 2); +} + +void +SpectrumDisplayForm::onTimePlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 3); +} + +void +SpectrumDisplayForm::onConstPlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 4); +} diff --git a/gr-qtgui/lib/spectrumdisplayform.h b/gr-qtgui/lib/spectrumdisplayform.h new file mode 100644 index 000000000..30303a36f --- /dev/null +++ b/gr-qtgui/lib/spectrumdisplayform.h @@ -0,0 +1,139 @@ +/* -*- 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. + */ + +#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(); + + void onFFTPlotPointSelected(const QPointF p); + void onWFallPlotPointSelected(const QPointF p); + void onTimePlotPointSelected(const QPointF p); + void onConstPlotPointSelected(const QPointF p); + +signals: + void plotPointSelected(const QPointF p, int type); + +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/timedisplayform.cc b/gr-qtgui/lib/timedisplayform.cc new file mode 100644 index 000000000..cc4ac9951 --- /dev/null +++ b/gr-qtgui/lib/timedisplayform.cc @@ -0,0 +1,177 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <cmath> +#include <QColorDialog> +#include <QMessageBox> +#include <timedisplayform.h> +#include <iostream> + +TimeDisplayForm::TimeDisplayForm(int nplots, QWidget* parent) + : QWidget(parent) +{ + _systemSpecifiedFlag = false; + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + + _layout = new QGridLayout(this); + _timeDomainDisplayPlot = new TimeDomainDisplayPlot(nplots, this); + _layout->addWidget(_timeDomainDisplayPlot, 0, 0); + + _numRealDataPoints = 1024; + + setLayout(_layout); + + Reset(); + + // Create a timer to update plots at the specified rate + displayTimer = new QTimer(this); + connect(displayTimer, SIGNAL(timeout()), this, SLOT(updateGuiTimer())); + + connect(_timeDomainDisplayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onTimePlotPointSelected(const QPointF))); +} + +TimeDisplayForm::~TimeDisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + delete _intValidator; + + displayTimer->stop(); + delete displayTimer; +} + +void +TimeDisplayForm::newData( const TimeUpdateEvent* spectrumUpdateEvent) +{ + const std::vector<double*> timeDomainDataPoints = spectrumUpdateEvent->getTimeDomainPoints(); + const uint64_t numTimeDomainDataPoints = spectrumUpdateEvent->getNumTimeDomainDataPoints(); + + _timeDomainDisplayPlot->PlotNewData(timeDomainDataPoints, + numTimeDomainDataPoints, + d_update_time); +} + +void +TimeDisplayForm::resizeEvent( QResizeEvent *e ) +{ + QSize s = size(); + emit _timeDomainDisplayPlot->resizeSlot(&s); +} + +void +TimeDisplayForm::customEvent( QEvent * e) +{ + if(e->type() == 10005) { + TimeUpdateEvent* timeUpdateEvent = (TimeUpdateEvent*)e; + newData(timeUpdateEvent); + } + //else if(e->type() == 10008){ + //setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel()); + //} + //else if(e->type() == 10009){ + //Reset(); + //if(_systemSpecifiedFlag){ + // _system->ResetPendingGUIUpdateEvents(); + //} + //} +} + +void +TimeDisplayForm::updateGuiTimer() +{ + _timeDomainDisplayPlot->canvas()->update(); +} + +void +TimeDisplayForm::onTimePlotPointSelected(const QPointF p) +{ + emit plotPointSelected(p, 3); +} + +void +TimeDisplayForm::setFrequencyRange(const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency) +{ + double fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency)); + + if(fdiff > 0) { + 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; + + _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency, + units, strtime[iunit]); + } +} + +void +TimeDisplayForm::Reset() +{ +} + + +void +TimeDisplayForm::closeEvent( QCloseEvent *e ) +{ + //if(_systemSpecifiedFlag){ + // _system->SetWindowOpenFlag(false); + //} + + qApp->processEvents(); + + QWidget::closeEvent(e); +} + +void +TimeDisplayForm::setTimeDomainAxis(double min, double max) +{ + _timeDomainDisplayPlot->setYaxis(min, max); +} + +void +TimeDisplayForm::setUpdateTime(double t) +{ + d_update_time = t; + // QTimer class takes millisecond input + displayTimer->start(d_update_time*1000); +} + +void +TimeDisplayForm::setTitle(int which, QString title) +{ + _timeDomainDisplayPlot->setTitle(which, title); +} + +void +TimeDisplayForm::setColor(int which, QString color) +{ + _timeDomainDisplayPlot->setColor(which, color); +} diff --git a/gr-qtgui/lib/timedisplayform.h b/gr-qtgui/lib/timedisplayform.h new file mode 100644 index 000000000..1216a1ef5 --- /dev/null +++ b/gr-qtgui/lib/timedisplayform.h @@ -0,0 +1,85 @@ +/* -*- c++ -*- */ +/* + * Copyright 2011 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIME_DISPLAY_FORM_H +#define TIME_DISPLAY_FORM_H + +#include <spectrumUpdateEvents.h> +#include <FrequencyDisplayPlot.h> +#include <WaterfallDisplayPlot.h> +#include <TimeDomainDisplayPlot.h> +#include <ConstellationDisplayPlot.h> +#include <QtGui/QApplication> +#include <QtGui/QGridLayout> +#include <QValidator> +#include <QTimer> +#include <vector> + +class TimeDisplayForm : public QWidget +{ + Q_OBJECT + + public: + TimeDisplayForm(int nplots=1, QWidget* parent = 0); + ~TimeDisplayForm(); + + void Reset(); + +public slots: + void resizeEvent( QResizeEvent * e ); + void customEvent( QEvent * e ); + void setFrequencyRange( const double newCenterFrequency, + const double newStartFrequency, + const double newStopFrequency ); + void closeEvent( QCloseEvent * e ); + + void setTimeDomainAxis(double min, double max); + + void setUpdateTime(double t); + + void setTitle(int which, QString title); + void setColor(int which, QString color); + +private slots: + void newData( const TimeUpdateEvent* ); + void updateGuiTimer(); + + void onTimePlotPointSelected(const QPointF p); + +signals: + void plotPointSelected(const QPointF p, int type); + +private: + uint64_t _numRealDataPoints; + QIntValidator* _intValidator; + + QGridLayout *_layout; + TimeDomainDisplayPlot* _timeDomainDisplayPlot; + bool _systemSpecifiedFlag; + double _startFrequency; + double _stopFrequency; + + QTimer *displayTimer; + double d_update_time; +}; + +#endif /* TIME_DISPLAY_FORM_H */ 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 */ |