diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 0a39be051..d3ab55f71 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -40,5 +40,6 @@ endif()
add_subdirectory(channelrx)
add_subdirectory(channeltx)
+add_subdirectory(feature)
add_subdirectory(samplesource)
add_subdirectory(samplesink)
diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt
new file mode 100644
index 000000000..40d228b3a
--- /dev/null
+++ b/plugins/feature/CMakeLists.txt
@@ -0,0 +1,3 @@
+project(feature)
+
+add_subdirectory(simpleptt)
diff --git a/plugins/feature/simpleptt/CMakeLists.txt b/plugins/feature/simpleptt/CMakeLists.txt
new file mode 100644
index 000000000..8902dd59d
--- /dev/null
+++ b/plugins/feature/simpleptt/CMakeLists.txt
@@ -0,0 +1,56 @@
+project(simpleptt)
+
+set(simpleptt_SOURCES
+ simpleptt.cpp
+ simplepttsettings.cpp
+ simplepttplugin.cpp
+ simplepttworker.cpp
+ simplepttreport.cpp
+)
+
+set(simpleptt_HEADERS
+ simpleptt.h
+ simplepttsettings.h
+ simplepttplugin.h
+ simplepttworker.h
+ simplepttreport.h
+)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
+)
+
+if(NOT SERVER_MODE)
+ set(simpleptt_SOURCES
+ ${simpleptt_SOURCES}
+ simplepttgui.cpp
+ simplepttgui.ui
+ )
+ set(simpleptt_HEADERS
+ ${simpleptt_HEADERS}
+ simplepttgui.h
+ )
+
+ set(TARGET_NAME featuresimpleptt)
+ set(TARGET_LIB "Qt5::Widgets")
+ set(TARGET_LIB_GUI "sdrgui")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
+else()
+ set(TARGET_NAME featuresimplepttsrv)
+ set(TARGET_LIB "")
+ set(TARGET_LIB_GUI "")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
+endif()
+
+add_library(${TARGET_NAME} SHARED
+ ${simpleptt_SOURCES}
+)
+
+target_link_libraries(${TARGET_NAME}
+ Qt5::Core
+ ${TARGET_LIB}
+ sdrbase
+ ${TARGET_LIB_GUI}
+)
+
+install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
diff --git a/plugins/feature/simpleptt/simpleptt.cpp b/plugins/feature/simpleptt/simpleptt.cpp
new file mode 100644
index 000000000..40d799bd5
--- /dev/null
+++ b/plugins/feature/simpleptt/simpleptt.cpp
@@ -0,0 +1,151 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "dsp/dspengine.h"
+
+#include "simplepttworker.h"
+#include "simpleptt.h"
+
+MESSAGE_CLASS_DEFINITION(SimplePTT::MsgConfigureSimplePTT, Message)
+MESSAGE_CLASS_DEFINITION(SimplePTT::MsgPTT, Message)
+MESSAGE_CLASS_DEFINITION(SimplePTT::MsgStartStop, Message)
+
+const QString SimplePTT::m_featureIdURI = "sdrangel.feature.simpleptt";
+const QString SimplePTT::m_featureId = "SimplePTT";
+
+SimplePTT::SimplePTT(WebAPIAdapterInterface *webAPIAdapterInterface) :
+ Feature(m_featureIdURI, webAPIAdapterInterface)
+{
+ setObjectName(m_featureId);
+ m_worker = new SimplePTTWorker(webAPIAdapterInterface);
+ m_state = StIdle;
+ m_errorMessage = "SimplePTT error";
+}
+
+SimplePTT::~SimplePTT()
+{
+ if (m_worker->isRunning()) {
+ stop();
+ }
+
+ delete m_worker;
+}
+
+void SimplePTT::start()
+{
+ qDebug("SimplePTT::start");
+
+ m_worker->reset();
+ m_worker->setMessageQueueToGUI(getMessageQueueToGUI());
+ bool ok = m_worker->startWork();
+ m_state = ok ? StRunning : StError;
+ m_thread.start();
+
+ SimplePTTWorker::MsgConfigureSimplePTTWorker *msg = SimplePTTWorker::MsgConfigureSimplePTTWorker::create(m_settings, true);
+ m_worker->getInputMessageQueue()->push(msg);
+}
+
+void SimplePTT::stop()
+{
+ qDebug("SimplePTT::stop");
+ m_worker->stopWork();
+ m_state = StIdle;
+ m_thread.quit();
+ m_thread.wait();
+}
+
+bool SimplePTT::handleMessage(const Message& cmd)
+{
+ if (MsgConfigureSimplePTT::match(cmd))
+ {
+ MsgConfigureSimplePTT& cfg = (MsgConfigureSimplePTT&) cmd;
+ qDebug() << "SimplePTT::handleMessage: MsgConfigureSimplePTT";
+ applySettings(cfg.getSettings(), cfg.getForce());
+
+ return true;
+ }
+ else if (MsgPTT::match(cmd))
+ {
+ MsgPTT& cfg = (MsgPTT&) cmd;
+ qDebug() << "SimplePTT::handleMessage: MsgPTT: tx:" << cfg.getTx();
+
+ SimplePTTWorker::MsgPTT *msg = SimplePTTWorker::MsgPTT::create(cfg.getTx());
+ m_worker->getInputMessageQueue()->push(msg);
+
+ return true;
+ }
+ else if (MsgStartStop::match(cmd))
+ {
+ MsgStartStop& cfg = (MsgStartStop&) cmd;
+ qDebug() << "SimplePTT::handleMessage: MsgStartStop: start:" << cfg.getStartStop();
+
+ if (cfg.getStartStop()) {
+ start();
+ } else {
+ stop();
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+QByteArray SimplePTT::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool SimplePTT::deserialize(const QByteArray& data)
+{
+ if (m_settings.deserialize(data))
+ {
+ MsgConfigureSimplePTT *msg = MsgConfigureSimplePTT::create(m_settings, true);
+ m_inputMessageQueue.push(msg);
+ return true;
+ }
+ else
+ {
+ m_settings.resetToDefaults();
+ MsgConfigureSimplePTT *msg = MsgConfigureSimplePTT::create(m_settings, true);
+ m_inputMessageQueue.push(msg);
+ return false;
+ }
+}
+
+void SimplePTT::applySettings(const SimplePTTSettings& settings, bool force)
+{
+ qDebug() << "SimplePTT::applySettings:"
+ << " m_title: " << settings.m_title
+ << " m_rgbColor: " << settings.m_rgbColor
+ << " m_rxDeviceSetIndex: " << settings.m_rxDeviceSetIndex
+ << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex
+ << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs
+ << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs
+ << " force: " << force;
+
+ SimplePTTWorker::MsgConfigureSimplePTTWorker *msg = SimplePTTWorker::MsgConfigureSimplePTTWorker::create(
+ settings, force
+ );
+ m_worker->getInputMessageQueue()->push(msg);
+
+ m_settings = settings;
+}
diff --git a/plugins/feature/simpleptt/simpleptt.h b/plugins/feature/simpleptt/simpleptt.h
new file mode 100644
index 000000000..8a22d3d71
--- /dev/null
+++ b/plugins/feature/simpleptt/simpleptt.h
@@ -0,0 +1,119 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTT_H_
+#define INCLUDE_FEATURE_SIMPLEPTT_H_
+
+#include
+
+#include "feature/feature.h"
+#include "util/message.h"
+
+#include "simplepttsettings.h"
+
+class WebAPIAdapterInterface;
+class SimplePTTWorker;
+
+class SimplePTT : public Feature
+{
+ Q_OBJECT
+public:
+ class MsgConfigureSimplePTT : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const SimplePTTSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigureSimplePTT* create(const SimplePTTSettings& settings, bool force) {
+ return new MsgConfigureSimplePTT(settings, force);
+ }
+
+ private:
+ SimplePTTSettings m_settings;
+ bool m_force;
+
+ MsgConfigureSimplePTT(const SimplePTTSettings& settings, bool force) :
+ Message(),
+ m_settings(settings),
+ m_force(force)
+ { }
+ };
+
+ class MsgPTT : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getTx() const { return m_tx; }
+
+ static MsgPTT* create(bool tx) {
+ return new MsgPTT(tx);
+ }
+
+ private:
+ bool m_tx;
+
+ MsgPTT(bool tx) :
+ Message(),
+ m_tx(tx)
+ { }
+ };
+
+ class MsgStartStop : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getStartStop() const { return m_startStop; }
+
+ static MsgStartStop* create(bool startStop) {
+ return new MsgStartStop(startStop);
+ }
+
+ protected:
+ bool m_startStop;
+
+ MsgStartStop(bool startStop) :
+ Message(),
+ m_startStop(startStop)
+ { }
+ };
+
+ SimplePTT(WebAPIAdapterInterface *webAPIAdapterInterface);
+ ~SimplePTT();
+ virtual void destroy() { delete this; }
+ virtual bool handleMessage(const Message& cmd);
+
+ virtual void getIdentifier(QString& id) { id = objectName(); }
+ virtual void getTitle(QString& title) { title = m_settings.m_title; }
+
+ virtual QByteArray serialize() const;
+ virtual bool deserialize(const QByteArray& data);
+
+ static const QString m_featureIdURI;
+ static const QString m_featureId;
+
+private:
+ QThread m_thread;
+ SimplePTTWorker *m_worker;
+ SimplePTTSettings m_settings;
+
+ void start();
+ void stop();
+ void applySettings(const SimplePTTSettings& settings, bool force = false);
+};
+
+#endif // INCLUDE_FEATURE_SIMPLEPTT_H_
\ No newline at end of file
diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp
new file mode 100644
index 000000000..e71c6d3f8
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttgui.cpp
@@ -0,0 +1,372 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "feature/featureuiset.h"
+#include "gui/basicfeaturesettingsdialog.h"
+#include "mainwindow.h"
+#include "device/deviceuiset.h"
+
+#include "ui_simplepttgui.h"
+#include "simplepttreport.h"
+#include "simpleptt.h"
+#include "simplepttgui.h"
+
+SimplePTTGUI* SimplePTTGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
+{
+ SimplePTTGUI* gui = new SimplePTTGUI(pluginAPI, featureUISet, feature);
+ return gui;
+}
+
+void SimplePTTGUI::destroy()
+{
+ delete this;
+}
+
+void SimplePTTGUI::setName(const QString& name)
+{
+ setObjectName(name);
+}
+
+QString SimplePTTGUI::getName() const
+{
+ return objectName();
+}
+
+void SimplePTTGUI::resetToDefaults()
+{
+ m_settings.resetToDefaults();
+ displaySettings();
+ applySettings(true);
+}
+
+QByteArray SimplePTTGUI::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool SimplePTTGUI::deserialize(const QByteArray& data)
+{
+ if (m_settings.deserialize(data))
+ {
+ displaySettings();
+ applySettings(true);
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+bool SimplePTTGUI::handleMessage(const Message& message)
+{
+ if (SimplePTT::MsgConfigureSimplePTT::match(message))
+ {
+ qDebug("SimplePTTGUI::handleMessage: SimplePTT::MsgConfigureSimplePTT");
+ const SimplePTT::MsgConfigureSimplePTT& cfg = (SimplePTT::MsgConfigureSimplePTT&) message;
+ m_settings = cfg.getSettings();
+ blockApplySettings(true);
+ displaySettings();
+ blockApplySettings(false);
+
+ return true;
+ }
+ else if (SimplePTTReport::MsgRadioState::match(message))
+ {
+ qDebug("SimplePTTGUI::handleMessage: SimplePTTReport::MsgRadioState");
+ const SimplePTTReport::MsgRadioState& cfg = (SimplePTTReport::MsgRadioState&) message;
+ SimplePTTReport::RadioState state = cfg.getState();
+ ui->statusIndicator->setStyleSheet("QLabel { background-color: " +
+ m_statusColors[(int) state] + "; border-radius: 12px; }");
+ ui->statusIndicator->setToolTip(m_statusTooltips[(int) state]);
+
+ return true;
+ }
+
+ return false;
+}
+
+void SimplePTTGUI::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = getInputMessageQueue()->pop()))
+ {
+ if (handleMessage(*message)) {
+ delete message;
+ }
+ }
+}
+
+void SimplePTTGUI::onWidgetRolled(QWidget* widget, bool rollDown)
+{
+ (void) widget;
+ (void) rollDown;
+}
+
+SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
+ RollupWidget(parent),
+ ui(new Ui::SimplePTTGUI),
+ m_pluginAPI(pluginAPI),
+ m_featureUISet(featureUISet),
+ m_doApplySettings(true),
+ m_lastFeatureState(0)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ setChannelWidget(false);
+ connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
+ m_simplePTT = reinterpret_cast(feature);
+ m_simplePTT->setMessageQueueToGUI(&m_inputMessageQueue);
+
+ m_featureUISet->registerFeatureInstance(SimplePTT::m_featureIdURI, this);
+ m_featureUISet->addRollupWidget(this);
+
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
+ connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
+
+ connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
+ m_statusTimer.start(1000);
+
+ m_statusTooltips.push_back("Idle"); // 0 - all off
+ m_statusTooltips.push_back("Rx on"); // 1 - Rx on
+ m_statusTooltips.push_back("Tx on"); // 2 - Tx on
+
+ m_statusColors.push_back("gray"); // All off
+ m_statusColors.push_back("rgb(85, 232, 85)"); // Rx on (green)
+ m_statusColors.push_back("rgb(232, 85, 85)"); // Tx on (red)
+
+ updateDeviceSetLists();
+ displaySettings();
+ applySettings(true);
+}
+
+SimplePTTGUI::~SimplePTTGUI()
+{
+ m_featureUISet->removeFeatureInstance(this);
+ delete m_simplePTT; // When the GUI closes it has to delete the demodulator because it can be done with (x)
+ delete ui;
+}
+
+void SimplePTTGUI::blockApplySettings(bool block)
+{
+ m_doApplySettings = !block;
+}
+
+void SimplePTTGUI::displaySettings()
+{
+ setTitleColor(m_settings.m_rgbColor);
+ setWindowTitle(m_settings.m_title);
+}
+
+void SimplePTTGUI::updateDeviceSetLists()
+{
+ MainWindow *mainWindow = MainWindow::getInstance();
+ std::vector& deviceUISets = mainWindow->getDeviceUISets();
+ std::vector::const_iterator it = deviceUISets.begin();
+
+ ui->rxDevice->blockSignals(true);
+ ui->txDevice->blockSignals(true);
+
+ ui->rxDevice->clear();
+ ui->txDevice->clear();
+ unsigned int deviceIndex = 0;
+ unsigned int rxIndex = 0;
+ unsigned int txIndex = 0;
+
+ for (; it != deviceUISets.end(); ++it, deviceIndex++)
+ {
+ DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine;
+ DSPDeviceSinkEngine *deviceSinkEngine = (*it)->m_deviceSinkEngine;
+
+ if (deviceSourceEngine)
+ {
+ ui->rxDevice->addItem(QString("R%1").arg(deviceIndex), deviceIndex);
+ rxIndex++;
+ }
+ else if (deviceSinkEngine)
+ {
+ ui->txDevice->addItem(QString("T%1").arg(deviceIndex), deviceIndex);
+ txIndex++;
+ }
+ }
+
+ if (rxIndex > 0)
+ {
+ if (m_settings.m_rxDeviceSetIndex < 0) {
+ ui->rxDevice->setCurrentIndex(0);
+ } else {
+ ui->rxDevice->setCurrentIndex(m_settings.m_rxDeviceSetIndex);
+ }
+ }
+
+ if (txIndex > 0)
+ {
+ if (m_settings.m_txDeviceSetIndex < 0) {
+ ui->txDevice->setCurrentIndex(0);
+ } else {
+ ui->txDevice->setCurrentIndex(m_settings.m_txDeviceSetIndex);
+ }
+ }
+
+ int rxDeviceIndex = ui->rxDevice->currentData().toInt();
+ int txDeviceIndex = ui->txDevice->currentData().toInt();
+
+ if ((rxDeviceIndex != m_settings.m_rxDeviceSetIndex) ||
+ (txDeviceIndex != m_settings.m_txDeviceSetIndex))
+ {
+ qDebug("SimplePTTGUI::updateDeviceSetLists: device index changed: %d:%d", rxDeviceIndex, txDeviceIndex);
+ m_settings.m_rxDeviceSetIndex = rxDeviceIndex;
+ m_settings.m_txDeviceSetIndex = txDeviceIndex;
+ applySettings();
+ }
+
+ ui->rxDevice->blockSignals(false);
+ ui->txDevice->blockSignals(false);
+}
+
+void SimplePTTGUI::leaveEvent(QEvent*)
+{
+}
+
+void SimplePTTGUI::enterEvent(QEvent*)
+{
+}
+
+void SimplePTTGUI::onMenuDialogCalled(const QPoint &p)
+{
+ if (m_contextMenuType == ContextMenuChannelSettings)
+ {
+ BasicFeatureSettingsDialog dialog(this);
+ dialog.setTitle(m_settings.m_title);
+ dialog.setColor(m_settings.m_rgbColor);
+
+ dialog.move(p);
+ dialog.exec();
+
+ m_settings.m_rgbColor = dialog.getColor().rgb();
+ m_settings.m_title = dialog.getTitle();
+
+ setWindowTitle(m_settings.m_title);
+ setTitleColor(m_settings.m_rgbColor);
+
+ applySettings();
+ }
+
+ resetContextMenuType();
+}
+
+void SimplePTTGUI::on_startStop_toggled(bool checked)
+{
+ if (m_doApplySettings)
+ {
+ SimplePTT::MsgStartStop *message = SimplePTT::MsgStartStop::create(checked);
+ m_simplePTT->getInputMessageQueue()->push(message);
+ }
+}
+
+void SimplePTTGUI::on_devicesRefresh_clicked()
+{
+ updateDeviceSetLists();
+ displaySettings();
+}
+
+void SimplePTTGUI::on_rxDevice_currentIndexChanged(int index)
+{
+ if (index >= 0)
+ {
+ m_settings.m_rxDeviceSetIndex = index;
+ applySettings();
+ }
+}
+
+void SimplePTTGUI::on_txDevice_currentIndexChanged(int index)
+{
+ if (index >= 0)
+ {
+ m_settings.m_txDeviceSetIndex = index;
+ applySettings();
+ }
+
+}
+
+void SimplePTTGUI::on_rxtxDelay_valueChanged(int value)
+{
+ m_settings.m_rx2TxDelayMs = value;
+ applySettings();
+}
+
+void SimplePTTGUI::on_txrxDelay_valueChanged(int value)
+{
+ m_settings.m_tx2RxDelayMs = value;
+ applySettings();
+}
+
+void SimplePTTGUI::on_ptt_toggled(bool checked)
+{
+ applyPTT(checked);
+}
+
+void SimplePTTGUI::updateStatus()
+{
+ int state = m_simplePTT->getState();
+
+ if (m_lastFeatureState != state)
+ {
+ switch (state)
+ {
+ case Feature::StNotStarted:
+ ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ break;
+ case Feature::StIdle:
+ ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
+ break;
+ case Feature::StRunning:
+ ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
+ break;
+ case Feature::StError:
+ ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
+ QMessageBox::information(this, tr("Message"), m_simplePTT->getErrorMessage());
+ break;
+ default:
+ break;
+ }
+
+ m_lastFeatureState = state;
+ }
+}
+
+void SimplePTTGUI::applySettings(bool force)
+{
+ if (m_doApplySettings)
+ {
+ SimplePTT::MsgConfigureSimplePTT* message = SimplePTT::MsgConfigureSimplePTT::create( m_settings, force);
+ m_simplePTT->getInputMessageQueue()->push(message);
+ }
+}
+
+void SimplePTTGUI::applyPTT(bool tx)
+{
+ if (m_doApplySettings)
+ {
+ SimplePTT::MsgPTT* message = SimplePTT::MsgPTT::create(tx);
+ m_simplePTT->getInputMessageQueue()->push(message);
+ }
+}
diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h
new file mode 100644
index 000000000..c94dc9a17
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttgui.h
@@ -0,0 +1,93 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTTGUI_H_
+#define INCLUDE_FEATURE_SIMPLEPTTGUI_H_
+
+#include
+
+#include "plugin/plugininstancegui.h"
+#include "gui/rollupwidget.h"
+#include "util/messagequeue.h"
+#include "simplepttsettings.h"
+
+class PluginAPI;
+class FeatureUISet;
+class SimplePTT;
+
+namespace Ui {
+ class SimplePTTGUI;
+}
+
+class SimplePTTGUI : public RollupWidget, public PluginInstanceGUI {
+ Q_OBJECT
+public:
+ static SimplePTTGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
+ virtual void destroy();
+ void setName(const QString& name);
+ QString getName() const;
+ virtual qint64 getCenterFrequency() const { return 0; }
+ virtual void setCenterFrequency(qint64 centerFrequency) {}
+
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+ virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
+ virtual bool handleMessage(const Message& message);
+
+private:
+ Ui::SimplePTTGUI* ui;
+ PluginAPI* m_pluginAPI;
+ FeatureUISet* m_featureUISet;
+ SimplePTTSettings m_settings;
+ bool m_doApplySettings;
+
+ SimplePTT* m_simplePTT;
+ MessageQueue m_inputMessageQueue;
+ QTimer m_statusTimer;
+ int m_lastFeatureState;
+ std::vector m_statusColors;
+ std::vector m_statusTooltips;
+
+ explicit SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
+ virtual ~SimplePTTGUI();
+
+ void blockApplySettings(bool block);
+ void applySettings(bool force = false);
+ void applyPTT(bool tx);
+ void displaySettings();
+ void updateDeviceSetLists();
+
+ void leaveEvent(QEvent*);
+ void enterEvent(QEvent*);
+
+private slots:
+ void onMenuDialogCalled(const QPoint &p);
+ void onWidgetRolled(QWidget* widget, bool rollDown);
+ void handleInputMessages();
+ void on_startStop_toggled(bool checked);
+ void on_devicesRefresh_clicked();
+ void on_rxDevice_currentIndexChanged(int index);
+ void on_txDevice_currentIndexChanged(int index);
+ void on_rxtxDelay_valueChanged(int value);
+ void on_txrxDelay_valueChanged(int value);
+ void on_ptt_toggled(bool checked);
+ void updateStatus();
+};
+
+
+#endif // INCLUDE_FEATURE_SIMPLEPTTGUI_H_
\ No newline at end of file
diff --git a/plugins/feature/simpleptt/simplepttgui.ui b/plugins/feature/simpleptt/simplepttgui.ui
new file mode 100644
index 000000000..ebe97b6fb
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttgui.ui
@@ -0,0 +1,327 @@
+
+
+ SimplePTTGUI
+
+
+
+ 0
+ 0
+ 320
+ 181
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 320
+ 100
+
+
+
+
+ 320
+ 16777215
+
+
+
+
+ Liberation Sans
+ 9
+
+
+
+ Simple PTT
+
+
+ Local Sink
+
+
+
+
+ 10
+ 10
+ 301
+ 151
+
+
+
+ Settings
+
+
+
+ 3
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
-
+
+
+ start/stop acquisition
+
+
+
+
+
+
+ :/play.png
+ :/stop.png:/play.png
+
+
+
+ -
+
+
+
+ 200
+ 50
+
+
+
+
+ 20
+ 75
+ true
+
+
+
+ Push To Talk
+
+
+ PTT
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 24
+ 24
+
+
+
+ Idle
+
+
+ QLabel { background-color: gray; border-radius: 12px; }
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 24
+ 16777215
+
+
+
+ Refresh indexes of available local devices
+
+
+
+
+
+
+ :/recycle.png:/recycle.png
+
+
+
+ -
+
+
+ Rx dev
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Receiver deviceset index
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Tx dev
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Transmitter deviceset index
+
+
+
+
+
+ -
+
+
-
+
+
+ Rx-Tx
+
+
+
+ -
+
+
+ Rx to Tx transition delay (ms)
+
+
+ 100
+
+
+ 5000
+
+
+ 100
+
+
+
+ -
+
+
+ ms
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ Tx-Rx
+
+
+
+ -
+
+
+ Tx to Rx transition delay (ms)
+
+
+ 100
+
+
+ 5000
+
+
+ 100
+
+
+
+ -
+
+
+ ms
+
+
+
+
+
+
+
+
+
+
+ RollupWidget
+ QWidget
+
+ 1
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+
+
+
+
+
diff --git a/plugins/feature/simpleptt/simplepttplugin.cpp b/plugins/feature/simpleptt/simplepttplugin.cpp
new file mode 100644
index 000000000..e99444ae4
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttplugin.cpp
@@ -0,0 +1,74 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+
+#include
+#include "plugin/pluginapi.h"
+
+#ifndef SERVER_MODE
+#include "simplepttgui.h"
+#endif
+#include "simpleptt.h"
+#include "simplepttplugin.h"
+
+const PluginDescriptor SimplePTTPlugin::m_pluginDescriptor = {
+ SimplePTT::m_featureId,
+ QString("Simple PTT"),
+ QString("5.12.0"),
+ QString("(c) Edouard Griffiths, F4EXB"),
+ QString("https://github.com/f4exb/sdrangel"),
+ true,
+ QString("https://github.com/f4exb/sdrangel")
+};
+
+SimplePTTPlugin::SimplePTTPlugin(QObject* parent) :
+ QObject(parent),
+ m_pluginAPI(nullptr)
+{
+}
+
+const PluginDescriptor& SimplePTTPlugin::getPluginDescriptor() const
+{
+ return m_pluginDescriptor;
+}
+
+void SimplePTTPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ m_pluginAPI = pluginAPI;
+
+ // register Simple PTT feature
+ m_pluginAPI->registerFeature(SimplePTT::m_featureIdURI, SimplePTT::m_featureId, this);
+}
+
+#ifdef SERVER_MODE
+PluginInstanceGUI* SimplePTTPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
+{
+ (void) featureUISet;
+ (void) feature;
+ return nullptr;
+}
+#else
+PluginInstanceGUI* SimplePTTPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
+{
+ return SimplePTTGUI::create(m_pluginAPI, featureUISet, feature);
+}
+#endif
+
+Feature* SimplePTTPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
+{
+ return new SimplePTT(webAPIAdapterInterface);
+}
diff --git a/plugins/feature/simpleptt/simplepttplugin.h b/plugins/feature/simpleptt/simplepttplugin.h
new file mode 100644
index 000000000..0d32b88fc
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttplugin.h
@@ -0,0 +1,46 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTTPLUGIN_H
+#define INCLUDE_FEATURE_SIMPLEPTTPLUGIN_H
+
+#include
+#include "plugin/plugininterface.h"
+
+class WebAPIAdapterInterface;
+
+class SimplePTTPlugin : public QObject, PluginInterface {
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID "sdrangel.feature.simpleptt")
+
+public:
+ explicit SimplePTTPlugin(QObject* parent = nullptr);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* pluginAPI);
+
+ virtual PluginInstanceGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
+ virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
+
+private:
+ static const PluginDescriptor m_pluginDescriptor;
+
+ PluginAPI* m_pluginAPI;
+};
+
+#endif // INCLUDE_FEATURE_SIMPLEPTTPLUGIN_H
diff --git a/plugins/feature/simpleptt/simplepttreport.cpp b/plugins/feature/simpleptt/simplepttreport.cpp
new file mode 100644
index 000000000..e84f57355
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttreport.cpp
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "simplepttreport.h"
+
+MESSAGE_CLASS_DEFINITION(SimplePTTReport::MsgRadioState, Message)
+
+SimplePTTReport::SimplePTTReport()
+{}
+
+SimplePTTReport::~SimplePTTReport()
+{}
diff --git a/plugins/feature/simpleptt/simplepttreport.h b/plugins/feature/simpleptt/simplepttreport.h
new file mode 100644
index 000000000..8f687b91b
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttreport.h
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTTREPORT_H_
+#define INCLUDE_FEATURE_SIMPLEPTTREPORT_H_
+
+#include "util/message.h"
+
+class SimplePTTReport
+{
+public:
+ enum RadioState {
+ RadioIdle,
+ RadioRx,
+ RadioTx
+ };
+ class MsgRadioState : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ RadioState getState() const { return m_state; }
+
+ static MsgRadioState* create(RadioState state)
+ {
+ return new MsgRadioState(state);
+ }
+
+ private:
+ RadioState m_state;
+
+ MsgRadioState(RadioState state) :
+ Message(),
+ m_state(state)
+ { }
+ };
+
+ SimplePTTReport();
+ ~SimplePTTReport();
+};
+
+#endif // INCLUDE_FEATURE_SIMPLEPTTREPORT_H_
diff --git a/plugins/feature/simpleptt/simplepttsettings.cpp b/plugins/feature/simpleptt/simplepttsettings.cpp
new file mode 100644
index 000000000..bfe9434d6
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttsettings.cpp
@@ -0,0 +1,85 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "util/simpleserializer.h"
+#include "settings/serializable.h"
+
+#include "simplepttsettings.h"
+
+SimplePTTSettings::SimplePTTSettings()
+{
+ resetToDefaults();
+}
+
+void SimplePTTSettings::resetToDefaults()
+{
+ m_title = "Simple PTT";
+ m_rgbColor = QColor(255, 0, 0).rgb();
+ m_rxDeviceSetIndex = -1;
+ m_txDeviceSetIndex = -1;
+ m_rx2TxDelayMs = 0;
+ m_tx2RxDelayMs = 0;
+}
+
+QByteArray SimplePTTSettings::serialize() const
+{
+ SimpleSerializer s(1);
+
+ s.writeString(1, m_title);
+ s.writeU32(2, m_rgbColor);
+ s.writeS32(3, m_rxDeviceSetIndex);
+ s.writeS32(4, m_txDeviceSetIndex);
+ s.writeU32(5, m_rx2TxDelayMs);
+ s.writeU32(6, m_tx2RxDelayMs);
+
+ return s.final();
+}
+
+bool SimplePTTSettings::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if(!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if(d.getVersion() == 1)
+ {
+ QByteArray bytetmp;
+ qint32 tmp;
+ uint32_t utmp;
+ QString strtmp;
+
+ d.readString(1, &m_title, "Simple PTT");
+ d.readU32(2, &m_rgbColor, QColor(255, 0, 0).rgb());
+ d.readS32(3, &m_rxDeviceSetIndex, -1);
+ d.readS32(4, &m_txDeviceSetIndex, -1);
+ d.readU32(5, &m_rx2TxDelayMs, 0);
+ d.readU32(6, &m_tx2RxDelayMs, 0);
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
diff --git a/plugins/feature/simpleptt/simplepttsettings.h b/plugins/feature/simpleptt/simplepttsettings.h
new file mode 100644
index 000000000..9d003141f
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttsettings.h
@@ -0,0 +1,41 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTTSETTINGS_H_
+#define INCLUDE_FEATURE_SIMPLEPTTSETTINGS_H_
+
+#include
+#include
+
+class Serializable;
+
+struct SimplePTTSettings
+{
+ QString m_title;
+ quint32 m_rgbColor;
+ int m_rxDeviceSetIndex;
+ int m_txDeviceSetIndex;
+ unsigned int m_rx2TxDelayMs;
+ unsigned int m_tx2RxDelayMs;
+
+ SimplePTTSettings();
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+};
+
+#endif // INCLUDE_FEATURE_SIMPLEPTTSETTINGS_H_
diff --git a/plugins/feature/simpleptt/simplepttworker.cpp b/plugins/feature/simpleptt/simplepttworker.cpp
new file mode 100644
index 000000000..c1d47d635
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttworker.cpp
@@ -0,0 +1,209 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "SWGDeviceState.h"
+#include "SWGSuccessResponse.h"
+#include "SWGErrorResponse.h"
+
+#include "webapi/webapiadapterinterface.h"
+
+#include "simplepttreport.h"
+#include "simplepttworker.h"
+
+MESSAGE_CLASS_DEFINITION(SimplePTTWorker::MsgConfigureSimplePTTWorker, Message)
+MESSAGE_CLASS_DEFINITION(SimplePTTWorker::MsgPTT, Message)
+
+SimplePTTWorker::SimplePTTWorker(WebAPIAdapterInterface *webAPIAdapterInterface) :
+ m_webAPIAdapterInterface(webAPIAdapterInterface),
+ m_msgQueueToGUI(nullptr),
+ m_running(false),
+ m_mutex(QMutex::Recursive)
+{
+ qDebug("SimplePTTWorker::SimplePTTWorker");
+ connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
+}
+
+SimplePTTWorker::~SimplePTTWorker()
+{
+ m_inputMessageQueue.clear();
+}
+
+void SimplePTTWorker::reset()
+{
+ QMutexLocker mutexLocker(&m_mutex);
+ m_inputMessageQueue.clear();
+}
+
+bool SimplePTTWorker::startWork()
+{
+ QMutexLocker mutexLocker(&m_mutex);
+ connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
+ m_running = true;
+ return m_running;
+}
+
+void SimplePTTWorker::stopWork()
+{
+ QMutexLocker mutexLocker(&m_mutex);
+ disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
+ m_running = false;
+}
+
+void SimplePTTWorker::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = m_inputMessageQueue.pop()) != nullptr)
+ {
+ if (handleMessage(*message)) {
+ delete message;
+ }
+ }
+}
+
+bool SimplePTTWorker::handleMessage(const Message& cmd)
+{
+ if (MsgConfigureSimplePTTWorker::match(cmd))
+ {
+ QMutexLocker mutexLocker(&m_mutex);
+ MsgConfigureSimplePTTWorker& cfg = (MsgConfigureSimplePTTWorker&) cmd;
+ qDebug() << "SimplePTTWorker::handleMessage: MsgConfigureSimplePTTWorker";
+
+ applySettings(cfg.getSettings(), cfg.getForce());
+
+ return true;
+ }
+ else if (MsgPTT::match(cmd))
+ {
+ MsgPTT& cfg = (MsgPTT&) cmd;
+ qDebug() << "SimplePTTWorker::handleMessage: MsgPTT";
+
+ sendPTT(cfg.getTx());
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void SimplePTTWorker::applySettings(const SimplePTTSettings& settings, bool force)
+{
+ qDebug() << "SimplePTTWorker::applySettings:"
+ << " m_title: " << settings.m_title
+ << " m_rgbColor: " << settings.m_rgbColor
+ << " m_rxDeviceSetIndex: " << settings.m_rxDeviceSetIndex
+ << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex
+ << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs
+ << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs
+ << " force: " << force;
+ m_settings = settings;
+}
+
+void SimplePTTWorker::sendPTT(bool tx)
+{
+ if (!m_updateTimer.isActive())
+ {
+ bool switchedOff = false;
+ m_mutex.lock();
+
+ if (tx)
+ {
+ if (m_settings.m_rxDeviceSetIndex >= 0)
+ {
+ m_tx = false;
+ switchedOff = turnDevice(false);
+ }
+
+ if (m_settings.m_txDeviceSetIndex >= 0)
+ {
+ m_tx = true;
+ m_updateTimer.start(m_settings.m_rx2TxDelayMs);
+ }
+ }
+ else
+ {
+ if (m_settings.m_txDeviceSetIndex >= 0)
+ {
+ m_tx = true;
+ switchedOff = turnDevice(false);
+ }
+
+ if (m_settings.m_rxDeviceSetIndex >= 0)
+ {
+ m_tx = false;
+ m_updateTimer.start(m_settings.m_tx2RxDelayMs);
+ }
+ }
+
+ if (switchedOff && (m_msgQueueToGUI))
+ {
+ SimplePTTReport::MsgRadioState *msg = SimplePTTReport::MsgRadioState::create(SimplePTTReport::RadioIdle);
+ m_msgQueueToGUI->push(msg);
+ }
+ }
+}
+
+void SimplePTTWorker::updateHardware()
+{
+ SWGSDRangel::SWGSuccessResponse response;
+ SWGSDRangel::SWGErrorResponse error;
+ m_updateTimer.stop();
+ m_mutex.unlock();
+
+ if (turnDevice(true))
+ {
+ m_webAPIAdapterInterface->devicesetFocusPatch(
+ m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error);
+
+ if (m_msgQueueToGUI)
+ {
+ SimplePTTReport::MsgRadioState *msg = SimplePTTReport::MsgRadioState::create(
+ m_tx ? SimplePTTReport::RadioTx : SimplePTTReport::RadioRx
+ );
+ m_msgQueueToGUI->push(msg);
+ }
+ }
+}
+
+bool SimplePTTWorker::turnDevice(bool on)
+{
+ SWGSDRangel::SWGDeviceState response;
+ SWGSDRangel::SWGErrorResponse error;
+ int httpCode;
+
+ if (on) {
+ httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost(
+ m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error);
+ } else {
+ httpCode = m_webAPIAdapterInterface->devicesetDeviceRunDelete(
+ m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error);
+ }
+
+ if (httpCode/100 == 2)
+ {
+ return true;
+ }
+ else
+ {
+ qWarning("SimplePTTWorker::turnDevice: error: %s", qPrintable(*error.getMessage()));
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/plugins/feature/simpleptt/simplepttworker.h b/plugins/feature/simpleptt/simplepttworker.h
new file mode 100644
index 000000000..b064021e6
--- /dev/null
+++ b/plugins/feature/simpleptt/simplepttworker.h
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 Edouard Griffiths, F4EXB //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_FEATURE_SIMPLEPTTWORKER_H_
+#define INCLUDE_FEATURE_SIMPLEPTTWORKER_H_
+
+#include
+#include
+
+#include "util/message.h"
+#include "util/messagequeue.h"
+
+#include "simplepttsettings.h"
+
+class WebAPIAdapterInterface;
+
+class SimplePTTWorker : public QObject
+{
+ Q_OBJECT
+public:
+ class MsgConfigureSimplePTTWorker : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const SimplePTTSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigureSimplePTTWorker* create(const SimplePTTSettings& settings, bool force)
+ {
+ return new MsgConfigureSimplePTTWorker(settings, force);
+ }
+
+ private:
+ SimplePTTSettings m_settings;
+ bool m_force;
+
+ MsgConfigureSimplePTTWorker(const SimplePTTSettings& settings, bool force) :
+ Message(),
+ m_settings(settings),
+ m_force(force)
+ { }
+ };
+
+ class MsgPTT : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getTx() const { return m_tx; }
+
+ static MsgPTT* create(bool tx) {
+ return new MsgPTT(tx);
+ }
+
+ private:
+ bool m_tx;
+
+ MsgPTT(bool tx) :
+ Message(),
+ m_tx(tx)
+ { }
+ };
+
+ SimplePTTWorker(WebAPIAdapterInterface *webAPIAdapterInterface);
+ ~SimplePTTWorker();
+ void reset();
+ bool startWork();
+ void stopWork();
+ bool isRunning() const { return m_running; }
+ MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
+ void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; }
+
+private:
+ WebAPIAdapterInterface *m_webAPIAdapterInterface;
+ MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
+ MessageQueue *m_msgQueueToGUI; //!< Queue to report state to GUI
+ SimplePTTSettings m_settings;
+ bool m_running;
+ bool m_tx;
+ QTimer m_updateTimer;
+ QMutex m_mutex;
+
+ bool handleMessage(const Message& cmd);
+ void applySettings(const SimplePTTSettings& settings, bool force = false);
+ void sendPTT(bool tx);
+ bool turnDevice(bool on);
+
+private slots:
+ void handleInputMessages();
+ void updateHardware();
+};
+
+#endif // INCLUDE_FEATURE_SIMPLEPTTWORKER_H_
\ No newline at end of file