1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-02-24 10:20:06 -05:00

Denoiser feature base commit

This commit is contained in:
f4exb 2026-01-06 07:54:29 +01:00
parent 3eae58bf4a
commit 455f8a8034
36 changed files with 3833 additions and 3 deletions

View File

@ -152,6 +152,7 @@ option(ENABLE_FEATURE_REMOTECONTROL "Enable feature remote control plugin" ON)
option(ENABLE_FEATURE_SKYMAP "Enable feature sky map plugin" ON)
option(ENABLE_FEATURE_SID "Enable feature sid plugin" ON)
option(ENABLE_FEATURE_MORSEDECODER "Enable feature morsedecoder plugin" ON)
option(ENABLE_FEATURE_DENOISER "Enable feature denoiser plugin" ON)
# on windows always build external libraries
if(WIN32)
@ -824,6 +825,7 @@ if (NOT ENABLE_EXTERNAL_LIBRARIES OR (ENABLE_EXTERNAL_LIBRARIES STREQUAL "AUTO")
find_package(HIDAPI)
find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
find_package(GGMorse)
find_package(RNnoise)
# Devices
if(ENABLE_AIRSPY)

View File

@ -3,7 +3,7 @@
"configurePresets": [
{
"name": "default",
"binaryDir": "build-default",
"binaryDir": "build",
"cacheVariables": {
"DEBUG_OUTPUT": "ON",
"AIRSPYHF_DIR": "/opt/install/libairspyhf",
@ -29,6 +29,7 @@
"UHD_DIR": "/opt/install/uhd",
"XTRX_DIR": "/opt/install/xtrx-images",
"GGMORSE_DIR": "/opt/install/ggmorse",
"RNNOISE_DIR": "/opt/install/rnnoise",
"CMAKE_INSTALL_PREFIX": "/opt/install/sdrangel"
},
"warnings": {

View File

@ -0,0 +1,38 @@
if (NOT RNNOISE_FOUND)
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_RNNoise "librnnoise")
FIND_PATH(RNNOISE_INCLUDE_DIR
NAMES rnnoise.h
HINTS ${RNNOISE_DIR}/include
${PC_RNNOISE_INCLUDE_DIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(RNNOISE_LIBRARIES
NAMES rnnoise librnnoise
HINTS ${RNNOISE_DIR}/lib
${RNNOISE_DIR}/lib64
${PC_RNNOISE_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
if (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES)
set(RNNOISE_FOUND TRUE CACHE INTERNAL "RNNoise found")
message(STATUS "Found RNNoise: ${RNNOISE_INCLUDE_DIR}, ${RNNOISE_LIBRARIES}")
else (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES)
set(RNNOISE_FOUND FALSE CACHE INTERNAL "RNNoise found")
message(STATUS "RNNoise not found")
endif (RNNOISE_INCLUDE_DIR AND RNNOISE_LIBRARIES)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(RNNoise DEFAULT_MSG RNNOISE_LIBRARIES RNNOISE_INCLUDE_DIR)
MARK_AS_ADVANCED(RNNOISE_LIBRARIES RNNOISE_INCLUDE_DIR)
endif (NOT RNNOISE_FOUND)

View File

@ -132,3 +132,9 @@ if (ENABLE_FEATURE_MORSEDECODER AND GGMORSE_FOUND)
else()
message(STATUS "Not building morsedecoder (ENABLE_FEATURE_MORSEDECODER=${ENABLE_FEATURE_MORSEDECODER} GGMODSE_FOUND=${GGMORSE_FOUND})")
endif()
if (ENABLE_FEATURE_DENOISER AND RNNOISE_FOUND)
add_subdirectory(denoiser)
else()
message(STATUS "Not building denoiser (ENABLE_FEATURE_DENOISER=${ENABLE_FEATURE_DENOISER} RNNOISE_FOUND=${RNNOISE_FOUND})")
endif()

View File

@ -0,0 +1,69 @@
project(denoiser)
set(denoiser_SOURCES
denoiser.cpp
denoisersettings.cpp
denoiserplugin.cpp
denoiserworker.cpp
denoiserwebapiadapter.cpp
)
set(denoiser_HEADERS
denoiser.h
denoisersettings.h
denoiserplugin.h
denoiserworker.h
denoiserwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${RNNOISE_INCLUDE_DIR}
)
if(NOT SERVER_MODE)
set(denoiser_SOURCES
${denoiser_SOURCES}
denoisergui.cpp
denoisergui.ui
)
set(denoiser_HEADERS
${denoiser_HEADERS}
denoisergui.h
)
set(TARGET_NAME ${PLUGINS_PREFIX}featuredenoiser)
set(TARGET_LIB "Qt::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME ${PLUGINSSRV_PREFIX}featuredenoisersrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
if(NOT Qt6_FOUND)
add_library(${TARGET_NAME} ${denoiser_SOURCES})
else()
qt_add_plugin(${TARGET_NAME} CLASS_NAME DenoiserPlugin ${denoiser_SOURCES})
endif()
if(NOT BUILD_SHARED_LIBS)
set_property(GLOBAL APPEND PROPERTY STATIC_PLUGINS_PROPERTY ${TARGET_NAME})
endif()
target_link_libraries(${TARGET_NAME} PRIVATE
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
${RNNOISE_LIBRARIES}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,648 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include "SWGFeatureSettings.h"
#include "SWGFeatureActions.h"
#include "SWGDeviceState.h"
#include "dsp/dspcommands.h"
#include "dsp/datafifo.h"
#include "settings/serializable.h"
#include "channel/channelapi.h"
#include "maincore.h"
#include "denoiserworker.h"
#include "denoiser.h"
MESSAGE_CLASS_DEFINITION(Denoiser::MsgConfigureDenoiser, Message)
MESSAGE_CLASS_DEFINITION(Denoiser::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(Denoiser::MsgReportChannels, Message)
MESSAGE_CLASS_DEFINITION(Denoiser::MsgSelectChannel, Message)
MESSAGE_CLASS_DEFINITION(Denoiser::MsgReportSampleRate, Message)
const char* const Denoiser::m_featureIdURI = "sdrangel.feature.denoiser";
const char* const Denoiser::m_featureId = "Denoiser";
Denoiser::Denoiser(WebAPIAdapterInterface *webAPIAdapterInterface) :
Feature(m_featureIdURI, webAPIAdapterInterface),
m_thread(nullptr),
m_running(false),
m_worker(nullptr),
m_availableChannelOrFeatureHandler(DenoiserSettings::m_channelURIs),
m_selectedChannel(nullptr),
m_dataPipe(nullptr)
{
qDebug("Denoiser::Denoiser: webAPIAdapterInterface: %p", webAPIAdapterInterface);
setObjectName(m_featureId);
m_state = StIdle;
m_errorMessage = "Denoiser error";
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&Denoiser::networkManagerFinished
);
QObject::connect(
&m_availableChannelOrFeatureHandler,
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
this,
&Denoiser::channelsOrFeaturesChanged
);
m_availableChannelOrFeatureHandler.scanAvailableChannelsAndFeatures();
}
Denoiser::~Denoiser()
{
QObject::disconnect(
&m_availableChannelOrFeatureHandler,
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
this,
&Denoiser::channelsOrFeaturesChanged
);
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&Denoiser::networkManagerFinished
);
delete m_networkManager;
stop();
}
void Denoiser::start()
{
QMutexLocker m_lock(&m_mutex);
if (m_running) {
return;
}
qDebug("Denoiser::start");
m_thread = new QThread();
m_worker = new DenoiserWorker();
m_worker->moveToThread(m_thread);
QObject::connect(
m_thread,
&QThread::started,
m_worker,
&DenoiserWorker::startWork
);
QObject::connect(
m_thread,
&QThread::finished,
m_worker,
&QObject::deleteLater
);
QObject::connect(
m_thread,
&QThread::finished,
m_thread,
&QThread::deleteLater
);
m_worker->setMessageQueueToFeature(getInputMessageQueue());
m_worker->startWork();
m_state = StRunning;
m_thread->start();
DenoiserWorker::MsgConfigureDenoiserWorker *msg
= DenoiserWorker::MsgConfigureDenoiserWorker::create(m_settings, QList<QString>(), true);
m_worker->getInputMessageQueue()->push(msg);
m_worker->applySampleRate(m_sampleRate);
if (m_dataPipe)
{
DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
if (fifo)
{
DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, true);
m_worker->getInputMessageQueue()->push(msg);
}
}
m_running = true;
}
void Denoiser::stop()
{
QMutexLocker m_lock(&m_mutex);
if (!m_running) {
return;
}
qDebug("Denoiser::stop");
m_running = false;
if (m_dataPipe)
{
DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
if (fifo)
{
DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false);
m_worker->getInputMessageQueue()->push(msg);
}
}
m_worker->stopWork();
m_state = StIdle;
m_thread->quit();
m_thread->wait();
}
double Denoiser::getMagSqAvg() const
{
return m_running ? m_worker->getMagSqAvg() : 0.0;
}
bool Denoiser::handleMessage(const Message& cmd)
{
if (MsgConfigureDenoiser::match(cmd))
{
MsgConfigureDenoiser& cfg = (MsgConfigureDenoiser&) cmd;
qDebug() << "Denoiser::handleMessage: MsgConfigureDenoiser";
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else if (MsgStartStop::match(cmd))
{
MsgStartStop& cfg = (MsgStartStop&) cmd;
qDebug() << "Denoiser::handleMessage: MsgStartStop: start:" << cfg.getStartStop();
if (cfg.getStartStop()) {
start();
} else {
stop();
}
return true;
}
else if (MsgSelectChannel::match(cmd))
{
MsgSelectChannel& cfg = (MsgSelectChannel&) cmd;
ChannelAPI *selectedChannel = cfg.getChannel();
qDebug("Denoiser::handleMessage: MsgSelectChannel: %p %s",
selectedChannel, qPrintable(selectedChannel->objectName()));
setChannel(selectedChannel);
MainCore::MsgChannelDemodQuery *msg = MainCore::MsgChannelDemodQuery::create();
selectedChannel->getInputMessageQueue()->push(msg);
return true;
}
else if (MainCore::MsgChannelDemodReport::match(cmd))
{
qDebug() << "Denoiser::handleMessage: MainCore::MsgChannelDemodReport";
MainCore::MsgChannelDemodReport& report = (MainCore::MsgChannelDemodReport&) cmd;
if (report.getChannelAPI() == m_selectedChannel)
{
m_sampleRate = report.getSampleRate();
if (m_running) {
m_worker->applySampleRate(m_sampleRate);
}
if (m_dataPipe)
{
DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
if (fifo) {
fifo->setSize(2*m_sampleRate);
}
}
if (getMessageQueueToGUI())
{
MsgReportSampleRate *msg = MsgReportSampleRate::create(m_sampleRate);
getMessageQueueToGUI()->push(msg);
}
}
return true;
}
else
{
return false;
}
}
QByteArray Denoiser::serialize() const
{
return m_settings.serialize();
}
bool Denoiser::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return false;
}
}
void Denoiser::applySettings(const DenoiserSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "Denoiser::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
if (m_running)
{
DenoiserWorker::MsgConfigureDenoiserWorker *msg = DenoiserWorker::MsgConfigureDenoiserWorker::create(
settings, settingsKeys, force
);
m_worker->getInputMessageQueue()->push(msg);
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||
settingsKeys.contains("reverseAPIAddress") ||
settingsKeys.contains("reverseAPIPort") ||
settingsKeys.contains("reverseAPIFeatureSetIndex") ||
settingsKeys.contains("m_reverseAPIFeatureIndex");
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
void Denoiser::channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo)
{
m_availableChannels = m_availableChannelOrFeatureHandler.getAvailableChannelOrFeatureList();
notifyUpdate(renameFrom, renameTo);
}
void Denoiser::notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo)
{
if (getMessageQueueToGUI())
{
MsgReportChannels *msg = MsgReportChannels::create(renameFrom, renameTo);
msg->getAvailableChannels() = m_availableChannels;
getMessageQueueToGUI()->push(msg);
}
}
void Denoiser::getAvailableChannelsReport()
{
notifyUpdate(QStringList{}, QStringList{});
}
void Denoiser::setChannel(ChannelAPI *selectedChannel)
{
if ((selectedChannel == m_selectedChannel) || (m_availableChannels.indexOfObject(selectedChannel) == -1)) {
return;
}
MainCore *mainCore = MainCore::instance();
if (m_selectedChannel)
{
ObjectPipe *pipe = mainCore->getDataPipes().unregisterProducerToConsumer(m_selectedChannel, this, "demod");
DataFifo *fifo = qobject_cast<DataFifo*>(pipe->m_element);
if ((fifo) && m_running)
{
DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false);
m_worker->getInputMessageQueue()->push(msg);
}
ObjectPipe *messagePipe = mainCore->getMessagePipes().unregisterProducerToConsumer(m_selectedChannel, this, "reportdemod");
if (messagePipe)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(messagePipe->m_element);
if (messageQueue) {
disconnect(messageQueue, &MessageQueue::messageEnqueued, this, nullptr); // Have to use nullptr, as slot is a lambda.
}
}
}
m_dataPipe = mainCore->getDataPipes().registerProducerToConsumer(selectedChannel, this, "demod");
connect(m_dataPipe, SIGNAL(toBeDeleted(int, QObject*)), this, SLOT(handleDataPipeToBeDeleted(int, QObject*)));
DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
if (fifo)
{
fifo->setSize(96000);
if (m_running)
{
DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, true);
m_worker->getInputMessageQueue()->push(msg);
}
}
ObjectPipe *messagePipe = mainCore->getMessagePipes().registerProducerToConsumer(selectedChannel, this, "reportdemod");
if (messagePipe)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(messagePipe->m_element);
if (messageQueue)
{
QObject::connect(
messageQueue,
&MessageQueue::messageEnqueued,
this,
[=](){ this->handleChannelMessageQueue(messageQueue); },
Qt::QueuedConnection
);
}
}
m_selectedChannel = selectedChannel;
}
int Denoiser::webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
getFeatureStateStr(*response.getState());
MsgStartStop *msg = MsgStartStop::create(run);
getInputMessageQueue()->push(msg);
return 202;
}
int Denoiser::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings());
response.getDenoiserSettings()->init();
webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int Denoiser::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
DenoiserSettings settings = m_settings;
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
MsgConfigureDenoiser *msg = MsgConfigureDenoiser::create(settings, featureSettingsKeys, force);
m_inputMessageQueue.push(msg);
qDebug("Denoiser::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureDenoiser *msgToGUI = MsgConfigureDenoiser::create(settings, featureSettingsKeys, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatFeatureSettings(response, settings);
return 200;
}
void Denoiser::webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const DenoiserSettings& settings)
{
if (response.getDenoiserSettings()->getTitle()) {
*response.getDenoiserSettings()->getTitle() = settings.m_title;
} else {
response.getDenoiserSettings()->setTitle(new QString(settings.m_title));
}
response.getDenoiserSettings()->setRgbColor(settings.m_rgbColor);
response.getDenoiserSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getDenoiserSettings()->getReverseApiAddress()) {
*response.getDenoiserSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getDenoiserSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getDenoiserSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getDenoiserSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
response.getDenoiserSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
if (response.getDenoiserSettings()->getFileRecordName()) {
*response.getDenoiserSettings()->getFileRecordName() = settings.m_fileRecordName;
} else {
response.getDenoiserSettings()->setFileRecordName(new QString(settings.m_fileRecordName));
}
response.getDenoiserSettings()->setRecordToFile(settings.m_recordToFile ? 1 : 0);
if (settings.m_rollupState)
{
if (response.getDenoiserSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getDenoiserSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getDenoiserSettings()->setRollupState(swgRollupState);
}
}
}
void Denoiser::webapiUpdateFeatureSettings(
DenoiserSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response)
{
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getDenoiserSettings()->getTitle();
}
if (featureSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getDenoiserSettings()->getRgbColor();
}
if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getDenoiserSettings()->getUseReverseApi() != 0;
}
if (featureSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getDenoiserSettings()->getReverseApiAddress();
}
if (featureSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getDenoiserSettings()->getReverseApiPort();
}
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
settings.m_reverseAPIFeatureSetIndex = response.getDenoiserSettings()->getReverseApiFeatureSetIndex();
}
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
settings.m_reverseAPIFeatureIndex = response.getDenoiserSettings()->getReverseApiFeatureIndex();
}
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getDenoiserSettings()->getRollupState());
}
if (featureSettingsKeys.contains("fileRecordName")) {
settings.m_fileRecordName = *response.getDenoiserSettings()->getFileRecordName();
}
if (featureSettingsKeys.contains("recordToFile")) {
settings.m_recordToFile = response.getDenoiserSettings()->getRecordToFile() != 0;
}
}
void Denoiser::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const DenoiserSettings& settings, bool force)
{
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
swgFeatureSettings->setFeatureType(new QString("Denoiser"));
swgFeatureSettings->setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings());
SWGSDRangel::SWGDenoiserSettings *swgDenoiserSettings = swgFeatureSettings->getDenoiserSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (featureSettingsKeys.contains("title") || force) {
swgDenoiserSettings->setTitle(new QString(settings.m_title));
}
if (featureSettingsKeys.contains("rgbColor") || force) {
swgDenoiserSettings->setRgbColor(settings.m_rgbColor);
}
if (featureSettingsKeys.contains("fileRecordName")) {
swgDenoiserSettings->setFileRecordName(new QString(settings.m_fileRecordName));
}
if (featureSettingsKeys.contains("recordToFile")) {
swgDenoiserSettings->setRecordToFile(settings.m_recordToFile ? 1 : 0);
}
if (settings.m_rollupState && (featureSettingsKeys.contains("rollupState") || force)) {
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgDenoiserSettings->setRollupState(swgRollupState);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIFeatureSetIndex)
.arg(settings.m_reverseAPIFeatureIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgFeatureSettings->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 swgFeatureSettings;
}
void Denoiser::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "Denoiser::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("Denoiser::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void Denoiser::handleChannelMessageQueue(MessageQueue* messageQueue)
{
Message* message;
while ((message = messageQueue->pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
void Denoiser::handleDataPipeToBeDeleted(int reason, QObject *object)
{
qDebug("Denoiser::handleDataPipeToBeDeleted: %d %p", reason, object);
if ((reason == 0) && (m_selectedChannel == object))
{
DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
if ((fifo) && m_running)
{
DenoiserWorker::MsgConnectFifo *msg = DenoiserWorker::MsgConnectFifo::create(fifo, false);
m_worker->getInputMessageQueue()->push(msg);
}
m_selectedChannel = nullptr;
}
}
int Denoiser::webapiActionsPost(
const QStringList&,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage) {
MainCore* m_core = MainCore::instance();
auto action = query.getDenoiserActions();
if (action == nullptr) {
errorMessage = QString("missing DenoiserActions in request");
return 404;
}
auto deviceId = action->getDeviceId();
auto chanId = action->getChannelId();
ChannelAPI * chan = m_core->getChannel(deviceId, chanId);
if (chan == nullptr) {
errorMessage = QString("device(%1) or channel (%2) on the device does not exist").arg(deviceId).arg(chanId);
return 404;
}
MsgSelectChannel *msg = MsgSelectChannel::create(chan);
getInputMessageQueue()->push(msg);
return 200;
}

View File

@ -0,0 +1,226 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DENOISER_H_
#define INCLUDE_FEATURE_DENOISER_H_
#include <QHash>
#include <QNetworkRequest>
#include <QRecursiveMutex>
#include "feature/feature.h"
#include "util/message.h"
#include "availablechannelorfeaturehandler.h"
#include "denoisersettings.h"
class WebAPIAdapterInterface;
class DenoiserWorker;
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class ObjectPipe;
namespace SWGSDRangel {
class SWGDeviceState;
}
class Denoiser : public Feature
{
Q_OBJECT
public:
class MsgConfigureDenoiser : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DenoiserSettings& getSettings() const { return m_settings; }
const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
bool getForce() const { return m_force; }
static MsgConfigureDenoiser* create(const DenoiserSettings& settings, const QList<QString>& settingsKeys, bool force) {
return new MsgConfigureDenoiser(settings, settingsKeys, force);
}
private:
DenoiserSettings m_settings;
QList<QString> m_settingsKeys;
bool m_force;
MsgConfigureDenoiser(const DenoiserSettings& settings, const QList<QString>& settingsKeys, bool force) :
Message(),
m_settings(settings),
m_settingsKeys(settingsKeys),
m_force(force)
{ }
};
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)
{ }
};
class MsgReportChannels : public Message {
MESSAGE_CLASS_DECLARATION
public:
AvailableChannelOrFeatureList& getAvailableChannels() { return m_availableChannels; }
const QStringList& getRenameFrom() const { return m_renameFrom; }
const QStringList& getRenameTo() const { return m_renameTo; }
static MsgReportChannels* create(const QStringList& renameFrom, const QStringList& renameTo) {
return new MsgReportChannels(renameFrom, renameTo);
}
private:
AvailableChannelOrFeatureList m_availableChannels;
QStringList m_renameFrom;
QStringList m_renameTo;
MsgReportChannels(const QStringList& renameFrom, const QStringList& renameTo) :
Message(),
m_renameFrom(renameFrom),
m_renameTo(renameTo)
{}
};
class MsgSelectChannel : public Message {
MESSAGE_CLASS_DECLARATION
public:
ChannelAPI *getChannel() { return m_channel; }
static MsgSelectChannel* create(ChannelAPI *channel) {
return new MsgSelectChannel(channel);
}
protected:
ChannelAPI *m_channel;
MsgSelectChannel(ChannelAPI *channel) :
Message(),
m_channel(channel)
{ }
};
class MsgReportSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
static MsgReportSampleRate* create(int sampleRate) {
return new MsgReportSampleRate(sampleRate);
}
private:
int m_sampleRate;
MsgReportSampleRate(int sampleRate) :
Message(),
m_sampleRate(sampleRate)
{}
};
Denoiser(WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~Denoiser();
virtual void destroy() { delete this; }
double getMagSqAvg() const;
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) const { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiActionsPost(
const QStringList& featureActionsKeys,
SWGSDRangel::SWGFeatureActions& query,
QString& errorMessage);
static void webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const DenoiserSettings& settings);
static void webapiUpdateFeatureSettings(
DenoiserSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response);
void getAvailableChannelsReport();
static const char* const m_featureIdURI;
static const char* const m_featureId;
private:
QThread *m_thread;
QRecursiveMutex m_mutex;
bool m_running;
DenoiserWorker *m_worker;
DenoiserSettings m_settings;
AvailableChannelOrFeatureList m_availableChannels;
AvailableChannelOrFeatureHandler m_availableChannelOrFeatureHandler;
ChannelAPI *m_selectedChannel;
ObjectPipe *m_dataPipe;
int m_sampleRate;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void start();
void stop();
void applySettings(const DenoiserSettings& settings, const QList<QString>& settingsKeys, bool force = false);
void notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo);
void setChannel(ChannelAPI *selectedChannel);
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const DenoiserSettings& settings, bool force);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleChannelMessageQueue(MessageQueue *messageQueues);
void channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo);
void handleDataPipeToBeDeleted(int reason, QObject *object);
};
#endif // INCLUDE_FEATURE_DENOISER_H_

