~ubuntu-branches/ubuntu/trusty/gnuradio/trusty

« back to all changes in this revision

Viewing changes to gr-qtgui/src/lib/WaterfallDisplayPlot.cc

  • Committer: Bazaar Package Importer
  • Author(s): Kamal Mostafa
  • Date: 2010-03-13 07:46:01 UTC
  • mfrom: (2.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100313074601-zjsa893a87bozyh7
Tags: 3.2.2.dfsg-1ubuntu1
* Fix build for Ubuntu lucid (LP: #260406)
  - add binary package dep for libusrp0, libusrp2-0: adduser
  - debian/rules clean: remove pre-built Qt moc files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifndef WATERFALL_DISPLAY_PLOT_C
 
2
#define WATERFALL_DISPLAY_PLOT_C
 
3
 
 
4
#include <WaterfallDisplayPlot.h>
 
5
 
 
6
#include <qwt_color_map.h>
 
7
#include <qwt_scale_widget.h>
 
8
#include <qwt_scale_draw.h>
 
9
#include <qwt_plot_zoomer.h>
 
10
#include <qwt_plot_panner.h>
 
11
#include <qwt_plot_layout.h>
 
12
 
 
13
#include <qapplication.h>
 
14
 
 
15
class FreqOffsetAndPrecisionClass
 
16
{
 
17
public:
 
18
  FreqOffsetAndPrecisionClass(const int freqPrecision){
 
19
    _frequencyPrecision = freqPrecision;
 
20
    _centerFrequency = 0;
 
21
  }
 
22
 
 
23
  virtual ~FreqOffsetAndPrecisionClass(){
 
24
 
 
25
  }
 
26
 
 
27
  virtual unsigned int GetFrequencyPrecision()const{
 
28
    return _frequencyPrecision;
 
29
  }
 
30
 
 
31
  virtual void SetFrequencyPrecision(const unsigned int newPrecision){
 
32
    _frequencyPrecision = newPrecision;
 
33
  }
 
34
 
 
35
  virtual double GetCenterFrequency()const{
 
36
    return _centerFrequency;
 
37
  }
 
38
 
 
39
  virtual void SetCenterFrequency(const double newFreq){
 
40
    _centerFrequency = newFreq;
 
41
  }
 
42
 
 
43
protected:
 
44
  unsigned int _frequencyPrecision;
 
45
  double _centerFrequency;
 
46
 
 
47
private:
 
48
 
 
49
};
 
50
 
 
51
class WaterfallFreqDisplayScaleDraw: public QwtScaleDraw, public FreqOffsetAndPrecisionClass{
 
52
public:
 
53
  WaterfallFreqDisplayScaleDraw(const unsigned int precision):QwtScaleDraw(), FreqOffsetAndPrecisionClass(precision){
 
54
 
 
55
  }
 
56
 
 
57
  virtual ~WaterfallFreqDisplayScaleDraw(){
 
58
 
 
59
  }
 
60
 
 
61
  QwtText label(double value)const{
 
62
    return QString("%1").arg((value + GetCenterFrequency()) / ((GetFrequencyPrecision() == 0) ? 1.0 : 1000.0), 0, 'f', GetFrequencyPrecision());
 
63
  }
 
64
 
 
65
  virtual void initiateUpdate(){
 
66
    invalidateCache();
 
67
  }
 
68
 
 
69
protected:
 
70
 
 
71
private:
 
72
 
 
73
};
 
74
 
 
75
class TimeScaleData
 
76
{
 
77
public:
 
78
  TimeScaleData(){
 
79
    timespec_reset(&_zeroTime);
 
80
    _secondsPerLine = 1.0;
 
81
    
 
82
  }
 
83
  
 
84
  virtual ~TimeScaleData(){
 
85
    
 
86
  }
 
87
 
 
88
  virtual timespec GetZeroTime()const{
 
89
    return _zeroTime;
 
90
  }
 
91
  
 
92
  virtual void SetZeroTime(const timespec newTime){
 
93
    _zeroTime = newTime;
 
94
  }
 
95
 
 
96
  virtual void SetSecondsPerLine(const double newTime){
 
97
    _secondsPerLine = newTime;
 
98
  }
 
99
 
 
100
  virtual double GetSecondsPerLine()const{
 
101
    return _secondsPerLine;
 
102
  }
 
103
 
 
104
  
 
105
protected:
 
106
  timespec _zeroTime;
 
107
  double _secondsPerLine;
 
108
  
 
109
private:
 
110
  
 
111
};
 
112
 
 
113
class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData
 
114
{
 
115
public:
 
116
  QwtTimeScaleDraw():QwtScaleDraw(),TimeScaleData(){
 
117
    
 
118
  }
 
119
 
 
120
  virtual ~QwtTimeScaleDraw(){
 
121
    
 
122
  }
 
123
 
 
124
  virtual QwtText label(double value)const{
 
125
    QwtText returnLabel("");
 
126
 
 
127
    timespec lineTime = timespec_add(GetZeroTime(), (-value) * GetSecondsPerLine());
 
128
    struct tm timeTm;
 
129
    gmtime_r(&lineTime.tv_sec, &timeTm);
 
130
    returnLabel = (QString("").sprintf("%04d/%02d/%02d\n%02d:%02d:%02d.%03ld", timeTm.tm_year+1900, timeTm.tm_mon+1, timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min, timeTm.tm_sec, lineTime.tv_nsec/1000000));
 
131
    
 
132
    return returnLabel;
 
133
  }
 
134
 
 
135
  virtual void initiateUpdate(){
 
136
    // Do this in one call rather than when zeroTime and secondsPerLine updates is to prevent the display from being updated too often...
 
137
    invalidateCache();
 
138
  }
 
139
  
 
140
protected:
 
141
 
 
142
private:
 
143
 
 
144
};
 
145
 
 
146
class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass
 
147
{
 
148
public:
 
149
  WaterfallZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision):QwtPlotZoomer(canvas), TimeScaleData(), FreqOffsetAndPrecisionClass(freqPrecision)
 
150
  {
 
151
    setTrackerMode(QwtPicker::AlwaysOn);
 
152
  }
 
153
 
 
154
  virtual ~WaterfallZoomer(){
 
155
 
 
156
  }
 
157
  
 
158
  virtual void updateTrackerText(){
 
159
    updateDisplay();
 
160
  }
 
161
 
 
162
protected:
 
163
  virtual QwtText trackerText( const QwtDoublePoint& p ) const 
 
164
  {
 
165
    QString yLabel("");
 
166
 
 
167
    timespec lineTime = timespec_add(GetZeroTime(), (-p.y()) * GetSecondsPerLine());
 
168
    struct tm timeTm;
 
169
    gmtime_r(&lineTime.tv_sec, &timeTm);
 
170
    yLabel = (QString("").sprintf("%04d/%02d/%02d %02d:%02d:%02d.%03ld", timeTm.tm_year+1900, timeTm.tm_mon+1, timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min, timeTm.tm_sec, lineTime.tv_nsec/1000000));
 
171
 
 
172
    QwtText t(QString("%1 %2, %3").arg((p.x() + GetCenterFrequency()) / ((GetFrequencyPrecision() == 0) ? 1.0 : 1000.0), 0, 'f', GetFrequencyPrecision()).arg( (GetFrequencyPrecision() == 0) ? "Hz" : "kHz").arg(yLabel));
 
173
 
 
174
    return t;
 
175
  }
 
176
};
 
177
 
 
178
 
 
179
const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
 
180
const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT;
 
181
const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT;
 
182
const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT;
 
183
const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED;
 
184
 
 
185
WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent)
 
