diff options
Diffstat (limited to 'gr-qtgui/src/lib/FrequencyDisplayPlot.cc')
-rw-r--r-- | gr-qtgui/src/lib/FrequencyDisplayPlot.cc | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/gr-qtgui/src/lib/FrequencyDisplayPlot.cc b/gr-qtgui/src/lib/FrequencyDisplayPlot.cc new file mode 100644 index 0000000000..9fb9253b53 --- /dev/null +++ b/gr-qtgui/src/lib/FrequencyDisplayPlot.cc @@ -0,0 +1,386 @@ +#ifndef FREQUENCY_DISPLAY_PLOT_C +#define FREQUENCY_DISPLAY_PLOT_C + +#include <FrequencyDisplayPlot.h> + +#include <qwt_scale_draw.h> + +class FreqPrecisionClass +{ +public: + FreqPrecisionClass(const int freqPrecision){ + _frequencyPrecision = freqPrecision; + } + + virtual ~FreqPrecisionClass(){ + } + + virtual unsigned int GetFrequencyPrecision()const{ + return _frequencyPrecision; + } + + virtual void SetFrequencyPrecision(const unsigned int newPrecision){ + _frequencyPrecision = newPrecision; + } +protected: + unsigned int _frequencyPrecision; + +private: + +}; + +class FreqDisplayScaleDraw: public QwtScaleDraw, public FreqPrecisionClass{ +public: + FreqDisplayScaleDraw(const unsigned int precision):QwtScaleDraw(), FreqPrecisionClass(precision){ + + } + + virtual ~FreqDisplayScaleDraw(){ + + } + + virtual QwtText label(double value)const{ + return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision()); + } + +protected: + +private: + +}; + +class FreqDisplayZoomer: public QwtPlotZoomer, public FreqPrecisionClass +{ +public: + FreqDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision):QwtPlotZoomer(canvas),FreqPrecisionClass(freqPrecision) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~FreqDisplayZoomer(){ + + } + + virtual void updateTrackerText(){ + updateDisplay(); + } + +protected: + virtual QwtText trackerText( const QwtDoublePoint& p ) const + { + QwtText t(QString("%1 %2, %3 dB").arg(p.x(), 0, 'f', GetFrequencyPrecision()).arg( (GetFrequencyPrecision() == 0) ? "Hz" : "kHz").arg(p.y(), 0, 'f', 2)); + + return t; + } +}; + +FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent):QwtPlot(parent){ + _startFrequency = 0; + _stopFrequency = 4000; + + timespec_reset(&_lastReplot); + + resize(parent->width(), parent->height()); + + _displayIntervalTime = (1.0/10.0); // 1/10 of a second between updates + + _useCenterFrequencyFlag = false; + + _numPoints = 1024; + _dataPoints = new double[_numPoints]; + _minFFTPoints = new double[_numPoints]; + _maxFFTPoints = new double[_numPoints]; + _xAxisPoints = new double[_numPoints]; + + // Disable polygon clipping + QwtPainter::setDeviceClipping(false); + + // We don't need the cache here + canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false); + canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false); + + QPalette palette; + palette.setColor(canvas()->backgroundRole(), QColor("white")); + canvas()->setPalette(palette); + + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); + setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency); + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScale(QwtPlot::yLeft, -210, 5); + setAxisTitle(QwtPlot::yLeft, "Power (dB)"); + + // Automatically deleted when parent is deleted + _fft_plot_curve = new QwtPlotCurve("Power Spectrum"); + _fft_plot_curve->attach(this); + _fft_plot_curve->setPen(QPen(Qt::blue)); + _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); + + _min_fft_plot_curve = new QwtPlotCurve("Minimum Power"); + _min_fft_plot_curve->attach(this); + _min_fft_plot_curve->setPen(QPen(Qt::magenta)); + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _min_fft_plot_curve->setVisible(false); + + _max_fft_plot_curve = new QwtPlotCurve("Maximum Power"); + _max_fft_plot_curve->attach(this); + _max_fft_plot_curve->setPen(QPen(Qt::darkYellow)); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); + _max_fft_plot_curve->setVisible(false); + + _lower_intensity_marker = new QwtPlotMarker(); + _lower_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + _lower_intensity_marker->setLinePen(QPen(Qt::cyan)); + _lower_intensity_marker->attach(this); + + _upper_intensity_marker = new QwtPlotMarker(); + _upper_intensity_marker->setLineStyle(QwtPlotMarker::HLine); + _upper_intensity_marker->setLinePen(QPen(Qt::green)); + _upper_intensity_marker->attach(this); + + memset(_dataPoints, 0x0, _numPoints*sizeof(double)); + memset(_xAxisPoints, 0x0, _numPoints*sizeof(double)); + + for(int64_t number = 0; number < _numPoints; number++){ + _minFFTPoints[number] = 200.0; + _maxFFTPoints[number] = -280.0; + } + + _resetXAxisPoints(); + + + // set up peak marker + QwtSymbol symbol; + + _markerPeakAmplitude = new QwtPlotMarker(); + _markerPeakAmplitude->setLinePen(QPen(Qt::yellow)); + symbol.setStyle(QwtSymbol::Diamond); + symbol.setSize(8); + symbol.setPen(QPen(Qt::yellow)); + symbol.setBrush(QBrush(Qt::yellow)); + _markerPeakAmplitude->setSymbol(symbol); + _markerPeakAmplitude->attach(this); + + _markerNoiseFloorAmplitude = new QwtPlotMarker(); + _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::HLine); + _markerNoiseFloorAmplitude->setLinePen(QPen(Qt::darkRed, 0, Qt::DotLine)); + _markerNoiseFloorAmplitude->attach(this); + + _peakFrequency = 0; + _peakAmplitude = -HUGE_VAL; + + _noiseFloorAmplitude = -HUGE_VAL; + + replot(); + + _zoomer = new FreqDisplayZoomer(canvas(), 0); +#if QT_VERSION < 0x040000 + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#else + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); +#endif + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + _panner = new QwtPlotPanner(canvas()); + _panner->setAxisEnabled(QwtPlot::yRight, false); + _panner->setMouseButton(Qt::MidButton); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font()); + QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft); + sd->setMinimumExtent( fm.width("100.00") ); + + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + +} + +FrequencyDisplayPlot::~FrequencyDisplayPlot(){ + delete[] _dataPoints; + delete[] _maxFFTPoints; + delete[] _minFFTPoints; + delete[] _xAxisPoints; + + // _fft_plot_curves deleted when parent deleted + // _zoomer and _panner deleted when parent deleted +} + +void FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq, const double constStopFreq, const double centerFrequency, const bool useCenterFrequencyFlag){ + double startFreq = constStartFreq; + double stopFreq = constStopFreq; + + _useCenterFrequencyFlag = useCenterFrequencyFlag; + + if(_useCenterFrequencyFlag){ + startFreq = (startFreq + centerFrequency) / 1000.0; + stopFreq = (stopFreq + centerFrequency) / 1000.0; + } + + if((stopFreq > 0) && (stopFreq > startFreq)){ + _startFrequency = startFreq; + _stopFrequency = stopFreq; + _resetXAxisPoints(); + + // Load up the new base zoom settings + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(_startFrequency); + newSize.setWidth(_stopFrequency-_startFrequency); + _zoomer->setZoomBase(newSize); + + // Zooms back to the base and clears any other zoom levels + _zoomer->zoom(0); + + setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency); + } + + if(useCenterFrequencyFlag){ + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(3)); + setAxisTitle(QwtPlot::xBottom, "RF Frequency (kHz)"); + ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(3); + } + else{ + setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0)); + setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)"); + ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(0); + } + + // Load up the new base zoom settings + QwtDoubleRect newSize = _zoomer->zoomBase(); + newSize.setLeft(_startFrequency); + newSize.setWidth(_stopFrequency-_startFrequency); + _zoomer->setZoomBase(newSize); + + // Zooms back to the base and clears any other zoom levels + _zoomer->zoom(0); +} + + +double FrequencyDisplayPlot::GetStartFrequency()const{ + return _startFrequency; +} + +double FrequencyDisplayPlot::GetStopFrequency()const{ + return _stopFrequency; +} + +void FrequencyDisplayPlot::replot(){ + + const timespec startTime = get_highres_clock(); + + _markerNoiseFloorAmplitude->setYValue(_noiseFloorAmplitude); + + // Make sure to take into account the start frequency + if(_useCenterFrequencyFlag){ + _markerPeakAmplitude->setXValue((_peakFrequency/1000.0) + _startFrequency); + } + else{ + _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency); + } + _markerPeakAmplitude->setYValue(_peakAmplitude); + + QwtPlot::replot(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + differenceTime *= 99.0; + // Require at least a 10% duty cycle + if(differenceTime > (1.0/10.0)){ + _displayIntervalTime = differenceTime; + } +} + +void FrequencyDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double noiseFloorAmplitude, const double peakFrequency, const double peakAmplitude){ + if(numDataPoints > 0){ + + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + delete[] _dataPoints; + delete[] _minFFTPoints; + delete[] _maxFFTPoints; + delete[] _xAxisPoints; + _dataPoints = new double[_numPoints]; + _xAxisPoints = new double[_numPoints]; + _minFFTPoints = new double[_numPoints]; + _maxFFTPoints = new double[_numPoints]; + + _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints); + _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints); + _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints); + + _resetXAxisPoints(); + ClearMaxData(); + ClearMinData(); + } + memcpy(_dataPoints, dataPoints, numDataPoints*sizeof(double)); + for(int64_t point = 0; point < numDataPoints; point++){ + if(dataPoints[point] < _minFFTPoints[point]){ + _minFFTPoints[point] = dataPoints[point]; + } + if(dataPoints[point] > _maxFFTPoints[point]){ + _maxFFTPoints[point] = dataPoints[point]; + } + } + + _noiseFloorAmplitude = noiseFloorAmplitude; + _peakFrequency = peakFrequency; + _peakAmplitude = peakAmplitude; + + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only replot the screen if it is visible + if(isVisible()){ + replot(); + } + _lastReplot = get_highres_clock(); + } +} + +void FrequencyDisplayPlot::ClearMaxData(){ + for(int64_t number = 0; number < _numPoints; number++){ + _maxFFTPoints[number] = -280.0; + } +} + +void FrequencyDisplayPlot::ClearMinData(){ + for(int64_t number = 0; number < _numPoints; number++){ + _minFFTPoints[number] = 200.0; + } +} + +void FrequencyDisplayPlot::SetMaxFFTVisible(const bool visibleFlag){ + _max_fft_plot_curve->setVisible(visibleFlag); +} + +void FrequencyDisplayPlot::SetMinFFTVisible(const bool visibleFlag){ + _min_fft_plot_curve->setVisible(visibleFlag); +} + +void FrequencyDisplayPlot::_resetXAxisPoints(){ + double fft_bin_size = (_stopFrequency-_startFrequency) / static_cast<double>(_numPoints); + double freqValue = _startFrequency; + for(int64_t loc = 0; loc < _numPoints; loc++){ + _xAxisPoints[loc] = freqValue; + freqValue += fft_bin_size; + } +} + +void FrequencyDisplayPlot::SetLowerIntensityLevel(const double lowerIntensityLevel){ + _lower_intensity_marker->setYValue( lowerIntensityLevel ); +} + +void FrequencyDisplayPlot::SetUpperIntensityLevel(const double upperIntensityLevel){ + _upper_intensity_marker->setYValue( upperIntensityLevel ); +} + + +#endif /* FREQUENCY_DISPLAY_PLOT_C */ |