mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 04:50:29 -04:00 
			
		
		
		
	Removed MIMO plugins and related code
This commit is contained in:
		
							parent
							
								
									bbba942eba
								
							
						
					
					
						commit
						e33ed72059
					
				| @ -34,6 +34,5 @@ endif() | ||||
| 
 | ||||
| add_subdirectory(channelrx) | ||||
| add_subdirectory(channeltx) | ||||
| add_subdirectory(samplemimo) | ||||
| add_subdirectory(samplesource) | ||||
| add_subdirectory(samplesink) | ||||
|  | ||||
| @ -1,3 +0,0 @@ | ||||
| project(samplemimo) | ||||
| 
 | ||||
| add_subdirectory(testmi) | ||||
| @ -1,56 +0,0 @@ | ||||
| project(testmi) | ||||
| 
 | ||||
| set(testmi_SOURCES | ||||
| 	testmi.cpp | ||||
| 	testmiplugin.cpp | ||||
| 	testmithread.cpp | ||||
|     testmisettings.cpp | ||||
|     testmiwebapiadapter.cpp | ||||
| ) | ||||
| 
 | ||||
| set(testmi_HEADERS | ||||
| 	testmi.h | ||||
| 	testmiplugin.h | ||||
| 	testmithread.h | ||||
|     testmisettings.h | ||||
|     testmiwebapiadapter.h | ||||
| ) | ||||
| 
 | ||||
| include_directories( | ||||
| 	${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client | ||||
| ) | ||||
| 
 | ||||
| if (NOT SERVER_MODE) | ||||
|     set (testmi_SOURCES | ||||
|         ${testmi_SOURCES} | ||||
|         testmigui.cpp | ||||
|         testmigui.ui | ||||
|     ) | ||||
|     set(testmi_HEADERS | ||||
|         ${testmi_HEADERS} | ||||
|         testsourcegui.h | ||||
|     ) | ||||
|     set(TARGET_NAME mimotestmi) | ||||
|     set(TARGET_LIB "Qt5::Widgets") | ||||
|     set(TARGET_LIB_GUI "sdrgui") | ||||
|     set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) | ||||
| else() | ||||
|     set(TARGET_NAME mimotestmisrv) | ||||
|     set(TARGET_LIB "") | ||||
|     set(TARGET_LIB_GUI "") | ||||
|     set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) | ||||
| endif() | ||||
| 
 | ||||
| add_library(${TARGET_NAME} SHARED | ||||
| 	${testmi_SOURCES} | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(${TARGET_NAME} | ||||
|     Qt5::Core | ||||
|     ${TARGET_LIB} | ||||
| 	sdrbase | ||||
| 	${TARGET_LIB_GUI} | ||||
|     swagger | ||||
| ) | ||||
| 
 | ||||
| install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) | ||||
| @ -1,134 +0,0 @@ | ||||
| <h1>Test source input plugin</h1> | ||||
| 
 | ||||
| <h2>Introduction</h2> | ||||
| 
 | ||||
| This input sample source plugin is an internal continuous wave generator that can be used to carry out test of software internals. | ||||
| 
 | ||||
| <h2>Build</h2> | ||||
| 
 | ||||
| The plugin is present in the core of the software and thus is always present in the list of sources. | ||||
| 
 | ||||
| <h2>Interface</h2> | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| <h3>1: Common stream parameters</h3> | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| <h4>1.1: Frequency</h4> | ||||
| 
 | ||||
| This is the center frequency of reception in kHz. | ||||
| 
 | ||||
| <h4>1.2: Start/Stop</h4> | ||||
| 
 | ||||
| Device start / stop button. | ||||
| 
 | ||||
|   - Blue triangle icon: device is ready and can be started | ||||
|   - Green square icon: device is running and can be stopped | ||||
|   - Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again. | ||||
| 
 | ||||
| <h4>1.3: Record</h4> | ||||
| 
 | ||||
| Record baseband I/Q stream toggle button | ||||
| 
 | ||||
| <h4>1.4: Stream sample rate</h4> | ||||
| 
 | ||||
| Baseband I/Q sample rate in kS/s. This is the device to host sample rate (3) divided by the decimation factor (4). | ||||
| 
 | ||||
| <h3>2: Various options</h3> | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| <h4>2.1: Auto corrections</h4> | ||||
| 
 | ||||
| This combo box control the local DSP auto correction options: | ||||
| 
 | ||||
|   - **None**: no correction | ||||
|   - **DC**: auto remove DC component | ||||
|   - **DC+IQ**: auto remove DC component and correct I/Q balance. | ||||
| 
 | ||||
| <h4>2.2: Decimation factor</h4> | ||||
| 
 | ||||
| The I/Q stream from the generator is downsampled by a power of two before being sent to the passband. Possible values are increasing powers of two: 1 (no decimation), 2, 4, 8, 16, 32. This exercises the decimation chain. | ||||
| 
 | ||||
| This exercises the decimation chain. | ||||
| 
 | ||||
| <h4>2.3: Baseband center frequency position relative the center frequency</h4> | ||||
| 
 | ||||
|   - **Cen**: the decimation operation takes place around the center frequency Fs | ||||
|   - **Inf**: the decimation operation takes place around Fs - Fc. | ||||
|   - **Sup**: the decimation operation takes place around Fs + Fc. | ||||
| 
 | ||||
| With SR as the sample rate before decimation Fc is calculated as: | ||||
| 
 | ||||
|   - if decimation n is 4 or lower:  Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband. | ||||
|   - if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband. | ||||
| 
 | ||||
| <h3>2.4: Sample size</h3> | ||||
| 
 | ||||
| This is the sample size in number of bits. It corresponds to the actual sample size used by the devices supported: | ||||
| 
 | ||||
|   - **8**: RTL-SDR, HackRF | ||||
|   - **12**: Airspy, BladeRF, LimeSDR, PlutoSDR, SDRplay | ||||
|   - **16**: Airspy HF+, FCD Pro, FCD Pro+ | ||||
| 
 | ||||
| <h3>3: Sample rate</h3> | ||||
| 
 | ||||
| This controls the generator sample rate in samples per second. | ||||
| 
 | ||||
| <h3>4: Modulation</h4> | ||||
| 
 | ||||
|   - **No**: No modulation | ||||
|   - **AM**: Amplitude modulation (AM) | ||||
|   - **FM**: Frequency modulation (FM) | ||||
|   - **P0**: Pattern 0 is a binary pattern | ||||
|     - Pulse width: 150 samples | ||||
|     - Sync pattern: 010 at full amplitude | ||||
|     - Binary pattern LSB first on 3 bits from 0 to 7 at 0.3 amplitude | ||||
|   - **P1**: Pattern 1 is a sawtooth pattern | ||||
|     - Pulse width: 1000 samples | ||||
|     - Starts at full amplitude then amplitude decreases linearly down to zero | ||||
|   - **P2**: Pattern 2 is a 50% duty cycle square pattern | ||||
|     - Pulse width: 1000 samples | ||||
|     - Starts with a full amplitude pulse then down to zero for the duration of one pulse | ||||
| 
 | ||||
| <h3>5: Modulating tone frequency</h3> | ||||
| 
 | ||||
| This controls the modulating tone frequency in kHz in 10 Hz steps. | ||||
| 
 | ||||
| <h3>6: Carrier shift from center frequency</h3> | ||||
| 
 | ||||
| Use this control to set the offset of the carrier from the center frequency of reception. | ||||
| 
 | ||||
| <h3>7: AM modulation factor</h3> | ||||
| 
 | ||||
| This controls the AM modulation factor from 0 to 99% | ||||
| 
 | ||||
| <h3>8: FM deviation</h3> | ||||
| 
 | ||||
| This controls the frequency modulation deviation in kHz in 100 Hz steps. It cannot exceed the sample rate. | ||||
| 
 | ||||
| <h3>9: Amplitude coarse control</h3> | ||||
| 
 | ||||
| This slider controls the number of amplitude bits by steps of 100 bits. The total number of amplitude bits appear on the right. | ||||
| 
 | ||||
| <h3>10: Amplitude fine control</h3> | ||||
| 
 | ||||
| This slider controls the number of amplitude bits by steps of 1 bit. The signal power in dB relative to the maximum power (full bit range) appear on the right. | ||||
| 
 | ||||
| <h3>11: DC bias</h3> | ||||
| 
 | ||||
| Use this slider to give a DC component in percentage of maximum amplitude. | ||||
| 
 | ||||
| <h3>12: I bias</h3> | ||||
| 
 | ||||
| Use this slider to give an in-phase (I) bias in percentage of maximum amplitude. | ||||
| 
 | ||||
| <h3>13: Q bias</h3> | ||||
| 
 | ||||
| Use this slider to give an quadrature-phase (Q) bias in percentage of maximum amplitude. | ||||
| 
 | ||||
| <h3>14: Phase imbalance</h3> | ||||
| 
 | ||||
| Use this slider to introduce a phase imbalance in percentage of full period (continuous wave) or percentage of I signal injected in Q (AM, FM). | ||||
| @ -1,906 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <QDebug> | ||||
| #include <QNetworkReply> | ||||
| #include <QNetworkAccessManager> | ||||
| #include <QBuffer> | ||||
| 
 | ||||
| #include "SWGDeviceSettings.h" | ||||
| #include "SWGDeviceState.h" | ||||
| #include "SWGTestMISettings.h" | ||||
| 
 | ||||
| #include "device/deviceapi.h" | ||||
| #include "dsp/dspcommands.h" | ||||
| #include "dsp/dspengine.h" | ||||
| #include "dsp/dspdevicemimoengine.h" | ||||
| #include "dsp/devicesamplesource.h" | ||||
| #include "dsp/filerecord.h" | ||||
| 
 | ||||
| #include "testmithread.h" | ||||
| #include "testmi.h" | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(TestMI::MsgConfigureTestSource, Message) | ||||
| MESSAGE_CLASS_DEFINITION(TestMI::MsgFileRecord, Message) | ||||
| MESSAGE_CLASS_DEFINITION(TestMI::MsgStartStop, Message) | ||||
| 
 | ||||
| 
 | ||||
| TestMI::TestMI(DeviceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
| 	m_settings(), | ||||
| 	m_deviceDescription("TestMI"), | ||||
| 	m_running(false), | ||||
| 	m_masterTimer(deviceAPI->getMasterTimer()) | ||||
| { | ||||
|     m_mimoType = MIMOAsynchronous; | ||||
|     m_sampleMIFifo.init(2, 96000 * 4); | ||||
|     //m_sampleSinkVectors.resize(2);
 | ||||
|     m_deviceAPI->setNbSourceStreams(2); | ||||
|     m_networkManager = new QNetworkAccessManager(); | ||||
|     connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); | ||||
| } | ||||
| 
 | ||||
| TestMI::~TestMI() | ||||
| { | ||||
|     disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); | ||||
|     delete m_networkManager; | ||||
| 
 | ||||
|     if (m_running) { | ||||
|         stopRx(); | ||||
|     } | ||||
| 
 | ||||
|     std::vector<FileRecord*>::iterator it = m_fileSinks.begin(); | ||||
|     int istream = 0; | ||||
| 
 | ||||
