summaryrefslogtreecommitdiff
path: root/gr-qtgui/src/lib/FrequencyDisplayPlot.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gr-qtgui/src/lib/FrequencyDisplayPlot.cc')
-rw-r--r--gr-qtgui/src/lib/FrequencyDisplayPlot.cc386
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 */