SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
glspectrum.cpp
Go to the documentation of this file.
1 // Copyright (C) 2016 F4EXB //
3 // written by Edouard Griffiths //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 
19 #if 0 //def USE_SSE2
20 #include <emmintrin.h>
21 #endif
22 
23 #include <QMouseEvent>
24 #include <QOpenGLShaderProgram>
25 #include <QOpenGLFunctions>
26 #include <QPainter>
27 #include "gui/glspectrum.h"
28 #include "util/messagequeue.h"
29 
30 #include <QDebug>
31 
33 
34 GLSpectrum::GLSpectrum(QWidget* parent) :
35  QGLWidget(parent),
36  m_cursorState(CSNormal),
37  m_cursorChannel(0),
38  m_mouseInside(false),
39  m_changesPending(true),
40  m_centerFrequency(100000000),
41  m_referenceLevel(0),
42  m_powerRange(100),
43  m_linear(false),
44  m_decay(1),
45  m_sampleRate(500000),
46  m_timingRate(1),
47  m_fftSize(512),
48  m_displayGrid(true),
49  m_displayGridIntensity(5),
50  m_displayTraceIntensity(50),
51  m_invertedWaterfall(false),
52  m_displayMaxHold(false),
53  m_currentSpectrum(0),
54  m_displayCurrent(false),
55  m_leftMargin(0),
56  m_waterfallBuffer(0),
57  m_waterfallBufferPos(0),
58  m_waterfallTextureHeight(-1),
59  m_waterfallTexturePos(0),
60  m_displayWaterfall(true),
61  m_ssbSpectrum(false),
62  m_lsbDisplay(false),
63  m_histogramBuffer(0),
64  m_histogram(0),
65  m_displayHistogram(true),
66  m_displayChanged(false),
67  m_displaySourceOrSink(true),
68  m_displayStreamIndex(0),
69  m_matrixLoc(0),
70  m_colorLoc(0),
71  m_messageQueueToGUI(0)
72 {
73  setAutoFillBackground(false);
74  setAttribute(Qt::WA_OpaquePaintEvent, true);
75  setAttribute(Qt::WA_NoSystemBackground, true);
76  setMouseTracking(true);
77 
78  setMinimumSize(200, 200);
79 
80  m_waterfallShare = 0.66;
81 
82  for(int i = 0; i <= 239; i++) {
83  QColor c;
84  c.setHsv(239 - i, 255, 15 + i);
85  ((quint8*)&m_waterfallPalette[i])[0] = c.red();
86  ((quint8*)&m_waterfallPalette[i])[1] = c.green();
87  ((quint8*)&m_waterfallPalette[i])[2] = c.blue();
88  ((quint8*)&m_waterfallPalette[i])[3] = c.alpha();
89  }
90  m_waterfallPalette[239] = 0xffffffff;
91 
92  m_histogramPalette[0] = 0;
93 
94  for (int i = 1; i < 240; i++)
95  {
96  QColor c;
97  int light = i < 60 ? 128 + (60-i) : 128;
98  int sat = i < 60 ? 140 + i : i < 180 ? 200 : 200 - (i-180);
99  c.setHsl(239 - i, sat, light);
100  ((quint8*)&m_histogramPalette[i])[0] = c.red();
101  ((quint8*)&m_histogramPalette[i])[1] = c.green();
102  ((quint8*)&m_histogramPalette[i])[2] = c.blue();
103  ((quint8*)&m_histogramPalette[i])[3] = c.alpha();
104  }
105 
106  // 4.2.3 palette
107 // for (int i = 1; i < 240; i++)
108 // {
109 // QColor c;
110 // int val = i < 60 ? 255 : 200;
111 // int sat = i < 60 ? 128 : i < 180 ? 255 : 180;
112 // c.setHsv(239 - i, sat, val);
113 // ((quint8*)&m_histogramPalette[i])[0] = c.red();
114 // ((quint8*)&m_histogramPalette[i])[1] = c.green();
115 // ((quint8*)&m_histogramPalette[i])[2] = c.blue();
116 // ((quint8*)&m_histogramPalette[i])[3] = c.alpha();
117 // }
118 
119  // Original palette:
120 // for(int i = 16; i < 240; i++) {
121 // QColor c;
122 // c.setHsv(239 - i, 255 - ((i < 200) ? 0 : (i - 200) * 3), 150 + ((i < 100) ? i : 100));
123 // ((quint8*)&m_histogramPalette[i])[0] = c.red();
124 // ((quint8*)&m_histogramPalette[i])[1] = c.green();
125 // ((quint8*)&m_histogramPalette[i])[2] = c.blue();
126 // ((quint8*)&m_histogramPalette[i])[3] = c.alpha();
127 // }
128 // for(int i = 1; i < 16; i++) {
129 // QColor c;
130 // c.setHsv(255, 128, 48 + i * 4);
131 // ((quint8*)&m_histogramPalette[i])[0] = c.red();
132 // ((quint8*)&m_histogramPalette[i])[1] = c.green();
133 // ((quint8*)&m_histogramPalette[i])[2] = c.blue();
134 // ((quint8*)&m_histogramPalette[i])[3] = c.alpha();
135 // }
136 
137  m_decayDivisor = 1;
138  m_decayDivisorCount = m_decayDivisor;
139  m_histogramStroke = 30;
140 
141  m_timeScale.setFont(font());
142  m_timeScale.setOrientation(Qt::Vertical);
143  m_timeScale.setRange(Unit::Time, 0, 1);
144  m_powerScale.setFont(font());
145  m_powerScale.setOrientation(Qt::Vertical);
146  m_frequencyScale.setFont(font());
147  m_frequencyScale.setOrientation(Qt::Horizontal);
148 
149  connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
150  m_timer.start(50);
151 }
152 
154 {
155  cleanup();
156 
157  QMutexLocker mutexLocker(&m_mutex);
158 
159  m_changesPending = true;
160 
161  if(m_waterfallBuffer != NULL) {
162  delete m_waterfallBuffer;
163  m_waterfallBuffer = NULL;
164  }
165  if(m_histogramBuffer != NULL) {
166  delete m_histogramBuffer;
167  m_histogramBuffer = NULL;
168  }
169  if(m_histogram != NULL) {
170  delete[] m_histogram;
171  m_histogram = NULL;
172  }
173 }
174 
175 void GLSpectrum::setCenterFrequency(qint64 frequency)
176 {
177  m_centerFrequency = frequency;
178  m_changesPending = true;
179  update();
180 }
181 
183 {
184  m_referenceLevel = referenceLevel;
185  m_changesPending = true;
186  update();
187 }
188 
190 {
191  m_powerRange = powerRange;
192  m_changesPending = true;
193  update();
194 }
195 
196 void GLSpectrum::setDecay(int decay)
197 {
198  m_decay = decay < 0 ? 0 : decay > 20 ? 20 : decay;
199 }
200 
201 void GLSpectrum::setDecayDivisor(int decayDivisor)
202 {
203  m_decayDivisor = decayDivisor < 1 ? 1 : decayDivisor > 20 ? 20 : decayDivisor;
204 }
205 
207 {
208  m_histogramStroke = stroke < 1 ? 1 : stroke > 60 ? 60 : stroke;
209 }
210 
211 void GLSpectrum::setSampleRate(qint32 sampleRate)
212 {
213  m_sampleRate = sampleRate;
214  if (m_messageQueueToGUI) {
216  }
217  m_changesPending = true;
218  update();
219 }
220 
221 void GLSpectrum::setTimingRate(qint32 timingRate)
222 {
223  m_timingRate = timingRate;
224  m_changesPending = true;
225  update();
226 }
227 
229 {
230  m_displayWaterfall = display;
231  m_changesPending = true;
232  stopDrag();
233  update();
234 }
235 
236 void GLSpectrum::setSsbSpectrum(bool ssbSpectrum)
237 {
238  m_ssbSpectrum = ssbSpectrum;
239  update();
240 }
241 
242 void GLSpectrum::setLsbDisplay(bool lsbDisplay)
243 {
244  m_lsbDisplay = lsbDisplay;
245  update();
246 }
247 
249 {
250  m_invertedWaterfall = inv;
251  m_changesPending = true;
252  stopDrag();
253  update();
254 }
255 
257 {
258  m_displayMaxHold = display;
259  m_changesPending = true;
260  stopDrag();
261  update();
262 }
263 
265 {
266  m_displayCurrent = display;
267  m_changesPending = true;
268  stopDrag();
269  update();
270 }
271 
273 {
274  m_displayHistogram = display;
275  m_changesPending = true;
276  stopDrag();
277  update();
278 }
279 
280 void GLSpectrum::setDisplayGrid(bool display)
281 {
282  m_displayGrid = display;
283  update();
284 }
285 
287 {
288  m_displayGridIntensity = intensity;
289  if (m_displayGridIntensity > 100) {
291  } else if (m_displayGridIntensity < 0) {
293  }
294  update();
295 }
296 
298 {
299  m_displayTraceIntensity = intensity;
300  if (m_displayTraceIntensity > 100) {
302  } else if (m_displayTraceIntensity < 0) {
304  }
305  update();
306 }
307 
308 void GLSpectrum::setLinear(bool linear)
309 {
310  m_linear = linear;
311  m_changesPending = true;
312  update();
313 }
314 
316 {
317  QMutexLocker mutexLocker(&m_mutex);
318 
319  connect(channelMarker, SIGNAL(changedByAPI()), this, SLOT(channelMarkerChanged()));
320  connect(channelMarker, SIGNAL(destroyed(QObject*)), this, SLOT(channelMarkerDestroyed(QObject*)));
321  m_channelMarkerStates.append(new ChannelMarkerState(channelMarker));
322  m_changesPending = true;
323  stopDrag();
324  update();
325 }
326 
328 {
329  QMutexLocker mutexLocker(&m_mutex);
330 
331  for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
332  if(m_channelMarkerStates[i]->m_channelMarker == channelMarker) {
333  channelMarker->disconnect(this);
334  delete m_channelMarkerStates.takeAt(i);
335  m_changesPending = true;
336  stopDrag();
337  update();
338  return;
339  }
340  }
341 }
342 
343 void GLSpectrum::newSpectrum(const std::vector<Real>& spectrum, int fftSize)
344 {
345  QMutexLocker mutexLocker(&m_mutex);
346 
347  m_displayChanged = true;
348 
349  if(m_changesPending) {
350  m_fftSize = fftSize;
351  return;
352  }
353 
354  if(fftSize != m_fftSize) {
355  m_fftSize = fftSize;
356  m_changesPending = true;
357  return;
358  }
359 
360  updateWaterfall(spectrum);
361  updateHistogram(spectrum);
362 }
363 
364 void GLSpectrum::updateWaterfall(const std::vector<Real>& spectrum)
365 {
366  if(m_waterfallBufferPos < m_waterfallBuffer->height()) {
367  quint32* pix = (quint32*)m_waterfallBuffer->scanLine(m_waterfallBufferPos);
368 
369  for(int i = 0; i < m_fftSize; i++) {
370  int v = (int)((spectrum[i] - m_referenceLevel) * 2.4 * 100.0 / m_powerRange + 240.0);
371  if(v > 239)
372  v = 239;
373  else if(v < 0)
374  v = 0;
375 
376  *pix++ = m_waterfallPalette[(int)v];
377  }
378 
380  }
381 }
382 
383 void GLSpectrum::updateHistogram(const std::vector<Real>& spectrum)
384 {
385  quint8* b = m_histogram;
386  int fftMulSize = 100 * m_fftSize;
387 
388  if ((m_displayHistogram || m_displayMaxHold) && (m_decay != 0))
389  {
391 
392  if ((m_decay > 1) || (m_decayDivisorCount <= 0))
393  {
394  for (int i = 0; i < fftMulSize; i++)
395  {
396  if (*b > m_decay) {
397  *b = *b - m_decay;
398  } else {
399  *b = 0;
400  }
401 
402  b++;
403  }
404 
406  }
407  }
408 
409  m_currentSpectrum = &spectrum; // Store spectrum for current spectrum line display
410 
411 #if 0 //def USE_SSE2
412  if(m_decay >= 0) { // normal
413  const __m128 refl = {m_referenceLevel, m_referenceLevel, m_referenceLevel, m_referenceLevel};
414  const __m128 power = {m_powerRange, m_powerRange, m_powerRange, m_powerRange};
415  const __m128 mul = {100.0f, 100.0f, 100.0f, 100.0f};
416 
417  for(int i = 0; i < m_fftSize; i += 4) {
418  __m128 abc = _mm_loadu_ps (&spectrum[i]);
419  abc = _mm_sub_ps(abc, refl);
420  abc = _mm_mul_ps(abc, mul);
421  abc = _mm_div_ps(abc, power);
422  abc = _mm_add_ps(abc, mul);
423  __m128i result = _mm_cvtps_epi32(abc);
424 
425  for(int j = 0; j < 4; j++) {
426  int v = ((int*)&result)[j];
427  if((v >= 0) && (v <= 99)) {
428  b = m_histogram + (i + j) * 100 + v;
429  if(*b < 220)
430  *b += m_histogramStroke; // was 4
431  else if(*b < 239)
432  *b += 1;
433  }
434  }
435  }
436  } else { // draw double pixels
437  int add = -m_decay * 4;
438  const __m128 refl = {m_referenceLevel, m_referenceLevel, m_referenceLevel, m_referenceLevel};
439  const __m128 power = {m_powerRange, m_powerRange, m_powerRange, m_powerRange};
440  const __m128 mul = {100.0f, 100.0f, 100.0f, 100.0f};
441 
442  for(int i = 0; i < m_fftSize; i += 4) {
443  __m128 abc = _mm_loadu_ps (&spectrum[i]);
444  abc = _mm_sub_ps(abc, refl);
445  abc = _mm_mul_ps(abc, mul);
446  abc = _mm_div_ps(abc, power);
447  abc = _mm_add_ps(abc, mul);
448  __m128i result = _mm_cvtps_epi32(abc);
449 
450  for(int j = 0; j < 4; j++) {
451  int v = ((int*)&result)[j];
452  if((v >= 1) && (v <= 98)) {
453  b = m_histogram + (i + j) * 100 + v;
454  if(b[-1] < 220)
455  b[-1] += add;
456  else if(b[-1] < 239)
457  b[-1] += 1;
458  if(b[0] < 220)
459  b[0] += add;
460  else if(b[0] < 239)
461  b[0] += 1;
462  if(b[1] < 220)
463  b[1] += add;
464  else if(b[1] < 239)
465  b[1] += 1;
466  } else if((v >= 0) && (v <= 99)) {
467  b = m_histogram + (i + j) * 100 + v;
468  if(*b < 220)
469  *b += add;
470  else if(*b < 239)
471  *b += 1;
472  }
473  }
474  }
475  }
476 #else
477  for (int i = 0; i < m_fftSize; i++)
478  {
479  int v = (int)((spectrum[i] - m_referenceLevel) * 100.0 / m_powerRange + 100.0);
480 
481  if ((v >= 0) && (v <= 99))
482  {
483  b = m_histogram + i * 100 + v;
484 
485  // capping to 239 as palette values are [0..239]
486  if (*b + m_histogramStroke <= 239) {
487  *b += m_histogramStroke; // was 4
488  } else {
489  *b = 239;
490  }
491  }
492  }
493 #endif
494 }
495 
497 {
498  QOpenGLContext *glCurrentContext = QOpenGLContext::currentContext();
499 
500  if (glCurrentContext) {
501  if (QOpenGLContext::currentContext()->isValid()) {
502  qDebug() << "GLSpectrum::initializeGL: context:"
503  << " major: " << (QOpenGLContext::currentContext()->format()).majorVersion()
504  << " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion()
505  << " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no");
506  }
507  else {
508  qDebug() << "GLSpectrum::initializeGL: current context is invalid";
509  }
510  } else {
511  qCritical() << "GLSpectrum::initializeGL: no current context";
512  return;
513  }
514 
515  connect(glCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this, &GLSpectrum::cleanup); // TODO: when migrating to QOpenGLWidget
516 
517  QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
518  glFunctions->initializeOpenGLFunctions();
519 
520  //glDisable(GL_DEPTH_TEST);
526 }
527 
528 void GLSpectrum::resizeGL(int width, int height)
529 {
530  QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
531  glFunctions->glViewport(0, 0, width, height);
532  m_changesPending = true;
533 }
534 
536 {
537  if(!m_mutex.tryLock(2))
538  return;
539 
540  memset(m_histogram, 0x00, 100 * m_fftSize);
541 
542  m_mutex.unlock();
543  update();
544 }
545 
547 {
548  if(!m_mutex.tryLock(2))
549  return;
550 
551  if(m_changesPending)
552  applyChanges();
553 
554  if(m_fftSize <= 0) {
555  m_mutex.unlock();
556  return;
557  }
558 
559  QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
560  glFunctions->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
561  glFunctions->glClear(GL_COLOR_BUFFER_BIT);
562 
563  // paint waterfall
564  if (m_displayWaterfall)
565  {
566  {
567  GLfloat vtx1[] = {
568  0, m_invertedWaterfall ? 0.0f : 1.0f,
569  1, m_invertedWaterfall ? 0.0f : 1.0f,
570  1, m_invertedWaterfall ? 1.0f : 0.0f,
571  0, m_invertedWaterfall ? 1.0f : 0.0f
572  };
573 
574 
576  {
579  }
580  else
581  {
583  int linesLeft = m_waterfallTexturePos + m_waterfallBufferPos - m_waterfallTextureHeight;
584  m_glShaderWaterfall.subTexture(0, m_waterfallTexturePos, m_fftSize, breakLine, m_waterfallBuffer->scanLine(0));
585  m_glShaderWaterfall.subTexture(0, 0, m_fftSize, linesLeft, m_waterfallBuffer->scanLine(breakLine));
586  m_waterfallTexturePos = linesLeft;
587  }
588 
590 
591  float prop_y = m_waterfallTexturePos / (m_waterfallTextureHeight - 1.0);
592  float off = 1.0 / (m_waterfallTextureHeight - 1.0);
593 
594  GLfloat tex1[] = {
595  0, prop_y + 1 - off,
596  1, prop_y + 1 - off,
597  1, prop_y,
598  0, prop_y
599  };
600 
602  }
603 
604  // paint channels
605  if (m_mouseInside)
606  {
607  for (int i = 0; i < m_channelMarkerStates.size(); ++i)
608  {
610 
611  if (dv->m_channelMarker->getVisible()
614  {
615  {
616  GLfloat q3[] {
617  0, 0,
618  1, 0,
619  1, 1,
620  0, 1,
621  0.5, 0,
622  0.5, 1,
623  };
624 
625  QVector4D color(dv->m_channelMarker->getColor().redF(), dv->m_channelMarker->getColor().greenF(), dv->m_channelMarker->getColor().blueF(), 0.3f);
627 
628  QVector4D colorLine(0.8f, 0.8f, 0.6f, 1.0f);
629  m_glShaderSimple.drawSegments(dv->m_glMatrixDsbWaterfall, colorLine, &q3[8], 2);
630 
631  }
632  }
633  }
634  }
635 
636  // draw rect around
637  {
638  GLfloat q3[] {
639  1, 1,
640  0, 1,
641  0, 0,
642  1, 0
643  };
644 
645  QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
647  }
648  }
649 
650  // paint histogram
652  {
654  {
655  {
656  // import new lines into the texture
657  quint32* pix;
658  quint8* bs = m_histogram;
659 
660  for (int y = 0; y < 100; y++)
661  {
662  quint8* b = bs;
663  pix = (quint32*)m_histogramBuffer->scanLine(99 - y);
664 
665  for (int x = 0; x < m_fftSize; x++)
666  {
667  *pix = m_histogramPalette[*b];
668  pix++;
669  b += 100;
670  }
671 
672  bs++;
673  }
674 
675  GLfloat vtx1[] = {
676  0, 0,
677  1, 0,
678  1, 1,
679  0, 1
680  };
681  GLfloat tex1[] = {
682  0, 0,
683  1, 0,
684  1, 1,
685  0, 1
686  };
687 
688  m_glShaderHistogram.subTexture(0, 0, m_fftSize, 100, m_histogramBuffer->scanLine(0));
690  }
691  }
692 
693 
694  // paint channels
695  if(m_mouseInside)
696  {
697  // Effective BW overlays
698  for(int i = 0; i < m_channelMarkerStates.size(); ++i)
699  {
701 
702  if (dv->m_channelMarker->getVisible()
705  {
706  {
707  GLfloat q3[] {
708  0, 0,
709  1, 0,
710  1, 1,
711  0, 1,
712  0.5, 0,
713  0.5, 1
714  };
715 
716  QVector4D color(dv->m_channelMarker->getColor().redF(), dv->m_channelMarker->getColor().greenF(), dv->m_channelMarker->getColor().blueF(), 0.3f);
718 
719  QVector4D colorLine(0.8f, 0.8f, 0.6f, 1.0f);
720 
722  q3[6] = 0.5;
723  }
724 
725  m_glShaderSimple.drawSegments(dv->m_glMatrixDsbHistogram, colorLine, &q3[8], 2);
726  m_glShaderSimple.drawSegments(dv->m_glMatrixFreqScale, colorLine, q3, 2);
727  }
728  }
729  }
730  }
731 
732  // draw rect around
733  {
734  GLfloat q3[] {
735  1, 1,
736  0, 1,
737  0, 0,
738  1, 0
739  };
740 
741  QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
743  }
744  }
745 
746  // paint left scales (time and power)
748  {
749  {
750  GLfloat vtx1[] = {
751  0, 1,
752  1, 1,
753  1, 0,
754  0, 0
755  };
756  GLfloat tex1[] = {
757  0, 1,
758  1, 1,
759  1, 0,
760  0, 0
761  };
762 
764  }
765  }
766 
767  // paint frequency scale
769  {
770  {
771  GLfloat vtx1[] = {
772  0, 1,
773  1, 1,
774  1, 0,
775  0, 0
776  };
777  GLfloat tex1[] = {
778  0, 1,
779  1, 1,
780  1, 0,
781  0, 0
782  };
783 
785  }
786 
787  // paint channels
788 
789  // Effective bandwidth overlays
790  for(int i = 0; i < m_channelMarkerStates.size(); ++i)
791  {
793 
794  // frequency scale channel overlay
795  if (dv->m_channelMarker->getVisible()
798  {
799  {
800  GLfloat q3[] {
801  1, 0.2,
802  0, 0.2,
803  0, 0,
804  1, 0,
805  0.5, 0,
806  0.5, 1
807  };
808 
809  QVector4D color(dv->m_channelMarker->getColor().redF(), dv->m_channelMarker->getColor().greenF(), dv->m_channelMarker->getColor().blueF(), 0.5f);
811 
812  if (dv->m_channelMarker->getHighlighted())
813  {
814  QVector4D colorLine(0.8f, 0.8f, 0.6f, 1.0f);
815  m_glShaderSimple.drawSegments(dv->m_glMatrixDsbFreqScale, colorLine, &q3[8], 2);
816  m_glShaderSimple.drawSegments(dv->m_glMatrixFreqScale, colorLine, &q3[4], 2);
817  }
818  }
819  }
820  }
821  }
822 
823  // paint max hold lines on top of histogram
824  if (m_displayMaxHold)
825  {
826  if (m_maxHold.size() < (uint) m_fftSize) {
827  m_maxHold.resize(m_fftSize);
828  }
829 
830  for (int i = 0; i < m_fftSize; i++)
831  {
832  int j;
833  quint8* bs = m_histogram + i * 100;
834 
835  for (j = 99; j >= 0; j--)
836  {
837  if (bs[j] > 0) {
838  break;
839  }
840  }
841 
842  // m_referenceLevel : top
843  // m_referenceLevel - m_powerRange : bottom
844  m_maxHold[i] = ((j - 99) * m_powerRange) / 99.0 + m_referenceLevel;
845  }
846  {
847  GLfloat *q3 = m_q3FFT.m_array;
848 
849  for (int i = 0; i < m_fftSize; i++)
850  {
852 
853  if (v >= 0) {
854  v = 0;
855  } else if (v < -m_powerRange) {
856  v = -m_powerRange;
857  }
858 
859  q3[2*i] = (Real) i;
860  q3[2*i+1] = v;
861  }
862 
863  QVector4D color(1.0f, 0.0f, 0.0f, (float) m_displayTraceIntensity / 100.0f);
865  }
866  }
867 
868  // paint current spectrum line on top of histogram
870  {
871  {
872  Real bottom = -m_powerRange;
873  GLfloat *q3 = m_q3FFT.m_array;
874 
875  for(int i = 0; i < m_fftSize; i++)
876  {
877  Real v = (*m_currentSpectrum)[i] - m_referenceLevel;
878 
879  if(v > 0) {
880  v = 0;
881  } else if(v < bottom) {
882  v = bottom;
883  }
884 
885  q3[2*i] = (Real) i;
886  q3[2*i+1] = v;
887  }
888 
889  QVector4D color(1.0f, 1.0f, 0.25f, (float) m_displayTraceIntensity / 100.0f);
891  }
892  }
893 
894  // paint waterfall grid
896  {
897  const ScaleEngine::TickList* tickList;
898  const ScaleEngine::Tick* tick;
899  tickList = &m_timeScale.getTickList();
900 
901  {
902  GLfloat *q3 = m_q3TickTime.m_array;
903  int effectiveTicks = 0;
904 
905  for (int i= 0; i < tickList->count(); i++)
906  {
907  tick = &(*tickList)[i];
908 
909  if (tick->major)
910  {
911  if(tick->textSize > 0)
912  {
913  float y = tick->pos / m_timeScale.getSize();
914  q3[4*effectiveTicks] = 0;
915  q3[4*effectiveTicks+1] = y;
916  q3[4*effectiveTicks+2] = 1;
917  q3[4*effectiveTicks+3] = y;
918  effectiveTicks++;
919  }
920  }
921  }
922 
923  QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
924  m_glShaderSimple.drawSegments(m_glWaterfallBoxMatrix, color, q3, 2*effectiveTicks);
925  }
926 
927  tickList = &m_frequencyScale.getTickList();
928 
929  {
930  GLfloat *q3 = m_q3TickFrequency.m_array;
931  int effectiveTicks = 0;
932 
933  for (int i= 0; i < tickList->count(); i++)
934  {
935  tick = &(*tickList)[i];
936 
937  if (tick->major)
938  {
939  if (tick->textSize > 0)
940  {
941  float x = tick->pos / m_frequencyScale.getSize();
942  q3[4*effectiveTicks] = x;
943  q3[4*effectiveTicks+1] = 0;
944  q3[4*effectiveTicks+2] = x;
945  q3[4*effectiveTicks+3] = 1;
946  effectiveTicks++;
947  }
948  }
949  }
950 
951  QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
952  m_glShaderSimple.drawSegments(m_glWaterfallBoxMatrix, color, q3, 2*effectiveTicks);
953  }
954  }
955 
956  // paint histogram grid
958  {
959  const ScaleEngine::TickList* tickList;
960  const ScaleEngine::Tick* tick;
961  tickList = &m_powerScale.getTickList();
962 
963  {
964  GLfloat *q3 = m_q3TickPower.m_array;
965  int effectiveTicks = 0;
966 
967  for(int i= 0; i < tickList->count(); i++)
968  {
969  tick = &(*tickList)[i];
970 
971  if(tick->major)
972  {
973  if(tick->textSize > 0)
974  {
975  float y = tick->pos / m_powerScale.getSize();
976  q3[4*effectiveTicks] = 0;
977  q3[4*effectiveTicks+1] = 1-y;
978  q3[4*effectiveTicks+2] = 1;
979  q3[4*effectiveTicks+3] = 1-y;
980  effectiveTicks++;
981  }
982  }
983  }
984 
985  QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
986  m_glShaderSimple.drawSegments(m_glHistogramBoxMatrix, color, q3, 2*effectiveTicks);
987  }
988 
989  tickList = &m_frequencyScale.getTickList();
990 
991  {
992  GLfloat *q3 = m_q3TickFrequency.m_array;
993  int effectiveTicks = 0;
994 
995  for(int i= 0; i < tickList->count(); i++)
996  {
997  tick = &(*tickList)[i];
998 
999  if(tick->major)
1000  {
1001  if(tick->textSize > 0)
1002  {
1003  float x = tick->pos / m_frequencyScale.getSize();
1004  q3[4*effectiveTicks] = x;
1005  q3[4*effectiveTicks+1] = 0;
1006  q3[4*effectiveTicks+2] = x;
1007  q3[4*effectiveTicks+3] = 1;
1008  effectiveTicks++;
1009  }
1010  }
1011  }
1012 
1013  QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
1014  m_glShaderSimple.drawSegments(m_glHistogramBoxMatrix, color, q3, 2*effectiveTicks);
1015  }
1016  }
1017 
1018  m_mutex.unlock();
1019 }
1020 
1022 {
1023  if(m_cursorState != CSNormal) {
1025  releaseMouse();
1026  setCursor(Qt::ArrowCursor);
1028  }
1029 }
1030 
1032 {
1033  m_changesPending = false;
1034 
1035  if(m_fftSize <= 0)
1036  return;
1037 
1038  QFontMetrics fm(font());
1039  int M = fm.width("-");
1040 
1041  int topMargin = fm.ascent() * 1.5;
1042  int bottomMargin = fm.ascent() * 1.5;
1043 
1044  int waterfallHeight = 0;
1045  int waterfallTop = 0;
1046  int frequencyScaleHeight = fm.height() * 3; // +1 line for marker frequency scale
1047  int frequencyScaleTop = 0;
1048  int histogramTop = 0;
1049  int histogramHeight = 20;
1050  //int m_leftMargin;
1051  int rightMargin = fm.width("000");
1052 
1053  // displays both histogram and waterfall
1055  {
1056  waterfallHeight = height() * m_waterfallShare - 1;
1057 
1058  if(waterfallHeight < 0)
1059  {
1060  waterfallHeight = 0;
1061  }
1062 
1063  if(!m_invertedWaterfall)
1064  {
1065  waterfallTop = topMargin;
1066  frequencyScaleTop = waterfallTop + waterfallHeight + 1;
1067  histogramTop = waterfallTop + waterfallHeight + frequencyScaleHeight + 1;
1068  histogramHeight = height() - topMargin - waterfallHeight - frequencyScaleHeight - bottomMargin;
1069  }
1070  else
1071  {
1072  histogramTop = topMargin;
1073  histogramHeight = height() - topMargin - waterfallHeight - frequencyScaleHeight - bottomMargin;
1074  waterfallTop = histogramTop + histogramHeight + frequencyScaleHeight + 1;
1075  frequencyScaleTop = histogramTop + histogramHeight + 1;
1076  }
1077 
1078  m_timeScale.setSize(waterfallHeight);
1079 
1080  if(m_sampleRate > 0)
1081  {
1082  float scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1);
1083 
1084  if(!m_invertedWaterfall)
1085  {
1086  m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (waterfallHeight * m_fftSize) / scaleDiv, 0);
1087  }
1088  else
1089  {
1090  m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, 0, (waterfallHeight * m_fftSize) / scaleDiv);
1091  }
1092  }
1093  else
1094  {
1096  }
1097 
1098  m_powerScale.setSize(histogramHeight);
1099 
1100  if (m_linear) {
1102  } else {
1104  }
1105 
1107 
1109  {
1111  }
1112 
1113  m_leftMargin += 2 * M;
1114 
1115  m_frequencyScale.setSize(width() - m_leftMargin - rightMargin);
1118 
1119  m_glWaterfallBoxMatrix.setToIdentity();
1120  m_glWaterfallBoxMatrix.translate(
1121  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1122  1.0f - ((float)(2*waterfallTop) / (float) height())
1123  );
1124  m_glWaterfallBoxMatrix.scale(
1125  ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(),
1126  (float) (-2*waterfallHeight) / (float) height()
1127  );
1128 
1129  m_glHistogramBoxMatrix.setToIdentity();
1130  m_glHistogramBoxMatrix.translate(
1131  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1132  1.0f - ((float)(2*histogramTop) / (float) height())
1133  );
1134  m_glHistogramBoxMatrix.scale(
1135  ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(),
1136  (float) (-2*histogramHeight) / (float) height()
1137  );
1138 
1139  m_glHistogramSpectrumMatrix.setToIdentity();
1140  m_glHistogramSpectrumMatrix.translate(
1141  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1142  1.0f - ((float)(2*histogramTop) / (float) height())
1143  );
1145  ((float) 2 * (width() - m_leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)),
1146  ((float) 2*histogramHeight / height()) / m_powerRange
1147  );
1148 
1149  m_frequencyScaleRect = QRect(
1150  0,
1151  frequencyScaleTop,
1152  width(),
1153  frequencyScaleHeight
1154  );
1155 
1156  m_glFrequencyScaleBoxMatrix.setToIdentity();
1157  m_glFrequencyScaleBoxMatrix.translate (
1158  -1.0f,
1159  1.0f - ((float) 2*frequencyScaleTop / (float) height())
1160  );
1162  2.0f,
1163  (float) -2*frequencyScaleHeight / (float) height()
1164  );
1165 
1166  m_glLeftScaleBoxMatrix.setToIdentity();
1167  m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f);
1168  m_glLeftScaleBoxMatrix.scale(
1169  (float)(2*(m_leftMargin - 1)) / (float) width(),
1170  -2.0f
1171  );
1172  }
1173  // displays waterfall only
1174  else if(m_displayWaterfall)
1175  {
1176  bottomMargin = frequencyScaleHeight;
1177  waterfallTop = topMargin;
1178  waterfallHeight = height() - topMargin - frequencyScaleHeight;
1179  frequencyScaleTop = topMargin + waterfallHeight + 1;
1180  histogramTop = 0;
1181 
1182  m_timeScale.setSize(waterfallHeight);
1183 
1184  if(m_sampleRate > 0)
1185  {
1186  float scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1);
1187 
1188  if(!m_invertedWaterfall)
1189  {
1190  m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (waterfallHeight * m_fftSize) / scaleDiv, 0);
1191  }
1192  else
1193  {
1194  m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, 0, (waterfallHeight * m_fftSize) / scaleDiv);
1195  }
1196  }
1197  else
1198  {
1199  if(!m_invertedWaterfall)
1200  {
1202  }
1203  else
1204  {
1206  }
1207  }
1208 
1210  m_leftMargin += 2 * M;
1211 
1212  m_frequencyScale.setSize(width() - m_leftMargin - rightMargin);
1215 
1216  m_glWaterfallBoxMatrix.setToIdentity();
1217  m_glWaterfallBoxMatrix.translate(
1218  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1219  1.0f - ((float)(2*topMargin) / (float) height())
1220  );
1221  m_glWaterfallBoxMatrix.scale(
1222  ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(),
1223  (float) (-2*waterfallHeight) / (float) height()
1224  );
1225 
1226  m_frequencyScaleRect = QRect(
1227  0,
1228  frequencyScaleTop,
1229  width(),
1230  frequencyScaleHeight
1231  );
1232 
1233  m_glFrequencyScaleBoxMatrix.setToIdentity();
1234  m_glFrequencyScaleBoxMatrix.translate (
1235  -1.0f,
1236  1.0f - ((float) 2*frequencyScaleTop / (float) height())
1237  );
1239  2.0f,
1240  (float) -2*frequencyScaleHeight / (float) height()
1241  );
1242 
1243  m_glLeftScaleBoxMatrix.setToIdentity();
1244  m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f);
1245  m_glLeftScaleBoxMatrix.scale(
1246  (float)(2*(m_leftMargin - 1)) / (float) width(),
1247  -2.0f
1248  );
1249  }
1250  // displays histogram only
1252  {
1253  bottomMargin = frequencyScaleHeight;
1254  frequencyScaleTop = height() - bottomMargin;
1255  histogramTop = topMargin - 1;
1256  waterfallHeight = 0;
1257  histogramHeight = height() - topMargin - frequencyScaleHeight;
1258 
1259  m_powerScale.setSize(histogramHeight);
1262  m_leftMargin += 2 * M;
1263 
1264  m_frequencyScale.setSize(width() - m_leftMargin - rightMargin);
1267 
1268  m_glHistogramSpectrumMatrix.setToIdentity();
1269  m_glHistogramSpectrumMatrix.translate(
1270  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1271  1.0f - ((float)(2*histogramTop) / (float) height())
1272  );
1274  ((float) 2 * (width() - m_leftMargin - rightMargin)) / ((float) width() * (float)(m_fftSize - 1)),
1275  ((float) 2*(height() - topMargin - frequencyScaleHeight) / height()) / m_powerRange
1276  );
1277 
1278  m_glHistogramBoxMatrix.setToIdentity();
1279  m_glHistogramBoxMatrix.translate(
1280  -1.0f + ((float)(2*m_leftMargin) / (float) width()),
1281  1.0f - ((float)(2*histogramTop) / (float) height())
1282  );
1283  m_glHistogramBoxMatrix.scale(
1284  ((float) 2 * (width() - m_leftMargin - rightMargin)) / (float) width(),
1285  (float) (-2*(height() - topMargin - frequencyScaleHeight)) / (float) height()
1286  );
1287 
1288  m_frequencyScaleRect = QRect(
1289  0,
1290  frequencyScaleTop,
1291  width(),
1292  frequencyScaleHeight
1293  );
1294 
1295  m_glFrequencyScaleBoxMatrix.setToIdentity();
1296  m_glFrequencyScaleBoxMatrix.translate (
1297  -1.0f,
1298  1.0f - ((float) 2*frequencyScaleTop / (float) height())
1299  );
1301  2.0f,
1302  (float) -2*frequencyScaleHeight / (float) height()
1303  );
1304 
1305  m_glLeftScaleBoxMatrix.setToIdentity();
1306  m_glLeftScaleBoxMatrix.translate(-1.0f, 1.0f);
1307  m_glLeftScaleBoxMatrix.scale(
1308  (float)(2*(m_leftMargin - 1)) / (float) width(),
1309  -2.0f
1310  );
1311  }
1312  else
1313  {
1314  m_leftMargin = 2;
1315  waterfallHeight = 0;
1316  }
1317 
1318  // channel overlays
1319  for(int i = 0; i < m_channelMarkerStates.size(); ++i)
1320  {
1322 
1323  qreal xc, pw, nw, dsbw;
1325  xc = m_centerFrequency + dv->m_channelMarker->getCenterFrequency(); // marker center frequency
1326  dsbw = dv->m_channelMarker->getBandwidth();
1327 
1328  if (sidebands == ChannelMarker::usb) {
1329  nw = dv->m_channelMarker->getLowCutoff(); // negative bandwidth
1330  int bw = dv->m_channelMarker->getBandwidth() / 2;
1331  pw = (qreal) bw; // positive bandwidth
1332  } else if (sidebands == ChannelMarker::lsb) {
1333  pw = dv->m_channelMarker->getLowCutoff();
1334  int bw = dv->m_channelMarker->getBandwidth() / 2;
1335  nw = (qreal) bw;
1336  } else if (sidebands == ChannelMarker::vusb) {
1337  nw = -dv->m_channelMarker->getOppositeBandwidth(); // negative bandwidth
1338  pw = dv->m_channelMarker->getBandwidth(); // positive bandwidth
1339  } else if (sidebands == ChannelMarker::vlsb) {
1340  pw = dv->m_channelMarker->getOppositeBandwidth(); // positive bandwidth
1341  nw = -dv->m_channelMarker->getBandwidth(); // negative bandwidth
1342  } else {
1343  pw = dsbw / 2;
1344  nw = -pw;
1345  }
1346 
1347  // draw the DSB rectangle
1348 
1349  QMatrix4x4 glMatrixDsb;
1350  glMatrixDsb.setToIdentity();
1351  glMatrixDsb.translate(
1352  -1.0f + 2.0f * ((m_leftMargin + m_frequencyScale.getPosFromValue(xc - (dsbw/2))) / (float) width()),
1353  1.0f
1354  );
1355  glMatrixDsb.scale(
1356  2.0f * (dsbw / (float)m_sampleRate),
1357  -2.0f
1358  );
1359 
1360  dv->m_glMatrixDsbWaterfall = glMatrixDsb;
1361  dv->m_glMatrixDsbWaterfall.translate(
1362  0.0f,
1363  (float) waterfallTop / (float) height()
1364  );
1365  dv->m_glMatrixDsbWaterfall.scale(
1366  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1367  (float) waterfallHeight / (float) height()
1368  );
1369 
1370  dv->m_glMatrixDsbHistogram = glMatrixDsb;
1371  dv->m_glMatrixDsbHistogram.translate(
1372  0.0f,
1373  (float) histogramTop / (float) height()
1374  );
1375  dv->m_glMatrixDsbHistogram.scale(
1376  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1377  (float) histogramHeight / (float) height()
1378  );
1379 
1380  dv->m_glMatrixDsbFreqScale = glMatrixDsb;
1381  dv->m_glMatrixDsbFreqScale.translate(
1382  0.0f,
1383  (float) frequencyScaleTop / (float) height()
1384  );
1385  dv->m_glMatrixDsbFreqScale.scale(
1386  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1387  (float) frequencyScaleHeight / (float) height()
1388  );
1389 
1390  // draw the effective BW rectangle
1391 
1392  QMatrix4x4 glMatrix;
1393  glMatrix.setToIdentity();
1394  glMatrix.translate(
1395  -1.0f + 2.0f * ((m_leftMargin + m_frequencyScale.getPosFromValue(xc + nw)) / (float) width()),
1396  1.0f
1397  );
1398  glMatrix.scale(
1399  2.0f * ((pw-nw) / (float)m_sampleRate),
1400  -2.0f
1401  );
1402 
1403  dv->m_glMatrixWaterfall = glMatrix;
1404  dv->m_glMatrixWaterfall.translate(
1405  0.0f,
1406  (float) waterfallTop / (float) height()
1407  );
1408  dv->m_glMatrixWaterfall.scale(
1409  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1410  (float) waterfallHeight / (float) height()
1411  );
1412 
1413  dv->m_glMatrixHistogram = glMatrix;
1414  dv->m_glMatrixHistogram.translate(
1415  0.0f,
1416  (float) histogramTop / (float) height()
1417  );
1418  dv->m_glMatrixHistogram.scale(
1419  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1420  (float) histogramHeight / (float) height()
1421  );
1422 
1423  dv->m_glMatrixFreqScale = glMatrix;
1424  dv->m_glMatrixFreqScale.translate(
1425  0.0f,
1426  (float) frequencyScaleTop / (float) height()
1427  );
1428  dv->m_glMatrixFreqScale.scale(
1429  (float) (width() - m_leftMargin - rightMargin) / (float) width(),
1430  (float) frequencyScaleHeight / (float) height()
1431  );
1432 
1433 
1434  /*
1435  dv->m_glRect.setRect(
1436  m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency() - dv->m_channelMarker->getBandwidth() / 2) / (float)(width() - m_leftMargin - rightMargin),
1437  0,
1438  (dv->m_channelMarker->getBandwidth() / (float)m_sampleRate),
1439  1);
1440  */
1441 
1443  {
1444  dv->m_rect.setRect(m_frequencyScale.getPosFromValue(xc) + m_leftMargin - 1,
1445  topMargin,
1446  5,
1447  height() - topMargin - bottomMargin);
1448  }
1449 
1450  /*
1451  if(m_displayHistogram || m_displayMaxHold || m_displayWaterfall) {
1452  dv->m_rect.setRect(m_frequencyScale.getPosFromValue(m_centerFrequency + dv->m_channelMarker->getCenterFrequency()) + m_leftMargin - 1,
1453  topMargin,
1454  5,
1455  height() - topMargin - bottomMargin);
1456  }
1457  */
1458  }
1459 
1460  // prepare left scales (time and power)
1461  {
1462  m_leftMarginPixmap = QPixmap(m_leftMargin - 1, height());
1463  m_leftMarginPixmap.fill(Qt::black);
1464  {
1465  QPainter painter(&m_leftMarginPixmap);
1466  painter.setPen(QColor(0xf0, 0xf0, 0xff));
1467  painter.setFont(font());
1468  const ScaleEngine::TickList* tickList;
1469  const ScaleEngine::Tick* tick;
1470  if(m_displayWaterfall) {
1471  tickList = &m_timeScale.getTickList();
1472  for(int i = 0; i < tickList->count(); i++) {
1473  tick = &(*tickList)[i];
1474  if(tick->major) {
1475  if(tick->textSize > 0)
1476  painter.drawText(QPointF(m_leftMargin - M - tick->textSize, waterfallTop + fm.ascent() + tick->textPos), tick->text);
1477  }
1478  }
1479  }
1481  tickList = &m_powerScale.getTickList();
1482  for(int i = 0; i < tickList->count(); i++) {
1483  tick = &(*tickList)[i];
1484  if(tick->major) {
1485  if(tick->textSize > 0)
1486  painter.drawText(QPointF(m_leftMargin - M - tick->textSize, histogramTop + histogramHeight - tick->textPos - 1), tick->text);
1487  }
1488  }
1489  }
1490  }
1491 
1493  }
1494  // prepare frequency scale
1496  m_frequencyPixmap = QPixmap(width(), frequencyScaleHeight);
1497  m_frequencyPixmap.fill(Qt::transparent);
1498  {
1499  QPainter painter(&m_frequencyPixmap);
1500  painter.setPen(Qt::NoPen);
1501  painter.setBrush(Qt::black);
1502  painter.setBrush(Qt::transparent);
1503  painter.drawRect(m_leftMargin, 0, width() - m_leftMargin, frequencyScaleHeight);
1504  painter.setPen(QColor(0xf0, 0xf0, 0xff));
1505  painter.setFont(font());
1506  const ScaleEngine::TickList* tickList = &m_frequencyScale.getTickList();
1507  const ScaleEngine::Tick* tick;
1508  for(int i = 0; i < tickList->count(); i++) {
1509  tick = &(*tickList)[i];
1510  if(tick->major) {
1511  if(tick->textSize > 0)
1512  painter.drawText(QPointF(m_leftMargin + tick->textPos, fm.height() + fm.ascent() / 2 - 1), tick->text);
1513  }
1514  }
1515 
1516  // Frequency overlay on highlighted marker
1517  for(int i = 0; i < m_channelMarkerStates.size(); ++i) {
1519 
1520  if (dv->m_channelMarker->getHighlighted()
1523  {
1524  qreal xc;
1525  int shift;
1526  //ChannelMarker::sidebands_t sidebands = dv->m_channelMarker->getSidebands();
1527  xc = m_centerFrequency + dv->m_channelMarker->getCenterFrequency(); // marker center frequency
1528  QString ftext;
1530  {
1532  ftext = QString::number((m_centerFrequency + dv->m_channelMarker->getCenterFrequency())/1e6, 'f', 6);
1533  break;
1535  ftext = dv->m_channelMarker->getTitle();
1536  break;
1538  ftext = dv->m_channelMarker->getDisplayAddressSend();
1539  break;
1542  break;
1543  default:
1544  ftext = QString::number((m_centerFrequency + dv->m_channelMarker->getCenterFrequency())/1e6, 'f', 6);
1545  break;
1546  }
1547  if (dv->m_channelMarker->getCenterFrequency() < 0) { // left half of scale
1548  ftext = " " + ftext;
1549  shift = 0;
1550  } else { // right half of scale
1551  ftext = ftext + " ";
1552  shift = - fm.width(ftext);
1553  }
1554  painter.drawText(QPointF(m_leftMargin + m_frequencyScale.getPosFromValue(xc) + shift, 2*fm.height() + fm.ascent() / 2 - 1), ftext);
1555  }
1556  }
1557 
1558  }
1559 
1561  }
1562 
1563  bool fftSizeChanged = true;
1564 
1565  if(m_waterfallBuffer != NULL) {
1566  fftSizeChanged = m_waterfallBuffer->width() != m_fftSize;
1567  }
1568 
1569  bool windowSizeChanged = m_waterfallTextureHeight != waterfallHeight;
1570 
1571  if (fftSizeChanged || windowSizeChanged)
1572  {
1573  if(m_waterfallBuffer != 0) {
1574  delete m_waterfallBuffer;
1575  }
1576 
1577  m_waterfallBuffer = new QImage(m_fftSize, waterfallHeight, QImage::Format_ARGB32);
1578 
1579  m_waterfallBuffer->fill(qRgb(0x00, 0x00, 0x00));
1582  }
1583 
1584  if(fftSizeChanged)
1585  {
1586  if(m_histogramBuffer != NULL) {
1587  delete m_histogramBuffer;
1588  m_histogramBuffer = NULL;
1589  }
1590  if(m_histogram != NULL) {
1591  delete[] m_histogram;
1592  m_histogram = NULL;
1593  }
1594 
1595  m_histogramBuffer = new QImage(m_fftSize, 100, QImage::Format_RGB32);
1596 
1597  m_histogramBuffer->fill(qRgb(0x00, 0x00, 0x00));
1598  m_glShaderHistogram.initTexture(*m_histogramBuffer, QOpenGLTexture::ClampToEdge);
1599 
1600  m_histogram = new quint8[100 * m_fftSize];
1601  memset(m_histogram, 0x00, 100 * m_fftSize);
1602 
1604  }
1605 
1606  if(fftSizeChanged || windowSizeChanged)
1607  {
1608  m_waterfallTextureHeight = waterfallHeight;
1610  }
1611 
1615 }
1616 
1617 void GLSpectrum::mouseMoveEvent(QMouseEvent* event)
1618 {
1620  {
1621  if(m_frequencyScaleRect.contains(event->pos()))
1622  {
1623  if(m_cursorState == CSNormal)
1624  {
1625  setCursor(Qt::SizeVerCursor);
1627  return;
1628  }
1629  }
1630  else
1631  {
1632  if(m_cursorState == CSSplitter)
1633  {
1634  setCursor(Qt::ArrowCursor);
1636  return;
1637  }
1638  }
1639  }
1640 
1642  {
1643  float newShare;
1644 
1645  if (!m_invertedWaterfall) {
1646  newShare = (float) (event->y() - m_frequencyScaleRect.height()) / (float) height();
1647  } else {
1648  newShare = 1.0 - (float) (event->y() + m_frequencyScaleRect.height()) / (float) height();
1649  }
1650 
1651  if (newShare < 0.1) {
1652  newShare = 0.1f;
1653  } else if (newShare > 0.8) {
1654  newShare = 0.8f;
1655  }
1656 
1657  m_waterfallShare = newShare;
1658  m_changesPending = true;
1659 
1660  update();
1661  return;
1662  }
1663  else if (m_cursorState == CSChannelMoving)
1664  {
1665  Real freq = m_frequencyScale.getValueFromPos(event->x() - m_leftMarginPixmap.width() - 1) - m_centerFrequency;
1666 
1667  if (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getMovable()
1668  && (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getSourceOrSinkStream() == m_displaySourceOrSink)
1669  && (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getStreamIndex() == m_displayStreamIndex))
1670  {
1671  m_channelMarkerStates[m_cursorChannel]->m_channelMarker->setCenterFrequencyByCursor(freq);
1673  }
1674  }
1675 
1677  {
1678  for (int i = 0; i < m_channelMarkerStates.size(); ++i)
1679  {
1680  if ((m_channelMarkerStates[i]->m_channelMarker->getSourceOrSinkStream() != m_displaySourceOrSink)
1681  || (m_channelMarkerStates[i]->m_channelMarker->getStreamIndex() != m_displayStreamIndex))
1682  {
1683  continue;
1684  }
1685 
1686  if (m_channelMarkerStates[i]->m_rect.contains(event->pos()))
1687  {
1688  if (m_cursorState == CSNormal)
1689  {
1690  setCursor(Qt::SizeHorCursor);
1692  m_cursorChannel = i;
1693  m_channelMarkerStates[i]->m_channelMarker->setHighlightedByCursor(true);
1695 
1696  return;
1697  }
1698  else if (m_cursorState == CSChannel)
1699  {
1700  return;
1701  }
1702  }
1703  else if (m_channelMarkerStates[i]->m_channelMarker->getHighlighted())
1704  {
1705  m_channelMarkerStates[i]->m_channelMarker->setHighlightedByCursor(false);
1707  }
1708  }
1709  }
1710 
1711  if(m_cursorState == CSChannel)
1712  {
1713  setCursor(Qt::ArrowCursor);
1715 
1716  return;
1717  }
1718 }
1719 
1720 void GLSpectrum::mousePressEvent(QMouseEvent* event)
1721 {
1722  if(event->button() != 1)
1723  return;
1724 
1725  if(m_cursorState == CSSplitter)
1726  {
1727  grabMouse();
1729  return;
1730  }
1731  else if(m_cursorState == CSChannel)
1732  {
1733  grabMouse();
1735  return;
1736  }
1737  else if((m_cursorState == CSNormal) && (m_channelMarkerStates.size() == 1))
1738  {
1739  grabMouse();
1740  setCursor(Qt::SizeHorCursor);
1742  m_cursorChannel = 0;
1743  Real freq = m_frequencyScale.getValueFromPos(event->x() - m_leftMarginPixmap.width() - 1) - m_centerFrequency;
1744 
1745  if (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getMovable()
1746  && (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getSourceOrSinkStream() == m_displaySourceOrSink)
1747  && (m_channelMarkerStates[m_cursorChannel]->m_channelMarker->getStreamIndex() == m_displayStreamIndex))
1748  {
1749  m_channelMarkerStates[m_cursorChannel]->m_channelMarker->setCenterFrequencyByCursor(freq);
1751  }
1752 
1753  return;
1754  }
1755 }
1756 
1758 {
1760  releaseMouse();
1762  } else if(m_cursorState == CSChannelMoving) {
1763  releaseMouse();
1765  }
1766 }
1767 
1768 void GLSpectrum::wheelEvent(QWheelEvent *event)
1769 {
1770  int mul;
1771 
1772  if (event->modifiers() & Qt::ShiftModifier) {
1773  mul = 100;
1774  } else if (event->modifiers() & Qt::ControlModifier) {
1775  mul = 10;
1776  } else {
1777  mul = 1;
1778  }
1779 
1780  for (int i = 0; i < m_channelMarkerStates.size(); ++i)
1781  {
1782  if ((m_channelMarkerStates[i]->m_channelMarker->getSourceOrSinkStream() != m_displaySourceOrSink)
1783  || (m_channelMarkerStates[i]->m_channelMarker->getStreamIndex() != m_displayStreamIndex))
1784  {
1785  continue;
1786  }
1787 
1788  if (m_channelMarkerStates[i]->m_rect.contains(event->pos()))
1789  {
1790  int freq = m_channelMarkerStates[i]->m_channelMarker->getCenterFrequency();
1791 
1792  if (event->delta() > 0) {
1793  freq += 10 * mul;
1794  } else if (event->delta() < 0) {
1795  freq -= 10 * mul;
1796  }
1797 
1798  // calculate scale relative cursor position for new frequency
1799  float x_pos = m_frequencyScale.getPosFromValue(m_centerFrequency + freq);
1800 
1801  if ((x_pos >= 0.0) && (x_pos < m_frequencyScale.getSize())) // cursor must be in scale
1802  {
1803  m_channelMarkerStates[i]->m_channelMarker->setCenterFrequencyByCursor(freq);
1804  m_channelMarkerStates[i]->m_channelMarker->setCenterFrequency(freq);
1805 
1806  // cursor follow-up
1807  int xd = x_pos + m_leftMargin;
1808  QCursor c = cursor();
1809  QPoint cp_a = c.pos();
1810  QPoint cp_w = mapFromGlobal(cp_a);
1811  cp_w.setX(xd);
1812  cp_a = mapToGlobal(cp_w);
1813  c.setPos(cp_a);
1814  setCursor(c);
1815  }
1816  }
1817  }
1818 }
1819 
1820 void GLSpectrum::enterEvent(QEvent* event)
1821 {
1822  m_mouseInside = true;
1823  update();
1824  QGLWidget::enterEvent(event);
1825 }
1826 
1827 void GLSpectrum::leaveEvent(QEvent* event)
1828 {
1829  m_mouseInside = false;
1830  update();
1831  QGLWidget::enterEvent(event);
1832 }
1833 
1835 {
1836  if(m_displayChanged) {
1837  m_displayChanged = false;
1838  update();
1839  }
1840 }
1841 
1843 {
1844  m_changesPending = true;
1845  update();
1846 }
1847 
1849 {
1851 }
1852 
1854 {
1855  if (waterfallShare < 0.1f) {
1856  m_waterfallShare = 0.1f;
1857  }
1858  else if (waterfallShare > 0.8f) {
1859  m_waterfallShare = 0.8f;
1860  } else {
1861  m_waterfallShare = waterfallShare;
1862  }
1863  m_changesPending = true;
1864 }
1865 
1866 void GLSpectrum::connectTimer(const QTimer& timer)
1867 {
1868  qDebug() << "GLSpectrum::connectTimer";
1869  disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
1870  connect(&timer, SIGNAL(timeout()), this, SLOT(tick()));
1871  m_timer.stop();
1872 }
1873 
1875 {
1876  //makeCurrent();
1882  //doneCurrent();
1883 }
void setLsbDisplay(bool lsbDisplay)
Definition: glspectrum.cpp:242
void mouseMoveEvent(QMouseEvent *event)
void leaveEvent(QEvent *event)
int getOppositeBandwidth() const
Definition: channelmarker.h:48
void setSize(float size)
void connectTimer(const QTimer &timer)
int getStreamIndex() const
Definition: channelmarker.h:78
int getCenterFrequency() const
Definition: channelmarker.h:42
int m_cursorChannel
Definition: glspectrum.h:131
int getLowCutoff() const
Definition: channelmarker.h:51
int m_displayStreamIndex
Definition: glspectrum.h:191
void setDisplayGrid(bool display)
Definition: glspectrum.cpp:280
void setTimingRate(qint32 timingRate)
Definition: glspectrum.cpp:221
void push(Message *message, bool emitSignal=true)
Push message onto queue.
void setSampleRate(qint32 sampleRate)
Definition: glspectrum.cpp:211
Real m_referenceLevel
Definition: glspectrum.h:139
sidebands_t getSidebands() const
Definition: channelmarker.h:54
int m_displayTraceIntensity
Definition: glspectrum.h:150
void setDisplayTraceIntensity(int intensity)
Definition: glspectrum.cpp:297
void setPowerRange(Real powerRange)
Definition: glspectrum.cpp:189
bool m_displayGrid
Definition: glspectrum.h:148
Real m_powerRange
Definition: glspectrum.h:140
QTimer m_timer
Definition: glspectrum.h:133
QMatrix4x4 m_glFrequencyScaleBoxMatrix
Definition: glspectrum.h:167
frequencyScaleDisplay_t getFrequencyScaleDisplayType() const
Definition: channelmarker.h:70
QMatrix4x4 m_glHistogramBoxMatrix
Definition: glspectrum.h:187
void stopDrag()
bool m_linear
Definition: glspectrum.h:141
void drawSegments(const QMatrix4x4 &transformMatrix, const QVector4D &color, GLfloat *vertices, int nbVertices)
QList< ChannelMarkerState * > m_channelMarkerStates
Definition: glspectrum.h:120
QRgb m_histogramPalette[240]
Definition: glspectrum.h:180
const QString & getDisplayAddressSend() const
Definition: channelmarker.h:72
const QString & getDisplayAddressReceive() const
Definition: channelmarker.h:73
IncrementalArray< GLfloat > m_q3TickFrequency
Definition: glspectrum.h:201
std::vector< Real > m_maxHold
Definition: glspectrum.h:153
bool getSourceOrSinkStream() const
Definition: channelmarker.h:76
void channelMarkerChanged()
float getPosFromValue(double value)
bool m_invertedWaterfall
Definition: glspectrum.h:151
bool m_displayChanged
Definition: glspectrum.h:189
ScaleEngine m_frequencyScale
Definition: glspectrum.h:165
void setReferenceLevel(Real referenceLevel)
Definition: glspectrum.cpp:182
qint64 m_centerFrequency
Definition: glspectrum.h:138
bool m_changesPending
Definition: glspectrum.h:136
CursorState m_cursorState
Definition: glspectrum.h:130
void initTexture(const QImage &image, QOpenGLTexture::WrapMode wrapMode=QOpenGLTexture::Repeat)
int m_decayDivisor
Definition: glspectrum.h:183
void setDisplayHistogram(bool display)
Definition: glspectrum.cpp:272
void setDecay(int decay)
Definition: glspectrum.cpp:196
void mouseReleaseEvent(QMouseEvent *event)
void setDecayDivisor(int decayDivisor)
Definition: glspectrum.cpp:201
bool m_displayWaterfall
Definition: glspectrum.h:176
float getValueFromPos(double pos)
void updateWaterfall(const std::vector< Real > &spectrum)
Definition: glspectrum.cpp:364
QMatrix4x4 m_glWaterfallBoxMatrix
Definition: glspectrum.h:175
QMatrix4x4 m_glLeftScaleBoxMatrix
Definition: glspectrum.h:168
QRect m_frequencyScaleRect
Definition: glspectrum.h:166
void setDisplayCurrent(bool display)
Definition: glspectrum.cpp:264
IncrementalArray< GLfloat > m_q3TickTime
Definition: glspectrum.h:200
quint32 m_sampleRate
Definition: glspectrum.h:143
QPixmap m_leftMarginPixmap
Definition: glspectrum.h:161
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
bool m_ssbSpectrum
Definition: glspectrum.h:177
float getSize()
Definition: scaleengine.h:80
int m_waterfallTextureHeight
Definition: glspectrum.h:173
void drawSurface(const QMatrix4x4 &transformMatrix, const QVector4D &color, GLfloat *vertices, int nbVertices)
void drawSurface(const QMatrix4x4 &transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices)
LSB with vestigial USB.
Definition: channelmarker.h:21
void wheelEvent(QWheelEvent *)
ScaleEngine m_powerScale
Definition: glspectrum.h:164
bool m_displaySourceOrSink
Definition: glspectrum.h:190
void subTexture(int xOffset, int yOffset, int width, int height, const void *pixels)
QPixmap m_frequencyPixmap
Definition: glspectrum.h:162
GLShaderTextured m_glShaderFrequencyScale
Definition: glspectrum.h:195
bool m_displayMaxHold
Definition: glspectrum.h:154
GLShaderTextured m_glShaderWaterfall
Definition: glspectrum.h:196
USB with vestigial LSB.
Definition: channelmarker.h:20
int m_decayDivisorCount
Definition: glspectrum.h:184
int32_t i
Definition: decimators.h:244
const std::vector< Real > * m_currentSpectrum
Definition: glspectrum.h:155
QImage * m_waterfallBuffer
Definition: glspectrum.h:171
void drawContour(const QMatrix4x4 &transformMatrix, const QVector4D &color, GLfloat *vertices, int nbVertices)
const QString & getTitle() const
Definition: channelmarker.h:38
void setInvertedWaterfall(bool inv)
Definition: glspectrum.cpp:248
QRgb m_waterfallPalette[240]
Definition: glspectrum.h:170
bool m_displayCurrent
Definition: glspectrum.h:156
enum ChannelMarker::sidebands_e sidebands_t
bool getHighlighted() const
Definition: channelmarker.h:61
Real m_waterfallShare
Definition: glspectrum.h:158
void addChannelMarker(ChannelMarker *channelMarker)
Definition: glspectrum.cpp:315
void newSpectrum(const std::vector< Real > &spectrum, int fftSize)
Definition: glspectrum.cpp:343
void setHistoStroke(int stroke)
Definition: glspectrum.cpp:206
int m_displayGridIntensity
Definition: glspectrum.h:149
int m_waterfallTexturePos
Definition: glspectrum.h:174
void updateHistogram(const std::vector< Real > &spectrum)
Definition: glspectrum.cpp:383
quint8 * m_histogram
Spectrum phosphor matrix of FFT width and PSD height scaled to 100. values [0..239].
Definition: glspectrum.h:182
QMatrix4x4 m_glHistogramSpectrumMatrix
Definition: glspectrum.h:186
int m_histogramStroke
Definition: glspectrum.h:185
void allocate(uint32_t size)
void setSsbSpectrum(bool ssbSpectrum)
Definition: glspectrum.cpp:236
void channelMarkerDestroyed(QObject *object)
const TickList & getTickList()
IncrementalArray< GLfloat > m_q3FFT
Definition: glspectrum.h:203
QList< Tick > TickList
Definition: scaleengine.h:37
MessageQueue * m_messageQueueToGUI
Definition: glspectrum.h:205
void mousePressEvent(QMouseEvent *event)
quint32 m_timingRate
Definition: glspectrum.h:144
void setLinear(bool linear)
Definition: glspectrum.cpp:308
void removeChannelMarker(ChannelMarker *channelMarker)
Definition: glspectrum.cpp:327
void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax)
void drawPolyline(const QMatrix4x4 &transformMatrix, const QVector4D &color, GLfloat *vertices, int nbVertices)
int getBandwidth() const
Definition: channelmarker.h:45
void setCenterFrequency(qint64 frequency)
Definition: glspectrum.cpp:175
GLShaderTextured m_glShaderLeftScale
Definition: glspectrum.h:194
void paintGL()
Definition: glspectrum.cpp:546
const QColor & getColor() const
Definition: channelmarker.h:64
void initializeGL()
Definition: glspectrum.cpp:496
void resizeGL(int width, int height)
Definition: glspectrum.cpp:528
QImage * m_histogramBuffer
Definition: glspectrum.h:181
bool getVisible() const
Definition: channelmarker.h:57
bool m_displayHistogram
Definition: glspectrum.h:188
void setDisplayGridIntensity(int intensity)
Definition: glspectrum.cpp:286
int m_fftSize
Definition: glspectrum.h:146
void applyChanges()
void setWaterfallShare(Real waterfallShare)
GLShaderTextured m_glShaderHistogram
Definition: glspectrum.h:197
QMutex m_mutex
Definition: glspectrum.h:134
float getScaleWidth()
void setMakeOpposite(bool makeOpposite)
Definition: scaleengine.h:82
GLShaderSimple m_glShaderSimple
Definition: glspectrum.h:193
ScaleEngine m_timeScale
Definition: glspectrum.h:163
void clearSpectrumHistogram()
Definition: glspectrum.cpp:535
float Real
Definition: dsptypes.h:42
bool m_lsbDisplay
Definition: glspectrum.h:178
void setDisplayMaxHold(bool display)
Definition: glspectrum.cpp:256
int m_waterfallBufferPos
Definition: glspectrum.h:172
void cleanup()
ChannelMarker * m_channelMarker
Definition: glspectrum.h:107
void enterEvent(QEvent *event)
void setDisplayWaterfall(bool display)
Definition: glspectrum.cpp:228
bool m_mouseInside
Definition: glspectrum.h:135
int m_leftMargin
Definition: glspectrum.h:160
IncrementalArray< GLfloat > m_q3TickPower
Definition: glspectrum.h:202