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.
spectrumvis.cpp
Go to the documentation of this file.
1 #include "dsp/spectrumvis.h"
2 #include "gui/glspectrum.h"
3 #include "dsp/dspcommands.h"
4 #include "util/messagequeue.h"
5 
6 #define MAX_FFT_SIZE 4096
7 
8 #ifndef LINUX
9 inline double log2f(double n)
10 {
11  return log(n) / log(2.0);
12 }
13 #endif
14 
16 
17 const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f));
18 
21  m_fft(FFTEngine::create()),
22  m_fftBuffer(MAX_FFT_SIZE),
23  m_powerSpectrum(MAX_FFT_SIZE),
24  m_fftBufferFill(0),
25  m_needMoreSamples(false),
26  m_scalef(scalef),
27  m_glSpectrum(glSpectrum),
28  m_averageNb(0),
29  m_avgMode(AvgModeNone),
30  m_linear(false),
31  m_ofs(0),
32  m_powFFTDiv(1.0),
33  m_mutex(QMutex::Recursive)
34 {
35  setObjectName("SpectrumVis");
37 }
38 
40 {
41  delete m_fft;
42 }
43 
45  int fftSize,
46  int overlapPercent,
47  unsigned int averagingNb,
48  int averagingMode,
49  FFTWindow::Function window,
50  bool linear)
51 {
52  MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window, linear);
53  msgQueue->push(cmd);
54 }
55 
56 void SpectrumVis::feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly)
57 {
58  feed(triggerPoint, end, positiveOnly); // normal feed from trigger point
59  /*
60  if (triggerPoint == end)
61  {
62  // the following piece of code allows to terminate the FFT that ends past the end of scope captured data
63  // that is the spectrum will include the captured data
64  // just do nothing if you want the spectrum to be included inside the scope captured data
65  // that is to drop the FFT that dangles past the end of captured data
66  if (m_needMoreSamples) {
67  feed(begin, end, positiveOnly);
68  m_needMoreSamples = false; // force finish
69  }
70  }
71  else
72  {
73  feed(triggerPoint, end, positiveOnly); // normal feed from trigger point
74  }*/
75 }
76 
77 void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly)
78 {
79  // if no visualisation is set, send the samples to /dev/null
80 
81  if (m_glSpectrum == 0) {
82  return;
83  }
84 
85  if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
86  return;
87  }
88 
89  SampleVector::const_iterator begin(cbegin);
90 
91  while (begin < end)
92  {
93  std::size_t todo = end - begin;
94  std::size_t samplesNeeded = m_refillSize - m_fftBufferFill;
95 
96  if (todo >= samplesNeeded)
97  {
98  // fill up the buffer
99  std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill;
100 
101  for (std::size_t i = 0; i < samplesNeeded; ++i, ++begin)
102  {
103  *it++ = Complex(begin->real() / m_scalef, begin->imag() / m_scalef);
104  }
105 
106  // apply fft window (and copy from m_fftBuffer to m_fftIn)
107  m_window.apply(&m_fftBuffer[0], m_fft->in());
108 
109  // calculate FFT
110  m_fft->transform();
111 
112  // extract power spectrum and reorder buckets
113  const Complex* fftOut = m_fft->out();
114  Complex c;
115  Real v;
116  std::size_t halfSize = m_fftSize / 2;
117 
118  if (m_avgMode == AvgModeNone)
119  {
120  if ( positiveOnly )
121  {
122  for (std::size_t i = 0; i < halfSize; i++)
123  {
124  c = fftOut[i];
125  v = c.real() * c.real() + c.imag() * c.imag();
126  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
127  m_powerSpectrum[i * 2] = v;
128  m_powerSpectrum[i * 2 + 1] = v;
129  }
130  }
131  else
132  {
133  for (std::size_t i = 0; i < halfSize; i++)
134  {
135  c = fftOut[i + halfSize];
136  v = c.real() * c.real() + c.imag() * c.imag();
137  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
138  m_powerSpectrum[i] = v;
139 
140  c = fftOut[i];
141  v = c.real() * c.real() + c.imag() * c.imag();
142  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
143  m_powerSpectrum[i + halfSize] = v;
144  }
145  }
146 
147  // send new data to visualisation
149  }
150  else if (m_avgMode == AvgModeMovingAvg)
151  {
152  if ( positiveOnly )
153  {
154  for (std::size_t i = 0; i < halfSize; i++)
155  {
156  c = fftOut[i];
157  v = c.real() * c.real() + c.imag() * c.imag();
159  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
160  m_powerSpectrum[i * 2] = v;
161  m_powerSpectrum[i * 2 + 1] = v;
162  }
163  }
164  else
165  {
166  for (std::size_t i = 0; i < halfSize; i++)
167  {
168  c = fftOut[i + halfSize];
169  v = c.real() * c.real() + c.imag() * c.imag();
170  v = m_movingAverage.storeAndGetAvg(v, i+halfSize);
171  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
172  m_powerSpectrum[i] = v;
173 
174  c = fftOut[i];
175  v = c.real() * c.real() + c.imag() * c.imag();
177  v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
178  m_powerSpectrum[i + halfSize] = v;
179  }
180  }
181 
182  // send new data to visualisation
185  }
186  else if (m_avgMode == AvgModeFixedAvg)
187  {
188  double avg;
189 
190  if ( positiveOnly )
191  {
192  for (std::size_t i = 0; i < halfSize; i++)
193  {
194  c = fftOut[i];
195  v = c.real() * c.real() + c.imag() * c.imag();
196 
197  if (m_fixedAverage.storeAndGetAvg(avg, v, i))
198  {
199  avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
200  m_powerSpectrum[i * 2] = avg;
201  m_powerSpectrum[i * 2 + 1] = avg;
202  }
203  }
204  }
205  else
206  {
207  for (std::size_t i = 0; i < halfSize; i++)
208  {
209  c = fftOut[i + halfSize];
210  v = c.real() * c.real() + c.imag() * c.imag();
211 
212  if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
213  { // result available
214  avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
215  m_powerSpectrum[i] = avg;
216  }
217 
218  c = fftOut[i];
219  v = c.real() * c.real() + c.imag() * c.imag();
220 
221  if (m_fixedAverage.storeAndGetAvg(avg, v, i))
222  { // result available
223  avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
224  m_powerSpectrum[i + halfSize] = avg;
225  }
226  }
227  }
228 
229  if (m_fixedAverage.nextAverage()) { // result available
230  m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
231  }
232  }
233  else if (m_avgMode == AvgModeMax)
234  {
235  double max;
236 
237  if ( positiveOnly )
238  {
239  for (std::size_t i = 0; i < halfSize; i++)
240  {
241  c = fftOut[i];
242  v = c.real() * c.real() + c.imag() * c.imag();
243 
244  if (m_max.storeAndGetMax(max, v, i))
245  {
246  max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
247  m_powerSpectrum[i * 2] = max;
248  m_powerSpectrum[i * 2 + 1] = max;
249  }
250  }
251  }
252  else
253  {
254  for (std::size_t i = 0; i < halfSize; i++)
255  {
256  c = fftOut[i + halfSize];
257  v = c.real() * c.real() + c.imag() * c.imag();
258 
259  if (m_max.storeAndGetMax(max, v, i+halfSize))
260  { // result available
261  max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
262  m_powerSpectrum[i] = max;
263  }
264 
265  c = fftOut[i];
266  v = c.real() * c.real() + c.imag() * c.imag();
267 
268  if (m_max.storeAndGetMax(max, v, i))
269  { // result available
270  max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
271  m_powerSpectrum[i + halfSize] = max;
272  }
273  }
274  }
275 
276  if (m_max.nextMax()) { // result available
277  m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
278  }
279  }
280 
281  // advance buffer respecting the fft overlap factor
282  std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin());
283 
284  // start over
285  m_fftBufferFill = m_overlapSize;
286  m_needMoreSamples = false;
287  }
288  else
289  {
290  // not enough samples for FFT - just fill in new data and return
291  for(std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin)
292  {
293  *it++ = Complex(begin->real() / m_scalef, begin->imag() / m_scalef);
294  }
295 
296  m_fftBufferFill += todo;
297  m_needMoreSamples = true;
298  }
299  }
300 
301  m_mutex.unlock();
302 }
303 
305 {
306 }
307 
309 {
310 }
311 
313 {
314  if (MsgConfigureSpectrumVis::match(message))
315  {
318  conf.getOverlapPercent(),
319  conf.getAverageNb(),
320  conf.getAvgMode(),
321  conf.getWindow(),
322  conf.getLinear());
323  return true;
324  }
325  else
326  {
327  return false;
328  }
329 }
330 
332  int overlapPercent,
333  unsigned int averageNb,
334  AvgMode averagingMode,
335  FFTWindow::Function window,
336  bool linear)
337 {
338 // qDebug("SpectrumVis::handleConfigure, fftSize: %d overlapPercent: %d averageNb: %u averagingMode: %d window: %d linear: %s",
339 // fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false");
340  QMutexLocker mutexLocker(&m_mutex);
341 
342  if (fftSize > MAX_FFT_SIZE)
343  {
344  fftSize = MAX_FFT_SIZE;
345  }
346  else if (fftSize < 64)
347  {
348  fftSize = 64;
349  }
350 
351  if (overlapPercent > 100)
352  {
353  m_overlapPercent = 100;
354  }
355  else if (overlapPercent < 0)
356  {
357  m_overlapPercent = 0;
358  }
359  else
360  {
361  m_overlapPercent = overlapPercent;
362  }
363 
364  m_fftSize = fftSize;
365  m_fft->configure(m_fftSize, false);
366  m_window.create(window, m_fftSize);
370  m_movingAverage.resize(fftSize, averageNb > 1000 ? 1000 : averageNb); // Capping to avoid out of memory condition
371  m_fixedAverage.resize(fftSize, averageNb);
372  m_max.resize(fftSize, averageNb);
373  m_averageNb = averageNb;
374  m_avgMode = averagingMode;
375  m_linear = linear;
376  m_ofs = 20.0f * log10f(1.0f / m_fftSize);
378 }
SpectrumVis(Real scalef, GLSpectrum *glSpectrum=0)
Definition: spectrumvis.cpp:19
QMutex m_mutex
Definition: spectrumvis.h:109
void push(Message *message, bool emitSignal=true)
Push message onto queue.
virtual Complex * in()=0
GLSpectrum * m_glSpectrum
Definition: spectrumvis.h:97
void resize(unsigned int width, unsigned int depth)
std::size_t m_overlapSize
Definition: spectrumvis.h:91
void create(Function function, int n)
Definition: fftwindow.cpp:21
bool m_needMoreSamples
Definition: spectrumvis.h:94
FixedAverage2D< double > m_fixedAverage
Definition: spectrumvis.h:99
void apply(const std::vector< Real > &in, std::vector< Real > *out)
Definition: fftwindow.cpp:58
virtual bool handleMessage(const Message &message)
Processing of a message. Returns true if message has actually been processed.
#define MAX_FFT_SIZE
Definition: spectrumvis.cpp:6
std::size_t m_overlapPercent
Definition: spectrumvis.h:90
Real m_scalef
Definition: spectrumvis.h:96
bool storeAndGetAvg(T &avg, T v, unsigned int index)
virtual void transform()=0
Real m_powFFTDiv
Definition: spectrumvis.h:106
SpectrumVis::AvgMode getAvgMode() const
Definition: spectrumvis.h:52
bool storeAndGetMax(T &max, T v, unsigned int index)
Definition: max2d.h:55
std::size_t m_refillSize
Definition: spectrumvis.h:92
virtual void configure(int n, bool inverse)=0
FFTEngine * m_fft
Definition: spectrumvis.h:83
void resize(unsigned int width, unsigned int size)
void * create(QString type)
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
unsigned int m_averageNb
Definition: spectrumvis.h:101
void resize(unsigned int width, unsigned int size)
Definition: max2d.h:37
Fixed< IntType, IntBits > log(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2295
virtual void stop()
std::vector< Real > m_powerSpectrum
Definition: spectrumvis.h:87
int32_t i
Definition: decimators.h:244
std::size_t m_fftBufferFill
Definition: spectrumvis.h:93
static bool match(const Message *message)
Definition: message.cpp:45
void newSpectrum(const std::vector< Real > &spectrum, int fftSize)
Definition: glspectrum.cpp:343
void handleConfigure(int fftSize, int overlapPercent, unsigned int averageNb, AvgMode averagingMode, FFTWindow::Function window, bool linear)
void feedTriggered(const SampleVector::const_iterator &triggerPoint, const SampleVector::const_iterator &end, bool positiveOnly)
Definition: spectrumvis.cpp:56
std::size_t m_fftSize
Definition: spectrumvis.h:89
static const Real m_mult
Definition: spectrumvis.h:107
bool nextMax()
Definition: max2d.h:81
Max2D< double > m_max
Definition: spectrumvis.h:100
AvgMode m_avgMode
Definition: spectrumvis.h:102
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool positiveOnly)
Definition: spectrumvis.cpp:77
void configure(MessageQueue *msgQueue, int fftSize, int overlapPercent, unsigned int averagingNb, int averagingMode, FFTWindow::Function window, bool m_linear)
Definition: spectrumvis.cpp:44
virtual Complex * out()=0
virtual ~SpectrumVis()
Definition: spectrumvis.cpp:39
T storeAndGetAvg(T v, unsigned int index)
FFTWindow m_window
Definition: spectrumvis.h:84
MovingAverage2D< double > m_movingAverage
Definition: spectrumvis.h:98
std::complex< Real > Complex
Definition: dsptypes.h:43
double log2f(double n)
Definition: spectrumvis.cpp:9
float Real
Definition: dsptypes.h:42
FFTWindow::Function getWindow() const
Definition: spectrumvis.h:53
unsigned int getAverageNb() const
Definition: spectrumvis.h:51
T max(const T &x, const T &y)
Definition: framework.h:446
std::vector< Complex > m_fftBuffer
Definition: spectrumvis.h:86
virtual void start()