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