From ba4c8d7a6805e8e177e838ad61bfa19bd6d98a82 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 10 May 2019 13:20:30 +0200 Subject: [PATCH] Added the Local Source plugin --- plugins/channelrx/localsink/localsinkgui.cpp | 2 +- plugins/channeltx/CMakeLists.txt | 1 + plugins/channeltx/localsource/CMakeLists.txt | 52 ++ plugins/channeltx/localsource/localsource.cpp | 556 ++++++++++++++++++ plugins/channeltx/localsource/localsource.h | 193 ++++++ .../channeltx/localsource/localsourcegui.cpp | 337 +++++++++++ .../channeltx/localsource/localsourcegui.h | 105 ++++ .../channeltx/localsource/localsourcegui.ui | 331 +++++++++++ .../localsource/localsourceplugin.cpp | 78 +++ .../channeltx/localsource/localsourceplugin.h | 49 ++ .../localsource/localsourcesettings.cpp | 111 ++++ .../localsource/localsourcesettings.h | 48 ++ .../localsource/localsourcethread.cpp | 110 ++++ .../channeltx/localsource/localsourcethread.h | 83 +++ sdrbase/dsp/samplesourcefifo.cpp | 10 + sdrbase/dsp/samplesourcefifo.h | 2 + .../doc/swagger/include/LocalSource.yaml | 25 + .../api/swagger/include/LocalSource.yaml | 25 + .../qt5/client/SWGLocalSourceSettings.cpp | 299 ++++++++++ .../code/qt5/client/SWGLocalSourceSettings.h | 113 ++++ 20 files changed, 2529 insertions(+), 1 deletion(-) create mode 100644 plugins/channeltx/localsource/CMakeLists.txt create mode 100644 plugins/channeltx/localsource/localsource.cpp create mode 100644 plugins/channeltx/localsource/localsource.h create mode 100644 plugins/channeltx/localsource/localsourcegui.cpp create mode 100644 plugins/channeltx/localsource/localsourcegui.h create mode 100644 plugins/channeltx/localsource/localsourcegui.ui create mode 100644 plugins/channeltx/localsource/localsourceplugin.cpp create mode 100644 plugins/channeltx/localsource/localsourceplugin.h create mode 100644 plugins/channeltx/localsource/localsourcesettings.cpp create mode 100644 plugins/channeltx/localsource/localsourcesettings.h create mode 100644 plugins/channeltx/localsource/localsourcethread.cpp create mode 100644 plugins/channeltx/localsource/localsourcethread.h create mode 100644 sdrbase/resources/webapi/doc/swagger/include/LocalSource.yaml create mode 100644 swagger/sdrangel/api/swagger/include/LocalSource.yaml create mode 100644 swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.h diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 00f7d44e2..30a11a121 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -124,7 +124,7 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_channelMarker.blockSignals(true); m_channelMarker.setColor(m_settings.m_rgbColor); m_channelMarker.setCenterFrequency(0); - m_channelMarker.setTitle("Local source"); + m_channelMarker.setTitle("Local Sink"); m_channelMarker.blockSignals(false); m_channelMarker.setVisible(true); // activate signal on the last setting only diff --git a/plugins/channeltx/CMakeLists.txt b/plugins/channeltx/CMakeLists.txt index ebb0bc8eb..aef30b191 100644 --- a/plugins/channeltx/CMakeLists.txt +++ b/plugins/channeltx/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(modnfm) add_subdirectory(modssb) add_subdirectory(modwfm) add_subdirectory(udpsource) +add_subdirectory(localsource) find_package(CM256cc) if(CM256CC_FOUND) diff --git a/plugins/channeltx/localsource/CMakeLists.txt b/plugins/channeltx/localsource/CMakeLists.txt new file mode 100644 index 000000000..8d28df293 --- /dev/null +++ b/plugins/channeltx/localsource/CMakeLists.txt @@ -0,0 +1,52 @@ +project(localsource) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(localsource_SOURCES + localsource.cpp + localsourcethread.cpp + localsourcegui.cpp + localsourceplugin.cpp + localsourcesettings.cpp +) + +set(localsource_HEADERS + localsource.h + localsourcethread.h + localsourcegui.h + localsourceplugin.h + localsourcesettings.h +) + +set(localsource_FORMS + localsourcegui.ui +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +qt5_wrap_ui(localsource_FORMS_HEADERS ${localsource_FORMS}) + +add_library(localsource SHARED + ${localsource_SOURCES} + ${localsource_HEADERS_MOC} + ${localsource_FORMS_HEADERS} +) + +target_include_directories(localsource PUBLIC + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +target_link_libraries(localsource + ${QT_LIBRARIES} + sdrbase + sdrgui + swagger +) + +target_link_libraries(localsource Qt5::Core Qt5::Widgets Qt5::Network) + +install(TARGETS localsource DESTINATION lib/plugins/channeltx) diff --git a/plugins/channeltx/localsource/localsource.cpp b/plugins/channeltx/localsource/localsource.cpp new file mode 100644 index 000000000..442812408 --- /dev/null +++ b/plugins/channeltx/localsource/localsource.cpp @@ -0,0 +1,556 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "localsource.h" + +#include +#include + +#include +#include +#include + +#include "SWGChannelSettings.h" + +#include "util/simpleserializer.h" +#include "dsp/threadedbasebandsamplesource.h" +#include "dsp/upchannelizer.h" +#include "dsp/dspcommands.h" +#include "dsp/dspdevicesinkengine.h" +#include "dsp/dspengine.h" +#include "dsp/devicesamplesink.h" +#include "dsp/hbfilterchainconverter.h" +#include "device/deviceapi.h" + +#include "localsourcethread.h" + +MESSAGE_CLASS_DEFINITION(LocalSource::MsgConfigureLocalSource, Message) +MESSAGE_CLASS_DEFINITION(LocalSource::MsgSampleRateNotification, Message) +MESSAGE_CLASS_DEFINITION(LocalSource::MsgConfigureChannelizer, Message) + +const QString LocalSource::m_channelIdURI = "sdrangel.channel.localsource"; +const QString LocalSource::m_channelId = "LocalSource"; + +LocalSource::LocalSource(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), + m_deviceAPI(deviceAPI), + m_running(false), + m_sinkThread(nullptr), + m_localSampleSourceFifo(nullptr), + m_chunkSize(0), + m_localSamplesIndex(0), + m_localSamplesIndexOffset(0), + m_centerFrequency(0), + m_frequencyOffset(0), + m_sampleRate(48000), + m_deviceSampleRate(48000) +{ + setObjectName(m_channelId); + + m_channelizer = new UpChannelizer(this); + m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); + m_deviceAPI->addChannelSource(m_threadedChannelizer); + m_deviceAPI->addChannelSourceAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); +} + +LocalSource::~LocalSource() +{ + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(m_threadedChannelizer); + delete m_threadedChannelizer; + delete m_channelizer; +} + +void LocalSource::pull(Sample& sample) +{ + sample = m_localSamples[m_localSamplesIndex + m_localSamplesIndexOffset]; + + if (m_localSamplesIndex < m_chunkSize - 1) + { + m_localSamplesIndex++; + } + else + { + m_localSamplesIndex = 0; + + if (m_localSamplesIndexOffset == 0) { + m_localSamplesIndexOffset = m_chunkSize; + } else { + m_localSamplesIndexOffset = 0; + } + + emit pullSamples(m_chunkSize); + } +} + +void LocalSource::processSamples(unsigned int offset) +{ + if (m_localSampleSourceFifo) + { + int destOffset = (m_localSamplesIndexOffset == 0 ? m_chunkSize : 0); + SampleVector::iterator beginSource; + SampleVector::iterator beginDestination = m_localSamples.begin() + destOffset; + m_localSampleSourceFifo->setIteratorFromOffset(beginSource, offset); + std::copy(beginSource, beginSource + m_chunkSize, beginDestination); + } +} + +void LocalSource::pullAudio(int nbSamples) +{ + (void) nbSamples; +} + +void LocalSource::start() +{ + qDebug("LocalSource::start"); + + if (m_running) { + stop(); + } + + m_sinkThread = new LocalSourceThread(); + DeviceSampleSink *deviceSink = getLocalDevice(m_settings.m_localDeviceIndex); + + if (deviceSink) + { + m_localSampleSourceFifo = deviceSink->getSampleFifo(); + m_chunkSize = m_localSampleSourceFifo->size() / 16; + m_localSamples.resize(2*m_chunkSize); + m_localSamplesIndex = 0; + m_sinkThread->setSampleFifo(m_localSampleSourceFifo); + } + else + { + m_localSampleSourceFifo = nullptr; + } + + connect(this, + SIGNAL(pullSamples(unsigned int)), + m_sinkThread, + SLOT(pullSamples(unsigned int)), + Qt::QueuedConnection); + + connect(m_sinkThread, + SIGNAL(samplesAvailable(int)), + this, + SLOT(pprocessSamples(int)), + Qt::QueuedConnection); + + m_sinkThread->startStop(true); + m_running = true; +} + +void LocalSource::stop() +{ + qDebug("LocalSource::stop"); + + if (m_sinkThread != 0) + { + m_sinkThread->startStop(false); + m_sinkThread->deleteLater(); + m_sinkThread = 0; + } + + m_running = false; +} + +bool LocalSource::handleMessage(const Message& cmd) +{ + if (UpChannelizer::MsgChannelizerNotification::match(cmd)) + { + UpChannelizer::MsgChannelizerNotification& notif = (UpChannelizer::MsgChannelizerNotification&) cmd; + + qDebug() << "LocalSource::handleMessage: MsgChannelizerNotification:" + << " channelSampleRate: " << notif.getSampleRate() + << " offsetFrequency: " << notif.getFrequencyOffset(); + + if (notif.getSampleRate() > 0) + { + setSampleRate(notif.getSampleRate()); + } + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + + qDebug() << "LocalSource::handleMessage: DSPSignalNotification:" + << " inputSampleRate: " << notif.getSampleRate() + << " centerFrequency: " << notif.getCenterFrequency(); + + setCenterFrequency(notif.getCenterFrequency()); + m_deviceSampleRate = notif.getSampleRate(); + calculateFrequencyOffset(); // This is when device sample rate changes + propagateSampleRateAndFrequency(m_settings.m_localDeviceIndex); + + // Redo the channelizer stuff with the new sample rate to re-synchronize everything + m_channelizer->set(m_channelizer->getInputMessageQueue(), + m_settings.m_log2Interp, + m_settings.m_filterChainHash); + + if (m_guiMessageQueue) + { + MsgSampleRateNotification *msg = MsgSampleRateNotification::create(notif.getSampleRate()); + m_guiMessageQueue->push(msg); + } + + return true; + } + else if (MsgConfigureLocalSource::match(cmd)) + { + MsgConfigureLocalSource& cfg = (MsgConfigureLocalSource&) cmd; + qDebug() << "LocalSource::handleMessage: MsgConfigureLocalSink"; + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgConfigureChannelizer::match(cmd)) + { + MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; + m_settings.m_log2Interp = cfg.getLog2Interp(); + m_settings.m_filterChainHash = cfg.getFilterChainHash(); + + qDebug() << "LocalSource::handleMessage: MsgConfigureChannelizer:" + << " log2Interp: " << m_settings.m_log2Interp + << " filterChainHash: " << m_settings.m_filterChainHash; + + m_channelizer->set(m_channelizer->getInputMessageQueue(), + m_settings.m_log2Interp, + m_settings.m_filterChainHash); + + calculateFrequencyOffset(); // This is when decimation or filter chain changes + propagateSampleRateAndFrequency(m_settings.m_localDeviceIndex); + + return true; + } + else + { + return false; + } +} + +QByteArray LocalSource::serialize() const +{ + return m_settings.serialize(); +} + +bool LocalSource::deserialize(const QByteArray& data) +{ + (void) data; + if (m_settings.deserialize(data)) + { + MsgConfigureLocalSource *msg = MsgConfigureLocalSource::create(m_settings, true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureLocalSource *msg = MsgConfigureLocalSource::create(m_settings, true); + m_inputMessageQueue.push(msg); + return false; + } +} + +void LocalSource::getLocalDevices(std::vector& indexes) +{ + indexes.clear(); + DSPEngine *dspEngine = DSPEngine::instance(); + + for (uint32_t i = 0; i < dspEngine->getDeviceSinkEnginesNumber(); i++) + { + DSPDeviceSinkEngine *deviceSinkEngine = dspEngine->getDeviceSinkEngineByIndex(i); + DeviceSampleSink *deviceSink = deviceSinkEngine->getSink(); + + if (deviceSink->getDeviceDescription() == "LocalOutput") { + indexes.push_back(i); + } + } +} + +DeviceSampleSink *LocalSource::getLocalDevice(uint32_t index) +{ + DSPEngine *dspEngine = DSPEngine::instance(); + + if (index < dspEngine->getDeviceSinkEnginesNumber()) + { + DSPDeviceSinkEngine *deviceSinkEngine = dspEngine->getDeviceSinkEngineByIndex(index); + DeviceSampleSink *deviceSink = deviceSinkEngine->getSink(); + + if (deviceSink->getDeviceDescription() == "LocalOutput") + { + if (!getDeviceAPI()) { + qDebug("LocalSource::getLocalDevice: the parent device is unset"); + } else if (getDeviceAPI()->getDeviceUID() == deviceSinkEngine->getUID()) { + qDebug("LocalSource::getLocalDevice: sink device at index %u is the parent device", index); + } else { + return deviceSink; + } + } + else + { + qDebug("LocalSource::getLocalDevice: sink device at index %u is not a Local Output source", index); + } + } + else + { + qDebug("LocalSource::getLocalDevice: non existent sink device index: %u", index); + } + + return nullptr; +} + +void LocalSource::propagateSampleRateAndFrequency(uint32_t index) +{ + DeviceSampleSink *deviceSink = getLocalDevice(index); + + if (deviceSink) + { + deviceSink->setSampleRate(m_deviceSampleRate / (1<setCenterFrequency(m_centerFrequency + m_frequencyOffset); + } +} + +void LocalSource::applySettings(const LocalSourceSettings& settings, bool force) +{ + qDebug() << "LocalSource::applySettings:" + << " m_localDeviceIndex: " << settings.m_localDeviceIndex + << " force: " << force; + + QList reverseAPIKeys; + + if ((settings.m_localDeviceIndex != m_settings.m_localDeviceIndex) || force) + { + reverseAPIKeys.append("localDeviceIndex"); + DeviceSampleSink *deviceSink = getLocalDevice(settings.m_localDeviceIndex); + + if (deviceSink) + { + if (m_sinkThread) { + m_sinkThread->setSampleFifo(deviceSink->getSampleFifo()); + } + + propagateSampleRateAndFrequency(settings.m_localDeviceIndex); + } + else + { + qWarning("LocalSource::applySettings: invalid local device for index %u", settings.m_localDeviceIndex); + } + } + + 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 LocalSource::validateFilterChainHash(LocalSourceSettings& settings) +{ + unsigned int s = 1; + + for (unsigned int i = 0; i < settings.m_log2Interp; i++) { + s *= 3; + } + + settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash; +} + +void LocalSource::calculateFrequencyOffset() +{ + double shiftFactor = HBFilterChainConverter::getShiftFactor(m_settings.m_log2Interp, m_settings.m_filterChainHash); + m_frequencyOffset = m_deviceSampleRate * shiftFactor; +} + +int LocalSource::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings()); + response.getLocalSinkSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + return 200; +} + +int LocalSource::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + LocalSourceSettings settings = m_settings; + + if (channelSettingsKeys.contains("localDeviceIndex")) { + settings.m_localDeviceIndex = response.getLocalSourceSettings()->getLocalDeviceIndex(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getLocalSourceSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getLocalSourceSettings()->getTitle(); + } + if (channelSettingsKeys.contains("log2Interp")) { + settings.m_log2Interp = response.getLocalSourceSettings()->getLog2Interp(); + } + + if (channelSettingsKeys.contains("filterChainHash")) + { + settings.m_filterChainHash = response.getLocalSourceSettings()->getFilterChainHash(); + validateFilterChainHash(settings); + } + + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getLocalSourceSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getLocalSourceSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getLocalSourceSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getLocalSourceSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getLocalSourceSettings()->getReverseApiChannelIndex(); + } + + MsgConfigureLocalSource *msg = MsgConfigureLocalSource::create(settings, force); + m_inputMessageQueue.push(msg); + + if ((settings.m_log2Interp != m_settings.m_log2Interp) || (settings.m_filterChainHash != m_settings.m_filterChainHash) || force) + { + MsgConfigureChannelizer *msg = MsgConfigureChannelizer::create(settings.m_log2Interp, settings.m_filterChainHash); + m_inputMessageQueue.push(msg); + } + + qDebug("LocalSource::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureLocalSource *msgToGUI = MsgConfigureLocalSource::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +void LocalSource::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const LocalSourceSettings& settings) +{ + response.getLocalSourceSettings()->setLocalDeviceIndex(settings.m_localDeviceIndex); + response.getLocalSourceSettings()->setRgbColor(settings.m_rgbColor); + + if (response.getLocalSourceSettings()->getTitle()) { + *response.getLocalSourceSettings()->getTitle() = settings.m_title; + } else { + response.getLocalSourceSettings()->setTitle(new QString(settings.m_title)); + } + + response.getLocalSourceSettings()->setLog2Interp(settings.m_log2Interp); + response.getLocalSourceSettings()->setFilterChainHash(settings.m_filterChainHash); + response.getLocalSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getLocalSourceSettings()->getReverseApiAddress()) { + *response.getLocalSourceSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getLocalSourceSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getLocalSourceSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getLocalSourceSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getLocalSourceSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); +} + +void LocalSource::webapiReverseSendSettings(QList& channelSettingsKeys, const LocalSourceSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setDirection(1); // single source (Tx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString("LocalSource")); + swgChannelSettings->setLocalSourceSettings(new SWGSDRangel::SWGLocalSourceSettings()); + SWGSDRangel::SWGLocalSourceSettings *swgLocalSourceSettings = swgChannelSettings->getLocalSourceSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("localDeviceIndex") || force) { + swgLocalSourceSettings->setLocalDeviceIndex(settings.m_localDeviceIndex); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgLocalSourceSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgLocalSourceSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("log2Interp") || force) { + swgLocalSourceSettings->setLog2Interp(settings.m_log2Interp); + } + if (channelSettingsKeys.contains("filterChainHash") || force) { + swgLocalSourceSettings->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 + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void LocalSource::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "LocalSource::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("LocalSource::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} diff --git a/plugins/channeltx/localsource/localsource.h b/plugins/channeltx/localsource/localsource.h new file mode 100644 index 000000000..93f235694 --- /dev/null +++ b/plugins/channeltx/localsource/localsource.h @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 INCLUDE_LOCALSOURCE_H_ +#define INCLUDE_LOCALSOURCE_H_ + +#include +#include +#include + +#include "dsp/basebandsamplesource.h" +#include "util/message.h" +#include "channel/channelapi.h" +#include "localsourcesettings.h" + +class DeviceAPI; +class DeviceSampleSink; +class ThreadedBasebandSampleSource; +class UpChannelizer; +class LocalSourceThread; +class QNetworkAccessManager; +class QNetworkReply; + +class LocalSource : public BasebandSampleSource, public ChannelAPI { + Q_OBJECT +public: + class MsgConfigureLocalSource : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const LocalSourceSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureLocalSource* create(const LocalSourceSettings& settings, bool force) + { + return new MsgConfigureLocalSource(settings, force); + } + + private: + LocalSourceSettings m_settings; + bool m_force; + + MsgConfigureLocalSource(const LocalSourceSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + 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; + }; + + class MsgConfigureChannelizer : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getLog2Interp() const { return m_log2Interp; } + int getFilterChainHash() const { return m_filterChainHash; } + + static MsgConfigureChannelizer* create(unsigned int m_log2Interp, unsigned int m_filterChainHash) { + return new MsgConfigureChannelizer(m_log2Interp, m_filterChainHash); + } + + private: + unsigned int m_log2Interp; + unsigned int m_filterChainHash; + + MsgConfigureChannelizer(unsigned int log2Interp, unsigned int filterChainHash) : + Message(), + m_log2Interp(log2Interp), + m_filterChainHash(filterChainHash) + { } + }; + + LocalSource(DeviceAPI *deviceAPI); + virtual ~LocalSource(); + virtual void destroy() { delete this; } + + virtual void pull(Sample& sample); + virtual void pullAudio(int nbSamples); + 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 = "Local Sink"; } + virtual qint64 getCenterFrequency() const { return m_frequencyOffset; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 0; } + virtual int getNbSourceStreams() const { return 1; } + + 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); + + /** Set center frequency given in Hz */ + void setCenterFrequency(uint64_t centerFrequency) { m_centerFrequency = centerFrequency; } + + /** Set sample rate given in Hz */ + void setSampleRate(uint32_t sampleRate) { m_sampleRate = sampleRate; } + + void setChannelizer(unsigned int log2Interp, unsigned int filterChainHash); + void getLocalDevices(std::vector& indexes); + + static const QString m_channelIdURI; + static const QString m_channelId; + +signals: + void pullSamples(unsigned int count); + +private: + DeviceAPI *m_deviceAPI; + ThreadedBasebandSampleSource* m_threadedChannelizer; + UpChannelizer* m_channelizer; + bool m_running; + + LocalSourceSettings m_settings; + LocalSourceThread *m_sinkThread; + SampleSourceFifo *m_localSampleSourceFifo; + int m_chunkSize; + SampleVector m_localSamples; + int m_localSamplesIndex; + int m_localSamplesIndexOffset; + + uint64_t m_centerFrequency; + int64_t m_frequencyOffset; + uint32_t m_sampleRate; + uint32_t m_deviceSampleRate; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void applySettings(const LocalSourceSettings& settings, bool force = false); + DeviceSampleSink *getLocalDevice(uint32_t index); + void propagateSampleRateAndFrequency(uint32_t index); + void validateFilterChainHash(LocalSourceSettings& settings); + void calculateFrequencyOffset(); + void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const LocalSourceSettings& settings); + void webapiReverseSendSettings(QList& channelSettingsKeys, const LocalSourceSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + void processSamples(unsigned int offset); +}; + +#endif /* INCLUDE_LOCALSOURCE_H_ */ diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp new file mode 100644 index 000000000..573dbf3e9 --- /dev/null +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 + +#include "device/deviceuiset.h" +#include "gui/basicchannelsettingsdialog.h" +#include "dsp/hbfilterchainconverter.h" +#include "mainwindow.h" + +#include "localsourcegui.h" +#include "localsource.h" +#include "ui_localsourcegui.h" + +LocalSourceGUI* LocalSourceGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx) +{ + LocalSourceGUI* gui = new LocalSourceGUI(pluginAPI, deviceUISet, channelTx); + return gui; +} + +void LocalSourceGUI::destroy() +{ + delete this; +} + +void LocalSourceGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString LocalSourceGUI::getName() const +{ + return objectName(); +} + +qint64 LocalSourceGUI::getCenterFrequency() const { + return 0; +} + +void LocalSourceGUI::setCenterFrequency(qint64 centerFrequency) +{ + (void) centerFrequency; +} + +void LocalSourceGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray LocalSourceGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool LocalSourceGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applySettings(true); + return true; + } else { + resetToDefaults(); + return false; + } +} + +bool LocalSourceGUI::handleMessage(const Message& message) +{ + if (LocalSource::MsgSampleRateNotification::match(message)) + { + LocalSource::MsgSampleRateNotification& notif = (LocalSource::MsgSampleRateNotification&) message; + //m_channelMarker.setBandwidth(notif.getSampleRate()); + m_sampleRate = notif.getSampleRate(); + displayRateAndShift(); + return true; + } + else if (LocalSource::MsgConfigureLocalSource::match(message)) + { + const LocalSource::MsgConfigureLocalSource& cfg = (LocalSource::MsgConfigureLocalSource&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + return true; + } + else + { + return false; + } +} + +LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channeltx, QWidget* parent) : + RollupWidget(parent), + ui(new Ui::LocalSourceGUI), + 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))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_localSource = (LocalSource*) channeltx; + m_localSource->setMessageQueueToGUI(getInputMessageQueue()); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(m_settings.m_rgbColor); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("Local Source"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + m_settings.setChannelMarker(&m_channelMarker); + + m_deviceUISet->registerRxChannelInstance(LocalSource::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())); + + m_time.start(); + + updateLocalDevices(); + displaySettings(); + applySettings(true); +} + +LocalSourceGUI::~LocalSourceGUI() +{ + m_deviceUISet->removeTxChannelInstance(this); + delete m_localSource; // TODO: check this: when the GUI closes it has to delete the demodulator + delete ui; +} + +void LocalSourceGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void LocalSourceGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + setTitleColor(m_channelMarker.getColor()); + + LocalSource::MsgConfigureLocalSource* message = LocalSource::MsgConfigureLocalSource::create(m_settings, force); + m_localSource->getInputMessageQueue()->push(message); + } +} + +void LocalSourceGUI::applyChannelSettings() +{ + if (m_doApplySettings) + { + LocalSource::MsgConfigureChannelizer *msgChan = LocalSource::MsgConfigureChannelizer::create( + m_settings.m_log2Interp, + m_settings.m_filterChainHash); + m_localSource->getInputMessageQueue()->push(msgChan); + } +} + +void LocalSourceGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.setBandwidth(m_sampleRate); // TODO + m_channelMarker.setMovable(false); // do not let user move the center arbitrarily + 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->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); + applyInterpolation(); + blockApplySettings(false); +} + +void LocalSourceGUI::displayRateAndShift() +{ + int shift = m_shiftFrequencyFactor * m_sampleRate; + double channelSampleRate = ((double) m_sampleRate) / (1<offsetFrequencyText->setText(tr("%1 Hz").arg(loc.toString(shift))); + ui->channelRateText->setText(tr("%1k").arg(QString::number(channelSampleRate / 1000.0, 'g', 5))); + m_channelMarker.setCenterFrequency(shift); + m_channelMarker.setBandwidth(channelSampleRate); +} + +void LocalSourceGUI::updateLocalDevices() +{ + std::vector localDevicesIndexes; + m_localSource->getLocalDevices(localDevicesIndexes); + ui->localDevice->clear(); + std::vector::const_iterator it = localDevicesIndexes.begin(); + + for (; it != localDevicesIndexes.end(); ++it) { + ui->localDevice->addItem(tr("%1").arg(*it), QVariant(*it)); + } +} + +void LocalSourceGUI::leaveEvent(QEvent*) +{ + m_channelMarker.setHighlighted(false); +} + +void LocalSourceGUI::enterEvent(QEvent*) +{ + m_channelMarker.setHighlighted(true); +} + +void LocalSourceGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void LocalSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; +} + +void LocalSourceGUI::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(); + } + + resetContextMenuType(); +} + +void LocalSourceGUI::on_interpolationFactor_currentIndexChanged(int index) +{ + m_settings.m_log2Interp = index; + applyInterpolation(); +} + +void LocalSourceGUI::on_position_valueChanged(int value) +{ + m_settings.m_filterChainHash = value; + applyPosition(); +} + +void LocalSourceGUI::on_localDevice_currentIndexChanged(int index) +{ + m_settings.m_localDeviceIndex = ui->localDevice->itemData(index).toInt(); + applySettings(); +} + +void LocalSourceGUI::on_localDevicesRefresh_clicked(bool checked) +{ + (void) checked; + updateLocalDevices(); +} + +void LocalSourceGUI::applyInterpolation() +{ + uint32_t maxHash = 1; + + for (uint32_t i = 0; i < m_settings.m_log2Interp; i++) { + maxHash *= 3; + } + + ui->position->setMaximum(maxHash-1); + ui->position->setValue(m_settings.m_filterChainHash); + m_settings.m_filterChainHash = ui->position->value(); + applyPosition(); +} + +void LocalSourceGUI::applyPosition() +{ + ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash)); + QString s; + m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); + ui->filterChainText->setText(s); + + displayRateAndShift(); + applyChannelSettings(); +} + +void LocalSourceGUI::tick() +{ + if (++m_tickCount == 20) { // once per second + m_tickCount = 0; + } +} diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h new file mode 100644 index 000000000..789160e2a --- /dev/null +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_CHANNELTX_LOCALSOURCE_LOCALSOURCEGUI_H_ +#define PLUGINS_CHANNELTX_LOCALSOURCE_LOCALSOURCEGUI_H_ + +#include + +#include +#include + +#include "plugin/plugininstancegui.h" +#include "dsp/channelmarker.h" +#include "gui/rollupwidget.h" +#include "util/messagequeue.h" + +#include "localsourcesettings.h" + +class PluginAPI; +class DeviceUISet; +class LocalSource; +class BasebandSampleSource; + +namespace Ui { + class LocalSourceGUI; +} + +class LocalSourceGUI : public RollupWidget, public PluginInstanceGUI { + Q_OBJECT +public: + static LocalSourceGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *txChannel); + 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::LocalSourceGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + LocalSourceSettings m_settings; + int m_sampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + double m_shiftFrequencyFactor; //!< Channel frequency shift factor + bool m_doApplySettings; + + LocalSource* m_localSource; + MessageQueue m_inputMessageQueue; + + QTime m_time; + uint32_t m_tickCount; + + explicit LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *txChannel, QWidget* parent = 0); + virtual ~LocalSourceGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void applyChannelSettings(); + void displaySettings(); + void displayRateAndShift(); + void updateLocalDevices(); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + + void applyInterpolation(); + void applyPosition(); + +private slots: + void handleSourceMessages(); + void on_interpolationFactor_currentIndexChanged(int index); + void on_position_valueChanged(int value); + void on_localDevice_currentIndexChanged(int index); + void on_localDevicesRefresh_clicked(bool checked); + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + void tick(); +}; + + + +#endif /* PLUGINS_CHANNELRX_LOCALSINK_LOCALSINKGUI_H_ */ diff --git a/plugins/channeltx/localsource/localsourcegui.ui b/plugins/channeltx/localsource/localsourcegui.ui new file mode 100644 index 000000000..6224aabaf --- /dev/null +++ b/plugins/channeltx/localsource/localsourcegui.ui @@ -0,0 +1,331 @@ + + + LocalSourceGUI + + + + 0 + 0 + 320 + 110 + + + + + 0 + 0 + + + + + 320 + 100 + + + + + 320 + 16777215 + + + + + Liberation Sans + 9 + + + + Local Source + + + Local Source + + + + + 10 + 10 + 301 + 91 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 3 + + + + + + + Int + + + + + + + + 55 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + + 50 + 0 + + + + Effective channel rate (kS/s) + + + 0000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + 0 + + + + Filter chain stages left to right (L: low, C: center, H: high) + + + LLLLLL + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 85 + 0 + + + + Offset frequency with thousands separator (Hz) + + + -9,999,999 Hz + + + 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 + + + + + + + + + + + + + Device + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Local device deviceset index + + + + + + + + 24 + 16777215 + + + + Refresh indexes of available local devices + + + + + + + :/recycle.png:/recycle.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+
+ + + + +
diff --git a/plugins/channeltx/localsource/localsourceplugin.cpp b/plugins/channeltx/localsource/localsourceplugin.cpp new file mode 100644 index 000000000..f30f04eec --- /dev/null +++ b/plugins/channeltx/localsource/localsourceplugin.cpp @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "localsourceplugin.h" + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "localsourcegui.h" +#endif +#include "localsource.h" + +const PluginDescriptor LocalSourcePlugin::m_pluginDescriptor = { + QString("Local channel source"), + QString("4.8.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +LocalSourcePlugin::LocalSourcePlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& LocalSourcePlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void LocalSourcePlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register channel Source + m_pluginAPI->registerTxChannel(LocalSource::m_channelIdURI, LocalSource::m_channelId, this); +} + +#ifdef SERVER_MODE +PluginInstanceGUI* LocalSourcePlugin::createTxChannelGUI( + DeviceUISet *deviceUISet __attribute__((unused)), + BasebandSampleSource *txChannel __attribute__((unused))) +{ + return 0; +} +#else +PluginInstanceGUI* LocalSourcePlugin::createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) +{ + return LocalSourceGUI::create(m_pluginAPI, deviceUISet, txChannel); +} +#endif + +BasebandSampleSource* LocalSourcePlugin::createTxChannelBS(DeviceAPI *deviceAPI) +{ + return new LocalSource(deviceAPI); +} + +ChannelAPI* LocalSourcePlugin::createTxChannelCS(DeviceAPI *deviceAPI) +{ + return new LocalSource(deviceAPI); +} diff --git a/plugins/channeltx/localsource/localsourceplugin.h b/plugins/channeltx/localsource/localsourceplugin.h new file mode 100644 index 000000000..78f1dacc2 --- /dev/null +++ b/plugins/channeltx/localsource/localsourceplugin.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_CHANNELTX_LOCALSOURCE_LOCALSOURCEPLUGIN_H_ +#define PLUGINS_CHANNELTX_LOCALSOURCE_LOCALSOURCEPLUGIN_H_ + + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSource; + +class LocalSourcePlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.demod.localsource") + +public: + explicit LocalSourcePlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual PluginInstanceGUI* createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel); + virtual BasebandSampleSource* createTxChannelBS(DeviceAPI *deviceAPI); + virtual ChannelAPI* createTxChannelCS(DeviceAPI *deviceAPI); + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif /* PLUGINS_CHANNELTX_LOCALSOURCE_LOCALSOURCEPLUGIN_H_ */ diff --git a/plugins/channeltx/localsource/localsourcesettings.cpp b/plugins/channeltx/localsource/localsourcesettings.cpp new file mode 100644 index 000000000..4c9eeaee2 --- /dev/null +++ b/plugins/channeltx/localsource/localsourcesettings.cpp @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "localsourcesettings.h" + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" + + +LocalSourceSettings::LocalSourceSettings() +{ + resetToDefaults(); +} + +void LocalSourceSettings::resetToDefaults() +{ + m_localDeviceIndex = 0; + m_rgbColor = QColor(140, 4, 4).rgb(); + m_title = "Local sink"; + m_log2Interp = 0; + m_filterChainHash = 0; + m_channelMarker = nullptr; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; +} + +QByteArray LocalSourceSettings::serialize() const +{ + SimpleSerializer s(1); + s.writeU32(1, m_localDeviceIndex); + 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_log2Interp); + s.writeU32(13, m_filterChainHash); + + return s.final(); +} + +bool LocalSourceSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + uint32_t tmp; + QString strtmp; + + d.readU32(1, &m_localDeviceIndex, 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_log2Interp = tmp > 6 ? 6 : tmp; + d.readU32(13, &m_filterChainHash, 0); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + + + + + diff --git a/plugins/channeltx/localsource/localsourcesettings.h b/plugins/channeltx/localsource/localsourcesettings.h new file mode 100644 index 000000000..c883f9f26 --- /dev/null +++ b/plugins/channeltx/localsource/localsourcesettings.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 INCLUDE_LOCALSOURCESETTINGS_H_ +#define INCLUDE_LOCALSOURCESETTINGS_H_ + +#include +#include + +class Serializable; + +struct LocalSourceSettings +{ + uint32_t m_localDeviceIndex; + quint32 m_rgbColor; + QString m_title; + uint32_t m_log2Interp; + uint32_t m_filterChainHash; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + + Serializable *m_channelMarker; + + LocalSourceSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* INCLUDE_LOCALSOURCESETTINGS_H_ */ diff --git a/plugins/channeltx/localsource/localsourcethread.cpp b/plugins/channeltx/localsource/localsourcethread.cpp new file mode 100644 index 000000000..854aa2f36 --- /dev/null +++ b/plugins/channeltx/localsource/localsourcethread.cpp @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "dsp/samplesourcefifo.h" + +#include "localsourcethread.h" + +MESSAGE_CLASS_DEFINITION(LocalSourceThread::MsgStartStop, Message) + +LocalSourceThread::LocalSourceThread(QObject* parent) : + QThread(parent), + m_running(false), + m_sampleFifo(0) +{ + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); +} + +LocalSourceThread::~LocalSourceThread() +{ + qDebug("LocalSourceThread::~LocalSourceThread"); +} + +void LocalSourceThread::startStop(bool start) +{ + MsgStartStop *msg = MsgStartStop::create(start); + m_inputMessageQueue.push(msg); +} + +void LocalSourceThread::setSampleFifo(SampleSourceFifo *sampleFifo) +{ + m_sampleFifo = sampleFifo; +} + +void LocalSourceThread::pullSamples(unsigned int count) +{ + SampleVector::iterator beginRead; + m_sampleFifo->readAdvance(beginRead, count); + emit samplesAvailable(m_sampleFifo->getIteratorOffset(beginRead)); +} + +void LocalSourceThread::startWork() +{ + qDebug("LocalSourceThread::startWork"); + m_startWaitMutex.lock(); + start(); + + while(!m_running) { + m_startWaiter.wait(&m_startWaitMutex, 100); + } + + m_startWaitMutex.unlock(); +} + +void LocalSourceThread::stopWork() +{ + qDebug("LocalSourceThread::stopWork"); + m_running = false; + wait(); +} + +void LocalSourceThread::run() +{ + qDebug("LocalSinkThread::run: begin"); + m_running = true; + m_startWaiter.wakeAll(); + + while (m_running) + { + sleep(1); // Do nothing as everything is in the data handler (dequeuer) + } + + m_running = false; + qDebug("LocalSinkThread::run: end"); +} + +void LocalSourceThread::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (MsgStartStop::match(*message)) + { + MsgStartStop* notif = (MsgStartStop*) message; + qDebug("LocalSourceThread::handleInputMessages: MsgStartStop: %s", notif->getStartStop() ? "start" : "stop"); + + if (notif->getStartStop()) { + startWork(); + } else { + stopWork(); + } + + delete message; + } + } +} diff --git a/plugins/channeltx/localsource/localsourcethread.h b/plugins/channeltx/localsource/localsourcethread.h new file mode 100644 index 000000000..94636378a --- /dev/null +++ b/plugins/channeltx/localsource/localsourcethread.h @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_CHANNELTX_LOCALOURCE_LOCALOURCETHREAD_H_ +#define PLUGINS_CHANNELTX_LOCALOURCE_LOCALOURCETHREAD_H_ + +#include +#include +#include + +#include "dsp/dsptypes.h" +#include "util/message.h" +#include "util/messagequeue.h" + +class SampleSourceFifo; + +class LocalSourceThread : public QThread { + Q_OBJECT + +public: + 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) + { } + }; + + LocalSourceThread(QObject* parent = 0); + ~LocalSourceThread(); + + void startStop(bool start); + void setSampleFifo(SampleSourceFifo *sampleFifo); + +public slots: + void pullSamples(unsigned int count); + +signals: + void samplesAvailable(int offset); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + volatile bool m_running; + SampleSourceFifo *m_sampleFifo; + + MessageQueue m_inputMessageQueue; + + void startWork(); + void stopWork(); + void run(); + +private slots: + void handleInputMessages(); +}; + +#endif // PLUGINS_CHANNELTX_LOCALSINK_LOCALSINKTHREAD_H_ + diff --git a/sdrbase/dsp/samplesourcefifo.cpp b/sdrbase/dsp/samplesourcefifo.cpp index 85cc1d6c7..47ef02ed2 100644 --- a/sdrbase/dsp/samplesourcefifo.cpp +++ b/sdrbase/dsp/samplesourcefifo.cpp @@ -92,3 +92,13 @@ void SampleSourceFifo::bumpIndex(SampleVector::iterator& writeAt) writeAt = m_data.begin() + m_iw; } + +int SampleSourceFifo::getIteratorOffset(const SampleVector::iterator& iterator) +{ + return iterator - m_data.begin(); +} + +void SampleSourceFifo::setIteratorFromOffset(SampleVector::iterator& iterator, int offset) +{ + iterator = m_data.begin() + offset; +} diff --git a/sdrbase/dsp/samplesourcefifo.h b/sdrbase/dsp/samplesourcefifo.h index c37ef1d6d..6878160b6 100644 --- a/sdrbase/dsp/samplesourcefifo.h +++ b/sdrbase/dsp/samplesourcefifo.h @@ -41,6 +41,8 @@ public: void getReadIterator(SampleVector::iterator& readUntil); //!< get iterator past the last sample of a read advance operation (i.e. current read iterator) void getWriteIterator(SampleVector::iterator& writeAt); //!< get iterator to current item for update - write phase 1 void bumpIndex(SampleVector::iterator& writeAt); //!< copy current item to second buffer and bump write index - write phase 2 + int getIteratorOffset(const SampleVector::iterator& iterator); + void setIteratorFromOffset(SampleVector::iterator& iterator, int offset); void write(const Sample& sample); //!< write directly - phase 1 + phase 2 diff --git a/sdrbase/resources/webapi/doc/swagger/include/LocalSource.yaml b/sdrbase/resources/webapi/doc/swagger/include/LocalSource.yaml new file mode 100644 index 000000000..b68504122 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/LocalSource.yaml @@ -0,0 +1,25 @@ +LocalSourceSettings: + description: "Local channel source settings" + properties: + localDeviceIndex: + description: "Sending local device index" + type: integer + rgbColor: + type: integer + title: + type: string + log2Interp: + type: integer + filterChainHash: + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer diff --git a/swagger/sdrangel/api/swagger/include/LocalSource.yaml b/swagger/sdrangel/api/swagger/include/LocalSource.yaml new file mode 100644 index 000000000..b68504122 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/LocalSource.yaml @@ -0,0 +1,25 @@ +LocalSourceSettings: + description: "Local channel source settings" + properties: + localDeviceIndex: + description: "Sending local device index" + type: integer + rgbColor: + type: integer + title: + type: string + log2Interp: + type: integer + filterChainHash: + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer diff --git a/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.cpp new file mode 100644 index 000000000..0a6bf5800 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.cpp @@ -0,0 +1,299 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 4.8.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGLocalSourceSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGLocalSourceSettings::SWGLocalSourceSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGLocalSourceSettings::SWGLocalSourceSettings() { + local_device_index = 0; + m_local_device_index_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + log2_interp = 0; + m_log2_interp_isSet = false; + filter_chain_hash = 0; + m_filter_chain_hash_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; +} + +SWGLocalSourceSettings::~SWGLocalSourceSettings() { + this->cleanup(); +} + +void +SWGLocalSourceSettings::init() { + local_device_index = 0; + m_local_device_index_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + log2_interp = 0; + m_log2_interp_isSet = false; + filter_chain_hash = 0; + m_filter_chain_hash_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; +} + +void +SWGLocalSourceSettings::cleanup() { + + + if(title != nullptr) { + delete title; + } + + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + +} + +SWGLocalSourceSettings* +SWGLocalSourceSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGLocalSourceSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&local_device_index, pJson["localDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&log2_interp, pJson["log2Interp"], "qint32", ""); + + ::SWGSDRangel::setValue(&filter_chain_hash, pJson["filterChainHash"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + +} + +QString +SWGLocalSourceSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGLocalSourceSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_local_device_index_isSet){ + obj->insert("localDeviceIndex", QJsonValue(local_device_index)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_log2_interp_isSet){ + obj->insert("log2Interp", QJsonValue(log2_interp)); + } + if(m_filter_chain_hash_isSet){ + obj->insert("filterChainHash", QJsonValue(filter_chain_hash)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } + + return obj; +} + +qint32 +SWGLocalSourceSettings::getLocalDeviceIndex() { + return local_device_index; +} +void +SWGLocalSourceSettings::setLocalDeviceIndex(qint32 local_device_index) { + this->local_device_index = local_device_index; + this->m_local_device_index_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getRgbColor() { + return rgb_color; +} +void +SWGLocalSourceSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGLocalSourceSettings::getTitle() { + return title; +} +void +SWGLocalSourceSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getLog2Interp() { + return log2_interp; +} +void +SWGLocalSourceSettings::setLog2Interp(qint32 log2_interp) { + this->log2_interp = log2_interp; + this->m_log2_interp_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getFilterChainHash() { + return filter_chain_hash; +} +void +SWGLocalSourceSettings::setFilterChainHash(qint32 filter_chain_hash) { + this->filter_chain_hash = filter_chain_hash; + this->m_filter_chain_hash_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGLocalSourceSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGLocalSourceSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGLocalSourceSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGLocalSourceSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGLocalSourceSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGLocalSourceSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGLocalSourceSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + + +bool +SWGLocalSourceSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_local_device_index_isSet){ isObjectUpdated = true; break;} + if(m_rgb_color_isSet){ isObjectUpdated = true; break;} + if(title != nullptr && *title != QString("")){ isObjectUpdated = true; break;} + if(m_log2_interp_isSet){ isObjectUpdated = true; break;} + if(m_filter_chain_hash_isSet){ isObjectUpdated = true; break;} + if(m_use_reverse_api_isSet){ isObjectUpdated = true; break;} + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ isObjectUpdated = true; break;} + if(m_reverse_api_port_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_device_index_isSet){ isObjectUpdated = true; break;} + if(m_reverse_api_channel_index_isSet){ isObjectUpdated = true; break;} + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.h b/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.h new file mode 100644 index 000000000..3a73e195f --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGLocalSourceSettings.h @@ -0,0 +1,113 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 4.8.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGLocalSourceSettings.h + * + * Local channel source settings + */ + +#ifndef SWGLocalSourceSettings_H_ +#define SWGLocalSourceSettings_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGLocalSourceSettings: public SWGObject { +public: + SWGLocalSourceSettings(); + SWGLocalSourceSettings(QString* json); + virtual ~SWGLocalSourceSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGLocalSourceSettings* fromJson(QString &jsonString) override; + + qint32 getLocalDeviceIndex(); + void setLocalDeviceIndex(qint32 local_device_index); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getLog2Interp(); + void setLog2Interp(qint32 log2_interp); + + qint32 getFilterChainHash(); + void setFilterChainHash(qint32 filter_chain_hash); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + + + virtual bool isSet() override; + +private: + qint32 local_device_index; + bool m_local_device_index_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + qint32 log2_interp; + bool m_log2_interp_isSet; + + qint32 filter_chain_hash; + bool m_filter_chain_hash_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + +}; + +} + +#endif /* SWGLocalSourceSettings_H_ */