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.
testsourcethread.cpp
Go to the documentation of this file.
1 // Copyright (C) 2018 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #define _USE_MATH_DEFINES
19 #include <math.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include "testsourcethread.h"
23 
24 #include "dsp/samplesinkfifo.h"
25 
26 #define TESTSOURCE_BLOCKSIZE 16384
27 
29 
30 TestSourceThread::TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent) :
31  QThread(parent),
32  m_running(false),
33  m_buf(0),
34  m_bufsize(0),
35  m_chunksize(0),
36  m_convertBuffer(TESTSOURCE_BLOCKSIZE),
37  m_sampleFifo(sampleFifo),
38  m_frequencyShift(0),
39  m_toneFrequency(440),
40  m_modulation(TestSourceSettings::ModulationNone),
41  m_amModulation(0.5f),
42  m_fmDeviationUnit(0.0f),
43  m_fmPhasor(0.0f),
44  m_pulseWidth(150),
45  m_pulseSampleCount(0),
46  m_pulsePatternCount(0),
47  m_pulsePatternCycle(8),
48  m_pulsePatternPlaces(3),
49  m_samplerate(48000),
50  m_log2Decim(4),
51  m_fcPos(0),
52  m_bitSizeIndex(0),
53  m_bitShift(8),
54  m_amplitudeBits(127),
55  m_dcBias(0.0f),
56  m_iBias(0.0f),
57  m_qBias(0.0f),
58  m_phaseImbalance(0.0f),
59  m_amplitudeBitsDC(0),
60  m_amplitudeBitsI(127),
61  m_amplitudeBitsQ(127),
62  m_frequency(435*1000),
63  m_fcPosShift(0),
64  m_throttlems(TESTSOURCE_THROTTLE_MS),
65  m_throttleToggle(false),
66  m_mutex(QMutex::Recursive),
67  m_histoCounter(0)
68 {
69  connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
70 }
71 
73 {
74 }
75 
77 {
78  m_timer.setTimerType(Qt::PreciseTimer);
79  connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
80  m_timer.start(50);
81  m_startWaitMutex.lock();
82  m_elapsedTimer.start();
83  start();
84  while(!m_running)
85  m_startWaiter.wait(&m_startWaitMutex, 100);
86  m_startWaitMutex.unlock();
87 }
88 
90 {
91  m_running = false;
92  wait();
93  m_timer.stop();
94  disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
95 }
96 
97 void TestSourceThread::setSamplerate(int samplerate)
98 {
99  QMutexLocker mutexLocker(&m_mutex);
100 
101  m_samplerate = samplerate;
102  m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
106 }
107 
108 void TestSourceThread::setLog2Decimation(unsigned int log2_decim)
109 {
110  m_log2Decim = log2_decim;
111 }
112 
114 {
115  m_fcPos = fcPos;
116 }
117 
118 void TestSourceThread::setBitSize(quint32 bitSizeIndex)
119 {
120  switch (bitSizeIndex)
121  {
122  case 0:
123  m_bitShift = 7;
124  m_bitSizeIndex = 0;
125  break;
126  case 1:
127  m_bitShift = 11;
128  m_bitSizeIndex = 1;
129  break;
130  case 2:
131  default:
132  m_bitShift = 15;
133  m_bitSizeIndex = 2;
134  break;
135  }
136 }
137 
139 {
140  m_amplitudeBits = amplitudeBits;
141  m_amplitudeBitsDC = m_dcBias * amplitudeBits;
142  m_amplitudeBitsI = (1.0f + m_iBias) * amplitudeBits;
143  m_amplitudeBitsQ = (1.0f + m_qBias) * amplitudeBits;
144 }
145 
146 void TestSourceThread::setDCFactor(float dcFactor)
147 {
148  m_dcBias = dcFactor;
150 }
151 
152 void TestSourceThread::setIFactor(float iFactor)
153 {
154  m_iBias = iFactor;
156 }
157 
158 void TestSourceThread::setQFactor(float iFactor)
159 {
160  m_qBias = iFactor;
162 }
163 
164 void TestSourceThread::setPhaseImbalance(float phaseImbalance)
165 {
166  m_phaseImbalance = phaseImbalance;
167 }
168 
170 {
171  m_nco.setFreq(shift, m_samplerate);
172 }
173 
174 void TestSourceThread::setToneFrequency(int toneFrequency)
175 {
176  m_toneNco.setFreq(toneFrequency, m_samplerate);
177 }
178 
180 {
181  m_modulation = modulation;
182 }
183 
184 void TestSourceThread::setAMModulation(float amModulation)
185 {
186  m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation;
187 }
188 
189 void TestSourceThread::setFMDeviation(float deviation)
190 {
191  float fmDeviationUnit = deviation / (float) m_samplerate;
192  m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 0.5f ? 0.5f : fmDeviationUnit;
193  qDebug("TestSourceThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit);
194 }
195 
197 {
198  MsgStartStop *msg = MsgStartStop::create(start);
200 }
201 
203 {
204  m_running = true;
205  m_startWaiter.wakeAll();
206 
207  while (m_running) // actual work is in the tick() function
208  {
209  sleep(1);
210  }
211 
212  m_running = false;
213 }
214 
215 void TestSourceThread::setBuffers(quint32 chunksize)
216 {
217  if (chunksize > m_bufsize)
218  {
219  m_bufsize = chunksize;
220 
221  if (m_buf == 0)
222  {
223  qDebug() << "TestSourceThread::setBuffer: Allocate buffer: "
224  << " size: " << m_bufsize << " bytes"
225  << " #samples: " << (m_bufsize/4);
226  m_buf = (qint16*) malloc(m_bufsize);
227  }
228  else
229  {
230  qDebug() << "TestSourceThread::setBuffer: Re-allocate buffer: "
231  << " size: " << m_bufsize << " bytes"
232  << " #samples: " << (m_bufsize/4);
233  free(m_buf);
234  m_buf = (qint16*) malloc(m_bufsize);
235  }
236 
237  m_convertBuffer.resize(chunksize/4);
238  }
239 }
240 
241 void TestSourceThread::generate(quint32 chunksize)
242 {
243  int n = chunksize / 2;
244  setBuffers(chunksize);
245 
246  for (int i = 0; i < n-1;)
247  {
248  switch (m_modulation)
249  {
251  {
252  Complex c = m_nco.nextIQ();
253  Real t, re, im;
254  pullAF(t);
255  t = (t*m_amModulation + 1.0f)*0.5f;
256  re = c.real()*t;
257  im = c.imag()*t + m_phaseImbalance*re;
258  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
259  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
260  }
261  break;
263  {
264  Complex c = m_nco.nextIQ();
265  Real t, re, im;
266  pullAF(t);
268  m_fmPhasor = m_fmPhasor < -1.0f ? -m_fmPhasor - 1.0f : m_fmPhasor > 1.0f ? m_fmPhasor - 1.0f : m_fmPhasor;
269  re = c.real()*cos(m_fmPhasor*M_PI) - c.imag()*sin(m_fmPhasor*M_PI);
270  im = (c.real()*sin(m_fmPhasor*M_PI) + c.imag()*cos(m_fmPhasor*M_PI)) + m_phaseImbalance*re;
271  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
272  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
273  }
274  break;
275  case TestSourceSettings::ModulationPattern0: // binary pattern
276  {
277  if (m_pulseSampleCount < m_pulseWidth) // sync pattern: 0
278  {
280  m_buf[i++] = 0;
281  }
282  else if (m_pulseSampleCount < 2*m_pulseWidth) // sync pattern: 1
283  {
285  m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ);
286  }
287  else if (m_pulseSampleCount < 3*m_pulseWidth) // sync pattern: 0
288  {
290  m_buf[i++] = 0;
291  }
292  else if (m_pulseSampleCount < (3+m_pulsePatternPlaces)*m_pulseWidth) // binary pattern
293  {
294  uint32_t patPulseSampleCount = m_pulseSampleCount - 3*m_pulseWidth;
295  uint32_t patPulseIndex = patPulseSampleCount / m_pulseWidth;
296  float patFigure = (m_pulsePatternCount & (1<<patPulseIndex)) != 0 ? 0.3 : 0.0; // make binary pattern ~-10dB vs sync pattern
297  m_buf[i++] = (int16_t) (patFigure * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
298  m_buf[i++] = (int16_t) (patFigure * (float) m_phaseImbalance * m_amplitudeBitsQ);
299  }
300 
302  {
304  }
305  else
306  {
309  } else {
311  }
312 
313  m_pulseSampleCount = 0;
314  }
315  }
316  break;
317  case TestSourceSettings::ModulationPattern1: // sawtooth pattern
318  {
319  Real re, im;
320  re = (float) (m_pulseWidth - m_pulseSampleCount) / (float) m_pulseWidth;
321  im = m_phaseImbalance*re;
322  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
323  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
324 
325  if (m_pulseSampleCount < m_pulseWidth - 1) {
327  } else {
328  m_pulseSampleCount = 0;
329  }
330  }
331  break;
332  case TestSourceSettings::ModulationPattern2: // 50% duty cycle square pattern
333  {
334  if (m_pulseSampleCount < m_pulseWidth) // 1
335  {
337  m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ);
338  } else { // 0
340  m_buf[i++] = 0;
341  }
342 
343  if (m_pulseSampleCount < 2*m_pulseWidth - 1) {
345  } else {
346  m_pulseSampleCount = 0;
347  }
348  }
349  break;
351  default:
352  {
354  m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
355  m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ);
356  }
357  break;
358  }
359  }
360 
361  callback(m_buf, n);
362 }
363 
365 {
366  afSample = m_toneNco.next();
367 }
368 
369 // call appropriate conversion (decimation) routine depending on the number of sample bits
370 void TestSourceThread::callback(const qint16* buf, qint32 len)
371 {
372  SampleVector::iterator it = m_convertBuffer.begin();
373 
374  switch (m_bitSizeIndex)
375  {
376  case 0: // 8 bit samples
377  convert_8(&it, buf, len);
378  break;
379  case 1: // 12 bit samples
380  convert_12(&it, buf, len);
381  break;
382  case 2: // 16 bit samples
383  default:
384  convert_16(&it, buf, len);
385  break;
386  }
387 
388  m_sampleFifo->write(m_convertBuffer.begin(), it);
389 }
390 
392 {
393  if (m_running)
394  {
395  qint64 throttlems = m_elapsedTimer.restart();
396 
397  std::map<int,int>::iterator it;
398  it = m_timerHistogram.find(throttlems);
399 
400  if (it == m_timerHistogram.end()) {
401  m_timerHistogram[throttlems] = 1;
402  } else {
403  it->second++;
404  }
405 
406  if (m_histoCounter < 49) {
407  m_histoCounter++;
408  } else {
409  qDebug("TestSourceThread::tick: -----------");
410  for (std::map<int,int>::iterator it = m_timerHistogram.begin(); it != m_timerHistogram.end(); ++it) {
411  qDebug("TestSourceThread::tick: %d: %d", it->first, it->second);
412  }
413  m_histoCounter = 0;
414  }
415 
416  if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems))
417  {
418  QMutexLocker mutexLocker(&m_mutex);
419  m_throttlems = throttlems;
420  m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
422  }
423 
425  }
426 }
427 
429 {
430  Message* message;
431 
432  while ((message = m_inputMessageQueue.pop()) != 0)
433  {
434  if (MsgStartStop::match(*message))
435  {
436  MsgStartStop* notif = (MsgStartStop*) message;
437  qDebug("TestSourceThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop");
438 
439  if (notif->getStartStop()) {
440  startWork();
441  } else {
442  stopWork();
443  }
444 
445  delete message;
446  }
447  }
448 }
449 
451 {
452  m_pulseWidth = 150;
453  m_pulseSampleCount = 0;
457 }
458 
460 {
461  m_pulseWidth = 1000;
462  m_pulseSampleCount = 0;
463 }
464 
466 {
467  m_pulseWidth = 1000;
468  m_pulseSampleCount = 0;
469 }
short int16_t
Definition: rtptypes_win.h:43
uint32_t m_pulsePatternPlaces
Message * pop()
Pop message from queue.
void push(Message *message, bool emitSignal=true)
Push message onto queue.
Fixed< IntType, IntBits > cos(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2271
static MsgStartStop * create(bool startStop)
uint32_t m_pulseWidth
pulse width in number of samples
uint32_t m_pulsePatternCount
void setPhaseImbalance(float phaseImbalance)
void setSamplerate(int samplerate)
uint32_t m_pulseSampleCount
void setIFactor(float iFactor)
uint write(const quint8 *data, uint count)
void setLog2Decimation(unsigned int log2_decim)
void startStop(bool start)
void setAMModulation(float amModulation)
void setBitSize(uint32_t bitSizeIndex)
void convert_12(SampleVector::iterator *it, const qint16 *buf, qint32 len)
#define M_PI
Definition: rdsdemod.cpp:27
SampleVector m_convertBuffer
void setModulation(TestSourceSettings::Modulation modulation)
void convert_8(SampleVector::iterator *it, const qint16 *buf, qint32 len)
SampleSinkFifo * m_sampleFifo
#define TESTSOURCE_THROTTLE_MS
Complex nextIQ()
Return next complex sample.
Definition: ncof.cpp:63
QWaitCondition m_startWaiter
unsigned int uint32_t
Definition: rtptypes_win.h:46
void setToneFrequency(int toneFrequency)
void setFrequencyShift(int shift)
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
volatile bool m_running
uint32_t m_pulsePatternCycle
Fixed< IntType, IntBits > sin(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2265
void generate(quint32 chunksize)
int32_t i
Definition: decimators.h:244
void setAmplitudeBits(int32_t amplitudeBits)
static bool match(const Message *message)
Definition: message.cpp:45
int int32_t
Definition: rtptypes_win.h:45
void callback(const qint16 *buf, qint32 len)
void setFMDeviation(float deviation)
void pullAF(Real &afSample)
void setDCFactor(float iFactor)
void setBuffers(quint32 chunksize)
unsigned int m_log2Decim
void convert_16(SampleVector::iterator *it, const qint16 *buf, qint32 len)
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
TestSourceSettings::Modulation m_modulation
MessageQueue m_inputMessageQueue
std::map< int, int > m_timerHistogram
void setQFactor(float qFactor)
Real next()
Return next real sample.
Definition: ncof.cpp:57
std::complex< Real > Complex
Definition: dsptypes.h:43
float Real
Definition: dsptypes.h:42
#define TESTSOURCE_BLOCKSIZE
QElapsedTimer m_elapsedTimer
void setFcPos(int fcPos)