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