186
  : QwtPlot(parent)
 
187
{
 
188
  _zoomer = NULL;
 
189
  _startFrequency = 0;
 
190
  _stopFrequency = 4000;
 
191
  
 
192
  resize(parent->width(), parent->height());
 
193
  _numPoints = 1024;
 
194
 
 
195
  _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates
 
196
 
 
197
  _waterfallData = new WaterfallData(_startFrequency, _stopFrequency, _numPoints, 200);
 
198
 
 
199
  QPalette palette;
 
200
  palette.setColor(canvas()->backgroundRole(), QColor("white"));
 
201
  canvas()->setPalette(palette);   
 
202
 
 
203
  setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)");
 
204
  setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(0));
 
205
 
 
206
  setAxisTitle(QwtPlot::yLeft, "Time");
 
207
  setAxisScaleDraw(QwtPlot::yLeft, new QwtTimeScaleDraw());
 
208
 
 
209
  timespec_reset(&_lastReplot);
 
210
 
 
211
  d_spectrogram = new PlotWaterfall(_waterfallData, "Waterfall Display");
 
212
 
 
213
  _intensityColorMapType = INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
 
214
 
 
215
  QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
 
216
  colorMap.addColorStop(0.25, Qt::cyan);
 
217
  colorMap.addColorStop(0.5, Qt::yellow);
 