View File

@ -0,0 +1,398 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QMessageBox>
#include <QFileDialog>
#include "feature/featureuiset.h"
#include "gui/basicfeaturesettingsdialog.h"
#include "gui/dialpopup.h"
#include "gui/dialogpositioner.h"
#include "util/db.h"
#include "maincore.h"
#include "ui_denoisergui.h"
#include "denoiser.h"
#include "denoisergui.h"
DenoiserGUI* DenoiserGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
{
DenoiserGUI* gui = new DenoiserGUI(pluginAPI, featureUISet, feature);
return gui;
}
void DenoiserGUI::destroy()
{
delete this;
}
void DenoiserGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray DenoiserGUI::serialize() const
{
return m_settings.serialize();
}
bool DenoiserGUI::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
displaySettings();
applySettings(true);
return true;
}
else
{
resetToDefaults();
return false;
}
}
bool DenoiserGUI::handleMessage(const Message& message)
{
if (Denoiser::MsgConfigureDenoiser::match(message))
{
qDebug("DenoiserGUI::handleMessage: Denoiser::MsgConfigureDenoiser");
const Denoiser::MsgConfigureDenoiser& cfg = (Denoiser::MsgConfigureDenoiser&) message;
if (cfg.getForce()) {
m_settings = cfg.getSettings();
} else {
m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings());
}
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (Denoiser::MsgReportChannels::match(message))
{
qDebug("DenoiserGUI::handleMessage: Denoiser::MsgReportChannels");
Denoiser::MsgReportChannels& report = (Denoiser::MsgReportChannels&) message;
m_availableChannels = report.getAvailableChannels();
updateChannelList();
return true;
}
else if (Denoiser::MsgReportSampleRate::match(message))
{
Denoiser::MsgReportSampleRate& report = (Denoiser::MsgReportSampleRate&) message;
int sampleRate = report.getSampleRate();
qDebug("DenoiserGUI::handleMessage: Denoiser::MsgReportSampleRate: %d", sampleRate);
displaySampleRate(sampleRate);
m_sampleRate = sampleRate;
return true;
}
return false;
}
void DenoiserGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()))
{
if (handleMessage(*message)) {
delete message;
}
}
}
void DenoiserGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
RollupContents *rollupContents = getRollupContents();
rollupContents->saveState(m_rollupState);
applySettings();
}
DenoiserGUI::DenoiserGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
FeatureGUI(parent),
ui(new Ui::DenoiserGUI),
m_pluginAPI(pluginAPI),
m_featureUISet(featureUISet),
m_sampleRate(48000),
m_doApplySettings(true),
m_lastFeatureState(0),
m_selectedChannel(nullptr)
{
m_feature = feature;
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/feature/denoiser/readme.md";
RollupContents *rollupContents = getRollupContents();
ui->setupUi(rollupContents);
rollupContents->arrangeRollups();
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
m_denoiser = reinterpret_cast<Denoiser*>(feature);
m_denoiser->setMessageQueueToGUI(&m_inputMessageQueue);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
displaySampleRate(m_sampleRate);
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
m_settings.setRollupState(&m_rollupState);
displaySettings();
applySettings(true);
makeUIConnections();
DialPopup::addPopupsToChildDials(this);
m_resizer.enableChildMouseTracking();
m_denoiser->getAvailableChannelsReport();
}
DenoiserGUI::~DenoiserGUI()
{
delete ui;
}
void DenoiserGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void DenoiserGUI::setWorkspaceIndex(int index)
{
m_settings.m_workspaceIndex = index;
m_feature->setWorkspaceIndex(index);
}
void DenoiserGUI::displaySettings()
{
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_settings.m_title);
setTitle(m_settings.m_title);
blockApplySettings(true);
ui->record->setChecked(m_settings.m_recordToFile);
ui->fileNameText->setText(m_settings.m_fileRecordName);
ui->showFileDialog->setEnabled(!m_settings.m_recordToFile);
getRollupContents()->restoreState(m_rollupState);
blockApplySettings(false);
}
void DenoiserGUI::displaySampleRate(int sampleRate)
{
QString s = QString::number(sampleRate/1000.0, 'f', 1);
ui->sinkSampleRateText->setText(tr("%1 kS/s").arg(s));
}
void DenoiserGUI::updateChannelList()
{
ui->channels->blockSignals(true);
ui->channels->clear();
AvailableChannelOrFeatureList::const_iterator it = m_availableChannels.begin();
int selectedItem = -1;
for (int i = 0; it != m_availableChannels.end(); ++it, i++)
{
ui->channels->addItem(it->getLongId());
if (it->m_object == m_selectedChannel) {
selectedItem = i;
}
}
ui->channels->blockSignals(false);
if (m_availableChannels.size() > 0)
{
if (selectedItem >= 0) {
ui->channels->setCurrentIndex(selectedItem);
} else {
ui->channels->setCurrentIndex(0);
}
}
}
void DenoiserGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuType::ContextMenuChannelSettings)
{
BasicFeatureSettingsDialog dialog(this);
dialog.setTitle(m_settings.m_title);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
dialog.setDefaultTitle(m_displayedName);
dialog.move(p);
new DialogPositioner(&dialog, false);
dialog.exec();
m_settings.m_title = dialog.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
setTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
m_settingsKeys.append("title");
m_settingsKeys.append("rgbColor");
m_settingsKeys.append("useReverseAPI");
m_settingsKeys.append("reverseAPIAddress");
m_settingsKeys.append("reverseAPIPort");
m_settingsKeys.append("reverseAPIFeatureSetIndex");
m_settingsKeys.append("reverseAPIFeatureIndex");
applySettings();
}
resetContextMenuType();
}
void DenoiserGUI::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
Denoiser::MsgStartStop *message = Denoiser::MsgStartStop::create(checked);
m_denoiser->getInputMessageQueue()->push(message);
}
}
void DenoiserGUI::on_channels_currentIndexChanged(int index)
{
if ((index >= 0) && (index < m_availableChannels.size()))
{
m_selectedChannel = qobject_cast<ChannelAPI*>(m_availableChannels[index].m_object);
Denoiser::MsgSelectChannel *msg = Denoiser::MsgSelectChannel::create(m_selectedChannel);
m_denoiser->getInputMessageQueue()->push(msg);
}
}
void DenoiserGUI::on_channelApply_clicked()
{
if (ui->channels->count() > 0) {
on_channels_currentIndexChanged(ui->channels->currentIndex());
}
}
void DenoiserGUI::on_record_toggled(bool checked)
{
ui->showFileDialog->setEnabled(!checked);
m_settings.m_recordToFile = checked;
m_settingsKeys.append("recordToFile");
applySettings();
}
void DenoiserGUI::on_showFileDialog_clicked(bool checked)
{
(void) checked;
QFileDialog fileDialog(
this,
tr("Save record file"),
m_settings.m_fileRecordName,
tr("WAV Files (*.wav)")
);
fileDialog.setOptions(QFileDialog::DontUseNativeDialog);
fileDialog.setFileMode(QFileDialog::AnyFile);
QStringList fileNames;
if (fileDialog.exec())
{
fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
m_settings.m_fileRecordName = fileNames.at(0);
ui->fileNameText->setText(m_settings.m_fileRecordName);
m_settingsKeys.append("fileRecordName");
applySettings();
}
}
}
void DenoiserGUI::tick()
{
m_channelPowerAvg(m_denoiser->getMagSqAvg());
double powDb = CalcDb::dbPower((double) m_channelPowerAvg);
ui->channelPower->setText(tr("%1 dB").arg(powDb, 0, 'f', 1));
}
void DenoiserGUI::updateStatus()
{
int state = m_denoiser->getState();
if (m_lastFeatureState != state)
{
switch (state)
{
case Feature::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case Feature::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case Feature::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case Feature::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_denoiser->getErrorMessage());
break;
default:
break;
}
m_lastFeatureState = state;
}
}
void DenoiserGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
Denoiser::MsgConfigureDenoiser* message = Denoiser::MsgConfigureDenoiser::create( m_settings, m_settingsKeys, force);
m_denoiser->getInputMessageQueue()->push(message);
}
m_settingsKeys.clear();
}
void DenoiserGUI::makeUIConnections()
{
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &DenoiserGUI::on_startStop_toggled);
QObject::connect(ui->channels, qOverload<int>(&QComboBox::currentIndexChanged), this, &DenoiserGUI::on_channels_currentIndexChanged);
QObject::connect(ui->channelApply, &QPushButton::clicked, this, &DenoiserGUI::on_channelApply_clicked);
QObject::connect(ui->record, &ButtonSwitch::toggled, this, &DenoiserGUI::on_record_toggled);
QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &DenoiserGUI::on_showFileDialog_clicked);
}

