From e520ef5aeb0fd785c3de50018473da7f277bfedf Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 00:02:28 +0100 Subject: [PATCH 01/39] Removed GLSpectrum getter from DeviceSourceAPI --- debian/changelog | 6 ++++++ plugins/samplesource/airspy/airspygui.cpp | 8 +++++--- plugins/samplesource/airspy/airspygui.h | 4 +++- plugins/samplesource/airspy/airspyplugin.cpp | 8 ++++++-- plugins/samplesource/airspy/airspyplugin.h | 6 +++++- .../bladerfinput/bladerfinputgui.cpp | 8 +++++--- .../bladerfinput/bladerfinputgui.h | 4 +++- .../bladerfinput/bladerfinputplugin.cpp | 8 ++++++-- .../bladerfinput/bladerfinputplugin.h | 7 ++++++- plugins/samplesource/fcdpro/fcdprogui.cpp | 8 +++++--- plugins/samplesource/fcdpro/fcdprogui.h | 4 +++- plugins/samplesource/fcdpro/fcdproplugin.cpp | 8 ++++++-- plugins/samplesource/fcdpro/fcdproplugin.h | 6 +++++- .../samplesource/fcdproplus/fcdproplusgui.cpp | 8 +++++--- .../samplesource/fcdproplus/fcdproplusgui.h | 4 +++- .../fcdproplus/fcdproplusplugin.cpp | 8 ++++++-- .../fcdproplus/fcdproplusplugin.h | 6 +++++- .../samplesource/filesource/filesourcegui.cpp | 8 +++++--- .../samplesource/filesource/filesourcegui.h | 4 +++- .../filesource/filesourceplugin.cpp | 8 ++++++-- .../filesource/filesourceplugin.h | 6 +++++- .../hackrfinput/hackrfinputgui.cpp | 8 +++++--- .../samplesource/hackrfinput/hackrfinputgui.h | 4 +++- .../hackrfinput/hackrfinputplugin.cpp | 8 ++++++-- .../hackrfinput/hackrfinputplugin.h | 6 +++++- .../limesdrinput/limesdrinputgui.cpp | 8 +++++--- .../limesdrinput/limesdrinputgui.h | 4 +++- .../limesdrinput/limesdrinputplugin.cpp | 8 ++++++-- .../limesdrinput/limesdrinputplugin.h | 6 +++++- .../plutosdrinput/plutosdrinputgui.cpp | 8 +++++--- .../plutosdrinput/plutosdrinputgui.h | 4 +++- .../plutosdrinput/plutosdrinputplugin.cpp | 8 ++++++-- .../plutosdrinput/plutosdrinputplugin.h | 6 +++++- plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 8 +++++--- plugins/samplesource/rtlsdr/rtlsdrgui.h | 4 +++- plugins/samplesource/rtlsdr/rtlsdrplugin.cpp | 8 ++++++-- plugins/samplesource/rtlsdr/rtlsdrplugin.h | 6 +++++- .../sdrdaemonsource/sdrdaemonsourcegui.cpp | 8 +++++--- .../sdrdaemonsource/sdrdaemonsourcegui.h | 4 +++- .../sdrdaemonsource/sdrdaemonsourceplugin.cpp | 8 ++++++-- .../sdrdaemonsource/sdrdaemonsourceplugin.h | 6 +++++- plugins/samplesource/sdrplay/sdrplaygui.cpp | 8 +++++--- plugins/samplesource/sdrplay/sdrplaygui.h | 4 +++- .../samplesource/sdrplay/sdrplayplugin.cpp | 8 ++++++-- plugins/samplesource/sdrplay/sdrplayplugin.h | 6 +++++- sdrgui/device/devicesinkapi.h | 4 ++-- sdrgui/device/devicesourceapi.cpp | 5 ----- sdrgui/device/devicesourceapi.h | 5 ++--- sdrgui/device/deviceuiset.h | 5 +++++ sdrgui/gui/aboutdialog.ui | 2 +- sdrgui/mainwindow.cpp | 19 ++++++++++++++----- sdrgui/plugin/plugininterface.h | 10 +++++++++- 52 files changed, 249 insertions(+), 94 deletions(-) diff --git a/debian/changelog b/debian/changelog index a3b390acd..68012d85f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sdrangel (3.7.9-1) unstable; urgency=medium + + * Refactoring: Separate DeviceAPI from the GUI + + -- Edouard Griffiths, F4EXB Sun, 05 Nov 2017 18:14:18 +0200 + sdrangel (3.7.8-1) unstable; urgency=medium * PlutoSDR: restored and fixed Tx support diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 6e4229e9b..acf8c20c5 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -22,6 +22,7 @@ #include "airspygui.h" #include +#include "device/deviceuiset.h" #include #include "ui_airspygui.h" @@ -30,10 +31,11 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" -AirspyGui::AirspyGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +AirspyGui::AirspyGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::AirspyGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(0), @@ -150,8 +152,8 @@ void AirspyGui::handleInputMessages() void AirspyGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000)); } diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index e2551f21a..28f2f62b0 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -27,6 +27,7 @@ #define AIRSPY_MAX_DEVICE (32) class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class AirspyGui; @@ -37,7 +38,7 @@ class AirspyGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit AirspyGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit AirspyGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~AirspyGui(); virtual void destroy(); @@ -58,6 +59,7 @@ private: Ui::AirspyGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; AirspySettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index 959c99e89..b354073b9 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -119,11 +119,15 @@ PluginInterface::SamplingDevices AirspyPlugin::enumSampleSources() return result; } -PluginInstanceGUI* AirspyPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* AirspyPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if (sourceId == m_deviceTypeID) { - AirspyGui* gui = new AirspyGui(deviceAPI); + AirspyGui* gui = new AirspyGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/airspy/airspyplugin.h b/plugins/samplesource/airspy/airspyplugin.h index 0406ffd68..6a2562f56 100644 --- a/plugins/samplesource/airspy/airspyplugin.h +++ b/plugins/samplesource/airspy/airspyplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/bladerfinput/bladerfinputgui.cpp b/plugins/samplesource/bladerfinput/bladerfinputgui.cpp index 5812e3504..7a14464ae 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputgui.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputgui.cpp @@ -27,11 +27,13 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include +#include "device/deviceuiset.h" -BladerfInputGui::BladerfInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +BladerfInputGui::BladerfInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::BladerfInputGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), @@ -148,8 +150,8 @@ void BladerfInputGui::handleInputMessages() void BladerfInputGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesource/bladerfinput/bladerfinputgui.h b/plugins/samplesource/bladerfinput/bladerfinputgui.h index ca2cd4db1..2289a71ac 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputgui.h +++ b/plugins/samplesource/bladerfinput/bladerfinputgui.h @@ -26,6 +26,7 @@ #include "bladerfinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class BladerfInputGui; @@ -35,7 +36,7 @@ class BladerfInputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit BladerfInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit BladerfInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~BladerfInputGui(); virtual void destroy(); @@ -54,6 +55,7 @@ private: Ui::BladerfInputGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; BladeRFInputSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp index ae7ba37fc..d87c3f81c 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp @@ -78,11 +78,15 @@ PluginInterface::SamplingDevices BlderfInputPlugin::enumSampleSources() return result; } -PluginInstanceGUI* BlderfInputPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId,QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* BlderfInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - BladerfInputGui* gui = new BladerfInputGui(deviceAPI); + BladerfInputGui* gui = new BladerfInputGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.h b/plugins/samplesource/bladerfinput/bladerfinputplugin.h index e21998654..0b9fa6788 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.h +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.h @@ -22,6 +22,7 @@ class PluginAPI; class DeviceSourceAPI; +class DeviceUISet; #define BLADERF_DEVICE_TYPE_ID "sdrangel.samplesource.bladerf" @@ -37,7 +38,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 6a65feb94..f4c253b00 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -24,13 +24,15 @@ #include "fcdprogui.h" #include +#include "device/deviceuiset.h" #include "fcdproconst.h" #include "fcdtraits.h" -FCDProGui::FCDProGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +FCDProGui::FCDProGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FCDProGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), @@ -235,8 +237,8 @@ void FCDProGui::handleInputMessages() void FCDProGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000)); } diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 792f7c827..4f550f2b1 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -27,6 +27,7 @@ class DeviceSourceAPI; class QWidget; +class DeviceUISet; namespace Ui { class FCDProGui; @@ -36,7 +37,7 @@ class FCDProGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FCDProGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit FCDProGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FCDProGui(); virtual void destroy(); @@ -55,6 +56,7 @@ private: Ui::FCDProGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; FCDProSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/fcdpro/fcdproplugin.cpp b/plugins/samplesource/fcdpro/fcdproplugin.cpp index f6d0bfdee..0f09edda8 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.cpp +++ b/plugins/samplesource/fcdpro/fcdproplugin.cpp @@ -74,11 +74,15 @@ PluginInterface::SamplingDevices FCDProPlugin::enumSampleSources() return result; } -PluginInstanceGUI* FCDProPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* FCDProPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == fcd_traits::interfaceIID) { - FCDProGui* gui = new FCDProGui(deviceAPI); + FCDProGui* gui = new FCDProGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/fcdpro/fcdproplugin.h b/plugins/samplesource/fcdpro/fcdproplugin.h index 4005bc47e..7cafa54c0 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.h +++ b/plugins/samplesource/fcdpro/fcdproplugin.h @@ -20,7 +20,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); private: diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index eb50a6281..9893d03d4 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -25,13 +25,15 @@ #include "fcdproplusgui.h" #include +#include "device/deviceuiset.h" #include "fcdproplusconst.h" #include "fcdtraits.h" -FCDProPlusGui::FCDProPlusGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +FCDProPlusGui::FCDProPlusGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FCDProPlusGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), @@ -153,8 +155,8 @@ void FCDProPlusGui::handleInputMessages() void FCDProPlusGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000)); } diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index f82f1b2fa..9ba7870b3 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -26,6 +26,7 @@ #include "fcdproplusinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class FCDProPlusGui; @@ -35,7 +36,7 @@ class FCDProPlusGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FCDProPlusGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit FCDProPlusGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FCDProPlusGui(); virtual void destroy(); @@ -54,6 +55,7 @@ private: Ui::FCDProPlusGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; FCDProPlusSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp index 14159c0b7..26b9c5c3c 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp @@ -76,11 +76,15 @@ PluginInterface::SamplingDevices FCDProPlusPlugin::enumSampleSources() return result; } -PluginInstanceGUI* FCDProPlusPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* FCDProPlusPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == fcd_traits::interfaceIID) { - FCDProPlusGui* gui = new FCDProPlusGui(deviceAPI); + FCDProPlusGui* gui = new FCDProPlusGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.h b/plugins/samplesource/fcdproplus/fcdproplusplugin.h index c122e8b9b..918274e40 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.h +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.h @@ -20,7 +20,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_deviceTypeID; diff --git a/plugins/samplesource/filesource/filesourcegui.cpp b/plugins/samplesource/filesource/filesourcegui.cpp index b46a4c028..27f877e86 100644 --- a/plugins/samplesource/filesource/filesourcegui.cpp +++ b/plugins/samplesource/filesource/filesourcegui.cpp @@ -33,11 +33,13 @@ #include "filesourcegui.h" #include +#include "device/deviceuiset.h" -FileSourceGui::FileSourceGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +FileSourceGui::FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FileSourceGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_sampleSource(NULL), m_acquisition(false), @@ -186,8 +188,8 @@ bool FileSourceGui::handleMessage(const Message& message) void FileSourceGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_deviceSampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); } diff --git a/plugins/samplesource/filesource/filesourcegui.h b/plugins/samplesource/filesource/filesourcegui.h index bef589100..7597e8ab3 100644 --- a/plugins/samplesource/filesource/filesourcegui.h +++ b/plugins/samplesource/filesource/filesourcegui.h @@ -26,6 +26,7 @@ #include "filesourceinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class FileSourceGui; @@ -35,7 +36,7 @@ class FileSourceGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FileSourceGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FileSourceGui(); virtual void destroy(); @@ -54,6 +55,7 @@ private: Ui::FileSourceGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; FileSourceInput::Settings m_settings; QTimer m_statusTimer; std::vector m_gains; diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index 04ed32ba6..71c258999 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -69,11 +69,15 @@ PluginInterface::SamplingDevices FileSourcePlugin::enumSampleSources() return result; } -PluginInstanceGUI* FileSourcePlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* FileSourcePlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - FileSourceGui* gui = new FileSourceGui(deviceAPI); + FileSourceGui* gui = new FileSourceGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/filesource/filesourceplugin.h b/plugins/samplesource/filesource/filesourceplugin.h index 04fb820ad..222b0968f 100644 --- a/plugins/samplesource/filesource/filesourceplugin.h +++ b/plugins/samplesource/filesource/filesourceplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index bc4cbec42..dcd99b200 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -27,14 +27,16 @@ #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "hackrf/devicehackrfvalues.h" #include "ui_hackrfinputgui.h" -HackRFInputGui::HackRFInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +HackRFInputGui::HackRFInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::HackRFInputGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), m_sampleSource(NULL), @@ -164,8 +166,8 @@ void HackRFInputGui::handleInputMessages() void HackRFInputGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(QString("%1k").arg(QString::number(m_sampleRate/1000.0, 'g', 5))); } diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 5f78a2f34..2c6c28ad9 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -28,6 +28,7 @@ #define HACKRF_MAX_DEVICE (32) class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class HackRFInputGui; @@ -45,7 +46,7 @@ public: HACKRF_IMGREJ_NB } HackRFImgRejValue; - explicit HackRFInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit HackRFInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~HackRFInputGui(); virtual void destroy(); @@ -64,6 +65,7 @@ private: Ui::HackRFInputGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; HackRFInputSettings m_settings; bool m_forceSettings; QTimer m_updateTimer; diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 833722be8..d0b49125a 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -117,11 +117,15 @@ PluginInterface::SamplingDevices HackRFInputPlugin::enumSampleSources() return result; } -PluginInstanceGUI* HackRFInputPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* HackRFInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - HackRFInputGui* gui = new HackRFInputGui(deviceAPI); + HackRFInputGui* gui = new HackRFInputGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.h b/plugins/samplesource/hackrfinput/hackrfinputplugin.h index c779cb880..b78559365 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.h +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 9a491c74d..1fa3da8f9 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -27,11 +27,13 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesourceapi.h" +#include "device/deviceuiset.h" -LimeSDRInputGUI::LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, QWidget* parent) : +LimeSDRInputGUI::LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::LimeSDRInputGUI), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_sampleRate(0), m_lastEngineState((DSPDeviceSourceEngine::State)-1), @@ -246,8 +248,8 @@ void LimeSDRInputGUI::updateADCRate() void LimeSDRInputGUI::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index c0b87ba1a..6e7296b9d 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -26,6 +26,7 @@ #include "limesdrinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class LimeSDRInputGUI; @@ -35,7 +36,7 @@ class LimeSDRInputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, QWidget* parent = 0); + explicit LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~LimeSDRInputGUI(); virtual void destroy(); @@ -54,6 +55,7 @@ private: Ui::LimeSDRInputGUI* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; LimeSDRInput* m_limeSDRInput; //!< Same object as above but gives easy access to LimeSDRInput methods and attributes that are used intensively LimeSDRInputSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index 7b9ce3789..4ab1d1dbc 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -97,11 +97,15 @@ PluginInterface::SamplingDevices LimeSDRInputPlugin::enumSampleSources() return result; } -PluginInstanceGUI* LimeSDRInputPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId,QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* LimeSDRInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - LimeSDRInputGUI* gui = new LimeSDRInputGUI(deviceAPI); + LimeSDRInputGUI* gui = new LimeSDRInputGUI(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.h b/plugins/samplesource/limesdrinput/limesdrinputplugin.h index ee1dc218f..e9165aac3 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.h +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 402df2112..21fb9bdcf 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -22,15 +22,17 @@ #include "dsp/dspcommands.h" #include "gui/glspectrum.h" #include "device/devicesourceapi.h" +#include "device/deviceuiset.h" #include "plutosdr/deviceplutosdr.h" #include "plutosdrinput.h" #include "ui_plutosdrinputgui.h" #include "plutosdrinputgui.h" -PlutoSDRInputGui::PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +PlutoSDRInputGui::PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::PlutoSDRInputGUI), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), m_sampleSource(NULL), @@ -464,7 +466,7 @@ void PlutoSDRInputGui::handleInputMessages() void PlutoSDRInputGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index e8b22eab5..636b0dd07 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -28,6 +28,7 @@ class DeviceSourceAPI; class DeviceSampleSource; +class DeviceUISet; namespace Ui { class PlutoSDRInputGUI; @@ -37,7 +38,7 @@ class PlutoSDRInputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, QWidget* parent = 0); + explicit PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~PlutoSDRInputGui(); virtual void destroy(); @@ -54,6 +55,7 @@ public: private: Ui::PlutoSDRInputGUI* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; PlutoSDRInputSettings m_settings; bool m_forceSettings; QTimer m_updateTimer; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index 937f13612..f61b710ae 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -81,11 +81,15 @@ PluginInterface::SamplingDevices PlutoSDRInputPlugin::enumSampleSources() return result; } -PluginInstanceGUI* PlutoSDRInputPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* PlutoSDRInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - PlutoSDRInputGui* gui = new PlutoSDRInputGui(deviceAPI); + PlutoSDRInputGui* gui = new PlutoSDRInputGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h index a4feaf21f..e5c8540df 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 2f0e6b7cb..99bc5b3d5 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -20,6 +20,7 @@ #include "rtlsdrgui.h" #include +#include "device/deviceuiset.h" #include #include "ui_rtlsdrgui.h" @@ -29,10 +30,11 @@ #include "dsp/dspcommands.h" -RTLSDRGui::RTLSDRGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +RTLSDRGui::RTLSDRGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::RTLSDRGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(0), @@ -173,8 +175,8 @@ void RTLSDRGui::handleInputMessages() void RTLSDRGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index fd36e7e3d..20b58ed8d 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -26,6 +26,7 @@ #include "rtlsdrinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class RTLSDRGui; @@ -36,7 +37,7 @@ class RTLSDRGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit RTLSDRGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit RTLSDRGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~RTLSDRGui(); virtual void destroy(); @@ -55,6 +56,7 @@ private: Ui::RTLSDRGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; RTLSDRSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index a6c44ae48..85eeb4519 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -62,10 +62,14 @@ PluginInterface::SamplingDevices RTLSDRPlugin::enumSampleSources() return result; } -PluginInstanceGUI* RTLSDRPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* RTLSDRPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - RTLSDRGui* gui = new RTLSDRGui(deviceAPI); + RTLSDRGui* gui = new RTLSDRGui(deviceAPI, deviceUISet); *widget = gui; return gui; } else { diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.h b/plugins/samplesource/rtlsdr/rtlsdrplugin.h index 72cee65ec..2119c1ebe 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.h +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.h @@ -20,7 +20,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp index 7135bd3f3..1814eaf8e 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp @@ -43,12 +43,14 @@ #include "util/simpleserializer.h" #include +#include "device/deviceuiset.h" #include "sdrdaemonsourcegui.h" -SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRdaemonSourceGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_sampleSource(NULL), m_acquisition(false), m_lastEngineState((DSPDeviceSourceEngine::State)-1), @@ -284,8 +286,8 @@ void SDRdaemonSourceGui::handleInputMessages() void SDRdaemonSourceGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_deviceSampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000)); } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h index dbc71a083..d2191f523 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h @@ -27,6 +27,7 @@ #include "sdrdaemonsourceinput.h" class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class SDRdaemonSourceGui; @@ -36,7 +37,7 @@ class SDRdaemonSourceGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SDRdaemonSourceGui(); virtual void destroy(); @@ -55,6 +56,7 @@ private: Ui::SDRdaemonSourceGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; SDRdaemonSourceSettings m_settings; //!< current settings SDRdaemonSourceSettings m_controlSettings; //!< settings last sent to device via control port QTimer m_updateTimer; diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index 0f4417cef..a64dacafd 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -70,11 +70,15 @@ PluginInterface::SamplingDevices SDRdaemonSourcePlugin::enumSampleSources() return result; } -PluginInstanceGUI* SDRdaemonSourcePlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* SDRdaemonSourcePlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - SDRdaemonSourceGui* gui = new SDRdaemonSourceGui(deviceAPI); + SDRdaemonSourceGui* gui = new SDRdaemonSourceGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h index 1c154d112..1fc76fe42 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index 96e4e8276..213b83e0e 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -20,6 +20,7 @@ #include "sdrplaygui.h" #include +#include "device/deviceuiset.h" #include "ui_sdrplaygui.h" #include "gui/colormapper.h" @@ -28,10 +29,11 @@ #include "dsp/dspcommands.h" -SDRPlayGui::SDRPlayGui(DeviceSourceAPI *deviceAPI, QWidget* parent) : +SDRPlayGui::SDRPlayGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRPlayGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true) { m_sampleSource = (SDRPlayInput*) m_deviceAPI->getSampleSource(); @@ -198,8 +200,8 @@ void SDRPlayGui::handleInputMessages() void SDRPlayGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000)); } diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index ac1fefc25..8c56b0713 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -29,6 +29,7 @@ class DeviceSampleSource; class DeviceSourceAPI; +class DeviceUISet; namespace Ui { class SDRPlayGui; @@ -38,7 +39,7 @@ class SDRPlayGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit SDRPlayGui(DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit SDRPlayGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SDRPlayGui(); virtual void destroy(); @@ -57,6 +58,7 @@ private: Ui::SDRPlayGui* ui; DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; SDRPlaySettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index 74a10d17c..910a7b539 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -83,11 +83,15 @@ PluginInterface::SamplingDevices SDRPlayPlugin::enumSampleSources() return result; } -PluginInstanceGUI* SDRPlayPlugin::createSampleSourcePluginInstanceGUI(const QString& sourceId,QWidget **widget, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* SDRPlayPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - SDRPlayGui* gui = new SDRPlayGui(deviceAPI); + SDRPlayGui* gui = new SDRPlayGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.h b/plugins/samplesource/sdrplay/sdrplayplugin.h index 12417334d..e46a6f363 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.h +++ b/plugins/samplesource/sdrplay/sdrplayplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSources(); - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId, QWidget **widget, DeviceSourceAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceSourceAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); static const QString m_hardwareID; diff --git a/sdrgui/device/devicesinkapi.h b/sdrgui/device/devicesinkapi.h index f3e9878ac..b83c4ba0d 100644 --- a/sdrgui/device/devicesinkapi.h +++ b/sdrgui/device/devicesinkapi.h @@ -112,7 +112,7 @@ public: bool isBuddyLeader() const { return m_isBuddyLeader; } void setBuddyLeader(bool isBuddyLeader) { m_isBuddyLeader = isBuddyLeader; } - const QTimer& getMasterTimer() const { return m_masterTimer; } + const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer protected: struct ChannelInstanceRegistration @@ -156,7 +156,7 @@ protected: std::vector m_sinkBuddies; //!< Device sink APIs referencing the same physical device void *m_buddySharedPtr; bool m_isBuddyLeader; - const QTimer& m_masterTimer; + const QTimer& m_masterTimer; //!< This is the DSPEngine master timer friend class DeviceSourceAPI; }; diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 138937ab4..5c9601193 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -128,11 +128,6 @@ void DeviceSourceAPI::configureCorrections(bool dcOffsetCorrection, bool iqImbal m_deviceSourceEngine->configureCorrections(dcOffsetCorrection, iqImbalanceCorrection); } -GLSpectrum *DeviceSourceAPI::getSpectrum() -{ - return m_spectrum; -} - void DeviceSourceAPI::addChannelMarker(ChannelMarker* channelMarker) { m_spectrum->addChannelMarker(channelMarker); diff --git a/sdrgui/device/devicesourceapi.h b/sdrgui/device/devicesourceapi.h index e6ead1370..a6f03b15c 100644 --- a/sdrgui/device/devicesourceapi.h +++ b/sdrgui/device/devicesourceapi.h @@ -68,7 +68,6 @@ public: void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection); //!< Configure current device engine DSP corrections // device related stuff - GLSpectrum *getSpectrum(); //!< Direct spectrum getter void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window @@ -113,7 +112,7 @@ public: bool isBuddyLeader() const { return m_isBuddyLeader; } void setBuddyLeader(bool isBuddyLeader) { m_isBuddyLeader = isBuddyLeader; } - const QTimer& getMasterTimer() const { return m_masterTimer; } + const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer protected: struct ChannelInstanceRegistration @@ -158,7 +157,7 @@ protected: std::vector m_sinkBuddies; //!< Device sink APIs referencing the same physical device void *m_buddySharedPtr; bool m_isBuddyLeader; - const QTimer& m_masterTimer; + const QTimer& m_masterTimer; //!< This is the DSPEngine master timer friend class DeviceSinkAPI; }; diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 844a3bdcd..236e54fd3 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -29,6 +29,7 @@ class DSPDeviceSourceEngine; class DeviceSourceAPI; class DSPDeviceSinkEngine; class DeviceSinkAPI; +class ChannelMarker; struct DeviceUISet { @@ -45,6 +46,10 @@ struct DeviceUISet DeviceUISet(QTimer& timer); ~DeviceUISet(); + + GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter + void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum + void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window }; diff --git a/sdrgui/gui/aboutdialog.ui b/sdrgui/gui/aboutdialog.ui index 285ed4dca..8b7cff58b 100644 --- a/sdrgui/gui/aboutdialog.ui +++ b/sdrgui/gui/aboutdialog.ui @@ -84,7 +84,7 @@ - <html><head/><body><p>Version 3.7.8 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html> + <html><head/><body><p>Version 3.7.9 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html> true diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 02cc19c60..77f5f6caf 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -152,7 +152,10 @@ MainWindow::MainWindow(QWidget* parent) : m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); QWidget *gui; PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), &gui, m_deviceUIs.back()->m_deviceSourceAPI); + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), + &gui, + m_deviceUIs.back()->m_deviceSourceAPI, + m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); setDeviceGUI(0, gui, m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceDisplayName()); @@ -236,7 +239,10 @@ void MainWindow::addSourceDevice() m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); QWidget *gui; PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), &gui, m_deviceUIs.back()->m_deviceSourceAPI); + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), + &gui, + m_deviceUIs.back()->m_deviceSourceAPI, + m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceDisplayName()); @@ -506,9 +512,9 @@ void MainWindow::createStatusBar() { QString qtVersionStr = QString("Qt %1 ").arg(QT_VERSION_STR); #if QT_VERSION >= 0x050400 - m_showSystemWidget = new QLabel("SDRangel v3.7.8 " + qtVersionStr + QSysInfo::prettyProductName(), this); + m_showSystemWidget = new QLabel("SDRangel v3.7.9 " + qtVersionStr + QSysInfo::prettyProductName(), this); #else - m_showSystemWidget = new QLabel("SDRangel v3.7.8 " + qtVersionStr, this); + m_showSystemWidget = new QLabel("SDRangel v3.7.9 " + qtVersionStr, this); #endif statusBar()->addPermanentWidget(m_showSystemWidget); @@ -901,7 +907,10 @@ void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unus deviceUI->m_deviceSourceAPI->setSampleSource(source); QWidget *gui; PluginInstanceGUI *pluginUI = deviceUI->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( - deviceUI->m_deviceSourceAPI->getSampleSourceId(), &gui, deviceUI->m_deviceSourceAPI); + deviceUI->m_deviceSourceAPI->getSampleSourceId(), + &gui, + deviceUI->m_deviceSourceAPI, + deviceUI); deviceUI->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); deviceUI->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginUI); setDeviceGUI(currentSourceTabIndex, gui, deviceUI->m_deviceSourceAPI->getSampleSourceDisplayName()); diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 612cbb05e..71a5a760e 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -16,6 +16,7 @@ struct PluginDescriptor { class PluginAPI; class DeviceSourceAPI; +class DeviceUISet; class DeviceSinkAPI; class PluginInstanceGUI; class QWidget; @@ -59,7 +60,14 @@ public: // device source plugins only virtual SamplingDevices enumSampleSources() { return SamplingDevices(); } - virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(const QString& sourceId __attribute__((unused)), QWidget **widget __attribute__((unused)), DeviceSourceAPI *deviceAPI __attribute__((unused))) { return 0; } + + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId __attribute__((unused)), + QWidget **widget __attribute__((unused)), + DeviceSourceAPI *deviceAPI __attribute__((unused)), + DeviceUISet *deviceUISet __attribute__((unused))) + { return 0; } + virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId __attribute__((unused)), DeviceSourceAPI *deviceAPI __attribute__((unused))) { return 0; } // creates the input "core" virtual void deleteSampleSourcePluginInstanceGUI(PluginInstanceGUI *ui); virtual void deleteSampleSourcePluginInstanceInput(DeviceSampleSource *source); From da3e6dcdfb9473a873319a8b707f904310c39c0a Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 00:45:23 +0100 Subject: [PATCH 02/39] Removed direct reference to the DeviceSourceAPI in the source GUIs --- plugins/samplesource/airspy/airspygui.cpp | 15 +++++++-------- plugins/samplesource/airspy/airspygui.h | 3 +-- plugins/samplesource/airspy/airspyplugin.cpp | 3 +-- plugins/samplesource/airspy/airspyplugin.h | 1 - .../bladerfinput/bladerfinputgui.cpp | 15 +++++++-------- .../bladerfinput/bladerfinputgui.h | 3 +-- .../bladerfinput/bladerfinputplugin.cpp | 3 +-- .../bladerfinput/bladerfinputplugin.h | 1 - plugins/samplesource/fcdpro/fcdprogui.cpp | 15 +++++++-------- plugins/samplesource/fcdpro/fcdprogui.h | 3 +-- plugins/samplesource/fcdpro/fcdproplugin.cpp | 3 +-- plugins/samplesource/fcdpro/fcdproplugin.h | 1 - .../samplesource/fcdproplus/fcdproplusgui.cpp | 15 +++++++-------- .../samplesource/fcdproplus/fcdproplusgui.h | 3 +-- .../fcdproplus/fcdproplusplugin.cpp | 3 +-- .../fcdproplus/fcdproplusplugin.h | 1 - .../samplesource/filesource/filesourcegui.cpp | 17 ++++++++--------- .../samplesource/filesource/filesourcegui.h | 3 +-- .../filesource/filesourceplugin.cpp | 3 +-- .../filesource/filesourceplugin.h | 1 - .../hackrfinput/hackrfinputgui.cpp | 19 +++++++++---------- .../samplesource/hackrfinput/hackrfinputgui.h | 3 +-- .../hackrfinput/hackrfinputplugin.cpp | 3 +-- .../hackrfinput/hackrfinputplugin.h | 1 - .../limesdrinput/limesdrinputgui.cpp | 17 ++++++++--------- .../limesdrinput/limesdrinputgui.h | 3 +-- .../limesdrinput/limesdrinputplugin.cpp | 3 +-- .../limesdrinput/limesdrinputplugin.h | 1 - .../plutosdrinput/plutosdrinputgui.cpp | 17 ++++++++--------- .../plutosdrinput/plutosdrinputgui.h | 3 +-- .../plutosdrinput/plutosdrinputplugin.cpp | 3 +-- .../plutosdrinput/plutosdrinputplugin.h | 1 - plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 15 +++++++-------- plugins/samplesource/rtlsdr/rtlsdrgui.h | 3 +-- plugins/samplesource/rtlsdr/rtlsdrplugin.cpp | 3 +-- plugins/samplesource/rtlsdr/rtlsdrplugin.h | 1 - .../sdrdaemonsource/sdrdaemonsourcegui.cpp | 17 ++++++++--------- .../sdrdaemonsource/sdrdaemonsourcegui.h | 3 +-- .../sdrdaemonsource/sdrdaemonsourceplugin.cpp | 3 +-- .../sdrdaemonsource/sdrdaemonsourceplugin.h | 1 - plugins/samplesource/sdrplay/sdrplaygui.cpp | 15 +++++++-------- plugins/samplesource/sdrplay/sdrplaygui.h | 3 +-- .../samplesource/sdrplay/sdrplayplugin.cpp | 3 +-- plugins/samplesource/sdrplay/sdrplayplugin.h | 1 - sdrgui/mainwindow.cpp | 3 --- sdrgui/plugin/plugininterface.h | 1 - 46 files changed, 105 insertions(+), 153 deletions(-) diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index acf8c20c5..736fe10b9 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -31,17 +31,16 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" -AirspyGui::AirspyGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::AirspyGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(0), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (AirspyInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (AirspyInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -343,15 +342,15 @@ void AirspyGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -389,7 +388,7 @@ void AirspyGui::updateHardware() void AirspyGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -406,7 +405,7 @@ void AirspyGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 28f2f62b0..29deed649 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -38,7 +38,7 @@ class AirspyGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit AirspyGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit AirspyGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~AirspyGui(); virtual void destroy(); @@ -58,7 +58,6 @@ public: private: Ui::AirspyGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; AirspySettings m_settings; diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index b354073b9..50ab53496 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -122,12 +122,11 @@ PluginInterface::SamplingDevices AirspyPlugin::enumSampleSources() PluginInstanceGUI* AirspyPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if (sourceId == m_deviceTypeID) { - AirspyGui* gui = new AirspyGui(deviceAPI, deviceUISet); + AirspyGui* gui = new AirspyGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/airspy/airspyplugin.h b/plugins/samplesource/airspy/airspyplugin.h index 6a2562f56..62953fa9a 100644 --- a/plugins/samplesource/airspy/airspyplugin.h +++ b/plugins/samplesource/airspy/airspyplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/bladerfinput/bladerfinputgui.cpp b/plugins/samplesource/bladerfinput/bladerfinputgui.cpp index 7a14464ae..ef205e89f 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputgui.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputgui.cpp @@ -29,10 +29,9 @@ #include #include "device/deviceuiset.h" -BladerfInputGui::BladerfInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +BladerfInputGui::BladerfInputGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::BladerfInputGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), @@ -40,7 +39,7 @@ BladerfInputGui::BladerfInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *device m_sampleRate(0), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (BladerfInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (BladerfInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -335,15 +334,15 @@ void BladerfInputGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -371,7 +370,7 @@ void BladerfInputGui::updateHardware() void BladerfInputGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -388,7 +387,7 @@ void BladerfInputGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/bladerfinput/bladerfinputgui.h b/plugins/samplesource/bladerfinput/bladerfinputgui.h index 2289a71ac..1aa1cb8f3 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputgui.h +++ b/plugins/samplesource/bladerfinput/bladerfinputgui.h @@ -36,7 +36,7 @@ class BladerfInputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit BladerfInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit BladerfInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~BladerfInputGui(); virtual void destroy(); @@ -54,7 +54,6 @@ public: private: Ui::BladerfInputGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; BladeRFInputSettings m_settings; diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp index d87c3f81c..a39bbe668 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp @@ -81,12 +81,11 @@ PluginInterface::SamplingDevices BlderfInputPlugin::enumSampleSources() PluginInstanceGUI* BlderfInputPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - BladerfInputGui* gui = new BladerfInputGui(deviceAPI, deviceUISet); + BladerfInputGui* gui = new BladerfInputGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.h b/plugins/samplesource/bladerfinput/bladerfinputplugin.h index 0b9fa6788..d9fcb6599 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.h +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.h @@ -41,7 +41,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index f4c253b00..a905dd679 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -28,17 +28,16 @@ #include "fcdproconst.h" #include "fcdtraits.h" -FCDProGui::FCDProGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FCDProGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (FCDProInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (FCDProInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -438,15 +437,15 @@ void FCDProGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -475,7 +474,7 @@ void FCDProGui::on_transverter_clicked() void FCDProGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -492,7 +491,7 @@ void FCDProGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 4f550f2b1..3e278dc54 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -37,7 +37,7 @@ class FCDProGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FCDProGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit FCDProGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FCDProGui(); virtual void destroy(); @@ -55,7 +55,6 @@ public: private: Ui::FCDProGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; FCDProSettings m_settings; diff --git a/plugins/samplesource/fcdpro/fcdproplugin.cpp b/plugins/samplesource/fcdpro/fcdproplugin.cpp index 0f09edda8..f2a52049b 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.cpp +++ b/plugins/samplesource/fcdpro/fcdproplugin.cpp @@ -77,12 +77,11 @@ PluginInterface::SamplingDevices FCDProPlugin::enumSampleSources() PluginInstanceGUI* FCDProPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == fcd_traits::interfaceIID) { - FCDProGui* gui = new FCDProGui(deviceAPI, deviceUISet); + FCDProGui* gui = new FCDProGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/fcdpro/fcdproplugin.h b/plugins/samplesource/fcdpro/fcdproplugin.h index 7cafa54c0..a6769bda7 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.h +++ b/plugins/samplesource/fcdpro/fcdproplugin.h @@ -23,7 +23,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 9893d03d4..76dd6458f 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -29,17 +29,16 @@ #include "fcdproplusconst.h" #include "fcdtraits.h" -FCDProPlusGui::FCDProPlusGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FCDProPlusGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (FCDProPlusInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (FCDProPlusInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); @@ -228,7 +227,7 @@ void FCDProPlusGui::updateHardware() void FCDProPlusGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -245,7 +244,7 @@ void FCDProPlusGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; @@ -303,15 +302,15 @@ void FCDProPlusGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 9ba7870b3..fff755603 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -36,7 +36,7 @@ class FCDProPlusGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FCDProPlusGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FCDProPlusGui(); virtual void destroy(); @@ -54,7 +54,6 @@ public: private: Ui::FCDProPlusGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; FCDProPlusSettings m_settings; diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp index 26b9c5c3c..4f3a075ef 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp @@ -79,12 +79,11 @@ PluginInterface::SamplingDevices FCDProPlusPlugin::enumSampleSources() PluginInstanceGUI* FCDProPlusPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == fcd_traits::interfaceIID) { - FCDProPlusGui* gui = new FCDProPlusGui(deviceAPI, deviceUISet); + FCDProPlusGui* gui = new FCDProPlusGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.h b/plugins/samplesource/fcdproplus/fcdproplusplugin.h index 918274e40..c82921e13 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.h +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.h @@ -23,7 +23,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/filesource/filesourcegui.cpp b/plugins/samplesource/filesource/filesourcegui.cpp index 27f877e86..8f157db36 100644 --- a/plugins/samplesource/filesource/filesourcegui.cpp +++ b/plugins/samplesource/filesource/filesourcegui.cpp @@ -35,10 +35,9 @@ #include #include "device/deviceuiset.h" -FileSourceGui::FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +FileSourceGui::FileSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FileSourceGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_sampleSource(NULL), @@ -58,7 +57,7 @@ FileSourceGui::FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISe ui->centerFrequency->setValueRange(7, 0, pow(10,7)); ui->fileNameText->setText(m_fileName); - connect(&(m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&(m_deviceUISet->m_deviceSourceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); @@ -68,7 +67,7 @@ FileSourceGui::FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISe ui->playLoop->setChecked(true); // FIXME: always play in a loop ui->playLoop->setEnabled(false); - m_sampleSource = m_deviceAPI->getSampleSource(); + m_sampleSource = m_deviceUISet->m_deviceSourceAPI->getSampleSource(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -210,22 +209,22 @@ void FileSourceGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } void FileSourceGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -242,7 +241,7 @@ void FileSourceGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/filesource/filesourcegui.h b/plugins/samplesource/filesource/filesourcegui.h index 7597e8ab3..ead6d3670 100644 --- a/plugins/samplesource/filesource/filesourcegui.h +++ b/plugins/samplesource/filesource/filesourcegui.h @@ -36,7 +36,7 @@ class FileSourceGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FileSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit FileSourceGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FileSourceGui(); virtual void destroy(); @@ -54,7 +54,6 @@ public: private: Ui::FileSourceGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; FileSourceInput::Settings m_settings; QTimer m_statusTimer; diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index 71c258999..21c871f66 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -72,12 +72,11 @@ PluginInterface::SamplingDevices FileSourcePlugin::enumSampleSources() PluginInstanceGUI* FileSourcePlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - FileSourceGui* gui = new FileSourceGui(deviceAPI, deviceUISet); + FileSourceGui* gui = new FileSourceGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/filesource/filesourceplugin.h b/plugins/samplesource/filesource/filesourceplugin.h index 222b0968f..2f077ce1c 100644 --- a/plugins/samplesource/filesource/filesourceplugin.h +++ b/plugins/samplesource/filesource/filesourceplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index dcd99b200..d790eb4bd 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -32,17 +32,16 @@ #include "ui_hackrfinputgui.h" -HackRFInputGui::HackRFInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::HackRFInputGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), m_sampleSource(NULL), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (HackRFInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (HackRFInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -332,21 +331,21 @@ void HackRFInputGui::on_startStop_toggled(bool checked) if (checked) { // forcibly stop the Tx if present before starting - if (m_deviceAPI->getSinkBuddies().size() > 0) + if (m_deviceUISet->m_deviceSourceAPI->getSinkBuddies().size() > 0) { - DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[0]; + DeviceSinkAPI *buddy = m_deviceUISet->m_deviceSourceAPI->getSinkBuddies()[0]; buddy->stopGeneration(); } - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -374,7 +373,7 @@ void HackRFInputGui::updateHardware() void HackRFInputGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -392,7 +391,7 @@ void HackRFInputGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 2c6c28ad9..f0ea24702 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -46,7 +46,7 @@ public: HACKRF_IMGREJ_NB } HackRFImgRejValue; - explicit HackRFInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~HackRFInputGui(); virtual void destroy(); @@ -64,7 +64,6 @@ public: private: Ui::HackRFInputGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; HackRFInputSettings m_settings; bool m_forceSettings; diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index d0b49125a..1a8cca807 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -120,12 +120,11 @@ PluginInterface::SamplingDevices HackRFInputPlugin::enumSampleSources() PluginInstanceGUI* HackRFInputPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - HackRFInputGui* gui = new HackRFInputGui(deviceAPI, deviceUISet); + HackRFInputGui* gui = new HackRFInputGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.h b/plugins/samplesource/hackrfinput/hackrfinputplugin.h index b78559365..1a90c44e4 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.h +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 1fa3da8f9..c5ee62509 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -29,10 +29,9 @@ #include "device/devicesourceapi.h" #include "device/deviceuiset.h" -LimeSDRInputGUI::LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::LimeSDRInputGUI), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_sampleRate(0), @@ -42,7 +41,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, DeviceUISet *device m_statusCounter(0), m_deviceStatusCounter(0) { - m_limeSDRInput = (LimeSDRInput*) m_deviceAPI->getSampleSource(); + m_limeSDRInput = (LimeSDRInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); @@ -331,7 +330,7 @@ void LimeSDRInputGUI::updateHardware() void LimeSDRInputGUI::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -348,7 +347,7 @@ void LimeSDRInputGUI::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; @@ -374,7 +373,7 @@ void LimeSDRInputGUI::updateStatus() } else { - if (m_deviceAPI->isBuddyLeader()) + if (m_deviceUISet->m_deviceSourceAPI->isBuddyLeader()) { LimeSDRInput::MsgGetDeviceInfo* message = LimeSDRInput::MsgGetDeviceInfo::create(); m_limeSDRInput->getInputMessageQueue()->push(message); @@ -393,15 +392,15 @@ void LimeSDRInputGUI::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index 6e7296b9d..e1959b2e6 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -36,7 +36,7 @@ class LimeSDRInputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit LimeSDRInputGUI(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~LimeSDRInputGUI(); virtual void destroy(); @@ -54,7 +54,6 @@ public: private: Ui::LimeSDRInputGUI* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; LimeSDRInput* m_limeSDRInput; //!< Same object as above but gives easy access to LimeSDRInput methods and attributes that are used intensively LimeSDRInputSettings m_settings; diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index 4ab1d1dbc..130a6cb88 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -100,12 +100,11 @@ PluginInterface::SamplingDevices LimeSDRInputPlugin::enumSampleSources() PluginInstanceGUI* LimeSDRInputPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - LimeSDRInputGUI* gui = new LimeSDRInputGUI(deviceAPI, deviceUISet); + LimeSDRInputGUI* gui = new LimeSDRInputGUI(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.h b/plugins/samplesource/limesdrinput/limesdrinputplugin.h index e9165aac3..bbd975698 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.h +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 21fb9bdcf..bc404d9a0 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -28,10 +28,9 @@ #include "ui_plutosdrinputgui.h" #include "plutosdrinputgui.h" -PlutoSDRInputGui::PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::PlutoSDRInputGUI), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), @@ -42,7 +41,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *devi m_doApplySettings(true), m_statusCounter(0) { - m_sampleSource = (PlutoSDRInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (PlutoSDRInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -156,15 +155,15 @@ void PlutoSDRInputGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -346,7 +345,7 @@ void PlutoSDRInputGui::blockApplySettings(bool block) void PlutoSDRInputGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -363,7 +362,7 @@ void PlutoSDRInputGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; @@ -395,7 +394,7 @@ void PlutoSDRInputGui::updateStatus() if (m_statusCounter % 10 == 0) // 5s { - if (m_deviceAPI->isBuddyLeader()) { + if (m_deviceUISet->m_deviceSourceAPI->isBuddyLeader()) { ((PlutoSDRInput *) m_sampleSource)->fetchTemperature(); } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 636b0dd07..09e52d805 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -38,7 +38,7 @@ class PlutoSDRInputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit PlutoSDRInputGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~PlutoSDRInputGui(); virtual void destroy(); @@ -54,7 +54,6 @@ public: private: Ui::PlutoSDRInputGUI* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; PlutoSDRInputSettings m_settings; bool m_forceSettings; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index f61b710ae..5441fdc2f 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -84,12 +84,11 @@ PluginInterface::SamplingDevices PlutoSDRInputPlugin::enumSampleSources() PluginInstanceGUI* PlutoSDRInputPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - PlutoSDRInputGui* gui = new PlutoSDRInputGui(deviceAPI, deviceUISet); + PlutoSDRInputGui* gui = new PlutoSDRInputGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h index e5c8540df..0587c3c90 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 99bc5b3d5..24789ff21 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -30,17 +30,16 @@ #include "dsp/dspcommands.h" -RTLSDRGui::RTLSDRGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::RTLSDRGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(0), m_lastEngineState((DSPDeviceSourceEngine::State)-1) { - m_sampleSource = (RTLSDRInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (RTLSDRInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -312,15 +311,15 @@ void RTLSDRGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -357,7 +356,7 @@ void RTLSDRGui::updateHardware() void RTLSDRGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -374,7 +373,7 @@ void RTLSDRGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index 20b58ed8d..f596112f0 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -37,7 +37,7 @@ class RTLSDRGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit RTLSDRGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~RTLSDRGui(); virtual void destroy(); @@ -55,7 +55,6 @@ public: private: Ui::RTLSDRGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; RTLSDRSettings m_settings; diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index 85eeb4519..aefe207f9 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -65,11 +65,10 @@ PluginInterface::SamplingDevices RTLSDRPlugin::enumSampleSources() PluginInstanceGUI* RTLSDRPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - RTLSDRGui* gui = new RTLSDRGui(deviceAPI, deviceUISet); + RTLSDRGui* gui = new RTLSDRGui(deviceUISet); *widget = gui; return gui; } else { diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.h b/plugins/samplesource/rtlsdr/rtlsdrplugin.h index 2119c1ebe..3337bd1d6 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.h +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.h @@ -23,7 +23,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp index 1814eaf8e..fc5ed4014 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.cpp @@ -46,10 +46,9 @@ #include "device/deviceuiset.h" #include "sdrdaemonsourcegui.h" -SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRdaemonSourceGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_sampleSource(NULL), m_acquisition(false), @@ -101,10 +100,10 @@ SDRdaemonSourceGui::SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet * connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - connect(&(deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&(m_deviceUISet->m_deviceSourceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); - m_sampleSource = (SDRdaemonSourceInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (SDRdaemonSourceInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); displaySettings(); @@ -594,15 +593,15 @@ void SDRdaemonSourceGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } @@ -733,7 +732,7 @@ void SDRdaemonSourceGui::updateHardware() void SDRdaemonSourceGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -750,7 +749,7 @@ void SDRdaemonSourceGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h index d2191f523..d9a038351 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h @@ -37,7 +37,7 @@ class SDRdaemonSourceGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit SDRdaemonSourceGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit SDRdaemonSourceGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SDRdaemonSourceGui(); virtual void destroy(); @@ -55,7 +55,6 @@ public: private: Ui::SDRdaemonSourceGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; SDRdaemonSourceSettings m_settings; //!< current settings SDRdaemonSourceSettings m_controlSettings; //!< settings last sent to device via control port diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index a64dacafd..abecda134 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -73,12 +73,11 @@ PluginInterface::SamplingDevices SDRdaemonSourcePlugin::enumSampleSources() PluginInstanceGUI* SDRdaemonSourcePlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - SDRdaemonSourceGui* gui = new SDRdaemonSourceGui(deviceAPI, deviceUISet); + SDRdaemonSourceGui* gui = new SDRdaemonSourceGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h index 1fc76fe42..1381ea617 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index 213b83e0e..790f9a75d 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -29,14 +29,13 @@ #include "dsp/dspcommands.h" -SDRPlayGui::SDRPlayGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRPlayGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true) { - m_sampleSource = (SDRPlayInput*) m_deviceAPI->getSampleSource(); + m_sampleSource = (SDRPlayInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -277,7 +276,7 @@ void SDRPlayGui::updateHardware() void SDRPlayGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSourceAPI->state(); if(m_lastEngineState != state) { @@ -294,7 +293,7 @@ void SDRPlayGui::updateStatus() break; case DSPDeviceSourceEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); break; default: break; @@ -436,15 +435,15 @@ void SDRPlayGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initAcquisition()) + if (m_deviceUISet->m_deviceSourceAPI->initAcquisition()) { - m_deviceAPI->startAcquisition(); + m_deviceUISet->m_deviceSourceAPI->startAcquisition(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopAcquisition(); + m_deviceUISet->m_deviceSourceAPI->stopAcquisition(); DSPEngine::instance()->stopAudioOutput(); } } diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 8c56b0713..5f1b0ca67 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -39,7 +39,7 @@ class SDRPlayGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit SDRPlayGui(DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SDRPlayGui(); virtual void destroy(); @@ -57,7 +57,6 @@ public: private: Ui::SDRPlayGui* ui; - DeviceSourceAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; SDRPlaySettings m_settings; diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index 910a7b539..cda7c23d1 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -86,12 +86,11 @@ PluginInterface::SamplingDevices SDRPlayPlugin::enumSampleSources() PluginInstanceGUI* SDRPlayPlugin::createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sourceId == m_deviceTypeID) { - SDRPlayGui* gui = new SDRPlayGui(deviceAPI, deviceUISet); + SDRPlayGui* gui = new SDRPlayGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.h b/plugins/samplesource/sdrplay/sdrplayplugin.h index e46a6f363..7f0505548 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.h +++ b/plugins/samplesource/sdrplay/sdrplayplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId, QWidget **widget, - DeviceSourceAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 77f5f6caf..388a79678 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -154,7 +154,6 @@ MainWindow::MainWindow(QWidget* parent) : PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), &gui, - m_deviceUIs.back()->m_deviceSourceAPI, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); @@ -241,7 +240,6 @@ void MainWindow::addSourceDevice() PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), &gui, - m_deviceUIs.back()->m_deviceSourceAPI, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); @@ -909,7 +907,6 @@ void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unus PluginInstanceGUI *pluginUI = deviceUI->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( deviceUI->m_deviceSourceAPI->getSampleSourceId(), &gui, - deviceUI->m_deviceSourceAPI, deviceUI); deviceUI->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); deviceUI->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginUI); diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 71a5a760e..6c2fd9fe0 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -64,7 +64,6 @@ public: virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( const QString& sourceId __attribute__((unused)), QWidget **widget __attribute__((unused)), - DeviceSourceAPI *deviceAPI __attribute__((unused)), DeviceUISet *deviceUISet __attribute__((unused))) { return 0; } From 35391e0d66201ae95be30d4427c06a0054e95746 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 01:11:35 +0100 Subject: [PATCH 03/39] Removed GLSpectrum getter from DeviceSinkAPI --- .../samplesink/bladerfoutput/bladerfoutputgui.cpp | 8 +++++--- .../samplesink/bladerfoutput/bladerfoutputgui.h | 4 +++- .../bladerfoutput/bladerfoutputplugin.cpp | 8 ++++++-- .../bladerfoutput/bladerfoutputplugin.h | 8 +++++++- plugins/samplesink/filesink/filesinkgui.cpp | 8 +++++--- plugins/samplesink/filesink/filesinkgui.h | 4 +++- plugins/samplesink/filesink/filesinkplugin.cpp | 8 ++++++-- plugins/samplesink/filesink/filesinkplugin.h | 6 +++++- .../samplesink/hackrfoutput/hackrfoutputgui.cpp | 8 +++++--- plugins/samplesink/hackrfoutput/hackrfoutputgui.h | 4 +++- .../hackrfoutput/hackrfoutputplugin.cpp | 8 ++++++-- .../samplesink/hackrfoutput/hackrfoutputplugin.h | 6 +++++- .../samplesink/limesdroutput/limesdroutputgui.cpp | 8 +++++--- .../samplesink/limesdroutput/limesdroutputgui.h | 4 +++- .../limesdroutput/limesdroutputplugin.cpp | 8 ++++++-- .../limesdroutput/limesdroutputplugin.h | 6 +++++- .../plutosdroutput/plutosdroutputgui.cpp | 8 +++++--- .../samplesink/plutosdroutput/plutosdroutputgui.h | 4 +++- .../plutosdroutput/plutosdroutputplugin.cpp | 8 ++++++-- .../plutosdroutput/plutosdroutputplugin.h | 6 +++++- .../samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp | 8 +++++--- .../samplesink/sdrdaemonsink/sdrdaemonsinkgui.h | 4 +++- .../sdrdaemonsink/sdrdaemonsinkplugin.cpp | 8 ++++++-- .../sdrdaemonsink/sdrdaemonsinkplugin.h | 6 +++++- sdrgui/device/devicesinkapi.cpp | 5 ----- sdrgui/device/devicesinkapi.h | 2 +- sdrgui/mainwindow.cpp | 10 ++++++++-- sdrgui/plugin/plugininterface.h | 15 +++++++++++++-- 28 files changed, 138 insertions(+), 52 deletions(-) diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp index 052996218..1416c2464 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp @@ -25,13 +25,15 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "bladerfoutputgui.h" #include "bladerf/devicebladerfvalues.h" -BladerfOutputGui::BladerfOutputGui(DeviceSinkAPI *deviceAPI, QWidget* parent) : +BladerfOutputGui::BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::BladerfOutputGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_deviceSampleSink(NULL), @@ -166,8 +168,8 @@ void BladerfOutputGui::handleInputMessages() void BladerfOutputGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(QString("%1k").arg(QString::number(m_sampleRate/1000.0, 'g', 5))); } diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputgui.h b/plugins/samplesink/bladerfoutput/bladerfoutputgui.h index b31157802..25ac938f0 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputgui.h +++ b/plugins/samplesink/bladerfoutput/bladerfoutputgui.h @@ -27,6 +27,7 @@ class DeviceSinkAPI; class DeviceSampleSink; +class DeviceUISet; namespace Ui { class BladerfOutputGui; @@ -36,7 +37,7 @@ class BladerfOutputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit BladerfOutputGui(DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~BladerfOutputGui(); virtual void destroy(); @@ -55,6 +56,7 @@ private: Ui::BladerfOutputGui* ui; DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; BladeRFOutputSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp index 5b870d6c6..09fef37ad 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp @@ -77,11 +77,15 @@ PluginInterface::SamplingDevices BladerfOutputPlugin::enumSampleSinks() return result; } -PluginInstanceGUI* BladerfOutputPlugin::createSampleSinkPluginInstanceGUI(const QString& sinkId,QWidget **widget, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* BladerfOutputPlugin::createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - BladerfOutputGui* gui = new BladerfOutputGui(deviceAPI); + BladerfOutputGui* gui = new BladerfOutputGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h index 62fd5755d..588ef982d 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h @@ -36,7 +36,13 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSinks(); - virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI); + + virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet); + virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesink/filesink/filesinkgui.cpp b/plugins/samplesink/filesink/filesinkgui.cpp index 69abb54ab..912592d50 100644 --- a/plugins/samplesink/filesink/filesinkgui.cpp +++ b/plugins/samplesink/filesink/filesinkgui.cpp @@ -32,12 +32,14 @@ #include "mainwindow.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "filesinkgui.h" -FileSinkGui::FileSinkGui(DeviceSinkAPI *deviceAPI, QWidget* parent) : +FileSinkGui::FileSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FileSinkGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_fileName("./test.sdriq"), @@ -177,8 +179,8 @@ void FileSinkGui::handleInputMessages() void FileSinkGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate*(1<getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(QString("%1k").arg(QString::number(m_sampleRate/1000.0, 'g', 5))); } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index 0db322c29..97feb4359 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -29,6 +29,7 @@ class DeviceSinkAPI; class DeviceSampleSink; +class DeviceUISet; namespace Ui { class HackRFOutputGui; @@ -46,7 +47,7 @@ public: HACKRF_IMGREJ_NB } HackRFImgRejValue; - explicit HackRFOutputGui(DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit HackRFOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~HackRFOutputGui(); virtual void destroy(); @@ -65,6 +66,7 @@ private: Ui::HackRFOutputGui* ui; DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; bool m_forceSettings; HackRFOutputSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index a6cd9f63d..06d2b2195 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -116,11 +116,15 @@ PluginInterface::SamplingDevices HackRFOutputPlugin::enumSampleSinks() return result; } -PluginInstanceGUI* HackRFOutputPlugin::createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* HackRFOutputPlugin::createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - HackRFOutputGui* gui = new HackRFOutputGui(deviceAPI); + HackRFOutputGui* gui = new HackRFOutputGui(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h index 6ba7809b2..38c243c1e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSinks(); - virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index e61ba2838..09bc4a87e 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -23,12 +23,14 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "limesdroutputgui.h" -LimeSDROutputGUI::LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent) : +LimeSDROutputGUI::LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::LimeSDROutputGUI), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_sampleRate(0), m_lastEngineState((DSPDeviceSinkEngine::State)-1), @@ -238,8 +240,8 @@ void LimeSDROutputGUI::handleInputMessages() void LimeSDROutputGUI::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index 2e34a76de..c3fefe642 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -27,6 +27,7 @@ class DeviceSinkAPI; class DeviceSampleSink; +class DeviceUISet; namespace Ui { class LimeSDROutputGUI; @@ -36,7 +37,7 @@ class LimeSDROutputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent = 0); + explicit LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~LimeSDROutputGUI(); virtual void destroy(); @@ -55,6 +56,7 @@ private: Ui::LimeSDROutputGUI* ui; DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; LimeSDROutput* m_limeSDROutput; //!< Same object as above but gives easy access to LimeSDROutput methods and attributes that are used intensively LimeSDROutputSettings m_settings; QTimer m_updateTimer; diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index 86583a99e..f0e5309ad 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -96,11 +96,15 @@ PluginInterface::SamplingDevices LimeSDROutputPlugin::enumSampleSinks() return result; } -PluginInstanceGUI* LimeSDROutputPlugin::createSampleSinkPluginInstanceGUI(const QString& sinkId,QWidget **widget, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* LimeSDROutputPlugin::createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - LimeSDROutputGUI* gui = new LimeSDROutputGUI(deviceAPI); + LimeSDROutputGUI* gui = new LimeSDROutputGUI(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.h b/plugins/samplesink/limesdroutput/limesdroutputplugin.h index bf012aa38..f971eac14 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.h +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSinks(); - virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index 893c2055e..b3918195f 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -22,15 +22,17 @@ #include "dsp/dspcommands.h" #include "gui/glspectrum.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "plutosdr/deviceplutosdr.h" #include "plutosdroutput.h" #include "plutosdroutputgui.h" #include "ui_plutosdroutputgui.h" -PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent) : +PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::PlutoSDROutputGUI), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), m_sampleSink(0), @@ -421,7 +423,7 @@ void PlutoSDROutputGUI::handleInputMessages() void PlutoSDROutputGUI::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index ece73df40..d5ea7a3df 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -28,6 +28,7 @@ class DeviceSinkAPI; class DeviceSampleSink; +class DeviceUISet; namespace Ui { class PlutoSDROutputGUI; @@ -37,7 +38,7 @@ class PlutoSDROutputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, QWidget* parent = 0); + explicit PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~PlutoSDROutputGUI(); virtual void destroy(); @@ -54,6 +55,7 @@ public: private: Ui::PlutoSDROutputGUI* ui; DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; PlutoSDROutputSettings m_settings; bool m_forceSettings; QTimer m_updateTimer; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 12b5831a6..8629517bb 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -81,11 +81,15 @@ PluginInterface::SamplingDevices PlutoSDROutputPlugin::enumSampleSinks() return result; } -PluginInstanceGUI* PlutoSDROutputPlugin::createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* PlutoSDROutputPlugin::createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - PlutoSDROutputGUI* gui = new PlutoSDROutputGUI(deviceAPI); + PlutoSDROutputGUI* gui = new PlutoSDROutputGUI(deviceAPI, deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h index 730a28473..051906eac 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h @@ -36,7 +36,11 @@ public: void initPlugin(PluginAPI* pluginAPI); virtual SamplingDevices enumSampleSinks(); - virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(const QString& sinkId, QWidget **widget, DeviceSinkAPI *deviceAPI); + virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( + const QString& sinkId, + QWidget **widget, + DeviceSinkAPI *deviceAPI, + DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); static const QString m_hardwareID; diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp index f3afc08ac..e9671c4cb 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp @@ -38,12 +38,14 @@ #include "mainwindow.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "sdrdaemonsinkgui.h" -SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceSinkAPI *deviceAPI, QWidget* parent) : +SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRdaemonSinkGui), m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_settings(), m_deviceSampleSink(0), m_sampleRate(0), @@ -211,8 +213,8 @@ void SDRdaemonSinkGui::handleInputMessages() void SDRdaemonSinkGui::updateSampleRateAndFrequency() { - m_deviceAPI->getSpectrum()->setSampleRate(m_sampleRate); - m_deviceAPI->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate*(1<getMessageQueueToGUI(); } -GLSpectrum *DeviceSinkAPI::getSpectrum() -{ - return m_spectrum; -} - void DeviceSinkAPI::addChannelMarker(ChannelMarker* channelMarker) { m_spectrum->addChannelMarker(channelMarker); diff --git a/sdrgui/device/devicesinkapi.h b/sdrgui/device/devicesinkapi.h index b83c4ba0d..100f25993 100644 --- a/sdrgui/device/devicesinkapi.h +++ b/sdrgui/device/devicesinkapi.h @@ -66,8 +66,8 @@ public: MessageQueue *getDeviceEngineInputMessageQueue(); MessageQueue *getSampleSinkInputMessageQueue(); MessageQueue *getSampleSinkGUIMessageQueue(); + // device related stuff - GLSpectrum *getSpectrum(); //!< Direct spectrum getter void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 388a79678..4345e1770 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -296,7 +296,10 @@ void MainWindow::addSinkDevice() m_deviceUIs.back()->m_deviceSinkAPI->setSampleSink(sink); QWidget *gui; PluginInstanceGUI *pluginUI = m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( - m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkId(), &gui, m_deviceUIs.back()->m_deviceSinkAPI); + m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkId(), + &gui, + m_deviceUIs.back()->m_deviceSinkAPI, + m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSinkAPI->getSampleSink()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkPluginInstanceUI(pluginUI); setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkDisplayName(), false); @@ -986,7 +989,10 @@ void MainWindow::on_sampleSink_confirmClicked(bool checked __attribute__((unused deviceUI->m_deviceSinkAPI->setSampleSink(sink); QWidget *gui; PluginInstanceGUI *pluginUI = deviceUI->m_deviceSinkAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( - deviceUI->m_deviceSinkAPI->getSampleSinkId(), &gui, deviceUI->m_deviceSinkAPI); + deviceUI->m_deviceSinkAPI->getSampleSinkId(), + &gui, + deviceUI->m_deviceSinkAPI, + deviceUI); deviceUI->m_deviceSinkAPI->getSampleSink()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); deviceUI->m_deviceSinkAPI->setSampleSinkPluginInstanceUI(pluginUI); setDeviceGUI(currentSinkTabIndex, gui, deviceUI->m_deviceSinkAPI->getSampleSinkDisplayName(), false); diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 6c2fd9fe0..a26165e13 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -53,12 +53,15 @@ public: virtual void initPlugin(PluginAPI* pluginAPI) = 0; // channel Rx plugins + virtual PluginInstanceGUI* createRxChannel(const QString& channelName __attribute__((unused)), DeviceSourceAPI *deviceAPI __attribute__((unused)) ) { return 0; } // channel Tx plugins + virtual PluginInstanceGUI* createTxChannel(const QString& channelName __attribute__((unused)), DeviceSinkAPI *deviceAPI __attribute__((unused)) ) { return 0; } // device source plugins only + virtual SamplingDevices enumSampleSources() { return SamplingDevices(); } virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( @@ -72,9 +75,17 @@ public: virtual void deleteSampleSourcePluginInstanceInput(DeviceSampleSource *source); // device sink plugins only + virtual SamplingDevices enumSampleSinks() { return SamplingDevices(); } - virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(const QString& sinkId __attribute__((unused)), QWidget **widget __attribute__((unused)), DeviceSinkAPI *deviceAPI __attribute__((unused))) { return 0; } - virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId __attribute__((unused)), DeviceSinkAPI *deviceAPI __attribute__((unused))) { return 0; } // creates the output "core" + + virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( + const QString& sinkId __attribute__((unused)), + QWidget **widget __attribute__((unused)), + DeviceSinkAPI *deviceAPI __attribute__((unused)), + DeviceUISet *deviceUISet __attribute__((unused))) + { return 0; } + + virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId __attribute__((unused)), DeviceSinkAPI *deviceAPI __attribute__((unused))) { return 0; } // creates the output "core" virtual void deleteSampleSinkPluginInstanceGUI(PluginInstanceGUI *ui); virtual void deleteSampleSinkPluginInstanceOutput(DeviceSampleSink *sink); }; From 7e2f302909f541e4b8753724f7f39049941ca3ff Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 01:48:36 +0100 Subject: [PATCH 04/39] DeviceSourceAPI: effectively delete the UI when changing the PluginInstanceGUI reference --- sdrgui/device/devicesourceapi.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 5c9601193..89f0e503c 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -176,6 +176,10 @@ void DeviceSourceAPI::setSampleSourcePluginInterface(PluginInterface *iface) void DeviceSourceAPI::setSampleSourcePluginInstanceGUI(PluginInstanceGUI *gui) { + if (m_sampleSourcePluginInstanceUI && (gui != m_sampleSourcePluginInstanceUI)) { + m_sampleSourcePluginInstanceUI->destroy(); + } + m_sampleSourcePluginInstanceUI = gui; } From c3b9c268ee2f63a364bdc6ef10856cd917d1afd7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 02:15:59 +0100 Subject: [PATCH 05/39] Better fix previous source plugin GUI delete by doing it explicitly in the main window --- sdrgui/device/devicesourceapi.cpp | 4 ---- sdrgui/mainwindow.cpp | 9 +++++++++ sdrgui/plugin/plugininterface.cpp | 8 ++++---- sdrgui/plugin/pluginmanager.cpp | 2 -- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 89f0e503c..5c9601193 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -176,10 +176,6 @@ void DeviceSourceAPI::setSampleSourcePluginInterface(PluginInterface *iface) void DeviceSourceAPI::setSampleSourcePluginInstanceGUI(PluginInstanceGUI *gui) { - if (m_sampleSourcePluginInstanceUI && (gui != m_sampleSourcePluginInstanceUI)) { - m_sampleSourcePluginInstanceUI->destroy(); - } - m_sampleSourcePluginInstanceUI = gui; } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 4345e1770..710a14ea7 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -147,6 +147,10 @@ MainWindow::MainWindow(QWidget* parent) : exit(0); } + // delete previous plugin GUI + m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); + DeviceSampleSource *source = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), m_deviceUIs.back()->m_deviceSourceAPI); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); @@ -233,6 +237,11 @@ void MainWindow::addSourceDevice() // Create a file source instance by default m_pluginManager->selectSampleSourceBySerialOrSequence("sdrangel.samplesource.filesource", "0", 0, m_deviceUIs.back()->m_deviceSourceAPI); + + // delete previous plugin GUI + m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); + DeviceSampleSource *source = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), m_deviceUIs.back()->m_deviceSourceAPI); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); diff --git a/sdrgui/plugin/plugininterface.cpp b/sdrgui/plugin/plugininterface.cpp index 7db1c63ed..458379fd6 100644 --- a/sdrgui/plugin/plugininterface.cpp +++ b/sdrgui/plugin/plugininterface.cpp @@ -6,20 +6,20 @@ void PluginInterface::deleteSampleSourcePluginInstanceGUI(PluginInstanceGUI *ui) { - ui->destroy(); + if (ui) { ui->destroy(); } } void PluginInterface::deleteSampleSourcePluginInstanceInput(DeviceSampleSource *source) { - source->destroy(); + if (source) { source->destroy(); } } void PluginInterface::deleteSampleSinkPluginInstanceGUI(PluginInstanceGUI *ui) { - ui->destroy(); + if (ui) { ui->destroy(); } } void PluginInterface::deleteSampleSinkPluginInstanceOutput(DeviceSampleSink *sink) { - sink->destroy(); + if (sink) { sink->destroy(); } } diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 4efe26d58..b3ef7ec25 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -355,7 +355,6 @@ int PluginManager::selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceA << " seq: " << m_sampleSourceDevices[index].m_deviceSequence; deviceAPI->stopAcquisition(); - deviceAPI->setSampleSourcePluginInstanceGUI(0); // this effectively destroys the previous GUI if it exists deviceAPI->setSampleSourceSequence(m_sampleSourceDevices[index].m_deviceSequence); deviceAPI->setHardwareId(m_sampleSourceDevices[index].m_hadrwareId); @@ -427,7 +426,6 @@ int PluginManager::selectSampleSourceBySerialOrSequence(const QString& sourceId, << " seq: " << m_sampleSourceDevices[index].m_deviceSequence; deviceAPI->stopAcquisition(); - deviceAPI->setSampleSourcePluginInstanceGUI(0); // this effectively destroys the previous GUI if it exists // m_sampleSourcePluginGUI = pluginGUI; deviceAPI->setSampleSourceSequence(m_sampleSourceDevices[index].m_deviceSequence); From b770e656b3924e74d34a0164b781324dcb63d6aa Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 02:23:06 +0100 Subject: [PATCH 06/39] Fix previous sink plugin GUI delete by doing it explicitly in the main window --- sdrgui/mainwindow.cpp | 5 +++++ sdrgui/plugin/pluginmanager.cpp | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 710a14ea7..b9a1475b2 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -300,6 +300,11 @@ void MainWindow::addSinkDevice() // create a file sink by default m_pluginManager->selectSampleSinkBySerialOrSequence("sdrangel.samplesink.filesink", "0", 0, m_deviceUIs.back()->m_deviceSinkAPI); + + // delete previous plugin GUI if it exists + m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkPluginInstanceGUI()); + DeviceSampleSink *sink = m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->createSampleSinkPluginInstanceOutput( m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkId(), m_deviceUIs.back()->m_deviceSinkAPI); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSink(sink); diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index b3ef7ec25..1f1836dcf 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -498,7 +498,6 @@ int PluginManager::selectSampleSinkBySerialOrSequence(const QString& sinkId, con << " seq: " << m_sampleSinkDevices[index].m_deviceSequence; deviceAPI->stopGeneration(); - deviceAPI->setSampleSinkPluginInstanceUI(0); // this effectively destroys the previous GUI if it exists // m_sampleSourcePluginGUI = pluginGUI; deviceAPI->setSampleSinkSequence(m_sampleSinkDevices[index].m_deviceSequence); From c3de8953014d49ff648cca701b6cf1c938598074 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 02:54:22 +0100 Subject: [PATCH 07/39] Removed direct reference to the DeviceSinkAPI in the sink GUIs. Removed DeviceSourceAPI forward declaration in source GUI headers --- .../bladerfoutput/bladerfoutputgui.cpp | 17 ++++++++--------- .../bladerfoutput/bladerfoutputgui.h | 4 +--- .../bladerfoutput/bladerfoutputplugin.cpp | 3 +-- .../bladerfoutput/bladerfoutputplugin.h | 1 - plugins/samplesink/filesink/filesinkgui.cpp | 17 ++++++++--------- plugins/samplesink/filesink/filesinkgui.h | 4 +--- .../samplesink/filesink/filesinkplugin.cpp | 3 +-- plugins/samplesink/filesink/filesinkplugin.h | 1 - .../hackrfoutput/hackrfoutputgui.cpp | 19 +++++++++---------- .../samplesink/hackrfoutput/hackrfoutputgui.h | 4 +--- .../hackrfoutput/hackrfoutputplugin.cpp | 3 +-- .../hackrfoutput/hackrfoutputplugin.h | 1 - .../limesdroutput/limesdroutputgui.cpp | 19 +++++++++---------- .../limesdroutput/limesdroutputgui.h | 4 +--- .../limesdroutput/limesdroutputplugin.cpp | 3 +-- .../limesdroutput/limesdroutputplugin.h | 1 - .../plutosdroutput/plutosdroutputgui.cpp | 17 ++++++++--------- .../plutosdroutput/plutosdroutputgui.h | 4 +--- .../plutosdroutput/plutosdroutputplugin.cpp | 3 +-- .../plutosdroutput/plutosdroutputplugin.h | 1 - .../sdrdaemonsink/sdrdaemonsinkgui.cpp | 17 ++++++++--------- .../sdrdaemonsink/sdrdaemonsinkgui.h | 4 +--- .../sdrdaemonsink/sdrdaemonsinkplugin.cpp | 3 +-- .../sdrdaemonsink/sdrdaemonsinkplugin.h | 1 - plugins/samplesource/airspy/airspygui.h | 1 - .../bladerfinput/bladerfinputgui.h | 1 - plugins/samplesource/fcdpro/fcdprogui.h | 1 - .../samplesource/fcdproplus/fcdproplusgui.h | 1 - .../samplesource/filesource/filesourcegui.h | 1 - .../samplesource/hackrfinput/hackrfinputgui.h | 1 - .../limesdrinput/limesdrinputgui.h | 1 - .../plutosdrinput/plutosdrinputgui.h | 1 - plugins/samplesource/rtlsdr/rtlsdrgui.h | 1 - .../sdrdaemonsource/sdrdaemonsourcegui.h | 1 - plugins/samplesource/sdrplay/sdrplaygui.h | 1 - sdrgui/mainwindow.cpp | 2 -- sdrgui/plugin/plugininterface.h | 1 - 37 files changed, 62 insertions(+), 106 deletions(-) diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp index 1416c2464..bfb0a3cf9 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputgui.cpp @@ -29,10 +29,9 @@ #include "bladerfoutputgui.h" #include "bladerf/devicebladerfvalues.h" -BladerfOutputGui::BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +BladerfOutputGui::BladerfOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::BladerfOutputGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), @@ -40,7 +39,7 @@ BladerfOutputGui::BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *device m_sampleRate(0), m_lastEngineState((DSPDeviceSinkEngine::State)-1) { - m_deviceSampleSink = (BladerfOutput*) m_deviceAPI->getSampleSink(); + m_deviceSampleSink = (BladerfOutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -63,7 +62,7 @@ BladerfOutputGui::BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *device displaySettings(); char recFileNameCStr[30]; - sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceAPI->getDeviceUID()); + sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceUISet->m_deviceSinkAPI->getDeviceUID()); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -309,15 +308,15 @@ void BladerfOutputGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - m_deviceAPI->startGeneration(); + m_deviceUISet->m_deviceSinkAPI->startGeneration(); DSPEngine::instance()->startAudioInput(); } } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->stopAudioInput(); } } @@ -333,7 +332,7 @@ void BladerfOutputGui::updateHardware() void BladerfOutputGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -350,7 +349,7 @@ void BladerfOutputGui::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputgui.h b/plugins/samplesink/bladerfoutput/bladerfoutputgui.h index 25ac938f0..e70aaa517 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputgui.h +++ b/plugins/samplesink/bladerfoutput/bladerfoutputgui.h @@ -25,7 +25,6 @@ #include "bladerfoutput.h" -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -37,7 +36,7 @@ class BladerfOutputGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit BladerfOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit BladerfOutputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~BladerfOutputGui(); virtual void destroy(); @@ -55,7 +54,6 @@ public: private: Ui::BladerfOutputGui* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; BladeRFOutputSettings m_settings; diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp index 09fef37ad..dddeac766 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp @@ -80,12 +80,11 @@ PluginInterface::SamplingDevices BladerfOutputPlugin::enumSampleSinks() PluginInstanceGUI* BladerfOutputPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - BladerfOutputGui* gui = new BladerfOutputGui(deviceAPI, deviceUISet); + BladerfOutputGui* gui = new BladerfOutputGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h index 588ef982d..a2e057995 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.h @@ -40,7 +40,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesink/filesink/filesinkgui.cpp b/plugins/samplesink/filesink/filesinkgui.cpp index 912592d50..8bcd9c22b 100644 --- a/plugins/samplesink/filesink/filesinkgui.cpp +++ b/plugins/samplesink/filesink/filesinkgui.cpp @@ -35,10 +35,9 @@ #include "device/deviceuiset.h" #include "filesinkgui.h" -FileSinkGui::FileSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +FileSinkGui::FileSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::FileSinkGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), @@ -61,14 +60,14 @@ FileSinkGui::FileSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWi ui->fileNameText->setText(m_fileName); - connect(&(m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&(m_deviceUISet->m_deviceSinkAPI->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_deviceSampleSink = (FileSinkOutput*) m_deviceAPI->getSampleSink(); + m_deviceSampleSink = (FileSinkOutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -208,7 +207,7 @@ void FileSinkGui::updateHardware() void FileSinkGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -225,7 +224,7 @@ void FileSinkGui::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; @@ -262,9 +261,9 @@ void FileSinkGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - if (!m_deviceAPI->startGeneration()) + if (!m_deviceUISet->m_deviceSinkAPI->startGeneration()) { qDebug("FileSinkGui::on_startStop_toggled: device start failed"); } @@ -274,7 +273,7 @@ void FileSinkGui::on_startStop_toggled(bool checked) } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->stopAudioInput(); } } diff --git a/plugins/samplesink/filesink/filesinkgui.h b/plugins/samplesink/filesink/filesinkgui.h index 786f6ffba..999394b78 100644 --- a/plugins/samplesink/filesink/filesinkgui.h +++ b/plugins/samplesink/filesink/filesinkgui.h @@ -27,7 +27,6 @@ #include "filesinksettings.h" -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -39,7 +38,7 @@ class FileSinkGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit FileSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit FileSinkGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~FileSinkGui(); virtual void destroy(); @@ -57,7 +56,6 @@ public: private: Ui::FileSinkGui* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; FileSinkSettings m_settings; diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index b317b0ced..afa70a6cd 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -72,12 +72,11 @@ PluginInterface::SamplingDevices FileSinkPlugin::enumSampleSinks() PluginInstanceGUI* FileSinkPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - FileSinkGui* gui = new FileSinkGui(deviceAPI, deviceUISet); + FileSinkGui* gui = new FileSinkGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/filesink/filesinkplugin.h b/plugins/samplesink/filesink/filesinkplugin.h index 4eebce404..e973f2e5e 100644 --- a/plugins/samplesink/filesink/filesinkplugin.h +++ b/plugins/samplesink/filesink/filesinkplugin.h @@ -40,7 +40,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index bace1f2d6..4675f8d4f 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -33,10 +33,9 @@ #include "ui_hackrfoutputgui.h" -HackRFOutputGui::HackRFOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::HackRFOutputGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), @@ -44,7 +43,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUI m_lastEngineState((DSPDeviceSinkEngine::State)-1), m_doApplySettings(true) { - m_deviceSampleSink = (HackRFOutput*) m_deviceAPI->getSampleSink(); + m_deviceSampleSink = (HackRFOutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -302,21 +301,21 @@ void HackRFOutputGui::on_startStop_toggled(bool checked) if (checked) { // forcibly stop the Rx if present before starting - if (m_deviceAPI->getSourceBuddies().size() > 0) + if (m_deviceUISet->m_deviceSinkAPI->getSourceBuddies().size() > 0) { - DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[0]; + DeviceSourceAPI *buddy = m_deviceUISet->m_deviceSinkAPI->getSourceBuddies()[0]; buddy->stopAcquisition(); } - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - m_deviceAPI->startGeneration(); + m_deviceUISet->m_deviceSinkAPI->startGeneration(); DSPEngine::instance()->startAudioInput(); } } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->startAudioInput(); } } @@ -335,7 +334,7 @@ void HackRFOutputGui::updateHardware() void HackRFOutputGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -353,7 +352,7 @@ void HackRFOutputGui::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index 97feb4359..fdcba768e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -27,7 +27,6 @@ #define HACKRF_MAX_DEVICE (32) -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -47,7 +46,7 @@ public: HACKRF_IMGREJ_NB } HackRFImgRejValue; - explicit HackRFOutputGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~HackRFOutputGui(); virtual void destroy(); @@ -65,7 +64,6 @@ public: private: Ui::HackRFOutputGui* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; bool m_forceSettings; HackRFOutputSettings m_settings; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index 06d2b2195..b2325ab69 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -119,12 +119,11 @@ PluginInterface::SamplingDevices HackRFOutputPlugin::enumSampleSinks() PluginInstanceGUI* HackRFOutputPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - HackRFOutputGui* gui = new HackRFOutputGui(deviceAPI, deviceUISet); + HackRFOutputGui* gui = new HackRFOutputGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h index 38c243c1e..33af573f8 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 09bc4a87e..ca7d7558a 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -26,10 +26,9 @@ #include "device/deviceuiset.h" #include "limesdroutputgui.h" -LimeSDROutputGUI::LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::LimeSDROutputGUI), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_sampleRate(0), @@ -39,7 +38,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *device m_statusCounter(0), m_deviceStatusCounter(0) { - m_limeSDROutput = (LimeSDROutput*) m_deviceAPI->getSampleSink(); + m_limeSDROutput = (LimeSDROutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); ui->setupUi(this); @@ -74,7 +73,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *device displaySettings(); char recFileNameCStr[30]; - sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceAPI->getDeviceUID()); + sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceUISet->m_deviceSinkAPI->getDeviceUID()); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -311,7 +310,7 @@ void LimeSDROutputGUI::updateHardware() void LimeSDROutputGUI::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -328,7 +327,7 @@ void LimeSDROutputGUI::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; @@ -354,7 +353,7 @@ void LimeSDROutputGUI::updateStatus() } else { - if (m_deviceAPI->isBuddyLeader()) + if (m_deviceUISet->m_deviceSinkAPI->isBuddyLeader()) { LimeSDROutput::MsgGetDeviceInfo* message = LimeSDROutput::MsgGetDeviceInfo::create(); m_limeSDROutput->getInputMessageQueue()->push(message); @@ -373,15 +372,15 @@ void LimeSDROutputGUI::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - m_deviceAPI->startGeneration(); + m_deviceUISet->m_deviceSinkAPI->startGeneration(); DSPEngine::instance()->startAudioInput(); } } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->stopAudioInput(); } } diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index c3fefe642..bbe3e6533 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -25,7 +25,6 @@ #include "limesdroutput.h" -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -37,7 +36,7 @@ class LimeSDROutputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit LimeSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~LimeSDROutputGUI(); virtual void destroy(); @@ -55,7 +54,6 @@ public: private: Ui::LimeSDROutputGUI* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; LimeSDROutput* m_limeSDROutput; //!< Same object as above but gives easy access to LimeSDROutput methods and attributes that are used intensively LimeSDROutputSettings m_settings; diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index f0e5309ad..e06f0f032 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -99,12 +99,11 @@ PluginInterface::SamplingDevices LimeSDROutputPlugin::enumSampleSinks() PluginInstanceGUI* LimeSDROutputPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - LimeSDROutputGUI* gui = new LimeSDROutputGUI(deviceAPI, deviceUISet); + LimeSDROutputGUI* gui = new LimeSDROutputGUI(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.h b/plugins/samplesink/limesdroutput/limesdroutputplugin.h index f971eac14..bdad3109b 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.h +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index b3918195f..e6cc6be5c 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -28,10 +28,9 @@ #include "plutosdroutputgui.h" #include "ui_plutosdroutputgui.h" -PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::PlutoSDROutputGUI), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_forceSettings(true), @@ -42,7 +41,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *devi m_doApplySettings(true), m_statusCounter(0) { - m_sampleSink = (PlutoSDROutput*) m_deviceAPI->getSampleSink(); + m_sampleSink = (PlutoSDROutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); ui->setupUi(this); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -156,15 +155,15 @@ void PlutoSDROutputGUI::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - m_deviceAPI->startGeneration(); + m_deviceUISet->m_deviceSinkAPI->startGeneration(); DSPEngine::instance()->startAudioOutput(); } } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->stopAudioOutput(); } } @@ -306,7 +305,7 @@ void PlutoSDROutputGUI::blockApplySettings(bool block) void PlutoSDROutputGUI::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -323,7 +322,7 @@ void PlutoSDROutputGUI::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; @@ -352,7 +351,7 @@ void PlutoSDROutputGUI::updateStatus() if (m_statusCounter % 10 == 0) // 5s { - if (m_deviceAPI->isBuddyLeader()) { + if (m_deviceUISet->m_deviceSinkAPI->isBuddyLeader()) { ((PlutoSDROutput *) m_sampleSink)->fetchTemperature(); } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index d5ea7a3df..f9a010f4c 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -26,7 +26,6 @@ #include "plutosdroutputsettings.h" -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -38,7 +37,7 @@ class PlutoSDROutputGUI : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit PlutoSDROutputGUI(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~PlutoSDROutputGUI(); virtual void destroy(); @@ -54,7 +53,6 @@ public: private: Ui::PlutoSDROutputGUI* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; PlutoSDROutputSettings m_settings; bool m_forceSettings; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 8629517bb..d360745f7 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -84,12 +84,11 @@ PluginInterface::SamplingDevices PlutoSDROutputPlugin::enumSampleSinks() PluginInstanceGUI* PlutoSDROutputPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - PlutoSDROutputGUI* gui = new PlutoSDROutputGUI(deviceAPI, deviceUISet); + PlutoSDROutputGUI* gui = new PlutoSDROutputGUI(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h index 051906eac..400add752 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.h @@ -39,7 +39,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp index e9671c4cb..e08580194 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp @@ -41,10 +41,9 @@ #include "device/deviceuiset.h" #include "sdrdaemonsinkgui.h" -SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent) : +SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : QWidget(parent), ui(new Ui::SDRdaemonSinkGui), - m_deviceAPI(deviceAPI), m_deviceUISet(deviceUISet), m_settings(), m_deviceSampleSink(0), @@ -80,12 +79,12 @@ SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *device ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); ui->sampleRate->setValueRange(7, 32000U, 9000000U); - connect(&(m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + connect(&(m_deviceUISet->m_deviceSinkAPI->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); - m_deviceSampleSink = (SDRdaemonSinkOutput*) m_deviceAPI->getSampleSink(); + m_deviceSampleSink = (SDRdaemonSinkOutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -352,7 +351,7 @@ void SDRdaemonSinkGui::updateHardware() void SDRdaemonSinkGui::updateStatus() { - int state = m_deviceAPI->state(); + int state = m_deviceUISet->m_deviceSinkAPI->state(); if(m_lastEngineState != state) { @@ -369,7 +368,7 @@ void SDRdaemonSinkGui::updateStatus() break; case DSPDeviceSinkEngine::StError: ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); - QMessageBox::information(this, tr("Message"), m_deviceAPI->errorMessage()); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage()); break; default: break; @@ -502,9 +501,9 @@ void SDRdaemonSinkGui::on_startStop_toggled(bool checked) { if (checked) { - if (m_deviceAPI->initGeneration()) + if (m_deviceUISet->m_deviceSinkAPI->initGeneration()) { - if (!m_deviceAPI->startGeneration()) + if (!m_deviceUISet->m_deviceSinkAPI->startGeneration()) { qDebug("SDRdaemonSinkGui::on_startStop_toggled: device start failed"); } @@ -514,7 +513,7 @@ void SDRdaemonSinkGui::on_startStop_toggled(bool checked) } else { - m_deviceAPI->stopGeneration(); + m_deviceUISet->m_deviceSinkAPI->stopGeneration(); DSPEngine::instance()->stopAudioInput(); } } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h index 91aee1007..e562d3675 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.h @@ -28,7 +28,6 @@ #include "sdrdaemonsinkoutput.h" -class DeviceSinkAPI; class DeviceSampleSink; class DeviceUISet; @@ -40,7 +39,7 @@ class SDRdaemonSinkGui : public QWidget, public PluginInstanceGUI { Q_OBJECT public: - explicit SDRdaemonSinkGui(DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SDRdaemonSinkGui(); virtual void destroy(); @@ -58,7 +57,6 @@ public: private: Ui::SDRdaemonSinkGui* ui; - DeviceSinkAPI* m_deviceAPI; DeviceUISet* m_deviceUISet; SDRdaemonSinkSettings m_settings; //!< current settings SDRdaemonSinkSettings m_controlSettings; //!< settings last sent to device via control port diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index a7de626de..bd206e01f 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -73,12 +73,11 @@ PluginInterface::SamplingDevices SDRdaemonSinkPlugin::enumSampleSinks() PluginInstanceGUI* SDRdaemonSinkPlugin::createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet) { if(sinkId == m_deviceTypeID) { - SDRdaemonSinkGui* gui = new SDRdaemonSinkGui(deviceAPI, deviceUISet); + SDRdaemonSinkGui* gui = new SDRdaemonSinkGui(deviceUISet); *widget = gui; return gui; } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h index d4f29518e..674b95c3e 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.h @@ -40,7 +40,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId, QWidget **widget, - DeviceSinkAPI *deviceAPI, DeviceUISet *deviceUISet); virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI); diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 29deed649..563bf720b 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -26,7 +26,6 @@ #define AIRSPY_MAX_DEVICE (32) -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/bladerfinput/bladerfinputgui.h b/plugins/samplesource/bladerfinput/bladerfinputgui.h index 1aa1cb8f3..094cda356 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputgui.h +++ b/plugins/samplesource/bladerfinput/bladerfinputgui.h @@ -25,7 +25,6 @@ #include "bladerfinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 3e278dc54..771c20b9e 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -25,7 +25,6 @@ #include "fcdproinput.h" -class DeviceSourceAPI; class QWidget; class DeviceUISet; diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index fff755603..70c566310 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -25,7 +25,6 @@ #include "fcdproplusinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/filesource/filesourcegui.h b/plugins/samplesource/filesource/filesourcegui.h index ead6d3670..c6a891fb1 100644 --- a/plugins/samplesource/filesource/filesourcegui.h +++ b/plugins/samplesource/filesource/filesourcegui.h @@ -25,7 +25,6 @@ #include "filesourceinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index f0ea24702..5a47a2fa0 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -27,7 +27,6 @@ #define HACKRF_MAX_DEVICE (32) -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index e1959b2e6..ed73d3994 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -25,7 +25,6 @@ #include "limesdrinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 09e52d805..3e2c622db 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -26,7 +26,6 @@ #include "plutosdrinputsettings.h" -class DeviceSourceAPI; class DeviceSampleSource; class DeviceUISet; diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index f596112f0..05ae61759 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -25,7 +25,6 @@ #include "rtlsdrinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h index d9a038351..b66b536b7 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourcegui.h @@ -26,7 +26,6 @@ #include "sdrdaemonsourceinput.h" -class DeviceSourceAPI; class DeviceUISet; namespace Ui { diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 5f1b0ca67..ea0f08b70 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -27,7 +27,6 @@ #include "sdrplayinput.h" #include "sdrplaysettings.h" -class DeviceSampleSource; class DeviceSourceAPI; class DeviceUISet; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index b9a1475b2..ac62be906 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -312,7 +312,6 @@ void MainWindow::addSinkDevice() PluginInstanceGUI *pluginUI = m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkId(), &gui, - m_deviceUIs.back()->m_deviceSinkAPI, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceSinkAPI->getSampleSink()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkPluginInstanceUI(pluginUI); @@ -1005,7 +1004,6 @@ void MainWindow::on_sampleSink_confirmClicked(bool checked __attribute__((unused PluginInstanceGUI *pluginUI = deviceUI->m_deviceSinkAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( deviceUI->m_deviceSinkAPI->getSampleSinkId(), &gui, - deviceUI->m_deviceSinkAPI, deviceUI); deviceUI->m_deviceSinkAPI->getSampleSink()->setMessageQueueToGUI(pluginUI->getInputMessageQueue()); deviceUI->m_deviceSinkAPI->setSampleSinkPluginInstanceUI(pluginUI); diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index a26165e13..584fc80f2 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -81,7 +81,6 @@ public: virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI( const QString& sinkId __attribute__((unused)), QWidget **widget __attribute__((unused)), - DeviceSinkAPI *deviceAPI __attribute__((unused)), DeviceUISet *deviceUISet __attribute__((unused))) { return 0; } From 2deccbc259e18528132211b9bcbe6541e175b5c1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 03:02:45 +0100 Subject: [PATCH 08/39] LimeSDR: have still a 1ms delay after start/stop stream as it makes the Tx gain setting a bit more fluid --- plugins/samplesink/limesdroutput/limesdroutputthread.cpp | 2 ++ plugins/samplesource/limesdrinput/limesdrinputthread.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugins/samplesink/limesdroutput/limesdroutputthread.cpp b/plugins/samplesink/limesdroutput/limesdroutputthread.cpp index 985c9ecfb..e6da26cc8 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputthread.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputthread.cpp @@ -41,6 +41,7 @@ void LimeSDROutputThread::startWork() if (LMS_StartStream(m_stream) < 0) { qCritical("LimeSDROutputThread::startWork: could not start stream"); } else { + usleep(1000); qDebug("LimeSDROutputThread::startWork: stream started"); } @@ -61,6 +62,7 @@ void LimeSDROutputThread::stopWork() if (LMS_StopStream(m_stream) < 0) { qCritical("LimeSDROutputThread::stopWork: could not stop stream"); } else { + usleep(1000); qDebug("LimeSDROutputThread::stopWork: stream stopped"); } } diff --git a/plugins/samplesource/limesdrinput/limesdrinputthread.cpp b/plugins/samplesource/limesdrinput/limesdrinputthread.cpp index 27e4375f3..7c99d004b 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputthread.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputthread.cpp @@ -42,6 +42,7 @@ void LimeSDRInputThread::startWork() if (LMS_StartStream(m_stream) < 0) { qCritical("LimeSDRInputThread::startWork: could not start stream"); } else { + usleep(1000); qDebug("LimeSDRInputThread::startWork: stream started"); } @@ -62,6 +63,7 @@ void LimeSDRInputThread::stopWork() if (LMS_StopStream(m_stream) < 0) { qCritical("LimeSDRInputThread::stopWork: could not stop stream"); } else { + usleep(1000); qDebug("LimeSDRInputThread::stopWork: stream stopped"); } } From edc427804cb9493b496e3ad9a29ff563bb1e6839 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 30 Oct 2017 22:45:53 +0100 Subject: [PATCH 09/39] Copy channel registration stuff from DeviceSourceAPI to DeviceUISet --- sdrgui/device/devicesourceapi.h | 5 +- sdrgui/device/deviceuiset.cpp | 156 +++++++++++++++++++++++++++++++- sdrgui/device/deviceuiset.h | 31 +++++++ 3 files changed, 188 insertions(+), 4 deletions(-) diff --git a/sdrgui/device/devicesourceapi.h b/sdrgui/device/devicesourceapi.h index a6f03b15c..a6119df33 100644 --- a/sdrgui/device/devicesourceapi.h +++ b/sdrgui/device/devicesourceapi.h @@ -90,13 +90,12 @@ public: void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); void removeChannelInstance(PluginInstanceGUI* pluginGUI); - void freeChannels(); + void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveChannelSettings(Preset* preset); void loadSourceSettings(const Preset* preset); void saveSourceSettings(Preset* preset); - void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveChannelSettings(Preset* preset); DSPDeviceSourceEngine *getDeviceSourceEngine() { return m_deviceSourceEngine; } diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 4590027b3..4998a3387 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -25,6 +25,10 @@ #include "dsp/dspdevicesinkengine.h" #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" +#include "plugin/plugininstancegui.h" +#include "plugin/pluginapi.h" +#include "plugin/plugininterface.h" +#include "settings/preset.h" #include "deviceuiset.h" @@ -59,6 +63,156 @@ DeviceUISet::~DeviceUISet() delete m_spectrum; } - +void DeviceUISet::registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) +{ + m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); + renameChannelInstances(); +} + +void DeviceUISet::removeChannelInstance(PluginInstanceGUI* pluginGUI) +{ + for(ChannelInstanceRegistrations::iterator it = m_channelInstanceRegistrations.begin(); it != m_channelInstanceRegistrations.end(); ++it) + { + if(it->m_gui == pluginGUI) + { + m_channelInstanceRegistrations.erase(it); + break; + } + } + + renameChannelInstances(); +} + +void DeviceUISet::freeChannels() +{ + for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + { + qDebug("DeviceUISet::freeAll: destroying channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); + m_channelInstanceRegistrations[i].m_gui->destroy(); + } +} + +void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI) +{ + if (preset->isSourcePreset()) + { + qDebug("DeviceUISet::loadChannelSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + // Available channel plugins + PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getRxChannelRegistrations(); + + // copy currently open channels and clear list + ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; + m_channelInstanceRegistrations.clear(); + + qDebug("DeviceUISet::loadChannelSettings: %d channel(s) in preset", preset->getChannelCount()); + + for(int i = 0; i < preset->getChannelCount(); i++) + { + const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); + ChannelInstanceRegistration reg; + + // if we have one instance available already, use it + + for(int i = 0; i < openChannels.count(); i++) + { + qDebug("DeviceUISet::loadChannelSettings: channels compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); + + if(openChannels[i].m_channelName == channelConfig.m_channel) + { + qDebug("DeviceSourceAPI::loadChannelSettings: channel [%s] found", qPrintable(openChannels[i].m_channelName)); + reg = openChannels.takeAt(i); + m_channelInstanceRegistrations.append(reg); + break; + } + } + + // if we haven't one already, create one + + if(reg.m_gui == NULL) + { + for(int i = 0; i < channelRegistrations->count(); i++) + { + if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) + { + qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); + // TOOO: replace m_deviceSourceAPI by this + reg = ChannelInstanceRegistration( + channelConfig.m_channel, + (*channelRegistrations)[i]. + m_plugin->createRxChannel(channelConfig.m_channel, m_deviceSourceAPI) + ); + break; + } + } + } + + if(reg.m_gui != NULL) + { + qDebug("DeviceUISet::loadChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channel)); + reg.m_gui->deserialize(channelConfig.m_config); + } + } + + // everything, that is still "available" is not needed anymore + for(int i = 0; i < openChannels.count(); i++) + { + qDebug("DeviceUISet::loadChannelSettings: destroying spare channel [%s]", qPrintable(openChannels[i].m_channelName)); + openChannels[i].m_gui->destroy(); + } + + renameChannelInstances(); + } + else + { + qDebug("DeviceUISet::loadChannelSettings: Loading preset [%s | %s] not a source preset", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + } +} + +void DeviceUISet::saveChannelSettings(Preset *preset) +{ + if (preset->isSourcePreset()) + { + qSort(m_channelInstanceRegistrations.begin(), m_channelInstanceRegistrations.end()); // sort by increasing delta frequency and type + + for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + { + qDebug("DeviceUISet::saveChannelSettings: channel [%s] saved", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); + preset->addChannel(m_channelInstanceRegistrations[i].m_channelName, m_channelInstanceRegistrations[i].m_gui->serialize()); + } + } + else + { + qDebug("DeviceSourceAPI::saveChannelSettings: not a source preset"); + } +} + +void DeviceUISet::renameChannelInstances() +{ + for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + { + m_channelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_channelInstanceRegistrations[i].m_channelName).arg(i)); + } +} + +// sort by increasing delta frequency and type (i.e. name) +bool DeviceUISet::ChannelInstanceRegistration::operator<(const ChannelInstanceRegistration& other) const +{ + if (m_gui && other.m_gui) + { + if (m_gui->getCenterFrequency() == other.m_gui->getCenterFrequency()) + { + return m_gui->getName() < other.m_gui->getName(); + } + else + { + return m_gui->getCenterFrequency() < other.m_gui->getCenterFrequency(); + } + } + else + { + return false; + } +} diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 236e54fd3..e0bd7de98 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -50,6 +50,37 @@ struct DeviceUISet GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window + + void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); + void removeChannelInstance(PluginInstanceGUI* pluginGUI); + void freeChannels(); + void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveChannelSettings(Preset* preset); + +private: + struct ChannelInstanceRegistration + { + QString m_channelName; + PluginInstanceGUI* m_gui; + + ChannelInstanceRegistration() : + m_channelName(), + m_gui(NULL) + { } + + ChannelInstanceRegistration(const QString& channelName, PluginInstanceGUI* pluginGUI) : + m_channelName(channelName), + m_gui(pluginGUI) + { } + + bool operator<(const ChannelInstanceRegistration& other) const; + }; + + typedef QList ChannelInstanceRegistrations; + + ChannelInstanceRegistrations m_channelInstanceRegistrations; + + void renameChannelInstances(); }; From da5f8aa6f9cc5edd3e0efea5b2c18a61c0e53e8b Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 31 Oct 2017 00:07:55 +0100 Subject: [PATCH 10/39] Hack to try ChannelAnalyzer channel plugin handled via DeviceUISet --- .../channelrx/chanalyzer/chanalyzergui.cpp | 20 ++++++++++-------- plugins/channelrx/chanalyzer/chanalyzergui.h | 9 ++++---- .../channelrx/chanalyzer/chanalyzerplugin.cpp | 8 +++---- .../channelrx/chanalyzer/chanalyzerplugin.h | 6 +++--- sdrgui/device/deviceuiset.cpp | 13 ++++++++++-- sdrgui/mainwindow.cpp | 7 ++++++- sdrgui/plugin/plugininterface.h | 10 ++++++++- sdrgui/plugin/pluginmanager.cpp | 21 +++++++++++++++++++ sdrgui/plugin/pluginmanager.h | 2 ++ 9 files changed, 72 insertions(+), 24 deletions(-) diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 35b299576..424295cf8 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -17,6 +17,7 @@ #include "chanalyzergui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -39,9 +40,9 @@ const QString ChannelAnalyzerGUI::m_channelID = "org.f4exb.sdrangelove.channel.chanalyzer"; -ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI, deviceAPI); + ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI, deviceUISet); return gui; } @@ -322,11 +323,12 @@ void ChannelAnalyzerGUI::onMenuDoubleClicked() } } -ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::ChannelAnalyzerGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), +// m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -342,7 +344,7 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceSourceAPI *de m_spectrumVis = new SpectrumVis(ui->glSpectrum); m_scopeVis = new ScopeVis(ui->glScope); m_spectrumScopeComboVis = new SpectrumScopeComboVis(m_spectrumVis, m_scopeVis); - m_channelAnalyzer = new ChannelAnalyzer(m_deviceAPI); + m_channelAnalyzer = new ChannelAnalyzer(m_deviceUISet->m_deviceSourceAPI); m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); @@ -368,9 +370,9 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceSourceAPI *de connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); @@ -383,7 +385,7 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceSourceAPI *de ChannelAnalyzerGUI::~ChannelAnalyzerGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_channelAnalyzer; delete m_spectrumVis; delete m_scopeVis; diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index c13238f40..a56f91a2d 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -24,7 +24,7 @@ #include "util/messagequeue.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -41,7 +41,7 @@ class ChannelAnalyzerGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static ChannelAnalyzerGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static ChannelAnalyzerGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUIset); virtual void destroy(); void setName(const QString& name); @@ -73,7 +73,8 @@ private slots: private: Ui::ChannelAnalyzerGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; +// DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; bool m_basicSettingsShown; bool m_doApplySettings; @@ -89,7 +90,7 @@ private: ScopeVis* m_scopeVis; MessageQueue m_inputMessageQueue; - explicit ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~ChannelAnalyzerGUI(); int getEffectiveLowCutoff(int lowCutoff); diff --git a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp index 428ccd78c..5c8d6c175 100644 --- a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp @@ -33,18 +33,18 @@ void ChannelAnalyzerPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(ChannelAnalyzerGUI::m_channelID, this); } -PluginInstanceGUI* ChannelAnalyzerPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* ChannelAnalyzerPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == ChannelAnalyzerGUI::m_channelID) { - ChannelAnalyzerGUI* gui = ChannelAnalyzerGUI::create(m_pluginAPI, deviceAPI); + ChannelAnalyzerGUI* gui = ChannelAnalyzerGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void ChannelAnalyzerPlugin::createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI) +void ChannelAnalyzerPlugin::createInstanceChannelAnalyzer(DeviceUISet *deviceUISet) { - ChannelAnalyzerGUI::create(m_pluginAPI, deviceAPI); + ChannelAnalyzerGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/chanalyzer/chanalyzerplugin.h b/plugins/channelrx/chanalyzer/chanalyzerplugin.h index aa42c8edb..eaa7530f9 100644 --- a/plugins/channelrx/chanalyzer/chanalyzerplugin.h +++ b/plugins/channelrx/chanalyzer/chanalyzerplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class ChannelAnalyzerPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI); + void createInstanceChannelAnalyzer(DeviceUISet *deviceUISet); }; #endif // INCLUDE_CHANALYZERPLUGIN_H diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 4998a3387..3a53f4909 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -63,6 +63,16 @@ DeviceUISet::~DeviceUISet() delete m_spectrum; } +void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker) +{ + m_spectrum->addChannelMarker(channelMarker); +} + +void DeviceUISet::addRollupWidget(QWidget *widget) +{ + m_channelWindow->addRollupWidget(widget); +} + void DeviceUISet::registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) { m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); @@ -136,11 +146,10 @@ void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) { qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); - // TOOO: replace m_deviceSourceAPI by this reg = ChannelInstanceRegistration( channelConfig.m_channel, (*channelRegistrations)[i]. - m_plugin->createRxChannel(channelConfig.m_channel, m_deviceSourceAPI) + m_plugin->createRxChannel(channelConfig.m_channel, this) ); break; } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index ac62be906..a3d52d2e5 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1024,7 +1024,12 @@ void MainWindow::on_channel_addClicked(bool checked __attribute__((unused))) if (deviceUI->m_deviceSourceEngine) // source device => Rx channels { - m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI->m_deviceSourceAPI); + qDebug("MainWindow::on_channel_addClicked: channel name: %s", qPrintable(m_pluginManager->getRxChannelInstanceName(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex()))); + if (m_pluginManager->getRxChannelInstanceName(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex()) == "org.f4exb.sdrangelove.channel.chanalyzer") { + m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); + } else { + m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI->m_deviceSourceAPI); + } } else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels { diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 584fc80f2..8abb4dd58 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -54,7 +54,15 @@ public: // channel Rx plugins - virtual PluginInstanceGUI* createRxChannel(const QString& channelName __attribute__((unused)), DeviceSourceAPI *deviceAPI __attribute__((unused)) ) { return 0; } + // TODO: remove this one when migration is complete + virtual PluginInstanceGUI* createRxChannel( + const QString& channelName __attribute__((unused)), + DeviceSourceAPI *deviceAPI __attribute__((unused)) ) + { return 0; } + virtual PluginInstanceGUI* createRxChannel( + const QString& channelName __attribute__((unused)), + DeviceUISet *deviceAPI __attribute__((unused)) ) + { return 0; } // channel Tx plugins diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 1f1836dcf..fa37b408a 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -627,6 +627,27 @@ void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceSource } } +void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet) +{ + if (channelPluginIndex < m_rxChannelRegistrations.size()) + { + PluginInterface *pluginInterface = m_rxChannelRegistrations[channelPluginIndex].m_plugin; + pluginInterface->createRxChannel(m_rxChannelRegistrations[channelPluginIndex].m_channelName, deviceUISet); + } +} + +QString PluginManager::getRxChannelInstanceName(int channelPluginIndex) +{ + if (channelPluginIndex < m_rxChannelRegistrations.size()) + { + return m_rxChannelRegistrations[channelPluginIndex].m_channelName; + } + else + { + return ""; + } +} + void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI) { if (channelPluginIndex < m_txChannelRegistrations.size()) diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index 801468cc8..13425a676 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -72,6 +72,8 @@ public: void populateRxChannelComboBox(QComboBox *channels); void createRxChannelInstance(int channelPluginIndex, DeviceSourceAPI *deviceAPI); + void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); + QString getRxChannelInstanceName(int channelPluginIndex); void populateTxChannelComboBox(QComboBox *channels); void createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI); From a61bb04dac1ca0068a6a64f683b7058761c275bd Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 31 Oct 2017 08:24:05 +0100 Subject: [PATCH 11/39] Rx Channel plugins now handled via DeviceUISet --- .../channelrx/chanalyzer/chanalyzerplugin.cpp | 2 +- .../chanalyzerng/chanalyzernggui.cpp | 19 +++++++-------- .../channelrx/chanalyzerng/chanalyzernggui.h | 8 +++---- .../chanalyzerng/chanalyzerngplugin.cpp | 10 ++++---- .../chanalyzerng/chanalyzerngplugin.h | 6 ++--- plugins/channelrx/demodam/amdemodgui.cpp | 19 +++++++-------- plugins/channelrx/demodam/amdemodgui.h | 8 +++---- plugins/channelrx/demodam/amdemodplugin.cpp | 10 ++++---- plugins/channelrx/demodam/amdemodplugin.h | 6 ++--- plugins/channelrx/demodatv/atvdemodgui.cpp | 23 ++++++++++--------- plugins/channelrx/demodatv/atvdemodgui.h | 8 +++---- plugins/channelrx/demodatv/atvdemodplugin.cpp | 10 ++++---- plugins/channelrx/demodatv/atvdemodplugin.h | 6 ++--- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 19 +++++++-------- plugins/channelrx/demodbfm/bfmdemodgui.h | 8 +++---- plugins/channelrx/demodbfm/bfmplugin.cpp | 10 ++++---- plugins/channelrx/demodbfm/bfmplugin.h | 6 ++--- plugins/channelrx/demoddsd/dsddemodgui.cpp | 19 +++++++-------- plugins/channelrx/demoddsd/dsddemodgui.h | 8 +++---- plugins/channelrx/demoddsd/dsddemodplugin.cpp | 14 +++++------ plugins/channelrx/demoddsd/dsddemodplugin.h | 6 ++--- plugins/channelrx/demodlora/lorademodgui.cpp | 19 +++++++-------- plugins/channelrx/demodlora/lorademodgui.h | 8 +++---- plugins/channelrx/demodlora/loraplugin.cpp | 10 ++++---- plugins/channelrx/demodlora/loraplugin.h | 6 ++--- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 19 +++++++-------- plugins/channelrx/demodnfm/nfmdemodgui.h | 8 +++---- plugins/channelrx/demodnfm/nfmplugin.cpp | 10 ++++---- plugins/channelrx/demodnfm/nfmplugin.h | 6 ++--- plugins/channelrx/demodssb/ssbdemodgui.cpp | 19 +++++++-------- plugins/channelrx/demodssb/ssbdemodgui.h | 8 +++---- plugins/channelrx/demodssb/ssbplugin.cpp | 14 +++++------ plugins/channelrx/demodssb/ssbplugin.h | 6 ++--- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 19 +++++++-------- plugins/channelrx/demodwfm/wfmdemodgui.h | 8 +++---- plugins/channelrx/demodwfm/wfmplugin.cpp | 10 ++++---- plugins/channelrx/demodwfm/wfmplugin.h | 6 ++--- plugins/channelrx/tcpsrc/tcpsrcgui.cpp | 19 +++++++-------- plugins/channelrx/tcpsrc/tcpsrcgui.h | 8 +++---- plugins/channelrx/tcpsrc/tcpsrcplugin.cpp | 14 +++++------ plugins/channelrx/tcpsrc/tcpsrcplugin.h | 6 ++--- plugins/channelrx/udpsrc/udpsrcgui.cpp | 19 +++++++-------- plugins/channelrx/udpsrc/udpsrcgui.h | 8 +++---- plugins/channelrx/udpsrc/udpsrcplugin.cpp | 10 ++++---- plugins/channelrx/udpsrc/udpsrcplugin.h | 6 ++--- sdrgui/mainwindow.cpp | 6 +---- 46 files changed, 252 insertions(+), 245 deletions(-) diff --git a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp index 5c8d6c175..e4b6cdc44 100644 --- a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor ChannelAnalyzerPlugin::m_pluginDescriptor = { QString("Channel Analyzer"), - QString("2.0.0"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp index bd36d521f..8640ca738 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp @@ -17,6 +17,7 @@ #include "chanalyzernggui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -39,9 +40,9 @@ const QString ChannelAnalyzerNGGUI::m_channelID = "sdrangel.channel.chanalyzerng"; -ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceAPI); + ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceUISet); return gui; } @@ -350,11 +351,11 @@ void ChannelAnalyzerNGGUI::onMenuDoubleClicked() } } -ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::ChannelAnalyzerNGGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -370,7 +371,7 @@ ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI m_spectrumVis = new SpectrumVis(ui->glSpectrum); m_scopeVis = new ScopeVisNG(ui->glScope); m_spectrumScopeComboVis = new SpectrumScopeNGComboVis(m_spectrumVis, m_scopeVis); - m_channelAnalyzer = new ChannelAnalyzerNG(m_deviceAPI); + m_channelAnalyzer = new ChannelAnalyzerNG(m_deviceUISet->m_deviceSourceAPI); m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); // m_channelizer = new DownChannelizer(m_channelAnalyzer); @@ -406,9 +407,9 @@ ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); @@ -421,7 +422,7 @@ ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI ChannelAnalyzerNGGUI::~ChannelAnalyzerNGGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); // m_deviceAPI->removeThreadedSink(m_threadedChannelizer); // delete m_threadedChannelizer; // delete m_channelizer; diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.h b/plugins/channelrx/chanalyzerng/chanalyzernggui.h index 7ef686b66..1b71d1de1 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.h +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.h @@ -24,7 +24,7 @@ #include "util/messagequeue.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -41,7 +41,7 @@ class ChannelAnalyzerNGGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static ChannelAnalyzerNGGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static ChannelAnalyzerNGGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -75,7 +75,7 @@ private slots: private: Ui::ChannelAnalyzerNGGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; bool m_basicSettingsShown; bool m_doApplySettings; @@ -91,7 +91,7 @@ private: ScopeVisNG* m_scopeVis; MessageQueue m_inputMessageQueue; - explicit ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~ChannelAnalyzerNGGUI(); int getRequestedChannelSampleRate(); diff --git a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp index 1ddff0440..80514d0a4 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp @@ -22,7 +22,7 @@ const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = { QString("Channel Analyzer NG"), - QString("3.5.3"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -48,18 +48,18 @@ void ChannelAnalyzerNGPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(ChannelAnalyzerNGGUI::m_channelID, this); } -PluginInstanceGUI* ChannelAnalyzerNGPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* ChannelAnalyzerNGPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == ChannelAnalyzerNGGUI::m_channelID) { - ChannelAnalyzerNGGUI* gui = ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceAPI); + ChannelAnalyzerNGGUI* gui = ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void ChannelAnalyzerNGPlugin::createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI) +void ChannelAnalyzerNGPlugin::createInstanceChannelAnalyzer(DeviceUISet *deviceUISet) { - ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceAPI); + ChannelAnalyzerNGGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.h b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.h index 2f4597254..a673ce0db 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.h +++ b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.h @@ -21,7 +21,7 @@ #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class ChannelAnalyzerNGPlugin : public QObject, PluginInterface { Q_OBJECT @@ -34,7 +34,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -42,7 +42,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceChannelAnalyzer(DeviceSourceAPI *deviceAPI); + void createInstanceChannelAnalyzer(DeviceUISet *deviceUISet); }; #endif // INCLUDE_CHANALYZERNGPLUGIN_H diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 94929d2d5..b69d8cddc 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -20,6 +20,7 @@ #include "amdemodgui.h" #include "device/devicesourceapi.h" +#include "device/deviceuiset.h" #include "dsp/downchannelizer.h" #include "dsp/threadedbasebandsamplesink.h" @@ -35,9 +36,9 @@ const QString AMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.am"; -AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - AMDemodGUI* gui = new AMDemodGUI(pluginAPI, deviceAPI); + AMDemodGUI* gui = new AMDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -169,11 +170,11 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.exec(); } -AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::AMDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_doApplySettings(true), m_squelchOpen(false), @@ -184,7 +185,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - m_amDemod = new AMDemod(m_deviceAPI); + m_amDemod = new AMDemod(m_deviceUISet->m_deviceSourceAPI); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms @@ -205,9 +206,9 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); displaySettings(); applySettings(true); @@ -215,7 +216,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget AMDemodGUI::~AMDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_amDemod; delete ui; } diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 0052c4839..32833328b 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -9,7 +9,7 @@ #include "amdemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -23,7 +23,7 @@ class AMDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static AMDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static AMDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -55,7 +55,7 @@ private slots: private: Ui::AMDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; AMDemodSettings m_settings; bool m_doApplySettings; @@ -67,7 +67,7 @@ private: uint32_t m_tickCount; MessageQueue m_inputMessageQueue; - explicit AMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~AMDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demodam/amdemodplugin.cpp b/plugins/channelrx/demodam/amdemodplugin.cpp index bca6226f2..ec7344408 100644 --- a/plugins/channelrx/demodam/amdemodplugin.cpp +++ b/plugins/channelrx/demodam/amdemodplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = { QString("AM Demodulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -33,18 +33,18 @@ void AMDemodPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(AMDemodGUI::m_channelID, this); } -PluginInstanceGUI* AMDemodPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* AMDemodPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == AMDemodGUI::m_channelID) { - AMDemodGUI* gui = AMDemodGUI::create(m_pluginAPI, deviceAPI); + AMDemodGUI* gui = AMDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void AMDemodPlugin::createInstanceDemodAM(DeviceSourceAPI *deviceAPI) +void AMDemodPlugin::createInstanceDemodAM(DeviceUISet *deviceUISet) { - AMDemodGUI::create(m_pluginAPI, deviceAPI); + AMDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodam/amdemodplugin.h b/plugins/channelrx/demodam/amdemodplugin.h index 903c6d7c4..965bee725 100644 --- a/plugins/channelrx/demodam/amdemodplugin.h +++ b/plugins/channelrx/demodam/amdemodplugin.h @@ -20,7 +20,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class AMDemodPlugin : public QObject, PluginInterface { Q_OBJECT @@ -33,7 +33,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceDemodAM(DeviceSourceAPI *deviceAPI); + void createInstanceDemodAM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_AMPLUGIN_H diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 114cb6673..278e6ade1 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -21,6 +21,7 @@ #include "atvdemodgui.h" #include "device/devicesourceapi.h" +#include "device/deviceuiset.h" #include "dsp/downchannelizer.h" #include "dsp/threadedbasebandsamplesink.h" @@ -38,9 +39,9 @@ const QString ATVDemodGUI::m_strChannelID = "sdrangel.channel.demodatv"; ATVDemodGUI* ATVDemodGUI::create(PluginAPI* objPluginAPI, - DeviceSourceAPI *objDeviceAPI) + DeviceUISet *deviceUISet) { - ATVDemodGUI* gui = new ATVDemodGUI(objPluginAPI, objDeviceAPI); + ATVDemodGUI* gui = new ATVDemodGUI(objPluginAPI, deviceUISet); return gui; } @@ -266,12 +267,12 @@ void ATVDemodGUI::onMenuDoubleClicked() } } -ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, +ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, QWidget* objParent) : RollupWidget(objParent), ui(new Ui::ATVDemodGUI), m_pluginAPI(objPluginAPI), - m_deviceAPI(objDeviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_blnBasicSettingsShown(false), m_blnDoApplySettings(true), @@ -284,14 +285,14 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); m_scopeVis = new ScopeVisNG(ui->glScope); - m_atvDemod = new ATVDemod(m_deviceAPI); + m_atvDemod = new ATVDemod(m_deviceUISet->m_deviceSourceAPI); m_atvDemod->setScopeSink(m_scopeVis); m_atvDemod->setMessageQueueToGUI(getInputMessageQueue()); m_atvDemod->setATVScreen(ui->screenTV); m_channelizer = new DownChannelizer(m_atvDemod); m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); - m_deviceAPI->addThreadedSink(m_threadedChannelizer); + m_deviceUISet->m_deviceSourceAPI->addThreadedSink(m_threadedChannelizer); ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms @@ -312,9 +313,9 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceAPI->registerChannelInstance(m_strChannelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_strChannelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); //ui->screenTV->connectTimer(m_objPluginAPI->getMainWindow()->getMasterTimer()); @@ -347,8 +348,8 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, ATVDemodGUI::~ATVDemodGUI() { - m_deviceAPI->removeChannelInstance(this); - m_deviceAPI->removeThreadedSink(m_threadedChannelizer); + m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->m_deviceSourceAPI->removeThreadedSink(m_threadedChannelizer); delete m_threadedChannelizer; delete m_channelizer; delete m_atvDemod; diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index 917076294..c33d135c0 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -25,7 +25,7 @@ #include "util/messagequeue.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class ThreadedBasebandSampleSink; class DownChannelizer; @@ -42,7 +42,7 @@ class ATVDemodGUI : public RollupWidget, public PluginInstanceGUI Q_OBJECT public: - static ATVDemodGUI* create(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI); + static ATVDemodGUI* create(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& strName); @@ -90,7 +90,7 @@ private slots: private: Ui::ATVDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; ThreadedBasebandSampleSink* m_threadedChannelizer; DownChannelizer* m_channelizer; @@ -109,7 +109,7 @@ private: int m_rfSliderDivisor; MessageQueue m_inputMessageQueue; - explicit ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, QWidget* objParent = NULL); + explicit ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, QWidget* objParent = 0); virtual ~ATVDemodGUI(); void blockApplySettings(bool blnBlock); diff --git a/plugins/channelrx/demodatv/atvdemodplugin.cpp b/plugins/channelrx/demodatv/atvdemodplugin.cpp index 745f13921..b560e2f4b 100644 --- a/plugins/channelrx/demodatv/atvdemodplugin.cpp +++ b/plugins/channelrx/demodatv/atvdemodplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor ATVDemodPlugin::m_ptrPluginDescriptor = { QString("ATV Demodulator"), - QString("3.7.3"), + QString("3.7.9"), QString("(c) F4HKW for F4EXB / SDRAngel"), QString("https://github.com/f4exb/sdrangel"), true, @@ -53,11 +53,11 @@ void ATVDemodPlugin::initPlugin(PluginAPI* ptrPluginAPI) m_ptrPluginAPI->registerRxChannel(ATVDemodGUI::m_strChannelID, this); } -PluginInstanceGUI* ATVDemodPlugin::createRxChannel(const QString& strChannelName, DeviceSourceAPI *ptrDeviceAPI) +PluginInstanceGUI* ATVDemodPlugin::createRxChannel(const QString& strChannelName, DeviceUISet *deviceUISet) { if(strChannelName == ATVDemodGUI::m_strChannelID) { - ATVDemodGUI* ptrGui = ATVDemodGUI::create(m_ptrPluginAPI, ptrDeviceAPI); + ATVDemodGUI* ptrGui = ATVDemodGUI::create(m_ptrPluginAPI, deviceUISet); return ptrGui; } else @@ -66,7 +66,7 @@ PluginInstanceGUI* ATVDemodPlugin::createRxChannel(const QString& strChannelName } } -void ATVDemodPlugin::createInstanceDemodATV(DeviceSourceAPI *ptrDeviceAPI) +void ATVDemodPlugin::createInstanceDemodATV(DeviceUISet *deviceUISet) { - ATVDemodGUI::create(m_ptrPluginAPI, ptrDeviceAPI); + ATVDemodGUI::create(m_ptrPluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodatv/atvdemodplugin.h b/plugins/channelrx/demodatv/atvdemodplugin.h index 93f0e11d5..c6061eea0 100644 --- a/plugins/channelrx/demodatv/atvdemodplugin.h +++ b/plugins/channelrx/demodatv/atvdemodplugin.h @@ -21,7 +21,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class ATVDemodPlugin : public QObject, PluginInterface { @@ -35,7 +35,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* ptrPluginAPI); - PluginInstanceGUI* createRxChannel(const QString& strChannelName, DeviceSourceAPI *ptrDeviceAPI); + PluginInstanceGUI* createRxChannel(const QString& strChannelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_ptrPluginDescriptor; @@ -43,7 +43,7 @@ private: PluginAPI* m_ptrPluginAPI; private slots: - void createInstanceDemodATV(DeviceSourceAPI *ptrDeviceAPI); + void createInstanceDemodATV(DeviceUISet *deviceUISet); }; #endif // INCLUDE_ATVPLUGIN_H diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 36c5f6f55..ec8449bba 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -18,6 +18,7 @@ #include "bfmdemodgui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -44,9 +45,9 @@ const QString BFMDemodGUI::m_channelID = "sdrangel.channel.bfm"; -BFMDemodGUI* BFMDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +BFMDemodGUI* BFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUIset) { - BFMDemodGUI* gui = new BFMDemodGUI(pluginAPI, deviceAPI); + BFMDemodGUI* gui = new BFMDemodGUI(pluginAPI, deviceUIset); return gui; } @@ -315,11 +316,11 @@ void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) } } -BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::BFMDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_rdsTimerCount(0), m_channelPowerDbAvg(20,0), @@ -337,7 +338,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_bfmDemod = new BFMDemod(m_deviceAPI); + m_bfmDemod = new BFMDemod(m_deviceUISet->m_deviceSourceAPI); m_bfmDemod->setMessageQueueToGUI(getInputMessageQueue()); m_bfmDemod->setSampleSink(m_spectrumVis); @@ -363,9 +364,9 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -382,7 +383,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg BFMDemodGUI::~BFMDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_bfmDemod; delete ui; } diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index 5fa5584b2..5d83edd10 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -26,7 +26,7 @@ #include "bfmdemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class RDSParser; class ThreadedBasebandSampleSink; @@ -42,7 +42,7 @@ class BFMDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static BFMDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static BFMDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceAPI); virtual void destroy(); void setName(const QString& name); @@ -82,7 +82,7 @@ private slots: private: Ui::BFMDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; BFMDemodSettings m_settings; bool m_doApplySettings; @@ -96,7 +96,7 @@ private: std::vector m_g14ComboIndex; MessageQueue m_inputMessageQueue; - explicit BFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~BFMDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demodbfm/bfmplugin.cpp b/plugins/channelrx/demodbfm/bfmplugin.cpp index f9a26f61f..096abba0e 100644 --- a/plugins/channelrx/demodbfm/bfmplugin.cpp +++ b/plugins/channelrx/demodbfm/bfmplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor BFMPlugin::m_pluginDescriptor = { QString("Broadcast FM Demodulator"), - QString("3.7.5"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -50,18 +50,18 @@ void BFMPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(BFMDemodGUI::m_channelID, this); } -PluginInstanceGUI* BFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* BFMPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == BFMDemodGUI::m_channelID) { - BFMDemodGUI* gui = BFMDemodGUI::create(m_pluginAPI, deviceAPI); + BFMDemodGUI* gui = BFMDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void BFMPlugin::createInstanceBFM(DeviceSourceAPI *deviceAPI) +void BFMPlugin::createInstanceBFM(DeviceUISet *deviceUISet) { - BFMDemodGUI::create(m_pluginAPI, deviceAPI); + BFMDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodbfm/bfmplugin.h b/plugins/channelrx/demodbfm/bfmplugin.h index 6879eb197..1da550ee8 100644 --- a/plugins/channelrx/demodbfm/bfmplugin.h +++ b/plugins/channelrx/demodbfm/bfmplugin.h @@ -21,7 +21,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class BFMPlugin : public QObject, PluginInterface { Q_OBJECT @@ -34,7 +34,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -42,7 +42,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceBFM(DeviceSourceAPI *deviceAPI); + void createInstanceBFM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_BFMPLUGIN_H diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index d8acd1828..931715192 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -18,6 +18,7 @@ #include "dsddemodgui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -39,9 +40,9 @@ const QString DSDDemodGUI::m_channelID = "sdrangel.channel.dsddemod"; -DSDDemodGUI* DSDDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +DSDDemodGUI* DSDDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - DSDDemodGUI* gui = new DSDDemodGUI(pluginAPI, deviceAPI); + DSDDemodGUI* gui = new DSDDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -230,11 +231,11 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.exec(); } -DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::DSDDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_doApplySettings(true), m_signalFormat(signalFormatNone), @@ -253,7 +254,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_scopeVis = new ScopeVis(ui->glScope); - m_dsdDemod = new DSDDemod(m_deviceAPI); + m_dsdDemod = new DSDDemod(m_deviceUISet->m_deviceSourceAPI); m_dsdDemod->setScopeSink(m_scopeVis); m_dsdDemod->setMessageQueueToGUI(getInputMessageQueue()); @@ -280,9 +281,9 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); @@ -297,7 +298,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg DSDDemodGUI::~DSDDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_dsdDemod; delete ui; } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index add6e5b83..46a9a7362 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -30,7 +30,7 @@ #include "dsddemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class ScopeVis; class DSDDemod; @@ -43,7 +43,7 @@ class DSDDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static DSDDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static DSDDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -94,7 +94,7 @@ private: Ui::DSDDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; DSDDemodSettings m_settings; bool m_doApplySettings; @@ -118,7 +118,7 @@ private: MessageQueue m_inputMessageQueue; - explicit DSDDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~DSDDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demoddsd/dsddemodplugin.cpp b/plugins/channelrx/demoddsd/dsddemodplugin.cpp index 2c1de0032..f310e643c 100644 --- a/plugins/channelrx/demoddsd/dsddemodplugin.cpp +++ b/plugins/channelrx/demoddsd/dsddemodplugin.cpp @@ -15,16 +15,16 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "../../channelrx/demoddsd/dsddemodplugin.h" +#include "dsddemodplugin.h" #include #include #include "plugin/pluginapi.h" -#include "../../channelrx/demoddsd/dsddemodgui.h" +#include "dsddemodgui.h" const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = { QString("DSD Demodulator"), - QString("3.7.3"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -50,18 +50,18 @@ void DSDDemodPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(DSDDemodGUI::m_channelID, this); } -PluginInstanceGUI* DSDDemodPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* DSDDemodPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == DSDDemodGUI::m_channelID) { - DSDDemodGUI* gui = DSDDemodGUI::create(m_pluginAPI, deviceAPI); + DSDDemodGUI* gui = DSDDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void DSDDemodPlugin::createInstanceDSDDemod(DeviceSourceAPI *deviceAPI) +void DSDDemodPlugin::createInstanceDSDDemod(DeviceUISet *deviceUISet) { - DSDDemodGUI::create(m_pluginAPI, deviceAPI); + DSDDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demoddsd/dsddemodplugin.h b/plugins/channelrx/demoddsd/dsddemodplugin.h index ee80b5033..fad614fe2 100644 --- a/plugins/channelrx/demoddsd/dsddemodplugin.h +++ b/plugins/channelrx/demoddsd/dsddemodplugin.h @@ -21,7 +21,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class DSDDemodPlugin : public QObject, PluginInterface { Q_OBJECT @@ -34,7 +34,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -42,7 +42,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceDSDDemod(DeviceSourceAPI *deviceAPI); + void createInstanceDSDDemod(DeviceUISet *deviceUISet); }; #endif // INCLUDE_DSDDEMODLUGIN_H diff --git a/plugins/channelrx/demodlora/lorademodgui.cpp b/plugins/channelrx/demodlora/lorademodgui.cpp index 97c6c0d2d..3c473c08c 100644 --- a/plugins/channelrx/demodlora/lorademodgui.cpp +++ b/plugins/channelrx/demodlora/lorademodgui.cpp @@ -1,5 +1,6 @@ #include +#include "device/deviceuiset.h" #include #include #include @@ -17,9 +18,9 @@ const QString LoRaDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.lora"; -LoRaDemodGUI* LoRaDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +LoRaDemodGUI* LoRaDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - LoRaDemodGUI* gui = new LoRaDemodGUI(pluginAPI, deviceAPI); + LoRaDemodGUI* gui = new LoRaDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -120,11 +121,11 @@ void LoRaDemodGUI::onMenuDoubleClicked() } } -LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::LoRaDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true) @@ -135,7 +136,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWi connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_LoRaDemod = new LoRaDemod(m_deviceAPI); + m_LoRaDemod = new LoRaDemod(m_deviceUISet->m_deviceSourceAPI); m_LoRaDemod->setSpectrumSink(m_spectrumVis); ui->glSpectrum->setCenterFrequency(16000); @@ -152,9 +153,9 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWi connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -167,7 +168,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWi LoRaDemodGUI::~LoRaDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_LoRaDemod; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/demodlora/lorademodgui.h b/plugins/channelrx/demodlora/lorademodgui.h index 812aedc86..343bbcd02 100644 --- a/plugins/channelrx/demodlora/lorademodgui.h +++ b/plugins/channelrx/demodlora/lorademodgui.h @@ -9,7 +9,7 @@ #include "lorademodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class LoRaDemod; class SpectrumVis; @@ -21,7 +21,7 @@ class LoRaDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static LoRaDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static LoRaDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceAPI); virtual void destroy(); void setName(const QString& name); @@ -47,7 +47,7 @@ private slots: private: Ui::LoRaDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; LoRaDemodSettings m_settings; bool m_basicSettingsShown; @@ -57,7 +57,7 @@ private: SpectrumVis* m_spectrumVis; MessageQueue m_inputMessageQueue; - explicit LoRaDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = NULL); virtual ~LoRaDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demodlora/loraplugin.cpp b/plugins/channelrx/demodlora/loraplugin.cpp index 05760a0c8..18ca4c459 100644 --- a/plugins/channelrx/demodlora/loraplugin.cpp +++ b/plugins/channelrx/demodlora/loraplugin.cpp @@ -6,7 +6,7 @@ const PluginDescriptor LoRaPlugin::m_pluginDescriptor = { QString("LoRa Demodulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) 2015 John Greb"), QString("http://www.maintech.de"), true, @@ -32,18 +32,18 @@ void LoRaPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(LoRaDemodGUI::m_channelID, this); } -PluginInstanceGUI* LoRaPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* LoRaPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == LoRaDemodGUI::m_channelID) { - LoRaDemodGUI* gui = LoRaDemodGUI::create(m_pluginAPI, deviceAPI); + LoRaDemodGUI* gui = LoRaDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void LoRaPlugin::createInstanceLoRa(DeviceSourceAPI *deviceAPI) +void LoRaPlugin::createInstanceLoRa(DeviceUISet *deviceUISet) { - LoRaDemodGUI::create(m_pluginAPI, deviceAPI); + LoRaDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodlora/loraplugin.h b/plugins/channelrx/demodlora/loraplugin.h index 412419c10..f1c3bae91 100644 --- a/plugins/channelrx/demodlora/loraplugin.h +++ b/plugins/channelrx/demodlora/loraplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class LoRaPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceLoRa(DeviceSourceAPI *deviceAPI); + void createInstanceLoRa(DeviceUISet *deviceUISet); }; #endif // INCLUDE_LoRaPLUGIN_H diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index e07fb0ea1..b39387f12 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -1,6 +1,7 @@ #include "nfmdemodgui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -17,9 +18,9 @@ const QString NFMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.nfm"; -NFMDemodGUI* NFMDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +NFMDemodGUI* NFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - NFMDemodGUI* gui = new NFMDemodGUI(pluginAPI, deviceAPI); + NFMDemodGUI* gui = new NFMDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -221,11 +222,11 @@ void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.exec(); } -NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::NFMDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -238,7 +239,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - m_nfmDemod = new NFMDemod(m_deviceAPI); + m_nfmDemod = new NFMDemod(m_deviceUISet->m_deviceSourceAPI); m_nfmDemod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -283,9 +284,9 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); QChar delta = QChar(0x94, 0x03); ui->deltaSquelch->setText(delta); @@ -298,7 +299,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg NFMDemodGUI::~NFMDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_nfmDemod; //delete m_channelMarker; delete ui; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index efa6e9c38..317ce2f4f 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -11,7 +11,7 @@ #include "nfmdemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class NFMDemod; @@ -23,7 +23,7 @@ class NFMDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static NFMDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static NFMDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -61,7 +61,7 @@ private slots: private: Ui::NFMDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; NFMDemodSettings m_settings; bool m_basicSettingsShown; @@ -72,7 +72,7 @@ private: uint32_t m_tickCount; MessageQueue m_inputMessageQueue; - explicit NFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~NFMDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demodnfm/nfmplugin.cpp b/plugins/channelrx/demodnfm/nfmplugin.cpp index 9dd6bb954..ff9398349 100644 --- a/plugins/channelrx/demodnfm/nfmplugin.cpp +++ b/plugins/channelrx/demodnfm/nfmplugin.cpp @@ -6,7 +6,7 @@ const PluginDescriptor NFMPlugin::m_pluginDescriptor = { QString("NFM Demodulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -32,17 +32,17 @@ void NFMPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(NFMDemodGUI::m_channelID, this); } -PluginInstanceGUI* NFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* NFMPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == NFMDemodGUI::m_channelID) { - NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI, deviceAPI); + NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void NFMPlugin::createInstanceNFM(DeviceSourceAPI *deviceAPI) +void NFMPlugin::createInstanceNFM(DeviceUISet *deviceUISet) { - NFMDemodGUI::create(m_pluginAPI, deviceAPI); + NFMDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodnfm/nfmplugin.h b/plugins/channelrx/demodnfm/nfmplugin.h index 10eb6b131..47fce8787 100644 --- a/plugins/channelrx/demodnfm/nfmplugin.h +++ b/plugins/channelrx/demodnfm/nfmplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class NFMPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceNFM(DeviceSourceAPI *deviceAPI); + void createInstanceNFM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_NFMPLUGIN_H diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index b053591a6..d1ce494c1 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -2,6 +2,7 @@ #include "ssbdemodgui.h" #include +#include "device/deviceuiset.h" #include #include @@ -18,9 +19,9 @@ const QString SSBDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.ssb"; -SSBDemodGUI* SSBDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +SSBDemodGUI* SSBDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - SSBDemodGUI* gui = new SSBDemodGUI(pluginAPI, deviceAPI); + SSBDemodGUI* gui = new SSBDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -286,11 +287,11 @@ void SSBDemodGUI::onMenuDoubleClicked() } } -SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::SSBDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -308,7 +309,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_ssbDemod = new SSBDemod(m_deviceAPI); + m_ssbDemod = new SSBDemod(m_deviceUISet->m_deviceSourceAPI); m_ssbDemod->setMessageQueueToGUI(getInputMessageQueue()); m_ssbDemod->setSampleSink(m_spectrumVis); @@ -330,9 +331,9 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -343,7 +344,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg SSBDemodGUI::~SSBDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_ssbDemod; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index d81238663..d3c42aa47 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -9,7 +9,7 @@ #include "ssbdemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class AudioFifo; class SSBDemod; @@ -23,7 +23,7 @@ class SSBDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static SSBDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static SSBDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -62,7 +62,7 @@ private slots: private: Ui::SSBDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; SSBDemodSettings m_settings; bool m_basicSettingsShown; @@ -80,7 +80,7 @@ private: SpectrumVis* m_spectrumVis; MessageQueue m_inputMessageQueue; - explicit SSBDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI* deviceAPI, QWidget* parent = NULL); + explicit SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet* deviceUISet, QWidget* parent = 0); virtual ~SSBDemodGUI(); int getEffectiveLowCutoff(int lowCutoff); diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index 3373cdc03..d8fa5fdc5 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -1,13 +1,13 @@ -#include "../../channelrx/demodssb/ssbplugin.h" +#include "ssbplugin.h" #include #include #include "plugin/pluginapi.h" -#include "../../channelrx/demodssb/ssbdemodgui.h" +#include "ssbdemodgui.h" const PluginDescriptor SSBPlugin::m_pluginDescriptor = { QString("SSB Demodulator"), - QString("3.7.3"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -33,18 +33,18 @@ void SSBPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(SSBDemodGUI::m_channelID, this); } -PluginInstanceGUI* SSBPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* SSBPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == SSBDemodGUI::m_channelID) { - SSBDemodGUI* gui = SSBDemodGUI::create(m_pluginAPI, deviceAPI); + SSBDemodGUI* gui = SSBDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void SSBPlugin::createInstanceSSB(DeviceSourceAPI *deviceAPI) +void SSBPlugin::createInstanceSSB(DeviceUISet *deviceUISet) { - SSBDemodGUI::create(m_pluginAPI, deviceAPI); + SSBDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodssb/ssbplugin.h b/plugins/channelrx/demodssb/ssbplugin.h index e87d6c03e..809ae630d 100644 --- a/plugins/channelrx/demodssb/ssbplugin.h +++ b/plugins/channelrx/demodssb/ssbplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class SSBPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceSSB(DeviceSourceAPI *deviceAPI); + void createInstanceSSB(DeviceUISet *deviceUISet); }; #endif // INCLUDE_SSBPLUGIN_H diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index e6a8e8865..0f91d1c5b 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -1,6 +1,7 @@ #include "wfmdemodgui.h" #include +#include "device/deviceuiset.h" #include #include #include @@ -19,9 +20,9 @@ const QString WFMDemodGUI::m_channelID = "de.maintech.sdrangelove.channel.wfm"; -WFMDemodGUI* WFMDemodGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +WFMDemodGUI* WFMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - WFMDemodGUI* gui = new WFMDemodGUI(pluginAPI, deviceAPI); + WFMDemodGUI* gui = new WFMDemodGUI(pluginAPI, deviceUISet); return gui; } @@ -143,11 +144,11 @@ void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.exec(); } -WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::WFMDemodGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_channelPowerDbAvg(20,0) @@ -171,7 +172,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - m_wfmDemod = new WFMDemod(m_deviceAPI); + m_wfmDemod = new WFMDemod(m_deviceUISet->m_deviceSourceAPI); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -184,9 +185,9 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); m_settings.setChannelMarker(&m_channelMarker); @@ -196,7 +197,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidg WFMDemodGUI::~WFMDemodGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_wfmDemod; //delete m_channelMarker; delete ui; diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index 45fd53e23..ac6379066 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -10,7 +10,7 @@ #include "wfmdemodsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class WFMDemod; @@ -22,7 +22,7 @@ class WFMDemodGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static WFMDemodGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static WFMDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -53,7 +53,7 @@ private slots: private: Ui::WFMDemodGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; WFMDemodSettings m_settings; bool m_basicSettingsShown; @@ -65,7 +65,7 @@ private: MovingAverage m_channelPowerDbAvg; MessageQueue m_inputMessageQueue; - explicit WFMDemodGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = NULL); + explicit WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~WFMDemodGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/demodwfm/wfmplugin.cpp b/plugins/channelrx/demodwfm/wfmplugin.cpp index cb647dade..2c887fd91 100644 --- a/plugins/channelrx/demodwfm/wfmplugin.cpp +++ b/plugins/channelrx/demodwfm/wfmplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor WFMPlugin::m_pluginDescriptor = { QString("WFM Demodulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -33,18 +33,18 @@ void WFMPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(WFMDemodGUI::m_channelID, this); } -PluginInstanceGUI* WFMPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* WFMPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == WFMDemodGUI::m_channelID) { - WFMDemodGUI* gui = WFMDemodGUI::create(m_pluginAPI, deviceAPI); + WFMDemodGUI* gui = WFMDemodGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return NULL; } } -void WFMPlugin::createInstanceWFM(DeviceSourceAPI *deviceAPI) +void WFMPlugin::createInstanceWFM(DeviceUISet *deviceUISet) { - WFMDemodGUI::create(m_pluginAPI, deviceAPI); + WFMDemodGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channelrx/demodwfm/wfmplugin.h b/plugins/channelrx/demodwfm/wfmplugin.h index 51cfc52ef..a93b36473 100644 --- a/plugins/channelrx/demodwfm/wfmplugin.h +++ b/plugins/channelrx/demodwfm/wfmplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class WFMPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceWFM(DeviceSourceAPI *deviceAPI); + void createInstanceWFM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_WFMPLUGIN_H diff --git a/plugins/channelrx/tcpsrc/tcpsrcgui.cpp b/plugins/channelrx/tcpsrc/tcpsrcgui.cpp index 8d721bf5a..7090f535a 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcgui.cpp +++ b/plugins/channelrx/tcpsrc/tcpsrcgui.cpp @@ -1,6 +1,7 @@ #include "tcpsrcgui.h" #include +#include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" @@ -13,9 +14,9 @@ const QString TCPSrcGUI::m_channelID = "sdrangel.channel.tcpsrc"; -TCPSrcGUI* TCPSrcGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +TCPSrcGUI* TCPSrcGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - TCPSrcGUI* gui = new TCPSrcGUI(pluginAPI, deviceAPI); + TCPSrcGUI* gui = new TCPSrcGUI(pluginAPI, deviceUISet); return gui; } @@ -121,11 +122,11 @@ void TCPSrcGUI::tick() ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1)); } -TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::TCPSrcGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_tcpSrc(0), m_channelMarker(this), m_channelPowerDbAvg(40,0), @@ -140,7 +141,7 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* setAttribute(Qt::WA_DeleteOnClose, true); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_tcpSrc = new TCPSrc(m_deviceAPI); + m_tcpSrc = new TCPSrc(m_deviceUISet->m_deviceSourceAPI); m_tcpSrc->setSpectrum(m_spectrumVis); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); @@ -164,9 +165,9 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -178,7 +179,7 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* TCPSrcGUI::~TCPSrcGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_tcpSrc; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/tcpsrc/tcpsrcgui.h b/plugins/channelrx/tcpsrc/tcpsrcgui.h index b75e608a2..a4697732d 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcgui.h +++ b/plugins/channelrx/tcpsrc/tcpsrcgui.h @@ -13,7 +13,7 @@ #include "tcpsrcsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class TCPSrc; class SpectrumVis; @@ -25,7 +25,7 @@ class TCPSrcGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static TCPSrcGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static TCPSrcGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -57,7 +57,7 @@ private slots: private: Ui::TCPSrcGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; TCPSrc* m_tcpSrc; ChannelMarker m_channelMarker; MovingAverage m_channelPowerDbAvg; @@ -77,7 +77,7 @@ private: SpectrumVis* m_spectrumVis; MessageQueue m_inputMessageQueue; - explicit TCPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = 0); + explicit TCPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~TCPSrcGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp b/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp index 69cb001e8..3835f6692 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp +++ b/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp @@ -1,13 +1,13 @@ -#include "../../channelrx/tcpsrc/tcpsrcplugin.h" +#include "tcpsrcplugin.h" #include #include "plugin/pluginapi.h" -#include "../../channelrx/tcpsrc/tcpsrcgui.h" +#include "tcpsrcgui.h" const PluginDescriptor TCPSrcPlugin::m_pluginDescriptor = { QString("TCP Channel Source"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -33,11 +33,11 @@ void TCPSrcPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(TCPSrcGUI::m_channelID, this); } -PluginInstanceGUI* TCPSrcPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* TCPSrcPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == TCPSrcGUI::m_channelID) { - TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI, deviceAPI); + TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.tcpsrc", gui); // m_pluginAPI->addChannelRollup(gui); return gui; @@ -46,9 +46,9 @@ PluginInstanceGUI* TCPSrcPlugin::createRxChannel(const QString& channelName, Dev } } -void TCPSrcPlugin::createInstanceTCPSrc(DeviceSourceAPI *deviceAPI) +void TCPSrcPlugin::createInstanceTCPSrc(DeviceUISet *deviceUISet) { - TCPSrcGUI::create(m_pluginAPI, deviceAPI); + TCPSrcGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.tcpsrc", gui); // m_pluginAPI->addChannelRollup(gui); } diff --git a/plugins/channelrx/tcpsrc/tcpsrcplugin.h b/plugins/channelrx/tcpsrc/tcpsrcplugin.h index a48356ba4..5650f7051 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcplugin.h +++ b/plugins/channelrx/tcpsrc/tcpsrcplugin.h @@ -4,7 +4,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class TCPSrcPlugin : public QObject, PluginInterface { Q_OBJECT @@ -17,7 +17,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -25,7 +25,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceTCPSrc(DeviceSourceAPI *deviceAPI); + void createInstanceTCPSrc(DeviceUISet *deviceUISet); }; #endif // INCLUDE_TCPSRCPLUGIN_H diff --git a/plugins/channelrx/udpsrc/udpsrcgui.cpp b/plugins/channelrx/udpsrc/udpsrcgui.cpp index 9c1e391fe..2ba6253ff 100644 --- a/plugins/channelrx/udpsrc/udpsrcgui.cpp +++ b/plugins/channelrx/udpsrc/udpsrcgui.cpp @@ -18,6 +18,7 @@ #include "udpsrcgui.h" #include "device/devicesourceapi.h" +#include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" @@ -31,9 +32,9 @@ const QString UDPSrcGUI::m_channelID = "sdrangel.channel.udpsrc"; -UDPSrcGUI* UDPSrcGUI::create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI) +UDPSrcGUI* UDPSrcGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - UDPSrcGUI* gui = new UDPSrcGUI(pluginAPI, deviceAPI); + UDPSrcGUI* gui = new UDPSrcGUI(pluginAPI, deviceUISet); return gui; } @@ -131,11 +132,11 @@ void UDPSrcGUI::tick() m_tickCount++; } -UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent) : +UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::UDPSrcGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_udpSrc(0), m_channelMarker(this), m_channelPowerAvg(4, 1e-10), @@ -150,7 +151,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* setAttribute(Qt::WA_DeleteOnClose, true); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_udpSrc = new UDPSrc(m_deviceAPI); + m_udpSrc = new UDPSrc(m_deviceUISet->m_deviceSourceAPI); m_udpSrc->setSpectrum(m_spectrumVis); ui->fmDeviation->setEnabled(false); @@ -184,9 +185,9 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -197,7 +198,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* UDPSrcGUI::~UDPSrcGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeChannelInstance(this); delete m_udpSrc; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/udpsrc/udpsrcgui.h b/plugins/channelrx/udpsrc/udpsrcgui.h index f32637592..63ad4e994 100644 --- a/plugins/channelrx/udpsrc/udpsrcgui.h +++ b/plugins/channelrx/udpsrc/udpsrcgui.h @@ -29,7 +29,7 @@ #include "udpsrcsettings.h" class PluginAPI; -class DeviceSourceAPI; +class DeviceUISet; class UDPSrc; class SpectrumVis; @@ -41,7 +41,7 @@ class UDPSrcGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static UDPSrcGUI* create(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI); + static UDPSrcGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -79,7 +79,7 @@ private slots: private: Ui::UDPSrcGUI* ui; PluginAPI* m_pluginAPI; - DeviceSourceAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; UDPSrc* m_udpSrc; UDPSrcSettings m_settings; ChannelMarker m_channelMarker; @@ -95,7 +95,7 @@ private: // RF path SpectrumVis* m_spectrumVis; - explicit UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget* parent = 0); + explicit UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~UDPSrcGUI(); void blockApplySettings(bool block); diff --git a/plugins/channelrx/udpsrc/udpsrcplugin.cpp b/plugins/channelrx/udpsrc/udpsrcplugin.cpp index 4afcf94e4..0c739a751 100644 --- a/plugins/channelrx/udpsrc/udpsrcplugin.cpp +++ b/plugins/channelrx/udpsrc/udpsrcplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor UDPSrcPlugin::m_pluginDescriptor = { QString("UDP Channel Source"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -50,11 +50,11 @@ void UDPSrcPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerRxChannel(UDPSrcGUI::m_channelID, this); } -PluginInstanceGUI* UDPSrcPlugin::createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI) +PluginInstanceGUI* UDPSrcPlugin::createRxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == UDPSrcGUI::m_channelID) { - UDPSrcGUI* gui = UDPSrcGUI::create(m_pluginAPI, deviceAPI); + UDPSrcGUI* gui = UDPSrcGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.udpsrc", gui); // m_pluginAPI->addChannelRollup(gui); return gui; @@ -63,9 +63,9 @@ PluginInstanceGUI* UDPSrcPlugin::createRxChannel(const QString& channelName, Dev } } -void UDPSrcPlugin::createInstanceUDPSrc(DeviceSourceAPI *deviceAPI) +void UDPSrcPlugin::createInstanceUDPSrc(DeviceUISet *deviceUISet) { - UDPSrcGUI::create(m_pluginAPI, deviceAPI); + UDPSrcGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.udpsrc", gui); // m_pluginAPI->addChannelRollup(gui); } diff --git a/plugins/channelrx/udpsrc/udpsrcplugin.h b/plugins/channelrx/udpsrc/udpsrcplugin.h index 798ecbd9c..2460854e9 100644 --- a/plugins/channelrx/udpsrc/udpsrcplugin.h +++ b/plugins/channelrx/udpsrc/udpsrcplugin.h @@ -21,7 +21,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSourceAPI; +class DeviceUISet; class UDPSrcPlugin : public QObject, PluginInterface { Q_OBJECT @@ -34,7 +34,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceSourceAPI *deviceAPI); + PluginInstanceGUI* createRxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -42,7 +42,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceUDPSrc(DeviceSourceAPI *deviceAPI); + void createInstanceUDPSrc(DeviceUISet *deviceUISet); }; #endif // INCLUDE_UDPSRCPLUGIN_H diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index a3d52d2e5..369b9d86d 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1025,11 +1025,7 @@ void MainWindow::on_channel_addClicked(bool checked __attribute__((unused))) if (deviceUI->m_deviceSourceEngine) // source device => Rx channels { qDebug("MainWindow::on_channel_addClicked: channel name: %s", qPrintable(m_pluginManager->getRxChannelInstanceName(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex()))); - if (m_pluginManager->getRxChannelInstanceName(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex()) == "org.f4exb.sdrangelove.channel.chanalyzer") { - m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); - } else { - m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI->m_deviceSourceAPI); - } + m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); } else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels { From e30050f01866cfdec46fbbd1ec0ecf85a1b237ed Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 31 Oct 2017 08:35:27 +0100 Subject: [PATCH 12/39] DeviceSourceAPI code cleanup --- sdrgui/device/devicesourceapi.cpp | 153 ------------------------------ sdrgui/device/devicesourceapi.h | 31 ------ sdrgui/mainwindow.cpp | 6 +- 3 files changed, 3 insertions(+), 187 deletions(-) diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 5c9601193..513b7d0af 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -179,49 +179,6 @@ void DeviceSourceAPI::setSampleSourcePluginInstanceGUI(PluginInstanceGUI *gui) m_sampleSourcePluginInstanceUI = gui; } -void DeviceSourceAPI::registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) -{ - m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); - renameChannelInstances(); -} - -void DeviceSourceAPI::removeChannelInstance(PluginInstanceGUI* pluginGUI) -{ - for(ChannelInstanceRegistrations::iterator it = m_channelInstanceRegistrations.begin(); it != m_channelInstanceRegistrations.end(); ++it) - { - if(it->m_gui == pluginGUI) - { - m_channelInstanceRegistrations.erase(it); - break; - } - } - - renameChannelInstances(); -} - -void DeviceSourceAPI::renameChannelInstances() -{ - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - m_channelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_channelInstanceRegistrations[i].m_channelName).arg(i)); - } -} - -void DeviceSourceAPI::freeChannels() -{ -// while(!m_channelInstanceRegistrations.isEmpty()) -// { -// ChannelInstanceRegistration reg(m_channelInstanceRegistrations.takeLast()); -// reg.m_gui->destroy(); -// } - - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - qDebug("DeviceSourceAPI::freeAll: destroying channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - m_channelInstanceRegistrations[i].m_gui->destroy(); - } -} - void DeviceSourceAPI::loadSourceSettings(const Preset* preset) { if (preset->isSourcePreset()) @@ -274,116 +231,6 @@ void DeviceSourceAPI::saveSourceSettings(Preset* preset) } } -void DeviceSourceAPI::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI) -{ - if (preset->isSourcePreset()) - { - qDebug("DeviceSourceAPI::loadChannelSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - - // Available channel plugins - PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getRxChannelRegistrations(); - - // copy currently open channels and clear list - ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; - m_channelInstanceRegistrations.clear(); - - qDebug("DeviceSourceAPI::loadChannelSettings: %d channel(s) in preset", preset->getChannelCount()); - - for(int i = 0; i < preset->getChannelCount(); i++) - { - const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); - ChannelInstanceRegistration reg; - - // if we have one instance available already, use it - - for(int i = 0; i < openChannels.count(); i++) - { - qDebug("DeviceSourceAPI::loadChannelSettings: channels compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); - - if(openChannels[i].m_channelName == channelConfig.m_channel) - { - qDebug("DeviceSourceAPI::loadChannelSettings: channel [%s] found", qPrintable(openChannels[i].m_channelName)); - reg = openChannels.takeAt(i); - m_channelInstanceRegistrations.append(reg); - break; - } - } - - // if we haven't one already, create one - - if(reg.m_gui == NULL) - { - for(int i = 0; i < channelRegistrations->count(); i++) - { - if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) - { - qDebug("DeviceSourceAPI::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); - reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createRxChannel(channelConfig.m_channel, this)); - break; - } - } - } - - if(reg.m_gui != NULL) - { - qDebug("DeviceSourceAPI::loadChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channel)); - reg.m_gui->deserialize(channelConfig.m_config); - } - } - - // everything, that is still "available" is not needed anymore - for(int i = 0; i < openChannels.count(); i++) - { - qDebug("DeviceSourceAPI::loadChannelSettings: destroying spare channel [%s]", qPrintable(openChannels[i].m_channelName)); - openChannels[i].m_gui->destroy(); - } - - renameChannelInstances(); - } - else - { - qDebug("DeviceSourceAPI::loadChannelSettings: Loading preset [%s | %s] not a source preset", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - } -} - -void DeviceSourceAPI::saveChannelSettings(Preset *preset) -{ - if (preset->isSourcePreset()) - { - qSort(m_channelInstanceRegistrations.begin(), m_channelInstanceRegistrations.end()); // sort by increasing delta frequency and type - - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - qDebug("DeviceSourceAPI::saveChannelSettings: channel [%s] saved", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelName, m_channelInstanceRegistrations[i].m_gui->serialize()); - } - } - else - { - qDebug("DeviceSourceAPI::saveChannelSettings: not a source preset"); - } -} - -// sort by increasing delta frequency and type (i.e. name) -bool DeviceSourceAPI::ChannelInstanceRegistration::operator<(const ChannelInstanceRegistration& other) const -{ - if (m_gui && other.m_gui) - { - if (m_gui->getCenterFrequency() == other.m_gui->getCenterFrequency()) - { - return m_gui->getName() < other.m_gui->getName(); - } - else - { - return m_gui->getCenterFrequency() < other.m_gui->getCenterFrequency(); - } - } - else - { - return false; - } -} - void DeviceSourceAPI::addSourceBuddy(DeviceSourceAPI* buddy) { m_sourceBuddies.push_back(buddy); diff --git a/sdrgui/device/devicesourceapi.h b/sdrgui/device/devicesourceapi.h index a6119df33..0946e5d7a 100644 --- a/sdrgui/device/devicesourceapi.h +++ b/sdrgui/device/devicesourceapi.h @@ -88,12 +88,6 @@ public: uint32_t getSampleSourceSequence() const { return m_sampleSourceSequence; } PluginInstanceGUI *getSampleSourcePluginInstanceGUI() { return m_sampleSourcePluginInstanceUI; } - void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); - void removeChannelInstance(PluginInstanceGUI* pluginGUI); - void freeChannels(); - void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveChannelSettings(Preset* preset); - void loadSourceSettings(const Preset* preset); void saveSourceSettings(Preset* preset); @@ -114,29 +108,6 @@ public: const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer protected: - struct ChannelInstanceRegistration - { - QString m_channelName; - PluginInstanceGUI* m_gui; - - ChannelInstanceRegistration() : - m_channelName(), - m_gui(NULL) - { } - - ChannelInstanceRegistration(const QString& channelName, PluginInstanceGUI* pluginGUI) : - m_channelName(channelName), - m_gui(pluginGUI) - { } - - bool operator<(const ChannelInstanceRegistration& other) const; - }; - - typedef QList ChannelInstanceRegistrations; - - - void renameChannelInstances(); - int m_deviceTabIndex; DSPDeviceSourceEngine *m_deviceSourceEngine; GLSpectrum *m_spectrum; @@ -150,8 +121,6 @@ protected: PluginInterface* m_pluginInterface; PluginInstanceGUI* m_sampleSourcePluginInstanceUI; - ChannelInstanceRegistrations m_channelInstanceRegistrations; - std::vector m_sourceBuddies; //!< Device source APIs referencing the same physical device std::vector m_sinkBuddies; //!< Device sink APIs referencing the same physical device void *m_buddySharedPtr; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 369b9d86d..4512723b5 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -330,7 +330,7 @@ void MainWindow::removeLastDevice() ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); // deletes old UI and input object - m_deviceUIs.back()->m_deviceSourceAPI->freeChannels(); // destroys the channel instances + m_deviceUIs.back()->freeChannels(); // destroys the channel instances m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(0); // have source stop sending messages to the GUI m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); @@ -479,7 +479,7 @@ void MainWindow::loadPresetSettings(const Preset* preset, int tabIndex) { deviceUI->m_spectrumGUI->deserialize(preset->getSpectrumConfig()); deviceUI->m_deviceSourceAPI->loadSourceSettings(preset); - deviceUI->m_deviceSourceAPI->loadChannelSettings(preset, m_pluginManager->getPluginAPI()); + deviceUI->loadChannelSettings(preset, m_pluginManager->getPluginAPI()); } else if (deviceUI->m_deviceSinkEngine) // sink device { @@ -507,7 +507,7 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex) { preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); preset->clearChannels(); - deviceUI->m_deviceSourceAPI->saveChannelSettings(preset); + deviceUI->saveChannelSettings(preset); deviceUI->m_deviceSourceAPI->saveSourceSettings(preset); } else if (deviceUI->m_deviceSinkEngine) // sink device From c0afeaefa3a18488556c1c73548bfc88a99c55f3 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 31 Oct 2017 21:57:29 +0100 Subject: [PATCH 13/39] More cleanup on DeviceSourceAPI and PluginManager --- sdrgui/device/devicesourceapi.cpp | 18 +----------------- sdrgui/device/devicesourceapi.h | 13 +------------ sdrgui/mainwindow.cpp | 3 +-- sdrgui/plugin/pluginmanager.cpp | 21 --------------------- sdrgui/plugin/pluginmanager.h | 2 -- 5 files changed, 3 insertions(+), 54 deletions(-) diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 513b7d0af..444c8ef97 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -20,20 +20,14 @@ #include "dsp/devicesamplesource.h" #include "plugin/pluginapi.h" #include "plugin/plugininterface.h" -#include "gui/glspectrum.h" -#include "gui/channelwindow.h" #include "settings/preset.h" #include "dsp/dspengine.h" // TODO: extract GUI dependencies in a separate object DeviceSourceAPI::DeviceSourceAPI(int deviceTabIndex, - DSPDeviceSourceEngine *deviceSourceEngine, - GLSpectrum *glSpectrum, - ChannelWindow *channelWindow) : + DSPDeviceSourceEngine *deviceSourceEngine) : m_deviceTabIndex(deviceTabIndex), m_deviceSourceEngine(deviceSourceEngine), - m_spectrum(glSpectrum), - m_channelWindow(channelWindow), m_sampleSourceSequence(0), m_pluginInterface(0), m_sampleSourcePluginInstanceUI(0), @@ -128,16 +122,6 @@ void DeviceSourceAPI::configureCorrections(bool dcOffsetCorrection, bool iqImbal m_deviceSourceEngine->configureCorrections(dcOffsetCorrection, iqImbalanceCorrection); } -void DeviceSourceAPI::addChannelMarker(ChannelMarker* channelMarker) -{ - m_spectrum->addChannelMarker(channelMarker); -} - -void DeviceSourceAPI::addRollupWidget(QWidget *widget) -{ - m_channelWindow->addRollupWidget(widget); -} - void DeviceSourceAPI::setHardwareId(const QString& id) { m_hardwareId = id; diff --git a/sdrgui/device/devicesourceapi.h b/sdrgui/device/devicesourceapi.h index 0946e5d7a..a090637fa 100644 --- a/sdrgui/device/devicesourceapi.h +++ b/sdrgui/device/devicesourceapi.h @@ -25,14 +25,11 @@ #include "util/export.h" -class GLSpectrum; -class ChannelWindow; class BasebandSampleSink; class ThreadedBasebandSampleSink; class DeviceSampleSource; class MessageQueue; class ChannelMarker; -class QWidget; class PluginInstanceGUI; class PluginAPI; class PluginInterface; @@ -44,9 +41,7 @@ class SDRANGEL_API DeviceSourceAPI : public QObject { public: DeviceSourceAPI(int deviceTabIndex, - DSPDeviceSourceEngine *deviceSourceEngine, - GLSpectrum *glSpectrum, - ChannelWindow *channelWindow); + DSPDeviceSourceEngine *deviceSourceEngine); ~DeviceSourceAPI(); // Device engine stuff @@ -67,10 +62,6 @@ public: MessageQueue *getSampleSourceGUIMessageQueue(); void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection); //!< Configure current device engine DSP corrections - // device related stuff - void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum - void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window - void setHardwareId(const QString& id); void setSampleSourceId(const QString& id); void resetSampleSourceId(); @@ -110,8 +101,6 @@ public: protected: int m_deviceTabIndex; DSPDeviceSourceEngine *m_deviceSourceEngine; - GLSpectrum *m_spectrum; - ChannelWindow *m_channelWindow; QString m_hardwareId; QString m_sampleSourceId; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 4512723b5..83e3ea0e9 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -211,7 +211,7 @@ void MainWindow::addSourceDevice() char tabNameCStr[16]; sprintf(tabNameCStr, "R%d", deviceTabIndex); - DeviceSourceAPI *deviceSourceAPI = new DeviceSourceAPI(deviceTabIndex, dspDeviceSourceEngine, m_deviceUIs.back()->m_spectrum, m_deviceUIs.back()->m_channelWindow); + DeviceSourceAPI *deviceSourceAPI = new DeviceSourceAPI(deviceTabIndex, dspDeviceSourceEngine); m_deviceUIs.back()->m_deviceSourceAPI = deviceSourceAPI; m_deviceUIs.back()->m_samplingDeviceControl->setDeviceAPI(deviceSourceAPI); @@ -1024,7 +1024,6 @@ void MainWindow::on_channel_addClicked(bool checked __attribute__((unused))) if (deviceUI->m_deviceSourceEngine) // source device => Rx channels { - qDebug("MainWindow::on_channel_addClicked: channel name: %s", qPrintable(m_pluginManager->getRxChannelInstanceName(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex()))); m_pluginManager->createRxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); } else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index fa37b408a..6892e4ea1 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -618,15 +618,6 @@ void PluginManager::populateTxChannelComboBox(QComboBox *channels) } } -void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceSourceAPI *deviceAPI) -{ - if (channelPluginIndex < m_rxChannelRegistrations.size()) - { - PluginInterface *pluginInterface = m_rxChannelRegistrations[channelPluginIndex].m_plugin; - pluginInterface->createRxChannel(m_rxChannelRegistrations[channelPluginIndex].m_channelName, deviceAPI); - } -} - void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet) { if (channelPluginIndex < m_rxChannelRegistrations.size()) @@ -636,18 +627,6 @@ void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet } } -QString PluginManager::getRxChannelInstanceName(int channelPluginIndex) -{ - if (channelPluginIndex < m_rxChannelRegistrations.size()) - { - return m_rxChannelRegistrations[channelPluginIndex].m_channelName; - } - else - { - return ""; - } -} - void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI) { if (channelPluginIndex < m_txChannelRegistrations.size()) diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index 13425a676..eef75d9f0 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -71,9 +71,7 @@ public: PluginInterface* getPluginInterfaceAt(int index); void populateRxChannelComboBox(QComboBox *channels); - void createRxChannelInstance(int channelPluginIndex, DeviceSourceAPI *deviceAPI); void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); - QString getRxChannelInstanceName(int channelPluginIndex); void populateTxChannelComboBox(QComboBox *channels); void createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI); From e206fc84a8fcf35a4a498825ff8255afea837415 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 31 Oct 2017 22:37:57 +0100 Subject: [PATCH 14/39] Create channel registration methods for the Tx part in DeviceUISet --- .../channelrx/chanalyzer/chanalyzergui.cpp | 998 +++++++------- .../chanalyzerng/chanalyzernggui.cpp | 1166 ++++++++--------- plugins/channelrx/demodam/amdemodgui.cpp | 4 +- plugins/channelrx/demodatv/atvdemodgui.cpp | 4 +- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 4 +- plugins/channelrx/demoddsd/dsddemodgui.cpp | 4 +- plugins/channelrx/demodlora/lorademodgui.cpp | 4 +- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 4 +- plugins/channelrx/demodssb/ssbdemodgui.cpp | 4 +- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 4 +- plugins/channelrx/tcpsrc/tcpsrcgui.cpp | 4 +- plugins/channelrx/udpsrc/udpsrcgui.cpp | 4 +- sdrgui/device/devicesinkapi.h | 8 +- sdrgui/device/deviceuiset.cpp | 176 ++- sdrgui/device/deviceuiset.h | 21 +- sdrgui/mainwindow.cpp | 6 +- 16 files changed, 1275 insertions(+), 1140 deletions(-) diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 424295cf8..107f8e1ee 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -1,499 +1,499 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2015 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 // -// // -// 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 "chanalyzergui.h" - -#include -#include "device/deviceuiset.h" -#include -#include -#include - -#include "dsp/threadedbasebandsamplesink.h" -#include "ui_chanalyzergui.h" -#include "dsp/spectrumscopecombovis.h" -#include "dsp/spectrumvis.h" -#include "dsp/scopevis.h" -#include "gui/glspectrum.h" -#include "gui/glscope.h" -#include "plugin/pluginapi.h" -#include "util/simpleserializer.h" -#include "util/db.h" -#include "gui/basicchannelsettingswidget.h" -#include "dsp/dspengine.h" -#include "mainwindow.h" - -#include "chanalyzer.h" - -const QString ChannelAnalyzerGUI::m_channelID = "org.f4exb.sdrangelove.channel.chanalyzer"; - -ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) -{ - ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI, deviceUISet); - return gui; -} - -void ChannelAnalyzerGUI::destroy() -{ - delete this; -} - -void ChannelAnalyzerGUI::setName(const QString& name) -{ - setObjectName(name); -} - -QString ChannelAnalyzerGUI::getName() const -{ - return objectName(); -} - -qint64 ChannelAnalyzerGUI::getCenterFrequency() const -{ - return m_channelMarker.getCenterFrequency(); -} - -void ChannelAnalyzerGUI::setCenterFrequency(qint64 centerFrequency) -{ - m_channelMarker.setCenterFrequency(centerFrequency); - applySettings(); -} - -void ChannelAnalyzerGUI::resetToDefaults() -{ - blockApplySettings(true); - - ui->BW->setValue(30); - ui->deltaFrequency->setValue(0); - ui->spanLog2->setValue(3); - - blockApplySettings(false); - applySettings(); -} - -QByteArray ChannelAnalyzerGUI::serialize() const -{ - SimpleSerializer s(1); - s.writeS32(1, m_channelMarker.getCenterFrequency()); - s.writeS32(2, ui->BW->value()); - s.writeBlob(3, ui->spectrumGUI->serialize()); - s.writeU32(4, m_channelMarker.getColor().rgb()); - s.writeS32(5, ui->lowCut->value()); - s.writeS32(6, ui->spanLog2->value()); - s.writeBool(7, ui->ssb->isChecked()); - s.writeBlob(8, ui->scopeGUI->serialize()); - return s.final(); -} - -bool ChannelAnalyzerGUI::deserialize(const QByteArray& data) -{ - SimpleDeserializer d(data); - - if(!d.isValid()) - { - resetToDefaults(); - return false; - } - - if(d.getVersion() == 1) - { - QByteArray bytetmp; - quint32 u32tmp; - qint32 tmp, bw, lowCut; - bool tmpBool; - - blockApplySettings(true); - m_channelMarker.blockSignals(true); - - d.readS32(1, &tmp, 0); - m_channelMarker.setCenterFrequency(tmp); - d.readS32(2, &bw, 30); - ui->BW->setValue(bw); - d.readBlob(3, &bytetmp); - ui->spectrumGUI->deserialize(bytetmp); - - if(d.readU32(4, &u32tmp)) - { - m_channelMarker.setColor(u32tmp); - } - - d.readS32(5, &lowCut, 3); - ui->lowCut->setValue(lowCut); - d.readS32(6, &tmp, 20); - ui->spanLog2->setValue(tmp); - setNewRate(tmp); - d.readBool(7, &tmpBool, false); - ui->ssb->setChecked(tmpBool); - d.readBlob(8, &bytetmp); - ui->scopeGUI->deserialize(bytetmp); - - blockApplySettings(false); - m_channelMarker.blockSignals(false); - - ui->BW->setValue(bw); - ui->lowCut->setValue(lowCut); // does applySettings(); - - return true; - } - else - { - resetToDefaults(); - return false; - } -} - -bool ChannelAnalyzerGUI::handleMessage(const Message& message) -{ - if (ChannelAnalyzer::MsgReportChannelSampleRateChanged::match(message)) - { - setNewRate(m_spanLog2); - return true; - } - - return false; -} - -void ChannelAnalyzerGUI::handleInputMessages() -{ - Message* message; - - while ((message = getInputMessageQueue()->pop()) != 0) - { - qDebug("ChannelAnalyzerGUI::handleInputMessages: message: %s", message->getIdentifier()); - - if (handleMessage(*message)) - { - delete message; - } - } -} - -void ChannelAnalyzerGUI::viewChanged() -{ - applySettings(); -} - -void ChannelAnalyzerGUI::tick() -{ - Real powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq()); - m_channelPowerDbAvg.feed(powDb); - ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1)); -} - -void ChannelAnalyzerGUI::on_deltaMinus_toggled(bool minus) -{ - int deltaFrequency = m_channelMarker.getCenterFrequency(); - bool minusDelta = (deltaFrequency < 0); - - if (minus ^ minusDelta) // sign change - { - m_channelMarker.setCenterFrequency(-deltaFrequency); - } -} - -void ChannelAnalyzerGUI::on_deltaFrequency_changed(quint64 value) -{ - if (ui->deltaMinus->isChecked()) { - m_channelMarker.setCenterFrequency(-value); - } else { - m_channelMarker.setCenterFrequency(value); - } -} - -void ChannelAnalyzerGUI::on_BW_valueChanged(int value) -{ - QString s = QString::number(value/10.0, 'f', 1); - ui->BWText->setText(tr("%1k").arg(s)); - m_channelMarker.setBandwidth(value * 100 * 2); - - if (ui->ssb->isChecked()) - { - if (value < 0) { - m_channelMarker.setSidebands(ChannelMarker::lsb); - } else { - m_channelMarker.setSidebands(ChannelMarker::usb); - } - } - else - { - m_channelMarker.setSidebands(ChannelMarker::dsb); - } - - on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); -} - -int ChannelAnalyzerGUI::getEffectiveLowCutoff(int lowCutoff) -{ - int ssbBW = m_channelMarker.getBandwidth() / 2; - int effectiveLowCutoff = lowCutoff; - const int guard = 100; - - if (ssbBW < 0) { - if (effectiveLowCutoff < ssbBW + guard) { - effectiveLowCutoff = ssbBW + guard; - } - if (effectiveLowCutoff > 0) { - effectiveLowCutoff = 0; - } - } else { - if (effectiveLowCutoff > ssbBW - guard) { - effectiveLowCutoff = ssbBW - guard; - } - if (effectiveLowCutoff < 0) { - effectiveLowCutoff = 0; - } - } - - return effectiveLowCutoff; -} - -void ChannelAnalyzerGUI::on_lowCut_valueChanged(int value) -{ - int lowCutoff = getEffectiveLowCutoff(value * 100); - m_channelMarker.setLowCutoff(lowCutoff); - QString s = QString::number(lowCutoff/1000.0, 'f', 1); - ui->lowCutText->setText(tr("%1k").arg(s)); - ui->lowCut->setValue(lowCutoff/100); - applySettings(); -} - -void ChannelAnalyzerGUI::on_spanLog2_valueChanged(int value) -{ - if (setNewRate(value)) { - applySettings(); - } - -} - -void ChannelAnalyzerGUI::on_ssb_toggled(bool checked) -{ - if (checked) - { - if (ui->BW->value() < 0) { - m_channelMarker.setSidebands(ChannelMarker::lsb); - } else { - m_channelMarker.setSidebands(ChannelMarker::usb); - } - - ui->glSpectrum->setCenterFrequency(m_rate/4); - ui->glSpectrum->setSampleRate(m_rate/2); - ui->glSpectrum->setSsbSpectrum(true); - - on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); - } - else - { - m_channelMarker.setSidebands(ChannelMarker::dsb); - - ui->glSpectrum->setCenterFrequency(0); - ui->glSpectrum->setSampleRate(m_rate); - ui->glSpectrum->setSsbSpectrum(false); - - applySettings(); - } -} - -void ChannelAnalyzerGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) -{ - /* - if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL)) - m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); - */ -} - -void ChannelAnalyzerGUI::onMenuDoubleClicked() -{ - if(!m_basicSettingsShown) { - m_basicSettingsShown = true; - BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this); - bcsw->show(); - } -} - -ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : - RollupWidget(parent), - ui(new Ui::ChannelAnalyzerGUI), - m_pluginAPI(pluginAPI), -// m_deviceAPI(deviceAPI), - m_deviceUISet(deviceUISet), - m_channelMarker(this), - m_basicSettingsShown(false), - m_doApplySettings(true), - m_rate(6000), - m_spanLog2(3), - m_channelPowerDbAvg(40,0) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); - connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - - m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_scopeVis = new ScopeVis(ui->glScope); - m_spectrumScopeComboVis = new SpectrumScopeComboVis(m_spectrumVis, m_scopeVis); - m_channelAnalyzer = new ChannelAnalyzer(m_deviceUISet->m_deviceSourceAPI); - m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); - m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); - - ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); - ui->deltaFrequency->setValueRange(7, 0U, 9999999U); - - ui->glSpectrum->setCenterFrequency(m_rate/2); - ui->glSpectrum->setSampleRate(m_rate); - ui->glSpectrum->setDisplayWaterfall(true); - ui->glSpectrum->setDisplayMaxHold(true); - ui->glSpectrum->setSsbSpectrum(true); - - ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); - ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); - connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); - - //m_channelMarker = new ChannelMarker(this); - m_channelMarker.setColor(Qt::gray); - m_channelMarker.setBandwidth(m_rate); - m_channelMarker.setSidebands(ChannelMarker::usb); - m_channelMarker.setCenterFrequency(0); - m_channelMarker.setVisible(true); - - connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - - m_deviceUISet->registerChannelInstance(m_channelID, this); - m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); - - ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); - ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); - - connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - - applySettings(); - setNewRate(m_spanLog2); -} - -ChannelAnalyzerGUI::~ChannelAnalyzerGUI() -{ - m_deviceUISet->removeChannelInstance(this); - delete m_channelAnalyzer; - delete m_spectrumVis; - delete m_scopeVis; - delete m_spectrumScopeComboVis; - //delete m_channelMarker; - delete ui; -} - -bool ChannelAnalyzerGUI::setNewRate(int spanLog2) -{ - qDebug("ChannelAnalyzerGUI::setNewRate"); - - if ((spanLog2 < 0) || (spanLog2 > 6)) { - return false; - } - - m_spanLog2 = spanLog2; - //m_rate = 48000 / (1<getSampleRate() / (1<BW->value() < -m_rate/200) { - ui->BW->setValue(-m_rate/200); - m_channelMarker.setBandwidth(-m_rate*2); - } else if (ui->BW->value() > m_rate/200) { - ui->BW->setValue(m_rate/200); - m_channelMarker.setBandwidth(m_rate*2); - } - - if (ui->lowCut->value() < -m_rate/200) { - ui->lowCut->setValue(-m_rate/200); - m_channelMarker.setLowCutoff(-m_rate); - } else if (ui->lowCut->value() > m_rate/200) { - ui->lowCut->setValue(m_rate/200); - m_channelMarker.setLowCutoff(m_rate); - } - - ui->BW->setMinimum(-m_rate/200); - ui->lowCut->setMinimum(-m_rate/200); - ui->BW->setMaximum(m_rate/200); - ui->lowCut->setMaximum(m_rate/200); - - QString s = QString::number(m_rate/1000.0, 'f', 1); - ui->spanText->setText(tr("%1k").arg(s)); - - if (ui->ssb->isChecked()) - { - if (ui->BW->value() < 0) { - m_channelMarker.setSidebands(ChannelMarker::lsb); - } else { - m_channelMarker.setSidebands(ChannelMarker::usb); - } - - ui->glSpectrum->setCenterFrequency(m_rate/4); - ui->glSpectrum->setSampleRate(m_rate/2); - ui->glSpectrum->setSsbSpectrum(true); - } - else - { - m_channelMarker.setSidebands(ChannelMarker::dsb); - - ui->glSpectrum->setCenterFrequency(0); - ui->glSpectrum->setSampleRate(m_rate); - ui->glSpectrum->setSsbSpectrum(false); - } - - ui->glScope->setSampleRate(m_rate); - m_scopeVis->setSampleRate(m_rate); - - return true; -} - -void ChannelAnalyzerGUI::blockApplySettings(bool block) -{ - ui->glScope->blockSignals(block); - ui->glSpectrum->blockSignals(block); - m_doApplySettings = !block; -} - -void ChannelAnalyzerGUI::applySettings() -{ - if (m_doApplySettings) - { - setTitleColor(m_channelMarker.getColor()); - ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency())); - ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0); - - ChannelAnalyzer::MsgConfigureChannelizer *msg = ChannelAnalyzer::MsgConfigureChannelizer::create(m_channelMarker.getCenterFrequency()); - m_channelAnalyzer->getInputMessageQueue()->push(msg); - - m_channelAnalyzer->configure(m_channelAnalyzer->getInputMessageQueue(), - ui->BW->value() * 100.0, - ui->lowCut->value() * 100.0, - m_spanLog2, - ui->ssb->isChecked()); - } -} - -void ChannelAnalyzerGUI::leaveEvent(QEvent*) -{ - blockApplySettings(true); - m_channelMarker.setHighlighted(false); - blockApplySettings(false); -} - -void ChannelAnalyzerGUI::enterEvent(QEvent*) -{ - blockApplySettings(true); - m_channelMarker.setHighlighted(true); - blockApplySettings(false); -} - +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 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 // +// // +// 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 "chanalyzergui.h" + +#include +#include "device/deviceuiset.h" +#include +#include +#include + +#include "dsp/threadedbasebandsamplesink.h" +#include "ui_chanalyzergui.h" +#include "dsp/spectrumscopecombovis.h" +#include "dsp/spectrumvis.h" +#include "dsp/scopevis.h" +#include "gui/glspectrum.h" +#include "gui/glscope.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "gui/basicchannelsettingswidget.h" +#include "dsp/dspengine.h" +#include "mainwindow.h" + +#include "chanalyzer.h" + +const QString ChannelAnalyzerGUI::m_channelID = "org.f4exb.sdrangelove.channel.chanalyzer"; + +ChannelAnalyzerGUI* ChannelAnalyzerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) +{ + ChannelAnalyzerGUI* gui = new ChannelAnalyzerGUI(pluginAPI, deviceUISet); + return gui; +} + +void ChannelAnalyzerGUI::destroy() +{ + delete this; +} + +void ChannelAnalyzerGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString ChannelAnalyzerGUI::getName() const +{ + return objectName(); +} + +qint64 ChannelAnalyzerGUI::getCenterFrequency() const +{ + return m_channelMarker.getCenterFrequency(); +} + +void ChannelAnalyzerGUI::setCenterFrequency(qint64 centerFrequency) +{ + m_channelMarker.setCenterFrequency(centerFrequency); + applySettings(); +} + +void ChannelAnalyzerGUI::resetToDefaults() +{ + blockApplySettings(true); + + ui->BW->setValue(30); + ui->deltaFrequency->setValue(0); + ui->spanLog2->setValue(3); + + blockApplySettings(false); + applySettings(); +} + +QByteArray ChannelAnalyzerGUI::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, m_channelMarker.getCenterFrequency()); + s.writeS32(2, ui->BW->value()); + s.writeBlob(3, ui->spectrumGUI->serialize()); + s.writeU32(4, m_channelMarker.getColor().rgb()); + s.writeS32(5, ui->lowCut->value()); + s.writeS32(6, ui->spanLog2->value()); + s.writeBool(7, ui->ssb->isChecked()); + s.writeBlob(8, ui->scopeGUI->serialize()); + return s.final(); +} + +bool ChannelAnalyzerGUI::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + quint32 u32tmp; + qint32 tmp, bw, lowCut; + bool tmpBool; + + blockApplySettings(true); + m_channelMarker.blockSignals(true); + + d.readS32(1, &tmp, 0); + m_channelMarker.setCenterFrequency(tmp); + d.readS32(2, &bw, 30); + ui->BW->setValue(bw); + d.readBlob(3, &bytetmp); + ui->spectrumGUI->deserialize(bytetmp); + + if(d.readU32(4, &u32tmp)) + { + m_channelMarker.setColor(u32tmp); + } + + d.readS32(5, &lowCut, 3); + ui->lowCut->setValue(lowCut); + d.readS32(6, &tmp, 20); + ui->spanLog2->setValue(tmp); + setNewRate(tmp); + d.readBool(7, &tmpBool, false); + ui->ssb->setChecked(tmpBool); + d.readBlob(8, &bytetmp); + ui->scopeGUI->deserialize(bytetmp); + + blockApplySettings(false); + m_channelMarker.blockSignals(false); + + ui->BW->setValue(bw); + ui->lowCut->setValue(lowCut); // does applySettings(); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool ChannelAnalyzerGUI::handleMessage(const Message& message) +{ + if (ChannelAnalyzer::MsgReportChannelSampleRateChanged::match(message)) + { + setNewRate(m_spanLog2); + return true; + } + + return false; +} + +void ChannelAnalyzerGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + qDebug("ChannelAnalyzerGUI::handleInputMessages: message: %s", message->getIdentifier()); + + if (handleMessage(*message)) + { + delete message; + } + } +} + +void ChannelAnalyzerGUI::viewChanged() +{ + applySettings(); +} + +void ChannelAnalyzerGUI::tick() +{ + Real powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq()); + m_channelPowerDbAvg.feed(powDb); + ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1)); +} + +void ChannelAnalyzerGUI::on_deltaMinus_toggled(bool minus) +{ + int deltaFrequency = m_channelMarker.getCenterFrequency(); + bool minusDelta = (deltaFrequency < 0); + + if (minus ^ minusDelta) // sign change + { + m_channelMarker.setCenterFrequency(-deltaFrequency); + } +} + +void ChannelAnalyzerGUI::on_deltaFrequency_changed(quint64 value) +{ + if (ui->deltaMinus->isChecked()) { + m_channelMarker.setCenterFrequency(-value); + } else { + m_channelMarker.setCenterFrequency(value); + } +} + +void ChannelAnalyzerGUI::on_BW_valueChanged(int value) +{ + QString s = QString::number(value/10.0, 'f', 1); + ui->BWText->setText(tr("%1k").arg(s)); + m_channelMarker.setBandwidth(value * 100 * 2); + + if (ui->ssb->isChecked()) + { + if (value < 0) { + m_channelMarker.setSidebands(ChannelMarker::lsb); + } else { + m_channelMarker.setSidebands(ChannelMarker::usb); + } + } + else + { + m_channelMarker.setSidebands(ChannelMarker::dsb); + } + + on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); +} + +int ChannelAnalyzerGUI::getEffectiveLowCutoff(int lowCutoff) +{ + int ssbBW = m_channelMarker.getBandwidth() / 2; + int effectiveLowCutoff = lowCutoff; + const int guard = 100; + + if (ssbBW < 0) { + if (effectiveLowCutoff < ssbBW + guard) { + effectiveLowCutoff = ssbBW + guard; + } + if (effectiveLowCutoff > 0) { + effectiveLowCutoff = 0; + } + } else { + if (effectiveLowCutoff > ssbBW - guard) { + effectiveLowCutoff = ssbBW - guard; + } + if (effectiveLowCutoff < 0) { + effectiveLowCutoff = 0; + } + } + + return effectiveLowCutoff; +} + +void ChannelAnalyzerGUI::on_lowCut_valueChanged(int value) +{ + int lowCutoff = getEffectiveLowCutoff(value * 100); + m_channelMarker.setLowCutoff(lowCutoff); + QString s = QString::number(lowCutoff/1000.0, 'f', 1); + ui->lowCutText->setText(tr("%1k").arg(s)); + ui->lowCut->setValue(lowCutoff/100); + applySettings(); +} + +void ChannelAnalyzerGUI::on_spanLog2_valueChanged(int value) +{ + if (setNewRate(value)) { + applySettings(); + } + +} + +void ChannelAnalyzerGUI::on_ssb_toggled(bool checked) +{ + if (checked) + { + if (ui->BW->value() < 0) { + m_channelMarker.setSidebands(ChannelMarker::lsb); + } else { + m_channelMarker.setSidebands(ChannelMarker::usb); + } + + ui->glSpectrum->setCenterFrequency(m_rate/4); + ui->glSpectrum->setSampleRate(m_rate/2); + ui->glSpectrum->setSsbSpectrum(true); + + on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); + } + else + { + m_channelMarker.setSidebands(ChannelMarker::dsb); + + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_rate); + ui->glSpectrum->setSsbSpectrum(false); + + applySettings(); + } +} + +void ChannelAnalyzerGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) +{ + /* + if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL)) + m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); + */ +} + +void ChannelAnalyzerGUI::onMenuDoubleClicked() +{ + if(!m_basicSettingsShown) { + m_basicSettingsShown = true; + BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this); + bcsw->show(); + } +} + +ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::ChannelAnalyzerGUI), + m_pluginAPI(pluginAPI), +// m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_basicSettingsShown(false), + m_doApplySettings(true), + m_rate(6000), + m_spanLog2(3), + m_channelPowerDbAvg(40,0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); + + m_spectrumVis = new SpectrumVis(ui->glSpectrum); + m_scopeVis = new ScopeVis(ui->glScope); + m_spectrumScopeComboVis = new SpectrumScopeComboVis(m_spectrumVis, m_scopeVis); + m_channelAnalyzer = new ChannelAnalyzer(m_deviceUISet->m_deviceSourceAPI); + m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); + m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); + + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); + ui->deltaFrequency->setValueRange(7, 0U, 9999999U); + + ui->glSpectrum->setCenterFrequency(m_rate/2); + ui->glSpectrum->setSampleRate(m_rate); + ui->glSpectrum->setDisplayWaterfall(true); + ui->glSpectrum->setDisplayMaxHold(true); + ui->glSpectrum->setSsbSpectrum(true); + + ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); + ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); + connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + //m_channelMarker = new ChannelMarker(this); + m_channelMarker.setColor(Qt::gray); + m_channelMarker.setBandwidth(m_rate); + m_channelMarker.setSidebands(ChannelMarker::usb); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setVisible(true); + + connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); + + m_deviceUISet->registerRxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); + + ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); + ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + applySettings(); + setNewRate(m_spanLog2); +} + +ChannelAnalyzerGUI::~ChannelAnalyzerGUI() +{ + m_deviceUISet->removeRxChannelInstance(this); + delete m_channelAnalyzer; + delete m_spectrumVis; + delete m_scopeVis; + delete m_spectrumScopeComboVis; + //delete m_channelMarker; + delete ui; +} + +bool ChannelAnalyzerGUI::setNewRate(int spanLog2) +{ + qDebug("ChannelAnalyzerGUI::setNewRate"); + + if ((spanLog2 < 0) || (spanLog2 > 6)) { + return false; + } + + m_spanLog2 = spanLog2; + //m_rate = 48000 / (1<getSampleRate() / (1<BW->value() < -m_rate/200) { + ui->BW->setValue(-m_rate/200); + m_channelMarker.setBandwidth(-m_rate*2); + } else if (ui->BW->value() > m_rate/200) { + ui->BW->setValue(m_rate/200); + m_channelMarker.setBandwidth(m_rate*2); + } + + if (ui->lowCut->value() < -m_rate/200) { + ui->lowCut->setValue(-m_rate/200); + m_channelMarker.setLowCutoff(-m_rate); + } else if (ui->lowCut->value() > m_rate/200) { + ui->lowCut->setValue(m_rate/200); + m_channelMarker.setLowCutoff(m_rate); + } + + ui->BW->setMinimum(-m_rate/200); + ui->lowCut->setMinimum(-m_rate/200); + ui->BW->setMaximum(m_rate/200); + ui->lowCut->setMaximum(m_rate/200); + + QString s = QString::number(m_rate/1000.0, 'f', 1); + ui->spanText->setText(tr("%1k").arg(s)); + + if (ui->ssb->isChecked()) + { + if (ui->BW->value() < 0) { + m_channelMarker.setSidebands(ChannelMarker::lsb); + } else { + m_channelMarker.setSidebands(ChannelMarker::usb); + } + + ui->glSpectrum->setCenterFrequency(m_rate/4); + ui->glSpectrum->setSampleRate(m_rate/2); + ui->glSpectrum->setSsbSpectrum(true); + } + else + { + m_channelMarker.setSidebands(ChannelMarker::dsb); + + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_rate); + ui->glSpectrum->setSsbSpectrum(false); + } + + ui->glScope->setSampleRate(m_rate); + m_scopeVis->setSampleRate(m_rate); + + return true; +} + +void ChannelAnalyzerGUI::blockApplySettings(bool block) +{ + ui->glScope->blockSignals(block); + ui->glSpectrum->blockSignals(block); + m_doApplySettings = !block; +} + +void ChannelAnalyzerGUI::applySettings() +{ + if (m_doApplySettings) + { + setTitleColor(m_channelMarker.getColor()); + ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency())); + ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0); + + ChannelAnalyzer::MsgConfigureChannelizer *msg = ChannelAnalyzer::MsgConfigureChannelizer::create(m_channelMarker.getCenterFrequency()); + m_channelAnalyzer->getInputMessageQueue()->push(msg); + + m_channelAnalyzer->configure(m_channelAnalyzer->getInputMessageQueue(), + ui->BW->value() * 100.0, + ui->lowCut->value() * 100.0, + m_spanLog2, + ui->ssb->isChecked()); + } +} + +void ChannelAnalyzerGUI::leaveEvent(QEvent*) +{ + blockApplySettings(true); + m_channelMarker.setHighlighted(false); + blockApplySettings(false); +} + +void ChannelAnalyzerGUI::enterEvent(QEvent*) +{ + blockApplySettings(true); + m_channelMarker.setHighlighted(true); + blockApplySettings(false); +} + diff --git a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp index 8640ca738..255fec1d1 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzernggui.cpp @@ -1,583 +1,583 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2017 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 // -// // -// 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 "chanalyzernggui.h" - -#include -#include "device/deviceuiset.h" -#include -#include -#include - -#include "dsp/threadedbasebandsamplesink.h" -#include "ui_chanalyzernggui.h" -#include "dsp/spectrumscopengcombovis.h" -#include "dsp/spectrumvis.h" -#include "dsp/scopevis.h" -#include "gui/glspectrum.h" -#include "gui/glscopeng.h" -#include "plugin/pluginapi.h" -#include "util/simpleserializer.h" -#include "util/db.h" -#include "gui/basicchannelsettingswidget.h" -#include "dsp/dspengine.h" -#include "mainwindow.h" - -#include "chanalyzerng.h" - -const QString ChannelAnalyzerNGGUI::m_channelID = "sdrangel.channel.chanalyzerng"; - -ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) -{ - ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceUISet); - return gui; -} - -void ChannelAnalyzerNGGUI::destroy() -{ - delete this; -} - -void ChannelAnalyzerNGGUI::setName(const QString& name) -{ - setObjectName(name); -} - -QString ChannelAnalyzerNGGUI::getName() const -{ - return objectName(); -} - -qint64 ChannelAnalyzerNGGUI::getCenterFrequency() const -{ - return m_channelMarker.getCenterFrequency(); -} - -void ChannelAnalyzerNGGUI::setCenterFrequency(qint64 centerFrequency) -{ - m_channelMarker.setCenterFrequency(centerFrequency); - applySettings(); -} - -void ChannelAnalyzerNGGUI::resetToDefaults() -{ - blockApplySettings(true); - - ui->useRationalDownsampler->setChecked(false); - ui->BW->setValue(30); - ui->deltaFrequency->setValue(0); - ui->spanLog2->setCurrentIndex(3); - - blockApplySettings(false); - applySettings(); -} - -QByteArray ChannelAnalyzerNGGUI::serialize() const -{ - SimpleSerializer s(1); - s.writeS32(1, m_channelMarker.getCenterFrequency()); - s.writeS32(2, ui->BW->value()); - s.writeBlob(3, ui->spectrumGUI->serialize()); - s.writeU32(4, m_channelMarker.getColor().rgb()); - s.writeS32(5, ui->lowCut->value()); - s.writeS32(6, ui->spanLog2->currentIndex()); - s.writeBool(7, ui->ssb->isChecked()); - s.writeBlob(8, ui->scopeGUI->serialize()); - s.writeU64(9, ui->channelSampleRate->getValueNew()); - return s.final(); -} - -bool ChannelAnalyzerNGGUI::deserialize(const QByteArray& data) -{ - SimpleDeserializer d(data); - - if(!d.isValid()) - { - resetToDefaults(); - return false; - } - - if(d.getVersion() == 1) - { - QByteArray bytetmp; - quint32 u32tmp; - quint64 u64tmp; - qint32 tmp, spanLog2, bw, lowCut; - bool tmpBool; - - blockApplySettings(true); - m_channelMarker.blockSignals(true); - - d.readS32(1, &tmp, 0); - m_channelMarker.setCenterFrequency(tmp); - d.readS32(2, &bw, 30); - d.readBlob(3, &bytetmp); - ui->spectrumGUI->deserialize(bytetmp); - - if(d.readU32(4, &u32tmp)) - { - m_channelMarker.setColor(u32tmp); - } - - d.readS32(5, &lowCut, 3); - d.readS32(6, &spanLog2, 3); - d.readBool(7, &tmpBool, false); - ui->ssb->setChecked(tmpBool); - d.readBlob(8, &bytetmp); - ui->scopeGUI->deserialize(bytetmp); - d.readU64(9, &u64tmp, 2000U); - ui->channelSampleRate->setValue(u64tmp); - - blockApplySettings(false); - m_channelMarker.blockSignals(false); - - ui->spanLog2->setCurrentIndex(spanLog2); - setNewFinalRate(spanLog2); - ui->BW->setValue(bw); - ui->lowCut->setValue(lowCut); // does applySettings(); - - return true; - } - else - { - resetToDefaults(); - return false; - } -} - -bool ChannelAnalyzerNGGUI::handleMessage(const Message& message __attribute__((unused))) -{ - if (ChannelAnalyzerNG::MsgReportChannelSampleRateChanged::match(message)) - { - setNewFinalRate(m_spanLog2); - applySettings(); - return true; - } - - return false; -} - -void ChannelAnalyzerNGGUI::handleInputMessages() -{ - Message* message; - - while ((message = getInputMessageQueue()->pop()) != 0) - { - qDebug("ChannelAnalyzerGUI::handleInputMessages: message: %s", message->getIdentifier()); - - if (handleMessage(*message)) - { - delete message; - } - } -} - -void ChannelAnalyzerNGGUI::viewChanged() -{ - applySettings(); -} - -void ChannelAnalyzerNGGUI::tick() -{ - double powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq()); - m_channelPowerDbAvg.feed(powDb); - ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.average(), 0, 'f', 1)); -} - -//void ChannelAnalyzerNGGUI::channelizerInputSampleRateChanged() -//{ -// //ui->channelSampleRate->setValueRange(7, 2000U, m_channelAnalyzer->getInputSampleRate()); -// setNewFinalRate(m_spanLog2); -// applySettings(); -//} - -void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value) -{ - ui->channelSampleRate->setValueRange(7, 2000U, m_channelAnalyzer->getInputSampleRate()); - - if (ui->useRationalDownsampler->isChecked()) - { - qDebug("ChannelAnalyzerNGGUI::on_channelSampleRate_changed: %llu", value); - setNewFinalRate(m_spanLog2); - applySettings(); - } -} - -void ChannelAnalyzerNGGUI::on_useRationalDownsampler_toggled(bool checked __attribute__((unused))) -{ - setNewFinalRate(m_spanLog2); - applySettings(); -} - -int ChannelAnalyzerNGGUI::getRequestedChannelSampleRate() -{ - if (ui->useRationalDownsampler->isChecked()) { - return ui->channelSampleRate->getValueNew(); - } else { - return m_channelAnalyzer->getChannelizer()->getInputSampleRate(); - } -} - -void ChannelAnalyzerNGGUI::on_deltaFrequency_changed(qint64 value) -{ - m_channelMarker.setCenterFrequency(value); -} - -void ChannelAnalyzerNGGUI::on_BW_valueChanged(int value) -{ - m_channelMarker.setBandwidth(value * 100 * 2); - - if (ui->ssb->isChecked()) - { - QString s = QString::number(value/10.0, 'f', 1); - ui->BWText->setText(tr("%1k").arg(s)); - } - else - { - QString s = QString::number(value/5.0, 'f', 1); // BW = value * 2 - ui->BWText->setText(tr("%1k").arg(s)); - } - - displayBandwidth(); - on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); -} - -int ChannelAnalyzerNGGUI::getEffectiveLowCutoff(int lowCutoff) -{ - int ssbBW = m_channelMarker.getBandwidth() / 2; - int effectiveLowCutoff = lowCutoff; - const int guard = 100; - - if (ssbBW < 0) { - if (effectiveLowCutoff < ssbBW + guard) { - effectiveLowCutoff = ssbBW + guard; - } - if (effectiveLowCutoff > 0) { - effectiveLowCutoff = 0; - } - } else { - if (effectiveLowCutoff > ssbBW - guard) { - effectiveLowCutoff = ssbBW - guard; - } - if (effectiveLowCutoff < 0) { - effectiveLowCutoff = 0; - } - } - - return effectiveLowCutoff; -} - -void ChannelAnalyzerNGGUI::on_lowCut_valueChanged(int value) -{ - int lowCutoff = getEffectiveLowCutoff(value * 100); - m_channelMarker.setLowCutoff(lowCutoff); - QString s = QString::number(lowCutoff/1000.0, 'f', 1); - ui->lowCutText->setText(tr("%1k").arg(s)); - ui->lowCut->setValue(lowCutoff/100); - applySettings(); -} - -void ChannelAnalyzerNGGUI::on_spanLog2_currentIndexChanged(int index) -{ - if (setNewFinalRate(index)) { - applySettings(); - } - -} - -void ChannelAnalyzerNGGUI::on_ssb_toggled(bool checked) -{ - //int bw = m_channelMarker.getBandwidth(); - - if (checked) - { - setFiltersUIBoundaries(); - - ui->BWLabel->setText("LP"); - QString s = QString::number(ui->BW->value()/10.0, 'f', 1); // bw/2 - ui->BWText->setText(tr("%1k").arg(s)); - - on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); - } - else - { - if (ui->BW->value() < 0) { - ui->BW->setValue(-ui->BW->value()); - } - - setFiltersUIBoundaries(); - //m_channelMarker.setBandwidth(ui->BW->value() * 200.0); - - ui->BWLabel->setText("BP"); - QString s = QString::number(ui->BW->value()/5.0, 'f', 1); // bw - ui->BWText->setText(tr("%1k").arg(s)); - - ui->lowCut->setEnabled(false); - ui->lowCut->setValue(0); - ui->lowCutText->setText("0.0k"); - } - - applySettings(); - displayBandwidth(); -} - -void ChannelAnalyzerNGGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) -{ - /* - if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL)) - m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); - */ -} - -void ChannelAnalyzerNGGUI::onMenuDoubleClicked() -{ - if(!m_basicSettingsShown) { - m_basicSettingsShown = true; - BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this); - bcsw->show(); - } -} - -ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : - RollupWidget(parent), - ui(new Ui::ChannelAnalyzerNGGUI), - m_pluginAPI(pluginAPI), - m_deviceUISet(deviceUISet), - m_channelMarker(this), - m_basicSettingsShown(false), - m_doApplySettings(true), - m_rate(6000), - m_spanLog2(0), - m_channelPowerDbAvg(40,0) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); - connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - - m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_scopeVis = new ScopeVisNG(ui->glScope); - m_spectrumScopeComboVis = new SpectrumScopeNGComboVis(m_spectrumVis, m_scopeVis); - m_channelAnalyzer = new ChannelAnalyzerNG(m_deviceUISet->m_deviceSourceAPI); - m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); - m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); -// m_channelizer = new DownChannelizer(m_channelAnalyzer); -// m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); -// connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelizerInputSampleRateChanged())); -// m_deviceAPI->addThreadedSink(m_threadedChannelizer); - - ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); - ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); - ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); - - ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); - ui->channelSampleRate->setValueRange(7, 2000U, 9999999U); - - ui->glSpectrum->setCenterFrequency(m_rate/2); - ui->glSpectrum->setSampleRate(m_rate); - ui->glSpectrum->setDisplayWaterfall(true); - ui->glSpectrum->setDisplayMaxHold(true); - ui->glSpectrum->setSsbSpectrum(false); - ui->glSpectrum->setLsbDisplay(false); - ui->BWLabel->setText("BP"); - - ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); - ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); - connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); - - //m_channelMarker = new ChannelMarker(this); - m_channelMarker.setColor(Qt::gray); - m_channelMarker.setBandwidth(m_rate); - m_channelMarker.setSidebands(ChannelMarker::usb); - m_channelMarker.setCenterFrequency(0); - m_channelMarker.setVisible(true); - - connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - - m_deviceUISet->registerChannelInstance(m_channelID, this); - m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); - - ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); - ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); - - connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - - applySettings(); - setNewFinalRate(m_spanLog2); -} - -ChannelAnalyzerNGGUI::~ChannelAnalyzerNGGUI() -{ - m_deviceUISet->removeChannelInstance(this); -// m_deviceAPI->removeThreadedSink(m_threadedChannelizer); -// delete m_threadedChannelizer; -// delete m_channelizer; - delete m_channelAnalyzer; - delete m_spectrumVis; - delete m_scopeVis; - delete m_spectrumScopeComboVis; - //delete m_channelMarker; - delete ui; -} - -bool ChannelAnalyzerNGGUI::setNewFinalRate(int spanLog2) -{ - qDebug("ChannelAnalyzerNGGUI::setNewRate"); - - if ((spanLog2 < 0) || (spanLog2 > 6)) { - return false; - } - - m_spanLog2 = spanLog2; - //m_rate = 48000 / (1<getInputSampleRate() / (1<spanText->setText(tr("%1 kS/s").arg(s)); - - displayBandwidth(); - - ui->glScope->setSampleRate(m_rate); - m_scopeVis->setSampleRate(m_rate); - - return true; -} - -void ChannelAnalyzerNGGUI::displayBandwidth() -{ - if (ui->ssb->isChecked()) - { - if (ui->BW->value() < 0) - { - m_channelMarker.setSidebands(ChannelMarker::lsb); - ui->glSpectrum->setLsbDisplay(true); - } - else - { - m_channelMarker.setSidebands(ChannelMarker::usb); - ui->glSpectrum->setLsbDisplay(false); - } - - ui->glSpectrum->setCenterFrequency(m_rate/4); - ui->glSpectrum->setSampleRate(m_rate/2); - ui->glSpectrum->setSsbSpectrum(true); - } - else - { - m_channelMarker.setSidebands(ChannelMarker::dsb); - - ui->glSpectrum->setCenterFrequency(0); - ui->glSpectrum->setSampleRate(m_rate); - ui->glSpectrum->setLsbDisplay(false); - ui->glSpectrum->setSsbSpectrum(false); - } - - -} - -void ChannelAnalyzerNGGUI::setFiltersUIBoundaries() -{ - if (ui->BW->value() < -m_rate/200) { - ui->BW->setValue(-m_rate/200); - m_channelMarker.setBandwidth(-m_rate*2); - } else if (ui->BW->value() > m_rate/200) { - ui->BW->setValue(m_rate/200); - m_channelMarker.setBandwidth(m_rate*2); - } - - if (ui->lowCut->value() < -m_rate/200) { - ui->lowCut->setValue(-m_rate/200); - m_channelMarker.setLowCutoff(-m_rate); - } else if (ui->lowCut->value() > m_rate/200) { - ui->lowCut->setValue(m_rate/200); - m_channelMarker.setLowCutoff(m_rate); - } - - if (ui->ssb->isChecked()) { - ui->BW->setMinimum(-m_rate/200); - ui->lowCut->setMinimum(-m_rate/200); - } else { - ui->BW->setMinimum(0); - ui->lowCut->setMinimum(-m_rate/200); - ui->lowCut->setValue(0); - } - - ui->BW->setMaximum(m_rate/200); - ui->lowCut->setMaximum(m_rate/200); -} - -void ChannelAnalyzerNGGUI::blockApplySettings(bool block) -{ - ui->glScope->blockSignals(block); - ui->glSpectrum->blockSignals(block); - m_doApplySettings = !block; -} - -void ChannelAnalyzerNGGUI::applySettings() -{ - if (m_doApplySettings) - { - setTitleColor(m_channelMarker.getColor()); - ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); - - int sampleRate = getRequestedChannelSampleRate(); - - ChannelAnalyzerNG::MsgConfigureChannelizer *msgChannelizer = ChannelAnalyzerNG::MsgConfigureChannelizer::create(sampleRate, m_channelMarker.getCenterFrequency()); - m_channelAnalyzer->getInputMessageQueue()->push(msgChannelizer); - - ChannelAnalyzerNG::MsgConfigureChannelizer *msg = - ChannelAnalyzerNG::MsgConfigureChannelizer::create( - sampleRate, - m_channelMarker.getCenterFrequency()); - m_channelAnalyzer->getInputMessageQueue()->push(msg); - -// m_channelizer->configure(m_channelizer->getInputMessageQueue(), -// //m_channelizer->getInputSampleRate(), -// getRequestedChannelSampleRate(), -// m_channelMarker.getCenterFrequency()); - - m_channelAnalyzer->configure(m_channelAnalyzer->getInputMessageQueue(), - //m_channelizer->getInputSampleRate(), // TODO: specify required channel sample rate - sampleRate, // TODO: specify required channel sample rate - ui->BW->value() * 100.0, - ui->lowCut->value() * 100.0, - m_spanLog2, - ui->ssb->isChecked()); - } -} - -void ChannelAnalyzerNGGUI::leaveEvent(QEvent*) -{ - blockApplySettings(true); - m_channelMarker.setHighlighted(false); - blockApplySettings(false); -} - -void ChannelAnalyzerNGGUI::enterEvent(QEvent*) -{ - blockApplySettings(true); - m_channelMarker.setHighlighted(true); - blockApplySettings(false); -} - +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 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 // +// // +// 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 "chanalyzernggui.h" + +#include +#include "device/deviceuiset.h" +#include +#include +#include + +#include "dsp/threadedbasebandsamplesink.h" +#include "ui_chanalyzernggui.h" +#include "dsp/spectrumscopengcombovis.h" +#include "dsp/spectrumvis.h" +#include "dsp/scopevis.h" +#include "gui/glspectrum.h" +#include "gui/glscopeng.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "gui/basicchannelsettingswidget.h" +#include "dsp/dspengine.h" +#include "mainwindow.h" + +#include "chanalyzerng.h" + +const QString ChannelAnalyzerNGGUI::m_channelID = "sdrangel.channel.chanalyzerng"; + +ChannelAnalyzerNGGUI* ChannelAnalyzerNGGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) +{ + ChannelAnalyzerNGGUI* gui = new ChannelAnalyzerNGGUI(pluginAPI, deviceUISet); + return gui; +} + +void ChannelAnalyzerNGGUI::destroy() +{ + delete this; +} + +void ChannelAnalyzerNGGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString ChannelAnalyzerNGGUI::getName() const +{ + return objectName(); +} + +qint64 ChannelAnalyzerNGGUI::getCenterFrequency() const +{ + return m_channelMarker.getCenterFrequency(); +} + +void ChannelAnalyzerNGGUI::setCenterFrequency(qint64 centerFrequency) +{ + m_channelMarker.setCenterFrequency(centerFrequency); + applySettings(); +} + +void ChannelAnalyzerNGGUI::resetToDefaults() +{ + blockApplySettings(true); + + ui->useRationalDownsampler->setChecked(false); + ui->BW->setValue(30); + ui->deltaFrequency->setValue(0); + ui->spanLog2->setCurrentIndex(3); + + blockApplySettings(false); + applySettings(); +} + +QByteArray ChannelAnalyzerNGGUI::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, m_channelMarker.getCenterFrequency()); + s.writeS32(2, ui->BW->value()); + s.writeBlob(3, ui->spectrumGUI->serialize()); + s.writeU32(4, m_channelMarker.getColor().rgb()); + s.writeS32(5, ui->lowCut->value()); + s.writeS32(6, ui->spanLog2->currentIndex()); + s.writeBool(7, ui->ssb->isChecked()); + s.writeBlob(8, ui->scopeGUI->serialize()); + s.writeU64(9, ui->channelSampleRate->getValueNew()); + return s.final(); +} + +bool ChannelAnalyzerNGGUI::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + quint32 u32tmp; + quint64 u64tmp; + qint32 tmp, spanLog2, bw, lowCut; + bool tmpBool; + + blockApplySettings(true); + m_channelMarker.blockSignals(true); + + d.readS32(1, &tmp, 0); + m_channelMarker.setCenterFrequency(tmp); + d.readS32(2, &bw, 30); + d.readBlob(3, &bytetmp); + ui->spectrumGUI->deserialize(bytetmp); + + if(d.readU32(4, &u32tmp)) + { + m_channelMarker.setColor(u32tmp); + } + + d.readS32(5, &lowCut, 3); + d.readS32(6, &spanLog2, 3); + d.readBool(7, &tmpBool, false); + ui->ssb->setChecked(tmpBool); + d.readBlob(8, &bytetmp); + ui->scopeGUI->deserialize(bytetmp); + d.readU64(9, &u64tmp, 2000U); + ui->channelSampleRate->setValue(u64tmp); + + blockApplySettings(false); + m_channelMarker.blockSignals(false); + + ui->spanLog2->setCurrentIndex(spanLog2); + setNewFinalRate(spanLog2); + ui->BW->setValue(bw); + ui->lowCut->setValue(lowCut); // does applySettings(); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool ChannelAnalyzerNGGUI::handleMessage(const Message& message __attribute__((unused))) +{ + if (ChannelAnalyzerNG::MsgReportChannelSampleRateChanged::match(message)) + { + setNewFinalRate(m_spanLog2); + applySettings(); + return true; + } + + return false; +} + +void ChannelAnalyzerNGGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + qDebug("ChannelAnalyzerGUI::handleInputMessages: message: %s", message->getIdentifier()); + + if (handleMessage(*message)) + { + delete message; + } + } +} + +void ChannelAnalyzerNGGUI::viewChanged() +{ + applySettings(); +} + +void ChannelAnalyzerNGGUI::tick() +{ + double powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq()); + m_channelPowerDbAvg.feed(powDb); + ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.average(), 0, 'f', 1)); +} + +//void ChannelAnalyzerNGGUI::channelizerInputSampleRateChanged() +//{ +// //ui->channelSampleRate->setValueRange(7, 2000U, m_channelAnalyzer->getInputSampleRate()); +// setNewFinalRate(m_spanLog2); +// applySettings(); +//} + +void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value) +{ + ui->channelSampleRate->setValueRange(7, 2000U, m_channelAnalyzer->getInputSampleRate()); + + if (ui->useRationalDownsampler->isChecked()) + { + qDebug("ChannelAnalyzerNGGUI::on_channelSampleRate_changed: %llu", value); + setNewFinalRate(m_spanLog2); + applySettings(); + } +} + +void ChannelAnalyzerNGGUI::on_useRationalDownsampler_toggled(bool checked __attribute__((unused))) +{ + setNewFinalRate(m_spanLog2); + applySettings(); +} + +int ChannelAnalyzerNGGUI::getRequestedChannelSampleRate() +{ + if (ui->useRationalDownsampler->isChecked()) { + return ui->channelSampleRate->getValueNew(); + } else { + return m_channelAnalyzer->getChannelizer()->getInputSampleRate(); + } +} + +void ChannelAnalyzerNGGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); +} + +void ChannelAnalyzerNGGUI::on_BW_valueChanged(int value) +{ + m_channelMarker.setBandwidth(value * 100 * 2); + + if (ui->ssb->isChecked()) + { + QString s = QString::number(value/10.0, 'f', 1); + ui->BWText->setText(tr("%1k").arg(s)); + } + else + { + QString s = QString::number(value/5.0, 'f', 1); // BW = value * 2 + ui->BWText->setText(tr("%1k").arg(s)); + } + + displayBandwidth(); + on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); +} + +int ChannelAnalyzerNGGUI::getEffectiveLowCutoff(int lowCutoff) +{ + int ssbBW = m_channelMarker.getBandwidth() / 2; + int effectiveLowCutoff = lowCutoff; + const int guard = 100; + + if (ssbBW < 0) { + if (effectiveLowCutoff < ssbBW + guard) { + effectiveLowCutoff = ssbBW + guard; + } + if (effectiveLowCutoff > 0) { + effectiveLowCutoff = 0; + } + } else { + if (effectiveLowCutoff > ssbBW - guard) { + effectiveLowCutoff = ssbBW - guard; + } + if (effectiveLowCutoff < 0) { + effectiveLowCutoff = 0; + } + } + + return effectiveLowCutoff; +} + +void ChannelAnalyzerNGGUI::on_lowCut_valueChanged(int value) +{ + int lowCutoff = getEffectiveLowCutoff(value * 100); + m_channelMarker.setLowCutoff(lowCutoff); + QString s = QString::number(lowCutoff/1000.0, 'f', 1); + ui->lowCutText->setText(tr("%1k").arg(s)); + ui->lowCut->setValue(lowCutoff/100); + applySettings(); +} + +void ChannelAnalyzerNGGUI::on_spanLog2_currentIndexChanged(int index) +{ + if (setNewFinalRate(index)) { + applySettings(); + } + +} + +void ChannelAnalyzerNGGUI::on_ssb_toggled(bool checked) +{ + //int bw = m_channelMarker.getBandwidth(); + + if (checked) + { + setFiltersUIBoundaries(); + + ui->BWLabel->setText("LP"); + QString s = QString::number(ui->BW->value()/10.0, 'f', 1); // bw/2 + ui->BWText->setText(tr("%1k").arg(s)); + + on_lowCut_valueChanged(m_channelMarker.getLowCutoff()/100); + } + else + { + if (ui->BW->value() < 0) { + ui->BW->setValue(-ui->BW->value()); + } + + setFiltersUIBoundaries(); + //m_channelMarker.setBandwidth(ui->BW->value() * 200.0); + + ui->BWLabel->setText("BP"); + QString s = QString::number(ui->BW->value()/5.0, 'f', 1); // bw + ui->BWText->setText(tr("%1k").arg(s)); + + ui->lowCut->setEnabled(false); + ui->lowCut->setValue(0); + ui->lowCutText->setText("0.0k"); + } + + applySettings(); + displayBandwidth(); +} + +void ChannelAnalyzerNGGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) +{ + /* + if((widget == ui->spectrumContainer) && (m_ssbDemod != NULL)) + m_ssbDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); + */ +} + +void ChannelAnalyzerNGGUI::onMenuDoubleClicked() +{ + if(!m_basicSettingsShown) { + m_basicSettingsShown = true; + BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this); + bcsw->show(); + } +} + +ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::ChannelAnalyzerNGGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_basicSettingsShown(false), + m_doApplySettings(true), + m_rate(6000), + m_spanLog2(0), + m_channelPowerDbAvg(40,0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); + + m_spectrumVis = new SpectrumVis(ui->glSpectrum); + m_scopeVis = new ScopeVisNG(ui->glScope); + m_spectrumScopeComboVis = new SpectrumScopeNGComboVis(m_spectrumVis, m_scopeVis); + m_channelAnalyzer = new ChannelAnalyzerNG(m_deviceUISet->m_deviceSourceAPI); + m_channelAnalyzer->setSampleSink(m_spectrumScopeComboVis); + m_channelAnalyzer->setMessageQueueToGUI(getInputMessageQueue()); +// m_channelizer = new DownChannelizer(m_channelAnalyzer); +// m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); +// connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelizerInputSampleRateChanged())); +// m_deviceAPI->addThreadedSink(m_threadedChannelizer); + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); + + ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->channelSampleRate->setValueRange(7, 2000U, 9999999U); + + ui->glSpectrum->setCenterFrequency(m_rate/2); + ui->glSpectrum->setSampleRate(m_rate); + ui->glSpectrum->setDisplayWaterfall(true); + ui->glSpectrum->setDisplayMaxHold(true); + ui->glSpectrum->setSsbSpectrum(false); + ui->glSpectrum->setLsbDisplay(false); + ui->BWLabel->setText("BP"); + + ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); + ui->glScope->connectTimer(MainWindow::getInstance()->getMasterTimer()); + connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + //m_channelMarker = new ChannelMarker(this); + m_channelMarker.setColor(Qt::gray); + m_channelMarker.setBandwidth(m_rate); + m_channelMarker.setSidebands(ChannelMarker::usb); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setVisible(true); + + connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); + + m_deviceUISet->registerRxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); + + ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); + ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + applySettings(); + setNewFinalRate(m_spanLog2); +} + +ChannelAnalyzerNGGUI::~ChannelAnalyzerNGGUI() +{ + m_deviceUISet->removeRxChannelInstance(this); +// m_deviceAPI->removeThreadedSink(m_threadedChannelizer); +// delete m_threadedChannelizer; +// delete m_channelizer; + delete m_channelAnalyzer; + delete m_spectrumVis; + delete m_scopeVis; + delete m_spectrumScopeComboVis; + //delete m_channelMarker; + delete ui; +} + +bool ChannelAnalyzerNGGUI::setNewFinalRate(int spanLog2) +{ + qDebug("ChannelAnalyzerNGGUI::setNewRate"); + + if ((spanLog2 < 0) || (spanLog2 > 6)) { + return false; + } + + m_spanLog2 = spanLog2; + //m_rate = 48000 / (1<getInputSampleRate() / (1<spanText->setText(tr("%1 kS/s").arg(s)); + + displayBandwidth(); + + ui->glScope->setSampleRate(m_rate); + m_scopeVis->setSampleRate(m_rate); + + return true; +} + +void ChannelAnalyzerNGGUI::displayBandwidth() +{ + if (ui->ssb->isChecked()) + { + if (ui->BW->value() < 0) + { + m_channelMarker.setSidebands(ChannelMarker::lsb); + ui->glSpectrum->setLsbDisplay(true); + } + else + { + m_channelMarker.setSidebands(ChannelMarker::usb); + ui->glSpectrum->setLsbDisplay(false); + } + + ui->glSpectrum->setCenterFrequency(m_rate/4); + ui->glSpectrum->setSampleRate(m_rate/2); + ui->glSpectrum->setSsbSpectrum(true); + } + else + { + m_channelMarker.setSidebands(ChannelMarker::dsb); + + ui->glSpectrum->setCenterFrequency(0); + ui->glSpectrum->setSampleRate(m_rate); + ui->glSpectrum->setLsbDisplay(false); + ui->glSpectrum->setSsbSpectrum(false); + } + + +} + +void ChannelAnalyzerNGGUI::setFiltersUIBoundaries() +{ + if (ui->BW->value() < -m_rate/200) { + ui->BW->setValue(-m_rate/200); + m_channelMarker.setBandwidth(-m_rate*2); + } else if (ui->BW->value() > m_rate/200) { + ui->BW->setValue(m_rate/200); + m_channelMarker.setBandwidth(m_rate*2); + } + + if (ui->lowCut->value() < -m_rate/200) { + ui->lowCut->setValue(-m_rate/200); + m_channelMarker.setLowCutoff(-m_rate); + } else if (ui->lowCut->value() > m_rate/200) { + ui->lowCut->setValue(m_rate/200); + m_channelMarker.setLowCutoff(m_rate); + } + + if (ui->ssb->isChecked()) { + ui->BW->setMinimum(-m_rate/200); + ui->lowCut->setMinimum(-m_rate/200); + } else { + ui->BW->setMinimum(0); + ui->lowCut->setMinimum(-m_rate/200); + ui->lowCut->setValue(0); + } + + ui->BW->setMaximum(m_rate/200); + ui->lowCut->setMaximum(m_rate/200); +} + +void ChannelAnalyzerNGGUI::blockApplySettings(bool block) +{ + ui->glScope->blockSignals(block); + ui->glSpectrum->blockSignals(block); + m_doApplySettings = !block; +} + +void ChannelAnalyzerNGGUI::applySettings() +{ + if (m_doApplySettings) + { + setTitleColor(m_channelMarker.getColor()); + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + + int sampleRate = getRequestedChannelSampleRate(); + + ChannelAnalyzerNG::MsgConfigureChannelizer *msgChannelizer = ChannelAnalyzerNG::MsgConfigureChannelizer::create(sampleRate, m_channelMarker.getCenterFrequency()); + m_channelAnalyzer->getInputMessageQueue()->push(msgChannelizer); + + ChannelAnalyzerNG::MsgConfigureChannelizer *msg = + ChannelAnalyzerNG::MsgConfigureChannelizer::create( + sampleRate, + m_channelMarker.getCenterFrequency()); + m_channelAnalyzer->getInputMessageQueue()->push(msg); + +// m_channelizer->configure(m_channelizer->getInputMessageQueue(), +// //m_channelizer->getInputSampleRate(), +// getRequestedChannelSampleRate(), +// m_channelMarker.getCenterFrequency()); + + m_channelAnalyzer->configure(m_channelAnalyzer->getInputMessageQueue(), + //m_channelizer->getInputSampleRate(), // TODO: specify required channel sample rate + sampleRate, // TODO: specify required channel sample rate + ui->BW->value() * 100.0, + ui->lowCut->value() * 100.0, + m_spanLog2, + ui->ssb->isChecked()); + } +} + +void ChannelAnalyzerNGGUI::leaveEvent(QEvent*) +{ + blockApplySettings(true); + m_channelMarker.setHighlighted(false); + blockApplySettings(false); +} + +void ChannelAnalyzerNGGUI::enterEvent(QEvent*) +{ + blockApplySettings(true); + m_channelMarker.setHighlighted(true); + blockApplySettings(false); +} + diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index b69d8cddc..a4a836353 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -206,7 +206,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -216,7 +216,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* AMDemodGUI::~AMDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_amDemod; delete ui; } diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 278e6ade1..0b08215d4 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -313,7 +313,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceUISet->registerChannelInstance(m_strChannelID, this); + m_deviceUISet->registerRxChannelInstance(m_strChannelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -348,7 +348,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, ATVDemodGUI::~ATVDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); m_deviceUISet->m_deviceSourceAPI->removeThreadedSink(m_threadedChannelizer); delete m_threadedChannelizer; delete m_channelizer; diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index ec8449bba..0476f702f 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -364,7 +364,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -383,7 +383,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget BFMDemodGUI::~BFMDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_bfmDemod; delete ui; } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 931715192..16aa89359 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -281,7 +281,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -298,7 +298,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget DSDDemodGUI::~DSDDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_dsdDemod; delete ui; } diff --git a/plugins/channelrx/demodlora/lorademodgui.cpp b/plugins/channelrx/demodlora/lorademodgui.cpp index 3c473c08c..9920453fd 100644 --- a/plugins/channelrx/demodlora/lorademodgui.cpp +++ b/plugins/channelrx/demodlora/lorademodgui.cpp @@ -153,7 +153,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidg connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -168,7 +168,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidg LoRaDemodGUI::~LoRaDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_LoRaDemod; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index b39387f12..e21a71430 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -284,7 +284,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -299,7 +299,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget NFMDemodGUI::~NFMDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_nfmDemod; //delete m_channelMarker; delete ui; diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index d1ce494c1..1dbaf83ff 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -331,7 +331,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -344,7 +344,7 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget SSBDemodGUI::~SSBDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_ssbDemod; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 0f91d1c5b..144e2c5b2 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -185,7 +185,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -197,7 +197,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget WFMDemodGUI::~WFMDemodGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_wfmDemod; //delete m_channelMarker; delete ui; diff --git a/plugins/channelrx/tcpsrc/tcpsrcgui.cpp b/plugins/channelrx/tcpsrc/tcpsrcgui.cpp index 7090f535a..8965e911f 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcgui.cpp +++ b/plugins/channelrx/tcpsrc/tcpsrcgui.cpp @@ -165,7 +165,7 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -179,7 +179,7 @@ TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa TCPSrcGUI::~TCPSrcGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_tcpSrc; delete m_spectrumVis; delete ui; diff --git a/plugins/channelrx/udpsrc/udpsrcgui.cpp b/plugins/channelrx/udpsrc/udpsrcgui.cpp index 2ba6253ff..9141cdd82 100644 --- a/plugins/channelrx/udpsrc/udpsrcgui.cpp +++ b/plugins/channelrx/udpsrc/udpsrcgui.cpp @@ -185,7 +185,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceUISet->registerChannelInstance(m_channelID, this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); m_deviceUISet->addChannelMarker(&m_channelMarker); m_deviceUISet->addRollupWidget(this); @@ -198,7 +198,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* pa UDPSrcGUI::~UDPSrcGUI() { - m_deviceUISet->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_udpSrc; delete m_spectrumVis; delete ui; diff --git a/sdrgui/device/devicesinkapi.h b/sdrgui/device/devicesinkapi.h index 100f25993..73f844dd4 100644 --- a/sdrgui/device/devicesinkapi.h +++ b/sdrgui/device/devicesinkapi.h @@ -67,9 +67,12 @@ public: MessageQueue *getSampleSinkInputMessageQueue(); MessageQueue *getSampleSinkGUIMessageQueue(); - // device related stuff + // device GUI related stuff void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window + void freeChannels(); + void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveChannelSettings(Preset* preset); void setHardwareId(const QString& id); void setSampleSinkId(const QString& id); @@ -91,12 +94,9 @@ public: void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); void removeChannelInstance(PluginInstanceGUI* pluginGUI); - void freeChannels(); void loadSinkSettings(const Preset* preset); void saveSinkSettings(Preset* preset); - void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveChannelSettings(Preset* preset); DSPDeviceSinkEngine *getDeviceSinkEngine() { return m_deviceSinkEngine; } diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 3a53f4909..2957e97b8 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -73,36 +73,65 @@ void DeviceUISet::addRollupWidget(QWidget *widget) m_channelWindow->addRollupWidget(widget); } -void DeviceUISet::registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) +void DeviceUISet::registerRxChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) { - m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); - renameChannelInstances(); + m_rxChannelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); + renameRxChannelInstances(); } -void DeviceUISet::removeChannelInstance(PluginInstanceGUI* pluginGUI) +void DeviceUISet::registerTxChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) { - for(ChannelInstanceRegistrations::iterator it = m_channelInstanceRegistrations.begin(); it != m_channelInstanceRegistrations.end(); ++it) + m_txChannelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); + renameRxChannelInstances(); +} + +void DeviceUISet::removeRxChannelInstance(PluginInstanceGUI* pluginGUI) +{ + for(ChannelInstanceRegistrations::iterator it = m_rxChannelInstanceRegistrations.begin(); it != m_rxChannelInstanceRegistrations.end(); ++it) { if(it->m_gui == pluginGUI) { - m_channelInstanceRegistrations.erase(it); + m_rxChannelInstanceRegistrations.erase(it); break; } } - renameChannelInstances(); + renameRxChannelInstances(); } -void DeviceUISet::freeChannels() +void DeviceUISet::removeTxChannelInstance(PluginInstanceGUI* pluginGUI) { - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for(ChannelInstanceRegistrations::iterator it = m_txChannelInstanceRegistrations.begin(); it != m_txChannelInstanceRegistrations.end(); ++it) { - qDebug("DeviceUISet::freeAll: destroying channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - m_channelInstanceRegistrations[i].m_gui->destroy(); + if(it->m_gui == pluginGUI) + { + m_txChannelInstanceRegistrations.erase(it); + break; + } + } + + renameRxChannelInstances(); +} + +void DeviceUISet::freeRxChannels() +{ + for(int i = 0; i < m_rxChannelInstanceRegistrations.count(); i++) + { + qDebug("DeviceUISet::freeAll: destroying channel [%s]", qPrintable(m_rxChannelInstanceRegistrations[i].m_channelName)); + m_rxChannelInstanceRegistrations[i].m_gui->destroy(); } } -void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI) +void DeviceUISet::freeTxChannels() +{ + for(int i = 0; i < m_txChannelInstanceRegistrations.count(); i++) + { + qDebug("DeviceUISet::freeAll: destroying channel [%s]", qPrintable(m_txChannelInstanceRegistrations[i].m_channelName)); + m_txChannelInstanceRegistrations[i].m_gui->destroy(); + } +} + +void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginAPI) { if (preset->isSourcePreset()) { @@ -112,8 +141,8 @@ void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getRxChannelRegistrations(); // copy currently open channels and clear list - ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; - m_channelInstanceRegistrations.clear(); + ChannelInstanceRegistrations openChannels = m_rxChannelInstanceRegistrations; + m_rxChannelInstanceRegistrations.clear(); qDebug("DeviceUISet::loadChannelSettings: %d channel(s) in preset", preset->getChannelCount()); @@ -132,7 +161,7 @@ void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI { qDebug("DeviceSourceAPI::loadChannelSettings: channel [%s] found", qPrintable(openChannels[i].m_channelName)); reg = openChannels.takeAt(i); - m_channelInstanceRegistrations.append(reg); + m_rxChannelInstanceRegistrations.append(reg); break; } } @@ -170,7 +199,7 @@ void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI openChannels[i].m_gui->destroy(); } - renameChannelInstances(); + renameRxChannelInstances(); } else { @@ -178,16 +207,16 @@ void DeviceUISet::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI } } -void DeviceUISet::saveChannelSettings(Preset *preset) +void DeviceUISet::saveRxChannelSettings(Preset *preset) { if (preset->isSourcePreset()) { - qSort(m_channelInstanceRegistrations.begin(), m_channelInstanceRegistrations.end()); // sort by increasing delta frequency and type + qSort(m_rxChannelInstanceRegistrations.begin(), m_rxChannelInstanceRegistrations.end()); // sort by increasing delta frequency and type - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for(int i = 0; i < m_rxChannelInstanceRegistrations.count(); i++) { - qDebug("DeviceUISet::saveChannelSettings: channel [%s] saved", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelName, m_channelInstanceRegistrations[i].m_gui->serialize()); + qDebug("DeviceUISet::saveChannelSettings: channel [%s] saved", qPrintable(m_rxChannelInstanceRegistrations[i].m_channelName)); + preset->addChannel(m_rxChannelInstanceRegistrations[i].m_channelName, m_rxChannelInstanceRegistrations[i].m_gui->serialize()); } } else @@ -196,11 +225,110 @@ void DeviceUISet::saveChannelSettings(Preset *preset) } } -void DeviceUISet::renameChannelInstances() +void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginAPI) { - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + if (preset->isSourcePreset()) { - m_channelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_channelInstanceRegistrations[i].m_channelName).arg(i)); + qDebug("DeviceUISet::loadChannelSettings: Loading preset [%s | %s] not a sink preset", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + } + else + { + qDebug("DeviceUISet::loadChannelSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + // Available channel plugins + PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getTxChannelRegistrations(); + + // copy currently open channels and clear list + ChannelInstanceRegistrations openChannels = m_txChannelInstanceRegistrations; + m_txChannelInstanceRegistrations.clear(); + + qDebug("DeviceUISet::loadChannelSettings: %d channel(s) in preset", preset->getChannelCount()); + + for(int i = 0; i < preset->getChannelCount(); i++) + { + const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); + ChannelInstanceRegistration reg; + + // if we have one instance available already, use it + + for(int i = 0; i < openChannels.count(); i++) + { + qDebug("DeviceUISet::loadChannelSettings: channels compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); + + if(openChannels[i].m_channelName == channelConfig.m_channel) + { + qDebug("DeviceUISet::loadChannelSettings: channel [%s] found", qPrintable(openChannels[i].m_channelName)); + reg = openChannels.takeAt(i); + m_txChannelInstanceRegistrations.append(reg); + break; + } + } + + // if we haven't one already, create one + + if(reg.m_gui == 0) + { + for(int i = 0; i < channelRegistrations->count(); i++) + { + if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) + { + qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); + // TODO: replace m_deviceSinkAPI by this + reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, m_deviceSinkAPI)); + break; + } + } + } + + if(reg.m_gui != 0) + { + qDebug("DeviceUISet::loadChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channel)); + reg.m_gui->deserialize(channelConfig.m_config); + } + } + + // everything, that is still "available" is not needed anymore + for(int i = 0; i < openChannels.count(); i++) + { + qDebug("DeviceUISet::loadChannelSettings: destroying spare channel [%s]", qPrintable(openChannels[i].m_channelName)); + openChannels[i].m_gui->destroy(); + } + + renameTxChannelInstances(); + } +} + +void DeviceUISet::saveTxChannelSettings(Preset *preset) +{ + if (preset->isSourcePreset()) + { + qDebug("DeviceUISet::saveChannelSettings: not a sink preset"); + } + else + { + qSort(m_txChannelInstanceRegistrations.begin(), m_txChannelInstanceRegistrations.end()); // sort by increasing delta frequency and type + + for(int i = 0; i < m_txChannelInstanceRegistrations.count(); i++) + { + qDebug("DeviceUISet::saveChannelSettings: channel [%s] saved", qPrintable(m_txChannelInstanceRegistrations[i].m_channelName)); + preset->addChannel(m_txChannelInstanceRegistrations[i].m_channelName, m_txChannelInstanceRegistrations[i].m_gui->serialize()); + } + } +} + +void DeviceUISet::renameRxChannelInstances() +{ + for(int i = 0; i < m_rxChannelInstanceRegistrations.count(); i++) + { + m_rxChannelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_rxChannelInstanceRegistrations[i].m_channelName).arg(i)); + } +} + +void DeviceUISet::renameTxChannelInstances() +{ + for(int i = 0; i < m_txChannelInstanceRegistrations.count(); i++) + { + m_txChannelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_txChannelInstanceRegistrations[i].m_channelName).arg(i)); } } diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index e0bd7de98..a0e7b946a 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -51,11 +51,16 @@ struct DeviceUISet void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window - void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); - void removeChannelInstance(PluginInstanceGUI* pluginGUI); - void freeChannels(); - void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveChannelSettings(Preset* preset); + void registerRxChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); + void registerTxChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); + void removeRxChannelInstance(PluginInstanceGUI* pluginGUI); + void removeTxChannelInstance(PluginInstanceGUI* pluginGUI); + void freeRxChannels(); + void freeTxChannels(); + void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveRxChannelSettings(Preset* preset); + void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveTxChannelSettings(Preset* preset); private: struct ChannelInstanceRegistration @@ -78,9 +83,11 @@ private: typedef QList ChannelInstanceRegistrations; - ChannelInstanceRegistrations m_channelInstanceRegistrations; + ChannelInstanceRegistrations m_rxChannelInstanceRegistrations; + ChannelInstanceRegistrations m_txChannelInstanceRegistrations; - void renameChannelInstances(); + void renameRxChannelInstances(); + void renameTxChannelInstances(); }; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 83e3ea0e9..696927d25 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -330,7 +330,7 @@ void MainWindow::removeLastDevice() ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); // deletes old UI and input object - m_deviceUIs.back()->freeChannels(); // destroys the channel instances + m_deviceUIs.back()->freeRxChannels(); // destroys the channel instances m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(0); // have source stop sending messages to the GUI m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); @@ -479,7 +479,7 @@ void MainWindow::loadPresetSettings(const Preset* preset, int tabIndex) { deviceUI->m_spectrumGUI->deserialize(preset->getSpectrumConfig()); deviceUI->m_deviceSourceAPI->loadSourceSettings(preset); - deviceUI->loadChannelSettings(preset, m_pluginManager->getPluginAPI()); + deviceUI->loadRxChannelSettings(preset, m_pluginManager->getPluginAPI()); } else if (deviceUI->m_deviceSinkEngine) // sink device { @@ -507,7 +507,7 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex) { preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); preset->clearChannels(); - deviceUI->saveChannelSettings(preset); + deviceUI->saveRxChannelSettings(preset); deviceUI->m_deviceSourceAPI->saveSourceSettings(preset); } else if (deviceUI->m_deviceSinkEngine) // sink device From d5d7d401021deda07076121914afea618b9cde20 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 00:00:03 +0100 Subject: [PATCH 15/39] Tx Channel plugins now handled via DeviceUISet --- plugins/channeltx/modam/ammodgui.cpp | 19 ++++++++++--------- plugins/channeltx/modam/ammodgui.h | 8 ++++---- plugins/channeltx/modam/ammodplugin.cpp | 10 +++++----- plugins/channeltx/modam/ammodplugin.h | 8 ++++---- plugins/channeltx/modatv/atvmodgui.cpp | 19 ++++++++++--------- plugins/channeltx/modatv/atvmodgui.h | 8 ++++---- plugins/channeltx/modatv/atvmodplugin.cpp | 10 +++++----- plugins/channeltx/modatv/atvmodplugin.h | 6 +++--- plugins/channeltx/modnfm/nfmmodgui.cpp | 19 ++++++++++--------- plugins/channeltx/modnfm/nfmmodgui.h | 8 ++++---- plugins/channeltx/modnfm/nfmmodplugin.cpp | 10 +++++----- plugins/channeltx/modnfm/nfmmodplugin.h | 8 ++++---- plugins/channeltx/modssb/ssbmodgui.cpp | 19 ++++++++++--------- plugins/channeltx/modssb/ssbmodgui.h | 8 ++++---- plugins/channeltx/modssb/ssbmodplugin.cpp | 10 +++++----- plugins/channeltx/modssb/ssbmodplugin.h | 8 ++++---- plugins/channeltx/modwfm/wfmmodgui.cpp | 19 ++++++++++--------- plugins/channeltx/modwfm/wfmmodgui.h | 8 ++++---- plugins/channeltx/modwfm/wfmmodplugin.cpp | 10 +++++----- plugins/channeltx/modwfm/wfmmodplugin.h | 4 ++-- plugins/channeltx/udpsink/udpsinkgui.cpp | 19 ++++++++++--------- plugins/channeltx/udpsink/udpsinkgui.h | 8 ++++---- plugins/channeltx/udpsink/udpsinkplugin.cpp | 10 +++++----- plugins/channeltx/udpsink/udpsinkplugin.h | 6 +++--- sdrgui/device/devicesinkapi.cpp | 2 +- sdrgui/device/deviceuiset.cpp | 3 +-- sdrgui/mainwindow.cpp | 6 +++--- sdrgui/plugin/plugininterface.h | 7 +++++-- sdrgui/plugin/pluginmanager.cpp | 4 ++-- sdrgui/plugin/pluginmanager.h | 2 +- 30 files changed, 147 insertions(+), 139 deletions(-) diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index 2eab227a0..576ca5123 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -23,6 +23,7 @@ #include "ammodgui.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "dsp/upchannelizer.h" #include "ui_ammodgui.h" @@ -35,9 +36,9 @@ const QString AMModGUI::m_channelID = "sdrangel.channeltx.modam"; -AMModGUI* AMModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +AMModGUI* AMModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - AMModGUI* gui = new AMModGUI(pluginAPI, deviceAPI); + AMModGUI* gui = new AMModGUI(pluginAPI, deviceUISet); return gui; } @@ -267,11 +268,11 @@ void AMModGUI::onMenuDoubleClicked() } } -AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::AMModGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -288,7 +289,7 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pare connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_amMod = new AMMod(m_deviceAPI); + m_amMod = new AMMod(m_deviceUISet->m_deviceSinkAPI); m_amMod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -305,9 +306,9 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pare connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerTxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->play->setEnabled(false); ui->play->setChecked(false); @@ -326,7 +327,7 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pare AMModGUI::~AMModGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeTxChannelInstance(this); delete m_amMod; delete ui; } diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 73f79da94..4d148ee6e 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -27,7 +27,7 @@ #include "ammodsettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class AMMod; @@ -39,7 +39,7 @@ class AMModGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static AMModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static AMModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -83,7 +83,7 @@ private slots: private: Ui::AMModGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; AMModSettings m_settings; bool m_basicSettingsShown; @@ -101,7 +101,7 @@ private: AMMod::AMModInputAF m_modAFInput; MessageQueue m_inputMessageQueue; - explicit AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~AMModGUI(); void blockApplySettings(bool block); diff --git a/plugins/channeltx/modam/ammodplugin.cpp b/plugins/channeltx/modam/ammodplugin.cpp index 2846d34c6..785a8b3ad 100644 --- a/plugins/channeltx/modam/ammodplugin.cpp +++ b/plugins/channeltx/modam/ammodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor AMModPlugin::m_pluginDescriptor = { QString("AM Modulator"), - QString("3.5.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -49,18 +49,18 @@ void AMModPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(AMModGUI::m_channelID, this); } -PluginInstanceGUI* AMModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* AMModPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == AMModGUI::m_channelID) { - AMModGUI* gui = AMModGUI::create(m_pluginAPI, deviceAPI); + AMModGUI* gui = AMModGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void AMModPlugin::createInstanceModAM(DeviceSinkAPI *deviceAPI) +void AMModPlugin::createInstanceModAM(DeviceUISet *deviceUISet) { - AMModGUI::create(m_pluginAPI, deviceAPI); + AMModGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channeltx/modam/ammodplugin.h b/plugins/channeltx/modam/ammodplugin.h index ce74622b7..5a795abc8 100644 --- a/plugins/channeltx/modam/ammodplugin.h +++ b/plugins/channeltx/modam/ammodplugin.h @@ -20,7 +20,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSinkAPI; +class DeviceUISet; class AMModPlugin : public QObject, PluginInterface { Q_OBJECT @@ -28,12 +28,12 @@ class AMModPlugin : public QObject, PluginInterface { Q_PLUGIN_METADATA(IID "sdrangel.channeltx.ammod") public: - explicit AMModPlugin(QObject* parent = NULL); + explicit AMModPlugin(QObject* parent = 0); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceModAM(DeviceSinkAPI *deviceAPI); + void createInstanceModAM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_AMMODPLUGIN_H diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 33cf98319..3e15e11a9 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -24,6 +24,7 @@ #include #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "gui/basicchannelsettingswidget.h" @@ -36,9 +37,9 @@ const QString ATVModGUI::m_channelID = "sdrangel.channeltx.modatv"; -ATVModGUI* ATVModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +ATVModGUI* ATVModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - ATVModGUI* gui = new ATVModGUI(pluginAPI, deviceAPI); + ATVModGUI* gui = new ATVModGUI(pluginAPI, deviceUISet); return gui; } @@ -586,11 +587,11 @@ void ATVModGUI::onMenuDoubleClicked() } } -ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::ATVModGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -608,7 +609,7 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_atvMod = new ATVMod(m_deviceAPI); + m_atvMod = new ATVMod(m_deviceUISet->m_deviceSinkAPI); m_atvMod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -624,9 +625,9 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerTxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); resetToDefaults(); @@ -649,7 +650,7 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa ATVModGUI::~ATVModGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeTxChannelInstance(this); delete m_atvMod; delete ui; } diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index 1158278ab..75b969181 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -27,7 +27,7 @@ #include "atvmodsettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class ATVMod; class QMessageBox; @@ -40,7 +40,7 @@ class ATVModGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static ATVModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static ATVModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -99,7 +99,7 @@ private slots: private: Ui::ATVModGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; ATVModSettings m_settings; bool m_basicSettingsShown; @@ -119,7 +119,7 @@ private: int m_rfSliderDivisor; MessageQueue m_inputMessageQueue; - explicit ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~ATVModGUI(); void blockApplySettings(bool block); diff --git a/plugins/channeltx/modatv/atvmodplugin.cpp b/plugins/channeltx/modatv/atvmodplugin.cpp index b007e961c..31ac039bc 100644 --- a/plugins/channeltx/modatv/atvmodplugin.cpp +++ b/plugins/channeltx/modatv/atvmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor ATVModPlugin::m_pluginDescriptor = { QString("ATV Modulator"), - QString("3.7.5"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -49,20 +49,20 @@ void ATVModPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(ATVModGUI::m_channelID, this); } -PluginInstanceGUI* ATVModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* ATVModPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == ATVModGUI::m_channelID) { - ATVModGUI* gui = ATVModGUI::create(m_pluginAPI, deviceAPI); + ATVModGUI* gui = ATVModGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void ATVModPlugin::createInstanceModATV(DeviceSinkAPI *deviceAPI) +void ATVModPlugin::createInstanceModATV(DeviceUISet *deviceUISet) { - ATVModGUI::create(m_pluginAPI, deviceAPI); + ATVModGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channeltx/modatv/atvmodplugin.h b/plugins/channeltx/modatv/atvmodplugin.h index 6061dbd26..76b561699 100644 --- a/plugins/channeltx/modatv/atvmodplugin.h +++ b/plugins/channeltx/modatv/atvmodplugin.h @@ -28,12 +28,12 @@ class ATVModPlugin : public QObject, PluginInterface { Q_PLUGIN_METADATA(IID "sdrangel.channeltx.atvmod") public: - explicit ATVModPlugin(QObject* parent = NULL); + explicit ATVModPlugin(QObject* parent = 0); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceModATV(DeviceSinkAPI *deviceAPI); + void createInstanceModATV(DeviceUISet *deviceUISet); }; #endif /* PLUGINS_CHANNELTX_MODATV_ATVMODPLUGIN_H_ */ diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index a6ec2a9f7..720ac8c44 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -21,6 +21,7 @@ #include #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" @@ -34,9 +35,9 @@ const QString NFMModGUI::m_channelID = "sdrangel.channeltx.modnfm"; -NFMModGUI* NFMModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +NFMModGUI* NFMModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - NFMModGUI* gui = new NFMModGUI(pluginAPI, deviceAPI); + NFMModGUI* gui = new NFMModGUI(pluginAPI, deviceUISet); return gui; } @@ -284,11 +285,11 @@ void NFMModGUI::onMenuDoubleClicked() } } -NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::NFMModGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -316,7 +317,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_nfmMod = new NFMMod(m_deviceAPI); + m_nfmMod = new NFMMod(m_deviceUISet->m_deviceSinkAPI); m_nfmMod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -330,9 +331,9 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerTxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->play->setEnabled(false); ui->play->setChecked(false); @@ -358,7 +359,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa NFMModGUI::~NFMModGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeTxChannelInstance(this); delete m_nfmMod; delete ui; } diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index e0fb2829d..c58fb84ea 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -27,7 +27,7 @@ #include "nfmmodsettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class NFMMod; namespace Ui { @@ -38,7 +38,7 @@ class NFMModGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static NFMModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static NFMModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -86,7 +86,7 @@ private slots: private: Ui::NFMModGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; NFMModSettings m_settings; bool m_basicSettingsShown; @@ -104,7 +104,7 @@ private: NFMMod::NFMModInputAF m_modAFInput; MessageQueue m_inputMessageQueue; - explicit NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~NFMModGUI(); void blockApplySettings(bool block); diff --git a/plugins/channeltx/modnfm/nfmmodplugin.cpp b/plugins/channeltx/modnfm/nfmmodplugin.cpp index 4d9aa9cc2..cd2c64ce3 100644 --- a/plugins/channeltx/modnfm/nfmmodplugin.cpp +++ b/plugins/channeltx/modnfm/nfmmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor NFMModPlugin::m_pluginDescriptor = { QString("NFM Modulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -49,18 +49,18 @@ void NFMModPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(NFMModGUI::m_channelID, this); } -PluginInstanceGUI* NFMModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* NFMModPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == NFMModGUI::m_channelID) { - NFMModGUI* gui = NFMModGUI::create(m_pluginAPI, deviceAPI); + NFMModGUI* gui = NFMModGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void NFMModPlugin::createInstanceModNFM(DeviceSinkAPI *deviceAPI) +void NFMModPlugin::createInstanceModNFM(DeviceUISet *deviceUISet) { - NFMModGUI::create(m_pluginAPI, deviceAPI); + NFMModGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channeltx/modnfm/nfmmodplugin.h b/plugins/channeltx/modnfm/nfmmodplugin.h index 8c35aba10..5b544e60f 100644 --- a/plugins/channeltx/modnfm/nfmmodplugin.h +++ b/plugins/channeltx/modnfm/nfmmodplugin.h @@ -20,7 +20,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSinkAPI; +class DeviceUISet; class NFMModPlugin : public QObject, PluginInterface { Q_OBJECT @@ -28,12 +28,12 @@ class NFMModPlugin : public QObject, PluginInterface { Q_PLUGIN_METADATA(IID "sdrangel.channeltx.nfmmod") public: - explicit NFMModPlugin(QObject* parent = NULL); + explicit NFMModPlugin(QObject* parent = 0); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceModNFM(DeviceSinkAPI *deviceAPI); + void createInstanceModNFM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_NFMMODPLUGIN_H diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index aeba20489..1df535507 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -23,6 +23,7 @@ #include "ssbmodgui.h" #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "dsp/spectrumvis.h" #include "ui_ssbmodgui.h" #include "plugin/pluginapi.h" @@ -34,9 +35,9 @@ const QString SSBModGUI::m_channelID = "sdrangel.channeltx.modssb"; -SSBModGUI* SSBModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +SSBModGUI* SSBModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - SSBModGUI* gui = new SSBModGUI(pluginAPI, deviceAPI); + SSBModGUI* gui = new SSBModGUI(pluginAPI, deviceUISet); return gui; } @@ -439,11 +440,11 @@ void SSBModGUI::onMenuDoubleClicked() } } -SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::SSBModGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -462,7 +463,7 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_ssbMod = new SSBMod(m_deviceAPI, m_spectrumVis); + m_ssbMod = new SSBMod(m_deviceUISet->m_deviceSinkAPI, m_spectrumVis); m_ssbMod->setMessageQueueToGUI(getInputMessageQueue()); resetToDefaults(); @@ -488,9 +489,9 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerRxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->cwKeyerGUI->setBuddies(m_ssbMod->getInputMessageQueue(), m_ssbMod->getCWKeyer()); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -509,7 +510,7 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa SSBModGUI::~SSBModGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeRxChannelInstance(this); delete m_ssbMod; delete m_spectrumVis; delete ui; diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index 8e8e3b4b4..171129bcf 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -27,7 +27,7 @@ #include "ssbmodsettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class SSBMod; class SpectrumVis; @@ -40,7 +40,7 @@ class SSBModGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static SSBModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static SSBModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -93,7 +93,7 @@ private slots: private: Ui::SSBModGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; SSBModSettings m_settings; bool m_basicSettingsShown; @@ -113,7 +113,7 @@ private: SSBMod::SSBModInputAF m_modAFInput; MessageQueue m_inputMessageQueue; - explicit SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~SSBModGUI(); int getEffectiveLowCutoff(int lowCutoff); diff --git a/plugins/channeltx/modssb/ssbmodplugin.cpp b/plugins/channeltx/modssb/ssbmodplugin.cpp index 42c2b7764..08f4b734f 100644 --- a/plugins/channeltx/modssb/ssbmodplugin.cpp +++ b/plugins/channeltx/modssb/ssbmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor SSBModPlugin::m_pluginDescriptor = { QString("SSB Modulator"), - QString("3.7.5"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -49,18 +49,18 @@ void SSBModPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(SSBModGUI::m_channelID, this); } -PluginInstanceGUI* SSBModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* SSBModPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == SSBModGUI::m_channelID) { - SSBModGUI* gui = SSBModGUI::create(m_pluginAPI, deviceAPI); + SSBModGUI* gui = SSBModGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void SSBModPlugin::createInstanceModSSB(DeviceSinkAPI *deviceAPI) +void SSBModPlugin::createInstanceModSSB(DeviceUISet *deviceUISet) { - SSBModGUI::create(m_pluginAPI, deviceAPI); + SSBModGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channeltx/modssb/ssbmodplugin.h b/plugins/channeltx/modssb/ssbmodplugin.h index 3c8ef8c98..51e3b273a 100644 --- a/plugins/channeltx/modssb/ssbmodplugin.h +++ b/plugins/channeltx/modssb/ssbmodplugin.h @@ -20,7 +20,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSinkAPI; +class DeviceUISet; class SSBModPlugin : public QObject, PluginInterface { Q_OBJECT @@ -28,12 +28,12 @@ class SSBModPlugin : public QObject, PluginInterface { Q_PLUGIN_METADATA(IID "sdrangel.channeltx.ssbmod") public: - explicit SSBModPlugin(QObject* parent = NULL); + explicit SSBModPlugin(QObject* parent = 0); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceModSSB(DeviceSinkAPI *deviceAPI); + void createInstanceModSSB(DeviceUISet *deviceUISet); }; #endif // INCLUDE_SSBMODPLUGIN_H diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 8f95b5694..e6192edd4 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -21,6 +21,7 @@ #include #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "dsp/upchannelizer.h" #include "dsp/threadedbasebandsamplesource.h" #include "plugin/pluginapi.h" @@ -35,9 +36,9 @@ const QString WFMModGUI::m_channelID = "sdrangel.channeltx.modwfm"; -WFMModGUI* WFMModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +WFMModGUI* WFMModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - WFMModGUI* gui = new WFMModGUI(pluginAPI, deviceAPI); + WFMModGUI* gui = new WFMModGUI(pluginAPI, deviceUISet); return gui; } @@ -273,11 +274,11 @@ void WFMModGUI::onMenuDoubleClicked() } } -WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::WFMModGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true), @@ -305,7 +306,7 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_wfmMod = new WFMMod(m_deviceAPI); + m_wfmMod = new WFMMod(m_deviceUISet->m_deviceSinkAPI); m_wfmMod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); @@ -319,9 +320,9 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerTxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->play->setEnabled(false); ui->play->setChecked(false); @@ -342,7 +343,7 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa WFMModGUI::~WFMModGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeTxChannelInstance(this); delete m_wfmMod; delete ui; } diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 5a56efabb..65b8abe09 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -27,7 +27,7 @@ #include "wfmmodsettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class ThreadedBasebandSampleSource; class UpChannelizer; @@ -41,7 +41,7 @@ class WFMModGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static WFMModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static WFMModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -86,7 +86,7 @@ private slots: private: Ui::WFMModGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; WFMModSettings m_settings; bool m_basicSettingsShown; @@ -106,7 +106,7 @@ private: WFMMod::WFMModInputAF m_modAFInput; MessageQueue m_inputMessageQueue; - explicit WFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = 0); virtual ~WFMModGUI(); void blockApplySettings(bool block); diff --git a/plugins/channeltx/modwfm/wfmmodplugin.cpp b/plugins/channeltx/modwfm/wfmmodplugin.cpp index 6788a774f..13881d30a 100644 --- a/plugins/channeltx/modwfm/wfmmodplugin.cpp +++ b/plugins/channeltx/modwfm/wfmmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor WFMModPlugin::m_pluginDescriptor = { QString("WFM Modulator"), - QString("3.7.4"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -49,18 +49,18 @@ void WFMModPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(WFMModGUI::m_channelID, this); } -PluginInstanceGUI* WFMModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* WFMModPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == WFMModGUI::m_channelID) { - WFMModGUI* gui = WFMModGUI::create(m_pluginAPI, deviceAPI); + WFMModGUI* gui = WFMModGUI::create(m_pluginAPI, deviceUISet); return gui; } else { return 0; } } -void WFMModPlugin::createInstanceModWFM(DeviceSinkAPI *deviceAPI) +void WFMModPlugin::createInstanceModWFM(DeviceUISet *deviceUISet) { - WFMModGUI::create(m_pluginAPI, deviceAPI); + WFMModGUI::create(m_pluginAPI, deviceUISet); } diff --git a/plugins/channeltx/modwfm/wfmmodplugin.h b/plugins/channeltx/modwfm/wfmmodplugin.h index e4b67ba1c..36c7e6db6 100644 --- a/plugins/channeltx/modwfm/wfmmodplugin.h +++ b/plugins/channeltx/modwfm/wfmmodplugin.h @@ -33,7 +33,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -41,7 +41,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceModWFM(DeviceSinkAPI *deviceAPI); + void createInstanceModWFM(DeviceUISet *deviceUISet); }; #endif // INCLUDE_NFMMODPLUGIN_H diff --git a/plugins/channeltx/udpsink/udpsinkgui.cpp b/plugins/channeltx/udpsink/udpsinkgui.cpp index 36d86750a..d42d0cd9c 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.cpp +++ b/plugins/channeltx/udpsink/udpsinkgui.cpp @@ -15,6 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include "device/devicesinkapi.h" +#include "device/deviceuiset.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" #include "util/simpleserializer.h" @@ -28,9 +29,9 @@ const QString UDPSinkGUI::m_channelID = "sdrangel.channeltx.udpsink"; -UDPSinkGUI* UDPSinkGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) +UDPSinkGUI* UDPSinkGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet) { - UDPSinkGUI* gui = new UDPSinkGUI(pluginAPI, deviceAPI); + UDPSinkGUI* gui = new UDPSinkGUI(pluginAPI, deviceUISet); return gui; } @@ -102,11 +103,11 @@ void UDPSinkGUI::handleSourceMessages() } } -UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : +UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent) : RollupWidget(parent), ui(new Ui::UDPSinkGUI), m_pluginAPI(pluginAPI), - m_deviceAPI(deviceAPI), + m_deviceUISet(deviceUISet), m_channelPowerAvg(4, 1e-10), m_inPowerAvg(4, 1e-10), m_tickCount(0), @@ -120,7 +121,7 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* setAttribute(Qt::WA_DeleteOnClose, true); m_spectrumVis = new SpectrumVis(ui->glSpectrum); - m_udpSink = new UDPSink(m_deviceAPI, m_spectrumVis); + m_udpSink = new UDPSink(m_deviceUISet->m_deviceSinkAPI, m_spectrumVis); m_udpSink->setMessageQueueToGUI(getInputMessageQueue()); ui->fmDeviation->setEnabled(false); @@ -146,9 +147,9 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged())); - m_deviceAPI->registerChannelInstance(m_channelID, this); - m_deviceAPI->addChannelMarker(&m_channelMarker); - m_deviceAPI->addRollupWidget(this); + m_deviceUISet->registerTxChannelInstance(m_channelID, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); @@ -161,7 +162,7 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* UDPSinkGUI::~UDPSinkGUI() { - m_deviceAPI->removeChannelInstance(this); + m_deviceUISet->removeTxChannelInstance(this); delete m_udpSink; delete m_spectrumVis; delete ui; diff --git a/plugins/channeltx/udpsink/udpsinkgui.h b/plugins/channeltx/udpsink/udpsinkgui.h index 86006a75d..d19c16d9d 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.h +++ b/plugins/channeltx/udpsink/udpsinkgui.h @@ -28,7 +28,7 @@ #include "udpsinksettings.h" class PluginAPI; -class DeviceSinkAPI; +class DeviceUISet; class UDPSink; class SpectrumVis; @@ -40,7 +40,7 @@ class UDPSinkGUI : public RollupWidget, public PluginInstanceGUI { Q_OBJECT public: - static UDPSinkGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); + static UDPSinkGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet); virtual void destroy(); void setName(const QString& name); @@ -80,7 +80,7 @@ private slots: private: Ui::UDPSinkGUI* ui; PluginAPI* m_pluginAPI; - DeviceSinkAPI* m_deviceAPI; + DeviceUISet* m_deviceUISet; SpectrumVis* m_spectrumVis; UDPSink* m_udpSink; MovingAverage m_channelPowerAvg; @@ -94,7 +94,7 @@ private: bool m_doApplySettings; MessageQueue m_inputMessageQueue; - explicit UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); + explicit UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, QWidget* parent = NULL); virtual ~UDPSinkGUI(); void blockApplySettings(bool block); diff --git a/plugins/channeltx/udpsink/udpsinkplugin.cpp b/plugins/channeltx/udpsink/udpsinkplugin.cpp index a40c7f2e6..91c331850 100644 --- a/plugins/channeltx/udpsink/udpsinkplugin.cpp +++ b/plugins/channeltx/udpsink/udpsinkplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor UDPSinkPlugin::m_pluginDescriptor = { QString("UDP Channel Sink"), - QString("3.7.5"), + QString("3.7.9"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, @@ -50,11 +50,11 @@ void UDPSinkPlugin::initPlugin(PluginAPI* pluginAPI) m_pluginAPI->registerTxChannel(UDPSinkGUI::m_channelID, this); } -PluginInstanceGUI* UDPSinkPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) +PluginInstanceGUI* UDPSinkPlugin::createTxChannel(const QString& channelName, DeviceUISet *deviceUISet) { if(channelName == UDPSinkGUI::m_channelID) { - UDPSinkGUI* gui = UDPSinkGUI::create(m_pluginAPI, deviceAPI); + UDPSinkGUI* gui = UDPSinkGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.udpsrc", gui); // m_pluginAPI->addChannelRollup(gui); return gui; @@ -63,9 +63,9 @@ PluginInstanceGUI* UDPSinkPlugin::createTxChannel(const QString& channelName, De } } -void UDPSinkPlugin::createInstanceUDPSink(DeviceSinkAPI *deviceAPI) +void UDPSinkPlugin::createInstanceUDPSink(DeviceUISet *deviceUISet) { - UDPSinkGUI::create(m_pluginAPI, deviceAPI); + UDPSinkGUI::create(m_pluginAPI, deviceUISet); // deviceAPI->registerChannelInstance("sdrangel.channel.udpsrc", gui); // m_pluginAPI->addChannelRollup(gui); } diff --git a/plugins/channeltx/udpsink/udpsinkplugin.h b/plugins/channeltx/udpsink/udpsinkplugin.h index 7574f2951..d68499ccc 100644 --- a/plugins/channeltx/udpsink/udpsinkplugin.h +++ b/plugins/channeltx/udpsink/udpsinkplugin.h @@ -21,7 +21,7 @@ #include #include "plugin/plugininterface.h" -class DeviceSinkAPI; +class DeviceUISet; class UDPSinkPlugin : public QObject, PluginInterface { Q_OBJECT @@ -34,7 +34,7 @@ public: const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); - PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); + PluginInstanceGUI* createTxChannel(const QString& channelName, DeviceUISet *deviceUISet); private: static const PluginDescriptor m_pluginDescriptor; @@ -42,7 +42,7 @@ private: PluginAPI* m_pluginAPI; private slots: - void createInstanceUDPSink(DeviceSinkAPI *deviceAPI); + void createInstanceUDPSink(DeviceUISet *deviceUISet); }; #endif // INCLUDE_UDPSINKPLUGIN_H diff --git a/sdrgui/device/devicesinkapi.cpp b/sdrgui/device/devicesinkapi.cpp index e7e874894..046afc5e4 100644 --- a/sdrgui/device/devicesinkapi.cpp +++ b/sdrgui/device/devicesinkapi.cpp @@ -319,7 +319,7 @@ void DeviceSinkAPI::loadChannelSettings(const Preset *preset, PluginAPI *pluginA if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) { qDebug("DeviceSinkAPI::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); - reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, this)); + //reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, this)); break; } } diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 2957e97b8..b09659d1c 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -273,8 +273,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) { qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); - // TODO: replace m_deviceSinkAPI by this - reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, m_deviceSinkAPI)); + reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, this)); break; } } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 696927d25..cb519b1ad 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -485,7 +485,7 @@ void MainWindow::loadPresetSettings(const Preset* preset, int tabIndex) { deviceUI->m_spectrumGUI->deserialize(preset->getSpectrumConfig()); deviceUI->m_deviceSinkAPI->loadSinkSettings(preset); - deviceUI->m_deviceSinkAPI->loadChannelSettings(preset, m_pluginManager->getPluginAPI()); + deviceUI->loadTxChannelSettings(preset, m_pluginManager->getPluginAPI()); } } @@ -515,7 +515,7 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex) preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); preset->clearChannels(); preset->setSourcePreset(false); - deviceUI->m_deviceSinkAPI->saveChannelSettings(preset); + deviceUI->saveTxChannelSettings(preset); deviceUI->m_deviceSinkAPI->saveSinkSettings(preset); } @@ -1034,7 +1034,7 @@ void MainWindow::on_channel_addClicked(bool checked __attribute__((unused))) QMessageBox::information(this, tr("Message"), tr("%1 channel(s) already in use. Multiple transmission channels is experimental. You may experience performance problems").arg(nbSources)); } - m_pluginManager->createTxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI->m_deviceSinkAPI); + m_pluginManager->createTxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); } } diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 8abb4dd58..679eccf66 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -61,12 +61,15 @@ public: { return 0; } virtual PluginInstanceGUI* createRxChannel( const QString& channelName __attribute__((unused)), - DeviceUISet *deviceAPI __attribute__((unused)) ) + DeviceUISet *deviceUISet __attribute__((unused)) ) { return 0; } // channel Tx plugins - virtual PluginInstanceGUI* createTxChannel(const QString& channelName __attribute__((unused)), DeviceSinkAPI *deviceAPI __attribute__((unused)) ) { return 0; } + virtual PluginInstanceGUI* createTxChannel( + const QString& channelName __attribute__((unused)), + DeviceUISet *deviceUISet __attribute__((unused)) ) + { return 0; } // device source plugins only diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 6892e4ea1..1ff3f9ef2 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -627,11 +627,11 @@ void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet } } -void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI) +void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet) { if (channelPluginIndex < m_txChannelRegistrations.size()) { PluginInterface *pluginInterface = m_txChannelRegistrations[channelPluginIndex].m_plugin; - pluginInterface->createTxChannel(m_txChannelRegistrations[channelPluginIndex].m_channelName, deviceAPI); + pluginInterface->createTxChannel(m_txChannelRegistrations[channelPluginIndex].m_channelName, deviceUISet); } } diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index eef75d9f0..2498ed177 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -74,7 +74,7 @@ public: void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); void populateTxChannelComboBox(QComboBox *channels); - void createTxChannelInstance(int channelPluginIndex, DeviceSinkAPI *deviceAPI); + void createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); private: struct SamplingDeviceRegistration { From f71803f58ea366102a39414e17bc4d7e65040c21 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 00:18:10 +0100 Subject: [PATCH 16/39] DeviceSinkAPI code cleanup --- sdrgui/device/devicesinkapi.cpp | 166 +----------------------------- sdrgui/device/devicesinkapi.h | 42 +------- sdrgui/device/devicesourceapi.cpp | 1 - sdrgui/device/devicesourceapi.h | 2 - sdrgui/device/deviceuiset.h | 1 + sdrgui/mainwindow.cpp | 4 +- sdrgui/plugin/plugininterface.h | 5 - 7 files changed, 5 insertions(+), 216 deletions(-) diff --git a/sdrgui/device/devicesinkapi.cpp b/sdrgui/device/devicesinkapi.cpp index 046afc5e4..a0b3922ca 100644 --- a/sdrgui/device/devicesinkapi.cpp +++ b/sdrgui/device/devicesinkapi.cpp @@ -18,22 +18,15 @@ #include "device/devicesinkapi.h" #include "device/devicesourceapi.h" #include "dsp/devicesamplesink.h" -#include "plugin/pluginapi.h" #include "plugin/plugininterface.h" -#include "gui/glspectrum.h" -#include "gui/channelwindow.h" #include "settings/preset.h" #include "dsp/dspengine.h" // TODO: extract GUI dependencies in a separate object DeviceSinkAPI::DeviceSinkAPI(int deviceTabIndex, - DSPDeviceSinkEngine *deviceSinkEngine, - GLSpectrum *glSpectrum, - ChannelWindow *channelWindow) : + DSPDeviceSinkEngine *deviceSinkEngine) : m_deviceTabIndex(deviceTabIndex), m_deviceSinkEngine(deviceSinkEngine), - m_spectrum(glSpectrum), - m_channelWindow(channelWindow), m_sampleSinkSequence(0), m_pluginInterface(0), m_sampleSinkPluginInstanceUI(0), @@ -136,16 +129,6 @@ MessageQueue *DeviceSinkAPI::getSampleSinkGUIMessageQueue() return getSampleSink()->getMessageQueueToGUI(); } -void DeviceSinkAPI::addChannelMarker(ChannelMarker* channelMarker) -{ - m_spectrum->addChannelMarker(channelMarker); -} - -void DeviceSinkAPI::addRollupWidget(QWidget *widget) -{ - m_channelWindow->addRollupWidget(widget); -} - void DeviceSinkAPI::setHardwareId(const QString& id) { m_hardwareId = id; @@ -187,43 +170,6 @@ void DeviceSinkAPI::setSampleSinkPluginInstanceUI(PluginInstanceGUI *gui) m_sampleSinkPluginInstanceUI = gui; } -void DeviceSinkAPI::registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI) -{ - m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelName, pluginGUI)); - renameChannelInstances(); -} - -void DeviceSinkAPI::removeChannelInstance(PluginInstanceGUI* pluginGUI) -{ - for(ChannelInstanceRegistrations::iterator it = m_channelInstanceRegistrations.begin(); it != m_channelInstanceRegistrations.end(); ++it) - { - if(it->m_gui == pluginGUI) - { - m_channelInstanceRegistrations.erase(it); - break; - } - } - - renameChannelInstances(); -} - -void DeviceSinkAPI::renameChannelInstances() -{ - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - m_channelInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_channelInstanceRegistrations[i].m_channelName).arg(i)); - } -} - -void DeviceSinkAPI::freeChannels() -{ - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - qDebug("DeviceSinkAPI::freeAll: destroying channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - m_channelInstanceRegistrations[i].m_gui->destroy(); - } -} - void DeviceSinkAPI::loadSinkSettings(const Preset* preset) { if (preset->isSourcePreset()) @@ -271,116 +217,6 @@ void DeviceSinkAPI::saveSinkSettings(Preset* preset) } } -void DeviceSinkAPI::loadChannelSettings(const Preset *preset, PluginAPI *pluginAPI) -{ - if (preset->isSourcePreset()) - { - qDebug("DeviceSinkAPI::loadChannelSettings: Loading preset [%s | %s] not a sink preset", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - } - else - { - qDebug("DeviceSinkAPI::loadChannelSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - - // Available channel plugins - PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getTxChannelRegistrations(); - - // copy currently open channels and clear list - ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; - m_channelInstanceRegistrations.clear(); - - qDebug("DeviceSinkAPI::loadChannelSettings: %d channel(s) in preset", preset->getChannelCount()); - - for(int i = 0; i < preset->getChannelCount(); i++) - { - const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); - ChannelInstanceRegistration reg; - - // if we have one instance available already, use it - - for(int i = 0; i < openChannels.count(); i++) - { - qDebug("DeviceSinkAPI::loadChannelSettings: channels compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); - - if(openChannels[i].m_channelName == channelConfig.m_channel) - { - qDebug("DeviceSinkAPI::loadChannelSettings: channel [%s] found", qPrintable(openChannels[i].m_channelName)); - reg = openChannels.takeAt(i); - m_channelInstanceRegistrations.append(reg); - break; - } - } - - // if we haven't one already, create one - - if(reg.m_gui == 0) - { - for(int i = 0; i < channelRegistrations->count(); i++) - { - if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) - { - qDebug("DeviceSinkAPI::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); - //reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, this)); - break; - } - } - } - - if(reg.m_gui != 0) - { - qDebug("DeviceSinkAPI::loadChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channel)); - reg.m_gui->deserialize(channelConfig.m_config); - } - } - - // everything, that is still "available" is not needed anymore - for(int i = 0; i < openChannels.count(); i++) - { - qDebug("DeviceSinkAPI::loadChannelSettings: destroying spare channel [%s]", qPrintable(openChannels[i].m_channelName)); - openChannels[i].m_gui->destroy(); - } - - renameChannelInstances(); - } -} - -void DeviceSinkAPI::saveChannelSettings(Preset *preset) -{ - if (preset->isSourcePreset()) - { - qDebug("DeviceSinkAPI::saveChannelSettings: not a sink preset"); - } - else - { - qSort(m_channelInstanceRegistrations.begin(), m_channelInstanceRegistrations.end()); // sort by increasing delta frequency and type - - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) - { - qDebug("DeviceSinkAPI::saveChannelSettings: channel [%s] saved", qPrintable(m_channelInstanceRegistrations[i].m_channelName)); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelName, m_channelInstanceRegistrations[i].m_gui->serialize()); - } - } -} - -// sort by increasing delta frequency and type (i.e. name) -bool DeviceSinkAPI::ChannelInstanceRegistration::operator<(const ChannelInstanceRegistration& other) const -{ - if (m_gui && other.m_gui) - { - if (m_gui->getCenterFrequency() == other.m_gui->getCenterFrequency()) - { - return m_gui->getName() < other.m_gui->getName(); - } - else - { - return m_gui->getCenterFrequency() < other.m_gui->getCenterFrequency(); - } - } - else - { - return false; - } -} - void DeviceSinkAPI::addSourceBuddy(DeviceSourceAPI* buddy) { m_sourceBuddies.push_back(buddy); diff --git a/sdrgui/device/devicesinkapi.h b/sdrgui/device/devicesinkapi.h index 73f844dd4..d20623f7f 100644 --- a/sdrgui/device/devicesinkapi.h +++ b/sdrgui/device/devicesinkapi.h @@ -23,16 +23,11 @@ #include "dsp/dspdevicesinkengine.h" #include "util/export.h" -class GLSpectrum; -class ChannelWindow; class BasebandSampleSource; class ThreadedBasebandSampleSource; class DeviceSampleSink; class MessageQueue; -class ChannelMarker; -class QWidget; class PluginInstanceGUI; -class PluginAPI; class PluginInterface; class Preset; class DeviceSourceAPI; @@ -42,9 +37,7 @@ class SDRANGEL_API DeviceSinkAPI : public QObject { public: DeviceSinkAPI(int deviceTabIndex, - DSPDeviceSinkEngine *deviceEngine, - GLSpectrum *glSpectrum, - ChannelWindow *channelWindow); + DSPDeviceSinkEngine *deviceEngine); ~DeviceSinkAPI(); // Device engine stuff @@ -67,13 +60,6 @@ public: MessageQueue *getSampleSinkInputMessageQueue(); MessageQueue *getSampleSinkGUIMessageQueue(); - // device GUI related stuff - void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum - void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window - void freeChannels(); - void loadChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveChannelSettings(Preset* preset); - void setHardwareId(const QString& id); void setSampleSinkId(const QString& id); void resetSampleSinkId(); @@ -115,32 +101,8 @@ public: const QTimer& getMasterTimer() const { return m_masterTimer; } //!< This is the DSPEngine master timer protected: - struct ChannelInstanceRegistration - { - QString m_channelName; - PluginInstanceGUI* m_gui; - - ChannelInstanceRegistration() : - m_channelName(), - m_gui(0) - { } - - ChannelInstanceRegistration(const QString& channelName, PluginInstanceGUI* pluginGUI) : - m_channelName(channelName), - m_gui(pluginGUI) - { } - - bool operator<(const ChannelInstanceRegistration& other) const; - }; - - typedef QList ChannelInstanceRegistrations; - - void renameChannelInstances(); - int m_deviceTabIndex; DSPDeviceSinkEngine *m_deviceSinkEngine; - GLSpectrum *m_spectrum; - ChannelWindow *m_channelWindow; QString m_hardwareId; QString m_sampleSinkId; @@ -150,8 +112,6 @@ protected: PluginInterface* m_pluginInterface; PluginInstanceGUI* m_sampleSinkPluginInstanceUI; - ChannelInstanceRegistrations m_channelInstanceRegistrations; - std::vector m_sourceBuddies; //!< Device source APIs referencing the same physical device std::vector m_sinkBuddies; //!< Device sink APIs referencing the same physical device void *m_buddySharedPtr; diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrgui/device/devicesourceapi.cpp index 444c8ef97..7ce8b1673 100644 --- a/sdrgui/device/devicesourceapi.cpp +++ b/sdrgui/device/devicesourceapi.cpp @@ -18,7 +18,6 @@ #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" #include "dsp/devicesamplesource.h" -#include "plugin/pluginapi.h" #include "plugin/plugininterface.h" #include "settings/preset.h" #include "dsp/dspengine.h" diff --git a/sdrgui/device/devicesourceapi.h b/sdrgui/device/devicesourceapi.h index a090637fa..8a58a556a 100644 --- a/sdrgui/device/devicesourceapi.h +++ b/sdrgui/device/devicesourceapi.h @@ -29,9 +29,7 @@ class BasebandSampleSink; class ThreadedBasebandSampleSink; class DeviceSampleSource; class MessageQueue; -class ChannelMarker; class PluginInstanceGUI; -class PluginAPI; class PluginInterface; class Preset; class DeviceSinkAPI; diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index a0e7b946a..a823c339b 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -30,6 +30,7 @@ class DeviceSourceAPI; class DSPDeviceSinkEngine; class DeviceSinkAPI; class ChannelMarker; +class PluginAPI; struct DeviceUISet { diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index cb519b1ad..e06ee717b 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -273,7 +273,7 @@ void MainWindow::addSinkDevice() char tabNameCStr[16]; sprintf(tabNameCStr, "T%d", deviceTabIndex); - DeviceSinkAPI *deviceSinkAPI = new DeviceSinkAPI(deviceTabIndex, dspDeviceSinkEngine, m_deviceUIs.back()->m_spectrum, m_deviceUIs.back()->m_channelWindow); + DeviceSinkAPI *deviceSinkAPI = new DeviceSinkAPI(deviceTabIndex, dspDeviceSinkEngine); m_deviceUIs.back()->m_deviceSourceAPI = 0; m_deviceUIs.back()->m_deviceSinkAPI = deviceSinkAPI; @@ -368,7 +368,7 @@ void MainWindow::removeLastDevice() ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); // deletes old UI and output object - m_deviceUIs.back()->m_deviceSinkAPI->freeChannels(); + m_deviceUIs.back()->freeTxChannels(); m_deviceUIs.back()->m_deviceSinkAPI->getSampleSink()->setMessageQueueToGUI(0); // have sink stop sending messages to the GUI m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSinkAPI->getSampleSinkPluginInstanceGUI()); diff --git a/sdrgui/plugin/plugininterface.h b/sdrgui/plugin/plugininterface.h index 679eccf66..666f2e2df 100644 --- a/sdrgui/plugin/plugininterface.h +++ b/sdrgui/plugin/plugininterface.h @@ -54,11 +54,6 @@ public: // channel Rx plugins - // TODO: remove this one when migration is complete - virtual PluginInstanceGUI* createRxChannel( - const QString& channelName __attribute__((unused)), - DeviceSourceAPI *deviceAPI __attribute__((unused)) ) - { return 0; } virtual PluginInstanceGUI* createRxChannel( const QString& channelName __attribute__((unused)), DeviceUISet *deviceUISet __attribute__((unused)) ) From 06b346fef242b89608b972a980a94e31483977ea Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 00:30:25 +0100 Subject: [PATCH 17/39] Moved device sink and source APIs amd plugin interface back to sdrbase --- sdrbase/CMakeLists.txt | 11 +++++++++-- {sdrgui => sdrbase}/device/devicesinkapi.cpp | 0 {sdrgui => sdrbase}/device/devicesinkapi.h | 0 {sdrgui => sdrbase}/device/devicesourceapi.cpp | 0 {sdrgui => sdrbase}/device/devicesourceapi.h | 0 {sdrgui => sdrbase}/plugin/plugininterface.cpp | 0 {sdrgui => sdrbase}/plugin/plugininterface.h | 0 sdrgui/CMakeLists.txt | 6 ------ 8 files changed, 9 insertions(+), 8 deletions(-) rename {sdrgui => sdrbase}/device/devicesinkapi.cpp (100%) rename {sdrgui => sdrbase}/device/devicesinkapi.h (100%) rename {sdrgui => sdrbase}/device/devicesourceapi.cpp (100%) rename {sdrgui => sdrbase}/device/devicesourceapi.h (100%) rename {sdrgui => sdrbase}/plugin/plugininterface.cpp (100%) rename {sdrgui => sdrbase}/plugin/plugininterface.h (100%) diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 49d3480d9..3641e6418 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -40,10 +40,12 @@ set(sdrbase_SOURCES dsp/threadedbasebandsamplesink.cpp dsp/threadedbasebandsamplesource.cpp dsp/wfir.cpp - dsp/devicesamplesource.cpp dsp/devicesamplesink.cpp + device/devicesourceapi.cpp + device/devicesinkapi.cpp + settings/preferences.cpp settings/preset.cpp settings/mainsettings.cpp @@ -57,6 +59,8 @@ set(sdrbase_SOURCES util/samplesourceserializer.cpp util/simpleserializer.cpp #util/spinlock.cpp + + plugin/plugininterface.cpp ) set(sdrbase_HEADERS @@ -115,11 +119,14 @@ set(sdrbase_HEADERS dsp/threadedbasebandsamplesink.h dsp/threadedbasebandsamplesource.h dsp/wfir.h - dsp/devicesamplesource.h dsp/devicesamplesink.h + device/devicesourceapi.h + device/devicesinkapi.h + plugin/plugininstancegui.h + plugin/plugininterface.h settings/preferences.h settings/preset.h diff --git a/sdrgui/device/devicesinkapi.cpp b/sdrbase/device/devicesinkapi.cpp similarity index 100% rename from sdrgui/device/devicesinkapi.cpp rename to sdrbase/device/devicesinkapi.cpp diff --git a/sdrgui/device/devicesinkapi.h b/sdrbase/device/devicesinkapi.h similarity index 100% rename from sdrgui/device/devicesinkapi.h rename to sdrbase/device/devicesinkapi.h diff --git a/sdrgui/device/devicesourceapi.cpp b/sdrbase/device/devicesourceapi.cpp similarity index 100% rename from sdrgui/device/devicesourceapi.cpp rename to sdrbase/device/devicesourceapi.cpp diff --git a/sdrgui/device/devicesourceapi.h b/sdrbase/device/devicesourceapi.h similarity index 100% rename from sdrgui/device/devicesourceapi.h rename to sdrbase/device/devicesourceapi.h diff --git a/sdrgui/plugin/plugininterface.cpp b/sdrbase/plugin/plugininterface.cpp similarity index 100% rename from sdrgui/plugin/plugininterface.cpp rename to sdrbase/plugin/plugininterface.cpp diff --git a/sdrgui/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h similarity index 100% rename from sdrgui/plugin/plugininterface.h rename to sdrbase/plugin/plugininterface.h diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 08c63e855..caceba76b 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -43,13 +43,10 @@ set(sdrgui_SOURCES dsp/spectrumscopecombovis.cpp dsp/spectrumscopengcombovis.cpp - device/devicesourceapi.cpp - device/devicesinkapi.cpp device/deviceuiset.cpp plugin/pluginapi.cpp plugin/pluginmanager.cpp - plugin/plugininterface.cpp ) set(sdrgui_HEADERS @@ -95,13 +92,10 @@ set(sdrgui_HEADERS dsp/spectrumscopecombovis.h dsp/spectrumscopengcombovis.h - device/devicesourceapi.h - device/devicesinkapi.h device/deviceuiset.h plugin/pluginapi.h plugin/pluginmanager.h - plugin/plugininterface.h ) set(sdrgui_SOURCES From 56bd15da85910951eb9c9435261de78c79195bc1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 00:34:35 +0100 Subject: [PATCH 18/39] Updated Windows build --- sdrbase/device/devicesinkapi.cpp | 1 - sdrbase/device/devicesourceapi.cpp | 1 - sdrbase/sdrbase.pro | 6 +++++- sdrgui/sdrgui.pro | 2 -- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdrbase/device/devicesinkapi.cpp b/sdrbase/device/devicesinkapi.cpp index a0b3922ca..228a07571 100644 --- a/sdrbase/device/devicesinkapi.cpp +++ b/sdrbase/device/devicesinkapi.cpp @@ -22,7 +22,6 @@ #include "settings/preset.h" #include "dsp/dspengine.h" -// TODO: extract GUI dependencies in a separate object DeviceSinkAPI::DeviceSinkAPI(int deviceTabIndex, DSPDeviceSinkEngine *deviceSinkEngine) : m_deviceTabIndex(deviceTabIndex), diff --git a/sdrbase/device/devicesourceapi.cpp b/sdrbase/device/devicesourceapi.cpp index 7ce8b1673..fe31d1968 100644 --- a/sdrbase/device/devicesourceapi.cpp +++ b/sdrbase/device/devicesourceapi.cpp @@ -22,7 +22,6 @@ #include "settings/preset.h" #include "dsp/dspengine.h" -// TODO: extract GUI dependencies in a separate object DeviceSourceAPI::DeviceSourceAPI(int deviceTabIndex, DSPDeviceSourceEngine *deviceSourceEngine) : m_deviceTabIndex(deviceTabIndex), diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index 6bb6b4435..c03ca0c14 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -49,6 +49,8 @@ SOURCES += audio/audiodeviceinfo.cpp\ audio/audiofifo.cpp\ audio/audiooutput.cpp\ audio/audioinput.cpp\ + device/devicesourceapi.cpp\ + device/devicesinkapi.cpp\ dsp/afsquelch.cpp\ dsp/agc.cpp\ dsp/downchannelizer.cpp\ @@ -96,7 +98,8 @@ SOURCES += audio/audiodeviceinfo.cpp\ util/prettyprint.cpp\ util/syncmessenger.cpp\ util/samplesourceserializer.cpp\ - util/simpleserializer.cpp + util/simpleserializer.cpp\ + plugin/plugininterface.cpp HEADERS += audio/audiodeviceinfo.h\ audio/audiofifo.h\ @@ -157,6 +160,7 @@ HEADERS += audio/audiodeviceinfo.h\ dsp/devicesamplesource.h\ dsp/devicesamplesink.h\ plugin/plugininstancegui.h\ + plugin/plugininterface.h\ settings/preferences.h\ settings/preset.h\ settings/mainsettings.h\ diff --git a/sdrgui/sdrgui.pro b/sdrgui/sdrgui.pro index 481b6f980..117637f5b 100644 --- a/sdrgui/sdrgui.pro +++ b/sdrgui/sdrgui.pro @@ -34,8 +34,6 @@ CONFIG(MINGW64):INCLUDEPATH += "D:\boost_1_58_0" CONFIG(macx):INCLUDEPATH += "../../../boost_1_64_0" SOURCES += mainwindow.cpp\ - device/devicesourceapi.cpp\ - device/devicesinkapi.cpp\ device/deviceuiset.cpp\ dsp/spectrumscopecombovis.cpp\ dsp/spectrumscopengcombovis.cpp\ From 9293d4bc1433c1f498e62d03e1bfd3d50129297f Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 08:32:44 +0100 Subject: [PATCH 19/39] Plugin manager: removed direct combo box population methods --- sdrgui/mainwindow.cpp | 10 ++++++++-- sdrgui/plugin/pluginapi.cpp | 1 - sdrgui/plugin/pluginmanager.cpp | 24 ++++++++++++++---------- sdrgui/plugin/pluginmanager.h | 7 +++++-- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index e06ee717b..99fd18e97 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -216,7 +216,10 @@ void MainWindow::addSourceDevice() m_deviceUIs.back()->m_deviceSourceAPI = deviceSourceAPI; m_deviceUIs.back()->m_samplingDeviceControl->setDeviceAPI(deviceSourceAPI); m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); - m_pluginManager->populateRxChannelComboBox(m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()); + QList channelNames; + m_pluginManager->listRxChannels(channelNames); + QStringList channelNamesList(channelNames); + m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()->addItems(channelNamesList); connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(on_channel_addClicked(bool))); @@ -279,7 +282,10 @@ void MainWindow::addSinkDevice() m_deviceUIs.back()->m_deviceSinkAPI = deviceSinkAPI; m_deviceUIs.back()->m_samplingDeviceControl->setDeviceAPI(deviceSinkAPI); m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); - m_pluginManager->populateTxChannelComboBox(m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()); + QList channelNames; + m_pluginManager->listTxChannels(channelNames); + QStringList channelNamesList(channelNames); + m_deviceUIs.back()->m_samplingDeviceControl->getChannelSelector()->addItems(channelNamesList); connect(m_deviceUIs.back()->m_samplingDeviceControl->getAddChannelButton(), SIGNAL(clicked(bool)), this, SLOT(on_channel_addClicked(bool))); diff --git a/sdrgui/plugin/pluginapi.cpp b/sdrgui/plugin/pluginapi.cpp index 1e2f55039..6f8d32c41 100644 --- a/sdrgui/plugin/pluginapi.cpp +++ b/sdrgui/plugin/pluginapi.cpp @@ -1,4 +1,3 @@ -#include #include "plugin/pluginapi.h" #include "plugin/pluginmanager.h" diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 1ff3f9ef2..cae4c42af 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -600,21 +600,25 @@ PluginInterface* PluginManager::getPluginInterfaceAt(int index) } } -void PluginManager::populateRxChannelComboBox(QComboBox *channels) +void PluginManager::listTxChannels(QList& list) { - for(PluginAPI::ChannelRegistrations::iterator it = m_rxChannelRegistrations.begin(); it != m_rxChannelRegistrations.end(); ++it) - { - const PluginDescriptor& pluginDescipror = it->m_plugin->getPluginDescriptor(); - channels->addItem(pluginDescipror.displayedName); - } -} + list.clear(); -void PluginManager::populateTxChannelComboBox(QComboBox *channels) -{ for(PluginAPI::ChannelRegistrations::iterator it = m_txChannelRegistrations.begin(); it != m_txChannelRegistrations.end(); ++it) { const PluginDescriptor& pluginDescipror = it->m_plugin->getPluginDescriptor(); - channels->addItem(pluginDescipror.displayedName); + list.append(pluginDescipror.displayedName); + } +} + +void PluginManager::listRxChannels(QList& list) +{ + list.clear(); + + for(PluginAPI::ChannelRegistrations::iterator it = m_rxChannelRegistrations.begin(); it != m_rxChannelRegistrations.end(); ++it) + { + const PluginDescriptor& pluginDescipror = it->m_plugin->getPluginDescriptor(); + list.append(pluginDescipror.displayedName); } } diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index 2498ed177..8516ef317 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include + #include "plugin/plugininterface.h" #include "plugin/pluginapi.h" #include "util/export.h" @@ -70,11 +73,11 @@ public: PluginInterface* getPluginInterfaceAt(int index); - void populateRxChannelComboBox(QComboBox *channels); void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); + void listRxChannels(QList& list); - void populateTxChannelComboBox(QComboBox *channels); void createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); + void listTxChannels(QList& list); private: struct SamplingDeviceRegistration { From 66b06dcd08d174e2dcbc212783d3b60aee72c7eb Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 10:37:00 +0100 Subject: [PATCH 20/39] Added Rx/Tx indicator, channel index and claiming device set index to the SamplingDevice struct used for device enumeration --- .../bladerfoutput/bladerfoutputplugin.cpp | 4 +++- .../samplesink/filesink/filesinkplugin.cpp | 4 +++- .../hackrfoutput/hackrfoutputplugin.cpp | 4 +++- .../limesdroutput/limesdroutputplugin.cpp | 24 +++++++++++++------ .../plutosdroutput/plutosdroutputplugin.cpp | 4 +++- .../sdrdaemonsink/sdrdaemonsinkplugin.cpp | 4 +++- plugins/samplesource/airspy/airspyplugin.cpp | 4 +++- .../bladerfinput/bladerfinputplugin.cpp | 4 +++- plugins/samplesource/fcdpro/fcdproplugin.cpp | 4 +++- .../fcdproplus/fcdproplusplugin.cpp | 4 +++- .../filesource/filesourceplugin.cpp | 4 +++- .../hackrfinput/hackrfinputplugin.cpp | 4 +++- .../limesdrinput/limesdrinputplugin.cpp | 23 ++++++++++++------ .../plutosdrinput/plutosdrinputplugin.cpp | 4 +++- plugins/samplesource/rtlsdr/rtlsdrplugin.cpp | 4 +++- .../sdrdaemonsource/sdrdaemonsourceplugin.cpp | 4 +++- .../samplesource/sdrplay/sdrplayplugin.cpp | 4 +++- sdrbase/plugin/plugininterface.h | 22 +++++++++++------ 18 files changed, 93 insertions(+), 36 deletions(-) diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp index dddeac766..5c8faf1eb 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp @@ -66,7 +66,9 @@ PluginInterface::SamplingDevices BladerfOutputPlugin::enumSampleSinks() m_hardwareID, m_deviceTypeID, QString(devinfo[i].serial), - i)); + i, + false, + 0)); } if (devinfo) diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index afa70a6cd..f7d7eeb7d 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -63,7 +63,9 @@ PluginInterface::SamplingDevices FileSinkPlugin::enumSampleSinks() m_hardwareID, m_deviceTypeID, QString::null, - i)); + i, + false, + 0)); } return result; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index b2325ab69..2e5a789cb 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -97,7 +97,9 @@ PluginInterface::SamplingDevices HackRFOutputPlugin::enumSampleSinks() m_hardwareID, m_deviceTypeID, serial_str, - i)); + i, + false, + 0)); qDebug("HackRFOutputPlugin::enumSampleSinks: enumerated HackRF device #%d", i); diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index e06f0f032..19365da88 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -24,6 +24,7 @@ #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "device/devicesinkapi.h" +#include "limesdr/devicelimesdrparam.h" #include "limesdroutputgui.h" #include "limesdroutputplugin.h" @@ -82,13 +83,22 @@ PluginInterface::SamplingDevices LimeSDROutputPlugin::enumSampleSinks() std::string serial("N/D"); findSerial((const char *) deviceList[i], serial); - qDebug("LimeSDROutputPlugin::enumSampleSources: device #%d: %s", i, (char *) deviceList[i]); - QString displayedName(QString("LimeSDR[%1] %2").arg(i).arg(serial.c_str())); - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString(deviceList[i]), - i)); + DeviceLimeSDRParams limeSDRParams; + limeSDRParams.open(deviceList[i]); + limeSDRParams.close(); + + for (unsigned int j = 0; j < limeSDRParams.m_nbTxChannels; j++) + { + qDebug("LimeSDROutputPlugin::enumSampleSources: device #%d channel %u: %s", i, j, (char *) deviceList[i]); + QString displayedName(QString("LimeSDR[%1:%2] %3").arg(i).arg(j).arg(serial.c_str())); + result.append(SamplingDevice(displayedName, + m_hardwareID, + m_deviceTypeID, + QString(deviceList[i]), + i, + false, + j)); + } } } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index d360745f7..38ed57e88 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -73,7 +73,9 @@ PluginInterface::SamplingDevices PlutoSDROutputPlugin::enumSampleSinks() m_hardwareID, m_deviceTypeID, serial_str, - i)); + i, + false, + 0)); qDebug("PlutoSDROutputPlugin::enumSampleSources: enumerated PlutoSDR device #%d", i); } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index bd206e01f..1727ab44e 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -64,7 +64,9 @@ PluginInterface::SamplingDevices SDRdaemonSinkPlugin::enumSampleSinks() m_hardwareID, m_deviceTypeID, QString::null, - i)); + i, + false, + 0)); } return result; diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index 50ab53496..563159d03 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -99,7 +99,9 @@ PluginInterface::SamplingDevices AirspyPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, serial_str, - i)); + i, + true, + 0)); qDebug("AirspyPlugin::enumSampleSources: enumerated Airspy device #%d", i); } diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp index a39bbe668..609939f78 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp @@ -67,7 +67,9 @@ PluginInterface::SamplingDevices BlderfInputPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, QString(devinfo[i].serial), - i)); + i, + true, + 0)); } if (devinfo) diff --git a/plugins/samplesource/fcdpro/fcdproplugin.cpp b/plugins/samplesource/fcdpro/fcdproplugin.cpp index f2a52049b..8b4437d2a 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.cpp +++ b/plugins/samplesource/fcdpro/fcdproplugin.cpp @@ -65,7 +65,9 @@ PluginInterface::SamplingDevices FCDProPlugin::enumSampleSources() fcd_traits::hardwareID, fcd_traits::interfaceIID, serialNumber, - i)); + i, + true, + 0)); device_info = device_info->next; i++; diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp index 4f3a075ef..d37a0c4ae 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp @@ -67,7 +67,9 @@ PluginInterface::SamplingDevices FCDProPlusPlugin::enumSampleSources() fcd_traits::hardwareID, fcd_traits::interfaceIID, serialNumber, - i)); + i, + true, + 0)); device_info = device_info->next; i++; diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index 21c871f66..ea5a4d4bd 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -63,7 +63,9 @@ PluginInterface::SamplingDevices FileSourcePlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, QString::null, - i)); + i, + true, + 0)); } return result; diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 1a8cca807..84f497484 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -98,7 +98,9 @@ PluginInterface::SamplingDevices HackRFInputPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, serial_str, - i)); + i, + true, + 0)); qDebug("HackRFPlugin::enumSampleSources: enumerated HackRF device #%d", i); diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index 130a6cb88..5d6299a92 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -83,13 +83,22 @@ PluginInterface::SamplingDevices LimeSDRInputPlugin::enumSampleSources() std::string serial("N/D"); findSerial((const char *) deviceList[i], serial); - qDebug("LimeSDRInputPlugin::enumSampleSources: device #%d: %s", i, (char *) deviceList[i]); - QString displayedName(QString("LimeSDR[%1] %2").arg(i).arg(serial.c_str())); - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString(deviceList[i]), - i)); + DeviceLimeSDRParams limeSDRParams; + limeSDRParams.open(deviceList[i]); + limeSDRParams.close(); + + for (unsigned int j = 0; j < limeSDRParams.m_nbRxChannels; j++) + { + qDebug("LimeSDRInputPlugin::enumSampleSources: device #%d channel %u: %s", i, j, (char *) deviceList[i]); + QString displayedName(QString("LimeSDR[%1:%2] %3").arg(i).arg(j).arg(serial.c_str())); + result.append(SamplingDevice(displayedName, + m_hardwareID, + m_deviceTypeID, + QString(deviceList[i]), + i, + true, + j)); + } } } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index 5441fdc2f..8533dab38 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -73,7 +73,9 @@ PluginInterface::SamplingDevices PlutoSDRInputPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, serial_str, - i)); + i, + true, + 0)); qDebug("PlutoSDRInputPlugin::enumSampleSources: enumerated PlutoSDR device #%d", i); } diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index aefe207f9..a1b125f9e 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -57,7 +57,9 @@ PluginInterface::SamplingDevices RTLSDRPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, QString(serial), - i)); + i, + true, + 0)); } return result; } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index abecda134..e579bb852 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -64,7 +64,9 @@ PluginInterface::SamplingDevices SDRdaemonSourcePlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, QString::null, - i)); + i, + true, + 0)); } return result; diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index cda7c23d1..49b59f36b 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -77,7 +77,9 @@ PluginInterface::SamplingDevices SDRPlayPlugin::enumSampleSources() m_hardwareID, m_deviceTypeID, QString(serial), - i)); + i, + true, + 0)); } return result; diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h index 666f2e2df..fecb4aacd 100644 --- a/sdrbase/plugin/plugininterface.h +++ b/sdrbase/plugin/plugininterface.h @@ -27,22 +27,30 @@ class PluginInterface { public: struct SamplingDevice { - QString displayedName; - QString hardwareId; - QString id; - QString serial; - int sequence; + QString displayedName; //!< The human readable name + QString hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) + QString id; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString serial; //!< The device serial number + int sequence; //!< The device sequence. >0 when more than one device of the same type is connected + bool rxElseTx; //!< This is the Rx part else the Tx part of the device + int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized + int claimed; //!< This is the device set index if claimed else -1 SamplingDevice(const QString& _displayedName, const QString& _hardwareId, const QString& _id, const QString& _serial, - int _sequence) : + int _sequence, + bool _rxElseTx, + int _deviceItemIndex) : displayedName(_displayedName), hardwareId(_hardwareId), id(_id), serial(_serial), - sequence(_sequence) + sequence(_sequence), + rxElseTx(_rxElseTx), + deviceItemIndex(_deviceItemIndex), + claimed(-1) { } }; typedef QList SamplingDevices; From 462eb90f66bb36fe08fcea1e1fdb69773571f0de Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 13:03:12 +0100 Subject: [PATCH 21/39] Methods to determine if a device is buit in or physical --- sdrbase/CMakeLists.txt | 2 ++ sdrbase/sdrbase.pro | 4 +++- sdrgui/device/deviceuiset.cpp | 4 ++-- sdrgui/plugin/pluginapi.cpp | 4 ++++ sdrgui/plugin/pluginapi.h | 9 ++++++--- sdrgui/plugin/pluginmanager.cpp | 25 +++++++++++++++++++------ sdrgui/plugin/pluginmanager.h | 18 ++++++++++++------ 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 3641e6418..4bc2edf0a 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -45,6 +45,7 @@ set(sdrbase_SOURCES device/devicesourceapi.cpp device/devicesinkapi.cpp +# device/deviceenumerator.cpp settings/preferences.cpp settings/preset.cpp @@ -124,6 +125,7 @@ set(sdrbase_HEADERS device/devicesourceapi.h device/devicesinkapi.h +# device/deviceenumerator.h plugin/plugininstancegui.h plugin/plugininterface.h diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index c03ca0c14..8be9677ba 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -50,7 +50,8 @@ SOURCES += audio/audiodeviceinfo.cpp\ audio/audiooutput.cpp\ audio/audioinput.cpp\ device/devicesourceapi.cpp\ - device/devicesinkapi.cpp\ + device/devicesinkapi.cpp\ + device/deviceenumerator.cpp\ dsp/afsquelch.cpp\ dsp/agc.cpp\ dsp/downchannelizer.cpp\ @@ -107,6 +108,7 @@ HEADERS += audio/audiodeviceinfo.h\ audio/audioinput.h\ device/devicesourceapi.h\ device/devicesinkapi.h\ + device/deviceenumerator.h\ dsp/afsquelch.h\ dsp/downchannelizer.h\ dsp/upchannelizer.h\ diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index b09659d1c..b3534dd63 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -172,7 +172,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA { for(int i = 0; i < channelRegistrations->count(); i++) { - if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) + if((*channelRegistrations)[i].m_channelId == channelConfig.m_channel) { qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); reg = ChannelInstanceRegistration( @@ -270,7 +270,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA { for(int i = 0; i < channelRegistrations->count(); i++) { - if((*channelRegistrations)[i].m_channelName == channelConfig.m_channel) + if((*channelRegistrations)[i].m_channelId == channelConfig.m_channel) { qDebug("DeviceUISet::loadChannelSettings: creating new channel [%s]", qPrintable(channelConfig.m_channel)); reg = ChannelInstanceRegistration(channelConfig.m_channel, (*channelRegistrations)[i].m_plugin->createTxChannel(channelConfig.m_channel, this)); diff --git a/sdrgui/plugin/pluginapi.cpp b/sdrgui/plugin/pluginapi.cpp index 6f8d32c41..eb19aa64b 100644 --- a/sdrgui/plugin/pluginapi.cpp +++ b/sdrgui/plugin/pluginapi.cpp @@ -31,6 +31,10 @@ PluginAPI::ChannelRegistrations *PluginAPI::getTxChannelRegistrations() return m_pluginManager->getTxChannelRegistrations(); } +bool PluginAPI::isBuiltInDevice(QString& deviceTypeID) +{ + return m_pluginManager->isBuiltInDevice(deviceTypeID); +} PluginAPI::PluginAPI(PluginManager* pluginManager) : m_pluginManager(pluginManager) diff --git a/sdrgui/plugin/pluginapi.h b/sdrgui/plugin/pluginapi.h index 76af7e80f..6e559e8bf 100644 --- a/sdrgui/plugin/pluginapi.h +++ b/sdrgui/plugin/pluginapi.h @@ -19,10 +19,10 @@ class SDRANGEL_API PluginAPI : public QObject { public: struct ChannelRegistration { - QString m_channelName; + QString m_channelId; //!< Channel or device type ID PluginInterface* m_plugin; - ChannelRegistration(const QString& channelName, PluginInterface* plugin) : - m_channelName(channelName), + ChannelRegistration(const QString& channelId, PluginInterface* plugin) : + m_channelId(channelId), m_plugin(plugin) { } }; @@ -43,6 +43,9 @@ public: // Sample Sink stuff void registerSampleSink(const QString& sinkName, PluginInterface* plugin); + // Categories enquiry + bool isBuiltInDevice(QString& deviceTypeID); + protected: PluginManager* m_pluginManager; diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index cae4c42af..0f747cfc9 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -31,10 +31,14 @@ #include "plugin/pluginmanager.h" -const QString PluginManager::m_sdrDaemonHardwareID = "SDRdaemonSource"; -const QString PluginManager::m_sdrDaemonDeviceTypeID = "sdrangel.samplesource.sdrdaemonsource"; +const QString PluginManager::m_sdrDaemonSourceHardwareID = "SDRdaemonSource"; +const QString PluginManager::m_sdrDaemonSourceDeviceTypeID = "sdrangel.samplesource.sdrdaemonsource"; const QString PluginManager::m_fileSourceHardwareID = "FileSource"; const QString PluginManager::m_fileSourceDeviceTypeID = "sdrangel.samplesource.filesource"; + +const QString PluginManager::m_sdrDaemonSinkHardwareID = "SDRdaemonSink"; +const QString PluginManager::m_sdrDaemonSinkDeviceTypeID = "sdrangel.samplesink.sdrdaemonsink"; +const QString PluginManager::m_fileSinkHardwareID = "FileSink"; const QString PluginManager::m_fileSinkDeviceTypeID = "sdrangel.samplesink.filesink"; PluginManager::PluginManager(QObject* parent) : @@ -172,7 +176,7 @@ void PluginManager::duplicateLocalSampleSourceDevices(uint deviceUID) for(int i = 0; i < m_sampleSourceDevices.count(); ++i) { - if (m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonDeviceTypeID) // SDRdaemon + if (m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) // SDRdaemon { if (m_sampleSourceDevices[i].m_deviceSequence == 0) { // reference to device 0 sdrDaemonSSD0 = &m_sampleSourceDevices[i]; @@ -265,7 +269,7 @@ void PluginManager::fillSampleSourceSelector(QComboBox* comboBox, uint deviceUID for(int i = 0; i < m_sampleSourceDevices.count(); i++) { // For "local" devices show only ones that concern this device set - if ((m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonDeviceTypeID) + if ((m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) || (m_sampleSourceDevices[i].m_deviceId == m_fileSourceDeviceTypeID)) { if (deviceUID != m_sampleSourceDevices[i].m_deviceSequence) { @@ -627,7 +631,7 @@ void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet if (channelPluginIndex < m_rxChannelRegistrations.size()) { PluginInterface *pluginInterface = m_rxChannelRegistrations[channelPluginIndex].m_plugin; - pluginInterface->createRxChannel(m_rxChannelRegistrations[channelPluginIndex].m_channelName, deviceUISet); + pluginInterface->createRxChannel(m_rxChannelRegistrations[channelPluginIndex].m_channelId, deviceUISet); } } @@ -636,6 +640,15 @@ void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceUISet if (channelPluginIndex < m_txChannelRegistrations.size()) { PluginInterface *pluginInterface = m_txChannelRegistrations[channelPluginIndex].m_plugin; - pluginInterface->createTxChannel(m_txChannelRegistrations[channelPluginIndex].m_channelName, deviceUISet); + pluginInterface->createTxChannel(m_txChannelRegistrations[channelPluginIndex].m_channelId, deviceUISet); } } + +bool PluginManager::isBuiltInDevice(QString& deviceTypeID) +{ + return ((deviceTypeID == m_fileSourceDeviceTypeID) || + (deviceTypeID == m_fileSinkDeviceTypeID) || + (deviceTypeID == m_sdrDaemonSourceDeviceTypeID) || + (deviceTypeID == m_sdrDaemonSinkDeviceTypeID)); +} + diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index 8516ef317..dae139fce 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -79,8 +79,11 @@ public: void createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); void listTxChannels(QList& list); + bool isBuiltInDevice(QString& deviceTypeID); + private: - struct SamplingDeviceRegistration { + struct SamplingDeviceRegistration //!< This is the channel registration + { QString m_deviceId; PluginInterface* m_plugin; SamplingDeviceRegistration(const QString& deviceId, PluginInterface* plugin) : @@ -91,7 +94,7 @@ private: typedef QList SamplingDeviceRegistrations; - struct SamplingDevice { + struct SamplingDevice { //!< This is the device registration PluginInterface* m_plugin; QString m_displayName; QString m_hadrwareId; @@ -128,12 +131,15 @@ private: SamplingDevices m_sampleSinkDevices; //!< Instances of output sinks present in the system // "Local" sample source device IDs - static const QString m_sdrDaemonHardwareID; //!< SDRdaemon hardware ID - static const QString m_sdrDaemonDeviceTypeID; //!< SDRdaemon source plugin ID - static const QString m_fileSourceHardwareID; //!< FileSource source hardware ID - static const QString m_fileSourceDeviceTypeID; //!< FileSource source plugin ID + static const QString m_sdrDaemonSourceHardwareID; //!< SDRdaemon source hardware ID + static const QString m_sdrDaemonSourceDeviceTypeID; //!< SDRdaemon source plugin ID + static const QString m_fileSourceHardwareID; //!< FileSource source hardware ID + static const QString m_fileSourceDeviceTypeID; //!< FileSource source plugin ID // "Local" sample sink device IDs + static const QString m_sdrDaemonSinkHardwareID; //!< SDRdaemon source hardware ID + static const QString m_sdrDaemonSinkDeviceTypeID; //!< SDRdaemon source plugin ID + static const QString m_fileSinkHardwareID; //!< FileSource source hardware ID static const QString m_fileSinkDeviceTypeID; //!< FileSink sink plugin ID void loadPlugins(const QDir& dir); From 7370eba0f71cc624825437c52088cb391a1dd2e1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 14:58:36 +0100 Subject: [PATCH 22/39] Bumped version to v3.8.0 --- debian/changelog | 3 ++- fcdlib/fcdtraits.cpp | 4 ++-- plugins/channelrx/chanalyzer/chanalyzerplugin.cpp | 2 +- plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp | 2 +- plugins/channelrx/demodam/amdemodplugin.cpp | 2 +- plugins/channelrx/demodatv/atvdemodplugin.cpp | 2 +- plugins/channelrx/demodbfm/bfmplugin.cpp | 2 +- plugins/channelrx/demoddsd/dsddemodplugin.cpp | 2 +- plugins/channelrx/demodlora/loraplugin.cpp | 2 +- plugins/channelrx/demodnfm/nfmplugin.cpp | 2 +- plugins/channelrx/demodssb/ssbplugin.cpp | 2 +- plugins/channelrx/demodwfm/wfmplugin.cpp | 2 +- plugins/channelrx/tcpsrc/tcpsrcplugin.cpp | 2 +- plugins/channelrx/udpsrc/udpsrcplugin.cpp | 2 +- plugins/channeltx/modam/ammodplugin.cpp | 2 +- plugins/channeltx/modatv/atvmodplugin.cpp | 2 +- plugins/channeltx/modnfm/nfmmodplugin.cpp | 2 +- plugins/channeltx/modssb/ssbmodplugin.cpp | 2 +- plugins/channeltx/modwfm/wfmmodplugin.cpp | 2 +- plugins/channeltx/udpsink/udpsinkplugin.cpp | 2 +- plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp | 2 +- plugins/samplesink/filesink/filesinkplugin.cpp | 2 +- plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp | 2 +- plugins/samplesink/limesdroutput/limesdroutputplugin.cpp | 2 +- plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp | 2 +- plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp | 2 +- plugins/samplesource/airspy/airspyplugin.cpp | 2 +- plugins/samplesource/bladerfinput/bladerfinputplugin.cpp | 2 +- plugins/samplesource/filesource/filesourceplugin.cpp | 2 +- plugins/samplesource/hackrfinput/hackrfinputplugin.cpp | 2 +- plugins/samplesource/limesdrinput/limesdrinputplugin.cpp | 2 +- plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp | 2 +- plugins/samplesource/rtlsdr/rtlsdrplugin.cpp | 2 +- .../samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp | 2 +- plugins/samplesource/sdrplay/sdrplayplugin.cpp | 2 +- sdrgui/gui/aboutdialog.ui | 2 +- sdrgui/mainwindow.cpp | 4 ++-- 37 files changed, 40 insertions(+), 39 deletions(-) diff --git a/debian/changelog b/debian/changelog index 68012d85f..10851c733 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ -sdrangel (3.7.9-1) unstable; urgency=medium +sdrangel (3.8.0-1) unstable; urgency=medium + * Redesigned the device handling * Refactoring: Separate DeviceAPI from the GUI -- Edouard Griffiths, F4EXB Sun, 05 Nov 2017 18:14:18 +0200 diff --git a/fcdlib/fcdtraits.cpp b/fcdlib/fcdtraits.cpp index 99f97baed..ca2d0cd70 100644 --- a/fcdlib/fcdtraits.cpp +++ b/fcdlib/fcdtraits.cpp @@ -22,8 +22,8 @@ const char *fcd_traits::displayedName = "FunCube Dongle Pro+"; const char *fcd_traits::pluginDisplayedName = "FunCube Pro Input"; const char *fcd_traits::pluginDisplayedName = "FunCube Pro+ Input"; -const char *fcd_traits::pluginVersion = "3.7.4"; -const char *fcd_traits::pluginVersion = "3.7.4"; +const char *fcd_traits::pluginVersion = "3.8.0"; +const char *fcd_traits::pluginVersion = "3.8.0"; const int64_t fcd_traits::loLowLimitFreq = 64000000L; const int64_t fcd_traits::loLowLimitFreq = 150000L; diff --git a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp index e4b6cdc44..d3d8fb772 100644 --- a/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzerplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor ChannelAnalyzerPlugin::m_pluginDescriptor = { QString("Channel Analyzer"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp index 80514d0a4..0f7367cdc 100644 --- a/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp +++ b/plugins/channelrx/chanalyzerng/chanalyzerngplugin.cpp @@ -22,7 +22,7 @@ const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = { QString("Channel Analyzer NG"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodam/amdemodplugin.cpp b/plugins/channelrx/demodam/amdemodplugin.cpp index ec7344408..11a0166b4 100644 --- a/plugins/channelrx/demodam/amdemodplugin.cpp +++ b/plugins/channelrx/demodam/amdemodplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = { QString("AM Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodatv/atvdemodplugin.cpp b/plugins/channelrx/demodatv/atvdemodplugin.cpp index b560e2f4b..a8d33335b 100644 --- a/plugins/channelrx/demodatv/atvdemodplugin.cpp +++ b/plugins/channelrx/demodatv/atvdemodplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor ATVDemodPlugin::m_ptrPluginDescriptor = { QString("ATV Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) F4HKW for F4EXB / SDRAngel"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodbfm/bfmplugin.cpp b/plugins/channelrx/demodbfm/bfmplugin.cpp index 096abba0e..6b860a99f 100644 --- a/plugins/channelrx/demodbfm/bfmplugin.cpp +++ b/plugins/channelrx/demodbfm/bfmplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor BFMPlugin::m_pluginDescriptor = { QString("Broadcast FM Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demoddsd/dsddemodplugin.cpp b/plugins/channelrx/demoddsd/dsddemodplugin.cpp index f310e643c..1f27653ae 100644 --- a/plugins/channelrx/demoddsd/dsddemodplugin.cpp +++ b/plugins/channelrx/demoddsd/dsddemodplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor DSDDemodPlugin::m_pluginDescriptor = { QString("DSD Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodlora/loraplugin.cpp b/plugins/channelrx/demodlora/loraplugin.cpp index 18ca4c459..67327edd3 100644 --- a/plugins/channelrx/demodlora/loraplugin.cpp +++ b/plugins/channelrx/demodlora/loraplugin.cpp @@ -6,7 +6,7 @@ const PluginDescriptor LoRaPlugin::m_pluginDescriptor = { QString("LoRa Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) 2015 John Greb"), QString("http://www.maintech.de"), true, diff --git a/plugins/channelrx/demodnfm/nfmplugin.cpp b/plugins/channelrx/demodnfm/nfmplugin.cpp index ff9398349..4b2369ec6 100644 --- a/plugins/channelrx/demodnfm/nfmplugin.cpp +++ b/plugins/channelrx/demodnfm/nfmplugin.cpp @@ -6,7 +6,7 @@ const PluginDescriptor NFMPlugin::m_pluginDescriptor = { QString("NFM Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index d8fa5fdc5..a5f668015 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor SSBPlugin::m_pluginDescriptor = { QString("SSB Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodwfm/wfmplugin.cpp b/plugins/channelrx/demodwfm/wfmplugin.cpp index 2c887fd91..0381cf013 100644 --- a/plugins/channelrx/demodwfm/wfmplugin.cpp +++ b/plugins/channelrx/demodwfm/wfmplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor WFMPlugin::m_pluginDescriptor = { QString("WFM Demodulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp b/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp index 3835f6692..8e0ba9f34 100644 --- a/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp +++ b/plugins/channelrx/tcpsrc/tcpsrcplugin.cpp @@ -7,7 +7,7 @@ const PluginDescriptor TCPSrcPlugin::m_pluginDescriptor = { QString("TCP Channel Source"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/udpsrc/udpsrcplugin.cpp b/plugins/channelrx/udpsrc/udpsrcplugin.cpp index 0c739a751..4dd5818d7 100644 --- a/plugins/channelrx/udpsrc/udpsrcplugin.cpp +++ b/plugins/channelrx/udpsrc/udpsrcplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor UDPSrcPlugin::m_pluginDescriptor = { QString("UDP Channel Source"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modam/ammodplugin.cpp b/plugins/channeltx/modam/ammodplugin.cpp index 785a8b3ad..616ecf826 100644 --- a/plugins/channeltx/modam/ammodplugin.cpp +++ b/plugins/channeltx/modam/ammodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor AMModPlugin::m_pluginDescriptor = { QString("AM Modulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modatv/atvmodplugin.cpp b/plugins/channeltx/modatv/atvmodplugin.cpp index 31ac039bc..107948411 100644 --- a/plugins/channeltx/modatv/atvmodplugin.cpp +++ b/plugins/channeltx/modatv/atvmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor ATVModPlugin::m_pluginDescriptor = { QString("ATV Modulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modnfm/nfmmodplugin.cpp b/plugins/channeltx/modnfm/nfmmodplugin.cpp index cd2c64ce3..4fe27d9af 100644 --- a/plugins/channeltx/modnfm/nfmmodplugin.cpp +++ b/plugins/channeltx/modnfm/nfmmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor NFMModPlugin::m_pluginDescriptor = { QString("NFM Modulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modssb/ssbmodplugin.cpp b/plugins/channeltx/modssb/ssbmodplugin.cpp index 08f4b734f..b0328b32c 100644 --- a/plugins/channeltx/modssb/ssbmodplugin.cpp +++ b/plugins/channeltx/modssb/ssbmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor SSBModPlugin::m_pluginDescriptor = { QString("SSB Modulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/modwfm/wfmmodplugin.cpp b/plugins/channeltx/modwfm/wfmmodplugin.cpp index 13881d30a..32f01351a 100644 --- a/plugins/channeltx/modwfm/wfmmodplugin.cpp +++ b/plugins/channeltx/modwfm/wfmmodplugin.cpp @@ -23,7 +23,7 @@ const PluginDescriptor WFMModPlugin::m_pluginDescriptor = { QString("WFM Modulator"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channeltx/udpsink/udpsinkplugin.cpp b/plugins/channeltx/udpsink/udpsinkplugin.cpp index 91c331850..818ea1bdf 100644 --- a/plugins/channeltx/udpsink/udpsinkplugin.cpp +++ b/plugins/channeltx/udpsink/udpsinkplugin.cpp @@ -24,7 +24,7 @@ const PluginDescriptor UDPSinkPlugin::m_pluginDescriptor = { QString("UDP Channel Sink"), - QString("3.7.9"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp index 5c8faf1eb..694c126e2 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor BladerfOutputPlugin::m_pluginDescriptor = { QString("BladerRF Output"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index f7d7eeb7d..52ae88945 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor FileSinkPlugin::m_pluginDescriptor = { QString("File sink output"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index 2e5a789cb..7ef95e4e1 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor HackRFOutputPlugin::m_pluginDescriptor = { QString("HackRF Output"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index 19365da88..e74d60a4e 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -31,7 +31,7 @@ const PluginDescriptor LimeSDROutputPlugin::m_pluginDescriptor = { QString("LimeSDR Output"), - QString("3.7.8"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 38ed57e88..684c84ea5 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -28,7 +28,7 @@ class DeviceSourceAPI; const PluginDescriptor PlutoSDROutputPlugin::m_pluginDescriptor = { QString("PlutoSDR Output"), - QString("3.7.8"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index 1727ab44e..29a817207 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor SDRdaemonSinkPlugin::m_pluginDescriptor = { QString("SDRdaemon sink output"), - QString("3.5.0"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index 563159d03..1dde76696 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor AirspyPlugin::m_pluginDescriptor = { QString("Airspy Input"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp index 609939f78..fa1834fee 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor BlderfInputPlugin::m_pluginDescriptor = { QString("BladerRF Input"), - QString("3.5.0"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index ea5a4d4bd..6959dc4b3 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor FileSourcePlugin::m_pluginDescriptor = { QString("File source input"), - QString("3.5.0"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 84f497484..724f98e53 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -29,7 +29,7 @@ const PluginDescriptor HackRFInputPlugin::m_pluginDescriptor = { QString("HackRF Input"), - QString("3.5.2"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index 5d6299a92..ece50b9ad 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -31,7 +31,7 @@ const PluginDescriptor LimeSDRInputPlugin::m_pluginDescriptor = { QString("LimeSDR Input"), - QString("3.7.8"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index 8533dab38..fa8d18ef3 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -28,7 +28,7 @@ class DeviceSourceAPI; const PluginDescriptor PlutoSDRInputPlugin::m_pluginDescriptor = { QString("PlutoSDR Input"), - QString("3.7.8"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index a1b125f9e..96bfcd82e 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -11,7 +11,7 @@ const PluginDescriptor RTLSDRPlugin::m_pluginDescriptor = { QString("RTL-SDR Input"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index e579bb852..e521eef1f 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -26,7 +26,7 @@ const PluginDescriptor SDRdaemonSourcePlugin::m_pluginDescriptor = { QString("SDRdaemon source input"), - QString("3.5.0"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index 49b59f36b..d5a1b145a 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -25,7 +25,7 @@ const PluginDescriptor SDRPlayPlugin::m_pluginDescriptor = { QString("SDRPlay RSP1 Input"), - QString("3.7.4"), + QString("3.8.0"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/sdrgui/gui/aboutdialog.ui b/sdrgui/gui/aboutdialog.ui index 8b7cff58b..8a0775dd3 100644 --- a/sdrgui/gui/aboutdialog.ui +++ b/sdrgui/gui/aboutdialog.ui @@ -84,7 +84,7 @@ - <html><head/><body><p>Version 3.7.9 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html> + <html><head/><body><p>Version 3.8.0 - Copyright (C) 2015-2017 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">RTL-SDRangelove</span></a></p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html> true diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 99fd18e97..e986ef629 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -532,9 +532,9 @@ void MainWindow::createStatusBar() { QString qtVersionStr = QString("Qt %1 ").arg(QT_VERSION_STR); #if QT_VERSION >= 0x050400 - m_showSystemWidget = new QLabel("SDRangel v3.7.9 " + qtVersionStr + QSysInfo::prettyProductName(), this); + m_showSystemWidget = new QLabel("SDRangel v3.8.0 " + qtVersionStr + QSysInfo::prettyProductName(), this); #else - m_showSystemWidget = new QLabel("SDRangel v3.7.9 " + qtVersionStr, this); + m_showSystemWidget = new QLabel("SDRangel v3.8.0 " + qtVersionStr, this); #endif statusBar()->addPermanentWidget(m_showSystemWidget); From aea8de22a123b2b2d1367bde44697e456af43dc4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 18:45:03 +0100 Subject: [PATCH 23/39] Removed useless reference to device source and sink APIs in the sampling device control widget --- sdrgui/gui/samplingdevicecontrol.cpp | 4 +--- sdrgui/gui/samplingdevicecontrol.h | 4 ---- sdrgui/mainwindow.cpp | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/sdrgui/gui/samplingdevicecontrol.cpp b/sdrgui/gui/samplingdevicecontrol.cpp index 8fd29a3b8..7d061e964 100644 --- a/sdrgui/gui/samplingdevicecontrol.cpp +++ b/sdrgui/gui/samplingdevicecontrol.cpp @@ -23,9 +23,7 @@ SamplingDeviceControl::SamplingDeviceControl(QWidget* parent) : QWidget(parent), ui(new Ui::SamplingDeviceControl), - m_pluginManager(0), - m_deviceSourceAPI(0), - m_deviceSinkAPI(0) + m_pluginManager(0) { ui->setupUi(this); } diff --git a/sdrgui/gui/samplingdevicecontrol.h b/sdrgui/gui/samplingdevicecontrol.h index dd5b6b4f9..3773f5e30 100644 --- a/sdrgui/gui/samplingdevicecontrol.h +++ b/sdrgui/gui/samplingdevicecontrol.h @@ -41,8 +41,6 @@ public: ~SamplingDeviceControl(); void setPluginManager(PluginManager *pluginManager) { m_pluginManager = pluginManager; } - void setDeviceAPI(DeviceSourceAPI *deviceAPI) { m_deviceSourceAPI = deviceAPI; } - void setDeviceAPI(DeviceSinkAPI *deviceAPI) { m_deviceSinkAPI = deviceAPI; } QComboBox *getDeviceSelector(); QPushButton *getDeviceSelectionConfirm(); QComboBox *getChannelSelector(); @@ -51,8 +49,6 @@ public: private: Ui::SamplingDeviceControl* ui; PluginManager *m_pluginManager; - DeviceSourceAPI *m_deviceSourceAPI; - DeviceSinkAPI *m_deviceSinkAPI; }; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index e986ef629..b46624d3f 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -214,7 +214,6 @@ void MainWindow::addSourceDevice() DeviceSourceAPI *deviceSourceAPI = new DeviceSourceAPI(deviceTabIndex, dspDeviceSourceEngine); m_deviceUIs.back()->m_deviceSourceAPI = deviceSourceAPI; - m_deviceUIs.back()->m_samplingDeviceControl->setDeviceAPI(deviceSourceAPI); m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); QList channelNames; m_pluginManager->listRxChannels(channelNames); @@ -280,7 +279,6 @@ void MainWindow::addSinkDevice() m_deviceUIs.back()->m_deviceSourceAPI = 0; m_deviceUIs.back()->m_deviceSinkAPI = deviceSinkAPI; - m_deviceUIs.back()->m_samplingDeviceControl->setDeviceAPI(deviceSinkAPI); m_deviceUIs.back()->m_samplingDeviceControl->setPluginManager(m_pluginManager); QList channelNames; m_pluginManager->listTxChannels(channelNames); From 9a055c643b5a0b17c771d2ff94b48f2be6e81087 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 20:06:33 +0100 Subject: [PATCH 24/39] Store device category (type) in the SamplingDevice structure --- .../bladerfoutput/bladerfoutputplugin.cpp | 1 + .../samplesink/filesink/filesinkplugin.cpp | 1 + .../hackrfoutput/hackrfoutputplugin.cpp | 1 + .../limesdroutput/limesdroutputplugin.cpp | 1 + .../plutosdroutput/plutosdroutputplugin.cpp | 1 + .../sdrdaemonsink/sdrdaemonsinkplugin.cpp | 1 + plugins/samplesource/airspy/airspyplugin.cpp | 1 + .../bladerfinput/bladerfinputplugin.cpp | 1 + plugins/samplesource/fcdpro/fcdproplugin.cpp | 1 + .../fcdproplus/fcdproplusplugin.cpp | 1 + .../filesource/filesourceplugin.cpp | 1 + .../hackrfinput/hackrfinputplugin.cpp | 1 + .../limesdrinput/limesdrinputplugin.cpp | 1 + .../plutosdrinput/plutosdrinputplugin.cpp | 1 + plugins/samplesource/rtlsdr/rtlsdrplugin.cpp | 1 + .../sdrdaemonsource/sdrdaemonsourceplugin.cpp | 1 + .../samplesource/sdrplay/sdrplayplugin.cpp | 1 + sdrbase/plugin/plugininterface.h | 25 +++++++++++++------ sdrgui/device/deviceuiset.cpp | 5 ++-- sdrgui/device/deviceuiset.h | 3 ++- sdrgui/gui/samplingdevicecontrol.cpp | 5 ++-- sdrgui/gui/samplingdevicecontrol.h | 3 ++- sdrgui/mainwindow.cpp | 8 +++--- sdrgui/plugin/pluginapi.cpp | 5 ---- sdrgui/plugin/pluginapi.h | 5 +--- sdrgui/plugin/pluginmanager.cpp | 9 ------- sdrgui/plugin/pluginmanager.h | 2 -- 27 files changed, 49 insertions(+), 38 deletions(-) diff --git a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp index 694c126e2..e37dc1108 100644 --- a/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp +++ b/plugins/samplesink/bladerfoutput/bladerfoutputplugin.cpp @@ -67,6 +67,7 @@ PluginInterface::SamplingDevices BladerfOutputPlugin::enumSampleSinks() m_deviceTypeID, QString(devinfo[i].serial), i, + PluginInterface::SamplingDevice::PhysicalDevice, false, 0)); } diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index 52ae88945..34cc827d5 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -64,6 +64,7 @@ PluginInterface::SamplingDevices FileSinkPlugin::enumSampleSinks() m_deviceTypeID, QString::null, i, + PluginInterface::SamplingDevice::BuiltInDevice, false, 0)); } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp index 7ef95e4e1..98a9e4481 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputplugin.cpp @@ -98,6 +98,7 @@ PluginInterface::SamplingDevices HackRFOutputPlugin::enumSampleSinks() m_deviceTypeID, serial_str, i, + PluginInterface::SamplingDevice::PhysicalDevice, false, 0)); diff --git a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp index e74d60a4e..766463a73 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputplugin.cpp @@ -96,6 +96,7 @@ PluginInterface::SamplingDevices LimeSDROutputPlugin::enumSampleSinks() m_deviceTypeID, QString(deviceList[i]), i, + PluginInterface::SamplingDevice::PhysicalDevice, false, j)); } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp index 684c84ea5..776472ca0 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputplugin.cpp @@ -74,6 +74,7 @@ PluginInterface::SamplingDevices PlutoSDROutputPlugin::enumSampleSinks() m_deviceTypeID, serial_str, i, + PluginInterface::SamplingDevice::PhysicalDevice, false, 0)); diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index 29a817207..d0b2d2b8b 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -65,6 +65,7 @@ PluginInterface::SamplingDevices SDRdaemonSinkPlugin::enumSampleSinks() m_deviceTypeID, QString::null, i, + PluginInterface::SamplingDevice::BuiltInDevice, false, 0)); } diff --git a/plugins/samplesource/airspy/airspyplugin.cpp b/plugins/samplesource/airspy/airspyplugin.cpp index 1dde76696..eccc2de54 100644 --- a/plugins/samplesource/airspy/airspyplugin.cpp +++ b/plugins/samplesource/airspy/airspyplugin.cpp @@ -100,6 +100,7 @@ PluginInterface::SamplingDevices AirspyPlugin::enumSampleSources() m_deviceTypeID, serial_str, i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); diff --git a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp index fa1834fee..0a5420174 100644 --- a/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp +++ b/plugins/samplesource/bladerfinput/bladerfinputplugin.cpp @@ -68,6 +68,7 @@ PluginInterface::SamplingDevices BlderfInputPlugin::enumSampleSources() m_deviceTypeID, QString(devinfo[i].serial), i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); } diff --git a/plugins/samplesource/fcdpro/fcdproplugin.cpp b/plugins/samplesource/fcdpro/fcdproplugin.cpp index 8b4437d2a..a813bdcb7 100644 --- a/plugins/samplesource/fcdpro/fcdproplugin.cpp +++ b/plugins/samplesource/fcdpro/fcdproplugin.cpp @@ -66,6 +66,7 @@ PluginInterface::SamplingDevices FCDProPlugin::enumSampleSources() fcd_traits::interfaceIID, serialNumber, i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); diff --git a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp index d37a0c4ae..4a5c58d29 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusplugin.cpp @@ -68,6 +68,7 @@ PluginInterface::SamplingDevices FCDProPlusPlugin::enumSampleSources() fcd_traits::interfaceIID, serialNumber, i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index 6959dc4b3..f0552853c 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -64,6 +64,7 @@ PluginInterface::SamplingDevices FileSourcePlugin::enumSampleSources() m_deviceTypeID, QString::null, i, + PluginInterface::SamplingDevice::BuiltInDevice, true, 0)); } diff --git a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp index 724f98e53..26a08c707 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputplugin.cpp @@ -99,6 +99,7 @@ PluginInterface::SamplingDevices HackRFInputPlugin::enumSampleSources() m_deviceTypeID, serial_str, i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); diff --git a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp index ece50b9ad..f3f623376 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputplugin.cpp @@ -96,6 +96,7 @@ PluginInterface::SamplingDevices LimeSDRInputPlugin::enumSampleSources() m_deviceTypeID, QString(deviceList[i]), i, + PluginInterface::SamplingDevice::PhysicalDevice, true, j)); } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp index fa8d18ef3..10711d61c 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputplugin.cpp @@ -74,6 +74,7 @@ PluginInterface::SamplingDevices PlutoSDRInputPlugin::enumSampleSources() m_deviceTypeID, serial_str, i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index 96bfcd82e..8cc93407a 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -58,6 +58,7 @@ PluginInterface::SamplingDevices RTLSDRPlugin::enumSampleSources() m_deviceTypeID, QString(serial), i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index e521eef1f..74f2bb76b 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -65,6 +65,7 @@ PluginInterface::SamplingDevices SDRdaemonSourcePlugin::enumSampleSources() m_deviceTypeID, QString::null, i, + PluginInterface::SamplingDevice::BuiltInDevice, true, 0)); } diff --git a/plugins/samplesource/sdrplay/sdrplayplugin.cpp b/plugins/samplesource/sdrplay/sdrplayplugin.cpp index d5a1b145a..f2030a4e3 100644 --- a/plugins/samplesource/sdrplay/sdrplayplugin.cpp +++ b/plugins/samplesource/sdrplay/sdrplayplugin.cpp @@ -78,6 +78,7 @@ PluginInterface::SamplingDevices SDRPlayPlugin::enumSampleSources() m_deviceTypeID, QString(serial), i, + PluginInterface::SamplingDevice::PhysicalDevice, true, 0)); } diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h index fecb4aacd..d3c20d552 100644 --- a/sdrbase/plugin/plugininterface.h +++ b/sdrbase/plugin/plugininterface.h @@ -27,20 +27,28 @@ class PluginInterface { public: struct SamplingDevice { - QString displayedName; //!< The human readable name - QString hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) - QString id; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) - QString serial; //!< The device serial number - int sequence; //!< The device sequence. >0 when more than one device of the same type is connected - bool rxElseTx; //!< This is the Rx part else the Tx part of the device - int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized - int claimed; //!< This is the device set index if claimed else -1 + enum SamplingDeviceType + { + PhysicalDevice, + BuiltInDevice + }; + + QString displayedName; //!< The human readable name + QString hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) + QString id; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString serial; //!< The device serial number + int sequence; //!< The device sequence. >0 when more than one device of the same type is connected + SamplingDeviceType type; //!< The sampling device type for behavior information + bool rxElseTx; //!< This is the Rx part else the Tx part of the device + int deviceItemIndex; //!< For composite devices this is the Rx or Tx stream index. -1 if not initialized + int claimed; //!< This is the device set index if claimed else -1 SamplingDevice(const QString& _displayedName, const QString& _hardwareId, const QString& _id, const QString& _serial, int _sequence, + SamplingDeviceType _type, bool _rxElseTx, int _deviceItemIndex) : displayedName(_displayedName), @@ -48,6 +56,7 @@ public: id(_id), serial(_serial), sequence(_sequence), + type(_type), rxElseTx(_rxElseTx), deviceItemIndex(_deviceItemIndex), claimed(-1) diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index b3534dd63..db255fada 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -32,7 +32,7 @@ #include "deviceuiset.h" -DeviceUISet::DeviceUISet(QTimer& timer) +DeviceUISet::DeviceUISet(int tabIndex, QTimer& timer) { m_spectrum = new GLSpectrum; m_spectrumVis = new SpectrumVis(m_spectrum); @@ -40,11 +40,12 @@ DeviceUISet::DeviceUISet(QTimer& timer) m_spectrumGUI = new GLSpectrumGUI; m_spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, m_spectrum); m_channelWindow = new ChannelWindow; - m_samplingDeviceControl = new SamplingDeviceControl; + m_samplingDeviceControl = new SamplingDeviceControl(tabIndex); m_deviceSourceEngine = 0; m_deviceSourceAPI = 0; m_deviceSinkEngine = 0; m_deviceSinkAPI = 0; + m_deviceTabIndex = tabIndex; // m_spectrum needs to have its font to be set since it cannot be inherited from the main window QFont font; diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index a823c339b..0e33d53f5 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -45,7 +45,7 @@ struct DeviceUISet DeviceSinkAPI *m_deviceSinkAPI; QByteArray m_mainWindowState; - DeviceUISet(QTimer& timer); + DeviceUISet(int tabIndex, QTimer& timer); ~DeviceUISet(); GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter @@ -86,6 +86,7 @@ private: ChannelInstanceRegistrations m_rxChannelInstanceRegistrations; ChannelInstanceRegistrations m_txChannelInstanceRegistrations; + int m_deviceTabIndex; void renameRxChannelInstances(); void renameTxChannelInstances(); diff --git a/sdrgui/gui/samplingdevicecontrol.cpp b/sdrgui/gui/samplingdevicecontrol.cpp index 7d061e964..cf68101f9 100644 --- a/sdrgui/gui/samplingdevicecontrol.cpp +++ b/sdrgui/gui/samplingdevicecontrol.cpp @@ -20,10 +20,11 @@ #include "ui_samplingdevicecontrol.h" -SamplingDeviceControl::SamplingDeviceControl(QWidget* parent) : +SamplingDeviceControl::SamplingDeviceControl(int tabIndex, QWidget* parent) : QWidget(parent), ui(new Ui::SamplingDeviceControl), - m_pluginManager(0) + m_pluginManager(0), + m_deviceTabIndex(tabIndex) { ui->setupUi(this); } diff --git a/sdrgui/gui/samplingdevicecontrol.h b/sdrgui/gui/samplingdevicecontrol.h index 3773f5e30..58dea4932 100644 --- a/sdrgui/gui/samplingdevicecontrol.h +++ b/sdrgui/gui/samplingdevicecontrol.h @@ -37,7 +37,7 @@ class SDRANGEL_API SamplingDeviceControl : public QWidget { Q_OBJECT public: - explicit SamplingDeviceControl(QWidget* parent = NULL); + explicit SamplingDeviceControl(int tabIndex, QWidget* parent = 0); ~SamplingDeviceControl(); void setPluginManager(PluginManager *pluginManager) { m_pluginManager = pluginManager; } @@ -49,6 +49,7 @@ public: private: Ui::SamplingDeviceControl* ui; PluginManager *m_pluginManager; + int m_deviceTabIndex; }; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index b46624d3f..f4ef07956 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -204,10 +204,10 @@ void MainWindow::addSourceDevice() char uidCStr[16]; sprintf(uidCStr, "UID:%d", dspDeviceSourceEngineUID); - m_deviceUIs.push_back(new DeviceUISet(m_masterTimer)); + int deviceTabIndex = m_deviceUIs.size(); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = dspDeviceSourceEngine; - int deviceTabIndex = m_deviceUIs.size()-1; char tabNameCStr[16]; sprintf(tabNameCStr, "R%d", deviceTabIndex); @@ -267,11 +267,11 @@ void MainWindow::addSinkDevice() char uidCStr[16]; sprintf(uidCStr, "UID:%d", dspDeviceSinkEngineUID); - m_deviceUIs.push_back(new DeviceUISet(m_masterTimer)); + int deviceTabIndex = m_deviceUIs.size(); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = 0; m_deviceUIs.back()->m_deviceSinkEngine = dspDeviceSinkEngine; - int deviceTabIndex = m_deviceUIs.size()-1; char tabNameCStr[16]; sprintf(tabNameCStr, "T%d", deviceTabIndex); diff --git a/sdrgui/plugin/pluginapi.cpp b/sdrgui/plugin/pluginapi.cpp index eb19aa64b..7a3e63e15 100644 --- a/sdrgui/plugin/pluginapi.cpp +++ b/sdrgui/plugin/pluginapi.cpp @@ -31,11 +31,6 @@ PluginAPI::ChannelRegistrations *PluginAPI::getTxChannelRegistrations() return m_pluginManager->getTxChannelRegistrations(); } -bool PluginAPI::isBuiltInDevice(QString& deviceTypeID) -{ - return m_pluginManager->isBuiltInDevice(deviceTypeID); -} - PluginAPI::PluginAPI(PluginManager* pluginManager) : m_pluginManager(pluginManager) { diff --git a/sdrgui/plugin/pluginapi.h b/sdrgui/plugin/pluginapi.h index 6e559e8bf..1820e697a 100644 --- a/sdrgui/plugin/pluginapi.h +++ b/sdrgui/plugin/pluginapi.h @@ -5,11 +5,11 @@ #include #include "util/export.h" +#include "plugin/plugininterface.h" class QString; class PluginManager; -class PluginInterface; class MessageQueue; class PluginInstanceGUI; @@ -43,9 +43,6 @@ public: // Sample Sink stuff void registerSampleSink(const QString& sinkName, PluginInterface* plugin); - // Categories enquiry - bool isBuiltInDevice(QString& deviceTypeID); - protected: PluginManager* m_pluginManager; diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 0f747cfc9..8c41399fa 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -643,12 +643,3 @@ void PluginManager::createTxChannelInstance(int channelPluginIndex, DeviceUISet pluginInterface->createTxChannel(m_txChannelRegistrations[channelPluginIndex].m_channelId, deviceUISet); } } - -bool PluginManager::isBuiltInDevice(QString& deviceTypeID) -{ - return ((deviceTypeID == m_fileSourceDeviceTypeID) || - (deviceTypeID == m_fileSinkDeviceTypeID) || - (deviceTypeID == m_sdrDaemonSourceDeviceTypeID) || - (deviceTypeID == m_sdrDaemonSinkDeviceTypeID)); -} - diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index dae139fce..644581edb 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -79,8 +79,6 @@ public: void createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); void listTxChannels(QList& list); - bool isBuiltInDevice(QString& deviceTypeID); - private: struct SamplingDeviceRegistration //!< This is the channel registration { From 55cc8ed33d6fcd32e38476a9a174ab16bef4fe03 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 22:02:34 +0100 Subject: [PATCH 25/39] PluginManager: removed useless methods referencing QComboBox --- sdrgui/plugin/pluginmanager.cpp | 35 +-------------------------------- sdrgui/plugin/pluginmanager.h | 2 -- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 8c41399fa..29fc2deb4 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -71,6 +71,7 @@ void PluginManager::loadPlugins() it->pluginInterface->initPlugin(&m_pluginAPI); } + // TODO: enumerate with DeviceEnumerator updateSampleSourceDevices(); updateSampleSinkDevices(); } @@ -281,23 +282,6 @@ void PluginManager::fillSampleSourceSelector(QComboBox* comboBox, uint deviceUID } } -int PluginManager::getSampleSourceSelectorIndex(QComboBox* comboBox, DeviceSourceAPI *deviceSourceAPI) -{ - for (int i = 0; i < comboBox->count(); i++) - { - SamplingDevice *samplingDevice = (SamplingDevice*) (comboBox->itemData(i)).value(); - - if ((samplingDevice->m_deviceId == deviceSourceAPI->getSampleSourceId()) && - (samplingDevice->m_deviceSerial == deviceSourceAPI->getSampleSourceSerial()) && - (samplingDevice->m_deviceSequence == deviceSourceAPI->getSampleSourceSequence())) - { - return i; - } - } - - return 0; // default to first item -} - void PluginManager::fillSampleSinkSelector(QComboBox* comboBox, uint deviceUID) { comboBox->clear(); @@ -316,23 +300,6 @@ void PluginManager::fillSampleSinkSelector(QComboBox* comboBox, uint deviceUID) } } -int PluginManager::getSampleSinkSelectorIndex(QComboBox* comboBox, DeviceSinkAPI *deviceSinkAPI) -{ - for (int i = 0; i < comboBox->count(); i++) - { - SamplingDevice *samplingDevice = (SamplingDevice*) (comboBox->itemData(i)).value(); - - if ((samplingDevice->m_deviceId == deviceSinkAPI->getSampleSinkId()) && - (samplingDevice->m_deviceSerial == deviceSinkAPI->getSampleSinkSerial()) && - (samplingDevice->m_deviceSequence == deviceSinkAPI->getSampleSinkSequence())) - { - return i; - } - } - - return 0; // default to first item -} - int PluginManager::selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceAPI) { qDebug("PluginManager::selectSampleSourceByIndex: index: %d", index); diff --git a/sdrgui/plugin/pluginmanager.h b/sdrgui/plugin/pluginmanager.h index 644581edb..c0abb24e9 100644 --- a/sdrgui/plugin/pluginmanager.h +++ b/sdrgui/plugin/pluginmanager.h @@ -57,12 +57,10 @@ public: void updateSampleSourceDevices(); void duplicateLocalSampleSourceDevices(uint deviceUID); void fillSampleSourceSelector(QComboBox* comboBox, uint deviceUID); - int getSampleSourceSelectorIndex(QComboBox* comboBox, DeviceSourceAPI *deviceSourceAPI); void updateSampleSinkDevices(); void duplicateLocalSampleSinkDevices(uint deviceUID); void fillSampleSinkSelector(QComboBox* comboBox, uint deviceUID); - int getSampleSinkSelectorIndex(QComboBox* comboBox, DeviceSinkAPI *deviceSinkAPI); int selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceAPI); int selectSampleSourceBySerialOrSequence(const QString& sourceId, const QString& sourceSerial, uint32_t sourceSequence, DeviceSourceAPI *deviceAPI); From 467e87730b5e64700d37576385637967a404c25c Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 22:40:41 +0100 Subject: [PATCH 26/39] Unimplement main window reload devices --- sdrgui/mainwindow.cpp | 111 ++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index f4ef07956..97d5938d0 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1070,61 +1070,64 @@ void MainWindow::on_action_removeLastDevice_triggered() void MainWindow::on_action_reloadDevices_triggered() { - // all devices must be stopped - std::vector::iterator it = m_deviceUIs.begin(); - for (; it != m_deviceUIs.end(); ++it) - { - if ((*it)->m_deviceSourceEngine) // it is a source device - { - if ((*it)->m_deviceSourceEngine->state() == DSPDeviceSourceEngine::StRunning) - { - QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); - return; - } - } + QMessageBox::information(this, tr("Message"), tr("Not implemented")); + return; - if ((*it)->m_deviceSinkEngine) // it is a sink device - { - if ((*it)->m_deviceSinkEngine->state() == DSPDeviceSinkEngine::StRunning) - { - QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); - return; - } - } - } - - // re-scan devices - m_pluginManager->updateSampleSourceDevices(); - m_pluginManager->updateSampleSinkDevices(); - - // re-populate device selectors keeping the same selection - it = m_deviceUIs.begin(); - for (; it != m_deviceUIs.end(); ++it) - { - if ((*it)->m_deviceSourceEngine) // it is a source device - { - QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); - bool sampleSourceSignalsBlocked = deviceSelectorComboBox->blockSignals(true); - uint dspDeviceSourceEngineUID = (*it)->m_deviceSourceEngine->getUID(); - m_pluginManager->duplicateLocalSampleSourceDevices(dspDeviceSourceEngineUID); - m_pluginManager->fillSampleSourceSelector(deviceSelectorComboBox, dspDeviceSourceEngineUID); - int newIndex = m_pluginManager->getSampleSourceSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSourceAPI); - deviceSelectorComboBox->setCurrentIndex(newIndex); - deviceSelectorComboBox->blockSignals(sampleSourceSignalsBlocked); - } - - if ((*it)->m_deviceSinkEngine) // it is a sink device - { - QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); - bool sampleSinkSignalsBlocked = deviceSelectorComboBox->blockSignals(true); - uint dspDeviceSinkEngineUID = (*it)->m_deviceSinkEngine->getUID(); - m_pluginManager->duplicateLocalSampleSinkDevices(dspDeviceSinkEngineUID); - m_pluginManager->fillSampleSinkSelector(deviceSelectorComboBox, dspDeviceSinkEngineUID); - int newIndex = m_pluginManager->getSampleSinkSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSinkAPI); - deviceSelectorComboBox->setCurrentIndex(newIndex); - deviceSelectorComboBox->blockSignals(sampleSinkSignalsBlocked); - } - } +// // all devices must be stopped +// std::vector::iterator it = m_deviceUIs.begin(); +// for (; it != m_deviceUIs.end(); ++it) +// { +// if ((*it)->m_deviceSourceEngine) // it is a source device +// { +// if ((*it)->m_deviceSourceEngine->state() == DSPDeviceSourceEngine::StRunning) +// { +// QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); +// return; +// } +// } +// +// if ((*it)->m_deviceSinkEngine) // it is a sink device +// { +// if ((*it)->m_deviceSinkEngine->state() == DSPDeviceSinkEngine::StRunning) +// { +// QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); +// return; +// } +// } +// } +// +// // re-scan devices +// m_pluginManager->updateSampleSourceDevices(); +// m_pluginManager->updateSampleSinkDevices(); +// +// // re-populate device selectors keeping the same selection +// it = m_deviceUIs.begin(); +// for (; it != m_deviceUIs.end(); ++it) +// { +// if ((*it)->m_deviceSourceEngine) // it is a source device +// { +// QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); +// bool sampleSourceSignalsBlocked = deviceSelectorComboBox->blockSignals(true); +// uint dspDeviceSourceEngineUID = (*it)->m_deviceSourceEngine->getUID(); +// m_pluginManager->duplicateLocalSampleSourceDevices(dspDeviceSourceEngineUID); +// m_pluginManager->fillSampleSourceSelector(deviceSelectorComboBox, dspDeviceSourceEngineUID); +// int newIndex = m_pluginManager->getSampleSourceSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSourceAPI); +// deviceSelectorComboBox->setCurrentIndex(newIndex); +// deviceSelectorComboBox->blockSignals(sampleSourceSignalsBlocked); +// } +// +// if ((*it)->m_deviceSinkEngine) // it is a sink device +// { +// QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); +// bool sampleSinkSignalsBlocked = deviceSelectorComboBox->blockSignals(true); +// uint dspDeviceSinkEngineUID = (*it)->m_deviceSinkEngine->getUID(); +// m_pluginManager->duplicateLocalSampleSinkDevices(dspDeviceSinkEngineUID); +// m_pluginManager->fillSampleSinkSelector(deviceSelectorComboBox, dspDeviceSinkEngineUID); +// int newIndex = m_pluginManager->getSampleSinkSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSinkAPI); +// deviceSelectorComboBox->setCurrentIndex(newIndex); +// deviceSelectorComboBox->blockSignals(sampleSinkSignalsBlocked); +// } +// } } void MainWindow::on_action_Exit_triggered() From 0e1cc647cd74bbb79682b14ecd0b90e0a15af8bd Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 22:56:32 +0100 Subject: [PATCH 27/39] Plugin manager: neutralize QComboBox --- sdrgui/mainwindow.cpp | 2 + sdrgui/plugin/pluginmanager.cpp | 72 +++++++++++++++++---------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 97d5938d0..57d2d8d08 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -229,6 +229,7 @@ void MainWindow::addSourceDevice() bool sampleSourceSignalsBlocked = m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(true); m_pluginManager->duplicateLocalSampleSourceDevices(dspDeviceSourceEngineUID); + // FIXME: replace with the device selection dialog based on static enumeration m_pluginManager->fillSampleSourceSelector(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector(), dspDeviceSourceEngineUID); connect(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelectionConfirm(), SIGNAL(clicked(bool)), this, SLOT(on_sampleSource_confirmClicked(bool))); @@ -294,6 +295,7 @@ void MainWindow::addSinkDevice() bool sampleSourceSignalsBlocked = m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(true); m_pluginManager->duplicateLocalSampleSinkDevices(dspDeviceSinkEngineUID); + // FIXME: replace with the device selection dialog based on static enumeration m_pluginManager->fillSampleSinkSelector(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector(), dspDeviceSinkEngineUID); connect(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelectionConfirm(), SIGNAL(clicked(bool)), this, SLOT(on_sampleSink_confirmClicked(bool))); diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrgui/plugin/pluginmanager.cpp index 29fc2deb4..ab5c23a37 100644 --- a/sdrgui/plugin/pluginmanager.cpp +++ b/sdrgui/plugin/pluginmanager.cpp @@ -14,9 +14,9 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include +#include #include -#include +//#include #include #include @@ -54,7 +54,7 @@ PluginManager::~PluginManager() void PluginManager::loadPlugins() { - QString applicationDirPath = QApplication::instance()->applicationDirPath(); + QString applicationDirPath = QCoreApplication::instance()->applicationDirPath(); QString applicationLibPath = applicationDirPath + "/../lib"; qDebug() << "PluginManager::loadPlugins: " << qPrintable(applicationDirPath) << ", " << qPrintable(applicationLibPath); @@ -263,41 +263,45 @@ void PluginManager::duplicateLocalSampleSinkDevices(uint deviceUID) } } -void PluginManager::fillSampleSourceSelector(QComboBox* comboBox, uint deviceUID) +void PluginManager::fillSampleSourceSelector( + QComboBox* comboBox __attribute__((unused)), + uint deviceUID __attribute__((unused))) { - comboBox->clear(); - - for(int i = 0; i < m_sampleSourceDevices.count(); i++) - { - // For "local" devices show only ones that concern this device set - if ((m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) - || (m_sampleSourceDevices[i].m_deviceId == m_fileSourceDeviceTypeID)) - { - if (deviceUID != m_sampleSourceDevices[i].m_deviceSequence) { - continue; - } - } - - comboBox->addItem(m_sampleSourceDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSourceDevices[i])); - } +// comboBox->clear(); +// +// for(int i = 0; i < m_sampleSourceDevices.count(); i++) +// { +// // For "local" devices show only ones that concern this device set +// if ((m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) +// || (m_sampleSourceDevices[i].m_deviceId == m_fileSourceDeviceTypeID)) +// { +// if (deviceUID != m_sampleSourceDevices[i].m_deviceSequence) { +// continue; +// } +// } +// +// comboBox->addItem(m_sampleSourceDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSourceDevices[i])); +// } } -void PluginManager::fillSampleSinkSelector(QComboBox* comboBox, uint deviceUID) +void PluginManager::fillSampleSinkSelector( + QComboBox* comboBox __attribute__((unused)), + uint deviceUID __attribute__((unused))) { - comboBox->clear(); - - for(int i = 0; i < m_sampleSinkDevices.count(); i++) - { - // For "local" devices show only ones that concern this device set - if (m_sampleSinkDevices[i].m_deviceId == m_fileSinkDeviceTypeID) - { - if (deviceUID != m_sampleSinkDevices[i].m_deviceSequence) { - continue; - } - } - - comboBox->addItem(m_sampleSinkDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSinkDevices[i])); - } +// comboBox->clear(); +// +// for(int i = 0; i < m_sampleSinkDevices.count(); i++) +// { +// // For "local" devices show only ones that concern this device set +// if (m_sampleSinkDevices[i].m_deviceId == m_fileSinkDeviceTypeID) +// { +// if (deviceUID != m_sampleSinkDevices[i].m_deviceSequence) { +// continue; +// } +// } +// +// comboBox->addItem(m_sampleSinkDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSinkDevices[i])); +// } } int PluginManager::selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceAPI) From 21ba11d90e7343c80cacfebd89dc2f30e796c19d Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 1 Nov 2017 23:00:00 +0100 Subject: [PATCH 28/39] Moved plugin classes back to sdrbase --- sdrbase/CMakeLists.txt | 4 ++++ {sdrgui => sdrbase}/plugin/pluginapi.cpp | 0 {sdrgui => sdrbase}/plugin/pluginapi.h | 0 {sdrgui => sdrbase}/plugin/pluginmanager.cpp | 0 {sdrgui => sdrbase}/plugin/pluginmanager.h | 0 sdrgui/CMakeLists.txt | 6 ------ 6 files changed, 4 insertions(+), 6 deletions(-) rename {sdrgui => sdrbase}/plugin/pluginapi.cpp (100%) rename {sdrgui => sdrbase}/plugin/pluginapi.h (100%) rename {sdrgui => sdrbase}/plugin/pluginmanager.cpp (100%) rename {sdrgui => sdrbase}/plugin/pluginmanager.h (100%) diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 4bc2edf0a..3b5ce6b70 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -62,6 +62,8 @@ set(sdrbase_SOURCES #util/spinlock.cpp plugin/plugininterface.cpp + plugin/pluginapi.cpp + plugin/pluginmanager.cpp ) set(sdrbase_HEADERS @@ -129,6 +131,8 @@ set(sdrbase_HEADERS plugin/plugininstancegui.h plugin/plugininterface.h + plugin/pluginapi.h + plugin/pluginmanager.h settings/preferences.h settings/preset.h diff --git a/sdrgui/plugin/pluginapi.cpp b/sdrbase/plugin/pluginapi.cpp similarity index 100% rename from sdrgui/plugin/pluginapi.cpp rename to sdrbase/plugin/pluginapi.cpp diff --git a/sdrgui/plugin/pluginapi.h b/sdrbase/plugin/pluginapi.h similarity index 100% rename from sdrgui/plugin/pluginapi.h rename to sdrbase/plugin/pluginapi.h diff --git a/sdrgui/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp similarity index 100% rename from sdrgui/plugin/pluginmanager.cpp rename to sdrbase/plugin/pluginmanager.cpp diff --git a/sdrgui/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h similarity index 100% rename from sdrgui/plugin/pluginmanager.h rename to sdrbase/plugin/pluginmanager.h diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index caceba76b..a7bfc1b8f 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -44,9 +44,6 @@ set(sdrgui_SOURCES dsp/spectrumscopengcombovis.cpp device/deviceuiset.cpp - - plugin/pluginapi.cpp - plugin/pluginmanager.cpp ) set(sdrgui_HEADERS @@ -93,9 +90,6 @@ set(sdrgui_HEADERS dsp/spectrumscopengcombovis.h device/deviceuiset.h - - plugin/pluginapi.h - plugin/pluginmanager.h ) set(sdrgui_SOURCES From 55ad76f43c68d9ce4c31a8367e20af62a5b0ef5c Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 03:30:54 +0100 Subject: [PATCH 29/39] New devices handling (1) --- sdrbase/CMakeLists.txt | 4 +- sdrbase/device/deviceenumerator.cpp | 179 +++++++++++ sdrbase/device/deviceenumerator.h | 69 +++++ sdrbase/plugin/pluginapi.h | 12 + sdrbase/plugin/pluginmanager.cpp | 430 +-------------------------- sdrbase/plugin/pluginmanager.h | 44 +-- sdrbase/settings/mainsettings.h | 2 + sdrgui/CMakeLists.txt | 3 + sdrgui/device/deviceuiset.cpp | 4 +- sdrgui/device/deviceuiset.h | 2 +- sdrgui/gui/samplingdevicecontrol.cpp | 47 ++- sdrgui/gui/samplingdevicecontrol.h | 16 +- sdrgui/gui/samplingdevicecontrol.ui | 43 ++- sdrgui/gui/samplingdevicedialog.cpp | 63 ++++ sdrgui/gui/samplingdevicedialog.h | 50 ++++ sdrgui/gui/samplingdevicedialog.ui | 91 ++++++ sdrgui/mainwindow.cpp | 187 +++++------- sdrgui/mainwindow.h | 4 +- sdrgui/resources/choose.png | Bin 0 -> 602 bytes sdrgui/resources/res.qrc | 1 + 20 files changed, 654 insertions(+), 597 deletions(-) create mode 100644 sdrbase/device/deviceenumerator.cpp create mode 100644 sdrbase/device/deviceenumerator.h create mode 100644 sdrgui/gui/samplingdevicedialog.cpp create mode 100644 sdrgui/gui/samplingdevicedialog.h create mode 100644 sdrgui/gui/samplingdevicedialog.ui create mode 100644 sdrgui/resources/choose.png diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 3b5ce6b70..38a5e8e2e 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -45,7 +45,7 @@ set(sdrbase_SOURCES device/devicesourceapi.cpp device/devicesinkapi.cpp -# device/deviceenumerator.cpp + device/deviceenumerator.cpp settings/preferences.cpp settings/preset.cpp @@ -127,7 +127,7 @@ set(sdrbase_HEADERS device/devicesourceapi.h device/devicesinkapi.h -# device/deviceenumerator.h + device/deviceenumerator.h plugin/plugininstancegui.h plugin/plugininterface.h diff --git a/sdrbase/device/deviceenumerator.cpp b/sdrbase/device/deviceenumerator.cpp new file mode 100644 index 000000000..7399bba0a --- /dev/null +++ b/sdrbase/device/deviceenumerator.cpp @@ -0,0 +1,179 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 // +// // +// 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/pluginmanager.h" +#include "deviceenumerator.h" + +Q_GLOBAL_STATIC(DeviceEnumerator, deviceEnumerator) +DeviceEnumerator *DeviceEnumerator::instance() +{ + return deviceEnumerator; +} + +DeviceEnumerator::DeviceEnumerator() +{} + +DeviceEnumerator::~DeviceEnumerator() +{} + +void DeviceEnumerator::enumerateRxDevices(PluginManager *pluginManager) +{ + m_rxEnumeration.clear(); + PluginAPI::SamplingDeviceRegistrations& rxDeviceRegistrations = pluginManager->getSourceDeviceRegistrations(); + int index = 0; + + for (int i = 0; i < rxDeviceRegistrations.count(); i++) + { + PluginInterface::SamplingDevices samplingDevices = rxDeviceRegistrations[i].m_plugin->enumSampleSources(); + + for (int j = 0; j < samplingDevices.count(); j++) + { + m_rxEnumeration.push_back( + DeviceEnumeration( + samplingDevices[j], + rxDeviceRegistrations[i].m_plugin, + index + ) + ); + index++; + } + } +} + +void DeviceEnumerator::enumerateTxDevices(PluginManager *pluginManager) +{ + m_txEnumeration.clear(); + PluginAPI::SamplingDeviceRegistrations& txDeviceRegistrations = pluginManager->getSinkDeviceRegistrations(); + int index = 0; + + for (int i = 0; i < txDeviceRegistrations.count(); i++) + { + PluginInterface::SamplingDevices samplingDevices = txDeviceRegistrations[i].m_plugin->enumSampleSources(); + + for (int j = 0; j < samplingDevices.count(); j++) + { + m_rxEnumeration.push_back( + DeviceEnumeration( + samplingDevices[j], + txDeviceRegistrations[i].m_plugin, + index + ) + ); + index++; + } + } +} + +void DeviceEnumerator::listRxDeviceNames(QList& list, std::vector& indexes) const +{ + for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) + { + list.append(it->m_samplingDevice.displayedName); + indexes.push_back(it->m_index); + } + } +} + +void DeviceEnumerator::listTxDeviceNames(QList& list, std::vector& indexes) const +{ + for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.claimed < 0) || (it->m_samplingDevice.type == PluginInterface::SamplingDevice::BuiltInDevice)) + { + list.append(it->m_samplingDevice.displayedName); + indexes.push_back(it->m_index); + } + } +} + +void DeviceEnumerator::changeRxSelection(int tabIndex, int deviceIndex) +{ + for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) + { + if (it->m_samplingDevice.claimed == tabIndex) { + it->m_samplingDevice.claimed = -1; + } + if (it->m_index == deviceIndex) { + it->m_samplingDevice.claimed = tabIndex; + } + } +} + +void DeviceEnumerator::changeTxSelection(int tabIndex, int deviceIndex) +{ + for (DevicesEnumeration::iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) + { + if (it->m_samplingDevice.claimed == tabIndex) { + it->m_samplingDevice.claimed = -1; + } + if (it->m_index == deviceIndex) { + it->m_samplingDevice.claimed = tabIndex; + } + } +} + +int DeviceEnumerator::getFileSourceDeviceIndex() const +{ + for (DevicesEnumeration::const_iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) + { + if (it->m_samplingDevice.id == PluginManager::getFileSourceDeviceId()) { + return it->m_index; + } + } + + return -1; +} + +int DeviceEnumerator::getFileSinkDeviceIndex() const +{ + for (DevicesEnumeration::const_iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) + { + if (it->m_samplingDevice.id == PluginManager::getFileSinkDeviceId()) { + return it->m_index; + } + } + + return -1; +} + +int DeviceEnumerator::getRxSamplingDeviceIndex(const QString& deviceId, int sequence) +{ + for (DevicesEnumeration::iterator it = m_rxEnumeration.begin(); it != m_rxEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.id == deviceId) && (it->m_samplingDevice.sequence == sequence)) { + return it->m_index; + } + } + + return -1; +} + +int DeviceEnumerator::getTxSamplingDeviceIndex(const QString& deviceId, int sequence) +{ + for (DevicesEnumeration::iterator it = m_txEnumeration.begin(); it != m_txEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.id == deviceId) && (it->m_samplingDevice.sequence == sequence)) { + return it->m_index; + } + } + + return -1; +} + diff --git a/sdrbase/device/deviceenumerator.h b/sdrbase/device/deviceenumerator.h new file mode 100644 index 000000000..36d4f4e41 --- /dev/null +++ b/sdrbase/device/deviceenumerator.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 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 // +// // +// 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 SDRBASE_DEVICE_DEVICEENUMERATOR_H_ +#define SDRBASE_DEVICE_DEVICEENUMERATOR_H_ + +#include + +#include "plugin/plugininterface.h" + +class PluginManager; + +class DeviceEnumerator +{ +public: + DeviceEnumerator(); + ~DeviceEnumerator(); + + static DeviceEnumerator *instance(); + + void enumerateRxDevices(PluginManager *pluginManager); + void enumerateTxDevices(PluginManager *pluginManager); + void listRxDeviceNames(QList& list, std::vector& indexes) const; + void listTxDeviceNames(QList& list, std::vector& indexes) const; + void changeRxSelection(int tabIndex, int deviceIndex); + void changeTxSelection(int tabIndex, int deviceIndex); + PluginInterface::SamplingDevice getRxSamplingDevice(int deviceIndex) const { return m_rxEnumeration[deviceIndex].m_samplingDevice; } + PluginInterface::SamplingDevice getTxSamplingDevice(int deviceIndex) const { return m_txEnumeration[deviceIndex].m_samplingDevice; } + PluginInterface *getRxPluginInterface(int deviceIndex) { return m_rxEnumeration[deviceIndex].m_pluginInterface; } + PluginInterface *getTxPluginInterface(int deviceIndex) { return m_txEnumeration[deviceIndex].m_pluginInterface; } + int getFileSourceDeviceIndex() const; + int getFileSinkDeviceIndex() const; + int getRxSamplingDeviceIndex(const QString& deviceId, int sequence); + int getTxSamplingDeviceIndex(const QString& deviceId, int sequence); + +private: + struct DeviceEnumeration + { + PluginInterface::SamplingDevice m_samplingDevice; + PluginInterface *m_pluginInterface; + int m_index; + + DeviceEnumeration(const PluginInterface::SamplingDevice& samplingDevice, PluginInterface *pluginInterface, int index) : + m_samplingDevice(samplingDevice), + m_pluginInterface(pluginInterface), + m_index(index) + {} + }; + + typedef std::vector DevicesEnumeration; + + DevicesEnumeration m_rxEnumeration; + DevicesEnumeration m_txEnumeration; +}; + +#endif /* SDRBASE_DEVICE_DEVICEENUMERATOR_H_ */ diff --git a/sdrbase/plugin/pluginapi.h b/sdrbase/plugin/pluginapi.h index 1820e697a..0cc629fa9 100644 --- a/sdrbase/plugin/pluginapi.h +++ b/sdrbase/plugin/pluginapi.h @@ -17,6 +17,18 @@ class SDRANGEL_API PluginAPI : public QObject { Q_OBJECT public: + struct SamplingDeviceRegistration //!< This is the device registration + { + QString m_deviceId; + PluginInterface* m_plugin; + SamplingDeviceRegistration(const QString& deviceId, PluginInterface* plugin) : + m_deviceId(deviceId), + m_plugin(plugin) + { } + }; + + typedef QList SamplingDeviceRegistrations; + struct ChannelRegistration { QString m_channelId; //!< Channel or device type ID diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index ab5c23a37..559cb47b6 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -24,6 +24,7 @@ #include #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" +#include "device/deviceenumerator.h" #include "settings/preset.h" #include "util/message.h" #include "dsp/dspdevicesourceengine.h" @@ -71,9 +72,8 @@ void PluginManager::loadPlugins() it->pluginInterface->initPlugin(&m_pluginAPI); } - // TODO: enumerate with DeviceEnumerator - updateSampleSourceDevices(); - updateSampleSinkDevices(); + DeviceEnumerator::instance()->enumerateRxDevices(this); + DeviceEnumerator::instance()->enumerateTxDevices(this); } void PluginManager::registerRxChannel(const QString& channelName, PluginInterface* plugin) @@ -100,7 +100,7 @@ void PluginManager::registerSampleSource(const QString& sourceName, PluginInterf << plugin->getPluginDescriptor().displayedName.toStdString().c_str() << " with source name " << sourceName.toStdString().c_str(); - m_sampleSourceRegistrations.append(SamplingDeviceRegistration(sourceName, plugin)); + m_sampleSourceRegistrations.append(PluginAPI::SamplingDeviceRegistration(sourceName, plugin)); } void PluginManager::registerSampleSink(const QString& sinkName, PluginInterface* plugin) @@ -109,418 +109,7 @@ void PluginManager::registerSampleSink(const QString& sinkName, PluginInterface* << plugin->getPluginDescriptor().displayedName.toStdString().c_str() << " with sink name " << sinkName.toStdString().c_str(); - m_sampleSinkRegistrations.append(SamplingDeviceRegistration(sinkName, plugin)); -} - -void PluginManager::updateSampleSourceDevices() -{ - m_sampleSourceDevices.clear(); - - for(int i = 0; i < m_sampleSourceRegistrations.count(); ++i) - { - PluginInterface::SamplingDevices ssd = m_sampleSourceRegistrations[i].m_plugin->enumSampleSources(); - - for(int j = 0; j < ssd.count(); ++j) - { - m_sampleSourceDevices.append(SamplingDevice(m_sampleSourceRegistrations[i].m_plugin, - ssd[j].displayedName, - ssd[j].hardwareId, - ssd[j].id, - ssd[j].serial, - ssd[j].sequence)); - qDebug("PluginManager::updateSampleSourceDevices: %s %s %s %s %d", - qPrintable(ssd[j].displayedName), - qPrintable(ssd[j].hardwareId), - qPrintable(ssd[j].id), - qPrintable(ssd[j].serial), - ssd[j].sequence); - } - } -} - -void PluginManager::updateSampleSinkDevices() -{ - m_sampleSinkDevices.clear(); - - for(int i = 0; i < m_sampleSinkRegistrations.count(); ++i) - { - PluginInterface::SamplingDevices ssd = m_sampleSinkRegistrations[i].m_plugin->enumSampleSinks(); - - for(int j = 0; j < ssd.count(); ++j) - { - m_sampleSinkDevices.append(SamplingDevice(m_sampleSinkRegistrations[i].m_plugin, - ssd[j].displayedName, - ssd[j].hardwareId, - ssd[j].id, - ssd[j].serial, - ssd[j].sequence)); - qDebug("PluginManager::updateSampleSinkDevices: %s %s %s %s %d", - qPrintable(ssd[j].displayedName), - qPrintable(ssd[j].hardwareId), - qPrintable(ssd[j].id), - qPrintable(ssd[j].serial), - ssd[j].sequence); - } - } -} - -void PluginManager::duplicateLocalSampleSourceDevices(uint deviceUID) -{ - if (deviceUID == 0) { - return; - } - - SamplingDevice *sdrDaemonSSD0 = 0; - SamplingDevice *fileSourceSSD0 = 0; - bool duplicateSDRDaemon = true; - bool duplicateFileSource = true; - - for(int i = 0; i < m_sampleSourceDevices.count(); ++i) - { - if (m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) // SDRdaemon - { - if (m_sampleSourceDevices[i].m_deviceSequence == 0) { // reference to device 0 - sdrDaemonSSD0 = &m_sampleSourceDevices[i]; - } - else if (m_sampleSourceDevices[i].m_deviceSequence == deviceUID) { // already there - duplicateSDRDaemon = false; - } - } - else if (m_sampleSourceDevices[i].m_deviceId == m_fileSourceDeviceTypeID) // File Source - { - if (m_sampleSourceDevices[i].m_deviceSequence == 0) { // reference to device 0 - fileSourceSSD0 = &m_sampleSourceDevices[i]; - } - else if (m_sampleSourceDevices[i].m_deviceSequence == deviceUID) { // already there - duplicateFileSource = false; - } - } - } - - if (sdrDaemonSSD0 && duplicateSDRDaemon) // append item for a new instance - { - m_sampleSourceDevices.append( - SamplingDevice( - sdrDaemonSSD0->m_plugin, - QString("SDRdaemonSource[%1]").arg(deviceUID), - sdrDaemonSSD0->m_hadrwareId, - sdrDaemonSSD0->m_deviceId, - sdrDaemonSSD0->m_deviceSerial, - deviceUID - ) - ); - } - - if (fileSourceSSD0 && duplicateFileSource) // append item for a new instance - { - m_sampleSourceDevices.append( - SamplingDevice( - fileSourceSSD0->m_plugin, - QString("FileSource[%1]").arg(deviceUID), - fileSourceSSD0->m_hadrwareId, - fileSourceSSD0->m_deviceId, - fileSourceSSD0->m_deviceSerial, - deviceUID - ) - ); - } -} - -void PluginManager::duplicateLocalSampleSinkDevices(uint deviceUID) -{ - if (deviceUID == 0) { - return; - } - - SamplingDevice *fileSinkSSD0 = 0; - bool duplicateFileSink = true; - - for(int i = 0; i < m_sampleSinkDevices.count(); ++i) - { - if (m_sampleSinkDevices[i].m_deviceId == m_fileSinkDeviceTypeID) // File Sink - { - if (m_sampleSinkDevices[i].m_deviceSequence == 0) { // reference to device 0 - fileSinkSSD0 = &m_sampleSinkDevices[i]; - } - else if (m_sampleSinkDevices[i].m_deviceSequence == deviceUID) { // already there - duplicateFileSink = false; - } - } - } - - if (fileSinkSSD0 && duplicateFileSink) // append item for a new instance - { - m_sampleSinkDevices.append( - SamplingDevice( - fileSinkSSD0->m_plugin, - QString("FileSink[%1]").arg(deviceUID), - fileSinkSSD0->m_hadrwareId, - fileSinkSSD0->m_deviceId, - fileSinkSSD0->m_deviceSerial, - deviceUID - ) - ); - } -} - -void PluginManager::fillSampleSourceSelector( - QComboBox* comboBox __attribute__((unused)), - uint deviceUID __attribute__((unused))) -{ -// comboBox->clear(); -// -// for(int i = 0; i < m_sampleSourceDevices.count(); i++) -// { -// // For "local" devices show only ones that concern this device set -// if ((m_sampleSourceDevices[i].m_deviceId == m_sdrDaemonSourceDeviceTypeID) -// || (m_sampleSourceDevices[i].m_deviceId == m_fileSourceDeviceTypeID)) -// { -// if (deviceUID != m_sampleSourceDevices[i].m_deviceSequence) { -// continue; -// } -// } -// -// comboBox->addItem(m_sampleSourceDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSourceDevices[i])); -// } -} - -void PluginManager::fillSampleSinkSelector( - QComboBox* comboBox __attribute__((unused)), - uint deviceUID __attribute__((unused))) -{ -// comboBox->clear(); -// -// for(int i = 0; i < m_sampleSinkDevices.count(); i++) -// { -// // For "local" devices show only ones that concern this device set -// if (m_sampleSinkDevices[i].m_deviceId == m_fileSinkDeviceTypeID) -// { -// if (deviceUID != m_sampleSinkDevices[i].m_deviceSequence) { -// continue; -// } -// } -// -// comboBox->addItem(m_sampleSinkDevices[i].m_displayName, qVariantFromValue((void *) &m_sampleSinkDevices[i])); -// } -} - -int PluginManager::selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceAPI) -{ - qDebug("PluginManager::selectSampleSourceByIndex: index: %d", index); - - if (m_sampleSourceDevices.count() == 0) - { - return -1; - } - - if (index < 0) - { - return -1; - } - - if (index >= m_sampleSourceDevices.count()) - { - index = 0; - } - - qDebug() << "PluginManager::selectSampleSourceByIndex: m_sampleSource at index " << index - << " hid: " << m_sampleSourceDevices[index].m_hadrwareId.toStdString().c_str() - << " id: " << m_sampleSourceDevices[index].m_deviceId.toStdString().c_str() - << " ser: " << m_sampleSourceDevices[index].m_deviceSerial.toStdString().c_str() - << " seq: " << m_sampleSourceDevices[index].m_deviceSequence; - - deviceAPI->stopAcquisition(); - - deviceAPI->setSampleSourceSequence(m_sampleSourceDevices[index].m_deviceSequence); - deviceAPI->setHardwareId(m_sampleSourceDevices[index].m_hadrwareId); - deviceAPI->setSampleSourceId(m_sampleSourceDevices[index].m_deviceId); - deviceAPI->setSampleSourceSerial(m_sampleSourceDevices[index].m_deviceSerial); - deviceAPI->setSampleSourceDisplayName(m_sampleSourceDevices[index].m_displayName); - deviceAPI->setSampleSourcePluginInterface(m_sampleSourceDevices[index].m_plugin); - - return index; -} - -int PluginManager::selectSampleSourceBySerialOrSequence(const QString& sourceId, const QString& sourceSerial, uint32_t sourceSequence, DeviceSourceAPI *deviceAPI) -{ - qDebug("PluginManager::selectSampleSourceBySequence by sequence: id: %s ser: %s seq: %d", qPrintable(sourceId), qPrintable(sourceSerial), sourceSequence); - - int index = -1; - int index_matchingSequence = -1; - int index_firstOfKind = -1; - - for (int i = 0; i < m_sampleSourceDevices.count(); i++) - { - if (m_sampleSourceDevices[i].m_deviceId == sourceId) - { - index_firstOfKind = i; - - if (m_sampleSourceDevices[i].m_deviceSerial == sourceSerial) - { - index = i; // exact match - break; - } - - if (m_sampleSourceDevices[i].m_deviceSequence == sourceSequence) - { - index_matchingSequence = i; - } - } - } - - if(index == -1) // no exact match - { - if (index_matchingSequence == -1) // no matching sequence - { - if (index_firstOfKind == -1) // no matching device type - { - if(m_sampleSourceDevices.count() > 0) // take first if any - { - index = 0; - } - else - { - return -1; // return if no device attached - } - } - else - { - index = index_firstOfKind; // take first that matches device type - } - } - else - { - index = index_matchingSequence; // take the one that matches the sequence in the device type - } - } - - qDebug() << "PluginManager::selectSampleSourceBySequence: m_sampleSource at index " << index - << " hid: " << m_sampleSourceDevices[index].m_hadrwareId.toStdString().c_str() - << " id: " << m_sampleSourceDevices[index].m_deviceId.toStdString().c_str() - << " ser: " << m_sampleSourceDevices[index].m_deviceSerial.toStdString().c_str() - << " seq: " << m_sampleSourceDevices[index].m_deviceSequence; - - deviceAPI->stopAcquisition(); - - // m_sampleSourcePluginGUI = pluginGUI; - deviceAPI->setSampleSourceSequence(m_sampleSourceDevices[index].m_deviceSequence); - deviceAPI->setHardwareId(m_sampleSourceDevices[index].m_hadrwareId); - deviceAPI->setSampleSourceId(m_sampleSourceDevices[index].m_deviceId); - deviceAPI->setSampleSourceSerial(m_sampleSourceDevices[index].m_deviceSerial); - deviceAPI->setSampleSourceDisplayName(m_sampleSourceDevices[index].m_displayName); - deviceAPI->setSampleSourcePluginInterface(m_sampleSourceDevices[index].m_plugin); - - return index; -} - -int PluginManager::selectSampleSinkBySerialOrSequence(const QString& sinkId, const QString& sinkSerial, uint32_t sinkSequence, DeviceSinkAPI *deviceAPI) -{ - qDebug("PluginManager::selectSampleSinkBySerialOrSequence by sequence: id: %s ser: %s seq: %d", qPrintable(sinkId), qPrintable(sinkSerial), sinkSequence); - - int index = -1; - int index_matchingSequence = -1; - int index_firstOfKind = -1; - - for (int i = 0; i < m_sampleSinkDevices.count(); i++) - { - if (m_sampleSinkDevices[i].m_deviceId == sinkId) - { - index_firstOfKind = i; - - if (m_sampleSinkDevices[i].m_deviceSerial == sinkSerial) - { - index = i; // exact match - break; - } - - if (m_sampleSinkDevices[i].m_deviceSequence == sinkSequence) - { - index_matchingSequence = i; - } - } - } - - if(index == -1) // no exact match - { - if (index_matchingSequence == -1) // no matching sequence - { - if (index_firstOfKind == -1) // no matching device type - { - if(m_sampleSinkDevices.count() > 0) // take first if any - { - index = 0; - } - else - { - return -1; // return if no device attached - } - } - else - { - index = index_firstOfKind; // take first that matches device type - } - } - else - { - index = index_matchingSequence; // take the one that matches the sequence in the device type - } - } - - qDebug() << "PluginManager::selectSampleSinkBySerialOrSequence: m_sampleSink at index " << index - << " hid: " << m_sampleSinkDevices[index].m_hadrwareId.toStdString().c_str() - << " id: " << m_sampleSinkDevices[index].m_deviceId.toStdString().c_str() - << " ser: " << m_sampleSinkDevices[index].m_deviceSerial.toStdString().c_str() - << " seq: " << m_sampleSinkDevices[index].m_deviceSequence; - - deviceAPI->stopGeneration(); - - // m_sampleSourcePluginGUI = pluginGUI; - deviceAPI->setSampleSinkSequence(m_sampleSinkDevices[index].m_deviceSequence); - deviceAPI->setHardwareId(m_sampleSinkDevices[index].m_hadrwareId); - deviceAPI->setSampleSinkId(m_sampleSinkDevices[index].m_deviceId); - deviceAPI->setSampleSinkSerial(m_sampleSinkDevices[index].m_deviceSerial); - deviceAPI->setSampleSinkDisplayName(m_sampleSinkDevices[index].m_displayName); - deviceAPI->setSampleSinkPluginInterface(m_sampleSinkDevices[index].m_plugin); - - return index; -} - -void PluginManager::selectSampleSourceByDevice(void *devicePtr, DeviceSourceAPI *deviceAPI) -{ - SamplingDevice *sampleSourceDevice = (SamplingDevice *) devicePtr; - - qDebug() << "PluginManager::selectSampleSourceByDevice: " - << " hid: " << sampleSourceDevice->m_hadrwareId.toStdString().c_str() - << " id: " << sampleSourceDevice->m_deviceId.toStdString().c_str() - << " ser: " << sampleSourceDevice->m_deviceSerial.toStdString().c_str() - << " seq: " << sampleSourceDevice->m_deviceSequence; - - // m_sampleSourcePluginGUI = pluginGUI; - deviceAPI->setSampleSourceSequence(sampleSourceDevice->m_deviceSequence); - deviceAPI->setHardwareId(sampleSourceDevice->m_hadrwareId); - deviceAPI->setSampleSourceId(sampleSourceDevice->m_deviceId); - deviceAPI->setSampleSourceSerial(sampleSourceDevice->m_deviceSerial); - deviceAPI->setSampleSourceDisplayName(sampleSourceDevice->m_displayName); - deviceAPI->setSampleSourcePluginInterface(sampleSourceDevice->m_plugin); -} - -void PluginManager::selectSampleSinkByDevice(void *devicePtr, DeviceSinkAPI *deviceAPI) -{ - SamplingDevice *sampleSinkDevice = (SamplingDevice *) devicePtr; - - qDebug() << "PluginManager::selectSampleSinkByDevice: " - << " hid: " << sampleSinkDevice->m_hadrwareId.toStdString().c_str() - << " id: " << sampleSinkDevice->m_deviceId.toStdString().c_str() - << " ser: " << sampleSinkDevice->m_deviceSerial.toStdString().c_str() - << " seq: " << sampleSinkDevice->m_deviceSequence; - - // m_sampleSourcePluginGUI = pluginGUI; - deviceAPI->setSampleSinkSequence(sampleSinkDevice->m_deviceSequence); - deviceAPI->setHardwareId(sampleSinkDevice->m_hadrwareId); - deviceAPI->setSampleSinkId(sampleSinkDevice->m_deviceId); - deviceAPI->setSampleSinkSerial(sampleSinkDevice->m_deviceSerial); - deviceAPI->setSampleSinkDisplayName(sampleSinkDevice->m_displayName); - deviceAPI->setSampleSinkPluginInterface(sampleSinkDevice->m_plugin); + m_sampleSinkRegistrations.append(PluginAPI::SamplingDeviceRegistration(sinkName, plugin)); } void PluginManager::loadPlugins(const QDir& dir) @@ -566,15 +155,6 @@ void PluginManager::loadPlugins(const QDir& dir) } } -PluginInterface* PluginManager::getPluginInterfaceAt(int index) -{ - if (index < m_sampleSourceDevices.size()) { - return m_sampleSourceDevices[index].m_plugin; - } else { - return 0; - } -} - void PluginManager::listTxChannels(QList& list) { list.clear(); diff --git a/sdrbase/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h index c0abb24e9..a49c634b9 100644 --- a/sdrbase/plugin/pluginmanager.h +++ b/sdrbase/plugin/pluginmanager.h @@ -51,45 +51,21 @@ public: void registerTxChannel(const QString& channelName, PluginInterface* plugin); void registerSampleSink(const QString& sourceName, PluginInterface* plugin); + PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; } + PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; } PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; } PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; } - void updateSampleSourceDevices(); - void duplicateLocalSampleSourceDevices(uint deviceUID); - void fillSampleSourceSelector(QComboBox* comboBox, uint deviceUID); - - void updateSampleSinkDevices(); - void duplicateLocalSampleSinkDevices(uint deviceUID); - void fillSampleSinkSelector(QComboBox* comboBox, uint deviceUID); - - int selectSampleSourceByIndex(int index, DeviceSourceAPI *deviceAPI); - int selectSampleSourceBySerialOrSequence(const QString& sourceId, const QString& sourceSerial, uint32_t sourceSequence, DeviceSourceAPI *deviceAPI); - void selectSampleSourceByDevice(void *devicePtr, DeviceSourceAPI *deviceAPI); - - int selectSampleSinkBySerialOrSequence(const QString& sinkId, const QString& sinkSerial, uint32_t sinkSequence, DeviceSinkAPI *deviceAPI); - void selectSampleSinkByDevice(void *devicePtr, DeviceSinkAPI *deviceAPI); - - PluginInterface* getPluginInterfaceAt(int index); - void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); void listRxChannels(QList& list); void createTxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet); void listTxChannels(QList& list); + static const QString& getFileSourceDeviceId() { return m_fileSourceDeviceTypeID; } + static const QString& getFileSinkDeviceId() { return m_fileSinkDeviceTypeID; } + private: - struct SamplingDeviceRegistration //!< This is the channel registration - { - QString m_deviceId; - PluginInterface* m_plugin; - SamplingDeviceRegistration(const QString& deviceId, PluginInterface* plugin) : - m_deviceId(deviceId), - m_plugin(plugin) - { } - }; - - typedef QList SamplingDeviceRegistrations; - struct SamplingDevice { //!< This is the device registration PluginInterface* m_plugin; QString m_displayName; @@ -118,13 +94,11 @@ private: PluginAPI m_pluginAPI; Plugins m_plugins; - PluginAPI::ChannelRegistrations m_rxChannelRegistrations; //!< Channel plugins register here - SamplingDeviceRegistrations m_sampleSourceRegistrations; //!< Input source plugins (one per device kind) register here - SamplingDevices m_sampleSourceDevices; //!< Instances of input sources present in the system + PluginAPI::ChannelRegistrations m_rxChannelRegistrations; //!< Channel plugins register here + PluginAPI::SamplingDeviceRegistrations m_sampleSourceRegistrations; //!< Input source plugins (one per device kind) register here - PluginAPI::ChannelRegistrations m_txChannelRegistrations; //!< Channel plugins register here - SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here - SamplingDevices m_sampleSinkDevices; //!< Instances of output sinks present in the system + PluginAPI::ChannelRegistrations m_txChannelRegistrations; //!< Channel plugins register here + PluginAPI::SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here // "Local" sample source device IDs static const QString m_sdrDaemonSourceHardwareID; //!< SDRdaemon source hardware ID diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index d5659745b..ea261ca06 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -25,6 +25,8 @@ public: Preset* getWorkingPreset() { return &m_workingPreset; } int getSourceIndex() const { return m_preferences.getSourceIndex(); } void setSourceIndex(int value) { m_preferences.setSourceIndex(value); } + const QString& getSourceDeviceId() const { return m_preferences.getSourceDevice(); } + void setSourceDeviceId(const QString& deviceId) { m_preferences.setSourceDevice(deviceId); } void setLatitude(float latitude) { m_preferences.setLatitude(latitude); } void setLongitude(float longitude) { m_preferences.setLongitude(longitude); } diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index a7bfc1b8f..efb8149df 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -29,6 +29,7 @@ set(sdrgui_SOURCES gui/presetitem.cpp gui/rollupwidget.cpp gui/samplingdevicecontrol.cpp + gui/samplingdevicedialog.cpp gui/scale.cpp gui/scaleengine.cpp gui/transverterbutton.cpp @@ -75,6 +76,7 @@ set(sdrgui_HEADERS gui/presetitem.h gui/rollupwidget.h gui/samplingdevicecontrol.h + gui/samplingdevicedialog.h gui/scale.h gui/scaleengine.h gui/transverterbutton.h @@ -111,6 +113,7 @@ set(sdrgui_FORMS gui/pluginsdialog.ui gui/audiodialog.ui gui/samplingdevicecontrol.ui + gui/samplingdevicedialog.ui gui/myposdialog.ui gui/transverterdialog.ui ) diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index db255fada..175e14994 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -32,7 +32,7 @@ #include "deviceuiset.h" -DeviceUISet::DeviceUISet(int tabIndex, QTimer& timer) +DeviceUISet::DeviceUISet(int tabIndex, bool rxElseTx, QTimer& timer) { m_spectrum = new GLSpectrum; m_spectrumVis = new SpectrumVis(m_spectrum); @@ -40,7 +40,7 @@ DeviceUISet::DeviceUISet(int tabIndex, QTimer& timer) m_spectrumGUI = new GLSpectrumGUI; m_spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, m_spectrum); m_channelWindow = new ChannelWindow; - m_samplingDeviceControl = new SamplingDeviceControl(tabIndex); + m_samplingDeviceControl = new SamplingDeviceControl(tabIndex, rxElseTx); m_deviceSourceEngine = 0; m_deviceSourceAPI = 0; m_deviceSinkEngine = 0; diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 0e33d53f5..74e2cd13d 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -45,7 +45,7 @@ struct DeviceUISet DeviceSinkAPI *m_deviceSinkAPI; QByteArray m_mainWindowState; - DeviceUISet(int tabIndex, QTimer& timer); + DeviceUISet(int tabIndex, bool rxElseTx, QTimer& timer); ~DeviceUISet(); GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter diff --git a/sdrgui/gui/samplingdevicecontrol.cpp b/sdrgui/gui/samplingdevicecontrol.cpp index cf68101f9..cf56f7641 100644 --- a/sdrgui/gui/samplingdevicecontrol.cpp +++ b/sdrgui/gui/samplingdevicecontrol.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2015-2017 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 // @@ -14,17 +14,20 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "gui/samplingdevicecontrol.h" -#include "gui/pluginsdialog.h" +#include "samplingdevicecontrol.h" +#include "samplingdevicedialog.h" #include "plugin/pluginmanager.h" +#include "device/deviceenumerator.h" #include "ui_samplingdevicecontrol.h" -SamplingDeviceControl::SamplingDeviceControl(int tabIndex, QWidget* parent) : +SamplingDeviceControl::SamplingDeviceControl(int tabIndex, bool rxElseTx, QWidget* parent) : QWidget(parent), ui(new Ui::SamplingDeviceControl), m_pluginManager(0), - m_deviceTabIndex(tabIndex) + m_deviceTabIndex(tabIndex), + m_rxElseTx(rxElseTx), + m_selectedDeviceIndex(-1) { ui->setupUi(this); } @@ -34,14 +37,40 @@ SamplingDeviceControl::~SamplingDeviceControl() delete ui; } -QComboBox *SamplingDeviceControl::getDeviceSelector() +void SamplingDeviceControl::on_deviceChange_clicked() { - return ui->deviceSelect; + SamplingDeviceDialog dialog(m_rxElseTx, m_deviceTabIndex, this); + dialog.exec(); + + if (dialog.getSelectedDeviceIndex() >= 0) + { + m_selectedDeviceIndex = dialog.getSelectedDeviceIndex(); + setSelectedDeviceIndex(m_selectedDeviceIndex); + emit changed(); + } } -QPushButton *SamplingDeviceControl::getDeviceSelectionConfirm() +void SamplingDeviceControl::on_deviceReload_clicked() { - return ui->deviceConfirm; + if (m_selectedDeviceIndex >= 0) { + emit changed(); + } +} + +void SamplingDeviceControl::setSelectedDeviceIndex(int index) +{ + if (m_rxElseTx) + { + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(index); + ui->deviceSelectedText->setText(samplingDevice.displayedName); + } + else + { + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(index); + ui->deviceSelectedText->setText(samplingDevice.displayedName); + } + + m_selectedDeviceIndex = index; } QComboBox *SamplingDeviceControl::getChannelSelector() diff --git a/sdrgui/gui/samplingdevicecontrol.h b/sdrgui/gui/samplingdevicecontrol.h index 58dea4932..621d26654 100644 --- a/sdrgui/gui/samplingdevicecontrol.h +++ b/sdrgui/gui/samplingdevicecontrol.h @@ -37,19 +37,29 @@ class SDRANGEL_API SamplingDeviceControl : public QWidget { Q_OBJECT public: - explicit SamplingDeviceControl(int tabIndex, QWidget* parent = 0); + explicit SamplingDeviceControl(int tabIndex, bool rxElseTx, QWidget* parent = 0); ~SamplingDeviceControl(); + int getSelectedDeviceIndex() const { return m_selectedDeviceIndex; } + void setSelectedDeviceIndex(int index); + void setPluginManager(PluginManager *pluginManager) { m_pluginManager = pluginManager; } - QComboBox *getDeviceSelector(); - QPushButton *getDeviceSelectionConfirm(); QComboBox *getChannelSelector(); QPushButton *getAddChannelButton(); +private slots: + void on_deviceChange_clicked(); + void on_deviceReload_clicked(); + private: Ui::SamplingDeviceControl* ui; PluginManager *m_pluginManager; int m_deviceTabIndex; + bool m_rxElseTx; + int m_selectedDeviceIndex; + +signals: + void changed(); }; diff --git a/sdrgui/gui/samplingdevicecontrol.ui b/sdrgui/gui/samplingdevicecontrol.ui index 6afb39add..3866aa90e 100644 --- a/sdrgui/gui/samplingdevicecontrol.ui +++ b/sdrgui/gui/samplingdevicecontrol.ui @@ -35,20 +35,29 @@ 3 - + + 2 + + + 2 + + + 2 + + 2 - - - Select sampling device + + + Device - + 24 @@ -62,14 +71,34 @@ - Confirm and change sampling device + Change sampling device - :/checkmark.png:/checkmark.png + :/choose.png:/choose.png + + + + + + + + 24 + 16777215 + + + + Reload sampling device + + + + + + + :/recycle.png:/recycle.png diff --git a/sdrgui/gui/samplingdevicedialog.cpp b/sdrgui/gui/samplingdevicedialog.cpp new file mode 100644 index 000000000..752fba4fe --- /dev/null +++ b/sdrgui/gui/samplingdevicedialog.cpp @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 F4EXB // +// written by Edouard Griffiths // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// 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 // +// // +// 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 "samplingdevicedialog.h" +#include "ui_samplingdevicedialog.h" +#include "device/deviceenumerator.h" + + +SamplingDeviceDialog::SamplingDeviceDialog(bool rxElseTx, int deviceTabIndex, QWidget* parent) : + QDialog(parent), + ui(new Ui::SamplingDeviceDialog), + m_rxElseTx(rxElseTx), + m_deviceTabIndex(deviceTabIndex), + m_selectedDeviceIndex(-1) +{ + ui->setupUi(this); + + QList deviceDisplayNames; + + if (m_rxElseTx) { + DeviceEnumerator::instance()->listRxDeviceNames(deviceDisplayNames, m_deviceIndexes); + } else { + DeviceEnumerator::instance()->listTxDeviceNames(deviceDisplayNames, m_deviceIndexes); + } + + QStringList devicesNamesList(deviceDisplayNames); + ui->deviceSelect->addItems(devicesNamesList); +} + +SamplingDeviceDialog::~SamplingDeviceDialog() +{ + delete ui; +} + +void SamplingDeviceDialog::accept() +{ + m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()]; + + if (m_rxElseTx) { + DeviceEnumerator::instance()->changeRxSelection(m_deviceTabIndex, m_selectedDeviceIndex); + } else { + DeviceEnumerator::instance()->changeTxSelection(m_deviceTabIndex, m_selectedDeviceIndex); + } + + QDialog::accept(); +} diff --git a/sdrgui/gui/samplingdevicedialog.h b/sdrgui/gui/samplingdevicedialog.h new file mode 100644 index 000000000..6e771fa33 --- /dev/null +++ b/sdrgui/gui/samplingdevicedialog.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 F4EXB // +// written by Edouard Griffiths // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// 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 // +// // +// 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 SDRGUI_GUI_SAMPLINGDEVICEDIALOG_H_ +#define SDRGUI_GUI_SAMPLINGDEVICEDIALOG_H_ + +#include +#include + +namespace Ui { + class SamplingDeviceDialog; +} + +class SamplingDeviceDialog : public QDialog { + Q_OBJECT + +public: + explicit SamplingDeviceDialog(bool rxElseTx, int deviceTabIndex, QWidget* parent = 0); + ~SamplingDeviceDialog(); + int getSelectedDeviceIndex() const { return m_selectedDeviceIndex; } + +private: + Ui::SamplingDeviceDialog* ui; + bool m_rxElseTx; + int m_deviceTabIndex; + int m_selectedDeviceIndex; + std::vector m_deviceIndexes; + +private slots: + void accept(); +}; + +#endif /* SDRGUI_GUI_SAMPLINGDEVICEDIALOG_H_ */ diff --git a/sdrgui/gui/samplingdevicedialog.ui b/sdrgui/gui/samplingdevicedialog.ui new file mode 100644 index 000000000..7555e1677 --- /dev/null +++ b/sdrgui/gui/samplingdevicedialog.ui @@ -0,0 +1,91 @@ + + + SamplingDeviceDialog + + + + 0 + 0 + 324 + 139 + + + + + Sans Serif + 9 + + + + Select sampling device + + + + + + + 16777215 + 70 + + + + Select from list + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + + + + + buttonBox + accepted() + SamplingDeviceDialog + accept() + + + 257 + 194 + + + 157 + 203 + + + + + buttonBox + rejected() + SamplingDeviceDialog + reject() + + + 314 + 194 + + + 286 + 203 + + + + + diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 57d2d8d08..2c853a0eb 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -30,6 +30,7 @@ #include "device/devicesourceapi.h" #include "device/devicesinkapi.h" #include "device/deviceuiset.h" +#include "device/deviceenumerator.h" #include "audio/audiodeviceinfo.h" #include "gui/indicator.h" #include "gui/presetitem.h" @@ -138,37 +139,43 @@ MainWindow::MainWindow(QWidget* parent) : qDebug() << "MainWindow::MainWindow: select SampleSource from settings..."; - int sampleSourceIndex = m_settings.getSourceIndex(); - sampleSourceIndex = m_pluginManager->selectSampleSourceByIndex(sampleSourceIndex, m_deviceUIs.back()->m_deviceSourceAPI); + int deviceIndex = DeviceEnumerator::instance()->getRxSamplingDeviceIndex(m_settings.getSourceDeviceId(), m_settings.getSourceIndex()); - if (sampleSourceIndex < 0) + if (deviceIndex >= 0) { - qCritical("MainWindow::MainWindow: no sample source. Exit"); - exit(0); + // delete previous plugin GUI + m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); + m_deviceUIs.back()->m_deviceSourceAPI->resetSampleSourceId(); + m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()); + m_deviceUIs.back()->m_deviceSourceAPI->clearBuddiesLists(); // clear old API buddies lists + + m_deviceUIs.back()->m_samplingDeviceControl->setSelectedDeviceIndex(deviceIndex); + + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceIndex); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceDisplayName(samplingDevice.displayedName); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(deviceIndex)); + + DeviceSampleSource *source = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), m_deviceUIs.back()->m_deviceSourceAPI); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); + QWidget *gui; + PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), + &gui, + m_deviceUIs.back()); + m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); + setDeviceGUI(0, gui, m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceDisplayName()); } - // delete previous plugin GUI - m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); - - DeviceSampleSource *source = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), m_deviceUIs.back()->m_deviceSourceAPI); - m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); - QWidget *gui; - PluginInstanceGUI *pluginGUI = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), - &gui, - m_deviceUIs.back()); - m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); - setDeviceGUI(0, gui, m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceDisplayName()); - m_deviceUIs.back()->m_deviceSourceAPI->setBuddyLeader(true); // the first device is always the leader - bool sampleSourceSignalsBlocked = m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(true); - m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->setCurrentIndex(sampleSourceIndex); - m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(sampleSourceSignalsBlocked); - qDebug() << "MainWindow::MainWindow: load current preset settings..."; loadPresetSettings(m_settings.getWorkingPreset(), 0); @@ -205,7 +212,7 @@ void MainWindow::addSourceDevice() sprintf(uidCStr, "UID:%d", dspDeviceSourceEngineUID); int deviceTabIndex = m_deviceUIs.size(); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_masterTimer)); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, true, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = dspDeviceSourceEngine; char tabNameCStr[16]; @@ -227,24 +234,28 @@ void MainWindow::addSourceDevice() ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); - bool sampleSourceSignalsBlocked = m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(true); - m_pluginManager->duplicateLocalSampleSourceDevices(dspDeviceSourceEngineUID); - // FIXME: replace with the device selection dialog based on static enumeration - m_pluginManager->fillSampleSourceSelector(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector(), dspDeviceSourceEngineUID); + connect(m_deviceUIs.back()->m_samplingDeviceControl, SIGNAL(changed()), this, SLOT(on_sampleSource_changed())); - connect(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelectionConfirm(), SIGNAL(clicked(bool)), this, SLOT(on_sampleSource_confirmClicked(bool))); - - m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(sampleSourceSignalsBlocked); ui->tabInputsSelect->addTab(m_deviceUIs.back()->m_samplingDeviceControl, tabNameCStr); ui->tabInputsSelect->setTabToolTip(deviceTabIndex, QString(uidCStr)); // Create a file source instance by default - m_pluginManager->selectSampleSourceBySerialOrSequence("sdrangel.samplesource.filesource", "0", 0, m_deviceUIs.back()->m_deviceSourceAPI); + int fileSourceDeviceIndex = DeviceEnumerator::instance()->getFileSourceDeviceIndex(); + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileSourceDeviceIndex); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceDisplayName(samplingDevice.displayedName); + m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(fileSourceDeviceIndex)); + + m_deviceUIs.back()->m_samplingDeviceControl->setSelectedDeviceIndex(fileSourceDeviceIndex); // delete previous plugin GUI m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourcePluginInstanceGUI()); + DeviceSampleSource *source = m_deviceUIs.back()->m_deviceSourceAPI->getPluginInterface()->createSampleSourcePluginInstanceInput( m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceId(), m_deviceUIs.back()->m_deviceSourceAPI); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSource(source); @@ -256,7 +267,6 @@ void MainWindow::addSourceDevice() m_deviceUIs.back()->m_deviceSourceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourcePluginInstanceGUI(pluginGUI); setDeviceGUI(deviceTabIndex, gui, m_deviceUIs.back()->m_deviceSourceAPI->getSampleSourceDisplayName()); - } void MainWindow::addSinkDevice() @@ -269,7 +279,7 @@ void MainWindow::addSinkDevice() sprintf(uidCStr, "UID:%d", dspDeviceSinkEngineUID); int deviceTabIndex = m_deviceUIs.size(); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_masterTimer)); + m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, false, m_masterTimer)); m_deviceUIs.back()->m_deviceSourceEngine = 0; m_deviceUIs.back()->m_deviceSinkEngine = dspDeviceSinkEngine; @@ -293,19 +303,22 @@ void MainWindow::addSinkDevice() ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); - bool sampleSourceSignalsBlocked = m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(true); - m_pluginManager->duplicateLocalSampleSinkDevices(dspDeviceSinkEngineUID); - // FIXME: replace with the device selection dialog based on static enumeration - m_pluginManager->fillSampleSinkSelector(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector(), dspDeviceSinkEngineUID); + connect(m_deviceUIs.back()->m_samplingDeviceControl, SIGNAL(changed()), this, SLOT(on_sampleSink_changed())); - connect(m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelectionConfirm(), SIGNAL(clicked(bool)), this, SLOT(on_sampleSink_confirmClicked(bool))); - - m_deviceUIs.back()->m_samplingDeviceControl->getDeviceSelector()->blockSignals(sampleSourceSignalsBlocked); ui->tabInputsSelect->addTab(m_deviceUIs.back()->m_samplingDeviceControl, tabNameCStr); ui->tabInputsSelect->setTabToolTip(deviceTabIndex, QString(uidCStr)); // create a file sink by default - m_pluginManager->selectSampleSinkBySerialOrSequence("sdrangel.samplesink.filesink", "0", 0, m_deviceUIs.back()->m_deviceSinkAPI); + int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileSinkDeviceIndex(); + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); + m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSinkAPI->setHardwareId(samplingDevice.hardwareId); + m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkId(samplingDevice.id); + m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkSerial(samplingDevice.serial); + m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkDisplayName(samplingDevice.displayedName); + m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkPluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(fileSinkDeviceIndex)); + + m_deviceUIs.back()->m_samplingDeviceControl->setSelectedDeviceIndex(fileSinkDeviceIndex); // delete previous plugin GUI if it exists m_deviceUIs.back()->m_deviceSinkAPI->getPluginInterface()->deleteSampleSourcePluginInstanceGUI( @@ -862,7 +875,7 @@ void MainWindow::on_action_DV_Serial_triggered(bool checked) } } -void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unused))) +void MainWindow::on_sampleSource_changed() { // Do it in the currently selected source tab int currentSourceTabIndex = ui->tabInputsSelect->currentIndex(); @@ -872,8 +885,6 @@ void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unus qDebug("MainWindow::on_sampleSource_confirmClicked: tab at %d", currentSourceTabIndex); DeviceUISet *deviceUI = m_deviceUIs[currentSourceTabIndex]; deviceUI->m_deviceSourceAPI->saveSourceSettings(m_settings.getWorkingPreset()); // save old API settings - int selectedComboIndex = deviceUI->m_samplingDeviceControl->getDeviceSelector()->currentIndex(); - void *devicePtr = deviceUI->m_samplingDeviceControl->getDeviceSelector()->itemData(selectedComboIndex).value(); deviceUI->m_deviceSourceAPI->stopAcquisition(); // deletes old UI and input object @@ -885,7 +896,13 @@ void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unus deviceUI->m_deviceSourceAPI->getSampleSource()); deviceUI->m_deviceSourceAPI->clearBuddiesLists(); // clear old API buddies lists - m_pluginManager->selectSampleSourceByDevice(devicePtr, deviceUI->m_deviceSourceAPI); // sets the new API + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); + deviceUI->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + deviceUI->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); + deviceUI->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); + deviceUI->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); + deviceUI->m_deviceSourceAPI->setSampleSourceDisplayName(samplingDevice.displayedName); + deviceUI->m_deviceSourceAPI->setSampleSourcePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex())); // add to buddies list std::vector::iterator it = m_deviceUIs.begin(); @@ -936,14 +953,15 @@ void MainWindow::on_sampleSource_confirmClicked(bool checked __attribute__((unus deviceUI->m_deviceSourceAPI->loadSourceSettings(m_settings.getWorkingPreset()); // load new API settings - if (currentSourceTabIndex == 0) + if (currentSourceTabIndex == 0) // save as default starting device { - m_settings.setSourceIndex(deviceUI->m_samplingDeviceControl->getDeviceSelector()->currentIndex()); + m_settings.setSourceIndex(samplingDevice.sequence); + m_settings.setSourceDeviceId(samplingDevice.id); } } } -void MainWindow::on_sampleSink_confirmClicked(bool checked __attribute__((unused))) +void MainWindow::on_sampleSink_changed() { // Do it in the currently selected source tab int currentSinkTabIndex = ui->tabInputsSelect->currentIndex(); @@ -953,8 +971,6 @@ void MainWindow::on_sampleSink_confirmClicked(bool checked __attribute__((unused qDebug("MainWindow::on_sampleSink_confirmClicked: tab at %d", currentSinkTabIndex); DeviceUISet *deviceUI = m_deviceUIs[currentSinkTabIndex]; deviceUI->m_deviceSinkAPI->saveSinkSettings(m_settings.getWorkingPreset()); // save old API settings - int selectedComboIndex = deviceUI->m_samplingDeviceControl->getDeviceSelector()->currentIndex(); - void *devicePtr = deviceUI->m_samplingDeviceControl->getDeviceSelector()->itemData(selectedComboIndex).value(); deviceUI->m_deviceSinkAPI->stopGeneration(); // deletes old UI and output object @@ -966,7 +982,13 @@ void MainWindow::on_sampleSink_confirmClicked(bool checked __attribute__((unused deviceUI->m_deviceSinkAPI->getSampleSink()); deviceUI->m_deviceSinkAPI->clearBuddiesLists(); // clear old API buddies lists - m_pluginManager->selectSampleSinkByDevice(devicePtr, deviceUI->m_deviceSinkAPI); // sets the new API + PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); + deviceUI->m_deviceSinkAPI->setSampleSinkSequence(samplingDevice.sequence); + deviceUI->m_deviceSinkAPI->setHardwareId(samplingDevice.hardwareId); + deviceUI->m_deviceSinkAPI->setSampleSinkId(samplingDevice.id); + deviceUI->m_deviceSinkAPI->setSampleSinkSerial(samplingDevice.serial); + deviceUI->m_deviceSinkAPI->setSampleSinkDisplayName(samplingDevice.displayedName); + deviceUI->m_deviceSinkAPI->setSampleSinkPluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex())); // add to buddies list std::vector::iterator it = m_deviceUIs.begin(); @@ -1043,7 +1065,6 @@ void MainWindow::on_channel_addClicked(bool checked __attribute__((unused))) m_pluginManager->createTxChannelInstance(deviceUI->m_samplingDeviceControl->getChannelSelector()->currentIndex(), deviceUI); } } - } void MainWindow::on_action_About_triggered() @@ -1074,62 +1095,6 @@ void MainWindow::on_action_reloadDevices_triggered() { QMessageBox::information(this, tr("Message"), tr("Not implemented")); return; - -// // all devices must be stopped -// std::vector::iterator it = m_deviceUIs.begin(); -// for (; it != m_deviceUIs.end(); ++it) -// { -// if ((*it)->m_deviceSourceEngine) // it is a source device -// { -// if ((*it)->m_deviceSourceEngine->state() == DSPDeviceSourceEngine::StRunning) -// { -// QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); -// return; -// } -// } -// -// if ((*it)->m_deviceSinkEngine) // it is a sink device -// { -// if ((*it)->m_deviceSinkEngine->state() == DSPDeviceSinkEngine::StRunning) -// { -// QMessageBox::information(this, tr("Message"), tr("Stop all devices for reload to take effect")); -// return; -// } -// } -// } -// -// // re-scan devices -// m_pluginManager->updateSampleSourceDevices(); -// m_pluginManager->updateSampleSinkDevices(); -// -// // re-populate device selectors keeping the same selection -// it = m_deviceUIs.begin(); -// for (; it != m_deviceUIs.end(); ++it) -// { -// if ((*it)->m_deviceSourceEngine) // it is a source device -// { -// QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); -// bool sampleSourceSignalsBlocked = deviceSelectorComboBox->blockSignals(true); -// uint dspDeviceSourceEngineUID = (*it)->m_deviceSourceEngine->getUID(); -// m_pluginManager->duplicateLocalSampleSourceDevices(dspDeviceSourceEngineUID); -// m_pluginManager->fillSampleSourceSelector(deviceSelectorComboBox, dspDeviceSourceEngineUID); -// int newIndex = m_pluginManager->getSampleSourceSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSourceAPI); -// deviceSelectorComboBox->setCurrentIndex(newIndex); -// deviceSelectorComboBox->blockSignals(sampleSourceSignalsBlocked); -// } -// -// if ((*it)->m_deviceSinkEngine) // it is a sink device -// { -// QComboBox *deviceSelectorComboBox = (*it)->m_samplingDeviceControl->getDeviceSelector(); -// bool sampleSinkSignalsBlocked = deviceSelectorComboBox->blockSignals(true); -// uint dspDeviceSinkEngineUID = (*it)->m_deviceSinkEngine->getUID(); -// m_pluginManager->duplicateLocalSampleSinkDevices(dspDeviceSinkEngineUID); -// m_pluginManager->fillSampleSinkSelector(deviceSelectorComboBox, dspDeviceSinkEngineUID); -// int newIndex = m_pluginManager->getSampleSinkSelectorIndex(deviceSelectorComboBox, (*it)->m_deviceSinkAPI); -// deviceSelectorComboBox->setCurrentIndex(newIndex); -// deviceSelectorComboBox->blockSignals(sampleSinkSignalsBlocked); -// } -// } } void MainWindow::on_action_Exit_triggered() diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index b626a6812..e5b37b832 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -140,8 +140,8 @@ private slots: void on_action_Audio_triggered(); void on_action_DV_Serial_triggered(bool checked); void on_action_My_Position_triggered(); - void on_sampleSource_confirmClicked(bool checked); - void on_sampleSink_confirmClicked(bool checked); + void on_sampleSource_changed(); + void on_sampleSink_changed(); void on_channel_addClicked(bool checked); void on_action_Loaded_Plugins_triggered(); void on_action_About_triggered(); diff --git a/sdrgui/resources/choose.png b/sdrgui/resources/choose.png new file mode 100644 index 0000000000000000000000000000000000000000..25193d6e7e1d4d4c08439c8a152e8342bce3b45b GIT binary patch literal 602 zcmV-g0;T?}mEilBvpzy1I_ z(LxO(Ckj?42wG_28Bh>T!o@p`ek^>alXdTMx5jU?v$Her^WMyRGYgs2ygi{FQ(veZ zwW;1w_wuh&r_{^-(ns~Kx}dhyWp%F1sX>j8sVBnt19gYGTivA|QeUYJ^|bGVO`>=dR%>{E~*#R!_i(#?Rd7-Q{m`efJwDhnDj*b>~MOnPL^%*ci>H-;PwNX zhCB?$yL(#%w1F#yBI}jPeNUUbc18;6SqHANj#}kiN~r_f1Ul;qh!pMiy#;Rnmk0v3 z6j4;w*HSh(U$wW!Xi6J_&%0)9_uD`q7anE(I)07*qoM6N<$f>GuL0RR91 literal 0 HcmV?d00001 diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index 816192ae1..ce9b3853b 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -79,5 +79,6 @@ stream.png antenna.png link.png + choose.png From 31c7ebf36cdd5889a485e15c5b82112ff80f6ce0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 03:42:59 +0100 Subject: [PATCH 30/39] Enumerate file and sdrdaemon plugins the new way --- .../samplesink/filesink/filesinkplugin.cpp | 23 ++++++++----------- .../sdrdaemonsink/sdrdaemonsinkplugin.cpp | 23 ++++++++----------- .../filesource/filesourceplugin.cpp | 23 ++++++++----------- .../sdrdaemonsource/sdrdaemonsourceplugin.cpp | 23 ++++++++----------- 4 files changed, 36 insertions(+), 56 deletions(-) diff --git a/plugins/samplesink/filesink/filesinkplugin.cpp b/plugins/samplesink/filesink/filesinkplugin.cpp index 34cc827d5..ba328713b 100644 --- a/plugins/samplesink/filesink/filesinkplugin.cpp +++ b/plugins/samplesink/filesink/filesinkplugin.cpp @@ -53,21 +53,16 @@ void FileSinkPlugin::initPlugin(PluginAPI* pluginAPI) PluginInterface::SamplingDevices FileSinkPlugin::enumSampleSinks() { SamplingDevices result; - int count = 1; - for(int i = 0; i < count; i++) - { - QString displayedName(QString("FileSink[%1]").arg(i)); - - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString::null, - i, - PluginInterface::SamplingDevice::BuiltInDevice, - false, - 0)); - } + result.append(SamplingDevice( + "FileSink", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + false, + 0)); return result; } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp index d0b2d2b8b..99491d3e8 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkplugin.cpp @@ -54,21 +54,16 @@ void SDRdaemonSinkPlugin::initPlugin(PluginAPI* pluginAPI) PluginInterface::SamplingDevices SDRdaemonSinkPlugin::enumSampleSinks() { SamplingDevices result; - int count = 1; - for(int i = 0; i < count; i++) - { - QString displayedName(QString("SDRdaemonSink[%1]").arg(i)); - - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString::null, - i, - PluginInterface::SamplingDevice::BuiltInDevice, - false, - 0)); - } + result.append(SamplingDevice( + "SDRdaemonSink", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + false, + 0)); return result; } diff --git a/plugins/samplesource/filesource/filesourceplugin.cpp b/plugins/samplesource/filesource/filesourceplugin.cpp index f0552853c..a749a2ae6 100644 --- a/plugins/samplesource/filesource/filesourceplugin.cpp +++ b/plugins/samplesource/filesource/filesourceplugin.cpp @@ -53,21 +53,16 @@ void FileSourcePlugin::initPlugin(PluginAPI* pluginAPI) PluginInterface::SamplingDevices FileSourcePlugin::enumSampleSources() { SamplingDevices result; - int count = 1; - for(int i = 0; i < count; i++) - { - QString displayedName(QString("FileSource[%1]").arg(i)); - - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString::null, - i, - PluginInterface::SamplingDevice::BuiltInDevice, - true, - 0)); - } + result.append(SamplingDevice( + "FileSource", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + true, + 0)); return result; } diff --git a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp index 74f2bb76b..9b4c7e989 100644 --- a/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp +++ b/plugins/samplesource/sdrdaemonsource/sdrdaemonsourceplugin.cpp @@ -54,21 +54,16 @@ void SDRdaemonSourcePlugin::initPlugin(PluginAPI* pluginAPI) PluginInterface::SamplingDevices SDRdaemonSourcePlugin::enumSampleSources() { SamplingDevices result; - int count = 1; - for(int i = 0; i < count; i++) - { - QString displayedName(QString("SDRdaemonSource[%1]").arg(i)); - - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString::null, - i, - PluginInterface::SamplingDevice::BuiltInDevice, - true, - 0)); - } + result.append(SamplingDevice( + "SDRdaemonSource", + m_hardwareID, + m_deviceTypeID, + QString::null, + 0, + PluginInterface::SamplingDevice::BuiltInDevice, + true, + 0)); return result; } From 7650d114867019a25505df8b96f22776ca55ed12 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 03:49:14 +0100 Subject: [PATCH 31/39] Added missing change registration when setting the device index in the sampling device control --- sdrgui/gui/samplingdevicecontrol.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdrgui/gui/samplingdevicecontrol.cpp b/sdrgui/gui/samplingdevicecontrol.cpp index cf56f7641..b97ac53c7 100644 --- a/sdrgui/gui/samplingdevicecontrol.cpp +++ b/sdrgui/gui/samplingdevicecontrol.cpp @@ -62,11 +62,13 @@ void SamplingDeviceControl::setSelectedDeviceIndex(int index) if (m_rxElseTx) { PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(index); + DeviceEnumerator::instance()->changeRxSelection(m_deviceTabIndex, index); ui->deviceSelectedText->setText(samplingDevice.displayedName); } else { PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(index); + DeviceEnumerator::instance()->changeTxSelection(m_deviceTabIndex, index); ui->deviceSelectedText->setText(samplingDevice.displayedName); } From a32d508256f3424406c6103ae1af19a1b897280b Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 09:17:38 +0100 Subject: [PATCH 32/39] Pass device item index to the device API when creating or assigning a new device --- sdrbase/device/devicesinkapi.cpp | 6 ++++++ sdrbase/device/devicesinkapi.h | 15 +++++++++------ sdrbase/device/devicesourceapi.cpp | 6 ++++++ sdrbase/device/devicesourceapi.h | 15 +++++++++------ sdrbase/plugin/plugininterface.h | 2 +- sdrgui/mainwindow.cpp | 5 +++++ 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/sdrbase/device/devicesinkapi.cpp b/sdrbase/device/devicesinkapi.cpp index 228a07571..6707723e6 100644 --- a/sdrbase/device/devicesinkapi.cpp +++ b/sdrbase/device/devicesinkapi.cpp @@ -27,6 +27,7 @@ DeviceSinkAPI::DeviceSinkAPI(int deviceTabIndex, m_deviceTabIndex(deviceTabIndex), m_deviceSinkEngine(deviceSinkEngine), m_sampleSinkSequence(0), + m_itemIndex(0), m_pluginInterface(0), m_sampleSinkPluginInstanceUI(0), m_buddySharedPtr(0), @@ -159,6 +160,11 @@ void DeviceSinkAPI::setSampleSinkSequence(int sequence) m_deviceSinkEngine->setSinkSequence(sequence); } +void DeviceSinkAPI::setItemIndex(uint32_t index) +{ + m_itemIndex = index; +} + void DeviceSinkAPI::setSampleSinkPluginInterface(PluginInterface *iface) { m_pluginInterface = iface; diff --git a/sdrbase/device/devicesinkapi.h b/sdrbase/device/devicesinkapi.h index d20623f7f..aebd8c67e 100644 --- a/sdrbase/device/devicesinkapi.h +++ b/sdrbase/device/devicesinkapi.h @@ -66,6 +66,7 @@ public: void setSampleSinkSerial(const QString& serial); void setSampleSinkDisplayName(const QString& serial); void setSampleSinkSequence(int sequence); + void setItemIndex(uint32_t index); void setSampleSinkPluginInterface(PluginInterface *iface); void setSampleSinkPluginInstanceUI(PluginInstanceGUI *gui); @@ -73,8 +74,9 @@ public: const QString& getSampleSinkId() const { return m_sampleSinkId; } const QString& getSampleSinkSerial() const { return m_sampleSinkSerial; } const QString& getSampleSinkDisplayName() const { return m_sampleSinkDisplayName; } - PluginInterface *getPluginInterface() { return m_pluginInterface; } uint32_t getSampleSinkSequence() const { return m_sampleSinkSequence; } + uint32_t getItemIndex() const { return m_itemIndex; } + PluginInterface *getPluginInterface() { return m_pluginInterface; } PluginInstanceGUI *getSampleSinkPluginInstanceGUI() { return m_sampleSinkPluginInstanceUI; } void registerChannelInstance(const QString& channelName, PluginInstanceGUI* pluginGUI); @@ -104,11 +106,12 @@ protected: int m_deviceTabIndex; DSPDeviceSinkEngine *m_deviceSinkEngine; - QString m_hardwareId; - QString m_sampleSinkId; - QString m_sampleSinkSerial; - QString m_sampleSinkDisplayName; - uint32_t m_sampleSinkSequence; + QString m_hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) + QString m_sampleSinkId; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString m_sampleSinkSerial; //!< The device serial number defined by the vendor + QString m_sampleSinkDisplayName; //!< The human readable name identifying this instance + uint32_t m_sampleSinkSequence; //!< The device sequence. >0 when more than one device of the same type is connected + uint32_t m_itemIndex; //!< The Rx stream index. Can be >0 for NxM devices (i.e. 0 or 1 for LimeSDR) PluginInterface* m_pluginInterface; PluginInstanceGUI* m_sampleSinkPluginInstanceUI; diff --git a/sdrbase/device/devicesourceapi.cpp b/sdrbase/device/devicesourceapi.cpp index fe31d1968..4522412fa 100644 --- a/sdrbase/device/devicesourceapi.cpp +++ b/sdrbase/device/devicesourceapi.cpp @@ -27,6 +27,7 @@ DeviceSourceAPI::DeviceSourceAPI(int deviceTabIndex, m_deviceTabIndex(deviceTabIndex), m_deviceSourceEngine(deviceSourceEngine), m_sampleSourceSequence(0), + m_itemIndex(0), m_pluginInterface(0), m_sampleSourcePluginInstanceUI(0), m_buddySharedPtr(0), @@ -151,6 +152,11 @@ void DeviceSourceAPI::setSampleSourceSequence(int sequence) m_deviceSourceEngine->setSourceSequence(sequence); } +void DeviceSourceAPI::setItemIndex(uint32_t index) +{ + m_itemIndex = index; +} + void DeviceSourceAPI::setSampleSourcePluginInterface(PluginInterface *iface) { m_pluginInterface = iface; diff --git a/sdrbase/device/devicesourceapi.h b/sdrbase/device/devicesourceapi.h index 8a58a556a..516b0c61e 100644 --- a/sdrbase/device/devicesourceapi.h +++ b/sdrbase/device/devicesourceapi.h @@ -66,6 +66,7 @@ public: void setSampleSourceSerial(const QString& serial); void setSampleSourceDisplayName(const QString& serial); void setSampleSourceSequence(int sequence); + void setItemIndex(uint32_t index); void setSampleSourcePluginInterface(PluginInterface *iface); void setSampleSourcePluginInstanceGUI(PluginInstanceGUI *gui); @@ -73,8 +74,9 @@ public: const QString& getSampleSourceId() const { return m_sampleSourceId; } const QString& getSampleSourceSerial() const { return m_sampleSourceSerial; } const QString& getSampleSourceDisplayName() const { return m_sampleSourceDisplayName; } - PluginInterface *getPluginInterface() { return m_pluginInterface; } uint32_t getSampleSourceSequence() const { return m_sampleSourceSequence; } + uint32_t getItemIndex() const { return m_itemIndex; } + PluginInterface *getPluginInterface() { return m_pluginInterface; } PluginInstanceGUI *getSampleSourcePluginInstanceGUI() { return m_sampleSourcePluginInstanceUI; } void loadSourceSettings(const Preset* preset); @@ -100,11 +102,12 @@ protected: int m_deviceTabIndex; DSPDeviceSourceEngine *m_deviceSourceEngine; - QString m_hardwareId; - QString m_sampleSourceId; - QString m_sampleSourceSerial; - QString m_sampleSourceDisplayName; - uint32_t m_sampleSourceSequence; + QString m_hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) + QString m_sampleSourceId; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) + QString m_sampleSourceSerial; //!< The device serial number defined by the vendor or a fake one (SDRplay) + QString m_sampleSourceDisplayName; //!< The human readable name identifying this instance + uint32_t m_sampleSourceSequence; //!< The device sequence. >0 when more than one device of the same type is connected + uint32_t m_itemIndex; //!< The Rx stream index. Can be >0 for NxM devices (i.e. 0 or 1 for LimeSDR) PluginInterface* m_pluginInterface; PluginInstanceGUI* m_sampleSourcePluginInstanceUI; diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h index d3c20d552..b840e1895 100644 --- a/sdrbase/plugin/plugininterface.h +++ b/sdrbase/plugin/plugininterface.h @@ -36,7 +36,7 @@ public: QString displayedName; //!< The human readable name QString hardwareId; //!< The internal id that identifies the type of hardware (i.e. HackRF, BladeRF, ...) QString id; //!< The internal plugin ID corresponding to the device (i.e. for HackRF input, for HackRF output ...) - QString serial; //!< The device serial number + QString serial; //!< The device serial number defined by the vendor or a fake one (SDRplay) int sequence; //!< The device sequence. >0 when more than one device of the same type is connected SamplingDeviceType type; //!< The sampling device type for behavior information bool rxElseTx; //!< This is the Rx part else the Tx part of the device diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 2c853a0eb..87c47d496 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -155,6 +155,7 @@ MainWindow::MainWindow(QWidget* parent) : PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceIndex); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSourceAPI->setItemIndex(samplingDevice.deviceItemIndex); m_deviceUIs.back()->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); @@ -243,6 +244,7 @@ void MainWindow::addSourceDevice() int fileSourceDeviceIndex = DeviceEnumerator::instance()->getFileSourceDeviceIndex(); PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileSourceDeviceIndex); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSourceAPI->setItemIndex(samplingDevice.deviceItemIndex); m_deviceUIs.back()->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); m_deviceUIs.back()->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); @@ -312,6 +314,7 @@ void MainWindow::addSinkDevice() int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileSinkDeviceIndex(); PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkSequence(samplingDevice.sequence); + m_deviceUIs.back()->m_deviceSinkAPI->setItemIndex(samplingDevice.deviceItemIndex); m_deviceUIs.back()->m_deviceSinkAPI->setHardwareId(samplingDevice.hardwareId); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkId(samplingDevice.id); m_deviceUIs.back()->m_deviceSinkAPI->setSampleSinkSerial(samplingDevice.serial); @@ -898,6 +901,7 @@ void MainWindow::on_sampleSource_changed() PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); deviceUI->m_deviceSourceAPI->setSampleSourceSequence(samplingDevice.sequence); + deviceUI->m_deviceSourceAPI->setItemIndex(samplingDevice.deviceItemIndex); deviceUI->m_deviceSourceAPI->setHardwareId(samplingDevice.hardwareId); deviceUI->m_deviceSourceAPI->setSampleSourceId(samplingDevice.id); deviceUI->m_deviceSourceAPI->setSampleSourceSerial(samplingDevice.serial); @@ -984,6 +988,7 @@ void MainWindow::on_sampleSink_changed() PluginInterface::SamplingDevice samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(deviceUI->m_samplingDeviceControl->getSelectedDeviceIndex()); deviceUI->m_deviceSinkAPI->setSampleSinkSequence(samplingDevice.sequence); + deviceUI->m_deviceSinkAPI->setItemIndex(samplingDevice.deviceItemIndex); deviceUI->m_deviceSinkAPI->setHardwareId(samplingDevice.hardwareId); deviceUI->m_deviceSinkAPI->setSampleSinkId(samplingDevice.id); deviceUI->m_deviceSinkAPI->setSampleSinkSerial(samplingDevice.serial); From 7f8d61cdb00495b1af8bb452adfc73ebd2570502 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 10:30:07 +0100 Subject: [PATCH 33/39] LimeSDR: use channel set in device management via device API --- .../limesdroutput/limesdroutput.cpp | 26 ++++++-------- .../limesdrinput/limesdrinput.cpp | 35 +++++++++---------- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/plugins/samplesink/limesdroutput/limesdroutput.cpp b/plugins/samplesink/limesdroutput/limesdroutput.cpp index 89573f94c..6b110e6aa 100644 --- a/plugins/samplesink/limesdroutput/limesdroutput.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutput.cpp @@ -68,6 +68,8 @@ void LimeSDROutput::destroy() bool LimeSDROutput::openDevice() { + int requestedChannel = m_deviceAPI->getItemIndex(); + // look for Tx buddies and get reference to common parameters // if there is a channel left take the first available if (m_deviceAPI->getSinkBuddies().size() > 0) // look sink sibling first @@ -98,7 +100,8 @@ bool LimeSDROutput::openDevice() qDebug("LimeSDROutput::openDevice: at least one more Tx channel is available in device"); } - // look for unused channel number + // check if the requested channel is busy and abort if so (should not happen if device management is working correctly) + char *busyChannels = new char[deviceParams->m_nbTxChannels]; memset(busyChannels, 0, deviceParams->m_nbTxChannels); @@ -107,21 +110,14 @@ bool LimeSDROutput::openDevice() DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[i]; DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr(); - if (buddyShared->m_channel >= 0) { - busyChannels[buddyShared->m_channel] = 1; + if (buddyShared->m_channel == requestedChannel) + { + qCritical("LimeSDROutput::openDevice: cannot open busy channel %u", requestedChannel); + return false; } } - std::size_t ch = 0; - - for (;ch < deviceParams->m_nbTxChannels; ch++) - { - if (busyChannels[ch] == 0) { - break; // first available is the good one - } - } - - m_deviceShared.m_channel = ch; + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel delete[] busyChannels; } // look for Rx buddies and get reference to common parameters @@ -143,7 +139,7 @@ bool LimeSDROutput::openDevice() qDebug("LimeSDROutput::openDevice: getting device parameters from Rx buddy"); } - m_deviceShared.m_channel = 0; // take first channel + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel } // There are no buddies then create the first LimeSDR common parameters // open the device this will also populate common fields @@ -156,7 +152,7 @@ bool LimeSDROutput::openDevice() char serial[256]; strcpy(serial, qPrintable(m_deviceAPI->getSampleSinkSerial())); m_deviceShared.m_deviceParams->open(serial); - m_deviceShared.m_channel = 0; // take first channel + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel } m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API diff --git a/plugins/samplesource/limesdrinput/limesdrinput.cpp b/plugins/samplesource/limesdrinput/limesdrinput.cpp index c2b2488c7..8652a25b3 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinput.cpp @@ -86,6 +86,8 @@ bool LimeSDRInput::openDevice() qDebug("LimeSDRInput::openDevice: allocated SampleFifo"); } + int requestedChannel = m_deviceAPI->getItemIndex(); + // look for Rx buddies and get reference to common parameters // if there is a channel left take the first available if (m_deviceAPI->getSourceBuddies().size() > 0) // look source sibling first @@ -116,7 +118,8 @@ bool LimeSDRInput::openDevice() qDebug("LimeSDRInput::openDevice: at least one more Rx channel is available in device"); } - // look for unused channel number + // check if the requested channel is busy and abort if so (should not happen if device management is working correctly) + char *busyChannels = new char[deviceParams->m_nbRxChannels]; memset(busyChannels, 0, deviceParams->m_nbRxChannels); @@ -125,21 +128,14 @@ bool LimeSDRInput::openDevice() DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[i]; DeviceLimeSDRShared *buddyShared = (DeviceLimeSDRShared *) buddy->getBuddySharedPtr(); - if (buddyShared->m_channel >= 0) { - busyChannels[buddyShared->m_channel] = 1; + if (buddyShared->m_channel == requestedChannel) + { + qCritical("LimeSDRInput::openDevice: cannot open busy channel %u", requestedChannel); + return false; } } - std::size_t ch = 0; - - for (;ch < deviceParams->m_nbRxChannels; ch++) - { - if (busyChannels[ch] == 0) { - break; // first available is the good one - } - } - - m_deviceShared.m_channel = ch; + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel delete[] busyChannels; } // look for Tx buddies and get reference to common parameters @@ -161,7 +157,7 @@ bool LimeSDRInput::openDevice() qDebug("LimeSDRInput::openDevice: getting device parameters from Tx buddy"); } - m_deviceShared.m_channel = 0; // take first channel + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel } // There are no buddies then create the first LimeSDR common parameters // open the device this will also populate common fields @@ -174,7 +170,7 @@ bool LimeSDRInput::openDevice() char serial[256]; strcpy(serial, qPrintable(m_deviceAPI->getSampleSourceSerial())); m_deviceShared.m_deviceParams->open(serial); - m_deviceShared.m_channel = 0; // take first channel + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel } m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API @@ -357,6 +353,8 @@ void LimeSDRInput::releaseChannel() resumeTxBuddies(); resumeRxBuddies(); + // The channel will be effectively released to be reused in another device set only at close time + m_channelAcquired = false; } @@ -939,9 +937,10 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, bool forc settings.m_antennaPath)) { doCalibration = true; - setAntennaAuto = (settings.m_antennaPath == 0); - qDebug("LimeSDRInput::applySettings: set antenna path to %d", - (int) settings.m_antennaPath); + //setAntennaAuto = (settings.m_antennaPath == 0); + qDebug("LimeSDRInput::applySettings: set antenna path to %d on channel %d", + (int) settings.m_antennaPath, + m_deviceShared.m_channel); } else { From cccd5bdb58ab6dd4fd1f0b5d2d5926c04e23e2e2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 10:42:54 +0100 Subject: [PATCH 34/39] Windows build: update .pro files --- debian/changelog | 4 ++-- sdrbase/sdrbase.pro | 6 +++++- sdrgui/sdrgui.pro | 13 +++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/debian/changelog b/debian/changelog index 10851c733..b329cf097 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,9 @@ sdrangel (3.8.0-1) unstable; urgency=medium - * Redesigned the device handling + * Redesigned the device handling to accomodate multi channel devices like LimeSDR * Refactoring: Separate DeviceAPI from the GUI - -- Edouard Griffiths, F4EXB Sun, 05 Nov 2017 18:14:18 +0200 + -- Edouard Griffiths, F4EXB Thu, 02 Nov 2017 18:14:18 +0200 sdrangel (3.7.8-1) unstable; urgency=medium diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index 8be9677ba..1d7b79544 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -101,6 +101,8 @@ SOURCES += audio/audiodeviceinfo.cpp\ util/samplesourceserializer.cpp\ util/simpleserializer.cpp\ plugin/plugininterface.cpp + plugin/pluginapi.cpp\ + plugin/pluginmanager.cpp HEADERS += audio/audiodeviceinfo.h\ audio/audiofifo.h\ @@ -162,7 +164,9 @@ HEADERS += audio/audiodeviceinfo.h\ dsp/devicesamplesource.h\ dsp/devicesamplesink.h\ plugin/plugininstancegui.h\ - plugin/plugininterface.h\ + plugin/plugininterface.h\ + plugin/pluginapi.h\ + plugin/pluginmanager.h\ settings/preferences.h\ settings/preset.h\ settings/mainsettings.h\ diff --git a/sdrgui/sdrgui.pro b/sdrgui/sdrgui.pro index 117637f5b..8a060c1e0 100644 --- a/sdrgui/sdrgui.pro +++ b/sdrgui/sdrgui.pro @@ -64,16 +64,14 @@ SOURCES += mainwindow.cpp\ gui/presetitem.cpp\ gui/rollupwidget.cpp\ gui/samplingdevicecontrol.cpp\ + gui/samplingdevicedialog.cpp\ gui/mypositiondialog.cpp\ gui/scale.cpp\ gui/scaleengine.cpp\ gui/transverterbutton.cpp\ gui/transverterdialog.cpp\ gui/valuedial.cpp\ - gui/valuedialz.cpp\ - plugin/pluginapi.cpp\ - plugin/pluginmanager.cpp\ - plugin/plugininterface.cpp + gui/valuedialz.cpp HEADERS += mainwindow.h\ device/devicesourceapi.h\ @@ -109,16 +107,14 @@ HEADERS += mainwindow.h\ gui/presetitem.h\ gui/rollupwidget.h\ gui/samplingdevicecontrol.h\ + gui/samplingdevicedialog.h\ gui/mypositiondialog.h\ gui/scale.h\ gui/scaleengine.h\ gui/transverterbutton.h\ gui/transverterdialog.h\ gui/valuedial.h\ - gui/valuedialz.h\ - plugin/pluginapi.h\ - plugin/pluginmanager.h\ - plugin/plugininterface.h + gui/valuedialz.h FORMS += mainwindow.ui\ gui/scopewindow.ui\ @@ -132,6 +128,7 @@ FORMS += mainwindow.ui\ gui/aboutdialog.ui\ gui/pluginsdialog.ui\ gui/samplingdevicecontrol.ui\ + gui/samplingdevicedialog.ui\ gui/myposdialog.ui\ gui/glspectrumgui.ui\ gui/transverterdialog.ui\ From af1b0c7c6e59dabc540391ddb6c29813c4567397 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 2 Nov 2017 12:15:13 +0100 Subject: [PATCH 35/39] Updated documentation --- doc/img/MainWindow_SDControl.png | Bin 12157 -> 13015 bytes doc/img/MainWindow_SDDialog.png | Bin 0 -> 15938 bytes doc/img/MainWindow_general.png | Bin 999745 -> 1000434 bytes doc/img/MainWindow_tabs.png | Bin 998516 -> 998545 bytes sdrbase/readme.md | 30 ++++++++++++++++++++++++------ 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 doc/img/MainWindow_SDDialog.png diff --git a/doc/img/MainWindow_SDControl.png b/doc/img/MainWindow_SDControl.png index b218c98f3bf883fde21887f63e4d6c26ff18d89e..af3b8eef066a6ff0451bf3d3cc7c2d6730769c48 100644 GIT binary patch literal 13015 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 literal 12157 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 PkC2mkCYkr-)!Y972LDhB diff --git a/doc/img/MainWindow_SDDialog.png b/doc/img/MainWindow_SDDialog.png new file mode 100644 index 0000000000000000000000000000000000000000..d155e7406fbb418385df7e9e7248bed08cd6597a GIT binary patch literal 15938 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 U*Lvwk;!$vg3raF6(z=iSAM#^Je*gdg literal 0 HcmV?d00001 diff --git a/doc/img/MainWindow_general.png b/doc/img/MainWindow_general.png index 4cbeefa3c8f7fd8bc9dfd0539ca6135ffc9fc4f1..0025aaa384f34068818e62b3e4138623eb77959f 100644 GIT binary patch delta 945452 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

Woi$DIAHS^5(`O!L2 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

  • -y|`# zWoYONU{z7JBnwFWHg8Y#jyhj?tNzzS%4{0XbJTm-d+tRKuA3~8ZDFwSXSkA`c4mr# zD#klTwaStK@2keucHVH3^gh-SA9(Ii@r_8PDEImkhUWUz*_TWk1BQ}U!IFRet7859 zE&mkT9{(@4tpH4TJQb#QG_r3Je!re@6`6{Dyo!cMWGlt}4{?8>G zm|k%6xCh!Lu4W-m?dm7vxcNizA?B#@qp?KM7Qkh67nQxgc$xb+r{=q0{V=z5V}Jkj z*mt~Ymwc=JNq$;8!KEG#q-`>)g(*&zfTWOXUE+6Y*>JPOHqS50Tgh>4W#ZmTW4Ih^ zN^H|(xoxihAjlEuD_CU(;pu#EeHYLiUvq3oHR^y#C)LLnNz~GzBeLDe4RPU1A_(0E z+9h|?-LK5>h~3(I$oKG|%s+`D7agn#5Mn|ozaA&1v08I~@W?c)PosY>g9P2#MZW6T_(?!2yn0nL<)omOOUPMayGP%`BAX z&y?|#N2o;gD?(f*%Q;kny{$&3R9~Y57)b?9=yn*N(K+!wIY9YS&#Xqy`OTeuzp=lg zB9?Jz0A0x9Evttl*w$+aNbZ2u+ZyAF88X%!B6^^$-*S>j7%?a&1-R6uD%X6}4RUPu zJn~54BekTRf7z6R{(K^xqqwZhDFs!Ec62_3`bp{c2Qpm@6%BeNh@E;%)4fCkK>blX z2R_sR?Nb=sucE+40uWbjZ6y>^?UC*;1D7)3KuEg&eY9vU|D!Kho1#TwzeBy)3DasJA@q?@b+9reGF%CvPc z0(hMntb4BQL8ivZ^NvK%=R>hiwRF2TNpTd*=irD4;Z7GWlwT!!4;iXJp6xfgxHS|E zDtr*O06KJHhW^*KdwAPf*|DJw?$tcX7ye4wxmhC3a`0}w$Gi^tdHH&a^UPc|?gvk# z4!7NDViGg@GRR^<(t_&?L@GaIri3e?t$2R70j31%jHka4F?@Bno}Q4X2YqD=mMIv>eLN5+f(GX&q6J^R0V0aj!b~!p9D*q`G=$ zaDTjl2dtew8C@0Mi(?nt%@#2Yb)seIRq$G! zqN@Z8SM0ts6w%V@pX{NVkMr(txV>}PoIe}B2R*-j;9-XsAu&9}F8+zF7_p!tp_rd{ zJjP43>XI?P%69bu1eLqjNmXa4>*7hwr_R1oxr@DZXJh=>U6WZhD;;X0p5`i_8M1kg zSW$(5jpym5(F%ECVC?T6oS^lWl$&3_yv3S^XPkF|hF))qeKj20C?VIxzn7?ACoj3N z>eGX{TbJ`+8Bq#J7%v--Ze@U@q<_DSB)=UXqN7xz5wNNN+J~=eN<49t)04K>lDAP$2WnwIdFM0p#gw%a(Xg?yVo9e$m@%o6H9AHFl5Pd%%5uVC;y z0&lIaQO3Dp`i;%l2NKK>8O(r&|R$hcz&3a+gmpuA-vC65!+D z2NNP)Wy=d+x9(;!GKCo)Sp5FQcpTXmhX?l#rE}Ojsj2m~!~~8=OU?j@95X~GmR&N% z>2dd8%4U15d#uph`PehRxH>mk%}*viCsR~_em1P^_z+EUf8^$OMn%jgMQ^ca(odRS zoxYH;GFkug9N=w=)O?f4OdfKE_#B@mMlVxB_)Ca%Z1`=gjJ9wNm|E=J1;#{QO z5>)jMC+0WDxb_DF+plp7T& zNR~ceZ>mpcg9hZq6*r6e#@5uY7W~V)A>4)XLjg|eD%}T+&2rw!cHkg)@)kY~QWS$I z-c}Ouo(d!{wr}|OV%kI(RK5N8ys{!$9_HYwvIx2wEyZ3ki(K=18zip&S?j}rPH-++ zKoM=~ON$@L8kzyQKgog@{ZN{Q$=Bv1L8n6)0oQhn;^_*ot%(_I(C)}kAOSAg5+D3^ znT$xTpjj&%PBwh}7wDgNAS-dOB;xy#*g?rnh%v#22||dvFWK3QDDm`v1$`%SOP$lS zeU~5EQmSB~PdrkbcmitCDL~u)#AvucG}4wrm`6kXs?LR<1(Z$cDK1#=Y#teeDUK?r zB4Q)-p`ytS>s28HwOA$Cvzh5gL9B1yrV5$Nn=Q|!<5g-G@NqYsOM6RlPU;SmI zXcdq5Z~hgiXR5L>pN5e?d7Nc?<3|e6kCuU?My*REq>j2^`@j$ftyV z!u?pO0&Zs7+d6rsTby&wok&`rPUDUCH~l9_kT*4REG9b|1H_PT{iB(!Xb;@TXqaa(6zVpI#m&rn z9V>)ZE7TF!eopAK5DCtbt#a(EuaCPQ8GAL&7}#gNSvHVg$TcpIb(Yu2r&Y@nA5O$g z$7hp217JX{U#QP~ddawruN?G-j~l2%*V&GcrxC(GO||C!W-MyyznLz+=Be-Gc>nGA z$agHaXB{=lJHx|k>_*Y7h`Q1vjC0G-2&78F;j9wHqcAnm{YxOApuVr+6XUgX+=LQk z$?N6TZHx?n)-%Ajuapm)Kl|IUS-AgId}`GfY5*CMy@CSdyFspHAvfMEP8Z@8Ut+#a7y6-@O%8_=9n3xOsm43{-qnwa^_{ z1%&z`$Nh*LG6^Ftx|mYyCk50-=Vhm>4m2FEcp0>?y9vz`kl1H0#l-I+*BjPq-q^0QS8IN9; z;x?uIu$ea|>#q{Cv?k6%cPa*;r3FbeK!h-Ad2$8|L9}qq%bCMcce~yzEp!u07I5X= zNeGjP)@KE+$icFZ(&SuJ2~mzCVGW)Snl>O;3%@rQSKzY#helN-({ieB|2s$63rGFQ zN*a{UV=uM*ge(A@2w$Fhi^=jgb}w`*5`J1-H^Tf|5hsOz5K8^1H%hGraduzDfw9K@ zq{9e5#_xLBjA;yH6O$Y=F<)Q#jTV0h6^~QaWJ*j{PdT2?#K(@v6bq+4(rvLqzLT;< zBxjrAP@h7Q>pNPqT$lHWy>^-0m(Jv!d&KV$&0UwT(VZj|Du0M9yJ%6uyN=zt3}1vy zs9>9JAjtZ*l;FLiKktR=`%Z>O0i*a1NVTc%yNN@qV1ohEuqqw)BXGap#Yy;6qo-^T zQzqz++pInutuNFl?69r9{=*;6TjNjk z9K@j`OLQx^nH64sd*F)(V88t`z!qKtiIW?3e+n|wv<9^8&RV=XEPrxg|7VTSM^Oh5)A17arKrDQE+Y4@6aLw(p^$Y*U%*( zGNgiZcMKuj&LAZXLk{gNAux1FBQbP1;Lx2zcdIYwIp_Uy{)oM=z1R9(Yqba@I&44y zvy^mUH{zllOMVmiv+k7~wXA?c(n?BnuIWVjg8{t?>LpA-qm1b%b5Ovu*=^dXI%nFr zTXhul{y0Er@-rY~Ogf#JQQdJs(kpCa;^FJGr&W?2`U#@)$uk<$?(tb3qAEu~fPViU z!^n%TqO{+po+5*7QF37;KY#|R%n=Y=@=lG(PvEf_SUbL+11afSZ8 zNFn04f`?VnGG734BRDNnRUS*uX!>Z!`ZAciOzuHr~b<4Y|