Imported Upstream version 3.2.2
[debian/gnuradio] / gr-qtgui / src / lib / FrequencyDisplayPlot.cc
1 #ifndef FREQUENCY_DISPLAY_PLOT_C
2 #define FREQUENCY_DISPLAY_PLOT_C
3
4 #include <FrequencyDisplayPlot.h>
5
6 #include <qwt_scale_draw.h>
7
8 class FreqPrecisionClass
9 {
10 public:
11   FreqPrecisionClass(const int freqPrecision)
12   {
13     _frequencyPrecision = freqPrecision;
14   }
15
16   virtual ~FreqPrecisionClass()
17   {
18   }
19
20   virtual unsigned int GetFrequencyPrecision() const
21   {
22     return _frequencyPrecision;
23   }
24
25   virtual void SetFrequencyPrecision(const unsigned int newPrecision)
26   {
27     _frequencyPrecision = newPrecision;
28   }
29 protected:
30   unsigned int _frequencyPrecision;
31
32 private:
33
34 };
35
36 class FreqDisplayScaleDraw: public QwtScaleDraw, public FreqPrecisionClass
37 {
38 public:
39   FreqDisplayScaleDraw(const unsigned int precision)
40     : QwtScaleDraw(), FreqPrecisionClass(precision)
41   {
42   }
43
44   virtual ~FreqDisplayScaleDraw() 
45   {
46   }
47
48   virtual QwtText label(double value) const
49   {
50     return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision());
51   }
52
53 protected:
54
55 private:
56
57 };
58
59 class FreqDisplayZoomer: public QwtPlotZoomer, public FreqPrecisionClass
60 {
61 public:
62   FreqDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision)
63     : QwtPlotZoomer(canvas),FreqPrecisionClass(freqPrecision)
64   {
65     setTrackerMode(QwtPicker::AlwaysOn);
66   }
67
68   virtual ~FreqDisplayZoomer(){
69
70   }
71   
72   virtual void updateTrackerText(){
73     updateDisplay();
74   }
75
76 protected:
77   virtual QwtText trackerText( const QwtDoublePoint& p ) const 
78   {
79     QwtText t(QString("%1 %2, %3 dB").arg(p.x(), 0, 'f', GetFrequencyPrecision()).arg( (GetFrequencyPrecision() == 0) ? "Hz" : "kHz").arg(p.y(), 0, 'f', 2));
80
81     return t;
82   }
83 };
84
85 FrequencyDisplayPlot::FrequencyDisplayPlot(QWidget* parent)
86   : QwtPlot(parent)
87 {
88   _startFrequency = 0;
89   _stopFrequency = 4000;
90   
91   timespec_reset(&_lastReplot);
92
93   resize(parent->width(), parent->height());
94
95   _displayIntervalTime = (1.0/10.0); // 1/10 of a second between updates
96
97   _useCenterFrequencyFlag = false;
98
99   _numPoints = 1024;
100   _dataPoints = new double[_numPoints];
101   _minFFTPoints = new double[_numPoints];
102   _maxFFTPoints = new double[_numPoints];
103   _xAxisPoints = new double[_numPoints];
104
105   // Disable polygon clipping
106   QwtPainter::setDeviceClipping(false);
107   
108   // We don't need the cache here
109   canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
110   canvas()->setPaintAttribute(QwtPlotCanvas::PaintPacked, false);
111   
112   QPalette palette;
113   palette.setColor(canvas()->backgroundRole(), QColor("white"));
114   canvas()->setPalette(palette);  
115
116   setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(0));
117   setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
118   setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)");
119
120   _minYAxis = -120;
121   _maxYAxis = 10;
122   setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
123   setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis);
124   setAxisTitle(QwtPlot::yLeft, "Power (dB)");
125
126   // Automatically deleted when parent is deleted
127   _fft_plot_curve = new QwtPlotCurve("Power Spectrum");
128   _fft_plot_curve->attach(this);
129   _fft_plot_curve->setPen(QPen(Qt::blue));
130   _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints);
131
132   _min_fft_plot_curve = new QwtPlotCurve("Minimum Power");
133   _min_fft_plot_curve->attach(this);
134   _min_fft_plot_curve->setPen(QPen(Qt::magenta));
135   _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints);
136   _min_fft_plot_curve->setVisible(false);
137
138   _max_fft_plot_curve = new QwtPlotCurve("Maximum Power");
139   _max_fft_plot_curve->attach(this);
140   _max_fft_plot_curve->setPen(QPen(Qt::darkYellow));
141   _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints);
142   _max_fft_plot_curve->setVisible(false);
143
144   _lower_intensity_marker = new QwtPlotMarker();
145   _lower_intensity_marker->setLineStyle(QwtPlotMarker::HLine);
146   _lower_intensity_marker->setLinePen(QPen(Qt::cyan));
147   _lower_intensity_marker->attach(this);
148
149   _upper_intensity_marker = new QwtPlotMarker();
150   _upper_intensity_marker->setLineStyle(QwtPlotMarker::HLine);
151   _upper_intensity_marker->setLinePen(QPen(Qt::green));
152   _upper_intensity_marker->attach(this);
153
154   memset(_dataPoints, 0x0, _numPoints*sizeof(double));
155   memset(_xAxisPoints, 0x0, _numPoints*sizeof(double));
156
157   for(int64_t number = 0; number < _numPoints; number++){
158     _minFFTPoints[number] = 200.0;
159     _maxFFTPoints[number] = -280.0;
160   }
161
162   _resetXAxisPoints();
163
164
165   // set up peak marker
166   QwtSymbol symbol;
167
168   _markerPeakAmplitude = new QwtPlotMarker();
169   _markerPeakAmplitude->setLinePen(QPen(Qt::yellow));
170   symbol.setStyle(QwtSymbol::Diamond);
171   symbol.setSize(8);
172   symbol.setPen(QPen(Qt::yellow));
173   symbol.setBrush(QBrush(Qt::yellow));
174   _markerPeakAmplitude->setSymbol(symbol);
175   _markerPeakAmplitude->attach(this);
176
177   _markerNoiseFloorAmplitude = new QwtPlotMarker();
178   _markerNoiseFloorAmplitude->setLineStyle(QwtPlotMarker::HLine);
179   _markerNoiseFloorAmplitude->setLinePen(QPen(Qt::darkRed, 0, Qt::DotLine));
180   _markerNoiseFloorAmplitude->attach(this);
181
182   _peakFrequency = 0;
183   _peakAmplitude = -HUGE_VAL;
184
185   _noiseFloorAmplitude = -HUGE_VAL;
186
187   replot();
188
189   _zoomer = new FreqDisplayZoomer(canvas(), 0);
190 #if QT_VERSION < 0x040000
191   _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
192                           Qt::RightButton, Qt::ControlModifier);
193 #else
194   _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
195                           Qt::RightButton, Qt::ControlModifier);
196 #endif
197   _zoomer->setMousePattern(QwtEventPattern::MouseSelect3,
198                           Qt::RightButton);
199
200   _panner = new QwtPlotPanner(canvas());
201   _panner->setAxisEnabled(QwtPlot::yRight, false);
202   _panner->setMouseButton(Qt::MidButton);
203
204   // Avoid jumping when labels with more/less digits
205   // appear/disappear when scrolling vertically
206
207   const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font());
208   QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft);
209   sd->setMinimumExtent( fm.width("100.00") );
210
211   const QColor c(Qt::darkRed);
212   _zoomer->setRubberBandPen(c);
213   _zoomer->setTrackerPen(c);
214 }
215
216 FrequencyDisplayPlot::~FrequencyDisplayPlot()
217 {
218   delete[] _dataPoints;
219   delete[] _maxFFTPoints;
220   delete[] _minFFTPoints;
221   delete[] _xAxisPoints;
222
223   // _fft_plot_curves deleted when parent deleted
224   // _zoomer and _panner deleted when parent deleted
225 }
226
227 void
228 FrequencyDisplayPlot::set_yaxis(double min, double max)
229 {
230   // Get the new max/min values for the plot
231   _minYAxis = min;
232   _maxYAxis = max;
233
234   // Set the axis max/min to the new values
235   setAxisScale(QwtPlot::yLeft, _minYAxis, _maxYAxis);
236
237   // Reset the base zoom level to the new axis scale set here
238   _zoomer->setZoomBase();
239 }
240
241 void
242 FrequencyDisplayPlot::SetFrequencyRange(const double constStartFreq,
243                                         const double constStopFreq,
244                                         const double constCenterFreq,
245                                         const bool useCenterFrequencyFlag,
246                                         const double units, const std::string &strunits)
247 {
248   double startFreq = constStartFreq / units;
249   double stopFreq = constStopFreq / units;
250   double centerFreq = constCenterFreq / units;
251
252   _useCenterFrequencyFlag = useCenterFrequencyFlag;
253
254   if(_useCenterFrequencyFlag){
255     startFreq = (startFreq + centerFreq);
256     stopFreq = (stopFreq + centerFreq);
257   }
258
259   _startFrequency = startFreq;
260   _stopFrequency = stopFreq;
261   _resetXAxisPoints();
262
263   setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
264   setAxisScaleDraw(QwtPlot::xBottom, new FreqDisplayScaleDraw(2));
265   setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str()));
266   ((FreqDisplayZoomer*)_zoomer)->SetFrequencyPrecision(2);
267
268   // Load up the new base zoom settings
269   _zoomer->setZoomBase();
270   
271   // Zooms back to the base and clears any other zoom levels
272   _zoomer->zoom(0);
273 }
274
275
276 double
277 FrequencyDisplayPlot::GetStartFrequency() const
278 {
279   return _startFrequency;
280 }
281
282 double
283 FrequencyDisplayPlot::GetStopFrequency() const
284 {
285   return _stopFrequency;
286 }
287
288 void
289 FrequencyDisplayPlot::replot()
290 {
291   const timespec startTime = get_highres_clock();
292
293   _markerNoiseFloorAmplitude->setYValue(_noiseFloorAmplitude);
294   
295   // Make sure to take into account the start frequency
296   if(_useCenterFrequencyFlag){
297     _markerPeakAmplitude->setXValue((_peakFrequency/1000.0) + _startFrequency);
298   }
299   else{
300     _markerPeakAmplitude->setXValue(_peakFrequency + _startFrequency);
301   }
302   _markerPeakAmplitude->setYValue(_peakAmplitude);
303   
304   QwtPlot::replot();
305
306   double differenceTime = (diff_timespec(get_highres_clock(), startTime));
307
308   differenceTime *= 99.0;
309   // Require at least a 10% duty cycle
310   if(differenceTime > (1.0/10.0)){
311     _displayIntervalTime = differenceTime;
312   }
313 }
314
315 void
316 FrequencyDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints,
317                                   const double noiseFloorAmplitude, const double peakFrequency,
318                                   const double peakAmplitude)
319 {
320   if(numDataPoints > 0){
321
322     if(numDataPoints != _numPoints){
323       _numPoints = numDataPoints;
324
325       delete[] _dataPoints;
326       delete[] _minFFTPoints;
327       delete[] _maxFFTPoints;
328       delete[] _xAxisPoints;
329       _dataPoints = new double[_numPoints];
330       _xAxisPoints = new double[_numPoints];
331       _minFFTPoints = new double[_numPoints];
332       _maxFFTPoints = new double[_numPoints];
333       
334       _fft_plot_curve->setRawData(_xAxisPoints, _dataPoints, _numPoints);
335       _min_fft_plot_curve->setRawData(_xAxisPoints, _minFFTPoints, _numPoints);
336       _max_fft_plot_curve->setRawData(_xAxisPoints, _maxFFTPoints, _numPoints);
337
338       _resetXAxisPoints();
339       ClearMaxData();
340       ClearMinData();
341     }
342     memcpy(_dataPoints, dataPoints, numDataPoints*sizeof(double));
343     for(int64_t point = 0; point < numDataPoints; point++){
344       if(dataPoints[point] < _minFFTPoints[point]){
345         _minFFTPoints[point] = dataPoints[point];
346       }
347       if(dataPoints[point] > _maxFFTPoints[point]){
348         _maxFFTPoints[point] = dataPoints[point];
349       }
350     }
351
352     _noiseFloorAmplitude = noiseFloorAmplitude;
353     _peakFrequency = peakFrequency;
354     _peakAmplitude = peakAmplitude;
355
356   }
357
358   // Allow at least a 50% duty cycle
359   if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){
360     // Only replot the screen if it is visible
361     if(isVisible()){
362       replot();
363     }
364     _lastReplot = get_highres_clock();
365   }
366 }
367
368 void
369 FrequencyDisplayPlot::ClearMaxData()
370 {
371   for(int64_t number = 0; number < _numPoints; number++){
372     _maxFFTPoints[number] = _maxYAxis;
373   }
374 }
375
376 void
377 FrequencyDisplayPlot::ClearMinData()
378 {
379   for(int64_t number = 0; number < _numPoints; number++){
380     _minFFTPoints[number] = _minYAxis;
381   }
382 }
383
384 void
385 FrequencyDisplayPlot::SetMaxFFTVisible(const bool visibleFlag)
386 {
387   _max_fft_plot_curve->setVisible(visibleFlag);
388 }
389
390 void
391 FrequencyDisplayPlot::SetMinFFTVisible(const bool visibleFlag)
392 {
393   _min_fft_plot_curve->setVisible(visibleFlag);
394 }
395
396 void
397 FrequencyDisplayPlot::_resetXAxisPoints()
398 {
399   double fft_bin_size = (_stopFrequency-_startFrequency) / static_cast<double>(_numPoints);
400   double freqValue = _startFrequency;
401   for(int64_t loc = 0; loc < _numPoints; loc++){
402     _xAxisPoints[loc] = freqValue;
403     freqValue += fft_bin_size;
404   }
405 }
406
407 void
408 FrequencyDisplayPlot::SetLowerIntensityLevel(const double lowerIntensityLevel)
409 {
410   _lower_intensity_marker->setYValue( lowerIntensityLevel );
411 }
412
413 void
414 FrequencyDisplayPlot::SetUpperIntensityLevel(const double upperIntensityLevel)
415 {
416   _upper_intensity_marker->setYValue( upperIntensityLevel );
417 }
418
419
420 #endif /* FREQUENCY_DISPLAY_PLOT_C */