| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // Copyright (C) 2018 Edouard Griffiths, F4EXB                                   //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							| 
									
										
										
										
											2019-04-11 06:57:41 +02:00
										 |  |  | // (at your option) any later version.                                           //
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-13 11:16:49 +01:00
										 |  |  | #define _USE_MATH_DEFINES
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include "testsourcethread.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "dsp/samplesinkfifo.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define TESTSOURCE_BLOCKSIZE 16384
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  | MESSAGE_CLASS_DEFINITION(TestSourceThread::MsgStartStop, Message) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | TestSourceThread::TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent) : | 
					
						
							|  |  |  | 	QThread(parent), | 
					
						
							|  |  |  | 	m_running(false), | 
					
						
							|  |  |  |     m_buf(0), | 
					
						
							|  |  |  |     m_bufsize(0), | 
					
						
							|  |  |  |     m_chunksize(0), | 
					
						
							|  |  |  | 	m_convertBuffer(TESTSOURCE_BLOCKSIZE), | 
					
						
							|  |  |  | 	m_sampleFifo(sampleFifo), | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  | 	m_frequencyShift(0), | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  | 	m_toneFrequency(440), | 
					
						
							|  |  |  | 	m_modulation(TestSourceSettings::ModulationNone), | 
					
						
							|  |  |  | 	m_amModulation(0.5f), | 
					
						
							|  |  |  | 	m_fmDeviationUnit(0.0f), | 
					
						
							|  |  |  | 	m_fmPhasor(0.0f), | 
					
						
							| 
									
										
										
										
											2018-10-23 14:34:56 +02:00
										 |  |  |     m_pulseWidth(150), | 
					
						
							|  |  |  |     m_pulseSampleCount(0), | 
					
						
							|  |  |  |     m_pulsePatternCount(0), | 
					
						
							|  |  |  |     m_pulsePatternCycle(8), | 
					
						
							|  |  |  |     m_pulsePatternPlaces(3), | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | 	m_samplerate(48000), | 
					
						
							|  |  |  | 	m_log2Decim(4), | 
					
						
							|  |  |  | 	m_fcPos(0), | 
					
						
							|  |  |  | 	m_bitSizeIndex(0), | 
					
						
							|  |  |  | 	m_bitShift(8), | 
					
						
							|  |  |  | 	m_amplitudeBits(127), | 
					
						
							| 
									
										
										
										
											2018-01-14 11:58:36 +01:00
										 |  |  | 	m_dcBias(0.0f), | 
					
						
							|  |  |  | 	m_iBias(0.0f), | 
					
						
							|  |  |  | 	m_qBias(0.0f), | 
					
						
							| 
									
										
										
										
											2018-01-31 22:34:55 +01:00
										 |  |  | 	m_phaseImbalance(0.0f), | 
					
						
							| 
									
										
										
										
											2018-01-14 11:58:36 +01:00
										 |  |  | 	m_amplitudeBitsDC(0), | 
					
						
							|  |  |  | 	m_amplitudeBitsI(127), | 
					
						
							|  |  |  | 	m_amplitudeBitsQ(127), | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | 	m_frequency(435*1000), | 
					
						
							|  |  |  | 	m_fcPosShift(0), | 
					
						
							|  |  |  |     m_throttlems(TESTSOURCE_THROTTLE_MS), | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |     m_throttleToggle(false), | 
					
						
							| 
									
										
										
										
											2019-05-21 21:18:49 +02:00
										 |  |  |     m_mutex(QMutex::Recursive), | 
					
						
							|  |  |  |     m_histoCounter(0) | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  |     connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TestSourceThread::~TestSourceThread() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::startWork() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-21 23:33:36 +02:00
										 |  |  |     m_timer.setTimerType(Qt::PreciseTimer); | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  |     connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); | 
					
						
							|  |  |  |     m_timer.start(50); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | 	m_startWaitMutex.lock(); | 
					
						
							|  |  |  | 	m_elapsedTimer.start(); | 
					
						
							|  |  |  | 	start(); | 
					
						
							|  |  |  | 	while(!m_running) | 
					
						
							|  |  |  | 		m_startWaiter.wait(&m_startWaitMutex, 100); | 
					
						
							|  |  |  | 	m_startWaitMutex.unlock(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::stopWork() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_running = false; | 
					
						
							|  |  |  | 	wait(); | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  |     m_timer.stop(); | 
					
						
							|  |  |  |     disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setSamplerate(int samplerate) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |     QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | 	m_samplerate = samplerate; | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |     m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); | 
					
						
							|  |  |  |     m_throttleToggle = !m_throttleToggle; | 
					
						
							|  |  |  | 	m_nco.setFreq(m_frequencyShift, m_samplerate); | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  | 	m_toneNco.setFreq(m_toneFrequency, m_samplerate); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setLog2Decimation(unsigned int log2_decim) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_log2Decim = log2_decim; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setFcPos(int fcPos) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_fcPos = fcPos; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setBitSize(quint32 bitSizeIndex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (bitSizeIndex) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case 0: | 
					
						
							|  |  |  |         m_bitShift = 7; | 
					
						
							|  |  |  |         m_bitSizeIndex = 0; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 1: | 
					
						
							|  |  |  |         m_bitShift = 11; | 
					
						
							|  |  |  |         m_bitSizeIndex = 1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 2: | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         m_bitShift = 15; | 
					
						
							|  |  |  |         m_bitSizeIndex = 2; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setAmplitudeBits(int32_t amplitudeBits) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_amplitudeBits = amplitudeBits; | 
					
						
							| 
									
										
										
										
											2018-01-14 11:58:36 +01:00
										 |  |  |     m_amplitudeBitsDC = m_dcBias * amplitudeBits; | 
					
						
							|  |  |  |     m_amplitudeBitsI = (1.0f + m_iBias) * amplitudeBits; | 
					
						
							|  |  |  |     m_amplitudeBitsQ = (1.0f + m_qBias) * amplitudeBits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setDCFactor(float dcFactor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_dcBias = dcFactor; | 
					
						
							|  |  |  |     m_amplitudeBitsDC = m_dcBias * m_amplitudeBits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setIFactor(float iFactor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_iBias = iFactor; | 
					
						
							|  |  |  |     m_amplitudeBitsI = (1.0f + m_iBias) * m_amplitudeBits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setQFactor(float iFactor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_qBias = iFactor; | 
					
						
							|  |  |  |     m_amplitudeBitsQ = (1.0f + m_qBias) * m_amplitudeBits; | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-31 22:34:55 +01:00
										 |  |  | void TestSourceThread::setPhaseImbalance(float phaseImbalance) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_phaseImbalance = phaseImbalance; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | void TestSourceThread::setFrequencyShift(int shift) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_nco.setFreq(shift, m_samplerate); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  | void TestSourceThread::setToneFrequency(int toneFrequency) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_toneNco.setFreq(toneFrequency, m_samplerate); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setModulation(TestSourceSettings::Modulation modulation) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_modulation = modulation; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setAMModulation(float amModulation) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setFMDeviation(float deviation) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     float fmDeviationUnit = deviation / (float) m_samplerate; | 
					
						
							| 
									
										
										
										
											2018-02-11 18:58:28 +01:00
										 |  |  |     m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 0.5f ? 0.5f : fmDeviationUnit; | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  |     qDebug("TestSourceThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  | void TestSourceThread::startStop(bool start) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     MsgStartStop *msg = MsgStartStop::create(start); | 
					
						
							|  |  |  |     m_inputMessageQueue.push(msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | void TestSourceThread::run() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_running = true; | 
					
						
							|  |  |  |     m_startWaiter.wakeAll(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (m_running) // actual work is in the tick() function
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         sleep(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_running = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setBuffers(quint32 chunksize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (chunksize > m_bufsize) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_bufsize = chunksize; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (m_buf == 0) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |             qDebug() << "TestSourceThread::setBuffer: Allocate buffer:    " | 
					
						
							|  |  |  |                     << " size: " << m_bufsize << " bytes" | 
					
						
							|  |  |  |                     << " #samples: " << (m_bufsize/4); | 
					
						
							|  |  |  |             m_buf = (qint16*) malloc(m_bufsize); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |             qDebug() << "TestSourceThread::setBuffer: Re-allocate buffer: " | 
					
						
							|  |  |  |                     << " size: " << m_bufsize << " bytes" | 
					
						
							|  |  |  |                     << " #samples: " << (m_bufsize/4); | 
					
						
							|  |  |  |             free(m_buf); | 
					
						
							|  |  |  |             m_buf = (qint16*) malloc(m_bufsize); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         m_convertBuffer.resize(chunksize/4); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::generate(quint32 chunksize) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |     int n = chunksize / 2; | 
					
						
							|  |  |  |     setBuffers(chunksize); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |     for (int i = 0; i < n-1;) | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  |         switch (m_modulation) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case TestSourceSettings::ModulationAM: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             Complex c = m_nco.nextIQ(); | 
					
						
							|  |  |  |             Real t, re, im; | 
					
						
							|  |  |  |             pullAF(t); | 
					
						
							|  |  |  |             t = (t*m_amModulation + 1.0f)*0.5f; | 
					
						
							|  |  |  |             re = c.real()*t; | 
					
						
							|  |  |  |             im = c.imag()*t + m_phaseImbalance*re; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |         case TestSourceSettings::ModulationFM: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             Complex c = m_nco.nextIQ(); | 
					
						
							|  |  |  |             Real t, re, im; | 
					
						
							|  |  |  |             pullAF(t); | 
					
						
							|  |  |  |             m_fmPhasor += m_fmDeviationUnit * t; | 
					
						
							|  |  |  |             m_fmPhasor = m_fmPhasor < -1.0f ? -m_fmPhasor - 1.0f  : m_fmPhasor > 1.0f ? m_fmPhasor - 1.0f : m_fmPhasor; | 
					
						
							|  |  |  |             re =  c.real()*cos(m_fmPhasor*M_PI) - c.imag()*sin(m_fmPhasor*M_PI); | 
					
						
							|  |  |  |             im = (c.real()*sin(m_fmPhasor*M_PI) + c.imag()*cos(m_fmPhasor*M_PI)) + m_phaseImbalance*re; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-10-23 14:34:56 +02:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2018-10-24 00:10:12 +02:00
										 |  |  |         case TestSourceSettings::ModulationPattern0: // binary pattern
 | 
					
						
							| 
									
										
										
										
											2018-10-23 14:34:56 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             if (m_pulseSampleCount < m_pulseWidth) // sync pattern: 0
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_buf[i++] = m_amplitudeBitsDC; | 
					
						
							|  |  |  |                 m_buf[i++] = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (m_pulseSampleCount < 2*m_pulseWidth) // sync pattern: 1
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_buf[i++] = (int16_t) (m_amplitudeBitsI + m_amplitudeBitsDC); | 
					
						
							|  |  |  |                 m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (m_pulseSampleCount < 3*m_pulseWidth) // sync pattern: 0
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_buf[i++] = m_amplitudeBitsDC; | 
					
						
							|  |  |  |                 m_buf[i++] = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (m_pulseSampleCount < (3+m_pulsePatternPlaces)*m_pulseWidth) // binary pattern
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 uint32_t patPulseSampleCount = m_pulseSampleCount - 3*m_pulseWidth; | 
					
						
							|  |  |  |                 uint32_t patPulseIndex = patPulseSampleCount / m_pulseWidth; | 
					
						
							| 
									
										
										
										
											2018-10-24 00:10:12 +02:00
										 |  |  |                 float patFigure = (m_pulsePatternCount & (1<<patPulseIndex)) != 0 ? 0.3 : 0.0; // make binary pattern ~-10dB vs sync pattern
 | 
					
						
							| 
									
										
										
										
											2018-10-23 14:34:56 +02:00
										 |  |  |                 m_buf[i++] = (int16_t) (patFigure * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; | 
					
						
							|  |  |  |                 m_buf[i++] = (int16_t) (patFigure * (float) m_phaseImbalance * m_amplitudeBitsQ); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_pulseSampleCount < (4+m_pulsePatternPlaces)*m_pulseWidth - 1) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_pulseSampleCount++; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (m_pulsePatternCount < m_pulsePatternCycle - 1) { | 
					
						
							|  |  |  |                     m_pulsePatternCount++; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     m_pulsePatternCount = 0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 m_pulseSampleCount = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2018-10-24 00:10:12 +02:00
										 |  |  |         case TestSourceSettings::ModulationPattern1: // sawtooth pattern
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             Real re, im; | 
					
						
							|  |  |  |             re = (float) (m_pulseWidth - m_pulseSampleCount) / (float) m_pulseWidth; | 
					
						
							|  |  |  |             im = m_phaseImbalance*re; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_pulseSampleCount < m_pulseWidth - 1) { | 
					
						
							|  |  |  |                 m_pulseSampleCount++; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 m_pulseSampleCount = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2018-10-25 13:53:58 +02:00
										 |  |  |         case TestSourceSettings::ModulationPattern2: // 50% duty cycle square pattern
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_pulseSampleCount < m_pulseWidth) // 1
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 m_buf[i++] = (int16_t) (m_amplitudeBitsI + m_amplitudeBitsDC); | 
					
						
							|  |  |  |                 m_buf[i++] = (int16_t) (m_phaseImbalance * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  |             } else { // 0
 | 
					
						
							|  |  |  |                 m_buf[i++] = m_amplitudeBitsDC; | 
					
						
							|  |  |  |                 m_buf[i++] = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_pulseSampleCount < 2*m_pulseWidth - 1) { | 
					
						
							|  |  |  |                 m_pulseSampleCount++; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 m_pulseSampleCount = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  |         case TestSourceSettings::ModulationNone: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             Complex c = m_nco.nextIQ(m_phaseImbalance); | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC; | 
					
						
							|  |  |  |             m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     callback(m_buf, n); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-11 13:48:17 +01:00
										 |  |  | void TestSourceThread::pullAF(Real& afSample) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     afSample = m_toneNco.next(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | //  call appropriate conversion (decimation) routine depending on the number of sample bits
 | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  | void TestSourceThread::callback(const qint16* buf, qint32 len) | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	SampleVector::iterator it = m_convertBuffer.begin(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (m_bitSizeIndex) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	case 0: // 8 bit samples
 | 
					
						
							|  |  |  | 	    convert_8(&it, buf, len); | 
					
						
							|  |  |  | 	    break; | 
					
						
							|  |  |  |     case 1: // 12 bit samples
 | 
					
						
							|  |  |  |         convert_12(&it, buf, len); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 2: // 16 bit samples
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         convert_16(&it, buf, len); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m_sampleFifo->write(m_convertBuffer.begin(), it); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::tick() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_running) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qint64 throttlems = m_elapsedTimer.restart(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 21:18:49 +02:00
										 |  |  |         std::map<int,int>::iterator it; | 
					
						
							|  |  |  |         it = m_timerHistogram.find(throttlems); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (it == m_timerHistogram.end()) { | 
					
						
							|  |  |  |             m_timerHistogram[throttlems] = 1; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             it->second++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (m_histoCounter < 49) { | 
					
						
							|  |  |  |             m_histoCounter++; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2019-12-12 18:23:59 +01:00
										 |  |  |             // qDebug("TestSourceThread::tick: -----------");
 | 
					
						
							|  |  |  |             // for (std::map<int,int>::iterator it = m_timerHistogram.begin(); it != m_timerHistogram.end(); ++it) {
 | 
					
						
							|  |  |  |             //     qDebug("TestSourceThread::tick: %d: %d", it->first, it->second);
 | 
					
						
							|  |  |  |             // }
 | 
					
						
							| 
									
										
										
										
											2019-05-21 21:18:49 +02:00
										 |  |  |             m_histoCounter = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  |         if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems)) | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-01-14 04:43:24 +01:00
										 |  |  |             QMutexLocker mutexLocker(&m_mutex); | 
					
						
							| 
									
										
										
										
											2018-01-14 02:25:24 +01:00
										 |  |  |             m_throttlems = throttlems; | 
					
						
							|  |  |  |             m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); | 
					
						
							|  |  |  |             m_throttleToggle = !m_throttleToggle; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         generate(m_chunksize); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 14:29:14 +02:00
										 |  |  | void TestSourceThread::handleInputMessages() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Message* message; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while ((message = m_inputMessageQueue.pop()) != 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (MsgStartStop::match(*message)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             MsgStartStop* notif = (MsgStartStop*) message; | 
					
						
							|  |  |  |             qDebug("TestSourceThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (notif->getStartStop()) { | 
					
						
							|  |  |  |                 startWork(); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 stopWork(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             delete message; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-24 00:10:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setPattern0() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_pulseWidth = 150; | 
					
						
							|  |  |  |     m_pulseSampleCount = 0; | 
					
						
							|  |  |  |     m_pulsePatternCount = 0; | 
					
						
							|  |  |  |     m_pulsePatternCycle = 8; | 
					
						
							|  |  |  |     m_pulsePatternPlaces = 3; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setPattern1() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_pulseWidth = 1000; | 
					
						
							|  |  |  |     m_pulseSampleCount = 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-25 13:53:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void TestSourceThread::setPattern2() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_pulseWidth = 1000; | 
					
						
							|  |  |  |     m_pulseSampleCount = 0; | 
					
						
							|  |  |  | } |