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.
testmithread.cpp
Go to the documentation of this file.
1 // Copyright (C) 2019 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 
23 #include "dsp/samplesinkfifo.h"
24 
25 #include "testmithread.h"
26 
27 #define TESTMI_BLOCKSIZE 16384
28 
30 
31 TestMIThread::TestMIThread(SampleSinkFifo* sampleFifo, int streamIndex, QObject* parent) :
32  QThread(parent),
33  m_running(false),
34  m_buf(0),
35  m_bufsize(0),
36  m_chunksize(0),
37  m_convertBuffer(TESTMI_BLOCKSIZE),
38  m_sampleFifo(sampleFifo),
39  m_streamIndex(streamIndex),
40  m_frequencyShift(0),
41  m_toneFrequency(440),
42  m_modulation(TestMIStreamSettings::ModulationNone),
43  m_amModulation(0.5f),
44  m_fmDeviationUnit(0.0f),
45  m_fmPhasor(0.0f),
46  m_pulseWidth(150),
47  m_pulseSampleCount(0),
48  m_pulsePatternCount(0),
49  m_pulsePatternCycle(8),
50  m_pulsePatternPlaces(3),
51  m_samplerate(48000),
52  m_log2Decim(4),
53  m_fcPos(0),
54  m_bitSizeIndex(0),
55  m_bitShift(8),
56  m_amplitudeBits(127),
57  m_dcBias(0.0f),
58  m_iBias(0.0f),
59  m_qBias(0.0f),
60  m_phaseImbalance(0.0f),
61  m_amplitudeBitsDC(0),
62  m_amplitudeBitsI(127),
63  m_amplitudeBitsQ(127),
64  m_frequency(435*1000),
65  m_fcPosShift(0),
66  m_throttlems(TESTMI_THROTTLE_MS),
67  m_throttleToggle(false),
68  m_mutex(QMutex::Recursive)
69 {
70  connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
71 }
72 
74 {
75 }
76 
78 {
79  m_timer.setTimerType(Qt::PreciseTimer);
80  connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
81  m_timer.start(50);
82  m_startWaitMutex.lock();
83  m_elapsedTimer.start();
84  start();
85  while(!m_running)
86  m_startWaiter.wait(&m_startWaitMutex, 100);
87  m_startWaitMutex.unlock();
88 }
89 
91 {
92  m_running = false;
93  wait();
94  m_timer.stop();
95  disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
96 }
97 
98 void TestMIThread::setSamplerate(int samplerate)
99 {
100  QMutexLocker mutexLocker(&m_mutex);
101 
102  m_samplerate = samplerate;
103  m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
107 }
108 
109 void TestMIThread::setLog2Decimation(unsigned int log2_decim)
110 {
111  m_log2Decim = log2_decim;
112 }
113 
114 void TestMIThread::setFcPos(int fcPos)
115 {
116  m_fcPos = fcPos;
117 }
118 
119 void TestMIThread::setBitSize(quint32 bitSizeIndex)
120 {
121  switch (bitSizeIndex)
122  {
123  case 0:
124  m_bitShift = 7;
125  m_bitSizeIndex = 0;
126  break;
127  case 1:
128  m_bitShift = 11;
129  m_bitSizeIndex = 1;
130  break;
131  case 2:
132  default:
133  m_bitShift = 15;
134  m_bitSizeIndex = 2;
135  break;
136  }
137 }
138 
140 {
141  m_amplitudeBits = amplitudeBits;
142  m_amplitudeBitsDC = m_dcBias * amplitudeBits;
143  m_amplitudeBitsI = (1.0f + m_iBias) * amplitudeBits;
144  m_amplitudeBitsQ = (1.0f + m_qBias) * amplitudeBits;
145 }
146 
147 void TestMIThread::setDCFactor(float dcFactor)
148 {
149  m_dcBias = dcFactor;
151 }
152 
153 void TestMIThread::setIFactor(float iFactor)
154 {
155  m_iBias = iFactor;
157 }
158 
159 void TestMIThread::setQFactor(float iFactor)
160 {
161  m_qBias = iFactor;
163 }
164 
165 void TestMIThread::setPhaseImbalance(float phaseImbalance)
166 {
167  m_phaseImbalance = phaseImbalance;
168 }
169 
171 {
172  m_nco.setFreq(shift, m_samplerate);
173 }
174 
175 void TestMIThread::setToneFrequency(int toneFrequency)
176 {
177  m_toneNco.setFreq(toneFrequency, m_samplerate);
178 }
179 
181 {
182  m_modulation = modulation;
183 }
184 
185 void TestMIThread::setAMModulation(float amModulation)
186 {
187  m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation;
188 }
189 
190 void TestMIThread::setFMDeviation(float deviation)
191 {
192  float fmDeviationUnit = deviation / (float) m_samplerate;
193  m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 0.5f ? 0.5f : fmDeviationUnit;
194  qDebug("TestMIThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit);
195 }
196 
197 void TestMIThread::startStop(bool start)
198 {
199  MsgStartStop *msg = MsgStartStop::create(start);
201 }
202 
204 {
205  m_running = true;
206  m_startWaiter.wakeAll();
207 
208  while (m_running) // actual work is in the tick() function
209  {
210  sleep(1);
211  }
212 
213  m_running = false;
214 }
215 
216 void TestMIThread::setBuffers(quint32 chunksize)
217 {
218  if (chunksize > m_bufsize)
219  {
220  m_bufsize = chunksize;
221 
222  if (m_buf == 0)
223  {
224  qDebug() << "TestMIThread::setBuffer: Allocate buffer: "
225  << " size: " << m_bufsize << " bytes"
226  << " #samples: " << (m_bufsize/4);
227  m_buf = (qint16*) malloc(m_bufsize);
228  }
229  else
230  {
231  qDebug() << "TestMIThread::setBuffer: Re-allocate buffer: "
232  << " size: " << m_bufsize << " bytes"
233  << " #samples: " << (m_bufsize/4);
234  free(m_buf);
235  m_buf = (qint16*) malloc(m_bufsize);
236  }
237 
238  m_convertBuffer.resize(chunksize/4);
239  }
240 }
241 
242 void TestMIThread::generate(quint32 chunksize)
243 {
244  int n = chunksize / 2;
245  setBuffers(chunksize);
246 
247  for (int i = 0; i < n-1;)
248  {
249  switch (m_modulation)
250  {
252  {
253  Complex c = m_nco.nextIQ();
254  Real t, re, im;
255  pullAF(t);
256  t = (t*m_amModulation + 1.0f)*0.5f;
257  re = c.real()*t;
258  im = c.imag()*t + m_phaseImbalance*re;
259  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
260  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
261  }
262  break;
264  {
265  Complex c = m_nco.nextIQ();
266  Real t, re, im;
267  pullAF(t);
269  m_fmPhasor = m_fmPhasor < -1.0f ? -m_fmPhasor - 1.0f : m_fmPhasor > 1.0f ? m_fmPhasor - 1.0f : m_fmPhasor;
270  re = c.real()*cos(m_fmPhasor*M_PI) - c.imag()*sin(m_fmPhasor*M_PI);
271  im = (c.real()*sin(m_fmPhasor*M_PI) + c.imag()*cos(m_fmPhasor*M_PI)) + m_phaseImbalance*re;
272  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
273  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
274  }
275  break;
276  case TestMIStreamSettings::ModulationPattern0: // binary pattern
277  {
278  if (m_pulseSampleCount < m_pulseWidth) // sync pattern: 0
279  {
281  m_buf[i++] = 0;
282  }
283  else if (m_pulseSampleCount < 2*m_pulseWidth) // sync pattern: 1
284  {
286  m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ);
287  }
288  else if (m_pulseSampleCount < 3*m_pulseWidth) // sync pattern: 0
289  {
291  m_buf[i++] = 0;
292  }
293  else if (m_pulseSampleCount < (3+m_pulsePatternPlaces)*m_pulseWidth) // binary pattern
294  {
295  uint32_t patPulseSampleCount = m_pulseSampleCount - 3*m_pulseWidth;
296  uint32_t patPulseIndex = patPulseSampleCount / m_pulseWidth;
297  float patFigure = (m_pulsePatternCount & (1<<patPulseIndex)) != 0 ? 0.3 : 0.0; // make binary pattern ~-10dB vs sync pattern
298  m_buf[i++] = (int16_t) (patFigure * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
299  m_buf[i++] = (int16_t) (patFigure * (float) m_phaseImbalance * m_amplitudeBitsQ);
300  }
301 
303  {
305  }
306  else
307  {
310  } else {
312  }
313 
314  m_pulseSampleCount = 0;
315  }
316  }
317  break;
318  case TestMIStreamSettings::ModulationPattern1: // sawtooth pattern
319  {
320  Real re, im;
321  re = (float) (m_pulseWidth - m_pulseSampleCount) / (float) m_pulseWidth;
322  im = m_phaseImbalance*re;
323  m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
324  m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
325 
326  if (m_pulseSampleCount < m_pulseWidth - 1) {
328  } else {
329  m_pulseSampleCount = 0;
330  }
331  }
332  break;
333  case TestMIStreamSettings::ModulationPattern2: // 50% duty cycle square pattern
334  {
335  if (m_pulseSampleCount < m_pulseWidth) // 1
336  {
338  m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ);
339  } else { // 0
341  m_buf[i++] = 0;
342  }
343 
344  if (m_pulseSampleCount < 2*m_pulseWidth - 1) {
346  } else {
347  m_pulseSampleCount = 0;
348  }
349  }
350  break;
352  default:
353  {
355  m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
356  m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ);
357  }
358  break;
359  }
360  }
361 
362  callback(m_buf, n);
363 }
364 
365 void TestMIThread::pullAF(Real& afSample)
366 {
367  afSample = m_toneNco.next();
368 }
369 
370 // call appropriate conversion (decimation) routine depending on the number of sample bits
371 void TestMIThread::callback(const qint16* buf, qint32 len)
372 {
373  SampleVector::iterator it = m_convertBuffer.begin();
374 
375  switch (m_bitSizeIndex)
376  {
377  case 0: // 8 bit samples
378  convert_8(&it, buf, len);
379  break;
380  case 1: // 12 bit samples
381  convert_12(&it, buf, len);
382  break;
383  case 2: // 16 bit samples
384  default:
385  convert_16(&it, buf, len);
386  break;
387  }
388 
389  m_sampleFifo->write(m_convertBuffer.begin(), it);
390 }
391 
393 {
394  if (m_running)
395  {
396  qint64 throttlems = m_elapsedTimer.restart();
397 
398  if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems))
399  {
400  QMutexLocker mutexLocker(&m_mutex);
401  m_throttlems = throttlems;
402  m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
404  }
405 
407  }
408 }
409 
411 {
412  Message* message;
413 
414  while ((message = m_inputMessageQueue.pop()) != 0)
415  {
416  if (MsgStartStop::match(*message))
417  {
418  MsgStartStop* notif = (MsgStartStop*) message;
419  qDebug("TestMIThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop");
420 
421  if (notif->getStartStop()) {
422  startWork();
423  } else {
424  stopWork();
425  }
426 
427  delete message;
428  }
429  }
430 }
431 
433 {
434  m_pulseWidth = 150;
435  m_pulseSampleCount = 0;
439 }
440 
442 {
443  m_pulseWidth = 1000;
444  m_pulseSampleCount = 0;
445 }
446 
448 {
449  m_pulseWidth = 1000;
450  m_pulseSampleCount = 0;
451 }
short int16_t
Definition: rtptypes_win.h:43
void setQFactor(float qFactor)
void setSamplerate(int samplerate)
SampleSinkFifo * m_sampleFifo
Definition: testmithread.h:92
void convert_16(SampleVector::iterator *it, const qint16 *buf, qint32 len)
Definition: testmithread.h:302
void setPattern2()
Message * pop()
Pop message from queue.
bool m_throttleToggle
Definition: testmithread.h:128
QElapsedTimer m_elapsedTimer
Definition: testmithread.h:127
void push(Message *message, bool emitSignal=true)
Push message onto queue.
Fixed< IntType, IntBits > cos(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2271
int32_t m_amplitudeBitsDC
Definition: testmithread.h:118
uint write(const quint8 *data, uint count)
unsigned int m_log2Decim
Definition: testmithread.h:109
TestMIStreamSettings::Modulation m_modulation
Definition: testmithread.h:98
QWaitCondition m_startWaiter
Definition: testmithread.h:85
static MsgStartStop * create(bool startStop)
Definition: testmithread.h:48
#define TESTMI_BLOCKSIZE
uint32_t m_pulseSampleCount
Definition: testmithread.h:103
void setFMDeviation(float deviation)
uint32_t m_bitSizeIndex
Definition: testmithread.h:111
void setIFactor(float iFactor)
float m_phaseImbalance
Definition: testmithread.h:117
uint32_t m_bitShift
Definition: testmithread.h:112
void setDCFactor(float iFactor)
void generate(quint32 chunksize)
uint32_t m_pulsePatternCycle
Definition: testmithread.h:105
int32_t m_amplitudeBitsQ
Definition: testmithread.h:120
#define M_PI
Definition: rdsdemod.cpp:27
uint32_t m_pulsePatternPlaces
Definition: testmithread.h:106
SampleVector m_convertBuffer
Definition: testmithread.h:91
Complex nextIQ()
Return next complex sample.
Definition: ncof.cpp:63
unsigned int uint32_t
Definition: rtptypes_win.h:46
#define TESTMI_THROTTLE_MS
Definition: testmithread.h:36
int32_t m_amplitudeBits
Definition: testmithread.h:113
uint32_t m_pulsePatternCount
Definition: testmithread.h:104
MessageQueue m_inputMessageQueue
Definition: testmithread.h:131
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
void handleInputMessages()
void convert_8(SampleVector::iterator *it, const qint16 *buf, qint32 len)
Definition: testmithread.h:146
void setBuffers(quint32 chunksize)
Fixed< IntType, IntBits > sin(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2265
int32_t i
Definition: decimators.h:244
float m_fmPhasor
Definition: testmithread.h:101
void setBitSize(uint32_t bitSizeIndex)
static bool match(const Message *message)
Definition: message.cpp:45
void pullAF(Real &afSample)
int int32_t
Definition: rtptypes_win.h:45
void callback(const qint16 *buf, qint32 len)
void setModulation(TestMIStreamSettings::Modulation modulation)
volatile bool m_running
Definition: testmithread.h:86
QMutex m_mutex
Definition: testmithread.h:129
void setPattern1()
int m_toneFrequency
Definition: testmithread.h:97
QMutex m_startWaitMutex
Definition: testmithread.h:84
void startWork()
void setFcPos(int fcPos)
void setPattern0()
void setToneFrequency(int toneFrequency)
float m_amModulation
Definition: testmithread.h:99
QTimer m_timer
Definition: testmithread.h:126
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
uint32_t m_pulseWidth
pulse width in number of samples
Definition: testmithread.h:102
void setAmplitudeBits(int32_t amplitudeBits)
void startStop(bool start)
int32_t m_amplitudeBitsI
Definition: testmithread.h:119
Real next()
Return next real sample.
Definition: ncof.cpp:57
quint32 m_chunksize
Definition: testmithread.h:90
void setLog2Decimation(unsigned int log2_decim)
std::complex< Real > Complex
Definition: dsptypes.h:43
qint16 * m_buf
Definition: testmithread.h:88
void convert_12(SampleVector::iterator *it, const qint16 *buf, qint32 len)
Definition: testmithread.h:224
void setPhaseImbalance(float phaseImbalance)
float Real
Definition: dsptypes.h:42
quint32 m_bufsize
Definition: testmithread.h:89
int m_frequencyShift
Definition: testmithread.h:96
void setAMModulation(float amModulation)
void setFrequencyShift(int shift)
float m_fmDeviationUnit
Definition: testmithread.h:100