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