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