diff options
Diffstat (limited to 'gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc')
-rw-r--r-- | gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc new file mode 100644 index 000000000..21011ed68 --- /dev/null +++ b/gr-qtgui/src/lib/Waterfall3DDisplayPlot.cc @@ -0,0 +1,351 @@ +#ifndef WATERFALL_3D_DISPLAY_PLOT_C +#define WATERFALL_3D_DISPLAY_PLOT_C + +#include <Waterfall3DDisplayPlot.h> + +#include <qwt3d_helper.h> +#include <qapplication.h> + +Waterfall3DColorMap::Waterfall3DColorMap(): Qwt3D::Color(), QwtLinearColorMap(){ + _interval.setInterval(0, 1.0); + +} + +Waterfall3DColorMap::~Waterfall3DColorMap(){ + +} + +Qwt3D::RGBA Waterfall3DColorMap::operator()(double, double, double z)const{ + return Qwt3D::RGBA(Qwt3D::Qt2GL(color(_interval, z))); +} + +void Waterfall3DColorMap::SetInterval(const double minValue, const double maxValue){ + _interval.setInterval(minValue, maxValue); +} + +Qwt3D::ColorVector& Waterfall3DColorMap::createVector(Qwt3D::ColorVector& vec) { + // Generate 100 interval values and then return those + Qwt3D::ColorVector colorVec; + for(unsigned int number = 0; number < 100; number++){ + double value = (_interval.width() * (static_cast<double>(number) / 100.0)) + _interval.minValue(); + colorVec.push_back(operator()(0,0,value)); + } + vec = colorVec; + return vec; +} + + +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_WHITE_HOT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_BLACK_HOT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_INCANDESCENT; +const int Waterfall3DDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED; + +Waterfall3DDisplayPlot::Waterfall3DDisplayPlot(QWidget* parent):Qwt3D::SurfacePlot(parent){ + _startFrequency = 0; + _stopFrequency = 4000; + + _createCoordinateSystemFlag = true; + + _initialized = false; + + _numPoints = 1024; + + _displayIntervalTime = (1.0/5.0); // 1/5 of a second between updates + + timespec_reset(&_lastReplot); + + _useCenterFrequencyFlag = false; + _centerFrequency = 0.0; + + _timePerFFT = 1.0; + timespec_reset(&_dataTimestamp); + + coordinates()->setAutoScale(false); + + _waterfallData = new Waterfall3DData(_startFrequency, _stopFrequency, _numPoints, 200); + _waterfallData->assign(this); + _waterfallData->create(); + + _intensityColorMapType = -1; + SetIntensityColorMapType(INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR, Qt::white, Qt::black, true, true); + + legend()->setScale(Qwt3D::LINEARSCALE); + legend()->setTitleString("Intensity"); + + enableMouse(true); +} + +Waterfall3DDisplayPlot::~Waterfall3DDisplayPlot(){ + delete _waterfallData; +} + +void Waterfall3DDisplayPlot::Init(){ + if(!_initialized && initializedGL()){ + resize(parentWidget()->width(), parentWidget()->height()); + + // Attempting to prevent valgrind uninitialized variable errors in QwtPlot3d::Drawable class + glDisable(GL_POLYGON_OFFSET_FILL); + + setPlotStyle(Qwt3D::FILLED); + + setCoordinateStyle(Qwt3D::FRAME); + setFloorStyle(Qwt3D::FLOORDATA); + setOrtho(true); + + _initialized = true; + } +} + +void Waterfall3DDisplayPlot::Reset(){ + _waterfallData->ResizeData(_startFrequency, _stopFrequency, _numPoints); + _waterfallData->Reset(); + + if(initializedGL()){ + setScale(1, static_cast<int>(((_stopFrequency - _startFrequency) / 200) ), 10); + } + + _createCoordinateSystemFlag = true; + + timespec_reset(&_dataTimestamp); + _timePerFFT = 1.0; +} + +void Waterfall3DDisplayPlot::SetFrequencyRange(const double startFreq, const double stopFreq, const double centerFreq, const bool useCenterFrequencyFlag){ + if((stopFreq > 0) && (stopFreq > startFreq)){ + _startFrequency = startFreq; + _stopFrequency = stopFreq; + + _useCenterFrequencyFlag = useCenterFrequencyFlag; + _centerFrequency = centerFreq; + + Reset(); + + // Only replot if screen is visible + if(isVisible()){ + replot(); + } + } +} + +bool Waterfall3DDisplayPlot::loadFromData(double** data, unsigned int columns, unsigned int rows + ,double minx, double maxx, double miny, double maxy){ + + Qwt3D::GridData* gridPtr = (Qwt3D::GridData*)actualData_p; + + gridPtr->setPeriodic(false,false); + gridPtr->setSize(columns,rows); + readIn(*gridPtr,data,columns,rows,minx,maxx,miny,maxy); + calcNormals(*gridPtr); + + updateData(); + updateNormals(); + + if( _createCoordinateSystemFlag ){ + createCoordinateSystem(); + + for (unsigned i=0; i!=coordinates()->axes.size(); ++i) + { + coordinates()->axes[i].setMajors(5); + coordinates()->axes[i].setMinors(3); + } + + coordinates()->axes[Qwt3D::Y1].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y2].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y3].setLabelString("Time"); + coordinates()->axes[Qwt3D::Y4].setLabelString("Time"); + coordinates()->axes[Qwt3D::Z1].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z2].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z3].setLabelString("Intensity (dB)"); + coordinates()->axes[Qwt3D::Z4].setLabelString("Intensity (dB)"); + + coordinates()->axes[Qwt3D::X1].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X2].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X3].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + coordinates()->axes[Qwt3D::X4].setLabelString((!_useCenterFrequencyFlag ? "Frequency (Hz)" : "Frequency (kHz)")); + + // The QwtPlot3D Interface takes ownership of these items, so there is no need to delete them... + coordinates()->axes[Qwt3D::X1].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X2].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X3].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + coordinates()->axes[Qwt3D::X4].setScale(new FrequencyScale(_useCenterFrequencyFlag, _centerFrequency)); + + coordinates()->axes[Qwt3D::Y1].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y2].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y3].setScale(new TimeScale(this)); + coordinates()->axes[Qwt3D::Y4].setScale(new TimeScale(this)); + + coordinates()->axes[Qwt3D::Z1].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z2].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z3].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + coordinates()->axes[Qwt3D::Z4].setScale(new IntensityScale(_waterfallData->GetFloorValue())); + + _createCoordinateSystemFlag = false; + } + + return true; +} + +double Waterfall3DDisplayPlot::GetStartFrequency()const{ + return _startFrequency; +} + +double Waterfall3DDisplayPlot::GetStopFrequency()const{ + return _stopFrequency; +} + +void Waterfall3DDisplayPlot::PlotNewData(const double* dataPoints, const int64_t numDataPoints, const double timePerFFT, const timespec timestamp, const int droppedFrames){ + if(numDataPoints > 0){ + if(numDataPoints != _numPoints){ + _numPoints = numDataPoints; + + Reset(); + + if(isVisible()){ + replot(); + } + + _createCoordinateSystemFlag = true; + + _lastReplot = get_highres_clock(); + } + + _dataTimestamp = timestamp; + _timePerFFT = timePerFFT; + + _waterfallData->addFFTData(dataPoints, numDataPoints, droppedFrames); + _waterfallData->IncrementNumLinesToUpdate(); + } + + // Allow at least a 50% duty cycle + if(diff_timespec(get_highres_clock(), _lastReplot) > _displayIntervalTime){ + // Only update when window is visible + if(isVisible()){ + replot(); + } + + _lastReplot = get_highres_clock(); + } +} + +void Waterfall3DDisplayPlot::SetIntensityRange(const double minIntensity, const double maxIntensity){ + _waterfallData->SetFloorValue(minIntensity); + _waterfallData->setMinZ(0); + _waterfallData->setMaxZ(maxIntensity-minIntensity); + + _createCoordinateSystemFlag = true; + + emit UpdatedLowerIntensityLevel(minIntensity); + emit UpdatedUpperIntensityLevel(maxIntensity); + + SetIntensityColorMapType(_intensityColorMapType, _userDefinedLowIntensityColor, _userDefinedLowIntensityColor, true); +} + +void Waterfall3DDisplayPlot::replot(){ + + if(!_initialized){ + Init(); + } + if(initializedGL()){ + const timespec startTime = get_highres_clock(); + + _waterfallData->create(); + + legend()->setMajors(4); + legend()->setMinors(5); + double start, stop; + coordinates()->axes[Qwt3D::Z1].limits(start,stop); + legend()->setLimits( _waterfallData->GetFloorValue(), _waterfallData->GetFloorValue() + stop - start ); + + coordinates()->axes[Qwt3D::X1].limits(start,stop); + + showColorLegend(true); + + updateGL(); + + double differenceTime = (diff_timespec(get_highres_clock(), startTime)); + + // Require at least a 20% duty cycle + differenceTime *= 4.0; + if(differenceTime > (1.0/5.0)){ + _displayIntervalTime = differenceTime; + } + } +} + +int Waterfall3DDisplayPlot::GetIntensityColorMapType()const{ + return _intensityColorMapType; +} + +void Waterfall3DDisplayPlot::SetIntensityColorMapType(const int newType, const QColor lowColor, const QColor highColor, const bool forceFlag, const bool noReplotFlag){ + if(((_intensityColorMapType != newType) || forceFlag) || + ((newType == INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) && + (lowColor.isValid() && highColor.isValid()))){ + + Waterfall3DColorMap* colorMap = new Waterfall3DColorMap(); + colorMap->SetInterval(_waterfallData->minZ(), _waterfallData->maxZ()); + + switch(newType){ + case INTENSITY_COLOR_MAP_TYPE_MULTI_COLOR:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::darkCyan, Qt::white); + colorMap->addColorStop(0.25, Qt::cyan); + colorMap->addColorStop(0.5, Qt::yellow); + colorMap->addColorStop(0.75, Qt::red); + setBackgroundColor(Qwt3D::Qt2GL(Qt::gray)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_WHITE_HOT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::black, Qt::white); + setBackgroundColor(Qwt3D::Qt2GL(Qt::blue)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_BLACK_HOT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::white, Qt::black); + setBackgroundColor(Qwt3D::Qt2GL(Qt::blue)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_INCANDESCENT:{ + _intensityColorMapType = newType; + colorMap->setColorInterval(Qt::black, Qt::white); + colorMap->addColorStop(0.5, Qt::darkRed); + setBackgroundColor(Qwt3D::Qt2GL(Qt::gray)); + break; + } + case INTENSITY_COLOR_MAP_TYPE_USER_DEFINED:{ + _userDefinedLowIntensityColor = lowColor; + _userDefinedHighIntensityColor = highColor; + _intensityColorMapType = newType; + colorMap->setColorInterval(_userDefinedLowIntensityColor, _userDefinedHighIntensityColor); + setBackgroundColor(Qwt3D::Qt2GL(Qt::white)); + break; + } + default: + colorMap->setColorInterval(Qt::black, Qt::white); + break; + } + + // Qwt3D takes over destruction of this object... + setDataColor(colorMap); + + if(!noReplotFlag){ + // Draw again + replot(); + + // Update the last replot timer + _lastReplot = get_highres_clock(); + } + } +} + +const QColor Waterfall3DDisplayPlot::GetUserDefinedLowIntensityColor()const{ + return _userDefinedLowIntensityColor; +} + +const QColor Waterfall3DDisplayPlot::GetUserDefinedHighIntensityColor()const{ + return _userDefinedHighIntensityColor; +} + +#endif /* WATERFALL_3D_DISPLAY_PLOT_C */ |