218
  colorMap.addColorStop(0.75, Qt::red);
 
219
 
 
220
  d_spectrogram->setColorMap(colorMap);
 
221
  
 
222
  d_spectrogram->attach(this);
 
223
  
 
224
  // LeftButton for the zooming
 
225
  // MidButton for the panning
 
226
  // RightButton: zoom out by 1
 
227
  // Ctrl+RighButton: zoom out to full size
 
228
  
 
229
  _zoomer = new WaterfallZoomer(canvas(), 0);
 
230
#if QT_VERSION < 0x040000
 
231
  _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
 
232
                           Qt::RightButton, Qt::ControlModifier);
 
233
#else
 
234
  _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
 
235
                           Qt::RightButton, Qt::ControlModifier);
 
236
#endif
 
237
  _zoomer->setMousePattern(QwtEventPattern::MouseSelect3,
 
238
                           Qt::RightButton);
 
239
  
 
240
  _panner = new QwtPlotPanner(canvas());
 
241
  _panner->setAxisEnabled(QwtPlot::yRight, false);
 
242
  _panner->setMouseButton(Qt::MidButton);
 
243
  
 
244
  // Avoid jumping when labels with more/less digits
 
245
  // appear/disappear when scrolling vertically
 
246
  
 
247
  const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font());
 
248
  QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft);
 
249
  sd->setMinimumExtent( fm.width("100.00") );
 
250
  
 
251
  const QColor c(Qt::white);
 
252
  _zoomer->setRubberBandPen(c);
 
253
  _zoomer->setTrackerPen(c);
 
254
 
 
255
  _UpdateIntensityRangeDisplay();
 
256
}
 
257
 
 
258
WaterfallDisplayPlot::~WaterfallDisplayPlot()
 
259
{
 
260
  delete _waterfallData;
 
261
}
 
262
 
 
263
void 
 
264
WaterfallDisplayPlot::Reset()
 
265
{
 
266
  _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints);
 
267
  _waterfallData->Reset();
 
268
 
 
269
  // Load up the new base zoom settings
 
270
  QwtDoubleRect newSize = _zoomer->zoomBase();
 
271
  newSize.setLeft(_startFrequency);
 
272
  newSize.setWidth(_stopFrequency-_startFrequency);
 
273
  _zoomer->zoom(newSize);
 
274
  _zoomer->setZoomBase(newSize);
 
275
  _zoomer->zoom(0);
 
276
}
 
277
 
 
278
void
 
279
WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq,
 
280
                                        const double constStopFreq,
 
281
                                        const double constCenterFreq,
 
282
                                        const bool useCenterFrequencyFlag,
 
283
                                        const double units, const std::string &strunits)
 