View File

@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DENOISERGUI_H_
#define INCLUDE_FEATURE_DENOISERGUI_H_
#include <QTimer>
#include <QList>
#include "feature/featuregui.h"
#include "util/movingaverage.h"
#include "util/messagequeue.h"
#include "availablechannelorfeaturehandler.h"
#include "settings/rollupstate.h"
#include "denoisersettings.h"
class PluginAPI;
class FeatureUISet;
class Denoiser;
class Feature;
namespace Ui {
class DenoiserGUI;
}
class DenoiserGUI : public FeatureGUI {
Q_OBJECT
public:
static DenoiserGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index);
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }
private:
Ui::DenoiserGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
DenoiserSettings m_settings;
QList<QString> m_settingsKeys;
RollupState m_rollupState;
int m_sampleRate;
bool m_doApplySettings;
Denoiser* m_denoiser;
MessageQueue m_inputMessageQueue;
QTimer m_statusTimer;
int m_lastFeatureState;
AvailableChannelOrFeatureList m_availableChannels;
ChannelAPI *m_selectedChannel;
MovingAverageUtil<double, double, 40> m_channelPowerAvg;
explicit DenoiserGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~DenoiserGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displaySampleRate(int sampleRate);
void updateChannelList();
bool handleMessage(const Message& message);
void makeUIConnections();
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_channels_currentIndexChanged(int index);
void on_channelApply_clicked();
void on_record_toggled(bool checked);
void on_showFileDialog_clicked(bool checked);
void updateStatus();
void tick();
};
#endif // INCLUDE_FEATURE_DENOISERGUI_H_

