diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt index b5099ce54..eb610d9e0 100644 --- a/plugins/samplemimo/CMakeLists.txt +++ b/plugins/samplemimo/CMakeLists.txt @@ -5,3 +5,4 @@ if(ENABLE_BLADERF AND LIBBLADERF_FOUND) 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..975274751 --- /dev/null +++ b/plugins/samplemimo/testmosync/CMakeLists.txt @@ -0,0 +1,55 @@ +project(testmosync) + +set(testmosync_SOURCES + testmosync.cpp + testmosyncplugin.cpp + testmosyncsettings.cpp + testmosyncworker.cpp +) + +set(testmosync_HEADERS + testmosync.h + testmosyncplugin.h + testmosyncsettings.h + testmosyncworker.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/readme.md b/plugins/samplemimo/testmosync/readme.md new file mode 100644 index 000000000..8de87ec99 --- /dev/null +++ b/plugins/samplemimo/testmosync/readme.md @@ -0,0 +1,49 @@ +

Test Multiple Output synchronized plugin

+ +

Introduction

+ +This is a v5 only plugin. + +This MO (Multiple Output) sample sink plugin sends its samples to a spectrum display. It features the synchronous pulling of samples from two baseband channels provided by a MIMO channel plugin (thus its "MO" part). Streams 0 and 1 are connected to streams 0 and 1 of the MIMO channel respectively. + +

Build

+ +The plugin is always built. + +

Interface

+ +![Test MO sync plugin GUI](../../../doc/img/TestMOSync_plugin.png) + +

1: Start/Stop

+ +Device start / stop button. + + - Blue triangle icon: device is ready and can be started + - Red square icon: device is running and can be stopped + - Magenta (or pink) square icon: an error occurred + +

2: Frequency

+ +This is the center frequency in kHz that will be put in the file header. + +

3: Output stream sample rate

+ +This is the output stream sample rate in kS/s after interpolation (5) from the baseband stream. Thus this is the sample rate (6) multiplied by the interpolation factor (5). + +

4: Stream selection

+ +This combo selects to which stream the UI applies for stream specific controls. + +

5: Interpolation factor

+ +The baseband streams are interpolated by this value before being sent to spectrum display. It can vary in powers of two from 1 (no interpolation) to 64. + +

6: Baseband sample rate

+ +This is the baseband sample rate before interpolation in S/s. + +Use the wheels to adjust the sample rate. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

7: Spectrum display

+ +This is the final output stream spectrum display after interpolation (5). This would be sent to the hardware device. Controls on the bottom of the panel are identical to the ones of the main spectrum display. \ No newline at end of file diff --git a/plugins/samplemimo/testmosync/testmosync.cpp b/plugins/samplemimo/testmosync/testmosync.cpp new file mode 100644 index 000000000..b1244b915 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosync.cpp @@ -0,0 +1,427 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "testmosyncworker.h" +#include "testmosync.h" + +MESSAGE_CLASS_DEFINITION(TestMOSync::MsgConfigureTestMOSync, Message) +MESSAGE_CLASS_DEFINITION(TestMOSync::MsgStartStop, Message) + +TestMOSync::TestMOSync(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_spectrumVis(SDR_TX_SCALEF), + m_settings(), + m_sinkWorker(nullptr), + m_deviceDescription("TestMOSync"), + m_runningTx(false), + m_masterTimer(deviceAPI->getMasterTimer()), + m_feedSpectrumIndex(0) +{ + m_mimoType = MIMOHalfSynchronous; + m_sampleMOFifo.init(2, SampleMOFifo::getSizePolicy(m_settings.m_sampleRate)); + 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_sinkWorker = new TestMOSyncWorker(); + m_sinkWorker->moveToThread(&m_sinkWorkerThread); + m_sampleMOFifo.reset(); + m_sinkWorker->setFifo(&m_sampleMOFifo); + m_sinkWorker->setFcPos(m_settings.m_fcPosTx); + m_sinkWorker->setSamplerate(m_settings.m_sampleRate); + m_sinkWorker->setLog2Interpolation(m_settings.m_log2Interp); + m_sinkWorker->setSpectrumSink(&m_spectrumVis); + m_sinkWorker->setFeedSpectrumIndex(m_feedSpectrumIndex); + m_sinkWorker->connectTimer(m_masterTimer); + startWorker(); + mutexLocker.unlock(); + m_runningTx = true; + + return true; +} + +void TestMOSync::stopTx() +{ + qDebug("TestMOSync::stopTx"); + + if (!m_sinkWorker) { + return; + } + + QMutexLocker mutexLocker(&m_mutex); + + stopWorker(); + delete m_sinkWorker; + m_sinkWorker = nullptr; + m_runningTx = false; +} + +void TestMOSync::startWorker() +{ + m_sinkWorker->startWork(); + m_sinkWorkerThread.start(); +} + +void TestMOSync::stopWorker() +{ + m_sinkWorker->stopWork(); + m_sinkWorkerThread.quit(); + m_sinkWorkerThread.wait(); +} + +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; + } +} + +void TestMOSync::setFeedSpectrumIndex(unsigned int feedSpectrumIndex) +{ + m_feedSpectrumIndex = feedSpectrumIndex > 1 ? 1 : feedSpectrumIndex; + + if (m_sinkWorker) { + m_sinkWorker->setFeedSpectrumIndex(m_feedSpectrumIndex); + } +} + +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_centerFrequency != settings.m_centerFrequency) || force) + { + forwardChangeTxDSP = true; + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) + || (m_settings.m_log2Interp != settings.m_log2Interp) || force) + { + m_sampleMOFifo.resize(SampleMOFifo::getSizePolicy(m_settings.m_sampleRate)); + } + + if ((m_settings.m_sampleRate != settings.m_sampleRate) || force) + { + if (m_sinkWorker) { + m_sinkWorker->setSamplerate(settings.m_sampleRate); + } + + forwardChangeTxDSP = true; + } + + if ((m_settings.m_fcPosTx != settings.m_fcPosTx) || force) + { + if (m_sinkWorker) { + m_sinkWorker->setFcPos((int) settings.m_fcPosTx); + } + + forwardChangeTxDSP = true; + } + + if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) + { + if (m_sinkWorker) + { + m_sinkWorker->setLog2Interpolation(settings.m_log2Interp); + qDebug() << "TestMOSync::applySettings: set interpolation to " << (1<getDeviceEngineInputMessageQueue()->push(notif0); + DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(settings.m_sampleRate, settings.m_centerFrequency, false, 1); + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1); + } + + m_settings = settings; + return true; +} + +int TestMOSync::webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + (void) errorMessage; + if (subsystemIndex == 1) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState(), 1); // Tx only + return 200; + } + else + { + errorMessage = QString("Subsystem index invalid: expect 1 (Tx) only"); + return 404; + } + + response.setState(new QString("N/A")); + return 200; +} + +int TestMOSync::webapiRun( + bool run, + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + if (subsystemIndex == 1) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState()); + MsgStartStop *message = MsgStartStop::create(run, true); // Tx only + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run, true); // Tx only + m_guiMessageQueue->push(msgToGUI); + } + + return 200; + } + else + { + errorMessage = QString("Subsystem index invalid: expect 1 (Tx) only"); + return 404; + } +} + +int TestMOSync::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setTestMoSyncSettings(new SWGSDRangel::SWGTestMOSyncSettings()); + response.getTestMoSyncSettings()->init(); + webapiFormatDeviceSettings(response, m_settings); + return 200; +} + +int TestMOSync::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) errorMessage; + TestMOSyncSettings settings = m_settings; + webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response); + + MsgConfigureTestMOSync *msg = MsgConfigureTestMOSync::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureTestMOSync *msgToGUI = MsgConfigureTestMOSync::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatDeviceSettings(response, settings); + return 200; +} + +void TestMOSync::webapiFormatDeviceSettings( + SWGSDRangel::SWGDeviceSettings& response, + const TestMOSyncSettings& settings) +{ + response.getTestMoSyncSettings()->setCenterFrequency(settings.m_centerFrequency); + response.getTestMoSyncSettings()->setFcPosTx((int) settings.m_fcPosTx); + response.getTestMoSyncSettings()->setLog2Interp(settings.m_log2Interp); + response.getTestMoSyncSettings()->setSampleRate(settings.m_sampleRate); +} + +void TestMOSync::webapiUpdateDeviceSettings( + TestMOSyncSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response) +{ + if (deviceSettingsKeys.contains("centerFrequency")) { + settings.m_centerFrequency = response.getTestMoSyncSettings()->getCenterFrequency(); + } + if (deviceSettingsKeys.contains("fcPosTx")) { + settings.m_fcPosTx = (TestMOSyncSettings::fcPos_t) response.getTestMoSyncSettings()->getFcPosTx(); + } + if (deviceSettingsKeys.contains("log2Interp")) { + settings.m_log2Interp = response.getTestMoSyncSettings()->getLog2Interp(); + } + if (deviceSettingsKeys.contains("sampleRate")) { + settings.m_sampleRate = response.getTestMoSyncSettings()->getSampleRate(); + } +} \ No newline at end of file diff --git a/plugins/samplemimo/testmosync/testmosync.h b/plugins/samplemimo/testmosync/testmosync.h new file mode 100644 index 000000000..76caf2587 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosync.h @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 +#include + +#include "dsp/devicesamplemimo.h" +#include "dsp/spectrumvis.h" +#include "testmosyncsettings.h" + +class DeviceAPI; +class TestMOSyncWorker; +class BasebandSampleSink; + +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() { return false; } + 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 unsigned int getMIMOSampleRate() const { return getSinkSampleRate(0); } + + virtual bool handleMessage(const Message& message); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + static void webapiFormatDeviceSettings( + SWGSDRangel::SWGDeviceSettings& response, + const TestMOSyncSettings& settings); + + static void webapiUpdateDeviceSettings( + TestMOSyncSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response); + + SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } + bool getRxRunning() const { return false; } + bool getTxRunning() const { return m_runningTx; } + void setFeedSpectrumIndex(unsigned int feedSpectrumIndex); + +private: + DeviceAPI *m_deviceAPI; + QMutex m_mutex; + SpectrumVis m_spectrumVis; + TestMOSyncSettings m_settings; + TestMOSyncWorker* m_sinkWorker; + QThread m_sinkWorkerThread; + QString m_deviceDescription; + bool m_runningTx; + const QTimer& m_masterTimer; + unsigned int m_feedSpectrumIndex; + + void startWorker(); + void stopWorker(); + 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..7f3e8198e --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -0,0 +1,285 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "dsp/spectrumvis.h" +#include "device/deviceapi.h" +#include "device/deviceuiset.h" +#include "maincore.h" + +#include "testmosync.h" +#include "ui_testmosyncgui.h" +#include "testmosyncgui.h" + +TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : + DeviceGUI(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); + m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); + + 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); + + m_spectrumVis = m_sampleMIMO->getSpectrumVis(); + m_spectrumVis->setGLSpectrum(ui->glSpectrum); + ui->glSpectrum->setCenterFrequency(m_settings.m_centerFrequency); + ui->glSpectrum->setSampleRate(m_settings.m_sampleRate*(1<glSpectrum->connectTimer(MainCore::instance()->getMasterTimer()); + ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); + + 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->setMessageQueueToGUI(&m_inputMessageQueue); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + + m_deviceUISet->m_spectrum->setDisplayedStream(false, 0); + m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, 0); + m_deviceUISet->setSpectrumScalingFactor(SDR_TX_SCALEF); +} + +TestMOSyncGui::~TestMOSyncGui() +{ + delete ui; +} + +void TestMOSyncGui::destroy() +{ + delete this; +} + +void TestMOSyncGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + 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 (DSPMIMOSignalNotification::match(message)) + { + const DSPMIMOSignalNotification& cfg = (DSPMIMOSignalNotification&) message; + int istream = cfg.getIndex(); + bool sourceOrSink = cfg.getSourceOrSink(); + qDebug("TestMOSyncGui::handleMessage: DSPMIMOSignalNotification: %s:%d SampleRate:%d, CenterFrequency:%llu", + sourceOrSink ? "Rx" : "Tx", + istream, + cfg.getSampleRate(), + cfg.getCenterFrequency() + ); + + if (!sourceOrSink) + { + m_sampleRate = cfg.getSampleRate(); + m_deviceCenterFrequency = cfg.getCenterFrequency(); + updateSampleRateAndFrequency(); + } + + return true; + } + else 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 (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() +{ + if (m_doApplySettings) + { + 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(1); + + 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; + ui->glSpectrum->setCenterFrequency(m_settings.m_centerFrequency); + sendSettings(); +} + +void TestMOSyncGui::on_sampleRate_changed(quint64 value) +{ + m_settings.m_sampleRate = value; + ui->glSpectrum->setSampleRate(m_settings.m_sampleRate*(1<glSpectrum->setSampleRate(m_settings.m_sampleRate*(1<getInputMessageQueue()->push(message); + } +} + +void TestMOSyncGui::on_spectrumIndex_currentIndexChanged(int index) +{ + m_deviceUISet->m_spectrum->setDisplayedStream(false, index); + m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, index); + m_sampleMIMO->setFeedSpectrumIndex(index); +} + +void TestMOSyncGui::tick() +{ +} diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h new file mode 100644 index 000000000..e6b066d0b --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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; +class SpectrumVis; + +namespace Ui { + class TestMOSyncGui; +} + +class TestMOSyncGui : public DeviceGUI { + Q_OBJECT + +public: + explicit TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr); + virtual ~TestMOSyncGui(); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + +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; + SpectrumVis* m_spectrumVis; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void sendSettings(); + void updateSampleRateAndFrequency(); + bool handleMessage(const Message& message); + +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 on_spectrumIndex_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..159663ced --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -0,0 +1,399 @@ + + + TestMOSyncGui + + + + 0 + 0 + 350 + 400 + + + + + 0 + 0 + + + + + 350 + 400 + + + + + 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 + + + + + + + + :/dsb.png + + + + + + + + 35 + 16777215 + + + + Select which stream index to display spectrum + + + + 0 + + + + + 1 + + + + + + + + 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 + + + + + + + + + + + + 200 + 200 + + + + + Liberation Mono + 8 + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + GLSpectrum + QWidget +
gui/glspectrum.h
+ 1 +
+ + GLSpectrumGUI + QWidget +
gui/glspectrumgui.h
+ 1 +
+
+ + + + +
diff --git a/plugins/samplemimo/testmosync/testmosyncplugin.cpp b/plugins/samplemimo/testmosync/testmosyncplugin.cpp new file mode 100644 index 000000000..411585a59 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncplugin.cpp @@ -0,0 +1,146 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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("TestMOSync"), + QString("Test Synchronous Multiple Output"), + QString("5.13.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 +DeviceGUI* TestMOSyncPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + (void) sourceId; + (void) widget; + (void) deviceUISet; + return 0; +} +#else +DeviceGUI* 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..4cd0b4485 --- /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 DeviceGUI* 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..596f90432 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncsettings.cpp @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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; + +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..dc40e9c5a --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncsettings.h @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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; + + TestMOSyncSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ */ diff --git a/plugins/samplemimo/testmosync/testmosyncworker.cpp b/plugins/samplemimo/testmosync/testmosyncworker.cpp new file mode 100644 index 000000000..2065d1794 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncworker.cpp @@ -0,0 +1,359 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "dsp/basebandsamplesink.h" + +#include "testmosyncsettings.h" +#include "testmosyncworker.h" + +TestMOSyncWorker::TestMOSyncWorker(QObject* parent) : + QObject(parent), + m_running(false), + m_buf(nullptr), + m_log2Interp(0), + m_throttlems(TestMOSyncSettings::m_msThrottle), + m_throttleToggle(false), + m_samplesRemainder(0), + m_samplerate(0), + m_feedSpectrumIndex(0), + m_spectrumSink(nullptr) +{ + qDebug("TestMOSyncWorker::TestMOSyncWorker"); + setSamplerate(48000); +} + +TestMOSyncWorker::~TestMOSyncWorker() +{ + qDebug("TestMOSyncWorker::~TestMOSyncWorker"); + + if (m_running) { + stopWork(); + } + + delete[] m_buf; +} + +void TestMOSyncWorker::startWork() +{ + qDebug("TestMOSyncWorker::startWork"); + m_elapsedTimer.start(); + m_running = true; +} + +void TestMOSyncWorker::stopWork() +{ + qDebug("TestMOSyncWorker::stopWork"); + m_running = false; +} + +void TestMOSyncWorker::connectTimer(const QTimer& timer) +{ + qDebug() << "TestMOSyncWorker::connectTimer"; + connect(&timer, SIGNAL(timeout()), this, SLOT(tick())); +} + +void TestMOSyncWorker::setSamplerate(int samplerate) +{ + if (samplerate != m_samplerate) + { + qDebug() << "TestMOSyncWorker::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; + m_blockSize = (m_samplerate * 50) / 1000; + + if (m_buf) { + delete[] m_buf; + } + + m_buf = new qint16[2*m_blockSize*2]; + + if (wasRunning) { + startWork(); + } + } +} + +void TestMOSyncWorker::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 TestMOSyncWorker::getLog2Interpolation() const +{ + return m_log2Interp; +} + +void TestMOSyncWorker::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +int TestMOSyncWorker::getFcPos() const +{ + return m_fcPos; +} + +void TestMOSyncWorker::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 TestMOSyncWorker::callbackPart(qint16* buf, qint32 nSamples, int iBegin) +{ + 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*nSamples], 2*nSamples); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + } + + if (channel == m_feedSpectrumIndex) { + feedSpectrum(&buf[channel*2*nSamples], nSamples*2); + } + } +} + +void TestMOSyncWorker::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; + } + + unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End; + std::vector& data = m_sampleFifo->getData(); + m_sampleFifo->readSync(m_samplesChunkSize, iPart1Begin, iPart1End, iPart2Begin, iPart2End); + + if (iPart1Begin != iPart1End) { + callbackPart(data, iPart1Begin, iPart1End); + } + + if (iPart2Begin != iPart2End) { + callbackPart(data, iPart2Begin, iPart2End); + } + } +} + +void TestMOSyncWorker::callbackPart(std::vector& data, unsigned int iBegin, unsigned int iEnd) +{ + unsigned int chunkSize = iEnd - iBegin; + + for (unsigned int channel = 0; channel < 2; channel++) + { + SampleVector::iterator beginRead = data[channel].begin() + iBegin; + + if (m_log2Interp == 0) + { + m_interpolators[channel].interpolate1(&beginRead, m_buf, 2*chunkSize); + + if (channel == m_feedSpectrumIndex) { + feedSpectrum(m_buf, 2*chunkSize); + } + } + else + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_cen(&beginRead, m_buf, chunkSize*(1< Sample { + return Sample{s.m_real, s.m_imag}; + } + ); + + m_spectrumSink->feed(m_samplesVector.m_vector.begin(), m_samplesVector.m_vector.begin() + (bufSize/2), false); +} diff --git a/plugins/samplemimo/testmosync/testmosyncworker.h b/plugins/samplemimo/testmosync/testmosyncworker.h new file mode 100644 index 000000000..1c8a964e6 --- /dev/null +++ b/plugins/samplemimo/testmosync/testmosyncworker.h @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_TESTMOSYNCWORKER_H_ +#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCWORKER_H_ + +// configure two Tx + +#include +#include + +#include "dsp/interpolators.h" +#include "util/incrementalvector.h" + +#define TESTMOSYNC_THROTTLE_MS 50 + +class QTimer; +class SampleMOFifo; +class BasebandSampleSink; + +class TestMOSyncWorker : public QObject { + Q_OBJECT + +public: + TestMOSyncWorker(QObject* parent = nullptr); + ~TestMOSyncWorker(); + + 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); + void setSpectrumSink(BasebandSampleSink *spectrumSink) { m_spectrumSink = spectrumSink; } + void setFeedSpectrumIndex(unsigned int feedSpectrumIndex) { m_feedSpectrumIndex = feedSpectrumIndex > 1 ? 1 : feedSpectrumIndex; } + +private: +#pragma pack(push, 1) + struct Sample16 + { + int16_t m_real; + int16_t m_imag; + }; +#pragma pack(pop) + 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_blockSize; + unsigned int m_samplesRemainder; + int m_samplerate; + + unsigned int m_feedSpectrumIndex; + BasebandSampleSink* m_spectrumSink; + IncrementalVector m_samplesVector; + IncrementalVector m_testVector; + + unsigned int getNbFifos(); + void callbackPart(qint16* buf, qint32 nSamples, int iBegin); + void callbackPart(std::vector& data, unsigned int iBegin, unsigned int iEnd); + void callback(qint16* buf, qint32 samplesPerChannel); + void feedSpectrum(int16_t *buf, unsigned int bufSize); + +private slots: + void tick(); +}; + +#endif // PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCWORKER_H_ diff --git a/sdrbase/dsp/devicesamplestatic.h b/sdrbase/dsp/devicesamplestatic.h index 092f19c8b..8b201329f 100644 --- a/sdrbase/dsp/devicesamplestatic.h +++ b/sdrbase/dsp/devicesamplestatic.h @@ -18,7 +18,9 @@ #include -class DeviceSampleStatic +#include "export.h" + +class SDRBASE_API DeviceSampleStatic { public: typedef enum {