From 0a1dec4a23c95ba0fe593a2f4859da2461cf4367 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 25 Oct 2019 18:24:39 +0200 Subject: [PATCH] TestMOSync plugin (1) --- plugins/samplemimo/CMakeLists.txt | 6 +- plugins/samplemimo/testmosync/CMakeLists.txt | 55 +++ plugins/samplemimo/testmosync/testmosync.cpp | 304 ++++++++++++++++ plugins/samplemimo/testmosync/testmosync.h | 134 +++++++ .../samplemimo/testmosync/testmosyncgui.cpp | 274 +++++++++++++++ plugins/samplemimo/testmosync/testmosyncgui.h | 90 +++++ .../samplemimo/testmosync/testmosyncgui.ui | 331 ++++++++++++++++++ .../testmosync/testmosyncplugin.cpp | 145 ++++++++ .../samplemimo/testmosync/testmosyncplugin.h | 55 +++ .../testmosync/testmosyncsettings.cpp | 74 ++++ .../testmosync/testmosyncsettings.h | 44 +++ .../testmosync/testmosyncthread.cpp | 290 +++++++++++++++ .../samplemimo/testmosync/testmosyncthread.h | 81 +++++ 13 files changed, 1882 insertions(+), 1 deletion(-) create mode 100644 plugins/samplemimo/testmosync/CMakeLists.txt create mode 100644 plugins/samplemimo/testmosync/testmosync.cpp create mode 100644 plugins/samplemimo/testmosync/testmosync.h create mode 100644 plugins/samplemimo/testmosync/testmosyncgui.cpp create mode 100644 plugins/samplemimo/testmosync/testmosyncgui.h create mode 100644 plugins/samplemimo/testmosync/testmosyncgui.ui create mode 100644 plugins/samplemimo/testmosync/testmosyncplugin.cpp create mode 100644 plugins/samplemimo/testmosync/testmosyncplugin.h create mode 100644 plugins/samplemimo/testmosync/testmosyncsettings.cpp create mode 100644 plugins/samplemimo/testmosync/testmosyncsettings.h create mode 100644 plugins/samplemimo/testmosync/testmosyncthread.cpp create mode 100644 plugins/samplemimo/testmosync/testmosyncthread.h diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt index 07c626536..eb610d9e0 100644 --- a/plugins/samplemimo/CMakeLists.txt +++ b/plugins/samplemimo/CMakeLists.txt @@ -1,4 +1,8 @@ project(samplemimo) -add_subdirectory(bladerf2mimo) +if(ENABLE_BLADERF AND LIBBLADERF_FOUND) + add_subdirectory(bladerf2mimo) +endif() + add_subdirectory(testmi) +add_subdirectory(testmosync) diff --git a/plugins/samplemimo/testmosync/CMakeLists.txt b/plugins/samplemimo/testmosync/CMakeLists.txt new file mode 100644 index 000000000..19a96566d --- /dev/null +++ b/plugins/samplemimo/testmosync/CMakeLists.txt @@ -0,0 +1,55 @@ +project(testmosync) + +set(testmosync_SOURCES + testmosync.cpp + testmosyncplugin.cpp + testmosyncsettings.cpp + testmosyncthread.cpp +) + +set(testmosync_HEADERS + testmosync.h + testmosyncplugin.h + testmosyncsettings.h + testmosyncthread.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(testmosync_SOURCES + ${testmosync_SOURCES} + testmosyncgui.cpp + testmosyncgui.ui + ) + set(testmosync_HEADERS + ${testmosync_HEADERS} + testmosyncgui.h + ) + + set(TARGET_NAME outputtestmosync) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME outputtestmosyncsrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${testmosync_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/samplemimo/testmosync/testmosync.cpp b/plugins/samplemimo/testmosync/testmosync.cpp new file mode 100644 index 000000000..8a3458655 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosync.cpp @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include + +#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/devicesamplesink.h" + +#include "testmosyncthread.h" +#include "testmosync.h" + +MESSAGE_CLASS_DEFINITION(TestMOSync::MsgConfigureTestMOSync, Message) +MESSAGE_CLASS_DEFINITION(TestMOSync::MsgStartStop, Message) + +TestMOSync::TestMOSync(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_sinkThread(nullptr), + m_deviceDescription("TestMOSync"), + m_runningTx(false) +{ + m_mimoType = MIMOHalfSynchronous; + m_sampleMOFifo.init(2, 96000 * 4); + m_deviceAPI->setNbSourceStreams(0); + m_deviceAPI->setNbSinkStreams(2); +} + +TestMOSync::~TestMOSync() +{} + +void TestMOSync::destroy() +{ + delete this; +} + +void TestMOSync::init() +{ + applySettings(m_settings, true); +} + +bool TestMOSync::startTx() +{ + qDebug("TestMOSync::startTx"); + QMutexLocker mutexLocker(&m_mutex); + + if (m_runningTx) { + stopTx(); + } + + m_sinkThread = new TestMOSyncThread(); + m_sampleMOFifo.reset(); + m_sinkThread->setFifo(&m_sampleMOFifo); + m_sinkThread->setFcPos(m_settings.m_fcPosTx); + m_sinkThread->setLog2Interpolation(m_settings.m_log2Interp); + m_sinkThread->startWork(); + mutexLocker.unlock(); + m_runningTx = true; + + return true; +} + +void TestMOSync::stopTx() +{ + qDebug("TestMOSync::stopTx"); + + if (!m_sinkThread) { + return; + } + + QMutexLocker mutexLocker(&m_mutex); + + m_sinkThread->stopWork(); + delete m_sinkThread; + m_sinkThread = nullptr; + m_runningTx = false; +} + +QByteArray TestMOSync::serialize() const +{ + return m_settings.serialize(); +} + +bool TestMOSync::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureTestMOSync* message = MsgConfigureTestMOSync::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureTestMOSync* messageToGUI = MsgConfigureTestMOSync::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& TestMOSync::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int TestMOSync::getSourceSampleRate(int index) const +{ + return 0; +} + +int TestMOSync::getSinkSampleRate(int index) const +{ + (void) index; + int rate = m_settings.m_sampleRate; + return (rate / (1<push(messageToGUI); + } +} + +bool TestMOSync::handleMessage(const Message& message) +{ + if (MsgConfigureTestMOSync::match(message)) + { + MsgConfigureTestMOSync& conf = (MsgConfigureTestMOSync&) message; + qDebug() << "TestMOSync::handleMessage: MsgConfigureTestMOSync"; + + bool success = applySettings(conf.getSettings(), conf.getForce()); + + if (!success) { + qDebug("TestMOSync::handleMessage: config error"); + } + + return true; + } + else if (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "TestMOSync::handleMessage: " + << " " << (cmd.getRxElseTx() ? "Rx" : "Tx") + << " MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + bool startStopRxElseTx = cmd.getRxElseTx(); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initDeviceEngine(startStopRxElseTx ? 0 : 1)) { + m_deviceAPI->startDeviceEngine(startStopRxElseTx ? 0 : 1); + } + } + else + { + m_deviceAPI->stopDeviceEngine(startStopRxElseTx ? 0 : 1); + } + + return true; + } + else + { + return false; + } +} + +bool TestMOSync::applySettings(const TestMOSyncSettings& settings, bool force) +{ + QList reverseAPIKeys; + bool forwardChangeTxDSP = false; + + qDebug() << "TestMOSync::applySettings: common: " + << " m_sampleRate: " << settings.m_sampleRate + << " m_centerFrequency: " << settings.m_centerFrequency + << " m_log2Interp: " << settings.m_log2Interp + << " m_fcPosTx: " << settings.m_fcPosTx + << " force: " << force; + + if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + if (m_sinkThread) { + m_sinkThread->setSamplerate(settings.m_sampleRate); + } + + forwardChangeTxDSP = true; + } + + if ((m_settings.m_fcPosTx != settings.m_fcPosTx) || force) + { + if (m_sinkThread) { + m_sinkThread->setFcPos((int) settings.m_fcPosTx); + } + + forwardChangeTxDSP = true; + } + + if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) + { + if (m_sinkThread) + { + m_sinkThread->setLog2Interpolation(settings.m_log2Interp); + qDebug() << "BladeRF2Input::applySettings: set interpolation to " << (1<getDeviceEngineInputMessageQueue()->push(notif0); + DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, settings.m_centerFrequency, false, 1); + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1); + } + + m_settings = settings; + return true; +} + +int TestMOSync::webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + return 200; +} + +int TestMOSync::webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + MsgStartStop *message = MsgStartStop::create(run, true); // TODO: Tx support + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run, true); // TODO: Tx support + m_guiMessageQueue->push(msgToGUI); + } + + return 200; +} diff --git a/plugins/samplemimo/testmosync/testmosync.h b/plugins/samplemimo/testmosync/testmosync.h new file mode 100644 index 000000000..321289298 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosync.h @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNC_H_ +#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNC_H_ + +#include + +#include +#include + +#include "dsp/devicesamplemimo.h" +#include "testmosyncsettings.h" + +class DeviceAPI; +class TestMOSyncThread; + +class TestMOSync : public DeviceSampleMIMO { + Q_OBJECT + +public: + class MsgConfigureTestMOSync : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const TestMOSyncSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureTestMOSync* create(const TestMOSyncSettings& settings, bool force) + { + return new MsgConfigureTestMOSync(settings, force); + } + + private: + TestMOSyncSettings m_settings; + bool m_force; + + MsgConfigureTestMOSync(const TestMOSyncSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + bool getRxElseTx() const { return m_rxElseTx; } + + static MsgStartStop* create(bool startStop, bool rxElseTx) { + return new MsgStartStop(startStop, rxElseTx); + } + + protected: + bool m_startStop; + bool m_rxElseTx; + + MsgStartStop(bool startStop, bool rxElseTx) : + Message(), + m_startStop(startStop), + m_rxElseTx(rxElseTx) + { } + }; + + TestMOSync(DeviceAPI *deviceAPI); + virtual ~TestMOSync(); + virtual void destroy(); + + virtual void init(); + virtual bool startRx() {} + virtual void stopRx() {} + virtual bool startTx(); + virtual void stopTx(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + + virtual int getSourceSampleRate(int index) const; + virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } + virtual quint64 getSourceCenterFrequency(int index) const; + virtual void setSourceCenterFrequency(qint64 centerFrequency, int index); + + virtual int getSinkSampleRate(int index) const; + virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; } + virtual quint64 getSinkCenterFrequency(int index) const; + virtual void setSinkCenterFrequency(qint64 centerFrequency, int index); + + virtual quint64 getMIMOCenterFrequency() const { return getSourceCenterFrequency(0); } + + virtual bool handleMessage(const Message& message); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + bool getRxRunning() const { return false; } + bool getTxRunning() const { return m_runningTx; } + +private: + DeviceAPI *m_deviceAPI; + QMutex m_mutex; + TestMOSyncSettings m_settings; + TestMOSyncThread* m_sinkThread; + QString m_deviceDescription; + bool m_runningTx; + + bool applySettings(const TestMOSyncSettings& settings, bool force); +}; + +#endif // PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNC_H_ diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp new file mode 100644 index 000000000..684d2692f --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -0,0 +1,274 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include + +#include "plugin/pluginapi.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "mainwindow.h" +#include "device/deviceapi.h" +#include "device/deviceuiset.h" + +#include "testmosync.h" +#include "ui_testmosyncgui.h" +#include "testmosyncgui.h" + +TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : + QWidget(parent), + ui(new Ui::TestMOSyncGui), + m_deviceUISet(deviceUISet), + m_doApplySettings(true), + m_forceSettings(true), + m_settings(), + m_sampleMIMO(nullptr), + m_sampleRate(0), + m_generation(false), + m_samplesCount(0), + m_tickCount(0), + m_lastEngineState(DeviceAPI::StNotStarted) +{ + ui->setupUi(this); + + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->centerFrequency->setValueRange(7, 0, pow(10,7)); + + ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->sampleRate->setValueRange(7, 32000U, 9000000U); + + connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(500); + + displaySettings(); + + m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); + m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); +} + +TestMOSyncGui::~TestMOSyncGui() +{ + delete ui; +} + +void TestMOSyncGui::destroy() +{ + delete this; +} + +void TestMOSyncGui::setName(const QString& name) +{ + setObjectName(name); +} + +QString TestMOSyncGui::getName() const +{ + return objectName(); +} + +void TestMOSyncGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 TestMOSyncGui::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void TestMOSyncGui::setCenterFrequency(qint64 centerFrequency) +{ + m_settings.m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray TestMOSyncGui::serialize() const +{ + return m_settings.serialize(); +} + +bool TestMOSyncGui::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool TestMOSyncGui::handleMessage(const Message& message) +{ + if (TestMOSync::MsgConfigureTestMOSync::match(message)) + { + qDebug("TestMOSyncGui::handleMessage: message: MsgConfigureTestMOSync"); + const TestMOSync::MsgConfigureTestMOSync& cfg = (TestMOSync::MsgConfigureTestMOSync&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else if (TestMOSync::MsgStartStop::match(message)) + { + TestMOSync::MsgStartStop& notif = (TestMOSync::MsgStartStop&) message; + qDebug("TestMOSyncGui::handleMessage: message: MsgStartStop: %s", notif.getStartStop() ? "start" : "stop"); + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +void TestMOSyncGui::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + + if (DSPSignalNotification::match(*message)) + { + DSPSignalNotification* notif = (DSPSignalNotification*) message; + qDebug("TestMOSyncGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency()); + m_sampleRate = notif->getSampleRate(); + m_deviceCenterFrequency = notif->getCenterFrequency(); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) + { + delete message; + } + } + } +} + +void TestMOSyncGui::updateSampleRateAndFrequency() +{ + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate*(1<centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->sampleRate->setValue(m_settings.m_sampleRate); +} + +void TestMOSyncGui::sendSettings() +{ + if (!m_updateTimer.isActive()) { + m_updateTimer.start(100); + } +} + +void TestMOSyncGui::updateHardware() +{ + qDebug() << "TestMOSyncGui::updateHardware"; + TestMOSync::MsgConfigureTestMOSync* message = TestMOSync::MsgConfigureTestMOSync::create(m_settings, m_forceSettings); + m_sampleMIMO->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); +} + +void TestMOSyncGui::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; + } +} + +void TestMOSyncGui::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + sendSettings(); +} + +void TestMOSyncGui::on_sampleRate_changed(quint64 value) +{ + m_settings.m_sampleRate = value; + sendSettings(); +} + +void TestMOSyncGui::on_interp_currentIndexChanged(int index) +{ + if (index < 0) { + return; + } + + m_settings.m_log2Interp = index; + updateSampleRateAndFrequency(); + sendSettings(); +} + +void TestMOSyncGui::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + TestMOSync::MsgStartStop *message = TestMOSync::MsgStartStop::create(checked, false); + m_sampleMIMO->getInputMessageQueue()->push(message); + } +} + +void TestMOSyncGui::tick() +{ +} diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h new file mode 100644 index 000000000..7d38b7083 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_TESTMOSYNCGUI_H +#define INCLUDE_TESTMOSYNCGUI_H + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "testmosyncsettings.h" + + +class DeviceUISet; +class TestMOSync; + +namespace Ui { + class TestMOSyncGui; +} + +class TestMOSyncGui : public QWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + explicit TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr); + virtual ~TestMOSyncGui(); + 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::TestMOSyncGui* ui; + + DeviceUISet* m_deviceUISet; + bool m_doApplySettings; + bool m_forceSettings; + TestMOSyncSettings m_settings; + QTimer m_updateTimer; + QTimer m_statusTimer; + TestMOSync* m_sampleMIMO; + int m_sampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + bool m_generation; + int m_samplesCount; + std::size_t m_tickCount; + int m_lastEngineState; + MessageQueue m_inputMessageQueue; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + +private slots: + void handleInputMessages(); + void on_centerFrequency_changed(quint64 value); + void on_sampleRate_changed(quint64 value); + void on_startStop_toggled(bool checked); + void on_interp_currentIndexChanged(int index); + void updateHardware(); + void updateStatus(); + void tick(); +}; + +#endif // INCLUDE_TESTMOSYNCGUI_H diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui new file mode 100644 index 000000000..ecc7611b4 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -0,0 +1,331 @@ + + + TestMOSyncGui + + + + 0 + 0 + 350 + 190 + + + + + 0 + 0 + + + + + 350 + 190 + + + + + Liberation Sans + 9 + + + + Test MO Sync + + + Test MO Sync + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + + + + + start/stop generation + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + + + + + 50 + 0 + + + + I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + true + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 20 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Record center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + 4 + + + + + Int + + + + + + + Interpolation + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
diff --git a/plugins/samplemimo/testmosync/testmosyncplugin.cpp b/plugins/samplemimo/testmosync/testmosyncplugin.cpp new file mode 100644 index 000000000..7fef0e3a4 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncplugin.cpp @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" + +#ifndef SERVER_MODE +#include "testmosyncgui.h" +#endif +#include "testmosync.h" +#include "testmosyncplugin.h" + +const PluginDescriptor TestMOSyncPlugin::m_pluginDescriptor = { + QString("Test Synchronous Multiple Output"), + QString("5.0.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +const QString TestMOSyncPlugin::m_hardwareID = "TestMOSync"; +const QString TestMOSyncPlugin::m_deviceTypeID = TESTMOSYNC_DEVICE_TYPE_ID; + +TestMOSyncPlugin::TestMOSyncPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& TestMOSyncPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void TestMOSyncPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleMIMO(m_deviceTypeID, this); +} + +void TestMOSyncPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices) +{ + if (listedHwIds.contains(m_hardwareID)) { // check if it was done + return; + } + + originDevices.append(OriginDevice( + "TestMOSync", // Displayable name + m_hardwareID, // Hardware ID + QString(), // Serial + 0, // Sequence + 0, // Number of Rx streams + 2 // Number of Tx streams + )); + + listedHwIds.append(m_hardwareID); +} + +PluginInterface::SamplingDevices TestMOSyncPlugin::enumSampleMIMO(const OriginDevices& originDevices) +{ + SamplingDevices result; + + for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it) + { + if (it->hardwareId == m_hardwareID) + { + result.append(SamplingDevice( + "TestMOSync", + m_hardwareID, + m_deviceTypeID, + it->serial, + it->sequence, + PluginInterface::SamplingDevice::BuiltInDevice, + PluginInterface::SamplingDevice::StreamMIMO, + 1, // MIMO is always considered as a single device + 0 + )); + } + } + + return result; +} + +#ifdef SERVER_MODE +PluginInstanceGUI* TestMOSyncPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + (void) sourceId; + (void) widget; + (void) deviceUISet; + return 0; +} +#else +PluginInstanceGUI* TestMOSyncPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if (sourceId == m_deviceTypeID) + { + TestMOSyncGui* gui = new TestMOSyncGui(deviceUISet); + *widget = gui; + return gui; + } + else + { + return nullptr; + } +} +#endif + +DeviceSampleMIMO *TestMOSyncPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI) +{ + if (mimoId == m_deviceTypeID) + { + TestMOSync* output = new TestMOSync(deviceAPI); + return output; + } + else + { + return nullptr; + } +} + +DeviceWebAPIAdapter *TestMOSyncPlugin::createDeviceWebAPIAdapter() const +{ + return nullptr; +} diff --git a/plugins/samplemimo/testmosync/testmosyncplugin.h b/plugins/samplemimo/testmosync/testmosyncplugin.h new file mode 100644 index 000000000..6cb6ca82c --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncplugin.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef _TESTMOSYNC_TESTMOSYNCPLUGIN_H +#define _TESTMOSYNC_TESTMOSYNCPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class PluginAPI; + +#define TESTMOSYNC_DEVICE_TYPE_ID "sdrangel.samplemimo.testmosync" + +class TestMOSyncPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID TESTMOSYNC_DEVICE_TYPE_ID) + +public: + explicit TestMOSyncPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices); + virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices); + virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const; + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // _TESTMI_TESTMIPLUGIN_H diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.cpp b/plugins/samplemimo/testmosync/testmosyncsettings.cpp new file mode 100644 index 000000000..a09b4960d --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncsettings.cpp @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/simpleserializer.h" +#include "testmosyncsettings.h" + +const unsigned int TestMOSyncSettings::m_msThrottle = 50U; +const unsigned int TestMOSyncSettings::m_blockSize = 16384U; + +TestMOSyncSettings::TestMOSyncSettings() +{ + resetToDefaults(); +} + +void TestMOSyncSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_sampleRate = 48000; + m_log2Interp = 0; + m_fcPosTx = FC_POS_CENTER; +} + +QByteArray TestMOSyncSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeU64(1, m_sampleRate); + s.writeU32(2, m_log2Interp); + s.writeS32(3, (int) m_fcPosTx); + + return s.final(); +} + +bool TestMOSyncSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + + d.readU64(1, &m_sampleRate, 48000); + d.readU32(2, &m_log2Interp, 0); + d.readS32(38, &intval, 2); + m_fcPosTx = (fcPos_t) intval; + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.h b/plugins/samplemimo/testmosync/testmosyncsettings.h new file mode 100644 index 000000000..74210f4c9 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncsettings.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ +#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ + +#include + +struct TestMOSyncSettings { + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + quint64 m_centerFrequency; + quint64 m_sampleRate; + quint32 m_log2Interp; + fcPos_t m_fcPosTx; + + static const unsigned int m_msThrottle; + static const unsigned int m_blockSize; + + TestMOSyncSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ */ diff --git a/plugins/samplemimo/testmosync/testmosyncthread.cpp b/plugins/samplemimo/testmosync/testmosyncthread.cpp new file mode 100644 index 000000000..9920264bc --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncthread.cpp @@ -0,0 +1,290 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "dsp/samplemofifo.h" + +#include "testmosyncsettings.h" +#include "testmosyncthread.h" + +TestMOSyncThread::TestMOSyncThread(QObject* parent) : + QThread(parent), + m_running(false), + m_log2Interp(0), + m_throttlems(TestMOSyncSettings::m_msThrottle), + m_throttleToggle(false), + m_samplesRemainder(0) +{ + qDebug("TestMOSyncThread::TestMOSyncThread"); + m_buf = new qint16[2*TestMOSyncSettings::m_blockSize*2]; +} + +TestMOSyncThread::~TestMOSyncThread() +{ + qDebug("TestMOSyncThread::~TestMOSyncThread"); + + if (m_running) { + stopWork(); + } + + delete[] m_buf; +} + +void TestMOSyncThread::startWork() +{ + m_startWaitMutex.lock(); + m_elapsedTimer.start(); + start(); + + while(!m_running) { + m_startWaiter.wait(&m_startWaitMutex, 100); + } + + m_startWaitMutex.unlock(); +} + +void TestMOSyncThread::stopWork() +{ + m_running = false; + wait(); +} + +void TestMOSyncThread::run() +{ + m_running = true; + m_startWaiter.wakeAll(); + + while(m_running) // actual work is in the tick() function + { + sleep(1); + } + + m_running = false; +} + +void TestMOSyncThread::connectTimer(const QTimer& timer) +{ + qDebug() << "TestMOSyncThread::connectTimer"; + connect(&timer, SIGNAL(timeout()), this, SLOT(tick())); +} + +void TestMOSyncThread::setSamplerate(int samplerate) +{ + if (samplerate != m_samplerate) + { + qDebug() << "TestMOSyncThread::setSamplerate:" + << " new:" << samplerate + << " old:" << m_samplerate; + + bool wasRunning = false; + + if (m_running) + { + stopWork(); + wasRunning = true; + } + + m_samplerate = samplerate; + m_samplesChunkSize = (m_samplerate * m_throttlems) / 1000; + + if (wasRunning) { + startWork(); + } + } +} + +void TestMOSyncThread::setLog2Interpolation(unsigned int log2Interpolation) +{ + if ((log2Interpolation < 0) || (log2Interpolation > 6)) { + return; + } + + if (log2Interpolation != m_log2Interp) + { + qDebug() << "TestSinkThread::setLog2Interpolation:" + << " new:" << log2Interpolation + << " old:" << m_log2Interp; + + bool wasRunning = false; + + if (m_running) + { + stopWork(); + wasRunning = true; + } + + m_log2Interp = log2Interpolation; + + if (wasRunning) { + startWork(); + } + } +} + +unsigned int TestMOSyncThread::getLog2Interpolation() const +{ + return m_log2Interp; +} + +void TestMOSyncThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +int TestMOSyncThread::getFcPos() const +{ + return m_fcPos; +} + +void TestMOSyncThread::callback(qint16* buf, qint32 samplesPerChannel) +{ + unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End; + m_sampleFifo->readSync(samplesPerChannel/(1< decim=16). len is a number of samples (not a number of I or Q) +void TestMOSyncThread::callbackPart(qint16* buf, qint32 samplesPerChannel, int iBegin, qint32 nSamples) +{ + for (unsigned int channel = 0; channel < 2; channel++) + { + SampleVector::iterator begin = m_sampleFifo->getData(channel).begin() + iBegin; + + if (m_log2Interp == 0) + { + m_interpolators[channel].interpolate1(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 2: + m_interpolators[channel].interpolate4_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 3: + m_interpolators[channel].interpolate8_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 4: + m_interpolators[channel].interpolate16_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 5: + m_interpolators[channel].interpolate32_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 6: + m_interpolators[channel].interpolate64_inf(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 2: + m_interpolators[channel].interpolate4_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 3: + m_interpolators[channel].interpolate8_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 4: + m_interpolators[channel].interpolate16_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 5: + m_interpolators[channel].interpolate32_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 6: + m_interpolators[channel].interpolate64_sup(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 2: + m_interpolators[channel].interpolate4_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 3: + m_interpolators[channel].interpolate8_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 4: + m_interpolators[channel].interpolate16_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 5: + m_interpolators[channel].interpolate32_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + case 6: + m_interpolators[channel].interpolate64_cen(&begin, &buf[channel*2*samplesPerChannel], nSamples*2); + break; + default: + break; + } + } + } + } +} + +void TestMOSyncThread::tick() +{ + if (m_running) + { + qint64 throttlems = m_elapsedTimer.restart(); + + if (throttlems != m_throttlems) + { + m_throttlems = throttlems; + m_samplesChunkSize = (m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000; + m_throttleToggle = !m_throttleToggle; + } + + int chunkSize = std::min((int) m_samplesChunkSize, m_samplerate) + m_samplesRemainder; + + while (chunkSize >= TestMOSyncSettings::m_blockSize) + { + callback(m_buf, TestMOSyncSettings::m_blockSize); + chunkSize -= TestMOSyncSettings::m_blockSize; + } + + m_samplesRemainder = chunkSize; + } +} diff --git a/plugins/samplemimo/testmosync/testmosyncthread.h b/plugins/samplemimo/testmosync/testmosyncthread.h new file mode 100644 index 000000000..552b9ae52 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncthread.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCTHREAD_H_ +#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCTHREAD_H_ + +// configure two Tx + +#include +#include +#include +#include + +#include "dsp/interpolators.h" + +#define TESTMOSYNC_THROTTLE_MS 50 + +class QTimer; +class SampleMOFifo; + +class TestMOSyncThread : public QThread { + Q_OBJECT + +public: + TestMOSyncThread(QObject* parent = nullptr); + ~TestMOSyncThread(); + + void startWork(); + void stopWork(); + bool isRunning() const { return m_running; } + void setSamplerate(int samplerate); + void setLog2Interpolation(unsigned int log2_interp); + unsigned int getLog2Interpolation() const; + void setFcPos(int fcPos); + int getFcPos() const; + void setFifo(SampleMOFifo *sampleFifo) { m_sampleFifo = sampleFifo; } + SampleMOFifo *getFifo() { return m_sampleFifo; } + void connectTimer(const QTimer& timer); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + qint16 *m_buf; //!< Full buffer for SISO or MIMO operation + SampleMOFifo* m_sampleFifo; + Interpolators m_interpolators[2]; + unsigned int m_log2Interp; + int m_fcPos; + + int m_throttlems; + QElapsedTimer m_elapsedTimer; + bool m_throttleToggle; + unsigned int m_samplesChunkSize; + unsigned int m_samplesRemainder; + int m_samplerate; + + void run(); + unsigned int getNbFifos(); + void callbackPart(qint16* buf, qint32 samplesPerChannel, int iBegin, qint32 nSamples); + void callback(qint16* buf, qint32 samplesPerChannel); + +private slots: + void tick(); +}; + +#endif // PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCTHREAD_H_