View File

@ -0,0 +1,419 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DenoiserGUI</class>
<widget class="RollupContents" name="DenoiserGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>452</width>
<height>200</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>452</width>
<height>200</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Denoiser</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>2</y>
<width>450</width>
<height>151</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>450</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="HeaderLayout">
<property name="topMargin">
<number>2</number>
</property>
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>start/stop acquisition</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="channelsLabel">
<property name="text">
<string>Chan</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="channels">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Channel index</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="channelApply">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>(Re) apply channel selection</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/checkmark.png</normaloff>:/checkmark.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sinkSampleRateText">
<property name="toolTip">
<string>Analyzer (sink) sample rate</string>
</property>
<property name="text">
<string>00000.0 kS/s</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="channelPower">
<property name="minimumSize">
<size>
<width>52</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Channel power</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>-100.0 dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="denoiserLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBox">
<property name="toolTip">
<string>Noise reduction scheme</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>RNnoise</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="audioMute">
<property name="toolTip">
<string>Left: Mute/Unmute audio Right: view/select audio device</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/sound_on.png</normaloff>
<normalon>:/sound_off.png</normalon>:/sound_on.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="volumeLayout">
<item>
<widget class="QLabel" name="volLabel">
<property name="text">
<string>Vol</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="volume">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio input gain</string>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>25</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio input gain value</string>
</property>
<property name="text">
<string>1.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="LevelMeterVU" name="volumeMeter" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Level (% full range) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="fileNameLayout">
<item>
<widget class="ButtonSwitch" name="record">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Start/stop recording</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/record_off.png</normaloff>:/record_off.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="showFileDialog">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Open file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/preset-load.png</normaloff>:/preset-load.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fileNameText">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Current recording file</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterVU</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,78 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "denoisergui.h"
#endif
#include "denoiser.h"
#include "denoiserplugin.h"
#include "denoiserwebapiadapter.h"
const PluginDescriptor DenoiserPlugin::m_pluginDescriptor = {
Denoiser::m_featureId,
QStringLiteral("Denoiser"),
QStringLiteral("7.23.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
DenoiserPlugin::DenoiserPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& DenoiserPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void DenoiserPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register RigCtl Server feature
m_pluginAPI->registerFeature(Denoiser::m_featureIdURI, Denoiser::m_featureId, this);
}
#ifdef SERVER_MODE
FeatureGUI* DenoiserPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
(void) featureUISet;
(void) feature;
return nullptr;
}
#else
FeatureGUI* DenoiserPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return DenoiserGUI::create(m_pluginAPI, featureUISet, feature);
}
#endif
Feature* DenoiserPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
{
return new Denoiser(webAPIAdapterInterface);
}
FeatureWebAPIAdapter* DenoiserPlugin::createFeatureWebAPIAdapter() const
{
return new DenoiserWebAPIAdapter();
}

View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DENOISERPLUGIN_H
#define INCLUDE_FEATURE_DENOISERPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FeatureGUI;
class WebAPIAdapterInterface;
class DenoiserPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.feature.denoiser")
public:
explicit DenoiserPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FEATURE_DENOISERPLUGIN_H

View File