284
{
 
285
  double startFreq = constStartFreq / units;
 
286
  double stopFreq = constStopFreq / units;
 
287
  double centerFreq = constCenterFreq / units;
 
288
 
 
289
  if(stopFreq > startFreq) {
 
290
    _startFrequency = 1000*startFreq;
 
291
    _stopFrequency = 1000*stopFreq;
 
292
 
 
293
    setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
 
294
 
 
295
    if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){
 
296
      WaterfallFreqDisplayScaleDraw* freqScale = ((WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom));
 
297
      freqScale->SetCenterFrequency(centerFreq);
 
298
      ((WaterfallZoomer*)_zoomer)->SetCenterFrequency(centerFreq);
 
299
 
 
300
      freqScale->SetFrequencyPrecision( 2 );
 
301
      ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision( 2 );
 
302
      setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str()));
 
303
    }
 
304
 
 
305
    Reset();
 
306
 
 
307
    // Only replot if screen is visible
 
308
    if(isVisible()){
 
309
      replot();
 
310
    }
 
311
  }
 
312
}
 
313
 
 
314
 
 
315
double WaterfallDisplayPlot::GetStartFrequency()const{
 
316
  return _startFrequency;
 
317
}
 
318
 
 
319
double WaterfallDisplayPlot::GetStopFrequency()const{
 
320
  return _stopFrequency;
 
321
}
 
322
 
 
323
void WaterfallDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames){
 
324
  if(numDataPoints > 0){
 
325
    if(numDataPoints != _numPoints){
 
326
      _numPoints = numDataPoints;
 
327
 
 
328
      Reset();
 
329
 
 
330
      d_spectrogram->invalidateCache();
 
331
      d_spectrogram->itemChanged();
 
332
 
 
333
      if(isVisible()){
 
334
        replot();
 
335
      }
 
336
 
 
337
      _lastReplot = get_highres_clock();
 
338
    }
 
339
 
 
340
    _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames);
 
341
    _waterfallData->IncrementNumLinesToUpdate();
 
342
 
 
343
    QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
 
344
    timeScale->SetSecondsPerLine(timePerFFT);
 
345
    timeScale->SetZeroTime(timestamp);
 
346
 
 
347
    ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT);
 
348
    ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp);
 
349
  }
 
350
 
 
351
  // Allow at least a 50% duty cycle
 
352
  if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){
 
353
 
 
354
    d_spectrogram->invalidateCache();
 
355
    d_spectrogram->itemChanged();
 
356
 
 
357
    // Only update when window is visible
 
358
    if(isVisible()){
 
359
      replot();
 
360
    }
 
361
 
 
362
    _lastReplot = get_highres_clock();
 
363
  }
 
364
}
 
365
 
 
366
void WaterfallDisplayPlot::SetIntensityRange(const double minIntensity, const double maxIntensity){
 
367
  _waterfallData->setRange(QwtDoubleInterval(minIntensity, maxIntensity));
 
368
 
 
369
  emit UpdatedLowerIntensityLevel(minIntensity);
 
370
  emit UpdatedUpperIntensityLevel(maxIntensity);
 
371
 
 
372
  _UpdateIntensityRangeDisplay();
 
373
}
 
374
 
 
375
void WaterfallDisplayPlot::replot(){
 
376
  const timespec startTime = get_highres_clock();
 
377
 
 
378
  QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
 
379
  timeScale->initiateUpdate();
 
380
 
 
381
  WaterfallFreqDisplayScaleDraw* freqScale = (WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom);
 
382
  freqScale->initiateUpdate();
 
383
 
 
384
  // Update the time axis display
 
385
  if(axisWidget(QwtPlot::yLeft) != NULL){
 
386
    axisWidget(QwtPlot::yLeft)->update();
 
387
  }
 
388
 
 
389
  // Update the Frequency Offset Display
 
390
  if(axisWidget(QwtPlot::xBottom) != NULL){
 
391
    axisWidget(QwtPlot::xBottom)->update();
 
392
  }
 
393
 
 
394
  if(_zoomer != NULL){
 
395
    ((WaterfallZoomer*)_zoomer)->updateTrackerText();
 
396
  }
 
397
 
 
398
  QwtPlot::replot();
 
399
 
 
400
  double differenceTime = (diff_timespec(get_highres_clock(), startTime));
 
401
  
 
402
  // Require at least a 5% duty cycle
 
403
  differenceTime *= 19.0;
 
404
  if(differenceTime > (1.0/5.0)){
 
405
    _displayIntervalTime = differenceTime;
 
406
  }
 
407
}
 
