From 2cb96ed187f8e66623178de603ec8bdefe1f7df2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 8 Jul 2020 01:46:35 +0200 Subject: [PATCH] SigMF file sink: initial commit --- plugins/channelrx/CMakeLists.txt | 4 + .../channelrx/sigmffilesink/CMakeLists.txt | 59 +++ .../channelrx/sigmffilesink/sigmffilesink.cpp | 436 ++++++++++++++++++ .../channelrx/sigmffilesink/sigmffilesink.h | 161 +++++++ .../sigmffilesink/sigmffilesinkbaseband.cpp | 166 +++++++ .../sigmffilesink/sigmffilesinkbaseband.h | 107 +++++ .../sigmffilesink/sigmffilesinkgui.cpp | 339 ++++++++++++++ .../sigmffilesink/sigmffilesinkgui.h | 101 ++++ .../sigmffilesink/sigmffilesinkgui.ui | 350 ++++++++++++++ .../sigmffilesink/sigmffilesinkplugin.cpp | 85 ++++ .../sigmffilesink/sigmffilesinkplugin.h | 50 ++ .../sigmffilesink/sigmffilesinksettings.cpp | 119 +++++ .../sigmffilesink/sigmffilesinksettings.h | 51 ++ .../sigmffilesink/sigmffilesinksink.cpp | 57 +++ .../sigmffilesink/sigmffilesinksink.h | 54 +++ .../sigmffilesinkwebapiadapter.cpp | 51 ++ .../sigmffilesinkwebapiadapter.h | 49 ++ 17 files changed, 2239 insertions(+) create mode 100644 plugins/channelrx/sigmffilesink/CMakeLists.txt create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesink.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesink.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkgui.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkplugin.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkplugin.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinksettings.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinksink.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinksink.h create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.cpp create mode 100644 plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.h diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 3b65fc952..d6c03d9ce 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -22,6 +22,10 @@ if (CODEC2_FOUND) add_subdirectory(demodfreedv) endif(CODEC2_FOUND) +if (LIBSIGMF_FOUND) + add_subdirectory(sigmffilesink) +endif(LIBSIGMF_FOUND) + if(NOT SERVER_MODE) add_subdirectory(chanalyzer) add_subdirectory(demodatv) diff --git a/plugins/channelrx/sigmffilesink/CMakeLists.txt b/plugins/channelrx/sigmffilesink/CMakeLists.txt new file mode 100644 index 000000000..447d4ba65 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/CMakeLists.txt @@ -0,0 +1,59 @@ +project(sigmffilesink) + +set(sigmffilesink_SOURCES + sigmffilesink.cpp + sigmffilesinkbaseband.cpp + sigmffilesinksink.cpp + sigmffilesinksettings.cpp + sigmffilesinkwebapiadapter.cpp + sigmffilesinkplugin.cpp +) + +set(sigmffilesink_HEADERS + sigmffilesink.h + sigmffilesinkbaseband.h + sigmffilesinksink.h + sigmffilesinksettings.h + sigmffilesinkwebapiadapter.h + sigmffilesinkplugin.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${Boost_INCLUDE_DIR} +) + +if(NOT SERVER_MODE) + set(sigmffilesink_SOURCES + ${sigmffilesink_SOURCES} + sigmffilesinkgui.cpp + sigmffilesinkgui.ui + ) + set(sigmffilesink_HEADERS + ${sigmffilesink_HEADERS} + sigmffilesinkgui.h + ) + set(TARGET_NAME sigmffilesink) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME sigmffilesinksrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${sigmffilesink_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp new file mode 100644 index 000000000..fc235e10f --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp @@ -0,0 +1,436 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 + +#include +#include +#include + +#include "SWGChannelSettings.h" + +#include "util/simpleserializer.h" +#include "dsp/dspcommands.h" +#include "dsp/dspdevicesourceengine.h" +#include "dsp/dspengine.h" +#include "dsp/devicesamplesource.h" +#include "dsp/hbfilterchainconverter.h" +#include "dsp/devicesamplemimo.h" +#include "device/deviceapi.h" + +#include "sigmffilesinkbaseband.h" +#include "sigmffilesink.h" + +MESSAGE_CLASS_DEFINITION(SigMFFileSink::MsgConfigureSigMFFileSink, Message) +MESSAGE_CLASS_DEFINITION(SigMFFileSink::MsgBasebandSampleRateNotification, Message) + +const QString SigMFFileSink::m_channelIdURI = "sdrangel.channel.sigmffilesink"; +const QString SigMFFileSink::m_channelId = "SigMFFileSink"; + +SigMFFileSink::SigMFFileSink(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), + m_deviceAPI(deviceAPI), + m_centerFrequency(0), + m_frequencyOffset(0), + m_basebandSampleRate(48000) +{ + setObjectName(m_channelId); + + m_thread = new QThread(this); + m_basebandSink = new SigMFFileSinkBaseband(); + m_basebandSink->moveToThread(m_thread); + + applySettings(m_settings, true); + + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +SigMFFileSink::~SigMFFileSink() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; +} + +uint32_t SigMFFileSink::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + +void SigMFFileSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +{ + (void) firstOfBurst; + m_basebandSink->feed(begin, end); +} + +void SigMFFileSink::start() +{ + qDebug("SigMFFileSink::start"); + m_basebandSink->reset(); + m_thread->start(); +} + +void SigMFFileSink::stop() +{ + qDebug("SigMFFileSink::stop"); + m_thread->exit(); + m_thread->wait(); +} + +bool SigMFFileSink::handleMessage(const Message& cmd) +{ + if (DSPSignalNotification::match(cmd)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + + qDebug() << "SigMFFileSink::handleMessage: DSPSignalNotification:" + << " inputSampleRate: " << notif.getSampleRate() + << " centerFrequency: " << notif.getCenterFrequency(); + + m_basebandSampleRate = notif.getSampleRate(); + m_centerFrequency = notif.getCenterFrequency(); + + calculateFrequencyOffset(m_settings.m_log2Decim, m_settings.m_filterChainHash); // This is when device sample rate changes + + DSPSignalNotification *msg = new DSPSignalNotification(notif.getSampleRate(), notif.getCenterFrequency()); + m_basebandSink->getInputMessageQueue()->push(msg); + + if (getMessageQueueToGUI()) + { + MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(notif.getSampleRate()); + getMessageQueueToGUI()->push(msg); + } + + return true; + } + else if (MsgConfigureSigMFFileSink::match(cmd)) + { + MsgConfigureSigMFFileSink& cfg = (MsgConfigureSigMFFileSink&) cmd; + qDebug() << "SigMFFileSink::handleMessage: MsgConfigureSigMFFileSink"; + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else + { + return false; + } +} + +QByteArray SigMFFileSink::serialize() const +{ + return m_settings.serialize(); +} + +bool SigMFFileSink::deserialize(const QByteArray& data) +{ + (void) data; + if (m_settings.deserialize(data)) + { + MsgConfigureSigMFFileSink *msg = MsgConfigureSigMFFileSink::create(m_settings, true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureSigMFFileSink *msg = MsgConfigureSigMFFileSink::create(m_settings, true); + m_inputMessageQueue.push(msg); + return false; + } +} + +void SigMFFileSink::getLocalDevices(std::vector& indexes) +{ + indexes.clear(); + DSPEngine *dspEngine = DSPEngine::instance(); + + for (uint32_t i = 0; i < dspEngine->getDeviceSourceEnginesNumber(); i++) + { + DSPDeviceSourceEngine *deviceSourceEngine = dspEngine->getDeviceSourceEngineByIndex(i); + DeviceSampleSource *deviceSource = deviceSourceEngine->getSource(); + + if (deviceSource->getDeviceDescription() == "LocalInput") { + indexes.push_back(i); + } + } +} + +DeviceSampleSource *SigMFFileSink::getLocalDevice(uint32_t index) +{ + DSPEngine *dspEngine = DSPEngine::instance(); + + if (index < dspEngine->getDeviceSourceEnginesNumber()) + { + DSPDeviceSourceEngine *deviceSourceEngine = dspEngine->getDeviceSourceEngineByIndex(index); + DeviceSampleSource *deviceSource = deviceSourceEngine->getSource(); + + if (deviceSource->getDeviceDescription() == "LocalInput") + { + if (!getDeviceAPI()) { + qDebug("SigMFFileSink::getLocalDevice: the parent device is unset"); + } else if (getDeviceAPI()->getDeviceUID() == deviceSourceEngine->getUID()) { + qDebug("SigMFFileSink::getLocalDevice: source device at index %u is the parent device", index); + } else { + return deviceSource; + } + } + else + { + qDebug("SigMFFileSink::getLocalDevice: source device at index %u is not a Local Input source", index); + } + } + else + { + qDebug("SigMFFileSink::getLocalDevice: non existent source device index: %u", index); + } + + return nullptr; +} + +void SigMFFileSink::applySettings(const SigMFFileSinkSettings& settings, bool force) +{ + qDebug() << "SigMFFileSink::applySettings:" + << "force: " << force; + + QList reverseAPIKeys; + + if ((settings.m_log2Decim != m_settings.m_log2Decim) || force) { + reverseAPIKeys.append("log2Decim"); + } + if ((settings.m_filterChainHash != m_settings.m_filterChainHash) || force) { + reverseAPIKeys.append("filterChainHash"); + } + + if ((settings.m_log2Decim != m_settings.m_log2Decim) + || (settings.m_filterChainHash != m_settings.m_filterChainHash) || force) + { + calculateFrequencyOffset(settings.m_log2Decim, settings.m_filterChainHash); + } + + SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkBaseband *msg = SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + + if ((settings.m_useReverseAPI) && (reverseAPIKeys.size() != 0)) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; +} + +void SigMFFileSink::record(bool record) +{ + SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkWork *msg = SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkWork::create(record); + m_basebandSink->getInputMessageQueue()->push(msg); +} + +void SigMFFileSink::validateFilterChainHash(SigMFFileSinkSettings& settings) +{ + unsigned int s = 1; + + for (unsigned int i = 0; i < settings.m_log2Decim; i++) { + s *= 3; + } + + settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash; +} + +void SigMFFileSink::calculateFrequencyOffset(uint32_t log2Decim, uint32_t filterChainHash) +{ + double shiftFactor = HBFilterChainConverter::getShiftFactor(log2Decim, filterChainHash); + m_frequencyOffset = m_basebandSampleRate * shiftFactor; +} + +int SigMFFileSink::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings()); + response.getLocalSinkSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + return 200; +} + +int SigMFFileSink::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + SigMFFileSinkSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigureSigMFFileSink *msg = MsgConfigureSigMFFileSink::create(settings, force); + m_inputMessageQueue.push(msg); + + qDebug("SigMFFileSink::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureSigMFFileSink *msgToGUI = MsgConfigureSigMFFileSink::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +void SigMFFileSink::webapiUpdateChannelSettings( + SigMFFileSinkSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getLocalSinkSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getLocalSinkSettings()->getTitle(); + } + if (channelSettingsKeys.contains("log2Decim")) { + settings.m_log2Decim = response.getLocalSinkSettings()->getLog2Decim(); + } + + if (channelSettingsKeys.contains("filterChainHash")) + { + settings.m_filterChainHash = response.getLocalSinkSettings()->getFilterChainHash(); + validateFilterChainHash(settings); + } + + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getLocalSinkSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getLocalSinkSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getLocalSinkSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getLocalSinkSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getLocalSinkSettings()->getReverseApiChannelIndex(); + } +} + +void SigMFFileSink::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const SigMFFileSinkSettings& settings) +{ + response.getLocalSinkSettings()->setRgbColor(settings.m_rgbColor); + + if (response.getLocalSinkSettings()->getTitle()) { + *response.getLocalSinkSettings()->getTitle() = settings.m_title; + } else { + response.getLocalSinkSettings()->setTitle(new QString(settings.m_title)); + } + + response.getLocalSinkSettings()->setLog2Decim(settings.m_log2Decim); + response.getLocalSinkSettings()->setFilterChainHash(settings.m_filterChainHash); + response.getLocalSinkSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getLocalSinkSettings()->getReverseApiAddress()) { + *response.getLocalSinkSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getLocalSinkSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getLocalSinkSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getLocalSinkSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getLocalSinkSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); +} + +void SigMFFileSink::webapiReverseSendSettings(QList& channelSettingsKeys, const SigMFFileSinkSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setDirection(0); // single sink (Rx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString("SigMFFileSink")); + swgChannelSettings->setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings()); + SWGSDRangel::SWGLocalSinkSettings *swgLocalSinkSettings = swgChannelSettings->getLocalSinkSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("rgbColor") || force) { + swgLocalSinkSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgLocalSinkSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("log2Decim") || force) { + swgLocalSinkSettings->setLog2Decim(settings.m_log2Decim); + } + if (channelSettingsKeys.contains("filterChainHash") || force) { + swgLocalSinkSettings->setFilterChainHash(settings.m_filterChainHash); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgChannelSettings; +} + +void SigMFFileSink::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "v::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("SigMFFileSink::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.h b/plugins/channelrx/sigmffilesink/sigmffilesink.h new file mode 100644 index 000000000..e1a591175 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.h @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_SIGMFFILESINK_H_ +#define INCLUDE_SIGMFFILESINK_H_ + +#include +#include +#include + +#include "dsp/basebandsamplesink.h" +#include "channel/channelapi.h" + +#include "sigmffilesinksettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; + +class DeviceAPI; +class DeviceSampleSource; +class SigMFFileSinkBaseband; + +class SigMFFileSink : public BasebandSampleSink, public ChannelAPI { + Q_OBJECT +public: + class MsgConfigureSigMFFileSink : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const SigMFFileSinkSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureSigMFFileSink* create(const SigMFFileSinkSettings& settings, bool force) + { + return new MsgConfigureSigMFFileSink(settings, force); + } + + private: + SigMFFileSinkSettings m_settings; + bool m_force; + + MsgConfigureSigMFFileSink(const SigMFFileSinkSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgBasebandSampleRateNotification : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgBasebandSampleRateNotification* create(int sampleRate) { + return new MsgBasebandSampleRateNotification(sampleRate); + } + + int getSampleRate() const { return m_sampleRate; } + + private: + + MsgBasebandSampleRateNotification(int sampleRate) : + Message(), + m_sampleRate(sampleRate) + { } + + int m_sampleRate; + }; + + SigMFFileSink(DeviceAPI *deviceAPI); + virtual ~SigMFFileSink(); + virtual void destroy() { delete this; } + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); + virtual void start(); + virtual void stop(); + virtual bool handleMessage(const Message& cmd); + + virtual void getIdentifier(QString& id) { id = objectName(); } + virtual void getTitle(QString& title) { title = "SigMF File Sink"; } + virtual qint64 getCenterFrequency() const { return m_frequencyOffset; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 1; } + virtual int getNbSourceStreams() const { return 0; } + + virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const + { + (void) streamIndex; + (void) sinkElseSource; + return m_frequencyOffset; + } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + static void webapiFormatChannelSettings( + SWGSDRangel::SWGChannelSettings& response, + const SigMFFileSinkSettings& settings); + + static void webapiUpdateChannelSettings( + SigMFFileSinkSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + void getLocalDevices(std::vector& indexes); + uint32_t getNumberOfDeviceStreams() const; + void record(bool record); + + static const QString m_channelIdURI; + static const QString m_channelId; + +private: + DeviceAPI *m_deviceAPI; + QThread *m_thread; + SigMFFileSinkBaseband *m_basebandSink; + SigMFFileSinkSettings m_settings; + + uint64_t m_centerFrequency; + int64_t m_frequencyOffset; + uint32_t m_basebandSampleRate; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void applySettings(const SigMFFileSinkSettings& settings, bool force = false); + void propagateSampleRateAndFrequency(uint32_t index, uint32_t log2Decim); + static void validateFilterChainHash(SigMFFileSinkSettings& settings); + void calculateFrequencyOffset(uint32_t log2Decim, uint32_t filterChainHash); + DeviceSampleSource *getLocalDevice(uint32_t index); + + void webapiReverseSendSettings(QList& channelSettingsKeys, const SigMFFileSinkSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif /* INCLUDE_SIGMFFILESINK_H_ */ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.cpp new file mode 100644 index 000000000..13c6c4a0e --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.cpp @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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/downchannelizer.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" + +#include "sigmffilesinkbaseband.h" + +MESSAGE_CLASS_DEFINITION(SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkBaseband, Message) +MESSAGE_CLASS_DEFINITION(SigMFFileSinkBaseband::MsgConfigureSigMFFileSinkWork, Message) + +SigMFFileSinkBaseband::SigMFFileSinkBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownChannelizer(&m_sink); + + qDebug("SigMFFileSinkBaseband::SigMFFileSinkBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &SigMFFileSinkBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +SigMFFileSinkBaseband::~SigMFFileSinkBaseband() +{ + delete m_channelizer; +} + +void SigMFFileSinkBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void SigMFFileSinkBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void SigMFFileSinkBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void SigMFFileSinkBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool SigMFFileSinkBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureSigMFFileSinkBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureSigMFFileSinkBaseband& cfg = (MsgConfigureSigMFFileSinkBaseband&) cmd; + qDebug() << "SigMFFileSinkBaseband::handleMessage: MsgConfigureSigMFFileSinkBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "SigMFFileSinkBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate(), true); // apply decimation + m_sink.setSampleRate(getChannelSampleRate()); + + return true; + } + else if (MsgConfigureSigMFFileSinkWork::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureSigMFFileSinkWork& conf = (MsgConfigureSigMFFileSinkWork&) cmd; + qDebug() << "SigMFFileSinkBaseband::handleMessage: MsgConfigureSigMFFileSinkWork: " << conf.isWorking(); + + if (conf.isWorking()) { + m_sink.startRecording(); + } else { + m_sink.stopRecording(); + } + + return true; + } + else + { + return false; + } +} + +void SigMFFileSinkBaseband::applySettings(const SigMFFileSinkSettings& settings, bool force) +{ + qDebug() << "SigMFFileSinkBaseband::applySettings:" + << "m_log2Decim:" << settings.m_log2Decim + << "m_filterChainHash:" << settings.m_filterChainHash + << " force: " << force; + + if ((settings.m_log2Decim != m_settings.m_log2Decim) + || (settings.m_filterChainHash != m_settings.m_filterChainHash) || force) + { + m_channelizer->setDecimation(settings.m_log2Decim, settings.m_filterChainHash); + m_sink.setSampleRate(getChannelSampleRate()); + } + + //m_source.applySettings(settings, force); + m_settings = settings; +} + +int SigMFFileSinkBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.h b/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.h new file mode 100644 index 000000000..04be9198f --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkbaseband.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_SIFMFFILESINKBASEBAND_H_ +#define INCLUDE_SIFMFFILESINKBASEBAND_H_ + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "sigmffilesinksink.h" +#include "sigmffilesinksettings.h" + +class DownChannelizer; + +class SigMFFileSinkBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureSigMFFileSinkBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const SigMFFileSinkSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureSigMFFileSinkBaseband* create(const SigMFFileSinkSettings& settings, bool force) + { + return new MsgConfigureSigMFFileSinkBaseband(settings, force); + } + + private: + SigMFFileSinkSettings m_settings; + bool m_force; + + MsgConfigureSigMFFileSinkBaseband(const SigMFFileSinkSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgConfigureSigMFFileSinkWork : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool isWorking() const { return m_working; } + + static MsgConfigureSigMFFileSinkWork* create(bool working) + { + return new MsgConfigureSigMFFileSinkWork(working); + } + + private: + bool m_working; + + MsgConfigureSigMFFileSinkWork(bool working) : + Message(), + m_working(working) + { } + }; + + SigMFFileSinkBaseband(); + ~SigMFFileSinkBaseband(); + + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setBasebandSampleRate(int sampleRate); + +private: + SampleSinkFifo m_sampleFifo; + DownChannelizer *m_channelizer; + SigMFFileSinkSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + SigMFFileSinkSettings m_settings; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const SigMFFileSinkSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed + +}; + + +#endif // INCLUDE_SIFMFFILESINKBASEBAND_H_ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp new file mode 100644 index 000000000..59c0721e2 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -0,0 +1,339 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "device/deviceuiset.h" +#include "gui/basicchannelsettingsdialog.h" +#include "gui/devicestreamselectiondialog.h" +#include "dsp/hbfilterchainconverter.h" +#include "mainwindow.h" + +#include "sigmffilesinkgui.h" +#include "sigmffilesink.h" +#include "ui_sigmffilesinkgui.h" + +SigMFFileSinkGUI* SigMFFileSinkGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelRx) +{ + SigMFFileSinkGUI* gui = new SigMFFileSinkGUI(pluginAPI, deviceUISet, channelRx); + return gui; +} + +void SigMFFileSinkGUI::destroy() +{ + delete this; +} + +void SigMFFileSinkGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString SigMFFileSinkGUI::getName() const +{ + return objectName(); +} + +qint64 SigMFFileSinkGUI::getCenterFrequency() const { + return 0; +} + +void SigMFFileSinkGUI::setCenterFrequency(qint64 centerFrequency) +{ + (void) centerFrequency; +} + +void SigMFFileSinkGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray SigMFFileSinkGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool SigMFFileSinkGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + displaySettings(); + applySettings(true); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool SigMFFileSinkGUI::handleMessage(const Message& message) +{ + if (SigMFFileSink::MsgBasebandSampleRateNotification::match(message)) + { + SigMFFileSink::MsgBasebandSampleRateNotification& notif = (SigMFFileSink::MsgBasebandSampleRateNotification&) message; + //m_channelMarker.setBandwidth(notif.getSampleRate()); + m_basebandSampleRate = notif.getSampleRate(); + displayRateAndShift(); + return true; + } + else if (SigMFFileSink::MsgConfigureSigMFFileSink::match(message)) + { + const SigMFFileSink::MsgConfigureSigMFFileSink& cfg = (SigMFFileSink::MsgConfigureSigMFFileSink&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelrx, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::SigMFFileSinkGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_basebandSampleRate(0), + m_tickCount(0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_sigMFFileSink = (SigMFFileSink*) channelrx; + m_sigMFFileSink->setMessageQueueToGUI(getInputMessageQueue()); + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 8, -99999999, 99999999); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(m_settings.m_rgbColor); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("SigMF File Sink"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + m_settings.setChannelMarker(&m_channelMarker); + + m_deviceUISet->registerRxChannelInstance(SigMFFileSink::m_channelIdURI, this); + m_deviceUISet->addChannelMarker(&m_channelMarker); + m_deviceUISet->addRollupWidget(this); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); + //connect(&(m_deviceUISet->m_deviceSourceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); + + displaySettings(); + applySettings(true); +} + +SigMFFileSinkGUI::~SigMFFileSinkGUI() +{ + m_deviceUISet->removeRxChannelInstance(this); + delete m_sigMFFileSink; // TODO: check this: when the GUI closes it has to delete the demodulator + delete ui; +} + +void SigMFFileSinkGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void SigMFFileSinkGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + setTitleColor(m_channelMarker.getColor()); + + SigMFFileSink::MsgConfigureSigMFFileSink* message = SigMFFileSink::MsgConfigureSigMFFileSink::create(m_settings, force); + m_sigMFFileSink->getInputMessageQueue()->push(message); + } +} + +void SigMFFileSinkGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.setBandwidth(m_basebandSampleRate / (1<decimationFactor->setCurrentIndex(m_settings.m_log2Decim); + applyDecimation(); + displayStreamIndex(); + + blockApplySettings(false); +} + +void SigMFFileSinkGUI::displayStreamIndex() +{ + if (m_deviceUISet->m_deviceMIMOEngine) { + setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); + } else { + setStreamIndicator("S"); // single channel indicator + } +} + +void SigMFFileSinkGUI::displayRateAndShift() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + ui->deltaFrequency->setValue(shift); + //QLocale loc; + //ui->offsetFrequencyText->setText(tr("%1 Hz").arg(loc.toString(shift))); + double channelSampleRate = ((double) m_basebandSampleRate) / (1<channelRateText->setText(tr("%1k").arg(QString::number(channelSampleRate / 1000.0, 'g', 5))); + m_channelMarker.setCenterFrequency(shift); + m_channelMarker.setBandwidth(channelSampleRate); +} + +void SigMFFileSinkGUI::leaveEvent(QEvent*) +{ + m_channelMarker.setHighlighted(false); +} + +void SigMFFileSinkGUI::enterEvent(QEvent*) +{ + m_channelMarker.setHighlighted(true); +} + +void SigMFFileSinkGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void SigMFFileSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; +} + +void SigMFFileSinkGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); + + setWindowTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); + } + else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) + { + DeviceStreamSelectionDialog dialog(this); + dialog.setNumberOfStreams(m_sigMFFileSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + dialog.move(p); + dialog.exec(); + + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + displayStreamIndex(); + applySettings(); + } + + resetContextMenuType(); +} + +void SigMFFileSinkGUI::on_decimationFactor_currentIndexChanged(int index) +{ + m_settings.m_log2Decim = index; + applyDecimation(); +} + +void SigMFFileSinkGUI::on_position_valueChanged(int value) +{ + m_settings.m_filterChainHash = value; + applyPosition(); +} + +void SigMFFileSinkGUI::on_record_toggled(bool checked) +{ + m_sigMFFileSink->record(checked); +} + +void SigMFFileSinkGUI::applyDecimation() +{ + uint32_t maxHash = 1; + + for (uint32_t i = 0; i < m_settings.m_log2Decim; i++) { + maxHash *= 3; + } + + ui->position->setMaximum(maxHash-1); + ui->position->setValue(m_settings.m_filterChainHash); + m_settings.m_filterChainHash = ui->position->value(); + applyPosition(); +} + +void SigMFFileSinkGUI::applyPosition() +{ + ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash)); + QString s; + m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Decim, m_settings.m_filterChainHash, s); + ui->filterChainText->setText(s); + + displayRateAndShift(); + applySettings(); +} + +void SigMFFileSinkGUI::tick() +{ + if (++m_tickCount == 20) { // once per second + m_tickCount = 0; + } +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h new file mode 100644 index 000000000..cdc3b8f94 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 PLUGINS_CHANNELRX_SIGMFFILESINK_SIGMFFILESINKGUI_H_ +#define PLUGINS_CHANNELRX_SIGMFFILESINK_SIGMFFILESINKGUI_H_ + +#include + +#include + +#include "plugin/plugininstancegui.h" +#include "dsp/channelmarker.h" +#include "gui/rollupwidget.h" +#include "util/messagequeue.h" + +#include "sigmffilesinksettings.h" + +class PluginAPI; +class DeviceUISet; +class SigMFFileSink; +class BasebandSampleSink; + +namespace Ui { + class SigMFFileSinkGUI; +} + +class SigMFFileSinkGUI : public RollupWidget, public PluginInstanceGUI { + Q_OBJECT +public: + static SigMFFileSinkGUI* 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::SigMFFileSinkGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + SigMFFileSinkSettings m_settings; + int m_basebandSampleRate; + double m_shiftFrequencyFactor; //!< Channel frequency shift factor + bool m_doApplySettings; + + SigMFFileSink* m_sigMFFileSink; + MessageQueue m_inputMessageQueue; + + uint32_t m_tickCount; + + explicit SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = nullptr); + virtual ~SigMFFileSinkGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void displaySettings(); + void displayStreamIndex(); + void displayRateAndShift(); + void updateLocalDevices(); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + + void applyDecimation(); + void applyPosition(); + +private slots: + void handleSourceMessages(); + void on_decimationFactor_currentIndexChanged(int index); + void on_position_valueChanged(int value); + void on_record_toggled(bool checked); + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + void tick(); +}; + + + +#endif /* PLUGINS_CHANNELRX_SIGMFFILESINK_SIGMFFILESINKGUI_H_ */ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui new file mode 100644 index 000000000..565d30a50 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui @@ -0,0 +1,350 @@ + + + SigMFFileSinkGUI + + + + 0 + 0 + 340 + 112 + + + + + 340 + 100 + + + + + Liberation Sans + 9 + + + + SigMF File Sink + + + SigMF File Sink + + + + + 0 + 0 + 341 + 101 + + + + Settings + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 3 + + + + + 2 + + + + + + 16 + 0 + + + + Df + + + + + + + false + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + NCO + + + + + + + Qt::Vertical + + + + + + + Dec + + + + + + + + 55 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + Effective channel rate (kS/s) + + + 0000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 10 + + + + + Pos + + + + + + + Center frequency position + + + 2 + + + 1 + + + Qt::Horizontal + + + + + + + + 24 + 0 + + + + Filter chain hash code + + + 000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + 0 + + + + Filter chain stages left to right (L: low, C: center, H: high) + + + LLLLLL + + + + + + + + + + + + 24 + 16777215 + + + + + + + + :/record_off.png:/record_off.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + + + +
diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.cpp new file mode 100644 index 000000000..be93c2c1c --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.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 "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "sigmffilesinkgui.h" +#endif +#include "sigmffilesink.h" +#include "sigmffilesinkwebapiadapter.h" +#include "sigmffilesinkplugin.h" + +const PluginDescriptor SigMFFileSinkPlugin::m_pluginDescriptor = { + SigMFFileSink::m_channelId, + QString("SigMF File Sink"), + QString("5.8.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +SigMFFileSinkPlugin::SigMFFileSinkPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& SigMFFileSinkPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void SigMFFileSinkPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register channel Source + m_pluginAPI->registerRxChannel(SigMFFileSink::m_channelIdURI, SigMFFileSink::m_channelId, this); +} + +#ifdef SERVER_MODE +PluginInstanceGUI* SigMFFileSinkPlugin::createRxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSink *rxChannel) const +{ + return 0; +} +#else +PluginInstanceGUI* SigMFFileSinkPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const +{ + return SigMFFileSinkGUI::create(m_pluginAPI, deviceUISet, rxChannel); +} +#endif + +BasebandSampleSink* SigMFFileSinkPlugin::createRxChannelBS(DeviceAPI *deviceAPI) const +{ + return new SigMFFileSink(deviceAPI); +} + +ChannelAPI* SigMFFileSinkPlugin::createRxChannelCS(DeviceAPI *deviceAPI) const +{ + return new SigMFFileSink(deviceAPI); +} + +ChannelWebAPIAdapter* SigMFFileSinkPlugin::createChannelWebAPIAdapter() const +{ + return new SigMFFileSinkWebAPIAdapter(); +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.h b/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.h new file mode 100644 index 000000000..f1059e744 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkplugin.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELRX_SIGMFFILESINK_SIGMFFILESINKPLUGIN_H_ +#define PLUGINS_CHANNELRX_SIGMFFILESINK_SIGMFFILESINKPLUGIN_H_ + + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSink; + +class SigMFFileSinkPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.demod.sigmffilesink") + +public: + explicit SigMFFileSinkPlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual PluginInstanceGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const; + virtual BasebandSampleSink* createRxChannelBS(DeviceAPI *deviceAPI) const; + virtual ChannelAPI* createRxChannelCS(DeviceAPI *deviceAPI) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif /* PLUGINS_CHANNELRX_LOCALSINK_LOCALSINKPLUGIN_H_ */ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp new file mode 100644 index 000000000..21c68b2f2 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" + +#include "sigmffilesinksettings.h" + +SigMFFileSinkSettings::SigMFFileSinkSettings() +{ + resetToDefaults(); +} + +void SigMFFileSinkSettings::resetToDefaults() +{ + m_ncoMode = false; + m_inputFrequencyOffset = 0; + m_fileRecordName = ""; + m_rgbColor = QColor(140, 4, 4).rgb(); + m_title = "Local sink"; + m_log2Decim = 0; + m_filterChainHash = 0; + m_channelMarker = nullptr; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; +} + +QByteArray SigMFFileSinkSettings::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, m_inputFrequencyOffset); + s.writeBool(2, m_ncoMode); + s.writeString(3, m_fileRecordName); + s.writeS32(4, m_streamIndex); + s.writeU32(5, m_rgbColor); + s.writeString(6, m_title); + s.writeBool(7, m_useReverseAPI); + s.writeString(8, m_reverseAPIAddress); + s.writeU32(9, m_reverseAPIPort); + s.writeU32(10, m_reverseAPIDeviceIndex); + s.writeU32(11, m_reverseAPIChannelIndex); + s.writeU32(12, m_log2Decim); + s.writeU32(13, m_filterChainHash); + + return s.final(); +} + +bool SigMFFileSinkSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + uint32_t tmp; + QString strtmp; + + d.readS32(1, &m_inputFrequencyOffset, 0); + d.readBool(2, &m_ncoMode, false); + d.readString(3, &m_fileRecordName, ""); + d.readS32(4, &m_streamIndex, 0); + d.readU32(5, &m_rgbColor, QColor(0, 255, 255).rgb()); + d.readString(6, &m_title, "Local sink"); + d.readBool(7, &m_useReverseAPI, false); + d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(9, &tmp, 0); + + if ((tmp > 1023) && (tmp < 65535)) { + m_reverseAPIPort = tmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(10, &tmp, 0); + m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp; + d.readU32(11, &tmp, 0); + m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp; + d.readU32(12, &tmp, 0); + m_log2Decim = tmp > 6 ? 6 : tmp; + d.readU32(13, &m_filterChainHash, 0); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + + + + + diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h new file mode 100644 index 000000000..d5ad9b8a4 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_SIFMFFILESINKSETTINGS_H_ +#define INCLUDE_SIFMFFILESINKSETTINGS_H_ + +#include +#include + +class Serializable; + +struct SigMFFileSinkSettings +{ + bool m_ncoMode; + qint32 m_inputFrequencyOffset; + QString m_fileRecordName; + quint32 m_rgbColor; + QString m_title; + uint32_t m_log2Decim; + uint32_t m_filterChainHash; + int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx). + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + + Serializable *m_channelMarker; + + SigMFFileSinkSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* INCLUDE_SIFMFFILESINKSETTINGS_H_ */ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksink.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinksink.cpp new file mode 100644 index 000000000..44399c376 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksink.cpp @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "dsp/filerecord.h" + +#include "sigmffilesinksink.h" + +SigMFFileSinkSink::SigMFFileSinkSink() : + m_record(false) +{} + +SigMFFileSinkSink::~SigMFFileSinkSink() +{} + +void SigMFFileSinkSink::startRecording() +{ + QString fileBase; + FileRecordInterface::RecordType recordType = FileRecordInterface::guessTypeFromFileName(m_settings.m_fileRecordName, fileBase); + + if (recordType == FileRecordInterface::RecordTypeSigMF) + { + m_fileSink.setFileName(fileBase); + m_fileSink.startRecording(); + m_record = true; + } +} + +void SigMFFileSinkSink::stopRecording() +{ + m_record = false; + m_fileSink.stopRecording(); +} + +void SigMFFileSinkSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + if (m_record) { + m_fileSink.feed(begin, end, true); + } +} + +void SigMFFileSinkSink::setSampleRate(int sampleRate) +{ +} \ No newline at end of file diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksink.h b/plugins/channelrx/sigmffilesink/sigmffilesinksink.h new file mode 100644 index 000000000..0df70ad85 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksink.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_SIFMFFILESINKSINK_H_ +#define INCLUDE_SIFMFFILESINKSINK_H_ + +#include "dsp/channelsamplesink.h" +#include "dsp/sigmffilerecord.h" + +#include "sigmffilesinksettings.h" + +class FileRecordInterface; + +class SigMFFileSinkSink : public ChannelSampleSink { +public: + SigMFFileSinkSink(); + ~SigMFFileSinkSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + SigMFFileRecord *getFileSink() { return &m_fileSink; } + void startRecording(); + void stopRecording(); + void setDeviceHwId(const QString& hwId) { m_deviceHwId = hwId; } + void setDeviceUId(int uid) { m_deviceUId = uid; } + void setSampleRate(int sampleRate); + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + void applySettings(const SigMFFileSinkSettings& settings, bool force = false); + +private: + int m_channelSampleRate; + int m_channelFrequencyOffset; + SigMFFileSinkSettings m_settings; + SigMFFileRecord m_fileSink; + bool m_record; + QString m_deviceHwId; + int m_deviceUId; +}; + +#endif // INCLUDE_SIFMFFILESINKSINK_H_ diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.cpp new file mode 100644 index 000000000..62ed15523 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGChannelSettings.h" +#include "sigmffilesink.h" +#include "sigmffilesinkwebapiadapter.h" + +SigMFFileSinkWebAPIAdapter::SigMFFileSinkWebAPIAdapter() +{} + +SigMFFileSinkWebAPIAdapter::~SigMFFileSinkWebAPIAdapter() +{} + +int SigMFFileSinkWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + (void) response; + // response.setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings()); + // response.getLocalSinkSettings()->init(); + // LocalSink::webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int SigMFFileSinkWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + SigMFFileSink::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + + return 200; +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.h b/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.h new file mode 100644 index 000000000..7639600d2 --- /dev/null +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkwebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_SIGMFFILESINK_WEBAPIADAPTER_H +#define INCLUDE_SIGMFFILESINK_WEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "sigmffilesinksettings.h" + +/** + * Standalone API adapter only for the settings + */ +class SigMFFileSinkWebAPIAdapter : public ChannelWebAPIAdapter { +public: + SigMFFileSinkWebAPIAdapter(); + virtual ~SigMFFileSinkWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + +private: + SigMFFileSinkSettings m_settings; +}; + +#endif // INCLUDE_LOCALSINK_WEBAPIADAPTER_H