@ -0,0 +1,220 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "denoisersettings.h"
const QStringList DenoiserSettings::m_channelURIs = {
QStringLiteral("sdrangel.channel.amdemod"),
QStringLiteral("sdrangel.channel.bfm"),
QStringLiteral("sdrangel.channel.dabdemod"),
QStringLiteral("sdrangel.channel.dsddemod"),
QStringLiteral("sdrangel.channel.m17demod"),
QStringLiteral("sdrangel.channel.nfmdemod"),
QStringLiteral("sdrangel.channel.ssbdemod"),
QStringLiteral("sdrangel.channel.wfmdemod"),
QStringLiteral("sdrangel.channel.wdsprx"),
};
DenoiserSettings::DenoiserSettings() :
m_rollupState(nullptr)
{
resetToDefaults();
}
void DenoiserSettings::resetToDefaults()
{
m_denoiserType = DenoiserType::DenoiserType_RNnoise;
m_title = "Denoiser";
m_audioMute = false;
m_rgbColor = 0xffd700; // gold
m_useReverseAPI = false;
m_reverseAPIAddress = "localhost";
m_reverseAPIPort = 8888;
m_reverseAPIFeatureSetIndex = 0;
m_reverseAPIFeatureIndex = 0;
m_fileRecordName = "denoiser_record.wav";
m_recordToFile = false;
m_workspaceIndex = -1;
m_geometryBytes.clear();
}
QByteArray DenoiserSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, static_cast<qint32>(m_denoiserType));
s.writeBool(14, m_audioMute);
s.writeString(2, m_title);
s.writeU32(3, m_rgbColor);
s.writeBool(4, m_useReverseAPI);
s.writeString(5, m_reverseAPIAddress);
s.writeU32(6, m_reverseAPIPort);
s.writeU32(7, m_reverseAPIFeatureSetIndex);
s.writeU32(8, m_reverseAPIFeatureIndex);
s.writeString(9, m_fileRecordName);
s.writeBool(10, m_recordToFile);
s.writeS32(11, m_workspaceIndex);
s.writeBlob(12, m_geometryBytes);
if (m_rollupState) {
s.writeBlob(13, m_rollupState->serialize());
}
return s.final();
}
bool DenoiserSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
qint32 itmp;
QByteArray bytetmp;
uint32_t utmp;
d.readS32(1, &itmp, 1);
m_denoiserType = static_cast<DenoiserType>(itmp);
d.readBool(14, &m_audioMute, false);
d.readString(2, &m_title, "Denoiser");
d.readU32(3, &m_rgbColor, 0xffd700); // gold
d.readBool(4, &m_useReverseAPI, false);
d.readString(5, &m_reverseAPIAddress, "localhost");
d.readU32(6, &utmp, 8888);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(7, &utmp, 0);
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
d.readU32(8, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
d.readString(9, &m_fileRecordName, "denoiser_record.wav");
d.readBool(10, &m_recordToFile, false);
d.readS32(11, &m_workspaceIndex, -1);
d.readBlob(12, &m_geometryBytes);
if (m_rollupState)
{
d.readBlob(13, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}
void DenoiserSettings::applySettings(const QStringList& settingsKeys, const DenoiserSettings& settings)
{
if (settingsKeys.contains("denoiserType")) {
m_denoiserType = settings.m_denoiserType;
}
if (settingsKeys.contains("audioMute")) {
m_audioMute = settings.m_audioMute;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
if (settingsKeys.contains("rgbColor")) {
m_rgbColor = settings.m_rgbColor;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
if (settingsKeys.contains("reverseAPIAddress")) {
m_reverseAPIAddress = settings.m_reverseAPIAddress;
}
if (settingsKeys.contains("reverseAPIPort")) {
m_reverseAPIPort = settings.m_reverseAPIPort;
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex")) {
m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex")) {
m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
if (settingsKeys.contains("fileRecordName")) {
m_fileRecordName = settings.m_fileRecordName;
}
if (settingsKeys.contains("recordToFile")) {
m_recordToFile = settings.m_recordToFile;
}
}
QString DenoiserSettings::getDebugString(const QStringList& settingsKeys, bool force) const
{
QString debugString;
if (settingsKeys.contains("denoiserType") || force) {
debugString += QString("DenoiserType: %1 ").arg(static_cast<qint32>(m_denoiserType));
}
if (settingsKeys.contains("audioMute") || force) {
debugString += QString("Audio Mute: %1 ").arg(m_audioMute ? "true" : "false");
}
if (settingsKeys.contains("title") || force) {
debugString += QString("Title: %1 ").arg(m_title);
}
if (settingsKeys.contains("rgbColor") || force) {
debugString += QString("RGB Color: 0x%1 ").arg(QString::number(m_rgbColor, 16).rightJustified(6, '0'));
}
if (settingsKeys.contains("useReverseAPI") || force) {
debugString += QString("Use Reverse API: %1 ").arg(m_useReverseAPI ? "true" : "false");
}
if (settingsKeys.contains("reverseAPIAddress") || force) {
debugString += QString("Reverse API Address: %1 ").arg(m_reverseAPIAddress);
}
if (settingsKeys.contains("reverseAPIPort") || force) {
debugString += QString("Reverse API Port: %1 ").arg(m_reverseAPIPort);
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) {
debugString += QString("Reverse API Feature Set Index: %1 ").arg(m_reverseAPIFeatureSetIndex);
}
if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
debugString += QString("Reverse API Feature Index: %1 ").arg(m_reverseAPIFeatureIndex);
}
if (settingsKeys.contains("workspaceIndex") || force) {
debugString += QString("Workspace Index: %1 ").arg(m_workspaceIndex);
}
if (settingsKeys.contains("fileRecordName") || force) {
debugString += QString("File Record Name: %1 ").arg(m_fileRecordName);
}
if (settingsKeys.contains("recordToFile") || force) {
debugString += QString("Record To File: %1 ").arg(m_recordToFile ? "true" : "false");
}
return debugString;
}

View File

@ -0,0 +1,62 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_
#define INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <QStringList>
class Serializable;
struct DenoiserSettings
{
enum class DenoiserType
{
DenoiserType_None = 0,
DenoiserType_RNnoise = 1,
};
DenoiserType m_denoiserType;
bool m_audioMute;
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIFeatureSetIndex;
uint16_t m_reverseAPIFeatureIndex;
QString m_fileRecordName;
bool m_recordToFile;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
DenoiserSettings();
~DenoiserSettings() = default;
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void applySettings(const QStringList& settingsKeys, const DenoiserSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
static const QStringList m_channelURIs;
};
#endif // INCLUDE_FEATURE_DENOISER_DENOISERSETTINGS_H_

View File

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGFeatureSettings.h"
#include "denoiser.h"
#include "denoiserwebapiadapter.h"
DenoiserWebAPIAdapter::DenoiserWebAPIAdapter()
{}
DenoiserWebAPIAdapter::~DenoiserWebAPIAdapter()
{}
int DenoiserWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDenoiserSettings(new SWGSDRangel::SWGDenoiserSettings());
response.getDenoiserSettings()->init();
Denoiser::webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int DenoiserWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
Denoiser::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DENOISER_WEBAPIADAPTER_H
#define INCLUDE_DENOISER_WEBAPIADAPTER_H
#include "feature/featurewebapiadapter.h"
#include "denoisersettings.h"
/**
* Standalone API adapter only for the settings
*/
class DenoiserWebAPIAdapter : public FeatureWebAPIAdapter {
public:
DenoiserWebAPIAdapter();
virtual ~DenoiserWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
private:
DenoiserSettings m_settings;
};
#endif // INCLUDE_DENOISER_WEBAPIADAPTER_H

View File

@ -0,0 +1,245 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "dsp/wavfilerecord.h"
#include "denoiserworker.h"
MESSAGE_CLASS_DEFINITION(DenoiserWorker::MsgConfigureDenoiserWorker, Message)
MESSAGE_CLASS_DEFINITION(DenoiserWorker::MsgConnectFifo, Message)
DenoiserWorker::DenoiserWorker(QObject *parent) :
QObject(parent),
m_dataFifo(nullptr),
m_sinkSampleRate(0),
m_msgQueueToFeature(nullptr),
m_magsq(0.0),
m_sampleBufferSize(0),
m_channelPowerAvg(),
m_wavFileRecord(nullptr),
m_nbBytes(0)
{
}
DenoiserWorker::~DenoiserWorker()
{
m_inputMessageQueue.clear();
}
void DenoiserWorker::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
}
void DenoiserWorker::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
m_wavFileRecord = new WavFileRecord(m_sinkSampleRate);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
void DenoiserWorker::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
if (m_wavFileRecord)
{
delete m_wavFileRecord;
m_wavFileRecord = nullptr;
}
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
void DenoiserWorker::feedPart(
const QByteArray::const_iterator& begin,
const QByteArray::const_iterator& end,
DataFifo::DataType dataType
)
{
int nbBytes;
switch(dataType)
{
case DataFifo::DataTypeCI16:
nbBytes = 4;
break;
case DataFifo::DataTypeI16:
default:
nbBytes = 2;
}
if ((nbBytes != m_nbBytes) && m_wavFileRecord)
{
m_wavFileRecord->stopRecording();
m_wavFileRecord->setMono(nbBytes == 2);
}
m_nbBytes = nbBytes;
int countSamples = (end - begin) / nbBytes;
if (countSamples > m_sampleBufferSize)
{
m_sampleBuffer.resize(countSamples);
m_sampleBufferSize = countSamples;
}
for (int i = 0; i < countSamples; i++) {
processSample(dataType, begin, i);
}
if (m_settings.m_recordToFile && m_wavFileRecord)
{
for (int is = 0; is < countSamples; is++)
{
const Sample& sample = m_sampleBuffer[is];
if ((sample.m_real == 0) && (sample.m_imag == 0))
{
if (m_wavFileRecord->isRecording()) {
m_wavFileRecord->stopRecording();
}
}
else
{
if (!m_wavFileRecord->isRecording()) {
m_wavFileRecord->startRecording();
}
writeSampleToFile(sample);
}
}
}
}
void DenoiserWorker::writeSampleToFile(const Sample& sample)
{
if (!m_wavFileRecord) {
return;
}
if (SDR_RX_SAMP_SZ == 16)
{
if (m_nbBytes == 2) {
m_wavFileRecord->writeMono(sample.m_real);
} else {
m_wavFileRecord->write(sample.m_real, sample.m_imag);
}
}
else
{
if (m_nbBytes == 2) {
m_wavFileRecord->writeMono(sample.m_real >> 8);
} else {
m_wavFileRecord->write(sample.m_real >> 8, sample.m_imag >> 8);
}
}
}
void DenoiserWorker::handleInputMessages()
{
Message* message = nullptr;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
const Message& cmd = *message;
handleMessage(cmd);
}
}
bool DenoiserWorker::handleMessage(const Message& cmd)
{
if (MsgConfigureDenoiserWorker::match(cmd))
{
const MsgConfigureDenoiserWorker& conf = static_cast<const MsgConfigureDenoiserWorker&>(cmd);
qDebug("DenoiserWorker::handleMessage: MsgConfigureDenoiserWorker");
applySettings(conf.getSettings(), conf.getSettingsKeys(), conf.getForce());
return true;
}
else if (MsgConnectFifo::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConnectFifo& msg = (MsgConnectFifo&) cmd;
m_dataFifo = msg.getFifo();
bool doConnect = msg.getConnect();
qDebug("DenoiserWorker::handleMessage: MsgConnectFifo: %s", (doConnect ? "connect" : "disconnect"));
if (doConnect) {
QObject::connect(
m_dataFifo,
&DataFifo::dataReady,
this,
&DenoiserWorker::handleData,
Qt::QueuedConnection
);
}
else
{
QObject::disconnect(
m_dataFifo,
&DataFifo::dataReady,
this,
&DenoiserWorker::handleData
);
}
return true;
}
return false;
}
void DenoiserWorker::applySettings(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force)
{
QMutexLocker mutexLocker(&m_mutex);
Q_UNUSED(settingsKeys)
Q_UNUSED(force)
m_settings = settings;
}
void DenoiserWorker::applySampleRate(int sampleRate)
{
QMutexLocker mutexLocker(&m_mutex);
m_sinkSampleRate = sampleRate;
}
void DenoiserWorker::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_dataFifo->fill() > 0) && (m_inputMessageQueue.size() == 0))
{
QByteArray::iterator part1begin;
QByteArray::iterator part1end;
QByteArray::iterator part2begin;
QByteArray::iterator part2end;
DataFifo::DataType dataType;
std::size_t count = m_dataFifo->readBegin(m_dataFifo->fill(), &part1begin, &part1end, &part2begin, &part2end, dataType);
// first part of FIFO data
if (part1begin != part1end) {
feedPart(part1begin, part1end, dataType);
}
// second part of FIFO data (used when block wraps around)
if (part2begin != part2end) {
feedPart(part2begin, part2end, dataType);
}
m_dataFifo->readCommit((unsigned int) count);
}
}

View File

