1 #ifndef WATERFALL_DISPLAY_PLOT_C
2 #define WATERFALL_DISPLAY_PLOT_C
4 #include <WaterfallDisplayPlot.h>
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>
13 #include <qapplication.h>
15 class FreqOffsetAndPrecisionClass
18 FreqOffsetAndPrecisionClass(const int freqPrecision)
20 _frequencyPrecision = freqPrecision;
24 virtual ~FreqOffsetAndPrecisionClass()
28 virtual unsigned int GetFrequencyPrecision() const
30 return _frequencyPrecision;
33 virtual void SetFrequencyPrecision(const unsigned int newPrecision)
35 _frequencyPrecision = newPrecision;
38 virtual double GetCenterFrequency() const
40 return _centerFrequency;
43 virtual void SetCenterFrequency(const double newFreq)
45 _centerFrequency = newFreq;
49 unsigned int _frequencyPrecision;
50 double _centerFrequency;
56 class WaterfallFreqDisplayScaleDraw: public QwtScaleDraw, public FreqOffsetAndPrecisionClass{
58 WaterfallFreqDisplayScaleDraw(const unsigned int precision)
59 : QwtScaleDraw(), FreqOffsetAndPrecisionClass(precision)
63 virtual ~WaterfallFreqDisplayScaleDraw()
67 QwtText label(double value) const
69 return QString("%1").arg(value, 0, 'f', GetFrequencyPrecision());
72 virtual void initiateUpdate()
88 timespec_reset(&_zeroTime);
89 _secondsPerLine = 1.0;
92 virtual ~TimeScaleData()
96 virtual timespec GetZeroTime() const
101 virtual void SetZeroTime(const timespec newTime)
106 virtual void SetSecondsPerLine(const double newTime)
108 _secondsPerLine = newTime;
111 virtual double GetSecondsPerLine() const
113 return _secondsPerLine;
119 double _secondsPerLine;
125 class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData
128 QwtTimeScaleDraw():QwtScaleDraw(),TimeScaleData()
132 virtual ~QwtTimeScaleDraw()
136 virtual QwtText label(double value) const
138 QwtText returnLabel("");
140 timespec lineTime = timespec_add(GetZeroTime(), (-value) * GetSecondsPerLine());
142 gmtime_r(&lineTime.tv_sec, &timeTm);
143 returnLabel = (QString("").sprintf("%04d/%02d/%02d\n%02d:%02d:%02d.%03ld",
144 timeTm.tm_year+1900, timeTm.tm_mon+1,
145 timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min,
146 timeTm.tm_sec, lineTime.tv_nsec/1000000));
150 virtual void initiateUpdate()
152 // Do this in one call rather than when zeroTime and secondsPerLine
153 // updates is to prevent the display from being updated too often...
163 class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass
166 WaterfallZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision)
167 : QwtPlotZoomer(canvas), TimeScaleData(),
168 FreqOffsetAndPrecisionClass(freqPrecision)
170 setTrackerMode(QwtPicker::AlwaysOn);
173 virtual ~WaterfallZoomer()
177 virtual void updateTrackerText()
182 void SetUnitType(const std::string &type)
188 virtual QwtText trackerText( const QwtDoublePoint& p ) const
192 timespec lineTime = timespec_add(GetZeroTime(), (-p.y()) * GetSecondsPerLine());
194 gmtime_r(&lineTime.tv_sec, &timeTm);
195 yLabel = (QString("").sprintf("%04d/%02d/%02d %02d:%02d:%02d.%03ld",
196 timeTm.tm_year+1900, timeTm.tm_mon+1,
197 timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min,
198 timeTm.tm_sec, lineTime.tv_nsec/1000000));
200 QwtText t(QString("%1 %2, %3").arg(p.x(), 0, 'f',
201 GetFrequencyPrecision()).arg(_unitType.c_str()).arg(yLabel));
207 std::string _unitType;
211 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
212 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT;
213 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT;
214 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT;
215 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED;
217 WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent)
222 _stopFrequency = 4000;
224 resize(parent->width(), parent->height());
227 _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates
229 _waterfallData = new WaterfallData(_startFrequency, _stopFrequency, _numPoints, 200);
232 palette.setColor(canvas()->backgroundRole(), QColor("white"));
233 canvas()->setPalette(palette);
235 setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)");
236 setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(0));
238 setAxisTitle(QwtPlot::yLeft, "Time");
239 setAxisScaleDraw(QwtPlot::yLeft, new QwtTimeScaleDraw());
241 timespec_reset(&_lastReplot);
243 d_spectrogram = new PlotWaterfall(_waterfallData, "Waterfall Display");
245 _intensityColorMapType = INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
247 QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
248 colorMap.addColorStop(0.25, Qt::cyan);
249 colorMap.addColorStop(0.5, Qt::yellow);
250 colorMap.addColorStop(0.75, Qt::red);
252 d_spectrogram->setColorMap(colorMap);
254 d_spectrogram->attach(this);
256 // LeftButton for the zooming
257 // MidButton for the panning
258 // RightButton: zoom out by 1
259 // Ctrl+RighButton: zoom out to full size
261 _zoomer = new WaterfallZoomer(canvas(), 0);
262 #if QT_VERSION < 0x040000
263 _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
264 Qt::RightButton, Qt::ControlModifier);
266 _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
267 Qt::RightButton, Qt::ControlModifier);
269 _zoomer->setMousePattern(QwtEventPattern::MouseSelect3,
272 _panner = new QwtPlotPanner(canvas());
273 _panner->setAxisEnabled(QwtPlot::yRight, false);
274 _panner->setMouseButton(Qt::MidButton);
276 // Avoid jumping when labels with more/less digits
277 // appear/disappear when scrolling vertically
279 const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font());
280 QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft);
281 sd->setMinimumExtent( fm.width("100.00") );
283 const QColor c(Qt::white);
284 _zoomer->setRubberBandPen(c);
285 _zoomer->setTrackerPen(c);
287 _UpdateIntensityRangeDisplay();
290 WaterfallDisplayPlot::~WaterfallDisplayPlot()
292 delete _waterfallData;
296 WaterfallDisplayPlot::Reset()
298 _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints);
299 _waterfallData->Reset();
301 // Load up the new base zoom settings
302 QwtDoubleRect newSize = _zoomer->zoomBase();
303 newSize.setLeft(_startFrequency);
304 newSize.setWidth(_stopFrequency-_startFrequency);
305 _zoomer->zoom(newSize);
306 _zoomer->setZoomBase(newSize);
311 WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq,
312 const double constStopFreq,
313 const double constCenterFreq,
314 const bool useCenterFrequencyFlag,
315 const double units, const std::string &strunits)
317 double startFreq = constStartFreq / units;
318 double stopFreq = constStopFreq / units;
319 double centerFreq = constCenterFreq / units;
321 _useCenterFrequencyFlag = useCenterFrequencyFlag;
323 if(_useCenterFrequencyFlag){
324 startFreq = (startFreq + centerFreq);
325 stopFreq = (stopFreq + centerFreq);
329 if((startFreq != _startFrequency) || (stopFreq != _stopFrequency))
332 if(stopFreq > startFreq) {
333 _startFrequency = startFreq;
334 _stopFrequency = stopFreq;
337 if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){
338 double display_units = ceil(log10(units)/2.0);
339 setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
340 setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(display_units));
346 ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision(display_units);
347 ((WaterfallZoomer*)_zoomer)->SetUnitType(strunits);
349 // Load up the new base zoom settings
350 _zoomer->setZoomBase();
352 // Zooms back to the base and clears any other zoom levels
360 WaterfallDisplayPlot::GetStartFrequency() const
362 return _startFrequency;
366 WaterfallDisplayPlot::GetStopFrequency() const
368 return _stopFrequency;
372 WaterfallDisplayPlot::PlotNewData(const double* dataPoints,
373 const int64_t numDataPoints,
374 const double timePerFFT,
375 const timespec timestamp,
376 const int droppedFrames)
378 if(numDataPoints > 0){
379 if(numDataPoints != _numPoints){
380 _numPoints = numDataPoints;
384 d_spectrogram->invalidateCache();
385 d_spectrogram->itemChanged();
391 _lastReplot = get_highres_clock();
394 _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames);
395 _waterfallData->IncrementNumLinesToUpdate();
397 QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
398 timeScale->SetSecondsPerLine(timePerFFT);
399 timeScale->SetZeroTime(timestamp);
401 ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT);
402 ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp);
405 // Allow at least a 50% duty cycle
406 if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){
408 d_spectrogram->invalidateCache();
409 d_spectrogram->itemChanged();
411 // Only update when window is visible
416 _lastReplot = get_highres_clock();
421 WaterfallDisplayPlot::SetIntensityRange(const double minIntensity,
422 const double maxIntensity)
424 _waterfallData->setRange(QwtDoubleInterval(minIntensity, maxIntensity));
426 emit UpdatedLowerIntensityLevel(minIntensity);
427 emit UpdatedUpperIntensityLevel(maxIntensity);
429 _UpdateIntensityRangeDisplay();
433 WaterfallDisplayPlot::replot()
435 const timespec startTime = get_highres_clock();
437 QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
438 timeScale->initiateUpdate();
440 WaterfallFreqDisplayScaleDraw* freqScale = (WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom);
441 freqScale->initiateUpdate();
443 // Update the time axis display
444 if(axisWidget(QwtPlot::yLeft) != NULL){
445 axisWidget(QwtPlot::yLeft)->update();
448 // Update the Frequency Offset Display
449 if(axisWidget(QwtPlot::xBottom) != NULL){
450 axisWidget(QwtPlot::xBottom)->update();
454 ((WaterfallZoomer*)_zoomer)->updateTrackerText();
459 double differenceTime = (diff_timespec(get_highres_clock(), startTime));
461 // Require at least a 5% duty cycle
462 differenceTime *= 19.0;
463 if(differenceTime > (1.0/5.0)){
464 _displayIntervalTime = differenceTime;
469 WaterfallDisplayPlot::resizeSlot( QSize *s )
471 resize(s->width(), s->height());
475 WaterfallDisplayPlot::GetIntensityColorMapType() const
477 return _intensityColorMapType;
481 WaterfallDisplayPlot::SetIntensityColorMapType(const int newType,
482 const QColor lowColor,
483 const QColor highColor)
485 if((_intensityColorMapType != newType) ||
486 ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) &&
487 (lowColor.isValid() && highColor.isValid()))){
489 case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{
490 _intensityColorMapType = newType;
491 QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
492 colorMap.addColorStop(0.25, Qt::cyan);
493 colorMap.addColorStop(0.5, Qt::yellow);
494 colorMap.addColorStop(0.75, Qt::red);
495 d_spectrogram->setColorMap(colorMap);
498 case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{
499 _intensityColorMapType = newType;
500 QwtLinearColorMap colorMap(Qt::black, Qt::white);
501 d_spectrogram->setColorMap(colorMap);
504 case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{
505 _intensityColorMapType = newType;
506 QwtLinearColorMap colorMap(Qt::white, Qt::black);
507 d_spectrogram->setColorMap(colorMap);
510 case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{
511 _intensityColorMapType = newType;
512 QwtLinearColorMap colorMap(Qt::black, Qt::white);
513 colorMap.addColorStop(0.5, Qt::darkRed);
514 d_spectrogram->setColorMap(colorMap);
517 case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{
518 _userDefinedLowIntensityColor = lowColor;
519 _userDefinedHighIntensityColor = highColor;
520 _intensityColorMapType = newType;
521 QwtLinearColorMap colorMap(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor);
522 d_spectrogram->setColorMap(colorMap);
528 _UpdateIntensityRangeDisplay();
533 WaterfallDisplayPlot::GetUserDefinedLowIntensityColor() const
535 return _userDefinedLowIntensityColor;
539 WaterfallDisplayPlot::GetUserDefinedHighIntensityColor() const
541 return _userDefinedHighIntensityColor;
545 WaterfallDisplayPlot::_UpdateIntensityRangeDisplay()
547 QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight);
548 rightAxis->setTitle("Intensity (dB)");
549 rightAxis->setColorBarEnabled(true);
550 rightAxis->setColorMap(d_spectrogram->data()->range(),
551 d_spectrogram->colorMap());
553 setAxisScale(QwtPlot::yRight,
554 d_spectrogram->data()->range().minValue(),
555 d_spectrogram->data()->range().maxValue() );
556 enableAxis(QwtPlot::yRight);
558 plotLayout()->setAlignCanvasToScales(true);
560 // Tell the display to redraw everything
561 d_spectrogram->invalidateCache();
562 d_spectrogram->itemChanged();
567 // Update the last replot timer
568 _lastReplot = get_highres_clock();
571 #endif /* WATERFALL_DISPLAY_PLOT_C */