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 + GetCenterFrequency()) / ((GetFrequencyPrecision() == 0) ? 1.0 : 1000.0),
70 0, 'f', GetFrequencyPrecision());
73 virtual void initiateUpdate()
89 timespec_reset(&_zeroTime);
90 _secondsPerLine = 1.0;
93 virtual ~TimeScaleData()
97 virtual timespec GetZeroTime() const
102 virtual void SetZeroTime(const timespec newTime)
107 virtual void SetSecondsPerLine(const double newTime)
109 _secondsPerLine = newTime;
112 virtual double GetSecondsPerLine() const
114 return _secondsPerLine;
120 double _secondsPerLine;
126 class QwtTimeScaleDraw: public QwtScaleDraw, public TimeScaleData
129 QwtTimeScaleDraw():QwtScaleDraw(),TimeScaleData()
133 virtual ~QwtTimeScaleDraw()
137 virtual QwtText label(double value) const
139 QwtText returnLabel("");
141 timespec lineTime = timespec_add(GetZeroTime(), (-value) * GetSecondsPerLine());
143 gmtime_r(&lineTime.tv_sec, &timeTm);
144 returnLabel = (QString("").sprintf("%04d/%02d/%02d\n%02d:%02d:%02d.%03ld",
145 timeTm.tm_year+1900, timeTm.tm_mon+1,
146 timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min,
147 timeTm.tm_sec, lineTime.tv_nsec/1000000));
151 virtual void initiateUpdate()
153 // Do this in one call rather than when zeroTime and secondsPerLine
154 // updates is to prevent the display from being updated too often...
164 class WaterfallZoomer: public QwtPlotZoomer, public TimeScaleData, public FreqOffsetAndPrecisionClass
167 WaterfallZoomer(QwtPlotCanvas* canvas, const unsigned int freqPrecision)
168 : QwtPlotZoomer(canvas), TimeScaleData(),
169 FreqOffsetAndPrecisionClass(freqPrecision)
171 setTrackerMode(QwtPicker::AlwaysOn);
174 virtual ~WaterfallZoomer()
178 virtual void updateTrackerText()
184 virtual QwtText trackerText( const QwtDoublePoint& p ) const
188 timespec lineTime = timespec_add(GetZeroTime(), (-p.y()) * GetSecondsPerLine());
190 gmtime_r(&lineTime.tv_sec, &timeTm);
191 yLabel = (QString("").sprintf("%04d/%02d/%02d %02d:%02d:%02d.%03ld",
192 timeTm.tm_year+1900, timeTm.tm_mon+1,
193 timeTm.tm_mday, timeTm.tm_hour, timeTm.tm_min,
194 timeTm.tm_sec, lineTime.tv_nsec/1000000));
196 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));
203 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
204 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT;
205 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT;
206 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT;
207 const int WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED;
209 WaterfallDisplayPlot::WaterfallDisplayPlot(QWidget* parent)
214 _stopFrequency = 4000;
216 resize(parent->width(), parent->height());
219 _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates
221 _waterfallData = new WaterfallData(_startFrequency, _stopFrequency, _numPoints, 200);
224 palette.setColor(canvas()->backgroundRole(), QColor("white"));
225 canvas()->setPalette(palette);
227 setAxisTitle(QwtPlot::xBottom, "Frequency (Hz)");
228 setAxisScaleDraw(QwtPlot::xBottom, new WaterfallFreqDisplayScaleDraw(0));
230 setAxisTitle(QwtPlot::yLeft, "Time");
231 setAxisScaleDraw(QwtPlot::yLeft, new QwtTimeScaleDraw());
233 timespec_reset(&_lastReplot);
235 d_spectrogram = new PlotWaterfall(_waterfallData, "Waterfall Display");
237 _intensityColorMapType = INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR;
239 QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
240 colorMap.addColorStop(0.25, Qt::cyan);
241 colorMap.addColorStop(0.5, Qt::yellow);
242 colorMap.addColorStop(0.75, Qt::red);
244 d_spectrogram->setColorMap(colorMap);
246 d_spectrogram->attach(this);
248 // LeftButton for the zooming
249 // MidButton for the panning
250 // RightButton: zoom out by 1
251 // Ctrl+RighButton: zoom out to full size
253 _zoomer = new WaterfallZoomer(canvas(), 0);
254 #if QT_VERSION < 0x040000
255 _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
256 Qt::RightButton, Qt::ControlModifier);
258 _zoomer->setMousePattern(QwtEventPattern::MouseSelect2,
259 Qt::RightButton, Qt::ControlModifier);
261 _zoomer->setMousePattern(QwtEventPattern::MouseSelect3,
264 _panner = new QwtPlotPanner(canvas());
265 _panner->setAxisEnabled(QwtPlot::yRight, false);
266 _panner->setMouseButton(Qt::MidButton);
268 // Avoid jumping when labels with more/less digits
269 // appear/disappear when scrolling vertically
271 const QFontMetrics fm(axisWidget(QwtPlot::yLeft)->font());
272 QwtScaleDraw *sd = axisScaleDraw(QwtPlot::yLeft);
273 sd->setMinimumExtent( fm.width("100.00") );
275 const QColor c(Qt::white);
276 _zoomer->setRubberBandPen(c);
277 _zoomer->setTrackerPen(c);
279 _UpdateIntensityRangeDisplay();
282 WaterfallDisplayPlot::~WaterfallDisplayPlot()
284 delete _waterfallData;
288 WaterfallDisplayPlot::Reset()
290 _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints);
291 _waterfallData->Reset();
293 // Load up the new base zoom settings
294 QwtDoubleRect newSize = _zoomer->zoomBase();
295 newSize.setLeft(_startFrequency);
296 newSize.setWidth(_stopFrequency-_startFrequency);
297 _zoomer->zoom(newSize);
298 _zoomer->setZoomBase(newSize);
303 WaterfallDisplayPlot::SetFrequencyRange(const double constStartFreq,
304 const double constStopFreq,
305 const double constCenterFreq,
306 const bool useCenterFrequencyFlag,
307 const double units, const std::string &strunits)
309 double startFreq = constStartFreq / units;
310 double stopFreq = constStopFreq / units;
311 double centerFreq = constCenterFreq / units;
313 if(stopFreq > startFreq) {
314 _startFrequency = 1000*startFreq;
315 _stopFrequency = 1000*stopFreq;
317 setAxisScale(QwtPlot::xBottom, _startFrequency, _stopFrequency);
319 if((axisScaleDraw(QwtPlot::xBottom) != NULL) && (_zoomer != NULL)){
320 WaterfallFreqDisplayScaleDraw* freqScale = ((WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom));
321 freqScale->SetCenterFrequency(centerFreq);
322 ((WaterfallZoomer*)_zoomer)->SetCenterFrequency(centerFreq);
324 freqScale->SetFrequencyPrecision( 2 );
325 ((WaterfallZoomer*)_zoomer)->SetFrequencyPrecision( 2 );
326 setAxisTitle(QwtPlot::xBottom, QString("Frequency (%1)").arg(strunits.c_str()));
331 // Only replot if screen is visible
340 WaterfallDisplayPlot::GetStartFrequency() const
342 return _startFrequency;
346 WaterfallDisplayPlot::GetStopFrequency() const
348 return _stopFrequency;
352 WaterfallDisplayPlot::PlotNewData(const double* dataPoints,
353 const int64_t numDataPoints,
354 const double timePerFFT,
355 const timespec timestamp,
356 const int droppedFrames)
358 if(numDataPoints > 0){
359 if(numDataPoints != _numPoints){
360 _numPoints = numDataPoints;
364 d_spectrogram->invalidateCache();
365 d_spectrogram->itemChanged();
371 _lastReplot = get_highres_clock();
374 _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames);
375 _waterfallData->IncrementNumLinesToUpdate();
377 QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
378 timeScale->SetSecondsPerLine(timePerFFT);
379 timeScale->SetZeroTime(timestamp);
381 ((WaterfallZoomer*)_zoomer)->SetSecondsPerLine(timePerFFT);
382 ((WaterfallZoomer*)_zoomer)->SetZeroTime(timestamp);
385 // Allow at least a 50% duty cycle
386 if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){
388 d_spectrogram->invalidateCache();
389 d_spectrogram->itemChanged();
391 // Only update when window is visible
396 _lastReplot = get_highres_clock();
401 WaterfallDisplayPlot::SetIntensityRange(const double minIntensity,
402 const double maxIntensity)
404 _waterfallData->setRange(QwtDoubleInterval(minIntensity, maxIntensity));
406 emit UpdatedLowerIntensityLevel(minIntensity);
407 emit UpdatedUpperIntensityLevel(maxIntensity);
409 _UpdateIntensityRangeDisplay();
413 WaterfallDisplayPlot::replot()
415 const timespec startTime = get_highres_clock();
417 QwtTimeScaleDraw* timeScale = (QwtTimeScaleDraw*)axisScaleDraw(QwtPlot::yLeft);
418 timeScale->initiateUpdate();
420 WaterfallFreqDisplayScaleDraw* freqScale = (WaterfallFreqDisplayScaleDraw*)axisScaleDraw(QwtPlot::xBottom);
421 freqScale->initiateUpdate();
423 // Update the time axis display
424 if(axisWidget(QwtPlot::yLeft) != NULL){
425 axisWidget(QwtPlot::yLeft)->update();
428 // Update the Frequency Offset Display
429 if(axisWidget(QwtPlot::xBottom) != NULL){
430 axisWidget(QwtPlot::xBottom)->update();
434 ((WaterfallZoomer*)_zoomer)->updateTrackerText();
439 double differenceTime = (diff_timespec(get_highres_clock(), startTime));
441 // Require at least a 5% duty cycle
442 differenceTime *= 19.0;
443 if(differenceTime > (1.0/5.0)){
444 _displayIntervalTime = differenceTime;
449 WaterfallDisplayPlot::resizeSlot( QSize *s )
451 resize(s->width(), s->height());
455 WaterfallDisplayPlot::GetIntensityColorMapType() const
457 return _intensityColorMapType;
461 WaterfallDisplayPlot::SetIntensityColorMapType(const int newType,
462 const QColor lowColor,
463 const QColor highColor)
465 if((_intensityColorMapType != newType) ||
466 ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) &&
467 (lowColor.isValid() && highColor.isValid()))){
469 case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{
470 _intensityColorMapType = newType;
471 QwtLinearColorMap colorMap(Qt::darkCyan, Qt::white);
472 colorMap.addColorStop(0.25, Qt::cyan);
473 colorMap.addColorStop(0.5, Qt::yellow);
474 colorMap.addColorStop(0.75, Qt::red);
475 d_spectrogram->setColorMap(colorMap);
478 case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{
479 _intensityColorMapType = newType;
480 QwtLinearColorMap colorMap(Qt::black, Qt::white);
481 d_spectrogram->setColorMap(colorMap);
484 case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{
485 _intensityColorMapType = newType;
486 QwtLinearColorMap colorMap(Qt::white, Qt::black);
487 d_spectrogram->setColorMap(colorMap);
490 case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{
491 _intensityColorMapType = newType;
492 QwtLinearColorMap colorMap(Qt::black, Qt::white);
493 colorMap.addColorStop(0.5, Qt::darkRed);
494 d_spectrogram->setColorMap(colorMap);
497 case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{
498 _userDefinedLowIntensityColor = lowColor;
499 _userDefinedHighIntensityColor = highColor;
500 _intensityColorMapType = newType;
501 QwtLinearColorMap colorMap(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor);
502 d_spectrogram->setColorMap(colorMap);
508 _UpdateIntensityRangeDisplay();
513 WaterfallDisplayPlot::GetUserDefinedLowIntensityColor() const
515 return _userDefinedLowIntensityColor;
519 WaterfallDisplayPlot::GetUserDefinedHighIntensityColor() const
521 return _userDefinedHighIntensityColor;
525 WaterfallDisplayPlot::_UpdateIntensityRangeDisplay()
527 QwtScaleWidget *rightAxis = axisWidget(QwtPlot::yRight);
528 rightAxis->setTitle("Intensity (dB)");
529 rightAxis->setColorBarEnabled(true);
530 rightAxis->setColorMap(d_spectrogram->data()->range(),
531 d_spectrogram->colorMap());
533 setAxisScale(QwtPlot::yRight,
534 d_spectrogram->data()->range().minValue(),
535 d_spectrogram->data()->range().maxValue() );
536 enableAxis(QwtPlot::yRight);
538 plotLayout()->setAlignCanvasToScales(true);
540 // Tell the display to redraw everything
541 d_spectrogram->invalidateCache();
542 d_spectrogram->itemChanged();
547 // Update the last replot timer
548 _lastReplot = get_highres_clock();
551 #endif /* WATERFALL_DISPLAY_PLOT_C */