kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			Morse Decoder: initial commit
							rodzic
							
								
									0714e5dba5
								
							
						
					
					
						commit
						4387f54f0d
					
				| 
						 | 
				
			
			@ -146,6 +146,7 @@ option(ENABLE_FEATURE_GS232CONTROLLER "Enable feature gs232controller plugin" ON
 | 
			
		|||
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)
 | 
			
		||||
 | 
			
		||||
# on windows always build external libraries
 | 
			
		||||
if(WIN32)
 | 
			
		||||
| 
						 | 
				
			
			@ -779,6 +780,7 @@ if (NOT ENABLE_EXTERNAL_LIBRARIES OR (ENABLE_EXTERNAL_LIBRARIES STREQUAL "AUTO")
 | 
			
		|||
    find_package(LibDAB)
 | 
			
		||||
    find_package(HIDAPI)
 | 
			
		||||
    find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
 | 
			
		||||
    find_package(GGMorse)
 | 
			
		||||
 | 
			
		||||
    # Devices
 | 
			
		||||
    if(ENABLE_AIRSPY)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@
 | 
			
		|||
        "SOAPYSDR_DIR": "/opt/install/SoapySDR",
 | 
			
		||||
        "UHD_DIR": "/opt/install/uhd",
 | 
			
		||||
        "XTRX_DIR": "/opt/install/xtrx-images",
 | 
			
		||||
        "GGMORSE_DIR": "/opt/install/ggmorse",
 | 
			
		||||
        "CMAKE_INSTALL_PREFIX": "/opt/install/sdrangel"
 | 
			
		||||
      },
 | 
			
		||||
      "warnings": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