@ -0,0 +1,150 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_
#define INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_
#include <QObject>
#include <QMutex>
#include <QByteArray>
#include "util/movingaverage.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "dsp/dsptypes.h"
#include "dsp/datafifo.h"
#include "denoisersettings.h"
class WavFileRecord;
class DenoiserWorker : public QObject {
Q_OBJECT
public:
class MsgConfigureDenoiserWorker : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DenoiserSettings& getSettings() const { return m_settings; }
const QStringList& getSettingsKeys() const { return m_settingsKeys; }
bool getForce() const { return m_force; }
static MsgConfigureDenoiserWorker* create(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force)
{
return new MsgConfigureDenoiserWorker(settings, settingsKeys, force);
}
private:
DenoiserSettings m_settings;
QStringList m_settingsKeys;
bool m_force;
MsgConfigureDenoiserWorker(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force) :
Message(),
m_settings(settings),
m_settingsKeys(settingsKeys),
m_force(force)
{ }
};
class MsgConnectFifo : public Message {
MESSAGE_CLASS_DECLARATION
public:
DataFifo *getFifo() { return m_fifo; }
bool getConnect() const { return m_connect; }
static MsgConnectFifo* create(DataFifo *fifo, bool connect) {
return new MsgConnectFifo(fifo, connect);
}
private:
DataFifo *m_fifo;
bool m_connect;
MsgConnectFifo(DataFifo *fifo, bool connect) :
Message(),
m_fifo(fifo),
m_connect(connect)
{ }
};
explicit DenoiserWorker(QObject *parent = nullptr);
~DenoiserWorker() override;
void reset();
void startWork();
void stopWork();
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; }
void applySampleRate(int sampleRate);
void applySettings(const DenoiserSettings& settings, const QStringList& settingsKeys, bool force = false);
double getMagSq() const { return m_magsq; }
double getMagSqAvg() const { return (double) m_channelPowerAvg; }
private:
DataFifo *m_dataFifo;
int m_sinkSampleRate;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
DenoiserSettings m_settings;
double m_magsq;
SampleVector m_sampleBuffer;
int m_sampleBufferSize;
MovingAverageUtil<double, double, 480> m_channelPowerAvg;
WavFileRecord* m_wavFileRecord;
int m_nbBytes;
QRecursiveMutex m_mutex;
void feedPart(
const QByteArray::const_iterator& begin,
const QByteArray::const_iterator& end,
DataFifo::DataType dataType
);
bool handleMessage(const Message& cmd);
void writeSampleToFile(const Sample& sample);
inline void processSample(
DataFifo::DataType dataType,
const QByteArray::const_iterator& begin,
int i
)
{
switch(dataType)
{
case DataFifo::DataTypeI16: {
int16_t *s = (int16_t*) begin;
double re = s[i] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(0);
}
break;
case DataFifo::DataTypeCI16: {
int16_t *s = (int16_t*) begin;
double re = s[2*i] / (double) std::numeric_limits<int16_t>::max();
double im = s[2*i+1] / (double) std::numeric_limits<int16_t>::max();
m_magsq = re*re + im*im;
m_channelPowerAvg(m_magsq);
m_sampleBuffer[i].setReal(re * SDR_RX_SCALEF);
m_sampleBuffer[i].setImag(im * SDR_RX_SCALEF);
}
break;
}
}
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_FEATURE_DENOISER_DENOISERWORKER_H_

View File

@ -40,6 +40,7 @@
<file>webapi/doc/swagger/include/DATVDemod.yaml</file>
<file>webapi/doc/swagger/include/DATVMod.yaml</file>
<file>webapi/doc/swagger/include/DemodAnalyzer.yaml</file>
<file>webapi/doc/swagger/include/Denoiser.yaml</file>
<file>webapi/doc/swagger/include/DeviceActions.yaml</file>
<file>webapi/doc/swagger/include/DeviceReports.yaml</file>
<file>webapi/doc/swagger/include/DeviceSettings.yaml</file>

View File

@ -5625,6 +5625,66 @@ margin-bottom: 20px;
}
},
"description" : "DemodAnalyzer"
};
defs.DenoiserActions = {
"required" : [ "channelId", "deviceId" ],
"properties" : {
"deviceId" : {
"type" : "integer",
"description" : "Set source device Id/Number that channel belongs to"
},
"channelId" : {
"type" : "integer",
"description" : "Set source channel Id/Number of the channel within the device"
}
},
"description" : "Denoiser actions"
};
defs.DenoiserSettings = {
"properties" : {
"denoiserType" : {
"type" : "integer",
"description" : "Denoiser type\n * 0 - none\n * 1 - RNnoise\n"
},
"audioMute" : {
"type" : "integer",
"description" : "Audio mute\n * 1 - mute\n * 0 - unmute\n"
},
"title" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
"fileRecordName" : {
"type" : "string",
"description" : "Output wave file name"
},
"recordToFile" : {
"type" : "integer",
"description" : "Recording status\n * 1 - recording\n * 0 - not recording\n"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API\n * 1 - yes\n * 0 - no\n"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIFeatureSetIndex" : {
"type" : "integer"
},
"reverseAPIFeatureIndex" : {
"type" : "integer"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "Denoiser"
};
defs.DeviceActions = {
"required" : [ "deviceHwType", "direction" ],
@ -6518,6 +6578,9 @@ margin-bottom: 20px;
},
"DemodAnalyzerActions" : {
"$ref" : "#/definitions/DemodAnalyzerActions"
},
"DenoiserActions" : {
"$ref" : "#/definitions/DenoiserActions"
}
},
"description" : "Base feature actions. Only the feature actions corresponding to the feature specified in the featureType field is or should be present."
@ -6735,6 +6798,9 @@ margin-bottom: 20px;
"DemodAnalyzerSettings" : {
"$ref" : "#/definitions/DemodAnalyzerSettings"
},
"DenoiserSettings" : {
"$ref" : "#/definitions/DenoiserSettings"
},
"JogdialControllerSettings" : {
"$ref" : "#/definitions/JogdialControllerSettings"
},
@ -59654,7 +59720,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2026-01-04T12:41:15.650+01:00
Generated 2026-01-06T07:31:33.605+01:00
</div>
</div>
</div>

View File

@ -0,0 +1,57 @@
DenoiserSettings:
description: Denoiser
properties:
denoiserType:
type: integer
description: >
Denoiser type
* 0 - none
* 1 - RNnoise
audioMute:
type: integer
description: >
Audio mute
* 1 - mute
* 0 - unmute
title:
type: string
rgbColor:
type: integer
fileRecordName:
type: string
description: Output wave file name
recordToFile:
type: integer
description: >
Recording status
* 1 - recording
* 0 - not recording
useReverseAPI:
type: integer
description: >
Synchronize with reverse API
* 1 - yes
* 0 - no
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIFeatureSetIndex:
type: integer
reverseAPIFeatureIndex:
type: integer
rollupState:
$ref: "/doc/swagger/include/RollupState.yaml#/RollupState"
DenoiserActions:
description: "Denoiser actions"
required:
- deviceId
- channelId
properties:
deviceId:
type: integer
description: "Set source device Id/Number that channel belongs to"
channelId:
type: integer
description: "Set source channel Id/Number of the channel within the device"

View File

@ -41,3 +41,5 @@ FeatureActions:
$ref: "/doc/swagger/include/VORLocalizer.yaml#/VORLocalizerActions"
DemodAnalyzerActions:
$ref: "/doc/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerActions"
DenoiserActions:
$ref: "/doc/swagger/include/Denoiser.yaml#/DenoiserActions"

View File

@ -25,6 +25,8 @@ FeatureSettings:
$ref: "/doc/swagger/include/APRS.yaml#/APRSSettings"
DemodAnalyzerSettings:
$ref: "/doc/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerSettings"
DenoiserSettings:
$ref: "/doc/swagger/include/Denoiser.yaml#/DenoiserSettings"
JogdialControllerSettings:
$ref: "/doc/swagger/include/JogdialController.yaml#/JogdialControllerSettings"
GS232ControllerSettings:

View File

@ -0,0 +1,57 @@
DenoiserSettings:
description: Denoiser
properties:
denoiserType:
type: integer
description: >
Denoiser type
* 0 - none
* 1 - RNnoise
audioMute:
type: integer
description: >
Audio mute
* 1 - mute
* 0 - unmute
title:
type: string
rgbColor:
type: integer
fileRecordName:
type: string
description: Output wave file name
recordToFile:
type: integer
description: >
Recording status
* 1 - recording
* 0 - not recording
useReverseAPI:
type: integer
description: >
Synchronize with reverse API
* 1 - yes
* 0 - no
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIFeatureSetIndex:
type: integer
reverseAPIFeatureIndex:
type: integer
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
DenoiserActions:
description: "Denoiser actions"
required:
- deviceId
- channelId
properties:
deviceId:
type: integer
description: "Set source device Id/Number that channel belongs to"
channelId:
type: integer
description: "Set source channel Id/Number of the channel within the device"

View File

@ -41,3 +41,5 @@ FeatureActions:
$ref: "http://swgserver:8081/api/swagger/include/VORLocalizer.yaml#/VORLocalizerActions"
DemodAnalyzerActions:
$ref: "http://swgserver:8081/api/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerActions"
DenoiserActions:
$ref: "http://swgserver:8081/api/swagger/include/Denoiser.yaml#/DenoiserActions"

View File

@ -25,6 +25,8 @@ FeatureSettings:
$ref: "http://swgserver:8081/api/swagger/include/APRS.yaml#/APRSSettings"
DemodAnalyzerSettings:
$ref: "http://swgserver:8081/api/swagger/include/DemodAnalyzer.yaml#/DemodAnalyzerSettings"
DenoiserSettings:
$ref: "http://swgserver:8081/api/swagger/include/Denoiser.yaml#/DenoiserSettings"
JogdialControllerSettings:
$ref: "http://swgserver:8081/api/swagger/include/JogdialController.yaml#/JogdialControllerSettings"
GS232ControllerSettings:

View File

@ -5625,6 +5625,66 @@ margin-bottom: 20px;
}
},
"description" : "DemodAnalyzer"
};
defs.DenoiserActions = {
"required" : [ "channelId", "deviceId" ],
"properties" : {
"deviceId" : {
"type" : "integer",
"description" : "Set source device Id/Number that channel belongs to"
},
"channelId" : {
"type" : "integer",
"description" : "Set source channel Id/Number of the channel within the device"
}
},
"description" : "Denoiser actions"
};
defs.DenoiserSettings = {
"properties" : {
"denoiserType" : {
"type" : "integer",
"description" : "Denoiser type\n * 0 - none\n * 1 - RNnoise\n"
},
"audioMute" : {
"type" : "integer",
"description" : "Audio mute\n * 1 - mute\n * 0 - unmute\n"
},
"title" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
"fileRecordName" : {
"type" : "string",
"description" : "Output wave file name"
},
"recordToFile" : {
"type" : "integer",
"description" : "Recording status\n * 1 - recording\n * 0 - not recording\n"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API\n * 1 - yes\n * 0 - no\n"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIFeatureSetIndex" : {
"type" : "integer"
},
"reverseAPIFeatureIndex" : {
"type" : "integer"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "Denoiser"
};
defs.DeviceActions = {
"required" : [ "deviceHwType", "direction" ],
@ -6518,6 +6578,9 @@ margin-bottom: 20px;
},
"DemodAnalyzerActions" : {
"$ref" : "#/definitions/DemodAnalyzerActions"
},
"DenoiserActions" : {
"$ref" : "#/definitions/DenoiserActions"
}
},
"description" : "Base feature actions. Only the feature actions corresponding to the feature specified in the featureType field is or should be present."
@ -6735,6 +6798,9 @@ margin-bottom: 20px;
"DemodAnalyzerSettings" : {
"$ref" : "#/definitions/DemodAnalyzerSettings"
},
"DenoiserSettings" : {
"$ref" : "#/definitions/DenoiserSettings"
},
"JogdialControllerSettings" : {
"$ref" : "#/definitions/JogdialControllerSettings"
},
@ -59654,7 +59720,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2026-01-04T12:41:15.650+01:00
Generated 2026-01-06T07:31:33.605+01:00
</div>
</div>
</div>

View File

@ -0,0 +1,131 @@
/**
* 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: 7.0.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 "SWGDenoiserActions.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDenoiserActions::SWGDenoiserActions(QString* json) {
init();
this->fromJson(*json);
}
SWGDenoiserActions::SWGDenoiserActions() {
device_id = 0;
m_device_id_isSet = false;
channel_id = 0;
m_channel_id_isSet = false;
}
SWGDenoiserActions::~SWGDenoiserActions() {
this->cleanup();
}
void
SWGDenoiserActions::init() {
device_id = 0;
m_device_id_isSet = false;
channel_id = 0;
m_channel_id_isSet = false;
}
void
SWGDenoiserActions::cleanup() {
}
SWGDenoiserActions*
SWGDenoiserActions::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDenoiserActions::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&device_id, pJson["deviceId"], "qint32", "");
::SWGSDRangel::setValue(&channel_id, pJson["channelId"], "qint32", "");
}
QString
SWGDenoiserActions::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDenoiserActions::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_device_id_isSet){
obj->insert("deviceId", QJsonValue(device_id));
}
if(m_channel_id_isSet){
obj->insert("channelId", QJsonValue(channel_id));
}
return obj;
}
qint32
SWGDenoiserActions::getDeviceId() {
return device_id;
}
void
SWGDenoiserActions::setDeviceId(qint32 device_id) {
this->device_id = device_id;
this->m_device_id_isSet = true;
}
qint32
SWGDenoiserActions::getChannelId() {
return channel_id;
}
void
SWGDenoiserActions::setChannelId(qint32 channel_id) {
this->channel_id = channel_id;
this->m_channel_id_isSet = true;
}
bool
SWGDenoiserActions::isSet(){
bool isObjectUpdated = false;
do{
if(m_device_id_isSet){
isObjectUpdated = true; break;
}
if(m_channel_id_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,64 @@
/**
* 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: 7.0.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.
*/
/*
* SWGDenoiserActions.h
*
* Denoiser actions
*/
#ifndef SWGDenoiserActions_H_
#define SWGDenoiserActions_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDenoiserActions: public SWGObject {
public:
SWGDenoiserActions();
SWGDenoiserActions(QString* json);
virtual ~SWGDenoiserActions();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDenoiserActions* fromJson(QString &jsonString) override;
qint32 getDeviceId();
void setDeviceId(qint32 device_id);
qint32 getChannelId();
void setChannelId(qint32 channel_id);
virtual bool isSet() override;
private:
qint32 device_id;
bool m_device_id_isSet;
qint32 channel_id;
bool m_channel_id_isSet;
};
}
#endif /* SWGDenoiserActions_H_ */