408
 
 
409
int WaterfallDisplayPlot::GetIntensityColorMapType()const{
 
410
  return _intensityColorMapType;
 
411
}
 
412
 
 
413
void WaterfallDisplayPlot::SetIntensityColorMapType(const int newType, const QColor lowColor, const QColor highColor){
 
414
  if((_intensityColorMapType != newType) || 
 
415
     ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) &&
 
416
      (lowColor.isValid() && highColor.isValid()))){
 
417
    switch(newType){
 
418
    case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{
 
419
      _intensityColorMapType = newType;
 
420
      QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
 
421
      colorMap.addColorStop(0.25, Qt::cyan);
 
422
      colorMap.addColorStop(0.5, Qt::yellow);
 
423
      colorMap.addColorStop(0.75, Qt::red);
 
424
      d_spectrogram->setColorMap(colorMap);
 
425
      break;
 
426
    }
 
427
    case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{
 
428
      _intensityColorMapType = newType;
 
429
      QwtLinearColorMap colorMap(Qt::black, Qt::white);
 
430
      d_spectrogram->setColorMap(colorMap);
 
431
      break;
 
432
    }
 
433
    case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{
 
434
      _intensityColorMapType = newType;
 
435
      QwtLinearColorMap colorMap(Qt::white, Qt::black);
 
436
      d_spectrogram->setColorMap(colorMap);
 
437
      break;
 
438
    }
 
439
    case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{
 
440
      _intensityColorMapType = newType;
 
441
      QwtLinearColorMap colorMap(Qt::black, Qt::white);
 
442
      colorMap.addColorStop(0.5, Qt::darkRed);
 
443
      d_spectrogram->setColorMap(colorMap);
 
444
      break;
 
445
    }
 
446
    case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{
 
447
      _userDefinedLowIntensityColor = lowColor;
 
448
      _userDefinedHighIntensityColor = highColor;
 
449
      _intensityColorMapType = newType;
 
450
      QwtLinearColorMap colorMap(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor);
 
451
      d_spectrogram->setColorMap(colorMap);
 
452
      break;
 
453
    }
 
454
    default: break;
 
455
    }
 
456
    
 
457
    _UpdateIntensityRangeDisplay();
 
458
  }
 
459
}
 
460
 
 
461
const QColor WaterfallDisplayPlot::GetUserDefinedLowIntensityColor()const{
 
462
  return _userDefinedLowIntensityColor;
 
463
}
 
464
 
 
465
const QColor WaterfallDisplayPlot::GetUserDefinedHighIntensityColor()const{
 
466
  return _userDefinedHighIntensityColor;
 
467
}
 
468
 
 
469
void WaterfallDisplayPlot::_UpdateIntensityRangeDisplay(){
 
470
  QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight);
 
471
  rightAxis->setTitle("Intensity (dB)");
 
472
  rightAxis->setColorBarEnabled(true);
 
473
  rightAxis->setColorMap(d_spectrogram->data()->range(),
 
474
                         d_spectrogram->colorMap());
 
475
 
 
476
  setAxisScale(QwtPlot::yRight, 
 
477
               d_spectrogram->data()->range().minValue(),
 
478
               d_spectrogram->data()->range().maxValue() );
 
479
  enableAxis(QwtPlot::yRight);
 
480
  
 
481
  plotLayout()->setAlignCanvasToScales(true);
 
482
 
 
483
  // Tell the display to redraw everything
 
484
  d_spectrogram->invalidateCache();
 
485
  d_spectrogram->itemChanged();
 
486
 
 
487
  // Draw again
 
488
  replot();
 
489
 
 
490
  // Update the last replot timer
 
491
  _lastReplot = get_highres_clock();
 
492
}
 
493
 
 
494
#endif /* WATERFALL_DISPLAY_PLOT_C */