|     for (; it != m_fileSinks.end(); ++it, istream++) | ||||
|     { | ||||
|         m_deviceAPI->removeAncillarySink(*it, istream); | ||||
|         delete *it; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMI::destroy() | ||||
| { | ||||
|     delete this; | ||||
| } | ||||
| 
 | ||||
| void TestMI::init() | ||||
| { | ||||
|     m_fileSinks.push_back(new FileRecord(QString("test_0_%1.sdriq").arg(m_deviceAPI->getDeviceUID()))); | ||||
|     m_fileSinks.push_back(new FileRecord(QString("test_1_%1.sdriq").arg(m_deviceAPI->getDeviceUID()))); | ||||
|     m_deviceAPI->addAncillarySink(m_fileSinks[0], 0); | ||||
|     m_deviceAPI->addAncillarySink(m_fileSinks[1], 1); | ||||
| 
 | ||||
|     applySettings(m_settings, true); | ||||
| } | ||||
| 
 | ||||
| bool TestMI::startRx() | ||||
| { | ||||
|     qDebug("TestMI::startRx"); | ||||
| 	QMutexLocker mutexLocker(&m_mutex); | ||||
| 
 | ||||
|     if (m_running) { | ||||
|         stopRx(); | ||||
|     } | ||||
| 
 | ||||
|     m_testSourceThreads.push_back(new TestMIThread(&m_sampleMIFifo, 0)); | ||||
| 	m_testSourceThreads.back()->setSamplerate(m_settings.m_streams[0].m_sampleRate); | ||||
| 	m_testSourceThreads.back()->startStop(true); | ||||
| 
 | ||||
|     m_testSourceThreads.push_back(new TestMIThread(&m_sampleMIFifo, 1)); | ||||
| 	m_testSourceThreads.back()->setSamplerate(m_settings.m_streams[1].m_sampleRate); | ||||
| 	m_testSourceThreads.back()->startStop(true); | ||||
| 
 | ||||
| 	mutexLocker.unlock(); | ||||
| 
 | ||||
| 	applySettings(m_settings, true); | ||||
| 	m_running = true; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool TestMI::startTx() | ||||
| { | ||||
|     qDebug("TestMI::startTx"); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void TestMI::stopRx() | ||||
| { | ||||
|     qDebug("TestMI::stopRx"); | ||||
| 	QMutexLocker mutexLocker(&m_mutex); | ||||
| 
 | ||||
|     std::vector<TestMIThread*>::iterator it = m_testSourceThreads.begin(); | ||||
| 
 | ||||
|     for (; it != m_testSourceThreads.end(); ++it) | ||||
|     { | ||||
|         (*it)->startStop(false); | ||||
|         (*it)->deleteLater(); | ||||
|     } | ||||
| 
 | ||||
|     m_testSourceThreads.clear(); | ||||
| 	m_running = false; | ||||
| } | ||||
| 
 | ||||
| void TestMI::stopTx() | ||||
| { | ||||
|     qDebug("TestMI::stopTx"); | ||||
| } | ||||
| 
 | ||||
| QByteArray TestMI::serialize() const | ||||
| { | ||||
|     return m_settings.serialize(); | ||||
| } | ||||
| 
 | ||||
| bool TestMI::deserialize(const QByteArray& data) | ||||
| { | ||||
|     bool success = true; | ||||
| 
 | ||||
|     if (!m_settings.deserialize(data)) | ||||
|     { | ||||
|         m_settings.resetToDefaults(); | ||||
|         success = false; | ||||
|     } | ||||
| 
 | ||||
|     MsgConfigureTestSource* message = MsgConfigureTestSource::create(m_settings, true); | ||||
|     m_inputMessageQueue.push(message); | ||||
| 
 | ||||
|     if (m_guiMessageQueue) | ||||
|     { | ||||
|         MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(m_settings, true); | ||||
|         m_guiMessageQueue->push(messageToGUI); | ||||
|     } | ||||
| 
 | ||||
|     return success; | ||||
| } | ||||
| 
 | ||||
| const QString& TestMI::getDeviceDescription() const | ||||
| { | ||||
| 	return m_deviceDescription; | ||||
| } | ||||
| 
 | ||||
| int TestMI::getSourceSampleRate(int index) const | ||||
| { | ||||
|     if (index < (int) m_settings.m_streams.size()) { | ||||
| 	    return m_settings.m_streams[index].m_sampleRate/(1<<m_settings.m_streams[index].m_log2Decim); | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| quint64 TestMI::getSourceCenterFrequency(int index) const | ||||
| { | ||||
|     if (index < (int) m_settings.m_streams.size()) { | ||||
|     	return m_settings.m_streams[index].m_centerFrequency; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMI::setSourceCenterFrequency(qint64 centerFrequency, int index) | ||||
| { | ||||
|     TestMISettings settings = m_settings; // note: calls copy constructor
 | ||||
| 
 | ||||
|     if (index < (int) settings.m_streams.size()) | ||||
|     { | ||||
|         settings.m_streams[index].m_centerFrequency = centerFrequency; | ||||
| 
 | ||||
|         MsgConfigureTestSource* message = MsgConfigureTestSource::create(settings, false); | ||||
|         m_inputMessageQueue.push(message); | ||||
| 
 | ||||
|         if (m_guiMessageQueue) | ||||
|         { | ||||
|             MsgConfigureTestSource* messageToGUI = MsgConfigureTestSource::create(settings, false); | ||||
|             m_guiMessageQueue->push(messageToGUI); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool TestMI::handleMessage(const Message& message) | ||||
| { | ||||
|     if (MsgConfigureTestSource::match(message)) | ||||
|     { | ||||
|         MsgConfigureTestSource& conf = (MsgConfigureTestSource&) message; | ||||
|         qDebug() << "TestMI::handleMessage: MsgConfigureTestSource"; | ||||
| 
 | ||||
|         bool success = applySettings(conf.getSettings(), conf.getForce()); | ||||
| 
 | ||||
|         if (!success) | ||||
|         { | ||||
|             qDebug("TestMI::handleMessage: config error"); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|     else if (MsgFileRecord::match(message)) | ||||
|     { | ||||
|         MsgFileRecord& conf = (MsgFileRecord&) message; | ||||
|         qDebug() << "TestMI::handleMessage: MsgFileRecord: " << conf.getStartStop(); | ||||
|         int istream = conf.getStreamIndex(); | ||||
| 
 | ||||
|         if (conf.getStartStop()) | ||||
|         { | ||||
|             if (m_settings.m_fileRecordName.size() != 0) { | ||||
|                 m_fileSinks[istream]->setFileName(m_settings.m_fileRecordName + "_0.sdriq"); | ||||
|             } else { | ||||
|                 m_fileSinks[istream]->genUniqueFileName(m_deviceAPI->getDeviceUID(), istream); | ||||
|             } | ||||
| 
 | ||||
|             m_fileSinks[istream]->startRecording(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_fileSinks[istream]->stopRecording(); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|     else if (MsgStartStop::match(message)) | ||||
|     { | ||||
|         MsgStartStop& cmd = (MsgStartStop&) message; | ||||
|         qDebug() << "TestMI::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); | ||||
| 
 | ||||
|         if (cmd.getStartStop()) | ||||
|         { | ||||
|             if (m_deviceAPI->initDeviceEngine()) | ||||
|             { | ||||
|                 m_deviceAPI->startDeviceEngine(); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_deviceAPI->stopDeviceEngine(); | ||||
|         } | ||||
| 
 | ||||
|         if (m_settings.m_useReverseAPI) { | ||||
|             webapiReverseSendStartStop(cmd.getStartStop()); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool TestMI::applySettings(const TestMISettings& settings, bool force) | ||||
| { | ||||
|     DeviceSettingsKeys deviceSettingsKeys; | ||||
| 
 | ||||
|     qDebug() << "TestMI::applySettings: common: " | ||||
|         << " m_fileRecordName: " << settings.m_fileRecordName | ||||
|         << " m_useReverseAPI: " << settings.m_useReverseAPI | ||||
|         << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress | ||||
|         << " m_reverseAPIPort: " << settings.m_reverseAPIPort | ||||
|         << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex; | ||||
| 
 | ||||
|     for (unsigned int istream = 0; (istream < m_settings.m_streams.size()) && (istream < settings.m_streams.size()); istream++) | ||||
|     { | ||||
|         qDebug() << "TestMI::applySettings: stream #" << istream << ": " | ||||
|             << " m_centerFrequency: " << settings.m_streams[istream].m_centerFrequency | ||||
|             << " m_frequencyShift: " << settings.m_streams[istream].m_frequencyShift | ||||
|             << " m_sampleRate: " << settings.m_streams[istream].m_sampleRate | ||||
|             << " m_log2Decim: " << settings.m_streams[istream].m_log2Decim | ||||
|             << " m_fcPos: " << settings.m_streams[istream].m_fcPos | ||||
|             << " m_amplitudeBits: " << settings.m_streams[istream].m_amplitudeBits | ||||
|             << " m_sampleSizeIndex: " << settings.m_streams[istream].m_sampleSizeIndex | ||||
|             << " m_autoCorrOptions: " << settings.m_streams[istream].m_autoCorrOptions | ||||
|             << " m_dcFactor: " << settings.m_streams[istream].m_dcFactor | ||||
|             << " m_iFactor: " << settings.m_streams[istream].m_iFactor | ||||
|             << " m_qFactor: " << settings.m_streams[istream].m_qFactor | ||||
|             << " m_phaseImbalance: " << settings.m_streams[istream].m_phaseImbalance | ||||
|             << " m_modulation: " << settings.m_streams[istream].m_modulation | ||||
|             << " m_amModulation: " << settings.m_streams[istream].m_amModulation | ||||
|             << " m_fmDeviation: " << settings.m_streams[istream].m_fmDeviation | ||||
|             << " m_modulationTone: " << settings.m_streams[istream].m_modulationTone; | ||||
| 
 | ||||
|         deviceSettingsKeys.m_streamsSettingsKeys.push_back(QList<QString>()); | ||||
|         QList<QString>& reverseAPIKeys = deviceSettingsKeys.m_streamsSettingsKeys.back(); | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_autoCorrOptions != settings.m_streams[istream].m_autoCorrOptions) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("autoCorrOptions"); | ||||
| 
 | ||||
|             switch(settings.m_streams[istream].m_autoCorrOptions) | ||||
|             { | ||||
|             case TestMIStreamSettings::AutoCorrDC: | ||||
|                 m_deviceAPI->configureCorrections(true, false, istream); | ||||
|                 break; | ||||
|             case TestMIStreamSettings::AutoCorrDCAndIQ: | ||||
|                 m_deviceAPI->configureCorrections(true, true, istream); | ||||
|                 break; | ||||
|             case TestMIStreamSettings::AutoCorrNone: | ||||
|             default: | ||||
|                 m_deviceAPI->configureCorrections(false, false, istream); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_sampleRate != settings.m_streams[istream].m_sampleRate) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("sampleRate"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) | ||||
|             { | ||||
|                 m_testSourceThreads[istream]->setSamplerate(settings.m_streams[istream].m_sampleRate); | ||||
|                 qDebug("TestMI::applySettings: thread on stream: %u sample rate set to %d", | ||||
|                     istream, settings.m_streams[istream].m_sampleRate); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_log2Decim != settings.m_streams[istream].m_log2Decim) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("log2Decim"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) | ||||
|             { | ||||
|                 m_testSourceThreads[istream]->setLog2Decimation(settings.m_streams[istream].m_log2Decim); | ||||
|                 qDebug("TestMI::applySettings: thread on stream: %u set decimation to %d", | ||||
|                     istream, (1<<settings.m_streams[istream].m_log2Decim)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_centerFrequency != settings.m_streams[istream].m_centerFrequency) | ||||
|             || (m_settings.m_streams[istream].m_fcPos != settings.m_streams[istream].m_fcPos) | ||||
|             || (m_settings.m_streams[istream].m_frequencyShift != settings.m_streams[istream].m_frequencyShift) | ||||
|             || (m_settings.m_streams[istream].m_sampleRate != settings.m_streams[istream].m_sampleRate) | ||||
|             || (m_settings.m_streams[istream].m_log2Decim != settings.m_streams[istream].m_log2Decim) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("centerFrequency"); | ||||
|             reverseAPIKeys.append("fcPos"); | ||||
|             reverseAPIKeys.append("frequencyShift"); | ||||
| 
 | ||||
|             qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency( | ||||
|                     settings.m_streams[istream].m_centerFrequency, | ||||
|                     0, // no transverter mode
 | ||||
|                     settings.m_streams[istream].m_log2Decim, | ||||
|                     (DeviceSampleSource::fcPos_t) settings.m_streams[istream].m_fcPos, | ||||
|                     settings.m_streams[istream].m_sampleRate, | ||||
|                     DeviceSampleSource::FrequencyShiftScheme::FSHIFT_STD, | ||||
|                     false); | ||||
| 
 | ||||
|             int frequencyShift = settings.m_streams[istream].m_frequencyShift; | ||||
|             quint32 devSampleRate = settings.m_streams[istream].m_sampleRate; | ||||
| 
 | ||||
|             if (settings.m_streams[istream].m_log2Decim != 0) | ||||
|             { | ||||
|                 frequencyShift += DeviceSampleSource::calculateFrequencyShift( | ||||
|                         settings.m_streams[istream].m_log2Decim, | ||||
|                         (DeviceSampleSource::fcPos_t) settings.m_streams[istream].m_fcPos, | ||||
|                         settings.m_streams[istream].m_sampleRate, | ||||
|                         DeviceSampleSource::FSHIFT_STD); | ||||
|             } | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) | ||||
|             { | ||||
|                 m_testSourceThreads[istream]->setFcPos((int) settings.m_streams[istream].m_fcPos); | ||||
|                 m_testSourceThreads[istream]->setFrequencyShift(frequencyShift); | ||||
|                 qDebug() << "TestMI::applySettings:" | ||||
|                         << " thread on istream: " << istream | ||||
|                         << " center freq: " << settings.m_streams[istream].m_centerFrequency << " Hz" | ||||
|                         << " device center freq: " << deviceCenterFrequency << " Hz" | ||||
|                         << " device sample rate: " << devSampleRate << "Hz" | ||||
|                         << " Actual sample rate: " << devSampleRate/(1<<m_settings.m_streams[istream].m_log2Decim) << "Hz" | ||||
|                         << " f shift: " << settings.m_streams[istream].m_frequencyShift; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_amplitudeBits != settings.m_streams[istream].m_amplitudeBits) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("amplitudeBits"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setAmplitudeBits(settings.m_streams[istream].m_amplitudeBits); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_dcFactor != settings.m_streams[istream].m_dcFactor) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("dcFactor"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setDCFactor(settings.m_streams[istream].m_dcFactor); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_iFactor != settings.m_streams[istream].m_iFactor) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("iFactor"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setIFactor(settings.m_streams[istream].m_iFactor); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_qFactor != settings.m_streams[istream].m_qFactor) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("qFactor"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setQFactor(settings.m_streams[istream].m_qFactor); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_phaseImbalance != settings.m_streams[istream].m_phaseImbalance) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("phaseImbalance"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setPhaseImbalance(settings.m_streams[istream].m_phaseImbalance); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_sampleSizeIndex != settings.m_streams[istream].m_sampleSizeIndex) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("sampleSizeIndex"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setBitSize(settings.m_streams[istream].m_sampleSizeIndex); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_sampleRate != settings.m_streams[istream].m_sampleRate) | ||||
|             || (m_settings.m_streams[istream].m_centerFrequency != settings.m_streams[istream].m_centerFrequency) | ||||
|             || (m_settings.m_streams[istream].m_log2Decim != settings.m_streams[istream].m_log2Decim) | ||||
|             || (m_settings.m_streams[istream].m_fcPos != settings.m_streams[istream].m_fcPos) || force) | ||||
|         { | ||||
|             int sampleRate = settings.m_streams[istream].m_sampleRate/(1<<settings.m_streams[istream].m_log2Decim); | ||||
|             DSPSignalNotification notif(sampleRate, settings.m_streams[istream].m_centerFrequency); | ||||
|             m_fileSinks[istream]->handleMessage(notif); // forward to file sink
 | ||||
|             DSPMIMOSignalNotification *engineNotif = new DSPMIMOSignalNotification( | ||||
|                 sampleRate, settings.m_streams[istream].m_centerFrequency, true, istream); | ||||
|             m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineNotif); | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_modulationTone != settings.m_streams[istream].m_modulationTone) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("modulationTone"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setToneFrequency(settings.m_streams[istream].m_modulationTone * 10); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_modulation != settings.m_streams[istream].m_modulation) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("modulation"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) | ||||
|             { | ||||
|                 m_testSourceThreads[istream]->setModulation(settings.m_streams[istream].m_modulation); | ||||
| 
 | ||||
|                 if (settings.m_streams[istream].m_modulation == TestMIStreamSettings::ModulationPattern0) { | ||||
|                     m_testSourceThreads[istream]->setPattern0(); | ||||
|                 } else if (settings.m_streams[istream].m_modulation == TestMIStreamSettings::ModulationPattern1) { | ||||
|                     m_testSourceThreads[istream]->setPattern1(); | ||||
|                 } else if (settings.m_streams[istream].m_modulation == TestMIStreamSettings::ModulationPattern2) { | ||||
|                     m_testSourceThreads[istream]->setPattern2(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_amModulation != settings.m_streams[istream].m_amModulation) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("amModulation"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setAMModulation(settings.m_streams[istream].m_amModulation / 100.0f); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ((m_settings.m_streams[istream].m_fmDeviation != settings.m_streams[istream].m_fmDeviation) || force) | ||||
|         { | ||||
|             reverseAPIKeys.append("fmDeviation"); | ||||
| 
 | ||||
|             if ((istream < m_testSourceThreads.size()) && (m_testSourceThreads[istream])) { | ||||
|                 m_testSourceThreads[istream]->setFMDeviation(settings.m_streams[istream].m_fmDeviation * 100.0f); | ||||
|             } | ||||
|         } | ||||
|     } // for each stream index
 | ||||
| 
 | ||||
|     if (settings.m_useReverseAPI) | ||||
|     { | ||||
|         qDebug("TestMI::applySettings: call webapiReverseSendSettings"); | ||||
|         bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || | ||||
|                 (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || | ||||
|                 (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || | ||||
|                 (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex); | ||||
|         webapiReverseSendSettings(deviceSettingsKeys, settings, fullUpdate || force); | ||||
|     } | ||||
| 
 | ||||
|     m_settings = settings; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| int TestMI::webapiRunGet( | ||||
|         int subsystemIndex, | ||||
|         SWGSDRangel::SWGDeviceState& response, | ||||
|         QString& errorMessage) | ||||
| { | ||||
|     if (subsystemIndex == 0) | ||||
|     { | ||||
|         m_deviceAPI->getDeviceEngineStateStr(*response.getState()); // Rx only
 | ||||
|         return 200; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         errorMessage = QString("Subsystem index invalid: expect 0 (Rx) only"); | ||||
|         return 404; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int TestMI::webapiRun( | ||||
|         bool run, | ||||
|         int subsystemIndex, | ||||
|         SWGSDRangel::SWGDeviceState& response, | ||||
|         QString& errorMessage) | ||||
| { | ||||
|     if (subsystemIndex == 0) | ||||
|     { | ||||
|         m_deviceAPI->getDeviceEngineStateStr(*response.getState()); // Rx only
 | ||||
|         MsgStartStop *message = MsgStartStop::create(run); | ||||
|         m_inputMessageQueue.push(message); | ||||
| 
 | ||||
|         if (m_guiMessageQueue) // forward to GUI if any
 | ||||
|         { | ||||
|             MsgStartStop *msgToGUI = MsgStartStop::create(run); | ||||
|             m_guiMessageQueue->push(msgToGUI); | ||||
|         } | ||||
| 
 | ||||
|         return 200; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         errorMessage = QString("Subsystem index invalid: expect 0 (Rx) only"); | ||||
|         return 404; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int TestMI::webapiSettingsGet( | ||||
|                 SWGSDRangel::SWGDeviceSettings& response, | ||||
|                 QString& errorMessage) | ||||
| { | ||||
|     (void) errorMessage; | ||||
|     response.setTestMiSettings(new SWGSDRangel::SWGTestMISettings()); | ||||
|     response.getTestMiSettings()->init(); | ||||
|     webapiFormatDeviceSettings(response, m_settings); | ||||
|     return 200; | ||||
| } | ||||
| 
 | ||||
| int TestMI::webapiSettingsPutPatch( | ||||
|                 bool force, | ||||
|                 const QStringList& deviceSettingsKeys, | ||||
|                 SWGSDRangel::SWGDeviceSettings& response, // query + response
 | ||||
|                 QString& errorMessage) | ||||
| { | ||||
|     (void) errorMessage; | ||||
|     TestMISettings settings = m_settings; | ||||
|     webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response); | ||||
| 
 | ||||
|     MsgConfigureTestSource *msg = MsgConfigureTestSource::create(settings, force); | ||||
|     m_inputMessageQueue.push(msg); | ||||
| 
 | ||||
|     if (m_guiMessageQueue) // forward to GUI if any
 | ||||
|     { | ||||
|         MsgConfigureTestSource *msgToGUI = MsgConfigureTestSource::create(settings, force); | ||||
|         m_guiMessageQueue->push(msgToGUI); | ||||
|     } | ||||
| 
 | ||||
|     webapiFormatDeviceSettings(response, settings); | ||||
|     return 200; | ||||
| } | ||||
| 
 | ||||
| void TestMI::webapiUpdateDeviceSettings( | ||||
|         TestMISettings& settings, | ||||
|         const QStringList& deviceSettingsKeys, | ||||
|         SWGSDRangel::SWGDeviceSettings& response) | ||||
| { | ||||
|     if (deviceSettingsKeys.contains("streams")) | ||||
|     { | ||||
|         QList<SWGSDRangel::SWGTestMiStreamSettings*> *streamsSettings = response.getTestMiSettings()->getStreams(); | ||||
|         QList<SWGSDRangel::SWGTestMiStreamSettings*>::const_iterator it = streamsSettings->begin(); | ||||
| 
 | ||||
|         for (; it != streamsSettings->end(); ++it) | ||||
|         { | ||||
|             int istream = (*it)->getStreamIndex(); | ||||
| 
 | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].centerFrequency").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_centerFrequency = (*it)->getCenterFrequency(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].frequencyShift").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_frequencyShift = (*it)->getFrequencyShift(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].sampleRate").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_sampleRate = (*it)->getSampleRate(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].log2Decim").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_log2Decim = (*it)->getLog2Decim(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].fcPos").arg(istream))) { | ||||
|                 int fcPos = (*it)->getFcPos(); | ||||
|                 fcPos = fcPos < 0 ? 0 : fcPos > 2 ? 2 : fcPos; | ||||
|                 settings.m_streams[istream].m_fcPos = (TestMIStreamSettings::fcPos_t) fcPos; | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].sampleSizeIndex").arg(istream))) { | ||||
|                 int sampleSizeIndex = (*it)->getSampleSizeIndex(); | ||||
|                 sampleSizeIndex = sampleSizeIndex < 0 ? 0 : sampleSizeIndex > 1 ? 2 : sampleSizeIndex; | ||||
|                 settings.m_streams[istream].m_sampleSizeIndex = sampleSizeIndex; | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].amplitudeBits").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_amplitudeBits = (*it)->getAmplitudeBits(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].autoCorrOptions").arg(istream))) { | ||||
|                 int autoCorrOptions = (*it)->getAutoCorrOptions(); | ||||
|                 autoCorrOptions = autoCorrOptions < 0 ? 0 : autoCorrOptions >= TestMIStreamSettings::AutoCorrLast ? TestMIStreamSettings::AutoCorrLast-1 : autoCorrOptions; | ||||
|                 settings.m_streams[istream].m_sampleSizeIndex = (TestMIStreamSettings::AutoCorrOptions) autoCorrOptions; | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].modulation").arg(istream))) { | ||||
|                 int modulation = (*it)->getModulation(); | ||||
|                 modulation = modulation < 0 ? 0 : modulation >= TestMIStreamSettings::ModulationLast ? TestMIStreamSettings::ModulationLast-1 : modulation; | ||||
|                 settings.m_streams[istream].m_modulation = (TestMIStreamSettings::Modulation) modulation; | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].modulationTone").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_modulationTone = (*it)->getModulationTone(); | ||||
|             } | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].amModulation").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_amModulation = (*it)->getAmModulation(); | ||||
|             }; | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].fmDeviation").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_fmDeviation = (*it)->getFmDeviation(); | ||||
|             }; | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].dcFactor").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_dcFactor = (*it)->getDcFactor(); | ||||
|             }; | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].iFactor").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_iFactor = (*it)->getIFactor(); | ||||
|             }; | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].qFactor").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_qFactor = (*it)->getQFactor(); | ||||
|             }; | ||||
|             if (deviceSettingsKeys.contains(QString("streams[%1].phaseImbalance").arg(istream))) { | ||||
|                 settings.m_streams[istream].m_phaseImbalance = (*it)->getPhaseImbalance(); | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     if (deviceSettingsKeys.contains("fileRecordName")) { | ||||
|         settings.m_fileRecordName = *response.getTestMiSettings()->getFileRecordName(); | ||||
|     } | ||||
|     if (deviceSettingsKeys.contains("useReverseAPI")) { | ||||
|         settings.m_useReverseAPI = response.getTestMiSettings()->getUseReverseApi() != 0; | ||||
|     } | ||||
|     if (deviceSettingsKeys.contains("reverseAPIAddress")) { | ||||
|         settings.m_reverseAPIAddress = *response.getTestMiSettings()->getReverseApiAddress(); | ||||
|     } | ||||
|     if (deviceSettingsKeys.contains("reverseAPIPort")) { | ||||
|         settings.m_reverseAPIPort = response.getTestMiSettings()->getReverseApiPort(); | ||||
|     } | ||||
|     if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { | ||||
|         settings.m_reverseAPIDeviceIndex = response.getTestMiSettings()->getReverseApiDeviceIndex(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMI::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestMISettings& settings) | ||||
| { | ||||
|     std::vector<TestMIStreamSettings>::const_iterator it = settings.m_streams.begin(); | ||||
|     int istream = 0; | ||||
| 
 | ||||
|     for (; it != settings.m_streams.end(); ++it, istream++) | ||||
|     { | ||||
|         QList<SWGSDRangel::SWGTestMiStreamSettings*> *streams = response.getTestMiSettings()->getStreams(); | ||||
|         streams->append(new SWGSDRangel::SWGTestMiStreamSettings); | ||||
|         streams->back()->init(); | ||||
|         streams->back()->setStreamIndex(istream); | ||||
|         streams->back()->setCenterFrequency(it->m_centerFrequency); | ||||
|         streams->back()->setFrequencyShift(it->m_frequencyShift); | ||||
|         streams->back()->setSampleRate(it->m_sampleRate); | ||||
|         streams->back()->setLog2Decim(it->m_log2Decim); | ||||
|         streams->back()->setFcPos((int) it->m_fcPos); | ||||
|         streams->back()->setSampleSizeIndex((int) it->m_sampleSizeIndex); | ||||
|         streams->back()->setAmplitudeBits(it->m_amplitudeBits); | ||||
|         streams->back()->setAutoCorrOptions((int) it->m_autoCorrOptions); | ||||
|         streams->back()->setModulation((int) it->m_modulation); | ||||
|         streams->back()->setModulationTone(it->m_modulationTone); | ||||
|         streams->back()->setAmModulation(it->m_amModulation); | ||||
|         streams->back()->setFmDeviation(it->m_fmDeviation); | ||||
|         streams->back()->setDcFactor(it->m_dcFactor); | ||||
|         streams->back()->setIFactor(it->m_iFactor); | ||||
|         streams->back()->setQFactor(it->m_qFactor); | ||||
|         streams->back()->setPhaseImbalance(it->m_phaseImbalance); | ||||
|     } | ||||
| 
 | ||||
|     if (response.getTestMiSettings()->getFileRecordName()) { | ||||
|         *response.getTestMiSettings()->getFileRecordName() = settings.m_fileRecordName; | ||||
|     } else { | ||||
|         response.getTestMiSettings()->setFileRecordName(new QString(settings.m_fileRecordName)); | ||||
|     } | ||||
| 
 | ||||
|     response.getTestMiSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); | ||||
| 
 | ||||
|     if (response.getTestMiSettings()->getReverseApiAddress()) { | ||||
|         *response.getTestMiSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; | ||||
|     } else { | ||||
|         response.getTestMiSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); | ||||
|     } | ||||
| 
 | ||||
|     response.getTestMiSettings()->setReverseApiPort(settings.m_reverseAPIPort); | ||||
|     response.getTestMiSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); | ||||
| } | ||||
| 
 | ||||
| void TestMI::webapiReverseSendSettings(const DeviceSettingsKeys& deviceSettingsKeys, const TestMISettings& settings, bool force) | ||||
| { | ||||
|     SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); | ||||
|     swgDeviceSettings->setDirection(0); // single Rx
 | ||||
|     swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); | ||||
|     swgDeviceSettings->setDeviceHwType(new QString("TestSource")); | ||||
|     swgDeviceSettings->setTestMiSettings(new SWGSDRangel::SWGTestMISettings()); | ||||
|     SWGSDRangel::SWGTestMISettings *swgTestMISettings = swgDeviceSettings->getTestMiSettings(); | ||||
| 
 | ||||
|     // transfer data that has been modified. When force is on transfer all data except reverse API data
 | ||||
| 
 | ||||
|     QList<QList<QString>>::const_iterator it = deviceSettingsKeys.m_streamsSettingsKeys.begin(); | ||||
|     int istream = 0; | ||||
| 
 | ||||
|     for (; it != deviceSettingsKeys.m_streamsSettingsKeys.end(); ++it, istream++) | ||||
|     { | ||||
|         if ((it->size() > 0) || force) | ||||
|         { | ||||
|             QList<SWGSDRangel::SWGTestMiStreamSettings*> *streams = swgTestMISettings->getStreams(); | ||||
|             streams->append(new SWGSDRangel::SWGTestMiStreamSettings); | ||||
|             streams->back()->init(); | ||||
|             streams->back()->setStreamIndex(istream); | ||||
|             const QList<QString>& streamSettingsKeys = *it; | ||||
| 
 | ||||
|             if (streamSettingsKeys.contains("centerFrequency") || force) { | ||||
|                 streams->back()->setCenterFrequency(settings.m_streams[istream].m_centerFrequency); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("frequencyShift") || force) { | ||||
|                 streams->back()->setFrequencyShift(settings.m_streams[istream].m_frequencyShift); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("sampleRate") || force) { | ||||
|                 streams->back()->setSampleRate(settings.m_streams[istream].m_sampleRate); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("log2Decim") || force) { | ||||
|                 streams->back()->setLog2Decim(settings.m_streams[istream].m_log2Decim); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("fcPos") || force) { | ||||
|                 streams->back()->setFcPos((int) settings.m_streams[istream].m_fcPos); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("sampleSizeIndex") || force) { | ||||
|                 streams->back()->setSampleSizeIndex(settings.m_streams[istream].m_sampleSizeIndex); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("amplitudeBits") || force) { | ||||
|                 streams->back()->setAmplitudeBits(settings.m_streams[istream].m_amplitudeBits); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("autoCorrOptions") || force) { | ||||
|                 streams->back()->setAutoCorrOptions((int) settings.m_streams[istream].m_sampleSizeIndex); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("modulation") || force) { | ||||
|                 streams->back()->setModulation((int) settings.m_streams[istream].m_modulation); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("modulationTone")) { | ||||
|                 streams->back()->setModulationTone(settings.m_streams[istream].m_modulationTone); | ||||
|             } | ||||
|             if (streamSettingsKeys.contains("amModulation") || force) { | ||||
|                 streams->back()->setAmModulation(settings.m_streams[istream].m_amModulation); | ||||
|             }; | ||||
|             if (streamSettingsKeys.contains("fmDeviation") || force) { | ||||
|                 streams->back()->setFmDeviation(settings.m_streams[istream].m_fmDeviation); | ||||
|             }; | ||||
|             if (streamSettingsKeys.contains("dcFactor") || force) { | ||||
|                 streams->back()->setDcFactor(settings.m_streams[istream].m_dcFactor); | ||||
|             }; | ||||
|             if (streamSettingsKeys.contains("iFactor") || force) { | ||||
|                 streams->back()->setIFactor(settings.m_streams[istream].m_iFactor); | ||||
|             }; | ||||
|             if (streamSettingsKeys.contains("qFactor") || force) { | ||||
|                 streams->back()->setQFactor(settings.m_streams[istream].m_qFactor); | ||||
|             }; | ||||
|             if (streamSettingsKeys.contains("phaseImbalance") || force) { | ||||
|                 streams->back()->setPhaseImbalance(settings.m_streams[istream].m_phaseImbalance); | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (deviceSettingsKeys.m_commonSettingsKeys.contains("fileRecordName") || force) { | ||||
|         swgTestMISettings->setFileRecordName(new QString(settings.m_fileRecordName)); | ||||
|     } | ||||
| 
 | ||||
|     QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") | ||||
|             .arg(settings.m_reverseAPIAddress) | ||||
|             .arg(settings.m_reverseAPIPort) | ||||
|             .arg(settings.m_reverseAPIDeviceIndex); | ||||
|     m_networkRequest.setUrl(QUrl(channelSettingsURL)); | ||||
|     m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | ||||
| 
 | ||||
|     QBuffer *buffer = new QBuffer(); | ||||
|     buffer->open((QBuffer::ReadWrite)); | ||||
|     buffer->write(swgDeviceSettings->asJson().toUtf8()); | ||||
|     buffer->seek(0); | ||||
| 
 | ||||
|     // Always use PATCH to avoid passing reverse API settings
 | ||||
|     QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); | ||||
|     buffer->setParent(reply); | ||||
| 
 | ||||
|     delete swgDeviceSettings; | ||||
| } | ||||
| 
 | ||||
| void TestMI::webapiReverseSendStartStop(bool start) | ||||
| { | ||||
|     SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); | ||||
|     swgDeviceSettings->setDirection(0); // single Rx
 | ||||
|     swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); | ||||
|     swgDeviceSettings->setDeviceHwType(new QString("TestSource")); | ||||
| 
 | ||||
|     QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") | ||||
|             .arg(m_settings.m_reverseAPIAddress) | ||||
|             .arg(m_settings.m_reverseAPIPort) | ||||
|             .arg(m_settings.m_reverseAPIDeviceIndex); | ||||
|     m_networkRequest.setUrl(QUrl(channelSettingsURL)); | ||||
|     m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | ||||
| 
 | ||||
|     QBuffer *buffer = new QBuffer(); | ||||
|     buffer->open((QBuffer::ReadWrite)); | ||||
|     buffer->write(swgDeviceSettings->asJson().toUtf8()); | ||||
|     buffer->seek(0); | ||||
|     QNetworkReply *reply; | ||||
| 
 | ||||
|     if (start) { | ||||
|         reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer); | ||||
|     } else { | ||||
|         reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer); | ||||
|     } | ||||
| 
 | ||||
|     buffer->setParent(reply); | ||||
|     delete swgDeviceSettings; | ||||
| } | ||||
| 
 | ||||
| void TestMI::networkManagerFinished(QNetworkReply *reply) | ||||
| { | ||||
|     QNetworkReply::NetworkError replyError = reply->error(); | ||||
| 
 | ||||
|     if (replyError) | ||||
|     { | ||||
|         qWarning() << "TestMI::networkManagerFinished:" | ||||
|                 << " error(" << (int) replyError | ||||
|                 << "): " << replyError | ||||
|                 << ": " << reply->errorString(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         QString answer = reply->readAll(); | ||||
|         answer.chop(1); // remove last \n
 | ||||
|         qDebug("TestMI::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); | ||||
|     } | ||||
| 
 | ||||
|     reply->deleteLater(); | ||||
| } | ||||
| 
 | ||||
| bool TestMI::isRecording(unsigned int istream) const | ||||
| { | ||||
|     if (istream < m_fileSinks.size()) { | ||||
|         return m_fileSinks[istream]->isRecording(); | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @ -1,191 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _TESTMI_TESTMI_H_ | ||||
| #define _TESTMI_TESTMI_H_ | ||||
| 
 | ||||
| #include <QString> | ||||
| #include <QByteArray> | ||||
| #include <QTimer> | ||||
| #include <QNetworkRequest> | ||||
| 
 | ||||
| #include "dsp/devicesamplemimo.h" | ||||
| #include "testmisettings.h" | ||||
| 
 | ||||
| class DeviceAPI; | ||||
| class TestMIThread; | ||||
| class FileRecord; | ||||
| class QNetworkAccessManager; | ||||
| class QNetworkReply; | ||||
| 
 | ||||
| class TestMI : public DeviceSampleMIMO { | ||||
|     Q_OBJECT | ||||
| public: | ||||
| 	class MsgConfigureTestSource : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		const TestMISettings& getSettings() const { return m_settings; } | ||||
| 		bool getForce() const { return m_force; } | ||||
| 
 | ||||
| 		static MsgConfigureTestSource* create(const TestMISettings& settings, bool force) | ||||
| 		{ | ||||
| 			return new MsgConfigureTestSource(settings, force); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		TestMISettings m_settings; | ||||
| 		bool m_force; | ||||
| 
 | ||||
| 		MsgConfigureTestSource(const TestMISettings& settings, bool force) : | ||||
| 			Message(), | ||||
| 			m_settings(settings), | ||||
| 			m_force(force) | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
|     class MsgFileRecord : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         bool getStartStop() const { return m_startStop; } | ||||
|         int getStreamIndex() const { return m_streamIndex; } | ||||
| 
 | ||||
|         static MsgFileRecord* create(bool startStop, int streamIndex) { | ||||
|             return new MsgFileRecord(startStop, streamIndex); | ||||
|         } | ||||
| 
 | ||||
|     protected: | ||||
|         bool m_startStop; | ||||
|         int m_streamIndex; | ||||
| 
 | ||||
|         MsgFileRecord(bool startStop, int streamIndex) : | ||||
|             Message(), | ||||
|             m_startStop(startStop), | ||||
|             m_streamIndex(streamIndex) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     class MsgStartStop : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         bool getStartStop() const { return m_startStop; } | ||||
| 
 | ||||
|         static MsgStartStop* create(bool startStop) { | ||||
|             return new MsgStartStop(startStop); | ||||
|         } | ||||
| 
 | ||||
|     protected: | ||||
|         bool m_startStop; | ||||
| 
 | ||||
|         MsgStartStop(bool startStop) : | ||||
|             Message(), | ||||
|             m_startStop(startStop) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
|     TestMI(DeviceAPI *deviceAPI); | ||||
| 	virtual ~TestMI(); | ||||
| 	virtual void destroy(); | ||||
| 
 | ||||
| 	virtual void init(); | ||||
| 	virtual bool startRx(); | ||||
| 	virtual void stopRx(); | ||||
| 	virtual bool startTx(); | ||||
| 	virtual void stopTx(); | ||||
| 
 | ||||
|     virtual QByteArray serialize() const; | ||||
|     virtual bool deserialize(const QByteArray& data); | ||||
| 
 | ||||
|     virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } | ||||
| 	virtual const QString& getDeviceDescription() const; | ||||
| 
 | ||||
| 	virtual int getSourceSampleRate(int index) const; | ||||
|     virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } | ||||
| 	virtual quint64 getSourceCenterFrequency(int index) const; | ||||
|     virtual void setSourceCenterFrequency(qint64 centerFrequency, int index); | ||||
| 
 | ||||
| 	virtual int getSinkSampleRate(int index) const { return 0; (void) index; } | ||||
|     virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } | ||||
| 	virtual quint64 getSinkCenterFrequency(int index) const { return 0; (void) index; } | ||||
|     virtual void setSinkCenterFrequency(qint64 centerFrequency, int index) { (void) centerFrequency; (void) index; } | ||||
| 
 | ||||
|     virtual quint64 getMIMOCenterFrequency() const { return getSourceCenterFrequency(0); } | ||||
|     virtual unsigned int getMIMOSampleRate() const { return getSourceSampleRate(0); } | ||||
| 
 | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
|     virtual int webapiSettingsGet( | ||||
|                 SWGSDRangel::SWGDeviceSettings& response, | ||||
|                 QString& errorMessage); | ||||
| 
 | ||||
|     virtual int webapiSettingsPutPatch( | ||||
|                 bool force, | ||||
|                 const QStringList& deviceSettingsKeys, | ||||
|                 SWGSDRangel::SWGDeviceSettings& response, // query + response
 | ||||
|                 QString& errorMessage); | ||||
| 
 | ||||
|     virtual int webapiRunGet( | ||||
|             int subsystemIndex, | ||||
|             SWGSDRangel::SWGDeviceState& response, | ||||
|             QString& errorMessage); | ||||
| 
 | ||||
|     virtual int webapiRun( | ||||
|             bool run, | ||||
|             int subsystemIndex, | ||||
|             SWGSDRangel::SWGDeviceState& response, | ||||
|             QString& errorMessage); | ||||
| 
 | ||||
|     static void webapiFormatDeviceSettings( | ||||
|             SWGSDRangel::SWGDeviceSettings& response, | ||||
|             const TestMISettings& settings); | ||||
| 
 | ||||
|     static void webapiUpdateDeviceSettings( | ||||
|             TestMISettings& settings, | ||||
|             const QStringList& deviceSettingsKeys, | ||||
|             SWGSDRangel::SWGDeviceSettings& response); | ||||
| 
 | ||||
|     bool isRecording(unsigned int istream) const; | ||||
| 
 | ||||
| private: | ||||
|     struct DeviceSettingsKeys | ||||
|     { | ||||
|         QList<QString> m_commonSettingsKeys; | ||||
|         QList<QList<QString>> m_streamsSettingsKeys; | ||||
|     }; | ||||
| 
 | ||||
| 	DeviceAPI *m_deviceAPI; | ||||
|     std::vector<FileRecord *> m_fileSinks; //!< File sinks to record device I/Q output
 | ||||
| 	QMutex m_mutex; | ||||
| 	TestMISettings m_settings; | ||||
| 	std::vector<TestMIThread*> m_testSourceThreads; | ||||
| 	QString m_deviceDescription; | ||||
| 	bool m_running; | ||||
|     const QTimer& m_masterTimer; | ||||
|     QNetworkAccessManager *m_networkManager; | ||||
|     QNetworkRequest m_networkRequest; | ||||
| 
 | ||||
| 	bool applySettings(const TestMISettings& settings, bool force); | ||||
|     void webapiReverseSendSettings(const DeviceSettingsKeys& deviceSettingsKeys, const TestMISettings& settings, bool force); | ||||
|     void webapiReverseSendStartStop(bool start); | ||||
| 
 | ||||
| private slots: | ||||
|     void networkManagerFinished(QNetworkReply *reply); | ||||
| }; | ||||
| 
 | ||||
| #endif // _TESTMI_TESTMI_H_
 | ||||
| @ -1,605 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include <QTime> | ||||
| #include <QDateTime> | ||||
| #include <QString> | ||||
| #include <QMessageBox> | ||||
| 
 | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "device/deviceapi.h" | ||||
| #include "device/deviceuiset.h" | ||||
| #include "gui/colormapper.h" | ||||
| #include "gui/glspectrum.h" | ||||
| #include "gui/crightclickenabler.h" | ||||
| #include "gui/basicdevicesettingsdialog.h" | ||||
| #include "dsp/dspengine.h" | ||||
| #include "dsp/dspdevicemimoengine.h" | ||||
| #include "dsp/dspcommands.h" | ||||
| #include "util/db.h" | ||||
| 
 | ||||
| #include "mainwindow.h" | ||||
| 
 | ||||
| #include "ui_testmigui.h" | ||||
| #include "testmigui.h" | ||||
| 
 | ||||
| TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : | ||||
|     QWidget(parent), | ||||
|     ui(new Ui::TestMIGui), | ||||
|     m_deviceUISet(deviceUISet), | ||||
|     m_settings(), | ||||
|     m_doApplySettings(true), | ||||
|     m_forceSettings(true), | ||||
|     m_sampleMIMO(nullptr), | ||||
|     m_tickCount(0), | ||||
|     m_lastEngineState(DeviceAPI::StNotStarted) | ||||
| { | ||||
|     qDebug("TestMIGui::TestMIGui"); | ||||
|     m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); | ||||
|     m_streamIndex = 0; | ||||
|     m_deviceCenterFrequencies.push_back(m_settings.m_streams[0].m_centerFrequency); | ||||
|     m_deviceCenterFrequencies.push_back(m_settings.m_streams[1].m_centerFrequency); | ||||
|     m_deviceSampleRates.push_back(m_settings.m_streams[0].m_sampleRate / (1<<m_settings.m_streams[0].m_log2Decim)); | ||||
|     m_deviceSampleRates.push_back(m_settings.m_streams[1].m_sampleRate / (1<<m_settings.m_streams[1].m_log2Decim)); | ||||
| 
 | ||||
|     ui->setupUi(this); | ||||
|     ui->spectrumSource->addItem("0"); | ||||
|     ui->spectrumSource->addItem("1"); | ||||
|     ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); | ||||
|     ui->centerFrequency->setValueRange(7, 0, 9999999); | ||||
|     ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); | ||||
|     ui->sampleRate->setValueRange(7, 48000, 9999999); | ||||
|     ui->frequencyShift->setColorMapper(ColorMapper(ColorMapper::GrayGold)); | ||||
|     ui->frequencyShift->setValueRange(false, 7, -9999999, 9999999); | ||||
|     ui->frequencyShiftLabel->setText(QString("%1").arg(QChar(0x94, 0x03))); | ||||
| 
 | ||||
|     displaySettings(); | ||||
| 
 | ||||
|     connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); | ||||
|     connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); | ||||
|     m_statusTimer.start(500); | ||||
| 
 | ||||
|     connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); | ||||
|     m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); | ||||
| 
 | ||||
