2 #include <QColorDialog>
4 #include <spectrumdisplayform.h>
6 int SpectrumDisplayForm::_openGLWaterfall3DFlag = -1;
8 SpectrumDisplayForm::SpectrumDisplayForm(bool useOpenGL, QWidget* parent)
13 _useOpenGL = useOpenGL;
14 _systemSpecifiedFlag = false;
15 _intValidator = new QIntValidator(this);
16 _intValidator->setBottom(0);
17 _frequencyDisplayPlot = new FrequencyDisplayPlot(FrequencyPlotDisplayFrame);
18 _waterfallDisplayPlot = new WaterfallDisplayPlot(WaterfallPlotDisplayFrame);
20 _timeDomainDisplayPlot = new TimeDomainDisplayPlot(TimeDomainDisplayFrame);
21 _constellationDisplayPlot = new ConstellationDisplayPlot(ConstellationDisplayFrame);
22 _numRealDataPoints = 1024;
23 _realFFTDataPoints = new double[_numRealDataPoints];
24 _averagedValues = new double[_numRealDataPoints];
25 _historyVector = new std::vector<double*>;
27 AvgLineEdit->setRange(0, 500); // Set range of Average box value from 0 to 500
28 MinHoldCheckBox_toggled( false );
29 MaxHoldCheckBox_toggled( false );
31 WaterfallMaximumIntensityWheel->setRange(-200, 0);
32 WaterfallMaximumIntensityWheel->setTickCnt(50);
33 WaterfallMinimumIntensityWheel->setRange(-200, 0);
34 WaterfallMinimumIntensityWheel->setTickCnt(50);
35 WaterfallMinimumIntensityWheel->setValue(-200);
38 _peakAmplitude = -HUGE_VAL;
40 _noiseFloorAmplitude = -HUGE_VAL;
42 connect(_waterfallDisplayPlot, SIGNAL(UpdatedLowerIntensityLevel(const double)),
43 _frequencyDisplayPlot, SLOT(SetLowerIntensityLevel(const double)));
44 connect(_waterfallDisplayPlot, SIGNAL(UpdatedUpperIntensityLevel(const double)),
45 _frequencyDisplayPlot, SLOT(SetUpperIntensityLevel(const double)));
47 _frequencyDisplayPlot->SetLowerIntensityLevel(-200);
48 _frequencyDisplayPlot->SetUpperIntensityLevel(-200);
50 // Load up the acceptable FFT sizes...
51 FFTSizeComboBox->clear();
52 for(long fftSize = SpectrumGUIClass::MIN_FFT_SIZE; fftSize <= SpectrumGUIClass::MAX_FFT_SIZE; fftSize *= 2){
53 FFTSizeComboBox->insertItem(FFTSizeComboBox->count(), QString("%1").arg(fftSize));
57 ToggleTabFrequency(false);
58 ToggleTabWaterfall(false);
60 ToggleTabConstellation(false);
62 // Create a timer to update plots at the specified rate
63 displayTimer = new QTimer(this);
64 connect(displayTimer, SIGNAL(timeout()), this, SLOT(UpdateGuiTimer()));
67 SpectrumDisplayForm::~SpectrumDisplayForm()
69 // Qt deletes children when parent is deleted
71 // Don't worry about deleting Display Plots - they are deleted when parents are deleted
74 delete[] _realFFTDataPoints;
75 delete[] _averagedValues;
77 for(unsigned int count = 0; count < _historyVector->size(); count++){
78 delete[] _historyVector->operator[](count);
81 delete _historyVector;
88 SpectrumDisplayForm::setSystem( SpectrumGUIClass * newSystem,
89 const uint64_t numFFTDataPoints,
90 const uint64_t numTimeDomainDataPoints )
92 ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints);
94 if(newSystem != NULL){
96 _systemSpecifiedFlag = true;
99 _systemSpecifiedFlag = false;
104 SpectrumDisplayForm::newFrequencyData( const SpectrumUpdateEvent* spectrumUpdateEvent)
106 //_lastSpectrumEvent = (SpectrumUpdateEvent)(*spectrumUpdateEvent);
107 const std::complex<float>* complexDataPoints = spectrumUpdateEvent->getFFTPoints();
108 const uint64_t numFFTDataPoints = spectrumUpdateEvent->getNumFFTDataPoints();
109 const double* realTimeDomainDataPoints = spectrumUpdateEvent->getRealTimeDomainPoints();
110 const double* imagTimeDomainDataPoints = spectrumUpdateEvent->getImagTimeDomainPoints();
111 const uint64_t numTimeDomainDataPoints = spectrumUpdateEvent->getNumTimeDomainDataPoints();
112 const timespec dataTimestamp = spectrumUpdateEvent->getDataTimestamp();
113 const bool repeatDataFlag = spectrumUpdateEvent->getRepeatDataFlag();
114 const bool lastOfMultipleUpdatesFlag = spectrumUpdateEvent->getLastOfMultipleUpdateFlag();
115 const timespec generatedTimestamp = spectrumUpdateEvent->getEventGeneratedTimestamp();
117 // REMEMBER: The dataTimestamp is NOT valid when the repeat data flag is true...
118 ResizeBuffers(numFFTDataPoints, numTimeDomainDataPoints);
120 // Calculate the Magnitude of the complex point
121 const std::complex<float>* complexDataPointsPtr = complexDataPoints+numFFTDataPoints/2;
122 double* realFFTDataPointsPtr = _realFFTDataPoints;
124 double sumMean = 0.0;
125 double localPeakAmplitude = -HUGE_VAL;
126 double localPeakFrequency = 0.0;
127 const double fftBinSize = (_stopFrequency-_startFrequency) /
128 static_cast<double>(numFFTDataPoints);
130 // Run this twice to perform the fftshift operation on the data here as well
131 std::complex<float> scaleFactor = std::complex<float>((float)numFFTDataPoints);
132 for(uint64_t point = 0; point < numFFTDataPoints/2; point++){
133 std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor;
134 *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20);
136 if(*realFFTDataPointsPtr > localPeakAmplitude) {
137 localPeakFrequency = static_cast<float>(point) * fftBinSize;
138 localPeakAmplitude = *realFFTDataPointsPtr;
140 sumMean += *realFFTDataPointsPtr;
142 complexDataPointsPtr++;
143 realFFTDataPointsPtr++;
146 // This loop takes the first half of the input data and puts it in the
147 // second half of the plotted data
148 complexDataPointsPtr = complexDataPoints;
149 for(uint64_t point = 0; point < numFFTDataPoints/2; point++){
150 std::complex<float> pt = (*complexDataPointsPtr) / scaleFactor;
151 *realFFTDataPointsPtr = 10.0*log10((pt.real() * pt.real() + pt.imag()*pt.imag()) + 1e-20);
153 if(*realFFTDataPointsPtr > localPeakAmplitude) {
154 localPeakFrequency = static_cast<float>(point) * fftBinSize;
155 localPeakAmplitude = *realFFTDataPointsPtr;
157 sumMean += *realFFTDataPointsPtr;
159 complexDataPointsPtr++;
160 realFFTDataPointsPtr++;
163 // Don't update the averaging history if this is repeated data
165 _AverageHistory(_realFFTDataPoints);
167 // Only use the local info if we are not repeating data
168 _peakAmplitude = localPeakAmplitude;
169 _peakFrequency = localPeakFrequency;
171 // calculate the spectral mean
172 // +20 because for the comparison below we only want to throw out bins
173 // that are significantly higher (and would, thus, affect the mean more)
174 const double meanAmplitude = (sumMean / numFFTDataPoints) + 20.0;
176 // now throw out any bins higher than the mean
178 uint64_t newNumDataPoints = numFFTDataPoints;
179 for(uint64_t number = 0; number < numFFTDataPoints; number++){
180 if (_realFFTDataPoints[number] <= meanAmplitude)
181 sumMean += _realFFTDataPoints[number];
186 if (newNumDataPoints == 0) // in the odd case that all
187 _noiseFloorAmplitude = meanAmplitude; // amplitudes are equal!
189 _noiseFloorAmplitude = sumMean / newNumDataPoints;
192 if(lastOfMultipleUpdatesFlag){
193 int tabindex = SpectrumTypeTab->currentIndex();
194 if(tabindex == d_plot_fft) {
195 _frequencyDisplayPlot->PlotNewData(_averagedValues, numFFTDataPoints,
196 _noiseFloorAmplitude, _peakFrequency,
197 _peakAmplitude, d_update_time);
199 if(tabindex == d_plot_time) {
200 _timeDomainDisplayPlot->PlotNewData(realTimeDomainDataPoints,
201 imagTimeDomainDataPoints,
202 numTimeDomainDataPoints,
205 if(tabindex == d_plot_constellation) {
206 _constellationDisplayPlot->PlotNewData(realTimeDomainDataPoints,
207 imagTimeDomainDataPoints,
208 numTimeDomainDataPoints,
212 // Don't update the repeated data for the waterfall
214 if(tabindex == d_plot_waterfall) {
215 _waterfallDisplayPlot->PlotNewData(_realFFTDataPoints, numFFTDataPoints,
216 d_update_time, dataTimestamp,
217 spectrumUpdateEvent->getDroppedFFTFrames());
222 // Tell the system the GUI has been updated
223 if(_systemSpecifiedFlag){
224 _system->SetLastGUIUpdateTime(generatedTimestamp);
225 _system->DecrementPendingGUIUpdateEvents();
231 SpectrumDisplayForm::resizeEvent( QResizeEvent *e )
234 s.setWidth(FrequencyPlotDisplayFrame->width());
235 s.setHeight(FrequencyPlotDisplayFrame->height());
236 emit _frequencyDisplayPlot->resizeSlot(&s);
238 s.setWidth(TimeDomainDisplayFrame->width());
239 s.setHeight(TimeDomainDisplayFrame->height());
240 emit _timeDomainDisplayPlot->resizeSlot(&s);
242 s.setWidth(WaterfallPlotDisplayFrame->width());
243 s.setHeight(WaterfallPlotDisplayFrame->height());
244 emit _waterfallDisplayPlot->resizeSlot(&s);
246 s.setWidth(ConstellationDisplayFrame->width());
247 s.setHeight(ConstellationDisplayFrame->height());
248 emit _constellationDisplayPlot->resizeSlot(&s);
252 SpectrumDisplayForm::customEvent( QEvent * e)
254 if(e->type() == QEvent::User+3){
255 if(_systemSpecifiedFlag){
256 WindowComboBox->setCurrentIndex(_system->GetWindowType());
257 FFTSizeComboBox->setCurrentIndex(_system->GetFFTSizeIndex());
258 //FFTSizeComboBox->setCurrentIndex(1);
261 waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensityWheel->value());
262 waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensityWheel->value());
264 // Clear any previous display
267 else if(e->type() == 10005){
268 SpectrumUpdateEvent* spectrumUpdateEvent = (SpectrumUpdateEvent*)e;
269 newFrequencyData(spectrumUpdateEvent);
271 else if(e->type() == 10008){
272 setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel());
274 else if(e->type() == 10009){
276 if(_systemSpecifiedFlag){
277 _system->ResetPendingGUIUpdateEvents();
280 else if(e->type() == 10010){
281 _startFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStartFrequency();
282 _stopFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStopFrequency();
283 _centerFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetCenterFrequency();
285 UseRFFrequenciesCB(UseRFFrequenciesCheckBox->isChecked());
290 SpectrumDisplayForm::UpdateGuiTimer()
292 // This is called by the displayTimer and redraws the canvases of
294 _frequencyDisplayPlot->canvas()->update();
295 _waterfallDisplayPlot->canvas()->update();
296 _timeDomainDisplayPlot->canvas()->update();
297 _constellationDisplayPlot->canvas()->update();
302 SpectrumDisplayForm::AvgLineEdit_valueChanged( int value )
304 SetAverageCount(value);
309 SpectrumDisplayForm::MaxHoldCheckBox_toggled( bool newState )
311 MaxHoldResetBtn->setEnabled(newState);
312 _frequencyDisplayPlot->SetMaxFFTVisible(newState);
313 MaxHoldResetBtn_clicked();
318 SpectrumDisplayForm::MinHoldCheckBox_toggled( bool newState )
320 MinHoldResetBtn->setEnabled(newState);
321 _frequencyDisplayPlot->SetMinFFTVisible(newState);
322 MinHoldResetBtn_clicked();
327 SpectrumDisplayForm::MinHoldResetBtn_clicked()
329 _frequencyDisplayPlot->ClearMinData();
330 _frequencyDisplayPlot->replot();
335 SpectrumDisplayForm::MaxHoldResetBtn_clicked()
337 _frequencyDisplayPlot->ClearMaxData();
338 _frequencyDisplayPlot->replot();
343 SpectrumDisplayForm::TabChanged(int index)
345 // This might be dangerous to call this with NULL
350 SpectrumDisplayForm::SetFrequencyRange(const double newCenterFrequency,
351 const double newStartFrequency,
352 const double newStopFrequency)
355 if(UseRFFrequenciesCheckBox->isChecked()) {
356 fdiff = newCenterFrequency;
359 fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency));
363 std::string strunits[4] = {"Hz", "kHz", "MHz", "GHz"};
364 std::string strtime[4] = {"sec", "ms", "us", "ns"};
365 double units10 = floor(log10(fdiff));
366 double units3 = std::max(floor(units10 / 3.0), 0.0);
367 double units = pow(10, (units10-fmod(units10, 3.0)));
368 int iunit = static_cast<int>(units3);
370 _startFrequency = newStartFrequency;
371 _stopFrequency = newStopFrequency;
372 _centerFrequency = newCenterFrequency;
374 _frequencyDisplayPlot->SetFrequencyRange(_startFrequency,
377 UseRFFrequenciesCheckBox->isChecked(),
378 units, strunits[iunit]);
379 _waterfallDisplayPlot->SetFrequencyRange(_startFrequency,
382 UseRFFrequenciesCheckBox->isChecked(),
383 units, strunits[iunit]);
384 _timeDomainDisplayPlot->SetSampleRate(_stopFrequency - _startFrequency,
385 units, strtime[iunit]);
390 SpectrumDisplayForm::GetAverageCount()
392 return _historyVector->size();
396 SpectrumDisplayForm::SetAverageCount(const int newCount)
399 if(newCount != static_cast<int>(_historyVector->size())){
400 std::vector<double*>::iterator pos;
401 while(newCount < static_cast<int>(_historyVector->size())){
402 pos = _historyVector->begin();
404 _historyVector->erase(pos);
407 while(newCount > static_cast<int>(_historyVector->size())){
408 _historyVector->push_back(new double[_numRealDataPoints]);
416 SpectrumDisplayForm::_AverageHistory(const double* newBuffer)
418 if(_numRealDataPoints > 0){
419 if(_historyVector->size() > 0){
420 memcpy(_historyVector->operator[](_historyEntry), newBuffer,
421 _numRealDataPoints*sizeof(double));
423 // Increment the next location to store data
424 _historyEntryCount++;
425 if(_historyEntryCount > static_cast<int>(_historyVector->size())){
426 _historyEntryCount = _historyVector->size();
428 _historyEntry = (++_historyEntry)%_historyVector->size();
430 // Total up and then average the values
432 for(uint64_t location = 0; location < _numRealDataPoints; location++){
434 for(int number = 0; number < _historyEntryCount; number++){
435 sum += _historyVector->operator[](number)[location];
437 _averagedValues[location] = sum/static_cast<double>(_historyEntryCount);
441 memcpy(_averagedValues, newBuffer, _numRealDataPoints*sizeof(double));
447 SpectrumDisplayForm::ResizeBuffers( const uint64_t numFFTDataPoints,
448 const uint64_t /*numTimeDomainDataPoints*/ )
450 // Convert from Complex to Real for certain Displays
451 if(_numRealDataPoints != numFFTDataPoints){
452 _numRealDataPoints = numFFTDataPoints;
453 delete[] _realFFTDataPoints;
454 delete[] _averagedValues;
456 _realFFTDataPoints = new double[_numRealDataPoints];
457 _averagedValues = new double[_numRealDataPoints];
458 memset(_realFFTDataPoints, 0x0, _numRealDataPoints*sizeof(double));
460 const int historySize = _historyVector->size();
461 SetAverageCount(0); // Clear the existing history
462 SetAverageCount(historySize);
469 SpectrumDisplayForm::Reset()
473 _waterfallDisplayPlot->Reset();
478 SpectrumDisplayForm::AverageDataReset()
481 _historyEntryCount = 0;
483 memset(_averagedValues, 0x0, _numRealDataPoints*sizeof(double));
485 MaxHoldResetBtn_clicked();
486 MinHoldResetBtn_clicked();
491 SpectrumDisplayForm::closeEvent( QCloseEvent *e )
493 if(_systemSpecifiedFlag){
494 _system->SetWindowOpenFlag(false);
497 qApp->processEvents();
499 QWidget::closeEvent(e);
504 SpectrumDisplayForm::WindowTypeChanged( int newItem )
506 if(_systemSpecifiedFlag){
507 _system->SetWindowType(newItem);
513 SpectrumDisplayForm::UseRFFrequenciesCB( bool useRFFlag )
515 SetFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency);
520 SpectrumDisplayForm::waterfallMaximumIntensityChangedCB( double newValue )
522 if(newValue > WaterfallMinimumIntensityWheel->value()){
523 WaterfallMaximumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0));
526 WaterfallMaximumIntensityWheel->setValue(WaterfallMinimumIntensityWheel->value());
529 _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(),
530 WaterfallMaximumIntensityWheel->value());
535 SpectrumDisplayForm::waterfallMinimumIntensityChangedCB( double newValue )
537 if(newValue < WaterfallMaximumIntensityWheel->value()){
538 WaterfallMinimumIntensityLabel->setText(QString("%1 dB").arg(newValue, 0, 'f', 0));
541 WaterfallMinimumIntensityWheel->setValue(WaterfallMaximumIntensityWheel->value());
544 _waterfallDisplayPlot->SetIntensityRange(WaterfallMinimumIntensityWheel->value(),
545 WaterfallMaximumIntensityWheel->value());
549 SpectrumDisplayForm::FFTComboBoxSelectedCB( const QString &fftSizeString )
551 if(_systemSpecifiedFlag){
552 _system->SetFFTSize(fftSizeString.toLong());
558 SpectrumDisplayForm::WaterfallAutoScaleBtnCB()
560 double minimumIntensity = _noiseFloorAmplitude - 5;
561 if(minimumIntensity < WaterfallMinimumIntensityWheel->minValue()){
562 minimumIntensity = WaterfallMinimumIntensityWheel->minValue();
564 WaterfallMinimumIntensityWheel->setValue(minimumIntensity);
565 double maximumIntensity = _peakAmplitude + 10;
566 if(maximumIntensity > WaterfallMaximumIntensityWheel->maxValue()){
567 maximumIntensity = WaterfallMaximumIntensityWheel->maxValue();
569 WaterfallMaximumIntensityWheel->setValue(maximumIntensity);
570 waterfallMaximumIntensityChangedCB(maximumIntensity);
574 SpectrumDisplayForm::WaterfallIntensityColorTypeChanged( int newType )
576 QColor lowIntensityColor;
577 QColor highIntensityColor;
578 if(newType == WaterfallDisplayPlot::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED){
579 // Select the Low Intensity Color
580 lowIntensityColor = _waterfallDisplayPlot->GetUserDefinedLowIntensityColor();
581 if(!lowIntensityColor.isValid()){
582 lowIntensityColor = Qt::black;
584 QMessageBox::information(this, "Low Intensity Color Selection", "In the next window, select the low intensity color for the waterfall display", QMessageBox::Ok);
585 lowIntensityColor = QColorDialog::getColor(lowIntensityColor, this);
587 // Select the High Intensity Color
588 highIntensityColor = _waterfallDisplayPlot->GetUserDefinedHighIntensityColor();
589 if(!highIntensityColor.isValid()){
590 highIntensityColor = Qt::white;
592 QMessageBox::information(this, "High Intensity Color Selection", "In the next window, select the high intensity color for the waterfall display", QMessageBox::Ok);
593 highIntensityColor = QColorDialog::getColor(highIntensityColor, this);
596 _waterfallDisplayPlot->SetIntensityColorMapType(newType, lowIntensityColor, highIntensityColor);
600 SpectrumDisplayForm::ToggleTabFrequency(const bool state)
603 if(d_plot_fft == -1) {
604 SpectrumTypeTab->addTab(FrequencyPage, "Frequency Display");
605 d_plot_fft = SpectrumTypeTab->count()-1;
609 SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(FrequencyPage));
615 SpectrumDisplayForm::ToggleTabWaterfall(const bool state)
618 if(d_plot_waterfall == -1) {
619 SpectrumTypeTab->addTab(WaterfallPage, "Waterfall Display");
620 d_plot_waterfall = SpectrumTypeTab->count()-1;
624 SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(WaterfallPage));
625 d_plot_waterfall = -1;
630 SpectrumDisplayForm::ToggleTabTime(const bool state)
633 if(d_plot_time == -1) {
634 SpectrumTypeTab->addTab(TimeDomainPage, "Time Domain Display");
635 d_plot_time = SpectrumTypeTab->count()-1;
639 SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(TimeDomainPage));
645 SpectrumDisplayForm::ToggleTabConstellation(const bool state)
648 if(d_plot_constellation == -1) {
649 SpectrumTypeTab->addTab(ConstellationPage, "Constellation Display");
650 d_plot_constellation = SpectrumTypeTab->count()-1;
654 SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(ConstellationPage));
655 d_plot_constellation = -1;
661 SpectrumDisplayForm::SetTimeDomainAxis(double min, double max)
663 _timeDomainDisplayPlot->set_yaxis(min, max);
667 SpectrumDisplayForm::SetConstellationAxis(double xmin, double xmax,
668 double ymin, double ymax)
670 _constellationDisplayPlot->set_axis(xmin, xmax, ymin, ymax);
674 SpectrumDisplayForm::SetConstellationPenSize(int size)
676 _constellationDisplayPlot->set_pen_size( size );
680 SpectrumDisplayForm::SetFrequencyAxis(double min, double max)
682 _frequencyDisplayPlot->set_yaxis(min, max);
686 SpectrumDisplayForm::SetUpdateTime(double t)
689 // QTimer class takes millisecond input
690 displayTimer->start(d_update_time*1000);