mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-05 07:24:44 -04:00
Merged latest developments on dev branch (4.8.1 and 4.8.2)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
project(samplemimo)
|
||||
|
||||
add_subdirectory(testmi)
|
||||
@@ -0,0 +1,54 @@
|
||||
project(testmi)
|
||||
|
||||
set(testmi_SOURCES
|
||||
testmi.cpp
|
||||
testmiplugin.cpp
|
||||
testmithread.cpp
|
||||
testmisettings.cpp
|
||||
)
|
||||
|
||||
set(testmi_HEADERS
|
||||
testmi.h
|
||||
testmiplugin.h
|
||||
testmithread.h
|
||||
testmisettings.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})
|
||||
@@ -0,0 +1,134 @@
|
||||
<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).
|
||||
@@ -0,0 +1,754 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "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_testSourceThread(0),
|
||||
m_deviceDescription(),
|
||||
m_running(false),
|
||||
m_masterTimer(deviceAPI->getMasterTimer())
|
||||
{
|
||||
m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID()));
|
||||
m_deviceAPI->setNbSourceStreams(1);
|
||||
m_deviceAPI->addSourceStream(); // Add a new source stream data set in the engine
|
||||
m_deviceAPI->addAncillarySink(m_fileSink);
|
||||
m_sampleSinkFifos.push_back(SampleSinkFifo(96000 * 4));
|
||||
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) {
|
||||
stop();
|
||||
}
|
||||
|
||||
m_deviceAPI->removeAncillarySink(m_fileSink);
|
||||
m_deviceAPI->removeLastSourceStream(); // Remove the last source stream data set in the engine
|
||||
delete m_fileSink;
|
||||
}
|
||||
|
||||
void TestMI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void TestMI::init()
|
||||
{
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
|
||||
bool TestMI::start()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (m_running) stop();
|
||||
|
||||
m_testSourceThread = new TestMIThread(&m_sampleSinkFifos[0]);
|
||||
m_testSourceThread->setSamplerate(m_settings.m_sampleRate);
|
||||
m_testSourceThread->startStop(true);
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
applySettings(m_settings, true);
|
||||
m_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestMI::stop()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->startStop(false);
|
||||
m_testSourceThread->deleteLater();
|
||||
m_testSourceThread = 0;
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
(void) index;
|
||||
return m_settings.m_sampleRate/(1<<m_settings.m_log2Decim);
|
||||
}
|
||||
|
||||
quint64 TestMI::getSourceCenterFrequency(int index) const
|
||||
{
|
||||
(void) index;
|
||||
return m_settings.m_centerFrequency;
|
||||
}
|
||||
|
||||
void TestMI::setSourceCenterFrequency(qint64 centerFrequency, int index)
|
||||
{
|
||||
(void) index;
|
||||
TestMISettings settings = m_settings;
|
||||
settings.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();
|
||||
|
||||
if (conf.getStartStop())
|
||||
{
|
||||
if (m_settings.m_fileRecordName.size() != 0) {
|
||||
m_fileSink->setFileName(m_settings.m_fileRecordName);
|
||||
} else {
|
||||
m_fileSink->genUniqueFileName(m_deviceAPI->getDeviceUID());
|
||||
}
|
||||
|
||||
m_fileSink->startRecording();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fileSink->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)
|
||||
{
|
||||
QList<QString> reverseAPIKeys;
|
||||
|
||||
if ((m_settings.m_autoCorrOptions != settings.m_autoCorrOptions) || force)
|
||||
{
|
||||
reverseAPIKeys.append("autoCorrOptions");
|
||||
|
||||
switch(settings.m_autoCorrOptions)
|
||||
{
|
||||
case TestMISettings::AutoCorrDC:
|
||||
m_deviceAPI->configureCorrections(true, false);
|
||||
break;
|
||||
case TestMISettings::AutoCorrDCAndIQ:
|
||||
m_deviceAPI->configureCorrections(true, true);
|
||||
break;
|
||||
case TestMISettings::AutoCorrNone:
|
||||
default:
|
||||
m_deviceAPI->configureCorrections(false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_sampleRate != settings.m_sampleRate) || force)
|
||||
{
|
||||
reverseAPIKeys.append("sampleRate");
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->setSamplerate(settings.m_sampleRate);
|
||||
qDebug("TestMI::applySettings: sample rate set to %d", settings.m_sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
reverseAPIKeys.append("log2Decim");
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->setLog2Decimation(settings.m_log2Decim);
|
||||
qDebug() << "TestMI::applySettings: set decimation to " << (1<<settings.m_log2Decim);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
|
||||
|| (m_settings.m_fcPos != settings.m_fcPos)
|
||||
|| (m_settings.m_frequencyShift != settings.m_frequencyShift)
|
||||
|| (m_settings.m_sampleRate != settings.m_sampleRate)
|
||||
|| (m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
reverseAPIKeys.append("centerFrequency");
|
||||
reverseAPIKeys.append("fcPos");
|
||||
reverseAPIKeys.append("frequencyShift");
|
||||
|
||||
qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency(
|
||||
settings.m_centerFrequency,
|
||||
0, // no transverter mode
|
||||
settings.m_log2Decim,
|
||||
(DeviceSampleSource::fcPos_t) settings.m_fcPos,
|
||||
settings.m_sampleRate,
|
||||
DeviceSampleSource::FrequencyShiftScheme::FSHIFT_STD,
|
||||
false);
|
||||
|
||||
int frequencyShift = settings.m_frequencyShift;
|
||||
quint32 devSampleRate = settings.m_sampleRate;
|
||||
|
||||
if (settings.m_log2Decim != 0)
|
||||
{
|
||||
frequencyShift += DeviceSampleSource::calculateFrequencyShift(
|
||||
settings.m_log2Decim,
|
||||
(DeviceSampleSource::fcPos_t) settings.m_fcPos,
|
||||
settings.m_sampleRate,
|
||||
DeviceSampleSource::FSHIFT_STD);
|
||||
}
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->setFcPos((int) settings.m_fcPos);
|
||||
m_testSourceThread->setFrequencyShift(frequencyShift);
|
||||
qDebug() << "TestMI::applySettings:"
|
||||
<< " center freq: " << settings.m_centerFrequency << " Hz"
|
||||
<< " device center freq: " << deviceCenterFrequency << " Hz"
|
||||
<< " device sample rate: " << devSampleRate << "Hz"
|
||||
<< " Actual sample rate: " << devSampleRate/(1<<m_settings.m_log2Decim) << "Hz"
|
||||
<< " f shift: " << settings.m_frequencyShift;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_amplitudeBits != settings.m_amplitudeBits) || force)
|
||||
{
|
||||
reverseAPIKeys.append("amplitudeBits");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setAmplitudeBits(settings.m_amplitudeBits);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_dcFactor != settings.m_dcFactor) || force)
|
||||
{
|
||||
reverseAPIKeys.append("dcFactor");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setDCFactor(settings.m_dcFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_iFactor != settings.m_iFactor) || force)
|
||||
{
|
||||
reverseAPIKeys.append("iFactor");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setIFactor(settings.m_iFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_qFactor != settings.m_qFactor) || force)
|
||||
{
|
||||
reverseAPIKeys.append("qFactor");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setQFactor(settings.m_qFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_phaseImbalance != settings.m_phaseImbalance) || force)
|
||||
{
|
||||
reverseAPIKeys.append("phaseImbalance");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setPhaseImbalance(settings.m_phaseImbalance);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_sampleSizeIndex != settings.m_sampleSizeIndex) || force)
|
||||
{
|
||||
reverseAPIKeys.append("sampleSizeIndex");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setBitSize(settings.m_sampleSizeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_sampleRate != settings.m_sampleRate)
|
||||
|| (m_settings.m_centerFrequency != settings.m_centerFrequency)
|
||||
|| (m_settings.m_log2Decim != settings.m_log2Decim)
|
||||
|| (m_settings.m_fcPos != settings.m_fcPos) || force)
|
||||
{
|
||||
int sampleRate = settings.m_sampleRate/(1<<settings.m_log2Decim);
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, settings.m_centerFrequency);
|
||||
m_fileSink->handleMessage(*notif); // forward to file sink
|
||||
DSPDeviceMIMOEngine::SignalNotification *engineNotif = new DSPDeviceMIMOEngine::SignalNotification(
|
||||
sampleRate, settings.m_centerFrequency, true, 0);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineNotif);
|
||||
}
|
||||
|
||||
if ((m_settings.m_modulationTone != settings.m_modulationTone) || force)
|
||||
{
|
||||
reverseAPIKeys.append("modulationTone");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setToneFrequency(settings.m_modulationTone * 10);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_modulation != settings.m_modulation) || force)
|
||||
{
|
||||
reverseAPIKeys.append("modulation");
|
||||
|
||||
if (m_testSourceThread != 0)
|
||||
{
|
||||
m_testSourceThread->setModulation(settings.m_modulation);
|
||||
|
||||
if (settings.m_modulation == TestMISettings::ModulationPattern0) {
|
||||
m_testSourceThread->setPattern0();
|
||||
} else if (settings.m_modulation == TestMISettings::ModulationPattern1) {
|
||||
m_testSourceThread->setPattern1();
|
||||
} else if (settings.m_modulation == TestMISettings::ModulationPattern2) {
|
||||
m_testSourceThread->setPattern2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_amModulation != settings.m_amModulation) || force)
|
||||
{
|
||||
reverseAPIKeys.append("amModulation");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setAMModulation(settings.m_amModulation / 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force)
|
||||
{
|
||||
reverseAPIKeys.append("fmDeviation");
|
||||
|
||||
if (m_testSourceThread != 0) {
|
||||
m_testSourceThread->setFMDeviation(settings.m_fmDeviation * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
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(reverseAPIKeys, settings, fullUpdate || force);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
return true;
|
||||
}
|
||||
|
||||
int TestMI::webapiRunGet(
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
|
||||
return 200;
|
||||
}
|
||||
|
||||
int TestMI::webapiRun(
|
||||
bool run,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (deviceSettingsKeys.contains("centerFrequency")) {
|
||||
settings.m_centerFrequency = response.getTestMiSettings()->getCenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("frequencyShift")) {
|
||||
settings.m_frequencyShift = response.getTestMiSettings()->getFrequencyShift();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleRate")) {
|
||||
settings.m_sampleRate = response.getTestMiSettings()->getSampleRate();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("log2Decim")) {
|
||||
settings.m_log2Decim = response.getTestMiSettings()->getLog2Decim();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("fcPos")) {
|
||||
int fcPos = response.getTestMiSettings()->getFcPos();
|
||||
fcPos = fcPos < 0 ? 0 : fcPos > 2 ? 2 : fcPos;
|
||||
settings.m_fcPos = (TestMISettings::fcPos_t) fcPos;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleSizeIndex")) {
|
||||
int sampleSizeIndex = response.getTestMiSettings()->getSampleSizeIndex();
|
||||
sampleSizeIndex = sampleSizeIndex < 0 ? 0 : sampleSizeIndex > 1 ? 2 : sampleSizeIndex;
|
||||
settings.m_sampleSizeIndex = sampleSizeIndex;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("amplitudeBits")) {
|
||||
settings.m_amplitudeBits = response.getTestMiSettings()->getAmplitudeBits();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("autoCorrOptions")) {
|
||||
int autoCorrOptions = response.getTestMiSettings()->getAutoCorrOptions();
|
||||
autoCorrOptions = autoCorrOptions < 0 ? 0 : autoCorrOptions >= TestMISettings::AutoCorrLast ? TestMISettings::AutoCorrLast-1 : autoCorrOptions;
|
||||
settings.m_sampleSizeIndex = (TestMISettings::AutoCorrOptions) autoCorrOptions;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("modulation")) {
|
||||
int modulation = response.getTestMiSettings()->getModulation();
|
||||
modulation = modulation < 0 ? 0 : modulation >= TestMISettings::ModulationLast ? TestMISettings::ModulationLast-1 : modulation;
|
||||
settings.m_modulation = (TestMISettings::Modulation) modulation;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("modulationTone")) {
|
||||
settings.m_modulationTone = response.getTestMiSettings()->getModulationTone();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("amModulation")) {
|
||||
settings.m_amModulation = response.getTestMiSettings()->getAmModulation();
|
||||
};
|
||||
if (deviceSettingsKeys.contains("fmDeviation")) {
|
||||
settings.m_fmDeviation = response.getTestMiSettings()->getFmDeviation();
|
||||
};
|
||||
if (deviceSettingsKeys.contains("dcFactor")) {
|
||||
settings.m_dcFactor = response.getTestMiSettings()->getDcFactor();
|
||||
};
|
||||
if (deviceSettingsKeys.contains("iFactor")) {
|
||||
settings.m_iFactor = response.getTestMiSettings()->getIFactor();
|
||||
};
|
||||
if (deviceSettingsKeys.contains("qFactor")) {
|
||||
settings.m_qFactor = response.getTestMiSettings()->getQFactor();
|
||||
};
|
||||
if (deviceSettingsKeys.contains("phaseImbalance")) {
|
||||
settings.m_phaseImbalance = response.getTestMiSettings()->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();
|
||||
}
|
||||
|
||||
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::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestMISettings& settings)
|
||||
{
|
||||
response.getTestMiSettings()->setCenterFrequency(settings.m_centerFrequency);
|
||||
response.getTestMiSettings()->setFrequencyShift(settings.m_frequencyShift);
|
||||
response.getTestMiSettings()->setSampleRate(settings.m_sampleRate);
|
||||
response.getTestMiSettings()->setLog2Decim(settings.m_log2Decim);
|
||||
response.getTestMiSettings()->setFcPos((int) settings.m_fcPos);
|
||||
response.getTestMiSettings()->setSampleSizeIndex((int) settings.m_sampleSizeIndex);
|
||||
response.getTestMiSettings()->setAmplitudeBits(settings.m_amplitudeBits);
|
||||
response.getTestMiSettings()->setAutoCorrOptions((int) settings.m_autoCorrOptions);
|
||||
response.getTestMiSettings()->setModulation((int) settings.m_modulation);
|
||||
response.getTestMiSettings()->setModulationTone(settings.m_modulationTone);
|
||||
response.getTestMiSettings()->setAmModulation(settings.m_amModulation);
|
||||
response.getTestMiSettings()->setFmDeviation(settings.m_fmDeviation);
|
||||
response.getTestMiSettings()->setDcFactor(settings.m_dcFactor);
|
||||
response.getTestMiSettings()->setIFactor(settings.m_iFactor);
|
||||
response.getTestMiSettings()->setQFactor(settings.m_qFactor);
|
||||
response.getTestMiSettings()->setPhaseImbalance(settings.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(QList<QString>& 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
|
||||
|
||||
if (deviceSettingsKeys.contains("centerFrequency") || force) {
|
||||
swgTestMISettings->setCenterFrequency(settings.m_centerFrequency);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("frequencyShift") || force) {
|
||||
swgTestMISettings->setFrequencyShift(settings.m_frequencyShift);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleRate") || force) {
|
||||
swgTestMISettings->setSampleRate(settings.m_sampleRate);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("log2Decim") || force) {
|
||||
swgTestMISettings->setLog2Decim(settings.m_log2Decim);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("fcPos") || force) {
|
||||
swgTestMISettings->setFcPos((int) settings.m_fcPos);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleSizeIndex") || force) {
|
||||
swgTestMISettings->setSampleSizeIndex(settings.m_sampleSizeIndex);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("amplitudeBits") || force) {
|
||||
swgTestMISettings->setAmplitudeBits(settings.m_amplitudeBits);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("autoCorrOptions") || force) {
|
||||
swgTestMISettings->setAutoCorrOptions((int) settings.m_sampleSizeIndex);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("modulation") || force) {
|
||||
swgTestMISettings->setModulation((int) settings.m_modulation);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("modulationTone")) {
|
||||
swgTestMISettings->setModulationTone(settings.m_modulationTone);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("amModulation") || force) {
|
||||
swgTestMISettings->setAmModulation(settings.m_amModulation);
|
||||
};
|
||||
if (deviceSettingsKeys.contains("fmDeviation") || force) {
|
||||
swgTestMISettings->setFmDeviation(settings.m_fmDeviation);
|
||||
};
|
||||
if (deviceSettingsKeys.contains("dcFactor") || force) {
|
||||
swgTestMISettings->setDcFactor(settings.m_dcFactor);
|
||||
};
|
||||
if (deviceSettingsKeys.contains("iFactor") || force) {
|
||||
swgTestMISettings->setIFactor(settings.m_iFactor);
|
||||
};
|
||||
if (deviceSettingsKeys.contains("qFactor") || force) {
|
||||
swgTestMISettings->setQFactor(settings.m_qFactor);
|
||||
};
|
||||
if (deviceSettingsKeys.contains("phaseImbalance") || force) {
|
||||
swgTestMISettings->setPhaseImbalance(settings.m_phaseImbalance);
|
||||
};
|
||||
if (deviceSettingsKeys.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);
|
||||
// qDebug("TestMI::webapiReverseSendSettings: %s", channelSettingsURL.toStdString().c_str());
|
||||
// qDebug("TestMI::webapiReverseSendSettings: query:\n%s", swgDeviceSettings->asJson().toStdString().c_str());
|
||||
|
||||
// Always use PATCH to avoid passing reverse API settings
|
||||
m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
|
||||
|
||||
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);
|
||||
|
||||
if (start) {
|
||||
m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
|
||||
} else {
|
||||
m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void TestMI::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "TestMI::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("TestMI::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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; }
|
||||
|
||||
static MsgFileRecord* create(bool startStop) {
|
||||
return new MsgFileRecord(startStop);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_startStop;
|
||||
|
||||
MsgFileRecord(bool startStop) :
|
||||
Message(),
|
||||
m_startStop(startStop)
|
||||
{ }
|
||||
};
|
||||
|
||||
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 start();
|
||||
virtual void stop();
|
||||
|
||||
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 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(
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiRun(
|
||||
bool run,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
FileRecord *m_fileSink; //!< File sink to record device I/Q output
|
||||
QMutex m_mutex;
|
||||
TestMISettings m_settings;
|
||||
TestMIThread* m_testSourceThread;
|
||||
QString m_deviceDescription;
|
||||
bool m_running;
|
||||
const QTimer& m_masterTimer;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
bool applySettings(const TestMISettings& settings, bool force);
|
||||
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const TestMISettings& settings);
|
||||
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const TestMISettings& settings, bool force);
|
||||
void webapiReverseSendStartStop(bool start);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // _TESTMI_TESTMI_H_
|
||||
@@ -0,0 +1,540 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (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();
|
||||
|
||||
ui->setupUi(this);
|
||||
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_centerFrequency;
|
||||
}
|
||||
|
||||
void TestMIGui::setCenterFrequency(qint64 centerFrequency)
|
||||
{
|
||||
m_settings.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_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
m_settings.m_centerFrequency = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_autoCorr_currentIndexChanged(int index)
|
||||
{
|
||||
if ((index < 0) || (index > TestMISettings::AutoCorrLast)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_autoCorrOptions = (TestMISettings::AutoCorrOptions) index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_frequencyShift_changed(qint64 value)
|
||||
{
|
||||
m_settings.m_frequencyShift = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_decimation_currentIndexChanged(int index)
|
||||
{
|
||||
if ((index < 0) || (index > 6)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_log2Decim = index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_fcPos_currentIndexChanged(int index)
|
||||
{
|
||||
if ((index < 0) || (index > 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_fcPos = (TestMISettings::fcPos_t) index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_sampleRate_changed(quint64 value)
|
||||
{
|
||||
updateFrequencyShiftLimit();
|
||||
m_settings.m_frequencyShift = ui->frequencyShift->getValueNew();
|
||||
m_settings.m_sampleRate = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_sampleSize_currentIndexChanged(int index)
|
||||
{
|
||||
if ((index < 0) || (index > 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateAmpCoarseLimit();
|
||||
updateAmpFineLimit();
|
||||
displayAmplitude();
|
||||
m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value();
|
||||
m_settings.m_sampleSizeIndex = index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_amplitudeCoarse_valueChanged(int value)
|
||||
{
|
||||
(void) value;
|
||||
updateAmpFineLimit();
|
||||
displayAmplitude();
|
||||
m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_amplitudeFine_valueChanged(int value)
|
||||
{
|
||||
(void) value;
|
||||
displayAmplitude();
|
||||
m_settings.m_amplitudeBits = ui->amplitudeCoarse->value() * 100 + ui->amplitudeFine->value();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_modulation_currentIndexChanged(int index)
|
||||
{
|
||||
if ((index < 0) || (index > TestMISettings::ModulationLast)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_modulation = (TestMISettings::Modulation) index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_modulationFrequency_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_modulationTone = value;
|
||||
ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_amModulation_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_amModulation = value;
|
||||
ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_fmDeviation_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_fmDeviation = value;
|
||||
ui->fmDeviationText->setText(QString("%1").arg(m_settings.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_dcFactor = value / 100.0f;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_iBias_valueChanged(int value)
|
||||
{
|
||||
ui->iBiasText->setText(QString(tr("%1 %").arg(value)));
|
||||
m_settings.m_iFactor = value / 100.0f;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_qBias_valueChanged(int value)
|
||||
{
|
||||
ui->qBiasText->setText(QString(tr("%1 %").arg(value)));
|
||||
m_settings.m_qFactor = value / 100.0f;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void TestMIGui::on_phaseImbalance_valueChanged(int value)
|
||||
{
|
||||
ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(value)));
|
||||
m_settings.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_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()
|
||||
{
|
||||
int sampleRate = ui->sampleRate->getValueNew();
|
||||
ui->frequencyShift->setValueRange(false, 7, -sampleRate, sampleRate);
|
||||
}
|
||||
|
||||
void TestMIGui::displaySettings()
|
||||
{
|
||||
blockApplySettings(true);
|
||||
ui->sampleSize->blockSignals(true);
|
||||
|
||||
ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
|
||||
ui->decimation->setCurrentIndex(m_settings.m_log2Decim);
|
||||
ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos);
|
||||
ui->sampleRate->setValue(m_settings.m_sampleRate);
|
||||
updateFrequencyShiftLimit();
|
||||
ui->frequencyShift->setValue(m_settings.m_frequencyShift);
|
||||
ui->sampleSize->setCurrentIndex(m_settings.m_sampleSizeIndex);
|
||||
updateAmpCoarseLimit();
|
||||
int amplitudeBits = m_settings.m_amplitudeBits;
|
||||
ui->amplitudeCoarse->setValue(amplitudeBits/100);
|
||||
updateAmpFineLimit();
|
||||
ui->amplitudeFine->setValue(amplitudeBits%100);
|
||||
displayAmplitude();
|
||||
int dcBiasPercent = roundf(m_settings.m_dcFactor * 100.0f);
|
||||
ui->dcBias->setValue((int) dcBiasPercent);
|
||||
ui->dcBiasText->setText(QString(tr("%1 %").arg(dcBiasPercent)));
|
||||
int iBiasPercent = roundf(m_settings.m_iFactor * 100.0f);
|
||||
ui->iBias->setValue((int) iBiasPercent);
|
||||
ui->iBiasText->setText(QString(tr("%1 %").arg(iBiasPercent)));
|
||||
int qBiasPercent = roundf(m_settings.m_qFactor * 100.0f);
|
||||
ui->qBias->setValue((int) qBiasPercent);
|
||||
ui->qBiasText->setText(QString(tr("%1 %").arg(qBiasPercent)));
|
||||
int phaseImbalancePercent = roundf(m_settings.m_phaseImbalance * 100.0f);
|
||||
ui->phaseImbalance->setValue((int) phaseImbalancePercent);
|
||||
ui->phaseImbalanceText->setText(QString(tr("%1 %").arg(phaseImbalancePercent)));
|
||||
ui->autoCorr->setCurrentIndex(m_settings.m_autoCorrOptions);
|
||||
ui->sampleSize->blockSignals(false);
|
||||
ui->modulation->setCurrentIndex((int) m_settings.m_modulation);
|
||||
ui->modulationFrequency->setValue(m_settings.m_modulationTone);
|
||||
ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2));
|
||||
ui->amModulation->setValue(m_settings.m_amModulation);
|
||||
ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation));
|
||||
ui->fmDeviation->setValue(m_settings.m_fmDeviation);
|
||||
ui->fmDeviationText->setText(QString("%1").arg(m_settings.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 (DSPDeviceMIMOEngine::SignalNotification::match(*message))
|
||||
{
|
||||
DSPDeviceMIMOEngine::SignalNotification* notif = (DSPDeviceMIMOEngine::SignalNotification*) message;
|
||||
m_deviceSampleRate = notif->getSampleRate();
|
||||
m_deviceCenterFrequency = notif->getCenterFrequency();
|
||||
// Do not consider multiple sources at this time
|
||||
qDebug("TestMIGui::handleInputMessages: DSPDeviceMIMOEngine::SignalNotification: SampleRate:%d, CenterFrequency:%llu",
|
||||
notif->getSampleRate(),
|
||||
notif->getCenterFrequency());
|
||||
updateSampleRateAndFrequency();
|
||||
|
||||
delete message;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handleMessage(*message))
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestMIGui::updateSampleRateAndFrequency()
|
||||
{
|
||||
m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate);
|
||||
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
|
||||
ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 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();
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
QTimer m_updateTimer;
|
||||
QTimer m_statusTimer;
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
DeviceSampleMIMO* m_sampleMIMO;
|
||||
std::size_t m_tickCount;
|
||||
int m_deviceSampleRate;
|
||||
quint64 m_deviceCenterFrequency; //!< 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();
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void on_startStop_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
@@ -0,0 +1,111 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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"
|
||||
|
||||
const PluginDescriptor TestMIPlugin::m_pluginDescriptor = {
|
||||
QString("Test Multiple Input"),
|
||||
QString("4.8.1"),
|
||||
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);
|
||||
}
|
||||
|
||||
PluginInterface::SamplingDevices TestMIPlugin::enumSampleMIMO()
|
||||
{
|
||||
SamplingDevices result;
|
||||
|
||||
result.append(SamplingDevice(
|
||||
"TestMI",
|
||||
m_hardwareID,
|
||||
m_deviceTypeID,
|
||||
QString::null,
|
||||
0,
|
||||
PluginInterface::SamplingDevice::BuiltInDevice,
|
||||
PluginInterface::SamplingDevice::StreamMIMO,
|
||||
1,
|
||||
0));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
PluginInstanceGUI* TestMIPlugin::createSampleMIMOPluginInstanceGUI(
|
||||
const QString& sourceId __attribute((unused)),
|
||||
QWidget **widget __attribute((unused)),
|
||||
DeviceUISet *deviceUISet __attribute((unused)))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 SamplingDevices enumSampleMIMO();
|
||||
virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet);
|
||||
virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
|
||||
|
||||
static const QString m_hardwareID;
|
||||
static const QString m_deviceTypeID;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
};
|
||||
|
||||
#endif // _TESTMI_TESTMIPLUGIN_H
|
||||
@@ -0,0 +1,150 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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"
|
||||
|
||||
TestMISettings::TestMISettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void TestMISettings::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;
|
||||
m_fileRecordName = "";
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray TestMISettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(2, m_frequencyShift);
|
||||
s.writeU32(3, m_sampleRate);
|
||||
s.writeU32(4, m_log2Decim);
|
||||
s.writeS32(5, (int) m_fcPos);
|
||||
s.writeU32(6, m_sampleSizeIndex);
|
||||
s.writeS32(7, m_amplitudeBits);
|
||||
s.writeS32(8, (int) m_autoCorrOptions);
|
||||
s.writeFloat(10, m_dcFactor);
|
||||
s.writeFloat(11, m_iFactor);
|
||||
s.writeFloat(12, m_qFactor);
|
||||
s.writeFloat(13, m_phaseImbalance);
|
||||
s.writeS32(14, (int) m_modulation);
|
||||
s.writeS32(15, m_modulationTone);
|
||||
s.writeS32(16, m_amModulation);
|
||||
s.writeS32(17, m_fmDeviation);
|
||||
s.writeBool(18, m_useReverseAPI);
|
||||
s.writeString(19, m_reverseAPIAddress);
|
||||
s.writeU32(20, m_reverseAPIPort);
|
||||
s.writeU32(21, m_reverseAPIDeviceIndex);
|
||||
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.readS32(2, &m_frequencyShift, 0);
|
||||
d.readU32(3, &m_sampleRate, 768*1000);
|
||||
d.readU32(4, &m_log2Decim, 4);
|
||||
d.readS32(5, &intval, 0);
|
||||
m_fcPos = (fcPos_t) intval;
|
||||
d.readU32(6, &m_sampleSizeIndex, 0);
|
||||
d.readS32(7, &m_amplitudeBits, 128);
|
||||
d.readS32(8, &intval, 0);
|
||||
|
||||
if (intval < 0 || intval > (int) AutoCorrLast) {
|
||||
m_autoCorrOptions = AutoCorrNone;
|
||||
} else {
|
||||
m_autoCorrOptions = (AutoCorrOptions) intval;
|
||||
}
|
||||
|
||||
d.readFloat(10, &m_dcFactor, 0.0f);
|
||||
d.readFloat(11, &m_iFactor, 0.0f);
|
||||
d.readFloat(12, &m_qFactor, 0.0f);
|
||||
d.readFloat(13, &m_phaseImbalance, 0.0f);
|
||||
d.readS32(14, &intval, 0);
|
||||
|
||||
if (intval < 0 || intval > (int) ModulationLast) {
|
||||
m_modulation = ModulationNone;
|
||||
} else {
|
||||
m_modulation = (Modulation) intval;
|
||||
}
|
||||
|
||||
d.readS32(15, &m_modulationTone, 44);
|
||||
d.readS32(16, &m_amModulation, 50);
|
||||
d.readS32(17, &m_fmDeviation, 50);
|
||||
|
||||
d.readBool(18, &m_useReverseAPI, false);
|
||||
d.readString(19, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(20, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(21, &utmp, 0);
|
||||
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 TestMISettings {
|
||||
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
|
||||
QString m_fileRecordName;
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
|
||||
TestMISettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* _TESTMI_TESTMISETTINGS_H_ */
|
||||
@@ -0,0 +1,450 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/samplesinkfifo.h"
|
||||
|
||||
#include "testmithread.h"
|
||||
|
||||
#define TESTMI_BLOCKSIZE 16384
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(TestMIThread::MsgStartStop, Message)
|
||||
|
||||
TestMIThread::TestMIThread(SampleSinkFifo* sampleFifo, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_buf(0),
|
||||
m_bufsize(0),
|
||||
m_chunksize(0),
|
||||
m_convertBuffer(TESTMI_BLOCKSIZE),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_frequencyShift(0),
|
||||
m_toneFrequency(440),
|
||||
m_modulation(TestMISettings::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(TestMISettings::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 TestMISettings::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 TestMISettings::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 TestMISettings::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 TestMISettings::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 TestMISettings::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 TestMISettings::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->write(m_convertBuffer.begin(), it);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/samplesinkfifo.h"
|
||||
#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 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(SampleSinkFifo* sampleFifo, QObject* parent = 0);
|
||||
~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(TestMISettings::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;
|
||||
SampleSinkFifo* m_sampleFifo;
|
||||
NCOF m_nco;
|
||||
NCOF m_toneNco;
|
||||
int m_frequencyShift;
|
||||
int m_toneFrequency;
|
||||
TestMISettings::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_
|
||||
@@ -0,0 +1,48 @@
|
||||
#--------------------------------------------------------
|
||||
#
|
||||
# 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
|
||||
Reference in New Issue
Block a user