|     CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); | ||||
|     connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); | ||||
| } | ||||
| 
 | ||||
| TestMIGui::~TestMIGui() | ||||
| { | ||||
|     delete ui; | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::destroy() | ||||
| { | ||||
|     delete this; | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::setName(const QString& name) | ||||
| { | ||||
|     setObjectName(name); | ||||
| } | ||||
| 
 | ||||
| QString TestMIGui::getName() const | ||||
| { | ||||
|     return objectName(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::resetToDefaults() | ||||
| { | ||||
|     m_settings.resetToDefaults(); | ||||
|     displaySettings(); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| qint64 TestMIGui::getCenterFrequency() const | ||||
| { | ||||
|     return m_settings.m_streams[m_streamIndex].m_centerFrequency; | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::setCenterFrequency(qint64 centerFrequency) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_centerFrequency = centerFrequency; | ||||
|     displaySettings(); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| QByteArray TestMIGui::serialize() const | ||||
| { | ||||
|     return m_settings.serialize(); | ||||
| } | ||||
| 
 | ||||
| bool TestMIGui::deserialize(const QByteArray& data) | ||||
| { | ||||
|     if(m_settings.deserialize(data)) { | ||||
|         displaySettings(); | ||||
|         m_forceSettings = true; | ||||
|         sendSettings(); | ||||
|         return true; | ||||
|     } else { | ||||
|         resetToDefaults(); | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_startStop_toggled(bool checked) | ||||
| { | ||||
|     if (m_doApplySettings) | ||||
|     { | ||||
|         TestMI::MsgStartStop *message = TestMI::MsgStartStop::create(checked); | ||||
|         m_sampleMIMO->getInputMessageQueue()->push(message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_streamIndex_currentIndexChanged(int index) | ||||
| { | ||||
|     if (ui->streamLock->isChecked()) | ||||
|     { | ||||
|         m_spectrumStreamIndex = index; | ||||
|         m_deviceUISet->m_spectrum->setDisplayedStream(true, index); | ||||
|         m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(true, m_spectrumStreamIndex); | ||||
|         ui->spectrumSource->blockSignals(true); | ||||
|         ui->spectrumSource->setCurrentIndex(index); | ||||
|         ui->spectrumSource->blockSignals(false); | ||||
|     } | ||||
| 
 | ||||
|     m_streamIndex = index; | ||||
|     updateFileRecordStatus(); | ||||
|     updateSampleRateAndFrequency(); | ||||
|     displaySettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_spectrumSource_currentIndexChanged(int index) | ||||
| { | ||||
|     m_spectrumStreamIndex = index; | ||||
|     m_deviceUISet->m_spectrum->setDisplayedStream(true, index); | ||||
|     m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(true, m_spectrumStreamIndex); | ||||
|     updateSampleRateAndFrequency(); | ||||
| 
 | ||||
|     if (ui->streamLock->isChecked()) | ||||
|     { | ||||
|         ui->streamIndex->blockSignals(true); | ||||
|         ui->streamIndex->setCurrentIndex(index); | ||||
|         ui->streamIndex->blockSignals(false); | ||||
|         m_streamIndex = index; | ||||
|         updateFileRecordStatus(); | ||||
|         displaySettings(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_streamLock_toggled(bool checked) | ||||
| { | ||||
|     if (checked && (ui->streamIndex->currentIndex() != ui->spectrumSource->currentIndex())) { | ||||
|         ui->spectrumSource->setCurrentIndex(ui->streamIndex->currentIndex()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_centerFrequency_changed(quint64 value) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_centerFrequency = value * 1000; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_autoCorr_currentIndexChanged(int index) | ||||
| { | ||||
|     if ((index < 0) || (index > TestMIStreamSettings::AutoCorrLast)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_settings.m_streams[m_streamIndex].m_autoCorrOptions = (TestMIStreamSettings::AutoCorrOptions) index; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_frequencyShift_changed(qint64 value) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_frequencyShift = value; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_decimation_currentIndexChanged(int index) | ||||
| { | ||||
|     if ((index < 0) || (index > 6)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_settings.m_streams[m_streamIndex].m_log2Decim = index; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_fcPos_currentIndexChanged(int index) | ||||
| { | ||||
|     if ((index < 0) || (index > 2)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_settings.m_streams[m_streamIndex].m_fcPos = (TestMIStreamSettings::fcPos_t) index; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_sampleRate_changed(quint64 value) | ||||
| { | ||||
|     updateFrequencyShiftLimit(); | ||||
|     m_settings.m_streams[m_streamIndex].m_frequencyShift = ui->frequencyShift->getValueNew(); | ||||
|     m_settings.m_streams[m_streamIndex].m_sampleRate = value; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_sampleSize_currentIndexChanged(int index) | ||||
| { | ||||
|     if ((index < 0) || (index > 2)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     updateAmpCoarseLimit(); | ||||
|     updateAmpFineLimit(); | ||||
|     displayAmplitude(); | ||||
|     m_settings.m_streams[m_streamIndex].m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); | ||||
|     m_settings.m_streams[m_streamIndex].m_sampleSizeIndex = index; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_amplitudeCoarse_valueChanged(int value) | ||||
| { | ||||
|     (void) value; | ||||
|     updateAmpFineLimit(); | ||||
|     displayAmplitude(); | ||||
|     m_settings.m_streams[m_streamIndex].m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_amplitudeFine_valueChanged(int value) | ||||
| { | ||||
|     (void) value; | ||||
|     displayAmplitude(); | ||||
|     m_settings.m_streams[m_streamIndex].m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_modulation_currentIndexChanged(int index) | ||||
| { | ||||
|     if ((index < 0) || (index > TestMIStreamSettings::ModulationLast)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_settings.m_streams[m_streamIndex].m_modulation = (TestMIStreamSettings::Modulation) index; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_modulationFrequency_valueChanged(int value) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_modulationTone = value; | ||||
|     ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_modulationTone / 100.0, 0, 'f', 2)); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_amModulation_valueChanged(int value) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_amModulation = value; | ||||
|     ui->amModulationText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_amModulation)); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_fmDeviation_valueChanged(int value) | ||||
| { | ||||
|     m_settings.m_streams[m_streamIndex].m_fmDeviation = value; | ||||
|     ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_fmDeviation / 10.0, 0, 'f', 1)); | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_dcBias_valueChanged(int value) | ||||
| { | ||||
|     ui->dcBiasText->setText(QString(tr("%1 %").arg(value))); | ||||
|     m_settings.m_streams[m_streamIndex].m_dcFactor = value / 100.0f; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_iBias_valueChanged(int value) | ||||
| { | ||||
|     ui->iBiasText->setText(QString(tr("%1 %").arg(value))); | ||||
|     m_settings.m_streams[m_streamIndex].m_iFactor = value / 100.0f; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_qBias_valueChanged(int value) | ||||
| { | ||||
|     ui->qBiasText->setText(QString(tr("%1 %").arg(value))); | ||||
|     m_settings.m_streams[m_streamIndex].m_qFactor = value / 100.0f; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_phaseImbalance_valueChanged(int value) | ||||
| { | ||||
|     ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(value))); | ||||
|     m_settings.m_streams[m_streamIndex].m_phaseImbalance = value / 100.0f; | ||||
|     sendSettings(); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::on_record_toggled(bool checked) | ||||
| { | ||||
|     if (checked) { | ||||
|         ui->record->setStyleSheet("QToolButton { background-color : red; }"); | ||||
|     } else { | ||||
|         ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); | ||||
|     } | ||||
| 
 | ||||
|     TestMI::MsgFileRecord* message = TestMI::MsgFileRecord::create(checked, m_streamIndex); | ||||
|     m_sampleMIMO->getInputMessageQueue()->push(message); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::displayAmplitude() | ||||
| { | ||||
|     int amplitudeInt = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value(); | ||||
|     double power; | ||||
| 
 | ||||
|     switch (ui->sampleSize->currentIndex()) | ||||
|     { | ||||
|     case 0: // 8 bits: 128
 | ||||
|         power = (double) amplitudeInt*amplitudeInt / (double) (1<<14); | ||||
|         break; | ||||
|     case 1: // 12 bits 2048
 | ||||
|         power = (double) amplitudeInt*amplitudeInt / (double) (1<<22); | ||||
|         break; | ||||
|     case 2: // 16 bits 32768
 | ||||
|     default: | ||||
|         power = (double) amplitudeInt*amplitudeInt / (double) (1<<30); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     ui->amplitudeBits->setText(QString(tr("%1 b").arg(amplitudeInt))); | ||||
|     double powerDb = CalcDb::dbPower(power); | ||||
|     ui->power->setText(QString(tr("%1 dB").arg(QString::number(powerDb, 'f', 1)))); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateAmpCoarseLimit() | ||||
| { | ||||
|     switch (ui->sampleSize->currentIndex()) | ||||
|     { | ||||
|     case 0: // 8 bits: 128
 | ||||
|         ui->amplitudeCoarse->setMaximum(1); | ||||
|         break; | ||||
|     case 1: // 12 bits 2048
 | ||||
|         ui->amplitudeCoarse->setMaximum(20); | ||||
|         break; | ||||
|     case 2: // 16 bits 32768
 | ||||
|     default: | ||||
|         ui->amplitudeCoarse->setMaximum(327); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateAmpFineLimit() | ||||
| { | ||||
|     switch (ui->sampleSize->currentIndex()) | ||||
|     { | ||||
|     case 0: // 8 bits: 128
 | ||||
|         if (ui->amplitudeCoarse->value() == 1) { | ||||
|             ui->amplitudeFine->setMaximum(27); | ||||
|         } else { | ||||
|             ui->amplitudeFine->setMaximum(99); | ||||
|         } | ||||
|         break; | ||||
|     case 1: // 12 bits 2048
 | ||||
|         if (ui->amplitudeCoarse->value() == 20) { | ||||
|             ui->amplitudeFine->setMaximum(47); | ||||
|         } else { | ||||
|             ui->amplitudeFine->setMaximum(99); | ||||
|         } | ||||
|         break; | ||||
|     case 2: // 16 bits 32768
 | ||||
|     default: | ||||
|         if (ui->amplitudeCoarse->value() == 327) { | ||||
|             ui->amplitudeFine->setMaximum(67); | ||||
|         } else { | ||||
|             ui->amplitudeFine->setMaximum(99); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateFrequencyShiftLimit() | ||||
| { | ||||
|     qint64 sampleRate = ui->sampleRate->getValueNew(); | ||||
|     ui->frequencyShift->setValueRange(false, 7, -sampleRate, sampleRate); | ||||
|     ui->frequencyShift->setValue(m_settings.m_streams[m_streamIndex].m_frequencyShift); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateFileRecordStatus() | ||||
| { | ||||
|     if (((TestMI*) m_sampleMIMO)->isRecording(m_streamIndex)) { | ||||
|         ui->record->setStyleSheet("QToolButton { background-color : red; }"); | ||||
|     } else { | ||||
|         ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::displaySettings() | ||||
| { | ||||
|     blockApplySettings(true); | ||||
|     ui->sampleSize->blockSignals(true); | ||||
| 
 | ||||
|     ui->streamIndex->setCurrentIndex(m_streamIndex); | ||||
|     ui->centerFrequency->setValue(m_settings.m_streams[m_streamIndex].m_centerFrequency / 1000); | ||||
|     ui->decimation->setCurrentIndex(m_settings.m_streams[m_streamIndex].m_log2Decim); | ||||
|     ui->fcPos->setCurrentIndex((int) m_settings.m_streams[m_streamIndex].m_fcPos); | ||||
|     ui->sampleRate->setValue(m_settings.m_streams[m_streamIndex].m_sampleRate); | ||||
|     updateFrequencyShiftLimit(); | ||||
|     ui->frequencyShift->setValue(m_settings.m_streams[m_streamIndex].m_frequencyShift); | ||||
|     ui->sampleSize->setCurrentIndex(m_settings.m_streams[m_streamIndex].m_sampleSizeIndex); | ||||
|     updateAmpCoarseLimit(); | ||||
|     int amplitudeBits = m_settings.m_streams[m_streamIndex].m_amplitudeBits; | ||||
|     ui->amplitudeCoarse->setValue(amplitudeBits/100); | ||||
|     updateAmpFineLimit(); | ||||
|     ui->amplitudeFine->setValue(amplitudeBits%100); | ||||
|     displayAmplitude(); | ||||
|     int dcBiasPercent = roundf(m_settings.m_streams[m_streamIndex].m_dcFactor * 100.0f); | ||||
|     ui->dcBias->setValue((int) dcBiasPercent); | ||||
|     ui->dcBiasText->setText(QString(tr("%1 %").arg(dcBiasPercent))); | ||||
|     int iBiasPercent = roundf(m_settings.m_streams[m_streamIndex].m_iFactor * 100.0f); | ||||
|     ui->iBias->setValue((int) iBiasPercent); | ||||
|     ui->iBiasText->setText(QString(tr("%1 %").arg(iBiasPercent))); | ||||
|     int qBiasPercent = roundf(m_settings.m_streams[m_streamIndex].m_qFactor * 100.0f); | ||||
|     ui->qBias->setValue((int) qBiasPercent); | ||||
|     ui->qBiasText->setText(QString(tr("%1 %").arg(qBiasPercent))); | ||||
|     int phaseImbalancePercent = roundf(m_settings.m_streams[m_streamIndex].m_phaseImbalance * 100.0f); | ||||
|     ui->phaseImbalance->setValue((int) phaseImbalancePercent); | ||||
|     ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(phaseImbalancePercent))); | ||||
|     ui->autoCorr->setCurrentIndex(m_settings.m_streams[m_streamIndex].m_autoCorrOptions); | ||||
|     ui->sampleSize->blockSignals(false); | ||||
|     ui->modulation->setCurrentIndex((int) m_settings.m_streams[m_streamIndex].m_modulation); | ||||
|     ui->modulationFrequency->setValue(m_settings.m_streams[m_streamIndex].m_modulationTone); | ||||
|     ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_modulationTone / 100.0, 0, 'f', 2)); | ||||
|     ui->amModulation->setValue(m_settings.m_streams[m_streamIndex].m_amModulation); | ||||
|     ui->amModulationText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_amModulation)); | ||||
|     ui->fmDeviation->setValue(m_settings.m_streams[m_streamIndex].m_fmDeviation); | ||||
|     ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_streams[m_streamIndex].m_fmDeviation / 10.0, 0, 'f', 1)); | ||||
|     blockApplySettings(false); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::sendSettings() | ||||
| { | ||||
|     if(!m_updateTimer.isActive()) { | ||||
|         m_updateTimer.start(100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateHardware() | ||||
| { | ||||
|     if (m_doApplySettings) | ||||
|     { | ||||
|         TestMI::MsgConfigureTestSource* message = TestMI::MsgConfigureTestSource::create(m_settings, m_forceSettings); | ||||
|         m_sampleMIMO->getInputMessageQueue()->push(message); | ||||
|         m_forceSettings = false; | ||||
|         m_updateTimer.stop(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateStatus() | ||||
| { | ||||
|     int state = m_deviceUISet->m_deviceAPI->state(); | ||||
| 
 | ||||
|     if(m_lastEngineState != state) | ||||
|     { | ||||
|         switch(state) | ||||
|         { | ||||
|             case DeviceAPI::StNotStarted: | ||||
|                 ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); | ||||
|                 break; | ||||
|             case DeviceAPI::StIdle: | ||||
|                 ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); | ||||
|                 break; | ||||
|             case DeviceAPI::StRunning: | ||||
|                 ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); | ||||
|                 break; | ||||
|             case DeviceAPI::StError: | ||||
|                 ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); | ||||
|                 QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage()); | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         m_lastEngineState = state; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool TestMIGui::handleMessage(const Message& message) | ||||
| { | ||||
|     if (TestMI::MsgConfigureTestSource::match(message)) | ||||
|     { | ||||
|         qDebug("TestMIGui::handleMessage: MsgConfigureTestSource"); | ||||
|         const TestMI::MsgConfigureTestSource& cfg = (TestMI::MsgConfigureTestSource&) message; | ||||
|         m_settings = cfg.getSettings(); | ||||
|         displaySettings(); | ||||
|         return true; | ||||
|     } | ||||
|     else if (TestMI::MsgStartStop::match(message)) | ||||
|     { | ||||
|         qDebug("TestMIGui::handleMessage: MsgStartStop"); | ||||
|         TestMI::MsgStartStop& notif = (TestMI::MsgStartStop&) message; | ||||
|         blockApplySettings(true); | ||||
|         ui->startStop->setChecked(notif.getStartStop()); | ||||
|         blockApplySettings(false); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::handleInputMessages() | ||||
| { | ||||
|     Message* message; | ||||
| 
 | ||||
|     while ((message = m_inputMessageQueue.pop()) != 0) | ||||
|     { | ||||
|         if (DSPMIMOSignalNotification::match(*message)) | ||||
|         { | ||||
|             DSPMIMOSignalNotification* notif = (DSPMIMOSignalNotification*) message; | ||||
|             int istream = notif->getIndex(); | ||||
|             bool sourceOrSink = notif->getSourceOrSink(); | ||||
|             m_deviceSampleRates[istream] = notif->getSampleRate(); | ||||
|             m_deviceCenterFrequencies[istream] = notif->getCenterFrequency(); | ||||
|             // Do not consider multiple sources at this time
 | ||||
|             qDebug("TestMIGui::handleInputMessages: DSPMIMOSignalNotification: %s stream: %d SampleRate:%d, CenterFrequency:%llu", | ||||
|                     sourceOrSink ? "source" : "sink", | ||||
|                     istream, | ||||
|                     notif->getSampleRate(), | ||||
|                     notif->getCenterFrequency()); | ||||
|             updateSampleRateAndFrequency(); | ||||
| 
 | ||||
|             delete message; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (handleMessage(*message)) | ||||
|             { | ||||
|                 delete message; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::updateSampleRateAndFrequency() | ||||
| { | ||||
|     m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRates[m_spectrumStreamIndex]); | ||||
|     m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequencies[m_spectrumStreamIndex]); | ||||
|     ui->deviceRateText->setText(tr("%1k").arg((float) m_deviceSampleRates[m_streamIndex] / 1000)); | ||||
| } | ||||
| 
 | ||||
| void TestMIGui::openDeviceSettingsDialog(const QPoint& p) | ||||
| { | ||||
|     BasicDeviceSettingsDialog dialog(this); | ||||
|     dialog.setUseReverseAPI(m_settings.m_useReverseAPI); | ||||
|     dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); | ||||
|     dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); | ||||
|     dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); | ||||
| 
 | ||||
|     dialog.move(p); | ||||
|     dialog.exec(); | ||||
| 
 | ||||
|     m_settings.m_useReverseAPI = dialog.useReverseAPI(); | ||||
|     m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); | ||||
|     m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); | ||||
|     m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); | ||||
| 
 | ||||
|     sendSettings(); | ||||
| } | ||||
| @ -1,112 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _TESTMI_TESTMIGUI_H_ | ||||
| #define _TESTMI_TESTMIGUI_H_ | ||||
| 
 | ||||
| #include <plugin/plugininstancegui.h> | ||||
| #include <QTimer> | ||||
| #include <QWidget> | ||||
| 
 | ||||
| #include "util/messagequeue.h" | ||||
| 
 | ||||
| #include "testmisettings.h" | ||||
| #include "testmi.h" | ||||
| 
 | ||||
| class DeviceUISet; | ||||
| 
 | ||||
| namespace Ui { | ||||
| 	class TestMIGui; | ||||
| } | ||||
| 
 | ||||
| class TestMIGui : public QWidget, public PluginInstanceGUI { | ||||
| 	Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
| 	explicit TestMIGui(DeviceUISet *deviceUISet, QWidget* parent = 0); | ||||
| 	virtual ~TestMIGui(); | ||||
| 	virtual void destroy(); | ||||
| 
 | ||||
| 	void setName(const QString& name); | ||||
| 	QString getName() const; | ||||
| 
 | ||||
| 	void resetToDefaults(); | ||||
| 	virtual qint64 getCenterFrequency() const; | ||||
| 	virtual void setCenterFrequency(qint64 centerFrequency); | ||||
| 	QByteArray serialize() const; | ||||
| 	bool deserialize(const QByteArray& data); | ||||
| 	virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } | ||||
| 	virtual bool handleMessage(const Message& message); | ||||
| 
 | ||||
| private: | ||||
| 	Ui::TestMIGui* ui; | ||||
| 
 | ||||
| 	DeviceUISet* m_deviceUISet; | ||||
| 	TestMISettings m_settings; | ||||
|     int m_streamIndex; //!< Current stream index being dealt with
 | ||||
|     int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum
 | ||||
|     QTimer m_updateTimer; | ||||
|     QTimer m_statusTimer; | ||||
| 	bool m_doApplySettings; | ||||
|     bool m_forceSettings; | ||||
| 	DeviceSampleMIMO* m_sampleMIMO; | ||||
| 	std::size_t m_tickCount; | ||||
|     std::vector<int> m_deviceSampleRates; | ||||
|     std::vector<quint64> m_deviceCenterFrequencies; //!< Center frequency in device
 | ||||
| 	int m_lastEngineState; | ||||
| 	MessageQueue m_inputMessageQueue; | ||||
| 
 | ||||
| 	void blockApplySettings(bool block) { m_doApplySettings = !block; } | ||||
| 	void displaySettings(); | ||||
| 	void sendSettings(); | ||||
|     void updateSampleRateAndFrequency(); | ||||
|     void displayAmplitude(); | ||||
|     void updateAmpCoarseLimit(); | ||||
|     void updateAmpFineLimit(); | ||||
|     void updateFrequencyShiftLimit(); | ||||
|     void updateFileRecordStatus(); | ||||
| 
 | ||||
| private slots: | ||||
|     void handleInputMessages(); | ||||
| 	void on_startStop_toggled(bool checked); | ||||
|     void on_streamIndex_currentIndexChanged(int index); | ||||
|     void on_spectrumSource_currentIndexChanged(int index); | ||||
|     void on_streamLock_toggled(bool checked); | ||||
|     void on_centerFrequency_changed(quint64 value); | ||||
|     void on_autoCorr_currentIndexChanged(int index); | ||||
|     void on_frequencyShift_changed(qint64 value); | ||||
|     void on_decimation_currentIndexChanged(int index); | ||||
|     void on_fcPos_currentIndexChanged(int index); | ||||
|     void on_sampleRate_changed(quint64 value); | ||||
|     void on_sampleSize_currentIndexChanged(int index); | ||||
|     void on_amplitudeCoarse_valueChanged(int value); | ||||
|     void on_amplitudeFine_valueChanged(int value); | ||||
|     void on_modulation_currentIndexChanged(int index); | ||||
|     void on_modulationFrequency_valueChanged(int value); | ||||
|     void on_amModulation_valueChanged(int value); | ||||
|     void on_fmDeviation_valueChanged(int value); | ||||
|     void on_dcBias_valueChanged(int value); | ||||
|     void on_iBias_valueChanged(int value); | ||||
|     void on_qBias_valueChanged(int value); | ||||
|     void on_phaseImbalance_valueChanged(int value); | ||||
|     void on_record_toggled(bool checked); | ||||
|     void openDeviceSettingsDialog(const QPoint& p); | ||||
|     void updateStatus(); | ||||
|     void updateHardware(); | ||||
| }; | ||||
| 
 | ||||
| #endif // _TESTMI_TESTMIGUI_H_
 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,144 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include <QtPlugin> | ||||
| 
 | ||||
| #include "plugin/pluginapi.h" | ||||
| #include "util/simpleserializer.h" | ||||
| 
 | ||||
| #ifdef SERVER_MODE | ||||
| #include "testmi.h" | ||||
| #else | ||||
| #include "testmigui.h" | ||||
| #endif | ||||
| #include "testmiplugin.h" | ||||
| #include "testmiwebapiadapter.h" | ||||
| 
 | ||||
| const PluginDescriptor TestMIPlugin::m_pluginDescriptor = { | ||||
|     QString("TestMI"), | ||||
| 	QString("Test Multiple Input"), | ||||
| 	QString("4.12.3"), | ||||
| 	QString("(c) Edouard Griffiths, F4EXB"), | ||||
| 	QString("https://github.com/f4exb/sdrangel"), | ||||
| 	true, | ||||
| 	QString("https://github.com/f4exb/sdrangel") | ||||
| }; | ||||
| 
 | ||||
| const QString TestMIPlugin::m_hardwareID = "TestMI"; | ||||
| const QString TestMIPlugin::m_deviceTypeID = TESTMI_DEVICE_TYPE_ID; | ||||
| 
 | ||||
| TestMIPlugin::TestMIPlugin(QObject* parent) : | ||||
| 	QObject(parent) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| const PluginDescriptor& TestMIPlugin::getPluginDescriptor() const | ||||
| { | ||||
| 	return m_pluginDescriptor; | ||||
| } | ||||
| 
 | ||||
| void TestMIPlugin::initPlugin(PluginAPI* pluginAPI) | ||||
| { | ||||
| 	pluginAPI->registerSampleMIMO(m_deviceTypeID, this); | ||||
| } | ||||
| 
 | ||||
| void TestMIPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices) | ||||
| { | ||||
|     if (listedHwIds.contains(m_hardwareID)) { // check if it was done
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     originDevices.append(OriginDevice( | ||||
|         "TestMI",         // Displayable name
 | ||||
|         m_hardwareID,     // Hardware ID
 | ||||
|         QString(),        // Serial
 | ||||
|         0,                // Sequence
 | ||||
|         2,                // Number of Rx streams
 | ||||
|         0                 // Number of Tx streams
 | ||||
|     )); | ||||
| 
 | ||||
|     listedHwIds.append(m_hardwareID); | ||||
| } | ||||
| 
 | ||||
| PluginInterface::SamplingDevices TestMIPlugin::enumSampleMIMO(const OriginDevices& originDevices) | ||||
| { | ||||
| 	SamplingDevices result; | ||||
| 
 | ||||
|     for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it) | ||||
|     { | ||||
|         if (it->hardwareId == m_hardwareID) | ||||
|         { | ||||
|             result.append(SamplingDevice( | ||||
|                     "TestMI", | ||||
|                     m_hardwareID, | ||||
|                     m_deviceTypeID, | ||||
|                     it->serial, | ||||
|                     it->sequence, | ||||
|                     PluginInterface::SamplingDevice::BuiltInDevice, | ||||
|                     PluginInterface::SamplingDevice::StreamMIMO, | ||||
|                     1,    // MIMO is always considered as a single device
 | ||||
|                     0)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| #ifdef SERVER_MODE | ||||
| PluginInstanceGUI* TestMIPlugin::createSampleMIMOPluginInstanceGUI( | ||||
|         const QString& sourceId, | ||||
|         QWidget **widget, | ||||
|         DeviceUISet *deviceUISet) | ||||
| { | ||||
|     (void) sourceId; | ||||
|     (void) widget; | ||||
|     (void) deviceUISet; | ||||
|     return 0; | ||||
| } | ||||
| #else | ||||
| PluginInstanceGUI* TestMIPlugin::createSampleMIMOPluginInstanceGUI( | ||||
|         const QString& sourceId, | ||||
|         QWidget **widget, | ||||
|         DeviceUISet *deviceUISet) | ||||
| { | ||||
| 	if (sourceId == m_deviceTypeID) { | ||||
| 		TestMIGui* gui = new TestMIGui(deviceUISet); | ||||
| 		*widget = gui; | ||||
| 		return gui; | ||||
| 	} else { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| DeviceSampleMIMO *TestMIPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI) | ||||
| { | ||||
|     if (mimoId == m_deviceTypeID) | ||||
|     { | ||||
|         TestMI* input = new TestMI(deviceAPI); | ||||
|         return input; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| DeviceWebAPIAdapter *TestMIPlugin::createDeviceWebAPIAdapter() const | ||||
| { | ||||
|     return new TestMIWebAPIAdapter(); | ||||
| } | ||||
| @ -1,55 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _TESTMI_TESTMIPLUGIN_H | ||||
| #define _TESTMI_TESTMIPLUGIN_H | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include "plugin/plugininterface.h" | ||||
| 
 | ||||
| class PluginAPI; | ||||
| 
 | ||||
| #define TESTMI_DEVICE_TYPE_ID "sdrangel.samplemimo.testmi" | ||||
| 
 | ||||
| class TestMIPlugin : public QObject, public PluginInterface { | ||||
| 	Q_OBJECT | ||||
| 	Q_INTERFACES(PluginInterface) | ||||
| 	Q_PLUGIN_METADATA(IID TESTMI_DEVICE_TYPE_ID) | ||||
| 
 | ||||
| public: | ||||
| 	explicit TestMIPlugin(QObject* parent = NULL); | ||||
| 
 | ||||
| 	const PluginDescriptor& getPluginDescriptor() const; | ||||
| 	void initPlugin(PluginAPI* pluginAPI); | ||||
| 
 | ||||
| 	virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices); | ||||
| 	virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices); | ||||
| 	virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI( | ||||
| 	        const QString& sourceId, | ||||
| 	        QWidget **widget, | ||||
| 	        DeviceUISet *deviceUISet); | ||||
| 	virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); | ||||
|     virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const; | ||||
| 
 | ||||
| 	static const QString m_hardwareID; | ||||
|     static const QString m_deviceTypeID; | ||||
| 
 | ||||
| private: | ||||
| 	static const PluginDescriptor m_pluginDescriptor; | ||||
| }; | ||||
| 
 | ||||
| #endif // _TESTMI_TESTMIPLUGIN_H
 | ||||
| @ -1,181 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include <QtGlobal> | ||||
| #include "util/simpleserializer.h" | ||||
| #include "testmisettings.h" | ||||
| 
 | ||||
| TestMIStreamSettings::TestMIStreamSettings() | ||||
| { | ||||
|     resetToDefaults(); | ||||
| } | ||||
| 
 | ||||
| void TestMIStreamSettings::resetToDefaults() | ||||
| { | ||||
|     m_centerFrequency = 435000*1000; | ||||
|     m_frequencyShift = 0; | ||||
|     m_sampleRate = 768*1000; | ||||
|     m_log2Decim = 4; | ||||
|     m_fcPos = FC_POS_CENTER; | ||||
|     m_sampleSizeIndex = 0; | ||||
|     m_amplitudeBits = 127; | ||||
|     m_autoCorrOptions = AutoCorrNone; | ||||
|     m_modulation = ModulationNone; | ||||
|     m_modulationTone = 44; // 440 Hz
 | ||||
|     m_amModulation = 50; // 50%
 | ||||
|     m_fmDeviation = 50; // 5 kHz
 | ||||
|     m_dcFactor = 0.0f; | ||||
|     m_iFactor = 0.0f; | ||||
|     m_qFactor = 0.0f; | ||||
|     m_phaseImbalance = 0.0f; | ||||
| } | ||||
| 
 | ||||
| TestMISettings::TestMISettings() | ||||
| { | ||||
|     m_fileRecordName = ""; | ||||
|     m_useReverseAPI = false; | ||||
|     m_reverseAPIAddress = "127.0.0.1"; | ||||
|     m_reverseAPIPort = 8888; | ||||
|     m_reverseAPIDeviceIndex = 0; | ||||
|     m_streams.push_back(TestMIStreamSettings()); | ||||
|     m_streams.push_back(TestMIStreamSettings()); | ||||
| } | ||||
| 
 | ||||
| TestMISettings::TestMISettings(const TestMISettings& other) : | ||||
|     m_streams(other.m_streams) | ||||
| { | ||||
|     m_fileRecordName = other.m_fileRecordName; | ||||
|     m_useReverseAPI = other.m_useReverseAPI; | ||||
|     m_reverseAPIAddress = other.m_reverseAPIAddress; | ||||
|     m_reverseAPIPort = other.m_reverseAPIPort; | ||||
|     m_reverseAPIDeviceIndex = other.m_reverseAPIDeviceIndex; | ||||
| } | ||||
| 
 | ||||
| void TestMISettings::resetToDefaults() | ||||
| { | ||||
|     for (unsigned int i = 0; i < m_streams.size(); i++) { | ||||
|         m_streams[i].resetToDefaults(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| QByteArray TestMISettings::serialize() const | ||||
| { | ||||
|     SimpleSerializer s(1); | ||||
| 
 | ||||
|     s.writeBool(1, m_useReverseAPI); | ||||
|     s.writeString(2, m_reverseAPIAddress); | ||||
|     s.writeU32(3, m_reverseAPIPort); | ||||
|     s.writeU32(4, m_reverseAPIDeviceIndex); | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < m_streams.size(); i++) | ||||
|     { | ||||
|         s.writeS32(10 + 30*i, m_streams[i].m_frequencyShift); | ||||
|         s.writeU32(11 + 30*i, m_streams[i].m_sampleRate); | ||||
|         s.writeU32(12 + 30*i, m_streams[i].m_log2Decim); | ||||
|         s.writeS32(13 + 30*i, (int) m_streams[i].m_fcPos); | ||||
|         s.writeU32(14 + 30*i, m_streams[i].m_sampleSizeIndex); | ||||
|         s.writeS32(15 + 30*i, m_streams[i].m_amplitudeBits); | ||||
|         s.writeS32(16 + 30*i, (int) m_streams[i].m_autoCorrOptions); | ||||
|         s.writeFloat(17 + 30*i, m_streams[i].m_dcFactor); | ||||
|         s.writeFloat(18 + 30*i, m_streams[i].m_iFactor); | ||||
|         s.writeFloat(19 + 30*i, m_streams[i].m_qFactor); | ||||
|         s.writeFloat(20 + 30*i, m_streams[i].m_phaseImbalance); | ||||
|         s.writeS32(21 + 30*i, (int) m_streams[i].m_modulation); | ||||
|         s.writeS32(22 + 30*i, m_streams[i].m_modulationTone); | ||||
|         s.writeS32(23 + 30*i, m_streams[i].m_amModulation); | ||||
|         s.writeS32(24 + 30*i, m_streams[i].m_fmDeviation); | ||||
|     } | ||||
| 
 | ||||
|     return s.final(); | ||||
| } | ||||
| 
 | ||||
| bool TestMISettings::deserialize(const QByteArray& data) | ||||
| { | ||||
|     SimpleDeserializer d(data); | ||||
| 
 | ||||
|     if (!d.isValid()) | ||||
|     { | ||||
|         resetToDefaults(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (d.getVersion() == 1) | ||||
|     { | ||||
|         int intval; | ||||
|         uint32_t utmp; | ||||
| 
 | ||||
|         d.readBool(1, &m_useReverseAPI, false); | ||||
|         d.readString(2, &m_reverseAPIAddress, "127.0.0.1"); | ||||
|         d.readU32(3, &utmp, 0); | ||||
| 
 | ||||
|         if ((utmp > 1023) && (utmp < 65535)) { | ||||
|             m_reverseAPIPort = utmp; | ||||
|         } else { | ||||
|             m_reverseAPIPort = 8888; | ||||
|         } | ||||
| 
 | ||||
|         d.readU32(4, &utmp, 0); | ||||
|         m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; | ||||
| 
 | ||||
|         for (unsigned int i = 0; i < m_streams.size(); i++) | ||||
|         { | ||||
|             d.readS32(10 + 30*i, &m_streams[i].m_frequencyShift, 0); | ||||
|             d.readU32(11 + 30*i, &m_streams[i].m_sampleRate, 768*1000); | ||||
|             d.readU32(12 + 30*i, &m_streams[i].m_log2Decim, 4); | ||||
|             d.readS32(13 + 30*i, &intval, 0); | ||||
|             m_streams[i].m_fcPos = (TestMIStreamSettings::fcPos_t) intval; | ||||
|             d.readU32(14 + 30*i, &m_streams[i].m_sampleSizeIndex, 0); | ||||
|             d.readS32(15 + 30*i, &m_streams[i].m_amplitudeBits, 128); | ||||
|             d.readS32(16 + 30*i, &intval, 0); | ||||
| 
 | ||||
|             if (intval < 0 || intval > (int) TestMIStreamSettings::AutoCorrLast) { | ||||
|                 m_streams[i].m_autoCorrOptions = TestMIStreamSettings::AutoCorrNone; | ||||
|             } else { | ||||
|                 m_streams[i].m_autoCorrOptions = (TestMIStreamSettings::AutoCorrOptions) intval; | ||||
|             } | ||||
| 
 | ||||
|             d.readFloat(17 + 30*i, &m_streams[i].m_dcFactor, 0.0f); | ||||
|             d.readFloat(18 + 30*i, &m_streams[i].m_iFactor, 0.0f); | ||||
|             d.readFloat(19 + 30*i, &m_streams[i].m_qFactor, 0.0f); | ||||
|             d.readFloat(20 + 30*i, &m_streams[i].m_phaseImbalance, 0.0f); | ||||
|             d.readS32(21 + 30*i, &intval, 0); | ||||
| 
 | ||||
|             if (intval < 0 || intval > (int) TestMIStreamSettings::ModulationLast) { | ||||
|                 m_streams[i].m_modulation = TestMIStreamSettings::ModulationNone; | ||||
|             } else { | ||||
|                 m_streams[i].m_modulation = (TestMIStreamSettings::Modulation) intval; | ||||
|             } | ||||
| 
 | ||||
|             d.readS32(22 + 30*i, &m_streams[i].m_modulationTone, 44); | ||||
|             d.readS32(23 + 30*i, &m_streams[i].m_amModulation, 50); | ||||
|             d.readS32(24 + 30*i, &m_streams[i].m_fmDeviation, 50); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         resetToDefaults(); | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -1,84 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _TESTMI_TESTMISETTINGS_H_ | ||||
| #define _TESTMI_TESTMISETTINGS_H_ | ||||
| 
 | ||||
| #include <QString> | ||||
| 
 | ||||
| struct TestMIStreamSettings { | ||||
|     typedef enum { | ||||
|         FC_POS_INFRA = 0, | ||||
|         FC_POS_SUPRA, | ||||
|         FC_POS_CENTER | ||||
|     } fcPos_t; | ||||
| 
 | ||||
|     typedef enum { | ||||
|         AutoCorrNone, | ||||
|         AutoCorrDC, | ||||
|         AutoCorrDCAndIQ, | ||||
|         AutoCorrLast, | ||||
|     } AutoCorrOptions; | ||||
| 
 | ||||
|     typedef enum { | ||||
|         ModulationNone, | ||||
|         ModulationAM, | ||||
|         ModulationFM, | ||||
|         ModulationPattern0, | ||||
|         ModulationPattern1, | ||||
|         ModulationPattern2, | ||||
|         ModulationLast | ||||
|     } Modulation; | ||||
| 
 | ||||
|     quint64 m_centerFrequency; | ||||
| 	qint32 m_frequencyShift; | ||||
| 	quint32 m_sampleRate; | ||||
|     quint32 m_log2Decim; | ||||
|     fcPos_t m_fcPos; | ||||
| 	quint32 m_sampleSizeIndex; | ||||
| 	qint32 m_amplitudeBits; | ||||
|     AutoCorrOptions m_autoCorrOptions; | ||||
|     Modulation m_modulation; | ||||
|     int m_modulationTone;   //!< 10'Hz
 | ||||
|     int m_amModulation;     //!< percent
 | ||||
|     int m_fmDeviation;      //!< 100'Hz
 | ||||
|     float m_dcFactor;       //!< -1.0 < x < 1.0
 | ||||
|     float m_iFactor;        //!< -1.0 < x < 1.0
 | ||||
|     float m_qFactor;        //!< -1.0 < x < 1.0
 | ||||
|     float m_phaseImbalance; //!< -1.0 < x < 1.0
 | ||||
| 
 | ||||
| 	TestMIStreamSettings(); | ||||
| 	void resetToDefaults(); | ||||
| }; | ||||
| 
 | ||||
| struct TestMISettings { | ||||
|     QString m_fileRecordName; | ||||
|     bool m_useReverseAPI; | ||||
|     QString m_reverseAPIAddress; | ||||
|     uint16_t m_reverseAPIPort; | ||||
|     uint16_t m_reverseAPIDeviceIndex; | ||||
|     std::vector<TestMIStreamSettings> m_streams; | ||||
| 
 | ||||
| 	TestMISettings(); | ||||
|     TestMISettings(const TestMISettings& other); | ||||
| 	void resetToDefaults(); | ||||
| 	QByteArray serialize() const; | ||||
| 	bool deserialize(const QByteArray& data); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #endif /* _TESTMI_TESTMISETTINGS_H_ */ | ||||
| @ -1,451 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #define _USE_MATH_DEFINES | ||||
| #include <math.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include "dsp/samplemififo.h" | ||||
| 
 | ||||
| #include "testmithread.h" | ||||
| 
 | ||||
| #define TESTMI_BLOCKSIZE 16384 | ||||
| 
 | ||||
| MESSAGE_CLASS_DEFINITION(TestMIThread::MsgStartStop, Message) | ||||
| 
 | ||||
| TestMIThread::TestMIThread(SampleMIFifo* sampleFifo, int streamIndex, QObject* parent) : | ||||
| 	QThread(parent), | ||||
| 	m_running(false), | ||||
|     m_buf(0), | ||||
|     m_bufsize(0), | ||||
|     m_chunksize(0), | ||||
| 	m_convertBuffer(TESTMI_BLOCKSIZE), | ||||
| 	m_sampleFifo(sampleFifo), | ||||
|     m_streamIndex(streamIndex), | ||||
| 	m_frequencyShift(0), | ||||
| 	m_toneFrequency(440), | ||||
| 	m_modulation(TestMIStreamSettings::ModulationNone), | ||||
| 	m_amModulation(0.5f), | ||||
| 	m_fmDeviationUnit(0.0f), | ||||
| 	m_fmPhasor(0.0f), | ||||
|     m_pulseWidth(150), | ||||
|     m_pulseSampleCount(0), | ||||
|     m_pulsePatternCount(0), | ||||
|     m_pulsePatternCycle(8), | ||||
|     m_pulsePatternPlaces(3), | ||||
| 	m_samplerate(48000), | ||||
| 	m_log2Decim(4), | ||||
| 	m_fcPos(0), | ||||
| 	m_bitSizeIndex(0), | ||||
| 	m_bitShift(8), | ||||
| 	m_amplitudeBits(127), | ||||
| 	m_dcBias(0.0f), | ||||
| 	m_iBias(0.0f), | ||||
| 	m_qBias(0.0f), | ||||
| 	m_phaseImbalance(0.0f), | ||||
| 	m_amplitudeBitsDC(0), | ||||
| 	m_amplitudeBitsI(127), | ||||
| 	m_amplitudeBitsQ(127), | ||||
| 	m_frequency(435*1000), | ||||
| 	m_fcPosShift(0), | ||||
|     m_throttlems(TESTMI_THROTTLE_MS), | ||||
|     m_throttleToggle(false), | ||||
|     m_mutex(QMutex::Recursive) | ||||
| { | ||||
|     connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); | ||||
| } | ||||
| 
 | ||||
| TestMIThread::~TestMIThread() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::startWork() | ||||
| { | ||||
|     m_timer.setTimerType(Qt::PreciseTimer); | ||||
|     connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); | ||||
|     m_timer.start(50); | ||||
| 	m_startWaitMutex.lock(); | ||||
| 	m_elapsedTimer.start(); | ||||
| 	start(); | ||||
| 	while(!m_running) | ||||
| 		m_startWaiter.wait(&m_startWaitMutex, 100); | ||||
| 	m_startWaitMutex.unlock(); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::stopWork() | ||||
| { | ||||
| 	m_running = false; | ||||
| 	wait(); | ||||
|     m_timer.stop(); | ||||
|     disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick())); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setSamplerate(int samplerate) | ||||
| { | ||||
|     QMutexLocker mutexLocker(&m_mutex); | ||||
| 
 | ||||
| 	m_samplerate = samplerate; | ||||
|     m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); | ||||
|     m_throttleToggle = !m_throttleToggle; | ||||
| 	m_nco.setFreq(m_frequencyShift, m_samplerate); | ||||
| 	m_toneNco.setFreq(m_toneFrequency, m_samplerate); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setLog2Decimation(unsigned int log2_decim) | ||||
| { | ||||
| 	m_log2Decim = log2_decim; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setFcPos(int fcPos) | ||||
| { | ||||
| 	m_fcPos = fcPos; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::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 TestMIThread::setAmplitudeBits(int32_t amplitudeBits) | ||||
| { | ||||
|     m_amplitudeBits = amplitudeBits; | ||||
|     m_amplitudeBitsDC = m_dcBias * amplitudeBits; | ||||
|     m_amplitudeBitsI = (1.0f + m_iBias) * amplitudeBits; | ||||
|     m_amplitudeBitsQ = (1.0f + m_qBias) * amplitudeBits; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setDCFactor(float dcFactor) | ||||
| { | ||||
|     m_dcBias = dcFactor; | ||||
|     m_amplitudeBitsDC = m_dcBias * m_amplitudeBits; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setIFactor(float iFactor) | ||||
| { | ||||
|     m_iBias = iFactor; | ||||
|     m_amplitudeBitsI = (1.0f + m_iBias) * m_amplitudeBits; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setQFactor(float iFactor) | ||||
| { | ||||
|     m_qBias = iFactor; | ||||
|     m_amplitudeBitsQ = (1.0f + m_qBias) * m_amplitudeBits; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setPhaseImbalance(float phaseImbalance) | ||||
| { | ||||
|     m_phaseImbalance = phaseImbalance; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setFrequencyShift(int shift) | ||||
| { | ||||
|     m_nco.setFreq(shift, m_samplerate); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setToneFrequency(int toneFrequency) | ||||
| { | ||||
|     m_toneNco.setFreq(toneFrequency, m_samplerate); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setModulation(TestMIStreamSettings::Modulation modulation) | ||||
| { | ||||
|     m_modulation = modulation; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setAMModulation(float amModulation) | ||||
| { | ||||
|     m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setFMDeviation(float deviation) | ||||
| { | ||||
|     float fmDeviationUnit = deviation / (float) m_samplerate; | ||||
|     m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 0.5f ? 0.5f : fmDeviationUnit; | ||||
|     qDebug("TestMIThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::startStop(bool start) | ||||
| { | ||||
|     MsgStartStop *msg = MsgStartStop::create(start); | ||||
|     m_inputMessageQueue.push(msg); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::run() | ||||
| { | ||||
|     m_running = true; | ||||
|     m_startWaiter.wakeAll(); | ||||
| 
 | ||||
|     while (m_running) // actual work is in the tick() function
 | ||||
|     { | ||||
|         sleep(1); | ||||
|     } | ||||
| 
 | ||||
|     m_running = false; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setBuffers(quint32 chunksize) | ||||
| { | ||||
|     if (chunksize > m_bufsize) | ||||
|     { | ||||
|         m_bufsize = chunksize; | ||||
| 
 | ||||
|         if (m_buf == 0) | ||||
|         { | ||||
|             qDebug() << "TestMIThread::setBuffer: Allocate buffer:    " | ||||
|                     << " size: " << m_bufsize << " bytes" | ||||
|                     << " #samples: " << (m_bufsize/4); | ||||
|             m_buf = (qint16*) malloc(m_bufsize); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             qDebug() << "TestMIThread::setBuffer: Re-allocate buffer: " | ||||
|                     << " size: " << m_bufsize << " bytes" | ||||
|                     << " #samples: " << (m_bufsize/4); | ||||
|             free(m_buf); | ||||
|             m_buf = (qint16*) malloc(m_bufsize); | ||||
|         } | ||||
| 
 | ||||
|         m_convertBuffer.resize(chunksize/4); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::generate(quint32 chunksize) | ||||
| { | ||||
|     int n = chunksize / 2; | ||||
|     setBuffers(chunksize); | ||||
| 
 | ||||
|     for (int i = 0; i < n-1;) | ||||
|     { | ||||
|         switch (m_modulation) | ||||
|         { | ||||
|         case TestMIStreamSettings::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 TestMIStreamSettings::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); | ||||
|         } | ||||
|         break; | ||||
|         case TestMIStreamSettings::ModulationPattern0: // binary pattern
 | ||||
|         { | ||||
|             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; | ||||
|                 float patFigure = (m_pulsePatternCount & (1<<patPulseIndex)) != 0 ? 0.3 : 0.0; // make binary pattern ~-10dB vs sync pattern
 | ||||
|                 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; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|         case TestMIStreamSettings::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; | ||||
|         case TestMIStreamSettings::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; | ||||
|         case TestMIStreamSettings::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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     callback(m_buf, n); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::pullAF(Real& afSample) | ||||
| { | ||||
|     afSample = m_toneNco.next(); | ||||
| } | ||||
| 
 | ||||
| //  call appropriate conversion (decimation) routine depending on the number of sample bits
 | ||||
| void TestMIThread::callback(const qint16* buf, qint32 len) | ||||
| { | ||||
| 	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->writeAsync(m_convertBuffer.begin(), it - m_convertBuffer.begin(), m_streamIndex); | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::tick() | ||||
| { | ||||
|     if (m_running) | ||||
|     { | ||||
|         qint64 throttlems = m_elapsedTimer.restart(); | ||||
| 
 | ||||
|         if ((throttlems > 45) && (throttlems < 55) && (throttlems != m_throttlems)) | ||||
|         { | ||||
|             QMutexLocker mutexLocker(&m_mutex); | ||||
|             m_throttlems = throttlems; | ||||
|             m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000); | ||||
|             m_throttleToggle = !m_throttleToggle; | ||||
|         } | ||||
| 
 | ||||
|         generate(m_chunksize); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::handleInputMessages() | ||||
| { | ||||
|     Message* message; | ||||
| 
 | ||||
|     while ((message = m_inputMessageQueue.pop()) != 0) | ||||
|     { | ||||
|         if (MsgStartStop::match(*message)) | ||||
|         { | ||||
|             MsgStartStop* notif = (MsgStartStop*) message; | ||||
|             qDebug("TestMIThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop"); | ||||
| 
 | ||||
|             if (notif->getStartStop()) { | ||||
|                 startWork(); | ||||
|             } else { | ||||
|                 stopWork(); | ||||
|             } | ||||
| 
 | ||||
|             delete message; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setPattern0() | ||||
| { | ||||
|     m_pulseWidth = 150; | ||||
|     m_pulseSampleCount = 0; | ||||
|     m_pulsePatternCount = 0; | ||||
|     m_pulsePatternCycle = 8; | ||||
|     m_pulsePatternPlaces = 3; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setPattern1() | ||||
| { | ||||
|     m_pulseWidth = 1000; | ||||
|     m_pulseSampleCount = 0; | ||||
| } | ||||
| 
 | ||||
| void TestMIThread::setPattern2() | ||||
| { | ||||
|     m_pulseWidth = 1000; | ||||
|     m_pulseSampleCount = 0; | ||||
| } | ||||
| @ -1,387 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _TESTMI_TESTMITHREAD_H_ | ||||
| #define _TESTMI_TESTMITHREAD_H_ | ||||
| 
 | ||||
| #include <QThread> | ||||
| #include <QMutex> | ||||
| #include <QWaitCondition> | ||||
| #include <QTimer> | ||||
| #include <QElapsedTimer> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include "dsp/decimators.h" | ||||
| #include "dsp/ncof.h" | ||||
| #include "util/message.h" | ||||
| #include "util/messagequeue.h" | ||||
| 
 | ||||
| #include "testmisettings.h" | ||||
| 
 | ||||
| #define TESTMI_THROTTLE_MS 50 | ||||
| 
 | ||||
| class SampleMIFifo; | ||||
| 
 | ||||
| class TestMIThread : public QThread { | ||||
| 	Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     class MsgStartStop : public Message { | ||||
|         MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
|     public: | ||||
|         bool getStartStop() const { return m_startStop; } | ||||
| 
 | ||||
|         static MsgStartStop* create(bool startStop) { | ||||
|             return new MsgStartStop(startStop); | ||||
|         } | ||||
| 
 | ||||
|     protected: | ||||
|         bool m_startStop; | ||||
| 
 | ||||
|         MsgStartStop(bool startStop) : | ||||
|             Message(), | ||||
|             m_startStop(startStop) | ||||
|         { } | ||||
|     }; | ||||
| 
 | ||||
| 	TestMIThread(SampleMIFifo* sampleFifo, int streamIndex, QObject* parent = nullptr); | ||||
| 	~TestMIThread(); | ||||
| 
 | ||||
|     void startStop(bool start); | ||||
| 	void setSamplerate(int samplerate); | ||||
|     void setLog2Decimation(unsigned int log2_decim); | ||||
|     void setFcPos(int fcPos); | ||||
| 	void setBitSize(uint32_t bitSizeIndex); | ||||
|     void setAmplitudeBits(int32_t amplitudeBits); | ||||
|     void setDCFactor(float iFactor); | ||||
|     void setIFactor(float iFactor); | ||||
|     void setQFactor(float qFactor); | ||||
|     void setPhaseImbalance(float phaseImbalance); | ||||
|     void setFrequencyShift(int shift); | ||||
|     void setToneFrequency(int toneFrequency); | ||||
|     void setModulation(TestMIStreamSettings::Modulation modulation); | ||||
|     void setAMModulation(float amModulation); | ||||
|     void setFMDeviation(float deviation); | ||||
|     void setPattern0(); | ||||
|     void setPattern1(); | ||||
|     void setPattern2(); | ||||
| 
 | ||||
| private: | ||||
| 	QMutex m_startWaitMutex; | ||||
| 	QWaitCondition m_startWaiter; | ||||
| 	volatile bool m_running; | ||||
| 
 | ||||
| 	qint16  *m_buf; | ||||
|     quint32 m_bufsize; | ||||
|     quint32 m_chunksize; | ||||
| 	SampleVector m_convertBuffer; | ||||
|     SampleMIFifo* m_sampleFifo; | ||||
|     int m_streamIndex; | ||||
| 	NCOF m_nco; | ||||
|     NCOF m_toneNco; | ||||
| 	int m_frequencyShift; | ||||
| 	int m_toneFrequency; | ||||
| 	TestMIStreamSettings::Modulation m_modulation; | ||||
| 	float m_amModulation; | ||||
| 	float m_fmDeviationUnit; | ||||
| 	float m_fmPhasor; | ||||
|     uint32_t m_pulseWidth; //!< pulse width in number of samples
 | ||||
|     uint32_t m_pulseSampleCount; | ||||
|     uint32_t m_pulsePatternCount; | ||||
|     uint32_t m_pulsePatternCycle; | ||||
|     uint32_t m_pulsePatternPlaces; | ||||
| 
 | ||||
| 	int m_samplerate; | ||||
|     unsigned int m_log2Decim; | ||||
|     int m_fcPos; | ||||
| 	uint32_t m_bitSizeIndex; | ||||
| 	uint32_t m_bitShift; | ||||
|     int32_t m_amplitudeBits; | ||||
|     float m_dcBias; | ||||
|     float m_iBias; | ||||
|     float m_qBias; | ||||
|     float m_phaseImbalance; | ||||
|     int32_t m_amplitudeBitsDC; | ||||
|     int32_t m_amplitudeBitsI; | ||||
|     int32_t m_amplitudeBitsQ; | ||||
| 
 | ||||
|     uint64_t m_frequency; | ||||
|     int m_fcPosShift; | ||||
| 
 | ||||
|     int m_throttlems; | ||||
|     QTimer m_timer; | ||||
|     QElapsedTimer m_elapsedTimer; | ||||
|     bool m_throttleToggle; | ||||
|     QMutex m_mutex; | ||||
| 
 | ||||
|     MessageQueue m_inputMessageQueue; | ||||
| 
 | ||||
| 	Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 8> m_decimators_8; | ||||
|     Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 12> m_decimators_12; | ||||
|     Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 16> m_decimators_16; | ||||
| 
 | ||||
| 	void startWork(); | ||||
| 	void stopWork(); | ||||
| 	void run(); | ||||
| 	void callback(const qint16* buf, qint32 len); | ||||
| 	void setBuffers(quint32 chunksize); | ||||
|     void generate(quint32 chunksize); | ||||
|     void pullAF(Real& afSample); | ||||
| 
 | ||||
| 	//  Decimate according to specified log2 (ex: log2=4 => decim=16)
 | ||||
| 	inline void convert_8(SampleVector::iterator* it, const qint16* buf, qint32 len) | ||||
| 	{ | ||||
| 	    if (m_log2Decim == 0) { | ||||
| 	        m_decimators_8.decimate1(it, buf, len); | ||||
| 	    } else { | ||||
| 	        if (m_fcPos == 0) { // Infradyne
 | ||||
| 	            switch (m_log2Decim) { | ||||
| 	            case 1: | ||||
| 	                m_decimators_8.decimate2_inf(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 2: | ||||
| 	                m_decimators_8.decimate4_inf(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 3: | ||||
| 	                m_decimators_8.decimate8_inf(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 4: | ||||
| 	                m_decimators_8.decimate16_inf(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 5: | ||||
| 	                m_decimators_8.decimate32_inf(it, buf, len); | ||||
| 	                break; | ||||
|                 case 6: | ||||
|                     m_decimators_8.decimate64_inf(it, buf, len); | ||||
|                     break; | ||||
| 	            default: | ||||
| 	                break; | ||||
| 	            } | ||||
| 	        } else if (m_fcPos == 1)  {// Supradyne
 | ||||
| 	            switch (m_log2Decim) { | ||||
| 	            case 1: | ||||
| 	                m_decimators_8.decimate2_sup(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 2: | ||||
| 	                m_decimators_8.decimate4_sup(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 3: | ||||
| 	                m_decimators_8.decimate8_sup(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 4: | ||||
| 	                m_decimators_8.decimate16_sup(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 5: | ||||
| 	                m_decimators_8.decimate32_sup(it, buf, len); | ||||
| 	                break; | ||||
|                 case 6: | ||||
|                     m_decimators_8.decimate64_sup(it, buf, len); | ||||
|                     break; | ||||
| 	            default: | ||||
| 	                break; | ||||
| 	            } | ||||
| 	        } else { // Centered
 | ||||
| 	            switch (m_log2Decim) { | ||||
| 	            case 1: | ||||
| 	                m_decimators_8.decimate2_cen(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 2: | ||||
| 	                m_decimators_8.decimate4_cen(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 3: | ||||
| 	                m_decimators_8.decimate8_cen(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 4: | ||||
| 	                m_decimators_8.decimate16_cen(it, buf, len); | ||||
| 	                break; | ||||
| 	            case 5: | ||||
| 	                m_decimators_8.decimate32_cen(it, buf, len); | ||||
| 	                break; | ||||
|                 case 6: | ||||
|                     m_decimators_8.decimate64_cen(it, buf, len); | ||||
|                     break; | ||||
| 	            default: | ||||
| 	                break; | ||||
| 	            } | ||||
| 	        } | ||||
| 	    } | ||||
| 	} | ||||
| 
 | ||||
| 	void convert_12(SampleVector::iterator* it, const qint16* buf, qint32 len) | ||||
|     { | ||||
|         if (m_log2Decim == 0) { | ||||
|             m_decimators_12.decimate1(it, buf, len); | ||||
|         } else { | ||||
|             if (m_fcPos == 0) { // Infradyne
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_12.decimate2_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_12.decimate4_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_12.decimate8_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_12.decimate16_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_12.decimate32_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_12.decimate64_inf(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } else if (m_fcPos == 1)  {// Supradyne
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_12.decimate2_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_12.decimate4_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_12.decimate8_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_12.decimate16_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_12.decimate32_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_12.decimate64_sup(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } else { // Centered
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_12.decimate2_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_12.decimate4_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_12.decimate8_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_12.decimate16_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_12.decimate32_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_12.decimate64_cen(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	void convert_16(SampleVector::iterator* it, const qint16* buf, qint32 len) | ||||
|     { | ||||
|         if (m_log2Decim == 0) { | ||||
|             m_decimators_16.decimate1(it, buf, len); | ||||
|         } else { | ||||
|             if (m_fcPos == 0) { // Infradyne
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_16.decimate2_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_16.decimate4_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_16.decimate8_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_16.decimate16_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_16.decimate32_inf(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_16.decimate64_inf(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } else if (m_fcPos == 1)  {// Supradyne
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_16.decimate2_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_16.decimate4_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_16.decimate8_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_16.decimate16_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_16.decimate32_sup(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_16.decimate64_sup(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } else { // Centered
 | ||||
|                 switch (m_log2Decim) { | ||||
|                 case 1: | ||||
|                     m_decimators_16.decimate2_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     m_decimators_16.decimate4_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     m_decimators_16.decimate8_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     m_decimators_16.decimate16_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 5: | ||||
|                     m_decimators_16.decimate32_cen(it, buf, len); | ||||
|                     break; | ||||
|                 case 6: | ||||
|                     m_decimators_16.decimate64_cen(it, buf, len); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| private slots: | ||||
|     void tick(); | ||||
|     void handleInputMessages(); | ||||
| }; | ||||
| 
 | ||||
| #endif // _TESTSOURCE_TESTSOURCETHREAD_H_
 | ||||
| @ -1,51 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 Edouard Griffiths, F4EXB                                   //
 | ||||
| //                                                                               //
 | ||||
| // Implementation of static web API adapters used for preset serialization and   //
 | ||||
| // deserialization                                                               //
 | ||||
| //                                                                               //
 | ||||
| // 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include "SWGDeviceSettings.h" | ||||
| #include "testmi.h" | ||||
| #include "testmiwebapiadapter.h" | ||||
| 
 | ||||
| TestMIWebAPIAdapter::TestMIWebAPIAdapter() | ||||
| {} | ||||
| 
 | ||||
| TestMIWebAPIAdapter::~TestMIWebAPIAdapter() | ||||
| {} | ||||
| 
 | ||||
| int TestMIWebAPIAdapter::webapiSettingsGet( | ||||
|         SWGSDRangel::SWGDeviceSettings& response, | ||||
|         QString& errorMessage) | ||||
| { | ||||
|     (void) errorMessage; | ||||
|     response.setTestMiSettings(new SWGSDRangel::SWGTestMISettings()); | ||||
|     response.getTestMiSettings()->init(); | ||||
|     TestMI::webapiFormatDeviceSettings(response, m_settings); | ||||
|     return 200; | ||||
| } | ||||
| 
 | ||||
| int TestMIWebAPIAdapter::webapiSettingsPutPatch( | ||||
|         bool force, | ||||
|         const QStringList& deviceSettingsKeys, | ||||
|         SWGSDRangel::SWGDeviceSettings& response, // query + response
 | ||||
|         QString& errorMessage) | ||||
| { | ||||
|     (void) errorMessage; | ||||
|     TestMI::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response); | ||||
|     return 200; | ||||
| } | ||||
| @ -1,44 +0,0 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Copyright (C) 2019 Edouard Griffiths, F4EXB                                   //
 | ||||
| //                                                                               //
 | ||||
| // Implementation of static web API adapters used for preset serialization and   //
 | ||||
| // deserialization                                                               //
 | ||||
| //                                                                               //
 | ||||
| // 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                  //
 | ||||
| // (at your option) any later version.                                           //
 | ||||
| //                                                                               //
 | ||||
| // 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/>.          //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #include "device/devicewebapiadapter.h" | ||||
| #include "testmisettings.h" | ||||
| 
 | ||||
| class TestMIWebAPIAdapter : public DeviceWebAPIAdapter | ||||
| { | ||||
| public: | ||||
|     TestMIWebAPIAdapter(); | ||||
|     virtual ~TestMIWebAPIAdapter(); | ||||
|     virtual QByteArray serialize() { return m_settings.serialize(); } | ||||
|     virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } | ||||
| 
 | ||||
|     virtual int webapiSettingsGet( | ||||
|             SWGSDRangel::SWGDeviceSettings& response, | ||||
|             QString& errorMessage); | ||||
| 
 | ||||
|     virtual int webapiSettingsPutPatch( | ||||
|             bool force, | ||||
|             const QStringList& deviceSettingsKeys, | ||||
|             SWGSDRangel::SWGDeviceSettings& response, // query + response
 | ||||
|             QString& errorMessage); | ||||
| 
 | ||||
| private: | ||||
|     TestMISettings m_settings; | ||||
| }; | ||||
| @ -1,48 +0,0 @@ | ||||
| #-------------------------------------------------------- | ||||
| # | ||||
| # Pro file for Android and Windows builds with Qt Creator | ||||
| # | ||||
| #-------------------------------------------------------- | ||||
| 
 | ||||
| TEMPLATE = lib | ||||
| CONFIG += plugin | ||||
| 
 | ||||
| QT += core gui widgets multimedia opengl | ||||
| 
 | ||||
| TARGET = mimotestsourcemi | ||||
| 
 | ||||
| DEFINES += USE_SSE2=1 | ||||
| QMAKE_CXXFLAGS += -msse2 | ||||
| DEFINES += USE_SSE4_1=1 | ||||
| QMAKE_CXXFLAGS += -msse4.1 | ||||
| QMAKE_CXXFLAGS += -std=c++11 | ||||
| macx:QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ | ||||
| 
 | ||||
| INCLUDEPATH += $$PWD | ||||
| INCLUDEPATH += ../../../exports | ||||
| INCLUDEPATH += ../../../sdrbase | ||||
| INCLUDEPATH += ../../../sdrgui | ||||
| INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client | ||||
| 
 | ||||
| CONFIG(Release):build_subdir = release | ||||
| CONFIG(Debug):build_subdir = debug | ||||
| 
 | ||||
| SOURCES += testsourcegui.cpp\ | ||||
| 	testsourcemiinput.cpp\ | ||||
| 	testsourcemiplugin.cpp\ | ||||
| 	testsourcemisettings.cpp\ | ||||
| 	testsourcemithread.cpp | ||||
| 
 | ||||
| HEADERS += testsourcemigui.h\ | ||||
| 	testsourcemiinput.h\ | ||||
| 	testsourcemiplugin.h\ | ||||
| 	testsourcemisettings.h\ | ||||
| 	testsourcemithread.h | ||||
| 
 | ||||
| FORMS += testsourcemigui.ui | ||||
| 
 | ||||
| LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase | ||||
| LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui | ||||
| LIBS += -L../../../swagger/$${build_subdir} -lswagger | ||||
| 
 | ||||
| RESOURCES = ../../../sdrgui/resources/res.qrc | ||||
| @ -388,18 +388,6 @@ int DeviceEnumerator::getFileSinkDeviceIndex() const | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| int DeviceEnumerator::getTestMIMODeviceIndex() const | ||||
| { | ||||
|     for (DevicesEnumeration::const_iterator it = m_mimoEnumeration.begin(); it != m_mimoEnumeration.end(); ++it) | ||||
|     { | ||||
|         if (it->m_samplingDevice.id == PluginManager::getTestMIMODeviceId()) { | ||||
|             return it->m_index; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| int DeviceEnumerator::getRxSamplingDeviceIndex(const QString& deviceId, int sequence) | ||||
| { | ||||
|     for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) | ||||
|  | ||||
| @ -58,7 +58,6 @@ public: | ||||
|     PluginInterface *getMIMOPluginInterface(int deviceIndex) { return m_mimoEnumeration[deviceIndex].m_pluginInterface; } | ||||
|     int getFileInputDeviceIndex() const;  //!< Get Rx default device
 | ||||
|     int getFileSinkDeviceIndex() const;   //!< Get Tx default device
 | ||||
|     int getTestMIMODeviceIndex() const;   //!< Get MIMO default device
 | ||||
|     int getRxSamplingDeviceIndex(const QString& deviceId, int sequence); | ||||
|     int getTxSamplingDeviceIndex(const QString& deviceId, int sequence); | ||||
|     int getMIMOSamplingDeviceIndex(const QString& deviceId, int sequence); | ||||
|  | ||||
| @ -44,9 +44,6 @@ const QString PluginManager::m_remoteOutputDeviceTypeID = "sdrangel.samplesink.r | ||||
| const QString PluginManager::m_fileSinkHardwareID = "FileSink"; | ||||
| const QString PluginManager::m_fileSinkDeviceTypeID = "sdrangel.samplesink.filesink"; | ||||
| 
 | ||||
| const QString PluginManager::m_testMIMOHardwareID = "TestMI"; | ||||
| const QString PluginManager::m_testMIMODeviceTypeID = "sdrangel.samplemimo.testmi"; | ||||
| 
 | ||||
| PluginManager::PluginManager(QObject* parent) : | ||||
| 	QObject(parent), | ||||
|     m_pluginAPI(this) | ||||
|  | ||||
| @ -92,7 +92,6 @@ public: | ||||
| 
 | ||||
| 	static const QString& getFileInputDeviceId() { return m_fileInputDeviceTypeID; } | ||||
| 	static const QString& getFileSinkDeviceId() { return m_fileSinkDeviceTypeID; } | ||||
| 	static const QString& getTestMIMODeviceId() { return m_testMIMODeviceTypeID; } | ||||
| 
 | ||||
| private: | ||||
| 	struct SamplingDevice { //!< This is the device registration
 | ||||
| @ -147,10 +146,6 @@ private: | ||||
|     static const QString m_fileSinkHardwareID;       //!< FileSink sink hardware ID
 | ||||
|     static const QString m_fileSinkDeviceTypeID;     //!< FileSink sink plugin ID
 | ||||
| 
 | ||||
|     // "Local" sample MIMO device IDs
 | ||||
|     static const QString m_testMIMOHardwareID;       //!< Test MIMO hardware ID
 | ||||
|     static const QString m_testMIMODeviceTypeID;     //!< Test MIMO plugin ID
 | ||||
| 
 | ||||
| 	void loadPluginsDir(const QDir& dir); | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -51,7 +51,6 @@ | ||||
|         <file>webapi/doc/swagger/include/SSBMod.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/Structs.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/TestSource.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/TestMI.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/UDPSource.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/UDPSink.yaml</file> | ||||
|         <file>webapi/doc/swagger/include/WFMDemod.yaml</file> | ||||
|  | ||||
| @ -68,8 +68,6 @@ DeviceSettings: | ||||
|       $ref: "/doc/swagger/include/SoapySDR.yaml#/SoapySDROutputSettings" | ||||
|     testSourceSettings: | ||||
|       $ref: "/doc/swagger/include/TestSource.yaml#/TestSourceSettings" | ||||
|     testMISettings: | ||||
|       $ref: "/doc/swagger/include/TestMI.yaml#/TestMISettings" | ||||
|     xtrxInputSettings: | ||||
|       $ref: "/doc/swagger/include/Xtrx.yaml#/XtrxInputSettings" | ||||
|     xtrxOutputSettings: | ||||
|  | ||||
| @ -117,7 +117,6 @@ const QMap<QString, QString> WebAPIRequestMapper::m_deviceIdToSettingsKey = { | ||||
|     {"sdrangel.samplesource.soapysdrinput", "soapySDRInputSettings"}, | ||||
|     {"sdrangel.samplesink.soapysdroutput", "soapySDROutputSettings"}, | ||||
|     {"sdrangel.samplesource.testsource", "testSourceSettings"}, | ||||
|     {"sdrangel.samplemimo.testmi", "testMISettings"}, | ||||
|     {"sdrangel.samplesource.xtrx", "XtrxInputSettings"}, | ||||
|     {"sdrangel.samplesink.xtrx", "XtrxOutputSettings"} | ||||
| }; | ||||
| @ -183,10 +182,6 @@ const QMap<QString, QString> WebAPIRequestMapper::m_sinkDeviceHwIdToSettingsKey | ||||
|     {"XTRX", "XtrxOutputSettings"} | ||||
| }; | ||||
| 
 | ||||
| const QMap<QString, QString> WebAPIRequestMapper::m_mimoDeviceHwIdToSettingsKey= { | ||||
|     {"TestMI", "testMISettings"} | ||||
| }; | ||||
| 
 | ||||
| WebAPIRequestMapper::WebAPIRequestMapper(QObject* parent) : | ||||
|     HttpRequestHandler(parent), | ||||
|     m_adapter(0) | ||||
| @ -2156,14 +2151,6 @@ bool WebAPIRequestMapper::validateDeviceSettings( | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else if (deviceSettings.getDirection() == 2) // MIMO
 | ||||
|     { | ||||
|         if (m_mimoDeviceHwIdToSettingsKey.contains(*deviceHwType)) { | ||||
|             deviceSettingsKey = m_mimoDeviceHwIdToSettingsKey[*deviceHwType]; | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return false; | ||||
|  | ||||
| @ -164,7 +164,6 @@ private: | ||||
|     static const QMap<QString, QString> m_channelTypeToSettingsKey; | ||||
|     static const QMap<QString, QString> m_sourceDeviceHwIdToSettingsKey; | ||||
|     static const QMap<QString, QString> m_sinkDeviceHwIdToSettingsKey; | ||||
|     static const QMap<QString, QString> m_mimoDeviceHwIdToSettingsKey; | ||||
| }; | ||||
| 
 | ||||
| #endif /* SDRBASE_WEBAPI_WEBAPIREQUESTMAPPER_H_ */ | ||||
|  | ||||
| @ -431,92 +431,6 @@ void MainWindow::addSinkDevice() | ||||
|     setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceDisplayName(), 1); | ||||
| } | ||||
| 
 | ||||
| void MainWindow::addMIMODevice() | ||||
| { | ||||
|     DSPDeviceMIMOEngine *dspDeviceMIMOEngine = m_dspEngine->addDeviceMIMOEngine(); | ||||
|     dspDeviceMIMOEngine->start(); | ||||
| 
 | ||||
|     uint dspDeviceMIMOEngineUID =  dspDeviceMIMOEngine->getUID(); | ||||
|     char uidCStr[16]; | ||||
|     sprintf(uidCStr, "UID:%d", dspDeviceMIMOEngineUID); | ||||
| 
 | ||||
|     int deviceTabIndex = m_deviceUIs.size(); | ||||
|     m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, 2, m_masterTimer)); | ||||
|     m_deviceUIs.back()->m_deviceSourceEngine = nullptr; | ||||
|     m_deviceUIs.back()->m_deviceSinkEngine = nullptr; | ||||
|     m_deviceUIs.back()->m_deviceMIMOEngine = dspDeviceMIMOEngine; | ||||
| 
 | ||||
|     char tabNameCStr[16]; | ||||
|     sprintf(tabNameCStr, "M%d", deviceTabIndex); | ||||
| 
 | ||||
|     DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamMIMO, deviceTabIndex, nullptr, nullptr, dspDeviceMIMOEngine); | ||||
| 
 | ||||
|     m_deviceUIs.back()->m_deviceAPI = deviceAPI; | ||||
|     m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); | ||||
|     QComboBox *channelSelector = m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector(); | ||||
|     // Add Rx channels
 | ||||
|     QList<QString> rxChannelNames; | ||||
|     m_pluginManager->listRxChannels(rxChannelNames); | ||||
|     QStringList rxChannelNamesList(rxChannelNames); | ||||
|     channelSelector->addItems(rxChannelNamesList); | ||||
|     m_deviceUIs.back()->setNumberOfAvailableRxChannels(rxChannelNamesList.size()); | ||||
|     // Add Tx channels
 | ||||
|     QList<QString> txChannelNames; | ||||
|     m_pluginManager->listTxChannels(txChannelNames); | ||||
|     QStringList txChannelNamesList(txChannelNames); | ||||
|     channelSelector->addItems(txChannelNamesList); | ||||
|     m_deviceUIs.back()->setNumberOfAvailableTxChannels(txChannelNamesList.size()); | ||||
| 
 | ||||
|     connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(channelAddClicked(bool))); | ||||
| 
 | ||||
|     dspDeviceMIMOEngine->addSpectrumSink(m_deviceUIs.back()->m_spectrumVis); | ||||
|     ui->tabSpectra->addTab(m_deviceUIs.back()->m_spectrum, tabNameCStr); | ||||
|     ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); | ||||
|     ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); | ||||
| 
 | ||||
|     connect(m_deviceUIs.back()->m_samplingDeviceControl, SIGNAL(changed()), this, SLOT(sampleMIMOChanged())); | ||||
| 
 | ||||
|     ui->tabInputsSelect->addTab(m_deviceUIs.back()->m_samplingDeviceControl, tabNameCStr); | ||||
|     ui->tabInputsSelect->setTabToolTip(deviceTabIndex, QString(uidCStr)); | ||||
| 
 | ||||
|     // create a test MIMO by default
 | ||||
|     int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); | ||||
|     const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); | ||||
| 
 | ||||
|     QString userArgs = m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); | ||||
| 
 | ||||
|     if (userArgs.size() > 0) { | ||||
|         m_deviceUIs.back()->m_deviceAPI->setHardwareUserArguments(userArgs); | ||||
|     } | ||||
| 
 | ||||
|     m_deviceUIs.back()->m_samplingDeviceControl->setSelectedDeviceIndex(testMIMODeviceIndex); | ||||
| 
 | ||||
|     // delete previous plugin GUI if it exists
 | ||||
|     m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( | ||||
|             m_deviceUIs.back()->m_deviceAPI->getSamplingDevicePluginInstanceGUI()); | ||||
| 
 | ||||
|     DeviceSampleMIMO *mimo = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( | ||||
|             m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceUIs.back()->m_deviceAPI); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSampleMIMO(mimo); | ||||
|     QWidget *gui; | ||||
|     PluginInstanceGUI *pluginUI = m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( | ||||
|             m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceId(), | ||||
|             &gui, | ||||
|             m_deviceUIs.back()); | ||||
|     m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); | ||||
|     m_deviceUIs.back()->m_deviceAPI->setSamplingDevicePluginInstanceGUI(pluginUI); | ||||
|     m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->init(); | ||||
|     setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceAPI->getSamplingDeviceDisplayName(), 2); | ||||
| } | ||||
| 
 | ||||
| void MainWindow::removeLastDevice() | ||||
| { | ||||
| 	if (m_deviceUIs.back()->m_deviceSourceEngine) // source tab
 | ||||
| @ -1012,8 +926,6 @@ bool MainWindow::handleMessage(const Message& cmd) | ||||
|             addSinkDevice(); | ||||
|         } else if (direction == 0) { // Single stream Rx
 | ||||
|             addSourceDevice(-1); // create with file source device by default
 | ||||
|         } else if (direction == 2) { // MIMO
 | ||||
|             addMIMODevice(); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
| @ -1987,11 +1899,7 @@ void MainWindow::on_action_addSinkDevice_triggered() | ||||
| 
 | ||||
| void MainWindow::on_action_addMIMODevice_triggered() | ||||
| { | ||||
|     if (m_dspEngine->getMIMOSupport()) { | ||||
|         addMIMODevice(); | ||||
|     } else { | ||||
|         QMessageBox::information(this, tr("Message"), tr("MIMO not supported in this version")); | ||||
|     } | ||||
|     QMessageBox::information(this, tr("Message"), tr("MIMO not supported in this version")); | ||||
| } | ||||
| 
 | ||||
| void MainWindow::on_action_removeLastDevice_triggered() | ||||
|  | ||||
| @ -361,7 +361,6 @@ private: | ||||
| 
 | ||||
| 	void addSourceDevice(int deviceIndex); | ||||
| 	void addSinkDevice(); | ||||
|     void addMIMODevice(); | ||||
|     void removeLastDevice(); | ||||
|     void deleteChannel(int deviceSetIndex, int channelIndex); | ||||
| 
 | ||||
|  | ||||
| @ -145,8 +145,6 @@ bool MainCore::handleMessage(const Message& cmd) | ||||
|             addSinkDevice(); | ||||
|         } else if (direction == 0) { // Single stream Rx
 | ||||
|             addSourceDevice(); | ||||
|         } else if (direction == 2) { // MIMO
 | ||||
|             addMIMODevice(); | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
| @ -362,49 +360,6 @@ void MainCore::addSourceDevice() | ||||
|     m_deviceSets.back()->m_deviceAPI->setSampleSource(source); | ||||
| } | ||||
| 
 | ||||
| void MainCore::addMIMODevice() | ||||
| { | ||||
|     DSPDeviceMIMOEngine *dspDeviceMIMOEngine = m_dspEngine->addDeviceMIMOEngine(); | ||||
|     dspDeviceMIMOEngine->start(); | ||||
| 
 | ||||
|     uint dspDeviceMIMOEngineUID =  dspDeviceMIMOEngine->getUID(); | ||||
|     char uidCStr[16]; | ||||
|     sprintf(uidCStr, "UID:%d", dspDeviceMIMOEngineUID); | ||||
| 
 | ||||
|     int deviceTabIndex = m_deviceSets.size(); | ||||
|     m_deviceSets.push_back(new DeviceSet(deviceTabIndex)); | ||||
|     m_deviceSets.back()->m_deviceSourceEngine = nullptr; | ||||
|     m_deviceSets.back()->m_deviceSinkEngine = nullptr; | ||||
|     m_deviceSets.back()->m_deviceMIMOEngine = dspDeviceMIMOEngine; | ||||
| 
 | ||||
|     char tabNameCStr[16]; | ||||
|     sprintf(tabNameCStr, "M%d", deviceTabIndex); | ||||
| 
 | ||||
|     DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamMIMO, deviceTabIndex, nullptr, nullptr, dspDeviceMIMOEngine); | ||||
| 
 | ||||
|     // create a test MIMO by default
 | ||||
|     int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); | ||||
|     const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); | ||||
|     m_deviceSets.back()->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); | ||||
|     m_deviceSets.back()->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); | ||||
|     m_deviceSets.back()->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); | ||||
| 
 | ||||
|     QString userArgs = m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); | ||||
| 
 | ||||
|     if (userArgs.size() > 0) { | ||||
|         m_deviceSets.back()->m_deviceAPI->setHardwareUserArguments(userArgs); | ||||
|     } | ||||
| 
 | ||||
|     DeviceSampleMIMO *mimo = m_deviceSets.back()->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( | ||||
|             m_deviceSets.back()->m_deviceAPI->getSamplingDeviceId(), m_deviceSets.back()->m_deviceAPI); | ||||
|     m_deviceSets.back()->m_deviceAPI->setSampleMIMO(mimo); | ||||
| } | ||||
| 
 | ||||
| void MainCore::removeLastDevice() | ||||
| { | ||||
|     if (m_deviceSets.back()->m_deviceSourceEngine) // source set
 | ||||
|  | ||||
| @ -61,7 +61,6 @@ public: | ||||
| 
 | ||||
|     void addSourceDevice(); | ||||
|     void addSinkDevice(); | ||||
|     void addMIMODevice(); | ||||
|     void removeLastDevice(); | ||||
|     void changeSampleSource(int deviceSetIndex, int selectedDeviceIndex); | ||||
|     void changeSampleSink(int deviceSetIndex, int selectedDeviceIndex); | ||||
|  | ||||
| @ -68,8 +68,6 @@ DeviceSettings: | ||||
|       $ref: "http://localhost:8081/api/swagger/include/SoapySDR.yaml#/SoapySDROutputSettings" | ||||
|     testSourceSettings: | ||||
|       $ref: "http://localhost:8081/api/swagger/include/TestSource.yaml#/TestSourceSettings" | ||||
|     testMISettings: | ||||
|       $ref: "http://localhost:8081/api/swagger/include/TestMI.yaml#/TestMISettings" | ||||
|     xtrxInputSettings: | ||||
|       $ref: "http://localhost:8081/api/swagger/include/Xtrx.yaml#/XtrxInputSettings" | ||||
|     xtrxOutputSettings: | ||||
|  | ||||
| @ -1,64 +0,0 @@ | ||||
| TestMISettings: | ||||
|   description: TestMISettings | ||||
|   properties: | ||||
|     fileRecordName: | ||||
|       type: string | ||||
|     useReverseAPI: | ||||
|       description: Synchronize with reverse API (1 for yes, 0 for no) | ||||
|       type: integer | ||||
|     reverseAPIAddress: | ||||
|       type: string | ||||
|     reverseAPIPort: | ||||
|       type: integer | ||||
|     reverseAPIDeviceIndex: | ||||
|       type: integer | ||||
|     streams: | ||||
|       description: Settings for each of the streams | ||||
|       type: array | ||||
|       items: | ||||
|         $ref: "#/definitions/TestMiStreamSettings" | ||||
| 
 | ||||
| definitions: | ||||
|   TestMiStreamSettings: | ||||
|     description: TestMiStreamSettings | ||||
|     properties: | ||||
|       streamIndex: | ||||
|         description: Index of the stream to which the settings apply | ||||
|         type: integer | ||||
|       centerFrequency: | ||||
|         type: integer | ||||
|         format: uint64 | ||||
|       frequencyShift: | ||||
|         type: integer | ||||
|       sampleRate: | ||||
|         type: integer | ||||
|       log2Decim: | ||||
|         type: integer | ||||
|       fcPos: | ||||
|         type: integer | ||||
|       sampleSizeIndex: | ||||
|         type: integer | ||||
|       amplitudeBits: | ||||
|         type: integer | ||||
|       autoCorrOptions: | ||||
|         type: integer | ||||
|       modulation: | ||||
|         type: integer | ||||
|       modulationTone: | ||||
|         type: integer | ||||
|       amModulation: | ||||
|         type: integer | ||||
|       fmDeviation: | ||||
|         type: integer | ||||
|       dcFactor: | ||||
|         type: number | ||||
|         format: float | ||||
|       iFactor: | ||||
|         type: number | ||||
|         format: float | ||||
|       qFactor: | ||||
|         type: number | ||||
|         format: float | ||||
|       phaseImbalance: | ||||
|         type: number | ||||
|         format: float | ||||
| @ -15,7 +15,7 @@ info: | ||||
| 
 | ||||
|       --- | ||||
| 
 | ||||
|   version: "4.11.6" | ||||
|   version: "4.12.5" | ||||
|   title: SDRangel | ||||
|   contact: | ||||
|     url: "https://github.com/f4exb/sdrangel" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user