View File

@ -0,0 +1,369 @@
/**
* 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: 7.0.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 "SWGDenoiserSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDenoiserSettings::SWGDenoiserSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGDenoiserSettings::SWGDenoiserSettings() {
denoiser_type = 0;
m_denoiser_type_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
title = nullptr;
m_title_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
file_record_name = nullptr;
m_file_record_name_isSet = false;
record_to_file = 0;
m_record_to_file_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_feature_set_index = 0;
m_reverse_api_feature_set_index_isSet = false;
reverse_api_feature_index = 0;
m_reverse_api_feature_index_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGDenoiserSettings::~SWGDenoiserSettings() {
this->cleanup();
}
void
SWGDenoiserSettings::init() {
denoiser_type = 0;
m_denoiser_type_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
title = new QString("");
m_title_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
file_record_name = new QString("");
m_file_record_name_isSet = false;
record_to_file = 0;
m_record_to_file_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_feature_set_index = 0;
m_reverse_api_feature_set_index_isSet = false;
reverse_api_feature_index = 0;
m_reverse_api_feature_index_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGDenoiserSettings::cleanup() {
if(title != nullptr) {
delete title;
}
if(file_record_name != nullptr) {
delete file_record_name;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGDenoiserSettings*
SWGDenoiserSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDenoiserSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&denoiser_type, pJson["denoiserType"], "qint32", "");
::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&file_record_name, pJson["fileRecordName"], "QString", "QString");
::SWGSDRangel::setValue(&record_to_file, pJson["recordToFile"], "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_feature_set_index, pJson["reverseAPIFeatureSetIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_feature_index, pJson["reverseAPIFeatureIndex"], "qint32", "");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGDenoiserSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDenoiserSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_denoiser_type_isSet){
obj->insert("denoiserType", QJsonValue(denoiser_type));
}
if(m_audio_mute_isSet){
obj->insert("audioMute", QJsonValue(audio_mute));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(file_record_name != nullptr && *file_record_name != QString("")){
toJsonValue(QString("fileRecordName"), file_record_name, obj, QString("QString"));
}
if(m_record_to_file_isSet){
obj->insert("recordToFile", QJsonValue(record_to_file));
}
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_feature_set_index_isSet){
obj->insert("reverseAPIFeatureSetIndex", QJsonValue(reverse_api_feature_set_index));
}
if(m_reverse_api_feature_index_isSet){
obj->insert("reverseAPIFeatureIndex", QJsonValue(reverse_api_feature_index));
}
if((rollup_state != nullptr) && (rollup_state->isSet())){
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
}
return obj;
}
qint32
SWGDenoiserSettings::getDenoiserType() {
return denoiser_type;
}
void
SWGDenoiserSettings::setDenoiserType(qint32 denoiser_type) {
this->denoiser_type = denoiser_type;
this->m_denoiser_type_isSet = true;
}
qint32
SWGDenoiserSettings::getAudioMute() {
return audio_mute;
}
void
SWGDenoiserSettings::setAudioMute(qint32 audio_mute) {
this->audio_mute = audio_mute;
this->m_audio_mute_isSet = true;
}
QString*
SWGDenoiserSettings::getTitle() {
return title;
}
void
SWGDenoiserSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGDenoiserSettings::getRgbColor() {
return rgb_color;
}
void
SWGDenoiserSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGDenoiserSettings::getFileRecordName() {
return file_record_name;
}
void
SWGDenoiserSettings::setFileRecordName(QString* file_record_name) {
this->file_record_name = file_record_name;
this->m_file_record_name_isSet = true;
}
qint32
SWGDenoiserSettings::getRecordToFile() {
return record_to_file;
}
void
SWGDenoiserSettings::setRecordToFile(qint32 record_to_file) {
this->record_to_file = record_to_file;
this->m_record_to_file_isSet = true;
}
qint32
SWGDenoiserSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGDenoiserSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGDenoiserSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGDenoiserSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGDenoiserSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGDenoiserSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGDenoiserSettings::getReverseApiFeatureSetIndex() {
return reverse_api_feature_set_index;
}
void
SWGDenoiserSettings::setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index) {
this->reverse_api_feature_set_index = reverse_api_feature_set_index;
this->m_reverse_api_feature_set_index_isSet = true;
}
qint32
SWGDenoiserSettings::getReverseApiFeatureIndex() {
return reverse_api_feature_index;
}
void
SWGDenoiserSettings::setReverseApiFeatureIndex(qint32 reverse_api_feature_index) {
this->reverse_api_feature_index = reverse_api_feature_index;
this->m_reverse_api_feature_index_isSet = true;
}
SWGRollupState*
SWGDenoiserSettings::getRollupState() {
return rollup_state;
}
void
SWGDenoiserSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGDenoiserSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_denoiser_type_isSet){
isObjectUpdated = true; break;
}
if(m_audio_mute_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(file_record_name && *file_record_name != QString("")){
isObjectUpdated = true; break;
}
if(m_record_to_file_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_feature_set_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_feature_index_isSet){
isObjectUpdated = true; break;
}
if(rollup_state && rollup_state->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,126 @@
/**
* 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: 7.0.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.
*/
/*
* SWGDenoiserSettings.h
*
* Denoiser
*/
#ifndef SWGDenoiserSettings_H_
#define SWGDenoiserSettings_H_
#include <QJsonObject>
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDenoiserSettings: public SWGObject {
public:
SWGDenoiserSettings();
SWGDenoiserSettings(QString* json);
virtual ~SWGDenoiserSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDenoiserSettings* fromJson(QString &jsonString) override;
qint32 getDenoiserType();
void setDenoiserType(qint32 denoiser_type);
qint32 getAudioMute();
void setAudioMute(qint32 audio_mute);
QString* getTitle();
void setTitle(QString* title);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getFileRecordName();
void setFileRecordName(QString* file_record_name);
qint32 getRecordToFile();
void setRecordToFile(qint32 record_to_file);
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 getReverseApiFeatureSetIndex();
void setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index);
qint32 getReverseApiFeatureIndex();
void setReverseApiFeatureIndex(qint32 reverse_api_feature_index);
SWGRollupState* getRollupState();
void setRollupState(SWGRollupState* rollup_state);
virtual bool isSet() override;
private:
qint32 denoiser_type;
bool m_denoiser_type_isSet;
qint32 audio_mute;
bool m_audio_mute_isSet;
QString* title;
bool m_title_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* file_record_name;
bool m_file_record_name_isSet;
qint32 record_to_file;
bool m_record_to_file_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_feature_set_index;
bool m_reverse_api_feature_set_index_isSet;
qint32 reverse_api_feature_index;
bool m_reverse_api_feature_index_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGDenoiserSettings_H_ */

