diff --git a/plugins/channelrx/daemonsink/CMakeLists.txt b/plugins/channelrx/daemonsink/CMakeLists.txt index 5c52986ce..8a21d9f50 100644 --- a/plugins/channelrx/daemonsink/CMakeLists.txt +++ b/plugins/channelrx/daemonsink/CMakeLists.txt @@ -4,18 +4,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(daemonsink_SOURCES daemonsink.cpp -# daemonsinkgui.cpp + daemonsinkgui.cpp daemonsinksettings.cpp daemonsinkthread.cpp -# daemonsinkplugin.cpp + daemonsinkplugin.cpp ) set(daemonsink_HEADERS daemonsink.h -# daemonsinkgui.h + daemonsinkgui.h daemonsinksettings.h daemonsinkthread.h -# daemonsinkplugin.h + daemonsinkplugin.h ) set(daemonsink_FORMS diff --git a/plugins/channelrx/daemonsink/daemonsink.cpp b/plugins/channelrx/daemonsink/daemonsink.cpp index b30e140ae..206860aa6 100644 --- a/plugins/channelrx/daemonsink/daemonsink.cpp +++ b/plugins/channelrx/daemonsink/daemonsink.cpp @@ -36,6 +36,7 @@ #include "daemonsink.h" MESSAGE_CLASS_DEFINITION(DaemonSink::MsgConfigureDaemonSink, Message) +MESSAGE_CLASS_DEFINITION(DaemonSink::MsgSampleRateNotification, Message) const QString DaemonSink::m_channelIdURI = "sdrangel.channel.daemonsink"; const QString DaemonSink::m_channelId = "DaemonSink"; @@ -237,6 +238,12 @@ bool DaemonSink::handleMessage(const Message& cmd __attribute__((unused))) setSampleRate(notif.getSampleRate()); } + if (m_guiMessageQueue) + { + MsgSampleRateNotification *msg = MsgSampleRateNotification::create(notif.getSampleRate()); + m_guiMessageQueue->push(msg); + } + return true; } else if (DSPSignalNotification::match(cmd)) diff --git a/plugins/channelrx/daemonsink/daemonsink.h b/plugins/channelrx/daemonsink/daemonsink.h index 247f0e1d9..8b0e38c1a 100644 --- a/plugins/channelrx/daemonsink/daemonsink.h +++ b/plugins/channelrx/daemonsink/daemonsink.h @@ -23,6 +23,7 @@ #ifndef INCLUDE_DAEMONSINK_H_ #define INCLUDE_DAEMONSINK_H_ +#include #include #include "cm256.h" @@ -64,6 +65,26 @@ public: { } }; + class MsgSampleRateNotification : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgSampleRateNotification* create(int sampleRate) { + return new MsgSampleRateNotification(sampleRate); + } + + int getSampleRate() const { return m_sampleRate; } + + private: + + MsgSampleRateNotification(int sampleRate) : + Message(), + m_sampleRate(sampleRate) + { } + + int m_sampleRate; + }; + DaemonSink(DeviceSourceAPI *deviceAPI); virtual ~DaemonSink(); virtual void destroy() { delete this; } diff --git a/plugins/channelrx/daemonsink/daemonsinkgui.cpp b/plugins/channelrx/daemonsink/daemonsinkgui.cpp new file mode 100644 index 000000000..908d80163 --- /dev/null +++ b/plugins/channelrx/daemonsink/daemonsinkgui.cpp @@ -0,0 +1,291 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 "device/devicesinkapi.h" +#include "device/deviceuiset.h" +#include "gui/basicchannelsettingsdialog.h" +#include "mainwindow.h" + +#include "daemonsink.h" +#include "ui_daemonsinkgui.h" +#include "daemonsinkgui.h" + +DaemonSinkGUI* DaemonSinkGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelRx) +{ + DaemonSinkGUI* gui = new DaemonSinkGUI(pluginAPI, deviceUISet, channelRx); + return gui; +} + +void DaemonSinkGUI::destroy() +{ + delete this; +} + +void DaemonSinkGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString DaemonSinkGUI::getName() const +{ + return objectName(); +} + +qint64 DaemonSinkGUI::getCenterFrequency() const { + return 0; +} + +void DaemonSinkGUI::setCenterFrequency(qint64 centerFrequency __attribute__((unused))) +{ +} + +void DaemonSinkGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray DaemonSinkGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool DaemonSinkGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applySettings(true); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool DaemonSinkGUI::handleMessage(const Message& message) +{ + if (DaemonSink::MsgSampleRateNotification::match(message)) + { + DaemonSink::MsgSampleRateNotification& notif = (DaemonSink::MsgSampleRateNotification&) message; + m_channelMarker.setBandwidth(notif.getSampleRate()); + m_sampleRate = notif.getSampleRate(); + return true; + } + else if (DaemonSink::MsgConfigureDaemonSink::match(message)) + { + const DaemonSink::MsgConfigureDaemonSink& cfg = (DaemonSink::MsgConfigureDaemonSink&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +DaemonSinkGUI::DaemonSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelrx, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::DaemonSinkGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_sampleRate(0), + m_tickCount(0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + + m_daemonSink = (DaemonSink*) channelrx; + m_daemonSink->setMessageQueueToGUI(getInputMessageQueue()); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(m_settings.m_rgbColor); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("Daemon source"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + m_settings.setChannelMarker(&m_channelMarker); + + m_deviceUISet->registerRxChannelInstance(DaemonSink::m_channelIdURI, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); + connect(&(m_deviceUISet->m_deviceSinkAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + + m_time.start(); + + displaySettings(); + applySettings(true); +} + +DaemonSinkGUI::~DaemonSinkGUI() +{ + m_deviceUISet->removeRxChannelInstance(this); + delete ui; +} + +void DaemonSinkGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void DaemonSinkGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + setTitleColor(m_channelMarker.getColor()); + + DaemonSink::MsgConfigureDaemonSink* message = DaemonSink::MsgConfigureDaemonSink::create(m_settings, force); + m_daemonSink->getInputMessageQueue()->push(message); + } +} + +void DaemonSinkGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.setBandwidth(5000); // TODO + m_channelMarker.blockSignals(false); + m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only + + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_channelMarker.getTitle()); + + blockApplySettings(true); + ui->dataAddress->setText(m_settings.m_dataAddress); + ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort)); + blockApplySettings(false); +} + +void DaemonSinkGUI::leaveEvent(QEvent*) +{ + m_channelMarker.setHighlighted(false); +} + +void DaemonSinkGUI::enterEvent(QEvent*) +{ + m_channelMarker.setHighlighted(true); +} + +void DaemonSinkGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void DaemonSinkGUI::onWidgetRolled(QWidget* widget __attribute__((unused)), bool rollDown __attribute__((unused))) +{ +} + +void DaemonSinkGUI::onMenuDialogCalled(const QPoint &p) +{ + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.move(p); + dialog.exec(); + + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + + setWindowTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); +} + +void DaemonSinkGUI::on_dataAddress_returnPressed() +{ + m_settings.m_dataAddress = ui->dataAddress->text(); + applySettings(); +} + +void DaemonSinkGUI::on_dataPort_returnPressed() +{ + bool dataOk; + int dataPort = ui->dataPort->text().toInt(&dataOk); + + if((!dataOk) || (dataPort < 1024) || (dataPort > 65535)) + { + return; + } + else + { + m_settings.m_dataPort = dataPort; + } + + applySettings(); +} + +void DaemonSinkGUI::on_dataApplyButton_clicked(bool checked __attribute__((unused))) +{ + m_settings.m_dataAddress = ui->dataAddress->text(); + + bool dataOk; + int udpDataPort = ui->dataPort->text().toInt(&dataOk); + + if((dataOk) && (udpDataPort >= 1024) && (udpDataPort < 65535)) + { + m_settings.m_dataPort = udpDataPort; + } + + applySettings(); +} + +void DaemonSinkGUI::on_txDelay_valueChanged(int value) +{ + m_settings.m_txDelay = value / 100.0; + ui->txDelayText->setText(tr("%1").arg(value)); + updateTxDelayTooltip(); + applySettings(); +} + +void DaemonSinkGUI::on_nbFECBlocks_valueChanged(int value) +{ + m_settings.m_nbFECBlocks = value; + int nbOriginalBlocks = 128; + int nbFECBlocks = value; + QString s = QString::number(nbOriginalBlocks + nbFECBlocks, 'f', 0); + QString s1 = QString::number(nbFECBlocks, 'f', 0); + ui->nominalNbBlocksText->setText(tr("%1/%2").arg(s).arg(s1)); + updateTxDelayTooltip(); + applySettings(); +} + +void DaemonSinkGUI::updateTxDelayTooltip() +{ + double delay = m_sampleRate == 0 ? 0.0 : ((127*127*m_settings.m_txDelay) / m_sampleRate)/(128 + m_settings.m_nbFECBlocks); + ui->txDelayText->setToolTip(tr("%1 us").arg(QString::number(delay*1e6, 'f', 0))); +} + +void DaemonSinkGUI::tick() +{ + if (++m_tickCount == 20) { // once per second + m_tickCount = 0; + } +} diff --git a/plugins/channelrx/daemonsink/daemonsinkgui.h b/plugins/channelrx/daemonsink/daemonsinkgui.h new file mode 100644 index 000000000..a161b8ebf --- /dev/null +++ b/plugins/channelrx/daemonsink/daemonsinkgui.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 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 PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKGUI_H_ +#define PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKGUI_H_ + +#include +#include + +#include "plugin/plugininstancegui.h" +#include "dsp/channelmarker.h" +#include "gui/rollupwidget.h" +#include "util/messagequeue.h" + +#include "daemonsinksettings.h" + +class PluginAPI; +class DeviceUISet; +class DaemonSink; +class BasebandSampleSink; + +namespace Ui { + class DaemonSinkGUI; +} + +class DaemonSinkGUI : public RollupWidget, public PluginInstanceGUI { + Q_OBJECT +public: + static DaemonSinkGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + virtual void destroy(); + + void setName(const QString& name); + QString getName() const; + virtual qint64 getCenterFrequency() const; + 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::DaemonSinkGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + DaemonSinkSettings m_settings; + int m_sampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + bool m_doApplySettings; + + DaemonSink* m_daemonSink; + MessageQueue m_inputMessageQueue; + + QTime m_time; + uint32_t m_tickCount; + + explicit DaemonSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); + virtual ~DaemonSinkGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void displaySettings(); + void updateTxDelayTooltip(); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + +private slots: + void handleSourceMessages(); + void on_dataAddress_returnPressed(); + void on_dataPort_returnPressed(); + void on_dataApplyButton_clicked(bool checked); + void on_nbFECBlocks_valueChanged(int value); + void on_txDelay_valueChanged(int value); + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + void tick(); +}; + + + +#endif /* PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKGUI_H_ */ diff --git a/plugins/channelrx/daemonsink/daemonsinkgui.ui b/plugins/channelrx/daemonsink/daemonsinkgui.ui index 138446c19..8fd0dc41c 100644 --- a/plugins/channelrx/daemonsink/daemonsinkgui.ui +++ b/plugins/channelrx/daemonsink/daemonsinkgui.ui @@ -139,7 +139,7 @@ - + 30 diff --git a/plugins/channelrx/daemonsink/daemonsinkplugin.cpp b/plugins/channelrx/daemonsink/daemonsinkplugin.cpp new file mode 100644 index 000000000..8de1289c0 --- /dev/null +++ b/plugins/channelrx/daemonsink/daemonsinkplugin.cpp @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "daemonsinkplugin.h" + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "daemonsinkgui.h" +#endif +#include "daemonsink.h" + +const PluginDescriptor DaemonSinkPlugin::m_pluginDescriptor = { + QString("Daemon Channel Sink"), + QString("4.1.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +DaemonSinkPlugin::DaemonSinkPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& DaemonSinkPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void DaemonSinkPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register TCP Channel Source + m_pluginAPI->registerRxChannel(DaemonSink::m_channelIdURI, DaemonSink::m_channelId, this); +} + +#ifdef SERVER_MODE +PluginInstanceGUI* DaemonSinkPlugin::createRxChannelGUI( + DeviceUISet *deviceUISet __attribute__((unused)), + BasebandSampleSink *rxChannel __attribute__((unused))) +{ + return 0; +} +#else +PluginInstanceGUI* DaemonSinkPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) +{ + return DaemonSinkGUI::create(m_pluginAPI, deviceUISet, rxChannel); +} +#endif + +BasebandSampleSink* DaemonSinkPlugin::createRxChannelBS(DeviceSourceAPI *deviceAPI) +{ + return new DaemonSink(deviceAPI); +} + +ChannelSinkAPI* DaemonSinkPlugin::createRxChannelCS(DeviceSourceAPI *deviceAPI) +{ + return new DaemonSink(deviceAPI); +} + + + + diff --git a/plugins/channelrx/daemonsink/daemonsinkplugin.h b/plugins/channelrx/daemonsink/daemonsinkplugin.h new file mode 100644 index 000000000..16e0792db --- /dev/null +++ b/plugins/channelrx/daemonsink/daemonsinkplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKPLUGIN_H_ +#define PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKPLUGIN_H_ + + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSink; + +class DaemonSinkPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.demod.daemonsink") + +public: + explicit DaemonSinkPlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual PluginInstanceGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + virtual BasebandSampleSink* createRxChannelBS(DeviceSourceAPI *deviceAPI); + virtual ChannelSinkAPI* createRxChannelCS(DeviceSourceAPI *deviceAPI); + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif /* PLUGINS_CHANNELRX_DAEMONSINK_DAEMONSINKPLUGIN_H_ */ diff --git a/plugins/channelrx/daemonsink/daemonsinksettings.cpp b/plugins/channelrx/daemonsink/daemonsinksettings.cpp index 6d2cfac79..d63435d8d 100644 --- a/plugins/channelrx/daemonsink/daemonsinksettings.cpp +++ b/plugins/channelrx/daemonsink/daemonsinksettings.cpp @@ -20,6 +20,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include + #include "util/simpleserializer.h" #include "settings/serializable.h" #include "daemonsinksettings.h" @@ -44,6 +46,8 @@ QByteArray DaemonSinkSettings::serialize() const s.writeU32(2, m_txDelay); s.writeString(3, m_dataAddress); s.writeU32(4, m_dataPort); + s.writeU32(5, m_rgbColor); + s.writeString(6, m_title); return s.final(); } @@ -81,6 +85,9 @@ bool DaemonSinkSettings::deserialize(const QByteArray& data) m_dataPort = 9090; } + d.readU32(5, &m_rgbColor, QColor(0, 255, 255).rgb()); + d.readString(6, &m_title, "Daemon sink"); + return true; } else diff --git a/plugins/channelrx/daemonsink/daemonsinksettings.h b/plugins/channelrx/daemonsink/daemonsinksettings.h index e0dd8fc55..49584c57c 100644 --- a/plugins/channelrx/daemonsink/daemonsinksettings.h +++ b/plugins/channelrx/daemonsink/daemonsinksettings.h @@ -33,9 +33,14 @@ struct DaemonSinkSettings uint32_t m_txDelay; QString m_dataAddress; uint16_t m_dataPort; + quint32 m_rgbColor; + QString m_title; + + Serializable *m_channelMarker; DaemonSinkSettings(); void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } QByteArray serialize() const; bool deserialize(const QByteArray& data); }; diff --git a/plugins/channeltx/daemonsrc/daemonsrcsettings.cpp b/plugins/channeltx/daemonsrc/daemonsrcsettings.cpp index 30db27dc6..62e8a4dcf 100644 --- a/plugins/channeltx/daemonsrc/daemonsrcsettings.cpp +++ b/plugins/channeltx/daemonsrc/daemonsrcsettings.cpp @@ -71,7 +71,7 @@ bool DaemonSrcSettings::deserialize(const QByteArray& data) } d.readU32(3, &m_rgbColor, QColor(0, 255, 255).rgb()); - d.readString(4, &m_title, "AM Modulator"); + d.readString(4, &m_title, "Daemon source"); return true; }