Test MO sync

This commit is contained in:
f4exb 2020-11-10 20:32:57 +01:00
parent 1d47ec39fd
commit 17a9d387c3
15 changed files with 2246 additions and 1 deletions

View File

@ -5,3 +5,4 @@ if(ENABLE_BLADERF AND LIBBLADERF_FOUND)
endif()
add_subdirectory(testmi)
add_subdirectory(testmosync)

View File

@ -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})

View File

@ -0,0 +1,49 @@
<h1>Test Multiple Output synchronized plugin</h1>
<h2>Introduction</h2>
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.
<h2>Build</h2>
The plugin is always built.
<h2>Interface</h2>
![Test MO sync plugin GUI](../../../doc/img/TestMOSync_plugin.png)
<h3>1: Start/Stop</h3>
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
<h3>2: Frequency</h3>
This is the center frequency in kHz that will be put in the file header.
<h3>3: Output stream sample rate</h3>
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).
<h3>4: Stream selection</h3>
This combo selects to which stream the UI applies for stream specific controls.
<h3>5: Interpolation factor</h3>
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.
<h3>6: Baseband sample rate</h3>
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.
<h3>7: Spectrum display</h3>
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.

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <errno.h>
#include <QDebug>
#include <QBuffer>
#include "SWGDeviceSettings.h"
#include "SWGDeviceState.h"
#include "device/deviceapi.h"
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "dsp/dspdevicemimoengine.h"
#include "dsp/devicesamplesource.h"
#include "dsp/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<<m_settings.m_log2Interp));
}
quint64 TestMOSync::getSourceCenterFrequency(int index) const
{
(void) index;
return 0;
}
void TestMOSync::setSourceCenterFrequency(qint64 centerFrequency, int index)
{
(void) centerFrequency;
(void) index;
}
quint64 TestMOSync::getSinkCenterFrequency(int index) const
{
(void) index;
return m_settings.m_centerFrequency;
}
void TestMOSync::setSinkCenterFrequency(qint64 centerFrequency, int index)
{
(void) index;
TestMOSyncSettings settings = m_settings;
settings.m_centerFrequency = centerFrequency;
MsgConfigureTestMOSync* message = MsgConfigureTestMOSync::create(settings, false);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureTestMOSync* messageToGUI = MsgConfigureTestMOSync::create(settings, false);
m_guiMessageQueue->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<QString> 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<<settings.m_log2Interp);
}
forwardChangeTxDSP = true;
}
if (forwardChangeTxDSP)
{
DSPMIMOSignalNotification *notif0 = new DSPMIMOSignalNotification(settings.m_sampleRate, settings.m_centerFrequency, false, 0);
m_deviceAPI->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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNC_H_
#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNC_H_
#include <stdint.h>
#include <QString>
#include <QByteArray>
#include <QTimer>
#include <QThread>
#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_

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QTime>
#include <QString>
#include <QMessageBox>
#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<<m_settings.m_log2Interp));
ui->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<<m_settings.m_log2Interp)) / 1000));
}
void TestMOSyncGui::displaySettings()
{
ui->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<<m_settings.m_log2Interp));
sendSettings();
}
void TestMOSyncGui::on_interp_currentIndexChanged(int index)
{
if (index < 0) {
return;
}
m_settings.m_log2Interp = index;
ui->glSpectrum->setSampleRate(m_settings.m_sampleRate*(1<<m_settings.m_log2Interp));
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::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()
{
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_TESTMOSYNCGUI_H
#define INCLUDE_TESTMOSYNCGUI_H
#include <device/devicegui.h>
#include <QTimer>
#include <QWidget>
#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

View File

@ -0,0 +1,399 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestMOSyncGui</class>
<widget class="QWidget" name="TestMOSyncGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>400</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Test MO Sync</string>
</property>
<property name="statusTip">
<string>Test MO Sync</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_freq">
<property name="topMargin">
<number>2</number>
</property>
<item>
<layout class="QVBoxLayout" name="deviceUILayout">
<item>
<layout class="QHBoxLayout" name="deviceButtonsLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>start/stop generation</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="deviceRateLayout">
<item>
<widget class="QLabel" name="deviceRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>I/Q sample rate kS/s</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="freqLeftSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="ValueDial" name="centerFrequency" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Record center frequency in kHz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="freqUnits">
<property name="text">
<string> kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="freqRightlSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_rate">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="rateControlLayout">
<property name="topMargin">
<number>4</number>
</property>
<item>
<widget class="QLabel" name="spectrumLabel">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../sdrgui/resources/res.qrc">:/dsb.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="spectrumIndex">
<property name="maximumSize">
<size>
<width>35</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select which stream index to display spectrum</string>
</property>
<item>
<property name="text">
<string>0</string>
</property>
</item>
<item>
<property name="text">
<string>1</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="interpLabel">
<property name="text">
<string>Int</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="interp">
<property name="toolTip">
<string>Interpolation</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateLabel">
<property name="text">
<string>SR</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDial" name="sampleRate" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="linePlay2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="spectrumLayout">
<item>
<widget class="GLSpectrum" name="glSpectrum" native="true">
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>8</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="padLayout">
<item>
<spacer name="verticaPadlSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef _TESTMOSYNC_TESTMOSYNCPLUGIN_H
#define _TESTMOSYNC_TESTMOSYNCPLUGIN_H
#include <QObject>
#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

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_
#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_
#include <QByteArray>
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_ */

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QTimer>
#include <QDebug>
#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<<m_log2Interp), iPart1Begin, iPart1End, iPart2Begin, iPart2End);
if (iPart1Begin != iPart1End)
{
callbackPart(buf, (iPart1End - iPart1Begin)*(1<<m_log2Interp), iPart1Begin);
}
if (iPart2Begin != iPart2End)
{
unsigned int shift = (iPart1End - iPart1Begin)*(1<<m_log2Interp);
callbackPart(buf + 2*shift, (iPart2End - iPart2Begin)*(1<<m_log2Interp), iPart2Begin);
}
}
// Interpolate according to specified log2 (ex: log2=4 => 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<SampleVector>& 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<SampleVector>& 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<<m_log2Interp)*2);
break;
case 2:
m_interpolators[channel].interpolate4_cen(&beginRead, m_buf, chunkSize*(1<<m_log2Interp)*2);
break;
case 3:
m_interpolators[channel].interpolate8_cen(&beginRead, m_buf, chunkSize*(1<<m_log2Interp)*2);
break;
case 4:
m_interpolators[channel].interpolate16_cen(&beginRead, m_buf, chunkSize*(1<<m_log2Interp)*2);
break;
case 5:
m_interpolators[channel].interpolate32_cen(&beginRead, m_buf, chunkSize*(1<<m_log2Interp)*2);
break;
case 6:
m_interpolators[channel].interpolate64_cen(&beginRead, m_buf, chunkSize*(1<<m_log2Interp)*2);
break;
default:
break;
}
if (channel == m_feedSpectrumIndex) {
feedSpectrum(m_buf, 2*chunkSize*(1<<m_log2Interp));
}
}
}
}
void TestMOSyncWorker::feedSpectrum(int16_t *buf, unsigned int bufSize)
{
if (!m_spectrumSink) {
return;
}
m_samplesVector.allocate(bufSize/2);
Sample16 *s16Buf = (Sample16*) buf;
std::transform(
s16Buf,
s16Buf + (bufSize/2),
m_samplesVector.m_vector.begin(),
[](Sample16 s) -> 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);
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCWORKER_H_
#define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCWORKER_H_
// configure two Tx
#include <QObject>
#include <QElapsedTimer>
#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<qint16, SDR_TX_SAMP_SZ, 16> 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<Sample> m_samplesVector;
IncrementalVector<Sample> m_testVector;
unsigned int getNbFifos();
void callbackPart(qint16* buf, qint32 nSamples, int iBegin);
void callbackPart(std::vector<SampleVector>& 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_

View File

@ -18,7 +18,9 @@
#include <stdint.h>
class DeviceSampleStatic
#include "export.h"
class SDRBASE_API DeviceSampleStatic
{
public:
typedef enum {