View File

@ -62,6 +62,8 @@ SWGFeatureActions::SWGFeatureActions() {
m_vor_localizer_actions_isSet = false;
demod_analyzer_actions = nullptr;
m_demod_analyzer_actions_isSet = false;
denoiser_actions = nullptr;
m_denoiser_actions_isSet = false;
}
SWGFeatureActions::~SWGFeatureActions() {
@ -104,6 +106,8 @@ SWGFeatureActions::init() {
m_vor_localizer_actions_isSet = false;
demod_analyzer_actions = new SWGDemodAnalyzerActions();
m_demod_analyzer_actions_isSet = false;
denoiser_actions = new SWGDenoiserActions();
m_denoiser_actions_isSet = false;
}
void
@ -155,6 +159,9 @@ SWGFeatureActions::cleanup() {
if(demod_analyzer_actions != nullptr) {
delete demod_analyzer_actions;
}
if(denoiser_actions != nullptr) {
delete denoiser_actions;
}
}
SWGFeatureActions*
@ -202,6 +209,8 @@ SWGFeatureActions::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&demod_analyzer_actions, pJson["DemodAnalyzerActions"], "SWGDemodAnalyzerActions", "SWGDemodAnalyzerActions");
::SWGSDRangel::setValue(&denoiser_actions, pJson["DenoiserActions"], "SWGDenoiserActions", "SWGDenoiserActions");
}
QString
@ -269,6 +278,9 @@ SWGFeatureActions::asJsonObject() {
if((demod_analyzer_actions != nullptr) && (demod_analyzer_actions->isSet())){
toJsonValue(QString("DemodAnalyzerActions"), demod_analyzer_actions, obj, QString("SWGDemodAnalyzerActions"));
}
if((denoiser_actions != nullptr) && (denoiser_actions->isSet())){
toJsonValue(QString("DenoiserActions"), denoiser_actions, obj, QString("SWGDenoiserActions"));
}
return obj;
}
@ -443,6 +455,16 @@ SWGFeatureActions::setDemodAnalyzerActions(SWGDemodAnalyzerActions* demod_analyz
this->m_demod_analyzer_actions_isSet = true;
}
SWGDenoiserActions*
SWGFeatureActions::getDenoiserActions() {
return denoiser_actions;
}
void
SWGFeatureActions::setDenoiserActions(SWGDenoiserActions* denoiser_actions) {
this->denoiser_actions = denoiser_actions;
this->m_denoiser_actions_isSet = true;
}
bool
SWGFeatureActions::isSet(){
@ -499,6 +521,9 @@ SWGFeatureActions::isSet(){
if(demod_analyzer_actions && demod_analyzer_actions->isSet()){
isObjectUpdated = true; break;
}
if(denoiser_actions && denoiser_actions->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}

View File

@ -25,6 +25,7 @@
#include "SWGAFCActions.h"
#include "SWGAMBEActions.h"
#include "SWGDemodAnalyzerActions.h"
#include "SWGDenoiserActions.h"
#include "SWGGS232ControllerActions.h"
#include "SWGLimeRFEActions.h"
#include "SWGMapActions.h"
@ -107,6 +108,9 @@ public:
SWGDemodAnalyzerActions* getDemodAnalyzerActions();
void setDemodAnalyzerActions(SWGDemodAnalyzerActions* demod_analyzer_actions);
SWGDenoiserActions* getDenoiserActions();
void setDenoiserActions(SWGDenoiserActions* denoiser_actions);
virtual bool isSet() override;
@ -162,6 +166,9 @@ private:
SWGDemodAnalyzerActions* demod_analyzer_actions;
bool m_demod_analyzer_actions_isSet;
SWGDenoiserActions* denoiser_actions;
bool m_denoiser_actions_isSet;
};
}

View File

@ -46,6 +46,8 @@ SWGFeatureSettings::SWGFeatureSettings() {
m_aprs_settings_isSet = false;
demod_analyzer_settings = nullptr;
m_demod_analyzer_settings_isSet = false;
denoiser_settings = nullptr;
m_denoiser_settings_isSet = false;
jogdial_controller_settings = nullptr;
m_jogdial_controller_settings_isSet = false;
gs232_controller_settings = nullptr;
@ -100,6 +102,8 @@ SWGFeatureSettings::init() {
m_aprs_settings_isSet = false;
demod_analyzer_settings = new SWGDemodAnalyzerSettings();
m_demod_analyzer_settings_isSet = false;
denoiser_settings = new SWGDenoiserSettings();
m_denoiser_settings_isSet = false;
jogdial_controller_settings = new SWGJogdialControllerSettings();
m_jogdial_controller_settings_isSet = false;
gs232_controller_settings = new SWGGS232ControllerSettings();
@ -155,6 +159,9 @@ SWGFeatureSettings::cleanup() {
if(demod_analyzer_settings != nullptr) {
delete demod_analyzer_settings;
}
if(denoiser_settings != nullptr) {
delete denoiser_settings;
}
if(jogdial_controller_settings != nullptr) {
delete jogdial_controller_settings;
}
@ -228,6 +235,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&demod_analyzer_settings, pJson["DemodAnalyzerSettings"], "SWGDemodAnalyzerSettings", "SWGDemodAnalyzerSettings");
::SWGSDRangel::setValue(&denoiser_settings, pJson["DenoiserSettings"], "SWGDenoiserSettings", "SWGDenoiserSettings");
::SWGSDRangel::setValue(&jogdial_controller_settings, pJson["JogdialControllerSettings"], "SWGJogdialControllerSettings", "SWGJogdialControllerSettings");
::SWGSDRangel::setValue(&gs232_controller_settings, pJson["GS232ControllerSettings"], "SWGGS232ControllerSettings", "SWGGS232ControllerSettings");
@ -299,6 +308,9 @@ SWGFeatureSettings::asJsonObject() {
if((demod_analyzer_settings != nullptr) && (demod_analyzer_settings->isSet())){
toJsonValue(QString("DemodAnalyzerSettings"), demod_analyzer_settings, obj, QString("SWGDemodAnalyzerSettings"));
}
if((denoiser_settings != nullptr) && (denoiser_settings->isSet())){
toJsonValue(QString("DenoiserSettings"), denoiser_settings, obj, QString("SWGDenoiserSettings"));
}
if((jogdial_controller_settings != nullptr) && (jogdial_controller_settings->isSet())){
toJsonValue(QString("JogdialControllerSettings"), jogdial_controller_settings, obj, QString("SWGJogdialControllerSettings"));
}
@ -435,6 +447,16 @@ SWGFeatureSettings::setDemodAnalyzerSettings(SWGDemodAnalyzerSettings* demod_ana
this->m_demod_analyzer_settings_isSet = true;
}
SWGDenoiserSettings*
SWGFeatureSettings::getDenoiserSettings() {
return denoiser_settings;
}
void
SWGFeatureSettings::setDenoiserSettings(SWGDenoiserSettings* denoiser_settings) {
this->denoiser_settings = denoiser_settings;
this->m_denoiser_settings_isSet = true;
}
SWGJogdialControllerSettings*
SWGFeatureSettings::getJogdialControllerSettings() {
return jogdial_controller_settings;
@ -607,6 +629,9 @@ SWGFeatureSettings::isSet(){
if(demod_analyzer_settings && demod_analyzer_settings->isSet()){
isObjectUpdated = true; break;
}
if(denoiser_settings && denoiser_settings->isSet()){
isObjectUpdated = true; break;
}
if(jogdial_controller_settings && jogdial_controller_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -28,6 +28,7 @@
#include "SWGAPRSSettings.h"
#include "SWGAntennaToolsSettings.h"
#include "SWGDemodAnalyzerSettings.h"
#include "SWGDenoiserSettings.h"
#include "SWGGS232ControllerSettings.h"
#include "SWGJogdialControllerSettings.h"
#include "SWGLimeRFESettings.h"
@ -89,6 +90,9 @@ public:
SWGDemodAnalyzerSettings* getDemodAnalyzerSettings();
void setDemodAnalyzerSettings(SWGDemodAnalyzerSettings* demod_analyzer_settings);
SWGDenoiserSettings* getDenoiserSettings();
void setDenoiserSettings(SWGDenoiserSettings* denoiser_settings);
SWGJogdialControllerSettings* getJogdialControllerSettings();
void setJogdialControllerSettings(SWGJogdialControllerSettings* jogdial_controller_settings);
@ -162,6 +166,9 @@ private:
SWGDemodAnalyzerSettings* demod_analyzer_settings;
bool m_demod_analyzer_settings_isSet;
SWGDenoiserSettings* denoiser_settings;
bool m_denoiser_settings_isSet;
SWGJogdialControllerSettings* jogdial_controller_settings;
bool m_jogdial_controller_settings_isSet;

View File

@ -115,6 +115,8 @@
#include "SWGDVSerialDevices.h"
#include "SWGDemodAnalyzerActions.h"
#include "SWGDemodAnalyzerSettings.h"
#include "SWGDenoiserActions.h"
#include "SWGDenoiserSettings.h"
#include "SWGDeviceActions.h"
#include "SWGDeviceConfig.h"
#include "SWGDeviceListItem.h"
@ -897,6 +899,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGDenoiserActions").compare(type) == 0) {
SWGDenoiserActions *obj = new SWGDenoiserActions();
obj->init();
return obj;
}
if(QString("SWGDenoiserSettings").compare(type) == 0) {
SWGDenoiserSettings *obj = new SWGDenoiserSettings();
obj->init();
return obj;
}
if(QString("SWGDeviceActions").compare(type) == 0) {
SWGDeviceActions *obj = new SWGDeviceActions();
obj->init();