if (NOT GGMORSE_FOUND)
 | 
			
		||||
    INCLUDE(FindPkgConfig)
 | 
			
		||||
    PKG_CHECK_MODULES(PC_GGMorse "libggmorse")
 | 
			
		||||
 | 
			
		||||
    FIND_PATH(GGMORSE_INCLUDE_DIR
 | 
			
		||||
        NAMES ggmorse/ggmorse.h
 | 
			
		||||
        HINTS ${GGMORSE_DIR}/include
 | 
			
		||||
            ${PC_GGMORSE_INCLUDE_DIR}
 | 
			
		||||
            ${CMAKE_INSTALL_PREFIX}/include
 | 
			
		||||
        PATHS /usr/local/include
 | 
			
		||||
            /usr/include
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    FIND_LIBRARY(GGMORSE_LIBRARIES
 | 
			
		||||
        NAMES ggmorse libggmorse
 | 
			
		||||
        HINTS ${GGMORSE_DIR}/lib
 | 
			
		||||
            ${GGMORSE_DIR}/lib64
 | 
			
		||||
            ${PC_GGMORSE_LIBDIR}
 | 
			
		||||
            ${CMAKE_INSTALL_PREFIX}/lib
 | 
			
		||||
            ${CMAKE_INSTALL_PREFIX}/lib64
 | 
			
		||||
        PATHS /usr/local/lib
 | 
			
		||||
            /usr/local/lib64
 | 
			
		||||
            /usr/lib
 | 
			
		||||
            /usr/lib64
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (GGMORSE_INCLUDE_DIR AND GGMORSE_LIBRARIES)
 | 
			
		||||
        set(GGMORSE_FOUND TRUE CACHE INTERNAL "GGMorse found")
 | 
			
		||||
        message(STATUS "Found GGMorse: ${GGMORSE_INCLUDE_DIR}, ${GGMORSE_LIBRARIES}")
 | 
			
		||||
    else (GGMORSE_INCLUDE_DIR AND GGMORSE_LIBRARIES)
 | 
			
		||||
        set(GGMORSE_FOUND FALSE CACHE INTERNAL "GGMorse found")
 | 
			
		||||
        message(STATUS "GGMorse not found")
 | 
			
		||||
    endif (GGMORSE_INCLUDE_DIR AND GGMORSE_LIBRARIES)
 | 
			
		||||
 | 
			
		||||
    INCLUDE(FindPackageHandleStandardArgs)
 | 
			
		||||
    FIND_PACKAGE_HANDLE_STANDARD_ARGS(GGMORSE DEFAULT_MSG GGMORSE_LIBRARIES GGMORSE_INCLUDE_DIR)
 | 
			
		||||
    MARK_AS_ADVANCED(GGMORSE_LIBRARIES GGMORSE_INCLUDE_DIR)
 | 
			
		||||
endif (NOT GGMORSE_FOUND)
 | 
			
		||||
| 
						 | 
				
			
			@ -124,5 +124,11 @@ endif()
 | 
			
		|||
if (ENABLE_FEATURE_SID)
 | 
			
		||||
    add_subdirectory(sid)
 | 
			
		||||
else()
 | 
			
		||||
    message(STATUS "Not building SID (ENABLED_FEATURE_SID=${ENABLED_FEATURE_SID})")
 | 
			
		||||
    message(STATUS "Not building SID (ENABLE_FEATURE_SID=${ENABLE_FEATURE_SID})")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (ENABLE_FEATURE_MORSEDECODER AND GGMORSE_FOUND)
 | 
			
		||||
    add_subdirectory(morsedecoder)
 | 
			
		||||
else()
 | 
			
		||||
    message(STATUS "Not building morsedecoder (ENABLE_FEATURE_MORSEDECODER=${ENABLE_FEATURE_MORSEDECODER} GGMODSE_FOUND=${GGMORSE_FOUND})")
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
project(morsedecoder)
 | 
			
		||||
 | 
			
		||||
set(morsedecoder_SOURCES
 | 
			
		||||
	morsedecoder.cpp
 | 
			
		||||
    morsedecodersettings.cpp
 | 
			
		||||
    morsedecoderplugin.cpp
 | 
			
		||||
    morsedecoderworker.cpp
 | 
			
		||||
    morsedecoderwebapiadapter.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(morsedecoder_HEADERS
 | 
			
		||||
	morsedecoder.h
 | 
			
		||||
    morsedecodersettings.h
 | 
			
		||||
    morsedecoderplugin.h
 | 
			
		||||
    morsedecoderworker.h
 | 
			
		||||
    morsedecoderwebapiadapter.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
include_directories(
 | 
			
		||||
    ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if(NOT SERVER_MODE)
 | 
			
		||||
    set(morsedecoder_SOURCES
 | 
			
		||||
        ${morsedecoder_SOURCES}
 | 
			
		||||
        morsedecodergui.cpp
 | 
			
		||||
        morsedecodergui.ui
 | 
			
		||||
    )
 | 
			
		||||
    set(morsedecoder_HEADERS
 | 
			
		||||
        ${morsedecoder_HEADERS}
 | 
			
		||||
        morsedecodergui.h
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    set(TARGET_NAME featuremorsedecoder)
 | 
			
		||||
    set(TARGET_LIB "Qt::Widgets")
 | 
			
		||||
    set(TARGET_LIB_GUI "sdrgui")
 | 
			
		||||
    set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
 | 
			
		||||
else()
 | 
			
		||||
    set(TARGET_NAME featuremorsedecodersrv)
 | 
			
		||||
    set(TARGET_LIB "")
 | 
			
		||||
    set(TARGET_LIB_GUI "")
 | 
			
		||||
    set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_library(${TARGET_NAME} SHARED
 | 
			
		||||
	${morsedecoder_SOURCES}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(${TARGET_NAME}
 | 
			
		||||
    Qt::Core
 | 
			
		||||
    ${TARGET_LIB}
 | 
			
		||||
	sdrbase
 | 
			
		||||
	${TARGET_LIB_GUI}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,611 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 "channel/channelapi.h"
 | 
			
		||||
#include "maincore.h"
 | 
			
		||||
#include "settings/serializable.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecoderworker.h"
 | 
			
		||||
#include "morsedecoder.h"
 | 
			
		||||
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgConfigureMorseDecoder, Message)
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgStartStop, Message)
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgReportChannels, Message)
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgSelectChannel, Message)
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgReportSampleRate, Message)
 | 
			
		||||
 | 
			
		||||
const char* const MorseDecoder::m_featureIdURI = "sdrangel.feature.morsedecoder";
 | 
			
		||||
const char* const MorseDecoder::m_featureId = "MorseDecoder";
 | 
			
		||||
 | 
			
		||||
MorseDecoder::MorseDecoder(WebAPIAdapterInterface *webAPIAdapterInterface) :
 | 
			
		||||
    Feature(m_featureIdURI, webAPIAdapterInterface),
 | 
			
		||||
    m_thread(nullptr),
 | 
			
		||||
    m_running(false),
 | 
			
		||||
    m_worker(nullptr),
 | 
			
		||||
    m_availableChannelOrFeatureHandler(MorseDecoderSettings::m_channelURIs),
 | 
			
		||||
    m_selectedChannel(nullptr),
 | 
			
		||||
    m_dataPipe(nullptr)
 | 
			
		||||
{
 | 
			
		||||
    qDebug("DemodAnalyzer::DemodAnalyzer: webAPIAdapterInterface: %p", webAPIAdapterInterface);
 | 
			
		||||
    setObjectName(m_featureId);
 | 
			
		||||
    m_state = StIdle;
 | 
			
		||||
    m_errorMessage = "DemodAnalyzer error";
 | 
			
		||||
    m_networkManager = new QNetworkAccessManager();
 | 
			
		||||
    QObject::connect(
 | 
			
		||||
        m_networkManager,
 | 
			
		||||
        &QNetworkAccessManager::finished,
 | 
			
		||||
        this,
 | 
			
		||||
        &MorseDecoder::networkManagerFinished
 | 
			
		||||
    );
 | 
			
		||||
    QObject::connect(
 | 
			
		||||
        &m_availableChannelOrFeatureHandler,
 | 
			
		||||
        &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
 | 
			
		||||
        this,
 | 
			
		||||
        &MorseDecoder::channelsOrFeaturesChanged
 | 
			
		||||
    );
 | 
			
		||||
    m_availableChannelOrFeatureHandler.scanAvailableChannelsAndFeatures();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MorseDecoder::~MorseDecoder()
 | 
			
		||||
{
 | 
			
		||||
    QObject::disconnect(
 | 
			
		||||
        &m_availableChannelOrFeatureHandler,
 | 
			
		||||
        &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
 | 
			
		||||
        this,
 | 
			
		||||
        &MorseDecoder::channelsOrFeaturesChanged
 | 
			
		||||
    );
 | 
			
		||||
    QObject::disconnect(
 | 
			
		||||
        m_networkManager,
 | 
			
		||||
        &QNetworkAccessManager::finished,
 | 
			
		||||
        this,
 | 
			
		||||
        &MorseDecoder::networkManagerFinished
 | 
			
		||||
    );
 | 
			
		||||
    delete m_networkManager;
 | 
			
		||||
    stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::start()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker m_lock(&m_mutex);
 | 
			
		||||
 | 
			
		||||
    if (m_running) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	qDebug("MorseDecoder::start");
 | 
			
		||||
    m_thread = new QThread();
 | 
			
		||||
    m_worker = new MorseDecoderWorker();
 | 
			
		||||
    m_worker->moveToThread(m_thread);
 | 
			
		||||
 | 
			
		||||
    QObject::connect(
 | 
			
		||||
        m_thread,
 | 
			
		||||
        &QThread::started,
 | 
			
		||||
        m_worker,
 | 
			
		||||
        &MorseDecoderWorker::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();
 | 
			
		||||
 | 
			
		||||
    MorseDecoderWorker::MsgConfigureMorseDecoderWorker *msg
 | 
			
		||||
        = MorseDecoderWorker::MsgConfigureMorseDecoderWorker::create(m_settings, QList<QString>(), true);
 | 
			
		||||
    m_worker->getInputMessageQueue()->push(msg);
 | 
			
		||||
 | 
			
		||||
    if (m_dataPipe)
 | 
			
		||||
    {
 | 
			
		||||
        DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
 | 
			
		||||
 | 
			
		||||
        if (fifo)
 | 
			
		||||
        {
 | 
			
		||||
            MorseDecoderWorker::MsgConnectFifo *msg = MorseDecoderWorker::MsgConnectFifo::create(fifo, true);
 | 
			
		||||
            m_worker->getInputMessageQueue()->push(msg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_running = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::stop()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker m_lock(&m_mutex);
 | 
			
		||||
 | 
			
		||||
    if (!m_running) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qDebug("MorseDecoder::stop");
 | 
			
		||||
    m_running = false;
 | 
			
		||||
 | 
			
		||||
    if (m_dataPipe)
 | 
			
		||||
    {
 | 
			
		||||
        DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
 | 
			
		||||
 | 
			
		||||
        if (fifo)
 | 
			
		||||
        {
 | 
			
		||||
            MorseDecoderWorker::MsgConnectFifo *msg = MorseDecoderWorker::MsgConnectFifo::create(fifo, false);
 | 
			
		||||
            m_worker->getInputMessageQueue()->push(msg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	m_worker->stopWork();
 | 
			
		||||
    m_state = StIdle;
 | 
			
		||||
	m_thread->quit();
 | 
			
		||||
	m_thread->wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MorseDecoder::handleMessage(const Message& cmd)
 | 
			
		||||
{
 | 
			
		||||
	if (MsgConfigureMorseDecoder::match(cmd))
 | 
			
		||||
	{
 | 
			
		||||
        MsgConfigureMorseDecoder& cfg = (MsgConfigureMorseDecoder&) cmd;
 | 
			
		||||
        qDebug() << "MorseDecoder::handleMessage: MsgConfigureDemodAnalyzer";
 | 
			
		||||
        applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
    else if (MsgStartStop::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        MsgStartStop& cfg = (MsgStartStop&) cmd;
 | 
			
		||||
        qDebug() << "MorseDecoder::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("MorseDecoder::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() << "MorseDecoder::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 MorseDecoder::serialize() const
 | 
			
		||||
{
 | 
			
		||||
    return m_settings.serialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MorseDecoder::deserialize(const QByteArray& data)
 | 
			
		||||
{
 | 
			
		||||
    if (m_settings.deserialize(data))
 | 
			
		||||
    {
 | 
			
		||||
        MsgConfigureMorseDecoder *msg = MsgConfigureMorseDecoder::create(m_settings, QList<QString>(), true);
 | 
			
		||||
        m_inputMessageQueue.push(msg);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        m_settings.resetToDefaults();
 | 
			
		||||
        MsgConfigureMorseDecoder *msg = MsgConfigureMorseDecoder::create(m_settings, QList<QString>(), true);
 | 
			
		||||
        m_inputMessageQueue.push(msg);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::applySettings(const MorseDecoderSettings& settings, const QList<QString>& settingsKeys, bool force)
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "MorseDecoder::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
 | 
			
		||||
 | 
			
		||||
    if (m_running)
 | 
			
		||||
    {
 | 
			
		||||
        MorseDecoderWorker::MsgConfigureMorseDecoderWorker *msg = MorseDecoderWorker::MsgConfigureMorseDecoderWorker::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 MorseDecoder::channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo)
 | 
			
		||||
{
 | 
			
		||||
    m_availableChannels = m_availableChannelOrFeatureHandler.getAvailableChannelOrFeatureList();
 | 
			
		||||
    notifyUpdate(renameFrom, renameTo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo)
 | 
			
		||||
{
 | 
			
		||||
    if (getMessageQueueToGUI())
 | 
			
		||||
    {
 | 
			
		||||
        MsgReportChannels *msg = MsgReportChannels::create(renameFrom, renameTo);
 | 
			
		||||
        msg->getAvailableChannels() = m_availableChannels;
 | 
			
		||||
        getMessageQueueToGUI()->push(msg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::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)
 | 
			
		||||
        {
 | 
			
		||||
            MorseDecoderWorker::MsgConnectFifo *msg = MorseDecoderWorker::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)
 | 
			
		||||
        {
 | 
			
		||||
            MorseDecoderWorker::MsgConnectFifo *msg = MorseDecoderWorker::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 MorseDecoder::webapiRun(bool run,
 | 
			
		||||
    SWGSDRangel::SWGDeviceState& response,
 | 
			
		||||
    QString& errorMessage)
 | 
			
		||||
{
 | 
			
		||||
    (void) errorMessage;
 | 
			
		||||
    getFeatureStateStr(*response.getState());
 | 
			
		||||
    MsgStartStop *msg = MsgStartStop::create(run);
 | 
			
		||||
    getInputMessageQueue()->push(msg);
 | 
			
		||||
    return 202;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MorseDecoder::webapiSettingsGet(
 | 
			
		||||
    SWGSDRangel::SWGFeatureSettings& response,
 | 
			
		||||
    QString& errorMessage)
 | 
			
		||||
{
 | 
			
		||||
    (void) errorMessage;
 | 
			
		||||
    response.setDemodAnalyzerSettings(new SWGSDRangel::SWGDemodAnalyzerSettings());
 | 
			
		||||
    response.getDemodAnalyzerSettings()->init();
 | 
			
		||||
    webapiFormatFeatureSettings(response, m_settings);
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MorseDecoder::webapiSettingsPutPatch(
 | 
			
		||||
    bool force,
 | 
			
		||||
    const QStringList& featureSettingsKeys,
 | 
			
		||||
    SWGSDRangel::SWGFeatureSettings& response,
 | 
			
		||||
    QString& errorMessage)
 | 
			
		||||
{
 | 
			
		||||
    (void) errorMessage;
 | 
			
		||||
    MorseDecoderSettings settings = m_settings;
 | 
			
		||||
    webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
 | 
			
		||||
 | 
			
		||||
    MsgConfigureMorseDecoder *msg = MsgConfigureMorseDecoder::create(settings, featureSettingsKeys, force);
 | 
			
		||||
    m_inputMessageQueue.push(msg);
 | 
			
		||||
 | 
			
		||||
    qDebug("DemodAnalyzer::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
 | 
			
		||||
    if (m_guiMessageQueue) // forward to GUI if any
 | 
			
		||||
    {
 | 
			
		||||
        MsgConfigureMorseDecoder *msgToGUI = MsgConfigureMorseDecoder::create(settings, featureSettingsKeys, force);
 | 
			
		||||
        m_guiMessageQueue->push(msgToGUI);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    webapiFormatFeatureSettings(response, settings);
 | 
			
		||||
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::webapiFormatFeatureSettings(
 | 
			
		||||
    SWGSDRangel::SWGFeatureSettings& response,
 | 
			
		||||
    const MorseDecoderSettings& settings)
 | 
			
		||||
{
 | 
			
		||||
    if (response.getDemodAnalyzerSettings()->getTitle()) {
 | 
			
		||||
        *response.getDemodAnalyzerSettings()->getTitle() = settings.m_title;
 | 
			
		||||
    } else {
 | 
			
		||||
        response.getDemodAnalyzerSettings()->setTitle(new QString(settings.m_title));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.getDemodAnalyzerSettings()->setRgbColor(settings.m_rgbColor);
 | 
			
		||||
    response.getDemodAnalyzerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
    if (response.getDemodAnalyzerSettings()->getReverseApiAddress()) {
 | 
			
		||||
        *response.getDemodAnalyzerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
 | 
			
		||||
    } else {
 | 
			
		||||
        response.getDemodAnalyzerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.getDemodAnalyzerSettings()->setReverseApiPort(settings.m_reverseAPIPort);
 | 
			
		||||
    response.getDemodAnalyzerSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
 | 
			
		||||
    response.getDemodAnalyzerSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
 | 
			
		||||
 | 
			
		||||
    if (settings.m_rollupState)
 | 
			
		||||
    {
 | 
			
		||||
        if (response.getDemodAnalyzerSettings()->getRollupState())
 | 
			
		||||
        {
 | 
			
		||||
            settings.m_rollupState->formatTo(response.getDemodAnalyzerSettings()->getRollupState());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
 | 
			
		||||
            settings.m_rollupState->formatTo(swgRollupState);
 | 
			
		||||
            response.getDemodAnalyzerSettings()->setRollupState(swgRollupState);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::webapiUpdateFeatureSettings(
 | 
			
		||||
    MorseDecoderSettings& settings,
 | 
			
		||||
    const QStringList& featureSettingsKeys,
 | 
			
		||||
    SWGSDRangel::SWGFeatureSettings& response)
 | 
			
		||||
{
 | 
			
		||||
    if (featureSettingsKeys.contains("title")) {
 | 
			
		||||
        settings.m_title = *response.getDemodAnalyzerSettings()->getTitle();
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("rgbColor")) {
 | 
			
		||||
        settings.m_rgbColor = response.getDemodAnalyzerSettings()->getRgbColor();
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("useReverseAPI")) {
 | 
			
		||||
        settings.m_useReverseAPI = response.getDemodAnalyzerSettings()->getUseReverseApi() != 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("reverseAPIAddress")) {
 | 
			
		||||
        settings.m_reverseAPIAddress = *response.getDemodAnalyzerSettings()->getReverseApiAddress();
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("reverseAPIPort")) {
 | 
			
		||||
        settings.m_reverseAPIPort = response.getDemodAnalyzerSettings()->getReverseApiPort();
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
 | 
			
		||||
        settings.m_reverseAPIFeatureSetIndex = response.getDemodAnalyzerSettings()->getReverseApiFeatureSetIndex();
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
 | 
			
		||||
        settings.m_reverseAPIFeatureIndex = response.getDemodAnalyzerSettings()->getReverseApiFeatureIndex();
 | 
			
		||||
    }
 | 
			
		||||
    if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
 | 
			
		||||
        settings.m_rollupState->updateFrom(featureSettingsKeys, response.getDemodAnalyzerSettings()->getRollupState());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const MorseDecoderSettings& settings, bool force)
 | 
			
		||||
{
 | 
			
		||||
    SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
 | 
			
		||||
    // swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
 | 
			
		||||
    // swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
 | 
			
		||||
    swgFeatureSettings->setFeatureType(new QString("DemodAnalyzer"));
 | 
			
		||||
    swgFeatureSettings->setDemodAnalyzerSettings(new SWGSDRangel::SWGDemodAnalyzerSettings());
 | 
			
		||||
    SWGSDRangel::SWGDemodAnalyzerSettings *swgDemodAnalyzerSettings = swgFeatureSettings->getDemodAnalyzerSettings();
 | 
			
		||||
 | 
			
		||||
    // transfer data that has been modified. When force is on transfer all data except reverse API data
 | 
			
		||||
 | 
			
		||||
    if (featureSettingsKeys.contains("title") || force) {
 | 
			
		||||
        swgDemodAnalyzerSettings->setTitle(new QString(settings.m_title));
 | 
			
		||||
    }
 | 
			
		||||
    if (featureSettingsKeys.contains("rgbColor") || force) {
 | 
			
		||||
        swgDemodAnalyzerSettings->setRgbColor(settings.m_rgbColor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 MorseDecoder::networkManagerFinished(QNetworkReply *reply)
 | 
			
		||||
{
 | 
			
		||||
    QNetworkReply::NetworkError replyError = reply->error();
 | 
			
		||||
 | 
			
		||||
    if (replyError)
 | 
			
		||||
    {
 | 
			
		||||
        qWarning() << "MorseDecoder::networkManagerFinished:"
 | 
			
		||||
                << " error(" << (int) replyError
 | 
			
		||||
                << "): " << replyError
 | 
			
		||||
                << ": " << reply->errorString();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        QString answer = reply->readAll();
 | 
			
		||||
        answer.chop(1); // remove last \n
 | 
			
		||||
        qDebug("MorseDecoder::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reply->deleteLater();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::handleChannelMessageQueue(MessageQueue* messageQueue)
 | 
			
		||||
{
 | 
			
		||||
    Message* message;
 | 
			
		||||
 | 
			
		||||
    while ((message = messageQueue->pop()) != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (handleMessage(*message)) {
 | 
			
		||||
            delete message;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoder::handleDataPipeToBeDeleted(int reason, QObject *object)
 | 
			
		||||
{
 | 
			
		||||
    qDebug("MorseDecoder::handleDataPipeToBeDeleted: %d %p", reason, object);
 | 
			
		||||
 | 
			
		||||
    if ((reason == 0) && (m_selectedChannel == object))
 | 
			
		||||
    {
 | 
			
		||||
        DataFifo *fifo = qobject_cast<DataFifo*>(m_dataPipe->m_element);
 | 
			
		||||
 | 
			
		||||
        if ((fifo) && m_running)
 | 
			
		||||
        {
 | 
			
		||||
            MorseDecoderWorker::MsgConnectFifo *msg = MorseDecoderWorker::MsgConnectFifo::create(fifo, false);
 | 
			
		||||
            m_worker->getInputMessageQueue()->push(msg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_selectedChannel = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MorseDecoder::webapiActionsPost(
 | 
			
		||||
            const QStringList&,
 | 
			
		||||
            SWGSDRangel::SWGFeatureActions& query,
 | 
			
		||||
            QString& errorMessage) {
 | 
			
		||||
 | 
			
		||||
    MainCore* m_core = MainCore::instance();
 | 
			
		||||
    auto action = query.getDemodAnalyzerActions();
 | 
			
		||||
    if (action == nullptr) {
 | 
			
		||||
        errorMessage = QString("missing MorseDecoderActions 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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,223 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODER_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODER_H_
 | 
			
		||||
 | 
			
		||||
#include <QHash>
 | 
			
		||||
#include <QNetworkRequest>
 | 
			
		||||
#include <QRecursiveMutex>
 | 
			
		||||
 | 
			
		||||
#include "feature/feature.h"
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
#include "availablechannelorfeaturehandler.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecodersettings.h"
 | 
			
		||||
 | 
			
		||||
class WebAPIAdapterInterface;
 | 
			
		||||
class MorseDecoderWorker;
 | 
			
		||||
class QNetworkAccessManager;
 | 
			
		||||
class QNetworkReply;
 | 
			
		||||
class QThread;
 | 
			
		||||
class ObjectPipe;
 | 
			
		||||
 | 
			
		||||
namespace SWGSDRangel {
 | 
			
		||||
    class SWGDeviceState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class MorseDecoder : public Feature
 | 
			
		||||
{
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    class MsgConfigureMorseDecoder : public Message {
 | 
			
		||||
        MESSAGE_CLASS_DECLARATION
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        const MorseDecoderSettings& getSettings() const { return m_settings; }
 | 
			
		||||
        const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
 | 
			
		||||
        bool getForce() const { return m_force; }
 | 
			
		||||
 | 
			
		||||
        static MsgConfigureMorseDecoder* create(const MorseDecoderSettings& settings, const QList<QString>& settingsKeys, bool force) {
 | 
			
		||||
            return new MsgConfigureMorseDecoder(settings, settingsKeys, force);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        MorseDecoderSettings m_settings;
 | 
			
		||||
        QList<QString> m_settingsKeys;
 | 
			
		||||
        bool m_force;
 | 
			
		||||
 | 
			
		||||
        MsgConfigureMorseDecoder(const MorseDecoderSettings& 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)
 | 
			
		||||
        {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MorseDecoder(WebAPIAdapterInterface *webAPIAdapterInterface);
 | 
			
		||||
    virtual ~MorseDecoder();
 | 
			
		||||
    virtual void destroy() { delete this; }
 | 
			
		||||
    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 MorseDecoderSettings& settings);
 | 
			
		||||
 | 
			
		||||
    static void webapiUpdateFeatureSettings(
 | 
			
		||||
            MorseDecoderSettings& settings,
 | 
			
		||||
            const QStringList& featureSettingsKeys,
 | 
			
		||||
            SWGSDRangel::SWGFeatureSettings& response);
 | 
			
		||||
 | 
			
		||||
    static const char* const m_featureIdURI;
 | 
			
		||||
    static const char* const m_featureId;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QThread *m_thread;
 | 
			
		||||
    QRecursiveMutex m_mutex;
 | 
			
		||||
    bool m_running;
 | 
			
		||||
    MorseDecoderWorker *m_worker;
 | 
			
		||||
    MorseDecoderSettings 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 MorseDecoderSettings& 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 MorseDecoderSettings& 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_MORSEDECODER_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,356 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 "dsp/spectrumvis.h"
 | 
			
		||||
#include "gui/basicfeaturesettingsdialog.h"
 | 
			
		||||
#include "gui/glspectrum.h"
 | 
			
		||||
#include "gui/glscope.h"
 | 
			
		||||
#include "gui/dialpopup.h"
 | 
			
		||||
#include "gui/dialogpositioner.h"
 | 
			
		||||
#include "util/db.h"
 | 
			
		||||
#include "maincore.h"
 | 
			
		||||
 | 
			
		||||
#include "ui_morsedecodergui.h"
 | 
			
		||||
#include "morsedecoder.h"
 | 
			
		||||
#include "morsedecodergui.h"
 | 
			
		||||
 | 
			
		||||
MorseDecoderGUI* MorseDecoderGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
 | 
			
		||||
{
 | 
			
		||||
	MorseDecoderGUI* gui = new MorseDecoderGUI(pluginAPI, featureUISet, feature);
 | 
			
		||||
	return gui;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::destroy()
 | 
			
		||||
{
 | 
			
		||||
	delete this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::resetToDefaults()
 | 
			
		||||
{
 | 
			
		||||
    m_settings.resetToDefaults();
 | 
			
		||||
    displaySettings();
 | 
			
		||||
	applySettings(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray MorseDecoderGUI::serialize() const
 | 
			
		||||
{
 | 
			
		||||
    return m_settings.serialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MorseDecoderGUI::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 MorseDecoderGUI::handleMessage(const Message& message)
 | 
			
		||||
{
 | 
			
		||||
    if (MorseDecoder::MsgConfigureMorseDecoder::match(message))
 | 
			
		||||
    {
 | 
			
		||||
        qDebug("MorseDecoderGUI::handleMessage: MorseDecoder::MsgConfigureMorseDecoder");
 | 
			
		||||
        const MorseDecoder::MsgConfigureMorseDecoder& cfg = (MorseDecoder::MsgConfigureMorseDecoder&) 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 (MorseDecoder::MsgReportChannels::match(message))
 | 
			
		||||
    {
 | 
			
		||||
        qDebug("MorseDecoderGUI::handleMessage: MorseDecoder::MsgReportChannels");
 | 
			
		||||
        MorseDecoder::MsgReportChannels& report = (MorseDecoder::MsgReportChannels&) message;
 | 
			
		||||
        m_availableChannels = report.getAvailableChannels();
 | 
			
		||||
        updateChannelList();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (MorseDecoder::MsgReportSampleRate::match(message))
 | 
			
		||||
    {
 | 
			
		||||
        MorseDecoder::MsgReportSampleRate& report = (MorseDecoder::MsgReportSampleRate&) message;
 | 
			
		||||
        int sampleRate = report.getSampleRate();
 | 
			
		||||
        qDebug("MorseDecoderGUI::handleMessage: MorseDecoder::MsgReportSampleRate: %d", sampleRate);
 | 
			
		||||
        displaySampleRate(sampleRate);
 | 
			
		||||
        m_sampleRate = sampleRate;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::handleInputMessages()
 | 
			
		||||
{
 | 
			
		||||
    Message* message;
 | 
			
		||||
 | 
			
		||||
    while ((message = getInputMessageQueue()->pop()))
 | 
			
		||||
    {
 | 
			
		||||
        if (handleMessage(*message)) {
 | 
			
		||||
            delete message;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::onWidgetRolled(QWidget* widget, bool rollDown)
 | 
			
		||||
{
 | 
			
		||||
    (void) widget;
 | 
			
		||||
    (void) rollDown;
 | 
			
		||||
 | 
			
		||||
    RollupContents *rollupContents = getRollupContents();
 | 
			
		||||
 | 
			
		||||
    rollupContents->saveState(m_rollupState);
 | 
			
		||||
    applySettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MorseDecoderGUI::MorseDecoderGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
 | 
			
		||||
	FeatureGUI(parent),
 | 
			
		||||
	ui(new Ui::MorseDecoderGUI),
 | 
			
		||||
	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/morsedecoder/readme.md";
 | 
			
		||||
    RollupContents *rollupContents = getRollupContents();
 | 
			
		||||
	ui->setupUi(rollupContents);
 | 
			
		||||
    rollupContents->arrangeRollups();
 | 
			
		||||
	connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
 | 
			
		||||
 | 
			
		||||
    m_morseDecoder = reinterpret_cast<MorseDecoder*>(feature);
 | 
			
		||||
    m_morseDecoder->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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MorseDecoderGUI::~MorseDecoderGUI()
 | 
			
		||||
{
 | 
			
		||||
	delete ui;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::blockApplySettings(bool block)
 | 
			
		||||
{
 | 
			
		||||
    m_doApplySettings = !block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::setWorkspaceIndex(int index)
 | 
			
		||||
{
 | 
			
		||||
    m_settings.m_workspaceIndex = index;
 | 
			
		||||
    m_feature->setWorkspaceIndex(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::displaySettings()
 | 
			
		||||
{
 | 
			
		||||
    setTitleColor(m_settings.m_rgbColor);
 | 
			
		||||
    setWindowTitle(m_settings.m_title);
 | 
			
		||||
    setTitle(m_settings.m_title);
 | 
			
		||||
    blockApplySettings(true);
 | 
			
		||||
    getRollupContents()->restoreState(m_rollupState);
 | 
			
		||||
    blockApplySettings(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::displaySampleRate(int sampleRate)
 | 
			
		||||
{
 | 
			
		||||
	QString s = QString::number(sampleRate/1000.0, 'f', 1);
 | 
			
		||||
	ui->sinkSampleRateText->setText(tr("%1 kS/s").arg(s));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::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 MorseDecoderGUI::onMenuDialogCalled(const QPoint &p)
 | 
			
		||||
{
 | 
			
		||||
    if (m_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 MorseDecoderGUI::on_startStop_toggled(bool checked)
 | 
			
		||||
{
 | 
			
		||||
    if (m_doApplySettings)
 | 
			
		||||
    {
 | 
			
		||||
        MorseDecoder::MsgStartStop *message = MorseDecoder::MsgStartStop::create(checked);
 | 
			
		||||
        m_morseDecoder->getInputMessageQueue()->push(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::on_channels_currentIndexChanged(int index)
 | 
			
		||||
{
 | 
			
		||||
    if ((index >= 0) && (index < m_availableChannels.size()))
 | 
			
		||||
    {
 | 
			
		||||
        m_selectedChannel = qobject_cast<ChannelAPI*>(m_availableChannels[index].m_object);
 | 
			
		||||
        MorseDecoder::MsgSelectChannel *msg = MorseDecoder::MsgSelectChannel::create(m_selectedChannel);
 | 
			
		||||
        m_morseDecoder->getInputMessageQueue()->push(msg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::on_channelApply_clicked()
 | 
			
		||||
{
 | 
			
		||||
    if (ui->channels->count() > 0) {
 | 
			
		||||
        on_channels_currentIndexChanged(ui->channels->currentIndex());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::tick()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::updateStatus()
 | 
			
		||||
{
 | 
			
		||||
    int state = m_morseDecoder->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_morseDecoder->getErrorMessage());
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_lastFeatureState = state;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::applySettings(bool force)
 | 
			
		||||
{
 | 
			
		||||
	if (m_doApplySettings)
 | 
			
		||||
	{
 | 
			
		||||
        MorseDecoder::MsgConfigureMorseDecoder* message = MorseDecoder::MsgConfigureMorseDecoder::create( m_settings, m_settingsKeys, force);
 | 
			
		||||
        m_morseDecoder->getInputMessageQueue()->push(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    m_settingsKeys.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderGUI::makeUIConnections()
 | 
			
		||||
{
 | 
			
		||||
	QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &MorseDecoderGUI::on_startStop_toggled);
 | 
			
		||||
	QObject::connect(ui->channels, qOverload<int>(&QComboBox::currentIndexChanged), this, &MorseDecoderGUI::on_channels_currentIndexChanged);
 | 
			
		||||
	QObject::connect(ui->channelApply, &QPushButton::clicked, this, &MorseDecoderGUI::on_channelApply_clicked);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODERGUI_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODERGUI_H_
 | 
			
		||||
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
#include <QList>
 | 
			
		||||
 | 
			
		||||
#include "feature/featuregui.h"
 | 
			
		||||
#include "util/messagequeue.h"
 | 
			
		||||
#include "availablechannelorfeaturehandler.h"
 | 
			
		||||
#include "settings/rollupstate.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecodersettings.h"
 | 
			
		||||
 | 
			
		||||
class PluginAPI;
 | 
			
		||||
class FeatureUISet;
 | 
			
		||||
class MorseDecoder;
 | 
			
		||||
class Feature;
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
	class MorseDecoderGUI;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class MorseDecoderGUI : public FeatureGUI {
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
	static MorseDecoderGUI* 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::MorseDecoderGUI* ui;
 | 
			
		||||
	PluginAPI* m_pluginAPI;
 | 
			
		||||
	FeatureUISet* m_featureUISet;
 | 
			
		||||
	MorseDecoderSettings m_settings;
 | 
			
		||||
    QList<QString> m_settingsKeys;
 | 
			
		||||
	RollupState m_rollupState;
 | 
			
		||||
	int m_sampleRate;
 | 
			
		||||
	bool m_doApplySettings;
 | 
			
		||||
 | 
			
		||||
	MorseDecoder* m_morseDecoder;
 | 
			
		||||
	MessageQueue m_inputMessageQueue;
 | 
			
		||||
	QTimer m_statusTimer;
 | 
			
		||||
	int m_lastFeatureState;
 | 
			
		||||
	AvailableChannelOrFeatureList m_availableChannels;
 | 
			
		||||
	ChannelAPI *m_selectedChannel;
 | 
			
		||||
 | 
			
		||||
	explicit MorseDecoderGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
 | 
			
		||||
	virtual ~MorseDecoderGUI();
 | 
			
		||||
 | 
			
		||||
    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 updateStatus();
 | 
			
		||||
	void tick();
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_FEATURE_MORSEDECODERGUI_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,468 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>MorseDecoderGUI</class>
 | 
			
		||||
 <widget class="RollupContents" name="MorseDecoderGUI">
 | 
			
		||||
  <property name="geometry">
 | 
			
		||||
   <rect>
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>407</width>
 | 
			
		||||
    <height>407</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="sizePolicy">
 | 
			
		||||
   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
 | 
			
		||||
    <horstretch>0</horstretch>
 | 
			
		||||
    <verstretch>0</verstretch>
 | 
			
		||||
   </sizepolicy>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="minimumSize">
 | 
			
		||||
   <size>
 | 
			
		||||
    <width>407</width>
 | 
			
		||||
    <height>407</height>
 | 
			
		||||
   </size>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="font">
 | 
			
		||||
   <font>
 | 
			
		||||
    <family>Liberation Sans</family>
 | 
			
		||||
    <pointsize>9</pointsize>
 | 
			
		||||
   </font>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
   <string>Morse Decoder</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <widget class="QWidget" name="settingsContainer" native="true">
 | 
			
		||||
   <property name="geometry">
 | 
			
		||||
    <rect>
 | 
			
		||||
     <x>0</x>
 | 
			
		||||
     <y>10</y>
 | 
			
		||||
     <width>401</width>
 | 
			
		||||
     <height>121</height>
 | 
			
		||||
    </rect>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="minimumSize">
 | 
			
		||||
    <size>
 | 
			
		||||
     <width>401</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>
 | 
			
		||||
    <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>Channel 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>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="udpLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="udpEnabled">
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Send messages via UDP</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="layoutDirection">
 | 
			
		||||
         <enum>Qt::RightToLeft</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>UDP</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLineEdit" name="udpAddress">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>120</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="focusPolicy">
 | 
			
		||||
         <enum>Qt::ClickFocus</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Destination UDP address</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="inputMask">
 | 
			
		||||
         <string>000.000.000.000</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>127.0.0.1</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="udpSeparator">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>:</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLineEdit" name="udpPort">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>50</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>50</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="focusPolicy">
 | 
			
		||||
         <enum>Qt::ClickFocus</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Destination UDP port</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="inputMask">
 | 
			
		||||
         <string>00000</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>9998</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <spacer name="udpSpacer">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Horizontal</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="sizeHint" stdset="0">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>40</width>
 | 
			
		||||
          <height>20</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
       </spacer>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
    <item>
 | 
			
		||||
     <layout class="QHBoxLayout" name="morseStatLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="pitchLabel">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Pitch</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="pitchText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>60</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>CW pitch (Hz)</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>2000.0 Hz</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="Line" name="line">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Vertical</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="speedLabel">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Speed</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="speedText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>50</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>CW speed (WPM)</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>25 WPM</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="Line" name="line_3">
 | 
			
		||||
        <property name="orientation">
 | 
			
		||||
         <enum>Qt::Vertical</enum>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QToolButton" name="statLock">
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Lock pitch and speed</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string/>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="icon">
 | 
			
		||||
         <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
          <normaloff>:/unlocked.png</normaloff>
 | 
			
		||||
          <normalon>:/locked.png</normalon>:/unlocked.png</iconset>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="checkable">
 | 
			
		||||
         <bool>true</bool>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="ButtonSwitch" name="logEnable">
 | 
			
		||||
        <property name="maximumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>24</width>
 | 
			
		||||
          <height>16777215</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Start/stop logging of received characters to .txt file</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="QToolButton" name="logFilename">
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Set log .csv filename</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>...</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="icon">
 | 
			
		||||
         <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
          <normaloff>:/save.png</normaloff>:/save.png</iconset>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="checkable">
 | 
			
		||||
         <bool>false</bool>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QPushButton" name="clearTable">
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>Clear messages</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string/>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="icon">
 | 
			
		||||
         <iconset resource="../../../sdrgui/resources/res.qrc">
 | 
			
		||||
          <normaloff>:/bin.png</normaloff>:/bin.png</iconset>
 | 
			
		||||
        </property>
 | 
			
		||||
       </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="QLabel" name="cLabel">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>C</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="cText">
 | 
			
		||||
        <property name="minimumSize">
 | 
			
		||||
         <size>
 | 
			
		||||
          <width>55</width>
 | 
			
		||||
          <height>0</height>
 | 
			
		||||
         </size>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>3000.000</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="alignment">
 | 
			
		||||
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </item>
 | 
			
		||||
   </layout>
 | 
			
		||||
  </widget>
 | 
			
		||||
  <widget class="QWidget" name="dataContainer" native="true">
 | 
			
		||||
   <property name="geometry">
 | 
			
		||||
    <rect>
 | 
			
		||||
     <x>0</x>
 | 
			
		||||
     <y>130</y>
 | 
			
		||||
     <width>401</width>
 | 
			
		||||
     <height>271</height>
 | 
			
		||||
    </rect>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="windowTitle">
 | 
			
		||||
    <string>Morse Text</string>
 | 
			
		||||
   </property>
 | 
			
		||||
   <property name="sizePolicy">
 | 
			
		||||
    <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
 | 
			
		||||
     <horstretch>0</horstretch>
 | 
			
		||||
     <verstretch>0</verstretch>
 | 
			
		||||
    </sizepolicy>
 | 
			
		||||
   </property>
 | 
			
		||||
   <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
    <item>
 | 
			
		||||
     <widget class="QTextEdit" name="text"/>
 | 
			
		||||
    </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>
 | 
			
		||||
 </customwidgets>
 | 
			
		||||
 <resources>
 | 
			
		||||
  <include location="../../../sdrgui/resources/res.qrc"/>
 | 
			
		||||
 </resources>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 "morsedecodergui.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "morsedecoder.h"
 | 
			
		||||
#include "morsedecoderplugin.h"
 | 
			
		||||
#include "morsedecoderwebapiadapter.h"
 | 
			
		||||
 | 
			
		||||
const PluginDescriptor MorseDecoderPlugin::m_pluginDescriptor = {
 | 
			
		||||
    MorseDecoder::m_featureId,
 | 
			
		||||
	QStringLiteral("Morse Decoder"),
 | 
			
		||||
    QStringLiteral("7.21.0"),
 | 
			
		||||
	QStringLiteral("(c) Edouard Griffiths, F4EXB"),
 | 
			
		||||
	QStringLiteral("https://github.com/f4exb/sdrangel"),
 | 
			
		||||
	true,
 | 
			
		||||
	QStringLiteral("https://github.com/f4exb/sdrangel")
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MorseDecoderPlugin::MorseDecoderPlugin(QObject* parent) :
 | 
			
		||||
	QObject(parent),
 | 
			
		||||
	m_pluginAPI(nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PluginDescriptor& MorseDecoderPlugin::getPluginDescriptor() const
 | 
			
		||||
{
 | 
			
		||||
	return m_pluginDescriptor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderPlugin::initPlugin(PluginAPI* pluginAPI)
 | 
			
		||||
{
 | 
			
		||||
	m_pluginAPI = pluginAPI;
 | 
			
		||||
 | 
			
		||||
	// register RigCtl Server feature
 | 
			
		||||
	m_pluginAPI->registerFeature(MorseDecoder::m_featureIdURI, MorseDecoder::m_featureId, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SERVER_MODE
 | 
			
		||||
FeatureGUI* MorseDecoderPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
 | 
			
		||||
{
 | 
			
		||||
	(void) featureUISet;
 | 
			
		||||
	(void) feature;
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
FeatureGUI* MorseDecoderPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
 | 
			
		||||
{
 | 
			
		||||
	return MorseDecoderGUI::create(m_pluginAPI, featureUISet, feature);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
Feature* MorseDecoderPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
 | 
			
		||||
{
 | 
			
		||||
    return new MorseDecoder(webAPIAdapterInterface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FeatureWebAPIAdapter* MorseDecoderPlugin::createFeatureWebAPIAdapter() const
 | 
			
		||||
{
 | 
			
		||||
	return new MorseDecoderWebAPIAdapter();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODERPLUGIN_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODERPLUGIN_H_
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include "plugin/plugininterface.h"
 | 
			
		||||
 | 
			
		||||
class FeatureGUI;
 | 
			
		||||
class WebAPIAdapterInterface;
 | 
			
		||||
 | 
			
		||||
class MorseDecoderPlugin : public QObject, PluginInterface {
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
	Q_INTERFACES(PluginInterface)
 | 
			
		||||
	Q_PLUGIN_METADATA(IID "sdrangel.feature.morsedecoder")
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit MorseDecoderPlugin(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_MORSEDECODERPLUGIN_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,236 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 <QColor>
 | 
			
		||||
#include <QStringList>
 | 
			
		||||
 | 
			
		||||
#include "util/simpleserializer.h"
 | 
			
		||||
#include "settings/serializable.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecodersettings.h"
 | 
			
		||||
 | 
			
		||||
const QStringList MorseDecoderSettings::m_channelURIs = {
 | 
			
		||||
    QStringLiteral("sdrangel.channel.amdemod"),
 | 
			
		||||
    QStringLiteral("sdrangel.channel.nfmdemod"),
 | 
			
		||||
    QStringLiteral("sdrangel.channel.ssbdemod"),
 | 
			
		||||
    QStringLiteral("sdrangel.channel.wfmdemod"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MorseDecoderSettings::MorseDecoderSettings() :
 | 
			
		||||
    m_rollupState(nullptr)
 | 
			
		||||
{
 | 
			
		||||
    resetToDefaults();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderSettings::resetToDefaults()
 | 
			
		||||
{
 | 
			
		||||
    m_title = "Morse Decoder";
 | 
			
		||||
    m_rgbColor = QColor(0, 255, 0).rgb();
 | 
			
		||||
    m_useReverseAPI = false;
 | 
			
		||||
    m_reverseAPIAddress = "127.0.0.1";
 | 
			
		||||
    m_reverseAPIPort = 8888;
 | 
			
		||||
    m_reverseAPIFeatureSetIndex = 0;
 | 
			
		||||
    m_reverseAPIFeatureIndex = 0;
 | 
			
		||||
    m_workspaceIndex = 0;
 | 
			
		||||
    m_udpEnabled = false;
 | 
			
		||||
    m_udpAddress = "127.0.0.1";
 | 
			
		||||
    m_udpPort = 9999;
 | 
			
		||||
    m_logFilename = "cw_log.txt";
 | 
			
		||||
    m_logEnabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QByteArray MorseDecoderSettings::serialize() const
 | 
			
		||||
{
 | 
			
		||||
    SimpleSerializer s(1);
 | 
			
		||||
 | 
			
		||||
    s.writeString(5, m_title);
 | 
			
		||||
    s.writeU32(6, m_rgbColor);
 | 
			
		||||
    s.writeBool(7, m_useReverseAPI);
 | 
			
		||||
    s.writeString(8, m_reverseAPIAddress);
 | 
			
		||||
    s.writeU32(9, m_reverseAPIPort);
 | 
			
		||||
    s.writeU32(10, m_reverseAPIFeatureSetIndex);
 | 
			
		||||
    s.writeU32(11, m_reverseAPIFeatureIndex);
 | 
			
		||||
 | 
			
		||||
    if (m_rollupState) {
 | 
			
		||||
        s.writeBlob(12, m_rollupState->serialize());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s.writeS32(13, m_workspaceIndex);
 | 
			
		||||
    s.writeBlob(14, m_geometryBytes);
 | 
			
		||||
 | 
			
		||||
    s.writeBool(22, m_udpEnabled);
 | 
			
		||||
    s.writeString(23, m_udpAddress);
 | 
			
		||||
    s.writeU32(24, m_udpPort);
 | 
			
		||||
    s.writeString(25, m_logFilename);
 | 
			
		||||
    s.writeBool(26, m_logEnabled);
 | 
			
		||||
 | 
			
		||||
    return s.final();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MorseDecoderSettings::deserialize(const QByteArray& data)
 | 
			
		||||
{
 | 
			
		||||
    SimpleDeserializer d(data);
 | 
			
		||||
 | 
			
		||||
    if (!d.isValid())
 | 
			
		||||
    {
 | 
			
		||||
        resetToDefaults();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (d.getVersion() == 1)
 | 
			
		||||
    {
 | 
			
		||||
        QByteArray bytetmp;
 | 
			
		||||
        uint32_t utmp;
 | 
			
		||||
        QString strtmp;
 | 
			
		||||
 | 
			
		||||
        d.readString(5, &m_title, "Demod Analyzer");
 | 
			
		||||
        d.readU32(6, &m_rgbColor, QColor(0, 255, 0).rgb());
 | 
			
		||||
        d.readBool(7, &m_useReverseAPI, false);
 | 
			
		||||
        d.readString(8, &m_reverseAPIAddress, "127.0.0.1");
 | 
			
		||||
        d.readU32(9, &utmp, 0);
 | 
			
		||||
 | 
			
		||||
        if ((utmp > 1023) && (utmp < 65535)) {
 | 
			
		||||
            m_reverseAPIPort = utmp;
 | 
			
		||||
        } else {
 | 
			
		||||
            m_reverseAPIPort = 8888;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        d.readU32(10, &utmp, 0);
 | 
			
		||||
        m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
 | 
			
		||||
        d.readU32(11, &utmp, 0);
 | 
			
		||||
        m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
 | 
			
		||||
 | 
			
		||||
        if (m_rollupState)
 | 
			
		||||
        {
 | 
			
		||||
            d.readBlob(12, &bytetmp);
 | 
			
		||||
            m_rollupState->deserialize(bytetmp);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        d.readS32(13, &m_workspaceIndex, 0);
 | 
			
		||||
        d.readBlob(14, &m_geometryBytes);
 | 
			
		||||
 | 
			
		||||
        d.readBool(22, &m_udpEnabled);
 | 
			
		||||
        d.readString(23, &m_udpAddress);
 | 
			
		||||
        d.readU32(24, &utmp);
 | 
			
		||||
 | 
			
		||||
        if ((utmp > 1023) && (utmp < 65535)) {
 | 
			
		||||
            m_udpPort = utmp;
 | 
			
		||||
        } else {
 | 
			
		||||
            m_udpPort = 9999;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        d.readString(25, &m_logFilename, "cw_log.txt");
 | 
			
		||||
        d.readBool(26, &m_logEnabled, false);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        resetToDefaults();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderSettings::applySettings(const QStringList& settingsKeys, const MorseDecoderSettings& settings)
 | 
			
		||||
{
 | 
			
		||||
    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("udpEnabled")) {
 | 
			
		||||
        m_udpEnabled = settings.m_udpEnabled;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("udpAddress")) {
 | 
			
		||||
        m_udpAddress = settings.m_udpAddress;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("udpPort")) {
 | 
			
		||||
        m_udpPort = settings.m_udpPort;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("logEnabled")) {
 | 
			
		||||
        m_logEnabled = settings.m_logEnabled;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("logFilename")) {
 | 
			
		||||
        m_logFilename = settings.m_logFilename;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString MorseDecoderSettings::getDebugString(const QStringList& settingsKeys, bool force) const
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ostr;
 | 
			
		||||
 | 
			
		||||
    if (settingsKeys.contains("title") || force) {
 | 
			
		||||
        ostr << " m_title: " << m_title.toStdString();
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("rgbColor") || force) {
 | 
			
		||||
        ostr << " m_rgbColor: " << m_rgbColor;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("useReverseAPI") || force) {
 | 
			
		||||
        ostr << " m_useReverseAPI: " << m_useReverseAPI;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("reverseAPIAddress") || force) {
 | 
			
		||||
        ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString();
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("reverseAPIPort") || force) {
 | 
			
		||||
        ostr << " m_reverseAPIPort: " << m_reverseAPIPort;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) {
 | 
			
		||||
        ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
 | 
			
		||||
        ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("workspaceIndex") || force) {
 | 
			
		||||
        ostr << " m_workspaceIndex: " << m_workspaceIndex;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("udpEnabled") || force) {
 | 
			
		||||
        ostr << " m_udpEnabled: " << m_udpEnabled;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("udpAddress") || force) {
 | 
			
		||||
        ostr << " m_udpAddress: " << m_udpAddress.toStdString();
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("udpPort") || force) {
 | 
			
		||||
        ostr << " m_udpPort: " << m_udpPort;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("logEnabled") || force) {
 | 
			
		||||
        ostr << " m_logEnabled: " << m_logEnabled;
 | 
			
		||||
    }
 | 
			
		||||
    if (settingsKeys.contains("logFilename") || force) {
 | 
			
		||||
        ostr << " m_logFilename: " << m_logFilename.toStdString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return QString(ostr.str().c_str());
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODERSETTINGS_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODERSETTINGS_H_
 | 
			
		||||
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
 | 
			
		||||
class Serializable;
 | 
			
		||||
 | 
			
		||||
struct MorseDecoderSettings
 | 
			
		||||
{
 | 
			
		||||
    QString m_title;
 | 
			
		||||
    quint32 m_rgbColor;
 | 
			
		||||
    bool m_useReverseAPI;
 | 
			
		||||
    QString m_reverseAPIAddress;
 | 
			
		||||
    uint16_t m_reverseAPIPort;
 | 
			
		||||
    uint16_t m_reverseAPIFeatureSetIndex;
 | 
			
		||||
    uint16_t m_reverseAPIFeatureIndex;
 | 
			
		||||
    Serializable *m_rollupState;
 | 
			
		||||
    int m_workspaceIndex;
 | 
			
		||||
    QByteArray m_geometryBytes;
 | 
			
		||||
    bool m_udpEnabled;
 | 
			
		||||
    QString m_udpAddress;
 | 
			
		||||
    uint16_t m_udpPort;
 | 
			
		||||
    QString m_logFilename;
 | 
			
		||||
    bool m_logEnabled;
 | 
			
		||||
 | 
			
		||||
    MorseDecoderSettings();
 | 
			
		||||
    void resetToDefaults();
 | 
			
		||||
    QByteArray serialize() const;
 | 
			
		||||
    bool deserialize(const QByteArray& data);
 | 
			
		||||
 | 
			
		||||
    void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
 | 
			
		||||
    void applySettings(const QStringList& settingsKeys, const MorseDecoderSettings& settings);
 | 
			
		||||
    QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
 | 
			
		||||
 | 
			
		||||
    static const QStringList m_channelURIs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_FEATURE_MORSEDECODERSETTINGS_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 "morsedecoder.h"
 | 
			
		||||
#include "morsedecoderwebapiadapter.h"
 | 
			
		||||
 | 
			
		||||
MorseDecoderWebAPIAdapter::MorseDecoderWebAPIAdapter()
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
MorseDecoderWebAPIAdapter::~MorseDecoderWebAPIAdapter()
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
int MorseDecoderWebAPIAdapter::webapiSettingsGet(
 | 
			
		||||
        SWGSDRangel::SWGFeatureSettings& response,
 | 
			
		||||
        QString& errorMessage)
 | 
			
		||||
{
 | 
			
		||||
    (void) errorMessage;
 | 
			
		||||
    response.setDemodAnalyzerSettings(new SWGSDRangel::SWGDemodAnalyzerSettings());
 | 
			
		||||
    response.getDemodAnalyzerSettings()->init();
 | 
			
		||||
    MorseDecoder::webapiFormatFeatureSettings(response, m_settings);
 | 
			
		||||
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int MorseDecoderWebAPIAdapter::webapiSettingsPutPatch(
 | 
			
		||||
        bool force,
 | 
			
		||||
        const QStringList& featureSettingsKeys,
 | 
			
		||||
        SWGSDRangel::SWGFeatureSettings& response,
 | 
			
		||||
        QString& errorMessage)
 | 
			
		||||
{
 | 
			
		||||
    (void) force; // no action
 | 
			
		||||
    (void) errorMessage;
 | 
			
		||||
    MorseDecoder::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
 | 
			
		||||
 | 
			
		||||
    return 200;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODERWEBAPIADAPTER_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODERWEBAPIADAPTER_H_
 | 
			
		||||
 | 
			
		||||
#include "feature/featurewebapiadapter.h"
 | 
			
		||||
#include "morsedecodersettings.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Standalone API adapter only for the settings
 | 
			
		||||
 */
 | 
			
		||||
class MorseDecoderWebAPIAdapter : public FeatureWebAPIAdapter {
 | 
			
		||||
public:
 | 
			
		||||
    MorseDecoderWebAPIAdapter();
 | 
			
		||||
    virtual ~MorseDecoderWebAPIAdapter();
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
    MorseDecoderSettings m_settings;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_FEATURE_MORSEDECODERWEBAPIADAPTER_H_
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,195 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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 "dsp/scopevis.h"
 | 
			
		||||
#include "dsp/datafifo.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecoderworker.h"
 | 
			
		||||
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoderWorker::MsgConfigureMorseDecoderWorker, Message)
 | 
			
		||||
MESSAGE_CLASS_DEFINITION(MorseDecoderWorker::MsgConnectFifo, Message)
 | 
			
		||||
 | 
			
		||||
MorseDecoderWorker::MorseDecoderWorker() :
 | 
			
		||||
    m_dataFifo(nullptr),
 | 
			
		||||
    m_msgQueueToFeature(nullptr),
 | 
			
		||||
    m_sampleBufferSize(0),
 | 
			
		||||
    m_nbBytes(0)
 | 
			
		||||
{
 | 
			
		||||
    qDebug("MorseDecoderWorker::MorseDecoderWorker");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MorseDecoderWorker::~MorseDecoderWorker()
 | 
			
		||||
{
 | 
			
		||||
    m_inputMessageQueue.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::reset()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    m_inputMessageQueue.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::startWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::stopWork()
 | 
			
		||||
{
 | 
			
		||||
    QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_nbBytes = nbBytes;
 | 
			
		||||
    int countSamples = (end - begin) / nbBytes;
 | 
			
		||||
 | 
			
		||||
    if (countSamples > m_sampleBufferSize)
 | 
			
		||||
    {
 | 
			
		||||
        m_sampleBuffer.resize(countSamples);
 | 
			
		||||
        m_sampleBufferSize = countSamples;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO
 | 
			
		||||
    // for (int i = 0; i < countSamples; i++) {
 | 
			
		||||
    //     processSample(dataType, begin, countSamples, i);
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::handleInputMessages()
 | 
			
		||||
{
 | 
			
		||||
	Message* message;
 | 
			
		||||
 | 
			
		||||
	while ((message = m_inputMessageQueue.pop()) != nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		if (handleMessage(*message)) {
 | 
			
		||||
			delete message;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MorseDecoderWorker::handleMessage(const Message& cmd)
 | 
			
		||||
{
 | 
			
		||||
    if (MsgConfigureMorseDecoderWorker::match(cmd))
 | 
			
		||||
    {
 | 
			
		||||
        QMutexLocker mutexLocker(&m_mutex);
 | 
			
		||||
        MsgConfigureMorseDecoderWorker& cfg = (MsgConfigureMorseDecoderWorker&) cmd;
 | 
			
		||||
        qDebug("MorseDecoderWorker::handleMessage: MsgConfigureMorseDecoderWorker");
 | 
			
		||||
 | 
			
		||||
        applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.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("DemodAnalyzerWorker::handleMessage: MsgConnectFifo: %s", (doConnect ? "connect" : "disconnect"));
 | 
			
		||||
 | 
			
		||||
        if (doConnect) {
 | 
			
		||||
            QObject::connect(
 | 
			
		||||
                m_dataFifo,
 | 
			
		||||
                &DataFifo::dataReady,
 | 
			
		||||
                this,
 | 
			
		||||
                &MorseDecoderWorker::handleData,
 | 
			
		||||
                Qt::QueuedConnection
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            QObject::disconnect(
 | 
			
		||||
                m_dataFifo,
 | 
			
		||||
                &DataFifo::dataReady,
 | 
			
		||||
                this,
 | 
			
		||||
                &MorseDecoderWorker::handleData
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::applySettings(const MorseDecoderSettings& settings, const QList<QString>& settingsKeys, bool force)
 | 
			
		||||
{
 | 
			
		||||
    qDebug() << "MorseDecoderWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << force;
 | 
			
		||||
 | 
			
		||||
    if (force) {
 | 
			
		||||
        m_settings = settings;
 | 
			
		||||
    } else {
 | 
			
		||||
        m_settings.applySettings(settingsKeys, settings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::applySampleRate(int sampleRate)
 | 
			
		||||
{
 | 
			
		||||
    m_sinkSampleRate = sampleRate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MorseDecoderWorker::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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
///////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Copyright (C) 2024 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_MORSEDECODERWORKER_H_
 | 
			
		||||
#define INCLUDE_FEATURE_MORSEDECODERWORKER_H_
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QRecursiveMutex>
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
 | 
			
		||||
#include "dsp/dsptypes.h"
 | 
			
		||||
#include "dsp/datafifo.h"
 | 
			
		||||
#include "util/message.h"
 | 
			
		||||
#include "util/messagequeue.h"
 | 
			
		||||
 | 
			
		||||
#include "morsedecodersettings.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MorseDecoderWorker : public QObject {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
public:
 | 
			
		||||
    class MsgConfigureMorseDecoderWorker : public Message {
 | 
			
		||||
        MESSAGE_CLASS_DECLARATION
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        const MorseDecoderSettings& getSettings() const { return m_settings; }
 | 
			
		||||
        const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
 | 
			
		||||
        bool getForce() const { return m_force; }
 | 
			
		||||
 | 
			
		||||
        static MsgConfigureMorseDecoderWorker* create(const MorseDecoderSettings& settings, const QList<QString>& settingsKeys, bool force)
 | 
			
		||||
        {
 | 
			
		||||
            return new MsgConfigureMorseDecoderWorker(settings, settingsKeys, force);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        MorseDecoderSettings m_settings;
 | 
			
		||||
        QList<QString> m_settingsKeys;
 | 
			
		||||
        bool m_force;
 | 
			
		||||
 | 
			
		||||
        MsgConfigureMorseDecoderWorker(const MorseDecoderSettings& settings, const QList<QString>& 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)
 | 
			
		||||
        { }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MorseDecoderWorker();
 | 
			
		||||
	~MorseDecoderWorker();
 | 
			
		||||
    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 MorseDecoderSettings& settings, const QList<QString>& settingsKeys, bool force = false);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    DataFifo *m_dataFifo;
 | 
			
		||||
    int m_channelSampleRate;
 | 
			
		||||
    int m_sinkSampleRate;
 | 
			
		||||
	MessageQueue m_inputMessageQueue;  //!< Queue for asynchronous inbound communication
 | 
			
		||||
    MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
 | 
			
		||||
    MorseDecoderSettings m_settings;
 | 
			
		||||
	double m_magsq;
 | 
			
		||||
	SampleVector m_sampleBuffer;
 | 
			
		||||
    int m_sampleBufferSize;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    void handleInputMessages();
 | 
			
		||||
    void handleData(); //!< Handle data when samples have to be processed
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // INCLUDE_FEATURE_MORSEDECODERWORKER_H_
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue