FT8 demod: copy from SSB demod

This commit is contained in:
f4exb 2023-01-15 01:22:57 +01:00
parent 9350bcd949
commit 6444d749d8
38 changed files with 6523 additions and 4 deletions

View File

@ -80,6 +80,7 @@ option(ENABLE_CHANNELRX_LOCALSINK "Enable channelrx localsink plugin" ON)
option(ENABLE_CHANNELRX_DEMODPACKET "Enable channelrx demodpacket plugin" ON)
option(ENABLE_CHANNELRX_DEMODAPT "Enable channelrx demodapt plugin" ON)
option(ENABLE_CHANNELRX_DEMODDSD "Enable channelrx demoddsd plugin" ON)
option(ENABLE_CHANNELRX_DEMODFT8 "Enable channelrx demodft8 plugin" ON)
# Channel Tx enablers
option(ENABLE_CHANNELTX "Enable channeltx plugins" ON)

View File

@ -109,6 +109,10 @@ if (ENABLE_CHANNELRX_NOISEFIGURE AND Boost_FOUND AND Boost_VERSION_STRING VERSIO
add_subdirectory(noisefigure)
endif()
if (ENABLE_CHANNELRX_DEMODFT8 AND FT8_SUPPORT)
add_subdirectory(demodft8)
endif()
if(NOT SERVER_MODE)
if (ENABLE_CHANNELRX_CHANALYZER)
add_subdirectory(chanalyzer)

View File

@ -0,0 +1,63 @@
project(demodft8)
set(demodft8_SOURCES
ft8demod.cpp
ft8demodsettings.cpp
ft8demodsink.cpp
ft8demodbaseband.cpp
ft8demodwebapiadapter.cpp
ft8plugin.cpp
)
set(demodft8_HEADERS
ft8demod.h
ft8demodsettings.h
ft8demodsink.h
ft8demodbaseband.h
ft8demodwebapiadapter.h
ft8plugin.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(demodft8_SOURCES
${demodft8_SOURCES}
ft8demodgui.cpp
ft8demodgui.ui
)
set(demodft8_HEADERS
${demodft8_HEADERS}
ft8demodgui.h
)
set(TARGET_NAME demodft8)
set(TARGET_LIB "Qt::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demodft8srv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${demodft8_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,847 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <QMutexLocker>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGFT8DemodSettings.h"
#include "SWGChannelReport.h"
#include "SWGFT8DemodReport.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/devicesamplemimo.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "util/db.h"
#include "maincore.h"
#include "ft8demod.h"
MESSAGE_CLASS_DEFINITION(FT8Demod::MsgConfigureFT8Demod, Message)
const char* const FT8Demod::m_channelIdURI = "sdrangel.channel.ft8demod";
const char* const FT8Demod::m_channelId = "FT8Demod";
FT8Demod::FT8Demod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_running(false),
m_spectrumVis(SDR_RX_SCALEF),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&FT8Demod::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&FT8Demod::handleIndexInDeviceSetChanged
);
start();
}
FT8Demod::~FT8Demod()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&FT8Demod::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
stop();
}
void FT8Demod::setDeviceAPI(DeviceAPI *deviceAPI)
{
if (deviceAPI != m_deviceAPI)
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
m_deviceAPI = deviceAPI;
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
}
}
uint32_t FT8Demod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void FT8Demod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly)
{
(void) positiveOnly;
if (m_running) {
m_basebandSink->feed(begin, end);
}
}
void FT8Demod::start()
{
QMutexLocker m_lock(&m_mutex);
if (m_running) {
return;
}
qDebug() << "FT8Demod::start";
m_thread = new QThread();
m_basebandSink = new FT8DemodBaseband();
m_basebandSink->setFifoLabel(QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(getIndexInDeviceSet())
);
m_basebandSink->setSpectrumSink(&m_spectrumVis);
m_basebandSink->setChannel(this);
m_basebandSink->setMessageQueueToGUI(getMessageQueueToGUI());
m_basebandSink->moveToThread(m_thread);
QObject::connect(
m_thread,
&QThread::finished,
m_basebandSink,
&QObject::deleteLater
);
QObject::connect(
m_thread,
&QThread::finished,
m_thread,
&QThread::deleteLater
);
if (m_basebandSampleRate != 0) {
m_basebandSink->setBasebandSampleRate(m_basebandSampleRate);
}
m_thread->start();
FT8DemodBaseband::MsgConfigureFT8DemodBaseband *msg = FT8DemodBaseband::MsgConfigureFT8DemodBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
m_running = true;
}
void FT8Demod::stop()
{
QMutexLocker m_lock(&m_mutex);
if (!m_running) {
return;
}
qDebug() << "FT8Demod::stop";
m_running = false;
m_thread->exit();
m_thread->wait();
}
bool FT8Demod::handleMessage(const Message& cmd)
{
if (MsgConfigureFT8Demod::match(cmd))
{
MsgConfigureFT8Demod& cfg = (MsgConfigureFT8Demod&) cmd;
qDebug("FT8Demod::handleMessage: MsgConfigureFT8Demod");
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
qDebug() << "FT8Demod::handleMessage: DSPSignalNotification";
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
// Forward to the sink
if (m_running) {
m_basebandSink->getInputMessageQueue()->push(new DSPSignalNotification(notif));
}
// Forwatd to GUI if any
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(new DSPSignalNotification(notif));
}
return true;
}
else if (MainCore::MsgChannelDemodQuery::match(cmd))
{
qDebug() << "FT8Demod::handleMessage: MsgChannelDemodQuery";
sendSampleRateToDemodAnalyzer();
return true;
}
else
{
return false;
}
}
void FT8Demod::setCenterFrequency(qint64 frequency)
{
FT8DemodSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureFT8Demod *msgToGUI = MsgConfigureFT8Demod::create(settings, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force)
{
qDebug() << "FT8Demod::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_filterIndex: " << settings.m_filterIndex
<< " [m_spanLog2: " << settings.m_filterBank[settings.m_filterIndex].m_spanLog2
<< " m_rfBandwidth: " << settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth
<< " m_lowCutoff: " << settings.m_filterBank[settings.m_filterIndex].m_lowCutoff
<< " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
<< " m_volume: " << settings.m_volume
<< " m_audioBinaual: " << settings.m_audioBinaural
<< " m_audioFlipChannels: " << settings.m_audioFlipChannels
<< " m_dsb: " << settings.m_dsb
<< " m_audioMute: " << settings.m_audioMute
<< " m_agcActive: " << settings.m_agc
<< " m_agcClamping: " << settings.m_agcClamping
<< " m_agcTimeLog2: " << settings.m_agcTimeLog2
<< " agcPowerThreshold: " << settings.m_agcPowerThreshold
<< " agcThresholdGate: " << settings.m_agcThresholdGate
<< " m_audioDeviceName: " << settings.m_audioDeviceName
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((m_settings.m_filterIndex != settings.m_filterIndex) || force) {
reverseAPIKeys.append("filterIndex");
}
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2 != settings.m_filterBank[settings.m_filterIndex].m_spanLog2) || force) {
reverseAPIKeys.append("spanLog2");
}
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth != settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff != settings.m_filterBank[settings.m_filterIndex].m_lowCutoff) || force) {
reverseAPIKeys.append("lowCutoff");
}
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow != settings.m_filterBank[settings.m_filterIndex].m_fftWindow) || force) {
reverseAPIKeys.append("fftWindow");
}
if ((m_settings.m_volume != settings.m_volume) || force) {
reverseAPIKeys.append("volume");
}
if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || force) {
reverseAPIKeys.append("agcTimeLog2");
}
if ((m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || force) {
reverseAPIKeys.append("agcPowerThreshold");
}
if ((m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || force) {
reverseAPIKeys.append("agcThresholdGate");
}
if ((m_settings.m_agcClamping != settings.m_agcClamping) || force) {
reverseAPIKeys.append("agcClamping");
}
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) {
reverseAPIKeys.append("audioDeviceName");
}
if ((m_settings.m_audioBinaural != settings.m_audioBinaural) || force) {
reverseAPIKeys.append("audioBinaural");
}
if ((m_settings.m_audioFlipChannels != settings.m_audioFlipChannels) || force) {
reverseAPIKeys.append("audioFlipChannels");
}
if ((m_settings.m_dsb != settings.m_dsb) || force) {
reverseAPIKeys.append("dsb");
}
if ((m_settings.m_audioMute != settings.m_audioMute) || force) {
reverseAPIKeys.append("audioMute");
}
if ((m_settings.m_agc != settings.m_agc) || force) {
reverseAPIKeys.append("agc");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
if ((settings.m_dsb != m_settings.m_dsb)
|| (settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth != m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth)
|| (settings.m_filterBank[settings.m_filterIndex].m_lowCutoff != m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff) || force)
{
SpectrumSettings spectrumSettings = m_spectrumVis.getSettings();
spectrumSettings.m_ssb = !settings.m_dsb;
spectrumSettings.m_usb = (settings.m_filterBank[settings.m_filterIndex].m_lowCutoff < settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth);
SpectrumVis::MsgConfigureSpectrumVis *msg = SpectrumVis::MsgConfigureSpectrumVis::create(spectrumSettings, false);
m_spectrumVis.getInputMessageQueue()->push(msg);
}
if (m_running)
{
FT8DemodBaseband::MsgConfigureFT8DemodBaseband *msg = FT8DemodBaseband::MsgConfigureFT8DemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "settings", pipes);
if (pipes.size() > 0) {
sendChannelSettings(pipes, reverseAPIKeys, settings, force);
}
m_settings = settings;
}
QByteArray FT8Demod::serialize() const
{
return m_settings.serialize();
}
bool FT8Demod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureFT8Demod *msg = MsgConfigureFT8Demod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureFT8Demod *msg = MsgConfigureFT8Demod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void FT8Demod::sendSampleRateToDemodAnalyzer()
{
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "reportdemod", pipes);
if (pipes.size() > 0)
{
for (const auto& pipe: pipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
if (messageQueue)
{
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
this,
getAudioSampleRate()
);
messageQueue->push(msg);
}
}
}
}
int FT8Demod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFt8DemodSettings(new SWGSDRangel::SWGFT8DemodSettings());
response.getFt8DemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int FT8Demod::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int FT8Demod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
FT8DemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureFT8Demod *msg = MsgConfigureFT8Demod::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("FT8Demod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureFT8Demod *msgToGUI = MsgConfigureFT8Demod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void FT8Demod::webapiUpdateChannelSettings(
FT8DemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getFt8DemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("filterIndex")) {
settings.m_filterIndex = response.getFt8DemodSettings()->getFilterIndex();
}
if (channelSettingsKeys.contains("spanLog2")) {
settings.m_filterBank[settings.m_filterIndex].m_spanLog2 = response.getFt8DemodSettings()->getSpanLog2();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth = response.getFt8DemodSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("lowCutoff")) {
settings.m_filterBank[settings.m_filterIndex].m_lowCutoff = response.getFt8DemodSettings()->getLowCutoff();
}
if (channelSettingsKeys.contains("fftWimdow")) {
settings.m_filterBank[settings.m_filterIndex].m_fftWindow = (FFTWindow::Function) response.getFt8DemodSettings()->getFftWindow();
}
if (channelSettingsKeys.contains("volume")) {
settings.m_volume = response.getFt8DemodSettings()->getVolume();
}
if (channelSettingsKeys.contains("audioBinaural")) {
settings.m_audioBinaural = response.getFt8DemodSettings()->getAudioBinaural() != 0;
}
if (channelSettingsKeys.contains("audioFlipChannels")) {
settings.m_audioFlipChannels = response.getFt8DemodSettings()->getAudioFlipChannels() != 0;
}
if (channelSettingsKeys.contains("dsb")) {
settings.m_dsb = response.getFt8DemodSettings()->getDsb() != 0;
}
if (channelSettingsKeys.contains("audioMute")) {
settings.m_audioMute = response.getFt8DemodSettings()->getAudioMute() != 0;
}
if (channelSettingsKeys.contains("agc")) {
settings.m_agc = response.getFt8DemodSettings()->getAgc() != 0;
}
if (channelSettingsKeys.contains("agcClamping")) {
settings.m_agcClamping = response.getFt8DemodSettings()->getAgcClamping() != 0;
}
if (channelSettingsKeys.contains("agcTimeLog2")) {
settings.m_agcTimeLog2 = response.getFt8DemodSettings()->getAgcTimeLog2();
}
if (channelSettingsKeys.contains("agcPowerThreshold")) {
settings.m_agcPowerThreshold = response.getFt8DemodSettings()->getAgcPowerThreshold();
}
if (channelSettingsKeys.contains("agcThresholdGate")) {
settings.m_agcThresholdGate = response.getFt8DemodSettings()->getAgcThresholdGate();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFt8DemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getFt8DemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("audioDeviceName")) {
settings.m_audioDeviceName = *response.getFt8DemodSettings()->getAudioDeviceName();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getFt8DemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getFt8DemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getFt8DemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getFt8DemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getFt8DemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getFt8DemodSettings()->getReverseApiChannelIndex();
}
if (settings.m_spectrumGUI && channelSettingsKeys.contains("spectrumConfig")) {
settings.m_spectrumGUI->updateFrom(channelSettingsKeys, response.getFt8DemodSettings()->getSpectrumConfig());
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getFt8DemodSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getFt8DemodSettings()->getRollupState());
}
}
int FT8Demod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFt8DemodReport(new SWGSDRangel::SWGFT8DemodReport());
response.getFt8DemodReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FT8DemodSettings& settings)
{
response.getFt8DemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0);
response.getFt8DemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getFt8DemodSettings()->setFilterIndex(settings.m_filterIndex);
response.getFt8DemodSettings()->setSpanLog2(settings.m_filterBank[settings.m_filterIndex].m_spanLog2);
response.getFt8DemodSettings()->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth);
response.getFt8DemodSettings()->setLowCutoff(settings.m_filterBank[settings.m_filterIndex].m_lowCutoff);
response.getFt8DemodSettings()->setFftWindow((int) settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
response.getFt8DemodSettings()->setVolume(settings.m_volume);
response.getFt8DemodSettings()->setAudioBinaural(settings.m_audioBinaural ? 1 : 0);
response.getFt8DemodSettings()->setAudioFlipChannels(settings.m_audioFlipChannels ? 1 : 0);
response.getFt8DemodSettings()->setDsb(settings.m_dsb ? 1 : 0);
response.getFt8DemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0);
response.getFt8DemodSettings()->setAgc(settings.m_agc ? 1 : 0);
response.getFt8DemodSettings()->setAgcClamping(settings.m_agcClamping ? 1 : 0);
response.getFt8DemodSettings()->setAgcTimeLog2(settings.m_agcTimeLog2);
response.getFt8DemodSettings()->setAgcPowerThreshold(settings.m_agcPowerThreshold);
response.getFt8DemodSettings()->setAgcThresholdGate(settings.m_agcThresholdGate);
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getFt8DemodSettings()->getTitle()) {
*response.getFt8DemodSettings()->getTitle() = settings.m_title;
} else {
response.getFt8DemodSettings()->setTitle(new QString(settings.m_title));
}
if (response.getFt8DemodSettings()->getAudioDeviceName()) {
*response.getFt8DemodSettings()->getAudioDeviceName() = settings.m_audioDeviceName;
} else {
response.getFt8DemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
}
response.getFt8DemodSettings()->setStreamIndex(settings.m_streamIndex);
response.getFt8DemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getFt8DemodSettings()->getReverseApiAddress()) {
*response.getFt8DemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getFt8DemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getFt8DemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getFt8DemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getFt8DemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_spectrumGUI)
{
if (response.getFt8DemodSettings()->getSpectrumConfig())
{
settings.m_spectrumGUI->formatTo(response.getFt8DemodSettings()->getSpectrumConfig());
}
else
{
SWGSDRangel::SWGGLSpectrum *swgGLSpectrum = new SWGSDRangel::SWGGLSpectrum();
settings.m_spectrumGUI->formatTo(swgGLSpectrum);
response.getFt8DemodSettings()->setSpectrumConfig(swgGLSpectrum);
}
}
if (settings.m_channelMarker)
{
if (response.getFt8DemodSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getFt8DemodSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getFt8DemodSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getFt8DemodSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getFt8DemodSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getFt8DemodSettings()->setRollupState(swgRollupState);
}
}
}
void FT8Demod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
response.getFt8DemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
if (m_running)
{
response.getFt8DemodReport()->setSquelch(m_basebandSink->getAudioActive() ? 1 : 0);
response.getFt8DemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate());
response.getFt8DemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
}
}
void FT8Demod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const FT8DemodSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgChannelSettings;
}
void FT8Demod::sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const FT8DemodSettings& settings,
bool force)
{
qDebug("FT8Demod::sendChannelSettings: %d pipes", pipes.size());
for (const auto& pipe : pipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
if (messageQueue)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
MainCore::MsgChannelSettings *msg = MainCore::MsgChannelSettings::create(
this,
channelSettingsKeys,
swgChannelSettings,
force
);
messageQueue->push(msg);
}
}
}
void FT8Demod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const FT8DemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString(m_channelId));
swgChannelSettings->setFt8DemodSettings(new SWGSDRangel::SWGFT8DemodSettings());
SWGSDRangel::SWGFT8DemodSettings *swgFT8DemodSettings = swgChannelSettings->getFt8DemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgFT8DemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("filteIndex") || force) {
swgFT8DemodSettings->setFilterIndex(settings.m_filterIndex);
}
if (channelSettingsKeys.contains("spanLog2") || force) {
swgFT8DemodSettings->setSpanLog2(settings.m_filterBank[settings.m_filterIndex].m_spanLog2);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgFT8DemodSettings->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth);
}
if (channelSettingsKeys.contains("lowCutoff") || force) {
swgFT8DemodSettings->setLowCutoff(settings.m_filterBank[settings.m_filterIndex].m_lowCutoff);
}
if (channelSettingsKeys.contains("fftWindow") || force) {
swgFT8DemodSettings->setLowCutoff(settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
}
if (channelSettingsKeys.contains("volume") || force) {
swgFT8DemodSettings->setVolume(settings.m_volume);
}
if (channelSettingsKeys.contains("audioBinaural") || force) {
swgFT8DemodSettings->setAudioBinaural(settings.m_audioBinaural ? 1 : 0);
}
if (channelSettingsKeys.contains("audioFlipChannels") || force) {
swgFT8DemodSettings->setAudioFlipChannels(settings.m_audioFlipChannels ? 1 : 0);
}
if (channelSettingsKeys.contains("dsb") || force) {
swgFT8DemodSettings->setDsb(settings.m_dsb ? 1 : 0);
}
if (channelSettingsKeys.contains("audioMute") || force) {
swgFT8DemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0);
}
if (channelSettingsKeys.contains("agc") || force) {
swgFT8DemodSettings->setAgc(settings.m_agc ? 1 : 0);
}
if (channelSettingsKeys.contains("agcClamping") || force) {
swgFT8DemodSettings->setAgcClamping(settings.m_agcClamping ? 1 : 0);
}
if (channelSettingsKeys.contains("agcTimeLog2") || force) {
swgFT8DemodSettings->setAgcTimeLog2(settings.m_agcTimeLog2);
}
if (channelSettingsKeys.contains("agcPowerThreshold") || force) {
swgFT8DemodSettings->setAgcPowerThreshold(settings.m_agcPowerThreshold);
}
if (channelSettingsKeys.contains("agcThresholdGate") || force) {
swgFT8DemodSettings->setAgcThresholdGate(settings.m_agcThresholdGate);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFT8DemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgFT8DemodSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("audioDeviceName") || force) {
swgFT8DemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgFT8DemodSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_spectrumGUI && (channelSettingsKeys.contains("spectrunConfig") || force))
{
SWGSDRangel::SWGGLSpectrum *swgGLSpectrum = new SWGSDRangel::SWGGLSpectrum();
settings.m_spectrumGUI->formatTo(swgGLSpectrum);
swgFT8DemodSettings->setSpectrumConfig(swgGLSpectrum);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgFT8DemodSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRolllupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRolllupState);
swgFT8DemodSettings->setRollupState(swgRolllupState);
}
}
void FT8Demod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "FT8Demod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("FT8Demod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void FT8Demod::handleIndexInDeviceSetChanged(int index)
{
if (!m_running || (index < 0)) {
return;
}
QString fifoLabel = QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(index);
m_basebandSink->setFifoLabel(fifoLabel);
m_basebandSink->setAudioFifoLabel(fifoLabel);
}

View File

@ -0,0 +1,184 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FT8DEMOD_H
#define INCLUDE_FT8DEMOD_H
#include <vector>
#include <QRecursiveMutex>
#include <QNetworkRequest>
#include "dsp/basebandsamplesink.h"
#include "dsp/spectrumvis.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "ft8demodsettings.h"
#include "ft8demodbaseband.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class ObjectPipe;
class FT8Demod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureFT8Demod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const FT8DemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureFT8Demod* create(const FT8DemodSettings& settings, bool force)
{
return new MsgConfigureFT8Demod(settings, force);
}
private:
FT8DemodSettings m_settings;
bool m_force;
MsgConfigureFT8Demod(const FT8DemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
FT8Demod(DeviceAPI *deviceAPI);
virtual ~FT8Demod();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
SpectrumVis *getSpectrumVis() { return &m_spectrumVis; }
using BasebandSampleSink::feed;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); }
virtual QString getSinkName() { return objectName(); }
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
virtual void setCenterFrequency(qint64 frequency);
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return m_settings.m_inputFrequencyOffset;
}
void setMessageQueueToGUI(MessageQueue* queue) override { ChannelAPI::setMessageQueueToGUI(queue); }
uint32_t getAudioSampleRate() const { return m_running ? m_basebandSink->getAudioSampleRate() : 0; }
uint32_t getChannelSampleRate() const { return m_running ? m_basebandSink->getChannelSampleRate() : 0; }
double getMagSq() const { return m_running ? m_basebandSink->getMagSq() : 0.0; }
bool getAudioActive() const { return m_running && m_basebandSink->getAudioActive(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_running) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
} else {
avg = 0.0; peak = 0.0; nbSamples = 1;
}
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const FT8DemodSettings& settings);
static void webapiUpdateChannelSettings(
FT8DemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
uint32_t getNumberOfDeviceStreams() const;
static const char* const m_channelIdURI;
static const char* const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread *m_thread;
FT8DemodBaseband* m_basebandSink;
QRecursiveMutex m_mutex;
bool m_running;
FT8DemodSettings m_settings;
SpectrumVis m_spectrumVis;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
virtual bool handleMessage(const Message& cmd);
void applySettings(const FT8DemodSettings& settings, bool force = false);
void sendSampleRateToDemodAnalyzer();
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const FT8DemodSettings& settings, bool force);
void sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const FT8DemodSettings& settings,
bool force
);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const FT8DemodSettings& settings,
bool force
);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);
};
#endif // INCLUDE_FT8DEMOD_H

View File

@ -0,0 +1,215 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/spectrumvis.h"
#include "ft8demodbaseband.h"
MESSAGE_CLASS_DEFINITION(FT8DemodBaseband::MsgConfigureFT8DemodBaseband, Message)
FT8DemodBaseband::FT8DemodBaseband() :
m_channelizer(&m_sink),
m_messageQueueToGUI(nullptr),
m_spectrumVis(nullptr)
{
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
qDebug("FT8DemodBaseband::FT8DemodBaseband");
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&FT8DemodBaseband::handleData,
Qt::QueuedConnection
);
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue());
m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate();
m_sink.applyAudioSampleRate(m_audioSampleRate);
m_channelSampleRate = 0;
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
FT8DemodBaseband::~FT8DemodBaseband()
{
DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo());
}
void FT8DemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate());
m_sampleFifo.reset();
m_channelSampleRate = 0;
}
void FT8DemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void FT8DemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void FT8DemodBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
{
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if (part1begin != part1end) {
m_channelizer.feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
m_channelizer.feed(part2begin, part2end);
}
m_sampleFifo.readCommit((unsigned int) count);
}
}
void FT8DemodBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool FT8DemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureFT8DemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureFT8DemodBaseband& cfg = (MsgConfigureFT8DemodBaseband&) cmd;
qDebug() << "FT8DemodBaseband::handleMessage: MsgConfigureFT8DemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "FT8DemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
m_channelizer.setBasebandSampleRate(notif.getSampleRate());
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
if (m_channelSampleRate != m_channelizer.getChannelSampleRate())
{
m_sink.applyAudioSampleRate(m_audioSampleRate); // reapply when channel sample rate changes
m_channelSampleRate = m_channelizer.getChannelSampleRate();
}
return true;
}
else
{
return false;
}
}
void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool force)
{
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer.setChannelization(m_audioSampleRate, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
if (m_channelSampleRate != m_channelizer.getChannelSampleRate())
{
m_sink.applyAudioSampleRate(m_audioSampleRate); // reapply when channel sample rate changes
m_channelSampleRate = m_channelizer.getChannelSampleRate();
}
}
if ((settings.m_filterBank[settings.m_filterIndex].m_spanLog2 != m_settings.m_filterBank[settings.m_filterIndex].m_spanLog2) || force)
{
if (m_spectrumVis)
{
DSPSignalNotification *msg = new DSPSignalNotification(m_audioSampleRate/(1<<settings.m_filterBank[settings.m_filterIndex].m_spanLog2), 0);
m_spectrumVis->getInputMessageQueue()->push(msg);
}
}
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
{
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName);
audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex);
unsigned int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
if (m_audioSampleRate != audioSampleRate)
{
m_sink.applyAudioSampleRate(audioSampleRate);
m_channelizer.setChannelization(audioSampleRate, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
m_audioSampleRate = audioSampleRate;
if (getMessageQueueToGUI())
{
DSPConfigureAudio *msg = new DSPConfigureAudio((int) audioSampleRate, DSPConfigureAudio::AudioOutput);
getMessageQueueToGUI()->push(msg);
}
if (m_spectrumVis)
{
DSPSignalNotification *msg = new DSPSignalNotification(m_audioSampleRate/(1<<m_settings.m_filterBank[settings.m_filterIndex].m_spanLog2), 0);
m_spectrumVis->getInputMessageQueue()->push(msg);
}
}
}
m_sink.applySettings(settings, force);
m_settings = settings;
}
int FT8DemodBaseband::getChannelSampleRate() const
{
return m_channelizer.getChannelSampleRate();
}
void FT8DemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer.setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer.getChannelSampleRate(), m_channelizer.getChannelFrequencyOffset());
}

View File

@ -0,0 +1,100 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FT8DEMODBASEBAND_H
#define INCLUDE_FT8DEMODBASEBAND_H
#include <QObject>
#include <QRecursiveMutex>
#include "dsp/samplesinkfifo.h"
#include "dsp/downchannelizer.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "ft8demodsettings.h"
#include "ft8demodsink.h"
class ChannelAPI;
class SpectrumVis;
class FT8DemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureFT8DemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const FT8DemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureFT8DemodBaseband* create(const FT8DemodSettings& settings, bool force)
{
return new MsgConfigureFT8DemodBaseband(settings, force);
}
private:
FT8DemodSettings m_settings;
bool m_force;
MsgConfigureFT8DemodBaseband(const FT8DemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
FT8DemodBaseband();
~FT8DemodBaseband();
void reset();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
int getChannelSampleRate() const;
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumVis = spectrumSink; m_sink.setSpectrumSink(spectrumSink); }
double getMagSq() const { return m_sink.getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); }
unsigned int getAudioSampleRate() const { return m_audioSampleRate; }
bool getAudioActive() const { return m_sink.getAudioActive(); }
void setBasebandSampleRate(int sampleRate);
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
void setChannel(ChannelAPI *channel);
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer m_channelizer;
FT8DemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
FT8DemodSettings m_settings;
unsigned int m_audioSampleRate;
int m_channelSampleRate;
MessageQueue *m_messageQueueToGUI;
SpectrumVis *m_spectrumVis;
QRecursiveMutex m_mutex;
bool handleMessage(const Message& cmd);
void applySettings(const FT8DemodSettings& settings, bool force = false);
MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; }
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_SSBDEMODBASEBAND_H

View File

@ -0,0 +1,770 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QPixmap>
#include "plugin/pluginapi.h"
#include "device/deviceuiset.h"
#include "dsp/spectrumvis.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "util/simpleserializer.h"
#include "gui/glspectrum.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "gui/crightclickenabler.h"
#include "gui/audioselectdialog.h"
#include "gui/dialpopup.h"
#include "gui/dialogpositioner.h"
#include "util/db.h"
#include "maincore.h"
#include "ui_ft8demodgui.h"
#include "ft8demodgui.h"
#include "ft8demod.h"
FT8DemodGUI* FT8DemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
FT8DemodGUI* gui = new FT8DemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void FT8DemodGUI::destroy()
{
delete this;
}
void FT8DemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
}
QByteArray FT8DemodGUI::serialize() const
{
return m_settings.serialize();
}
bool FT8DemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data))
{
ui->BW->setMaximum(480);
ui->BW->setMinimum(-480);
ui->lowCut->setMaximum(480);
ui->lowCut->setMinimum(-480);
displaySettings();
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
return true;
}
else
{
m_settings.resetToDefaults();
ui->BW->setMaximum(480);
ui->BW->setMinimum(-480);
ui->lowCut->setMaximum(480);
ui->lowCut->setMinimum(-480);
displaySettings();
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
return false;
}
}
bool FT8DemodGUI::handleMessage(const Message& message)
{
if (FT8Demod::MsgConfigureFT8Demod::match(message))
{
qDebug("FT8DemodGUI::handleMessage: FT8Demod::MsgConfigureFT8Demod");
const FT8Demod::MsgConfigureFT8Demod& cfg = (FT8Demod::MsgConfigureFT8Demod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
ui->spectrumGUI->updateSettings();
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPConfigureAudio::match(message))
{
qDebug("FT8DemodGUI::handleMessage: DSPConfigureAudio: %d", m_ft8Demod->getAudioSampleRate());
applyBandwidths(1 + ui->spanLog2->maximum() - ui->spanLog2->value()); // will update spectrum details with new sample rate
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPSignalNotification::match(message))
{
const DSPSignalNotification& notif = (const DSPSignalNotification&) message;
m_deviceCenterFrequency = notif.getCenterFrequency();
m_basebandSampleRate = notif.getSampleRate();
ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2));
updateAbsoluteCenterFrequency();
return true;
}
else
{
return false;
}
}
void FT8DemodGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void FT8DemodGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void FT8DemodGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void FT8DemodGUI::on_audioBinaural_toggled(bool binaural)
{
m_audioBinaural = binaural;
m_settings.m_audioBinaural = binaural;
applySettings();
}
void FT8DemodGUI::on_audioFlipChannels_toggled(bool flip)
{
m_audioFlipChannels = flip;
m_settings.m_audioFlipChannels = flip;
applySettings();
}
void FT8DemodGUI::on_dsb_toggled(bool dsb)
{
ui->flipSidebands->setEnabled(!dsb);
applyBandwidths(1 + ui->spanLog2->maximum() - ui->spanLog2->value());
}
void FT8DemodGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
updateAbsoluteCenterFrequency();
applySettings();
}
void FT8DemodGUI::on_BW_valueChanged(int value)
{
(void) value;
qDebug("FT8DemodGUI::on_BW_valueChanged: ui->spanLog2: %d", ui->spanLog2->value());
applyBandwidths(1 + ui->spanLog2->maximum() - ui->spanLog2->value());
}
void FT8DemodGUI::on_lowCut_valueChanged(int value)
{
(void) value;
applyBandwidths(1 + ui->spanLog2->maximum() - ui->spanLog2->value());
}
void FT8DemodGUI::on_volume_valueChanged(int value)
{
ui->volumeText->setText(QString("%1").arg(value));
m_settings.m_volume = CalcDb::powerFromdB(value);
applySettings();
}
void FT8DemodGUI::on_agc_toggled(bool checked)
{
m_settings.m_agc = checked;
applySettings();
}
void FT8DemodGUI::on_agcClamping_toggled(bool checked)
{
m_settings.m_agcClamping = checked;
applySettings();
}
void FT8DemodGUI::on_agcTimeLog2_valueChanged(int value)
{
QString s = QString::number((1<<value), 'f', 0);
ui->agcTimeText->setText(s);
m_settings.m_agcTimeLog2 = value;
applySettings();
}
void FT8DemodGUI::on_agcPowerThreshold_valueChanged(int value)
{
displayAGCPowerThreshold(value);
m_settings.m_agcPowerThreshold = value;
applySettings();
}
void FT8DemodGUI::on_agcThresholdGate_valueChanged(int value)
{
int agcThresholdGate = value < 20 ? value : ((value - 20) * 10) + 20;
QString s = QString::number(agcThresholdGate, 'f', 0);
ui->agcThresholdGateText->setText(s);
m_settings.m_agcThresholdGate = agcThresholdGate;
applySettings();
}
void FT8DemodGUI::on_audioMute_toggled(bool checked)
{
m_audioMute = checked;
m_settings.m_audioMute = checked;
applySettings();
}
void FT8DemodGUI::on_spanLog2_valueChanged(int value)
{
int s2max = spanLog2Max();
if ((value < 0) || (value > s2max-1)) {
return;
}
applyBandwidths(s2max - ui->spanLog2->value());
}
void FT8DemodGUI::on_flipSidebands_clicked(bool checked)
{
(void) checked;
int bwValue = ui->BW->value();
int lcValue = ui->lowCut->value();
ui->BW->setValue(-bwValue);
ui->lowCut->setValue(-lcValue);
}
void FT8DemodGUI::on_fftWindow_currentIndexChanged(int index)
{
m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow = (FFTWindow::Function) index;
applySettings();
}
void FT8DemodGUI::on_filterIndex_valueChanged(int value)
{
if ((value < 0) || (value >= 10)) {
return;
}
ui->filterIndexText->setText(tr("%1").arg(value));
m_settings.m_filterIndex = value;
ui->BW->setMaximum(480);
ui->BW->setMinimum(-480);
ui->lowCut->setMaximum(480);
ui->lowCut->setMinimum(-480);
displaySettings();
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
}
void FT8DemodGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.setDefaultTitle(m_displayedName);
if (m_deviceUISet->m_deviceMIMOEngine)
{
dialog.setNumberOfStreams(m_ft8Demod->getNumberOfDeviceStreams());
dialog.setStreamIndex(m_settings.m_streamIndex);
}
dialog.move(p);
new DialogPositioner(&dialog, false);
dialog.exec();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitle(m_channelMarker.getTitle());
setTitleColor(m_settings.m_rgbColor);
if (m_deviceUISet->m_deviceMIMOEngine)
{
m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
m_channelMarker.clearStreamIndexes();
m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
updateIndexLabel();
}
applySettings();
}
resetContextMenuType();
}
void FT8DemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
getRollupContents()->saveState(m_rollupState);
applySettings();
}
FT8DemodGUI::FT8DemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::FT8DemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_deviceCenterFrequency(0),
m_basebandSampleRate(1),
m_doApplySettings(true),
m_spectrumRate(6000),
m_audioBinaural(false),
m_audioFlipChannels(false),
m_audioMute(false),
m_squelchOpen(false),
m_audioSampleRate(-1)
{
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/channelrx/demodssb/readme.md";
RollupContents *rollupContents = getRollupContents();
ui->setupUi(rollupContents);
setSizePolicy(rollupContents->sizePolicy());
rollupContents->arrangeRollups();
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_ft8Demod = (FT8Demod*) rxChannel;
m_spectrumVis = m_ft8Demod->getSpectrumVis();
m_spectrumVis->setGLSpectrum(ui->glSpectrum);
m_ft8Demod->setMessageQueueToGUI(getInputMessageQueue());
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect(const QPoint &)));
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
ui->glSpectrum->setCenterFrequency(m_spectrumRate/2);
ui->glSpectrum->setSampleRate(m_spectrumRate);
ui->glSpectrum->setDisplayWaterfall(true);
ui->glSpectrum->setDisplayMaxHold(true);
ui->glSpectrum->setSsbSpectrum(true);
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::green);
m_channelMarker.setBandwidth(6000);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle("SSB Demodulator");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
setTitleColor(m_channelMarker.getColor());
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setSpectrumGUI(ui->spectrumGUI);
m_settings.setRollupState(&m_rollupState);
m_deviceUISet->addChannelMarker(&m_channelMarker);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum);
m_iconDSBUSB.addPixmap(QPixmap("://dsb.png"), QIcon::Normal, QIcon::On);
m_iconDSBUSB.addPixmap(QPixmap("://usb.png"), QIcon::Normal, QIcon::Off);
m_iconDSBLSB.addPixmap(QPixmap("://dsb.png"), QIcon::Normal, QIcon::On);
m_iconDSBLSB.addPixmap(QPixmap("://lsb.png"), QIcon::Normal, QIcon::Off);
ui->BW->setMaximum(480);
ui->BW->setMinimum(-480);
ui->lowCut->setMaximum(480);
ui->lowCut->setMinimum(-480);
displaySettings();
makeUIConnections();
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
DialPopup::addPopupsToChildDials(this);
}
FT8DemodGUI::~FT8DemodGUI()
{
delete ui;
}
bool FT8DemodGUI::blockApplySettings(bool block)
{
bool ret = !m_doApplySettings;
m_doApplySettings = !block;
return ret;
}
void FT8DemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
FT8Demod::MsgConfigureFT8Demod* message = FT8Demod::MsgConfigureFT8Demod::create( m_settings, force);
m_ft8Demod->getInputMessageQueue()->push(message);
}
}
unsigned int FT8DemodGUI::spanLog2Max()
{
unsigned int spanLog2 = 0;
for (; m_ft8Demod->getAudioSampleRate() / (1<<spanLog2) >= 1000; spanLog2++);
return spanLog2 == 0 ? 0 : spanLog2-1;
}
void FT8DemodGUI::applyBandwidths(unsigned int spanLog2, bool force)
{
unsigned int s2max = spanLog2Max();
spanLog2 = spanLog2 > s2max ? s2max : spanLog2;
unsigned int limit = s2max < 1 ? 0 : s2max - 1;
ui->spanLog2->setMaximum(limit);
bool dsb = ui->dsb->isChecked();
//int spanLog2 = ui->spanLog2->value();
m_spectrumRate = m_ft8Demod->getAudioSampleRate() / (1<<spanLog2);
int bw = ui->BW->value();
int lw = ui->lowCut->value();
int bwMax = m_ft8Demod->getAudioSampleRate() / (100*(1<<spanLog2));
int tickInterval = m_spectrumRate / 1200;
tickInterval = tickInterval == 0 ? 1 : tickInterval;
qDebug() << "FT8DemodGUI::applyBandwidths:"
<< " s2max:" << s2max
<< " dsb: " << dsb
<< " spanLog2: " << spanLog2
<< " m_spectrumRate: " << m_spectrumRate
<< " bw: " << bw
<< " lw: " << lw
<< " bwMax: " << bwMax
<< " tickInterval: " << tickInterval;
ui->BW->setTickInterval(tickInterval);
ui->lowCut->setTickInterval(tickInterval);
bw = bw < -bwMax ? -bwMax : bw > bwMax ? bwMax : bw;
if (bw < 0) {
lw = lw < bw+1 ? bw+1 : lw < 0 ? lw : 0;
} else if (bw > 0) {
lw = lw > bw-1 ? bw-1 : lw < 0 ? 0 : lw;
} else {
lw = 0;
}
if (dsb)
{
bw = bw < 0 ? -bw : bw;
lw = 0;
}
QString spanStr = QString::number(bwMax/10.0, 'f', 1);
QString bwStr = QString::number(bw/10.0, 'f', 1);
QString lwStr = QString::number(lw/10.0, 'f', 1);
if (dsb)
{
ui->BWText->setText(tr("%1%2k").arg(QChar(0xB1, 0x00)).arg(bwStr));
ui->spanText->setText(tr("%1%2k").arg(QChar(0xB1, 0x00)).arg(spanStr));
ui->scaleMinus->setText("0");
ui->scaleCenter->setText("");
ui->scalePlus->setText(tr("%1").arg(QChar(0xB1, 0x00)));
ui->lsbLabel->setText("");
ui->usbLabel->setText("");
ui->glSpectrum->setCenterFrequency(0);
ui->glSpectrum->setSampleRate(2*m_spectrumRate);
ui->glSpectrum->setSsbSpectrum(false);
ui->glSpectrum->setLsbDisplay(false);
}
else
{
ui->BWText->setText(tr("%1k").arg(bwStr));
ui->spanText->setText(tr("%1k").arg(spanStr));
ui->scaleMinus->setText("-");
ui->scaleCenter->setText("0");
ui->scalePlus->setText("+");
ui->lsbLabel->setText("LSB");
ui->usbLabel->setText("USB");
ui->glSpectrum->setCenterFrequency(m_spectrumRate/2);
ui->glSpectrum->setSampleRate(m_spectrumRate);
ui->glSpectrum->setSsbSpectrum(true);
ui->glSpectrum->setLsbDisplay(bw < 0);
}
ui->lowCutText->setText(tr("%1k").arg(lwStr));
ui->BW->blockSignals(true);
ui->lowCut->blockSignals(true);
ui->BW->setMaximum(bwMax);
ui->BW->setMinimum(dsb ? 0 : -bwMax);
ui->BW->setValue(bw);
ui->lowCut->setMaximum(dsb ? 0 : bwMax);
ui->lowCut->setMinimum(dsb ? 0 : -bwMax);
ui->lowCut->setValue(lw);
ui->lowCut->blockSignals(false);
ui->BW->blockSignals(false);
ui->channelPowerMeter->setRange(FT8DemodSettings::m_minPowerThresholdDB, 0);
m_settings.m_dsb = dsb;
m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2 = spanLog2;
m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth = bw * 100;
m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff = lw * 100;
applySettings(force);
bool wasBlocked = blockApplySettings(true);
m_channelMarker.setBandwidth(bw * 200);
m_channelMarker.setSidebands(dsb ? ChannelMarker::dsb : bw < 0 ? ChannelMarker::lsb : ChannelMarker::usb);
ui->dsb->setIcon(bw < 0 ? m_iconDSBLSB: m_iconDSBUSB);
if (!dsb) { m_channelMarker.setLowCutoff(lw * 100); }
blockApplySettings(wasBlocked);
}
void FT8DemodGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setBandwidth(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth * 2);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.setLowCutoff(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff);
ui->flipSidebands->setEnabled(!m_settings.m_dsb);
if (m_settings.m_dsb)
{
m_channelMarker.setSidebands(ChannelMarker::dsb);
}
else
{
if (m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth < 0)
{
m_channelMarker.setSidebands(ChannelMarker::lsb);
ui->dsb->setIcon(m_iconDSBLSB);
}
else
{
m_channelMarker.setSidebands(ChannelMarker::usb);
ui->dsb->setIcon(m_iconDSBUSB);
}
}
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
setTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->agc->setChecked(m_settings.m_agc);
ui->agcClamping->setChecked(m_settings.m_agcClamping);
ui->audioBinaural->setChecked(m_settings.m_audioBinaural);
ui->audioFlipChannels->setChecked(m_settings.m_audioFlipChannels);
ui->audioMute->setChecked(m_settings.m_audioMute);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->fftWindow->setCurrentIndex((int) m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow);
// Prevent uncontrolled triggering of applyBandwidths
ui->spanLog2->blockSignals(true);
ui->dsb->blockSignals(true);
ui->BW->blockSignals(true);
ui->filterIndex->blockSignals(true);
ui->filterIndex->setValue(m_settings.m_filterIndex);
ui->filterIndexText->setText(tr("%1").arg(m_settings.m_filterIndex));
ui->dsb->setChecked(m_settings.m_dsb);
ui->spanLog2->setValue(1 + ui->spanLog2->maximum() - m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2);
ui->BW->setValue(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth / 100.0);
QString s = QString::number(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth/1000.0, 'f', 1);
if (m_settings.m_dsb) {
ui->BWText->setText(tr("%1%2k").arg(QChar(0xB1, 0x00)).arg(s));
} else {
ui->BWText->setText(tr("%1k").arg(s));
}
ui->spanLog2->blockSignals(false);
ui->dsb->blockSignals(false);
ui->BW->blockSignals(false);
ui->filterIndex->blockSignals(false);
// The only one of the four signals triggering applyBandwidths will trigger it once only with all other values
// set correctly and therefore validate the settings and apply them to dependent widgets
ui->lowCut->setValue(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff / 100.0);
ui->lowCutText->setText(tr("%1k").arg(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff / 1000.0));
int volume = CalcDb::dbPower(m_settings.m_volume);
ui->volume->setValue(volume);
ui->volumeText->setText(QString("%1").arg(volume));
ui->agcTimeLog2->setValue(m_settings.m_agcTimeLog2);
s = QString::number((1<<ui->agcTimeLog2->value()), 'f', 0);
ui->agcTimeText->setText(s);
ui->agcPowerThreshold->setValue(m_settings.m_agcPowerThreshold);
displayAGCPowerThreshold(ui->agcPowerThreshold->value());
displayAGCThresholdGate(m_settings.m_agcThresholdGate);
updateIndexLabel();
getRollupContents()->restoreState(m_rollupState);
updateAbsoluteCenterFrequency();
blockApplySettings(false);
}
void FT8DemodGUI::displayAGCPowerThreshold(int value)
{
if (value == FT8DemodSettings::m_minPowerThresholdDB)
{
ui->agcPowerThresholdText->setText("---");
}
else
{
QString s = QString::number(value, 'f', 0);
ui->agcPowerThresholdText->setText(s);
}
}
void FT8DemodGUI::displayAGCThresholdGate(int value)
{
QString s = QString::number(value, 'f', 0);
ui->agcThresholdGateText->setText(s);
int dialValue = value;
if (value > 20) {
dialValue = ((value - 20) / 10) + 20;
}
ui->agcThresholdGate->setValue(dialValue);
}
void FT8DemodGUI::leaveEvent(QEvent* event)
{
m_channelMarker.setHighlighted(false);
ChannelGUI::leaveEvent(event);
}
void FT8DemodGUI::enterEvent(EnterEventType* event)
{
m_channelMarker.setHighlighted(true);
ChannelGUI::enterEvent(event);
}
void FT8DemodGUI::audioSelect(const QPoint& p)
{
qDebug("FT8DemodGUI::audioSelect");
AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName);
audioSelect.move(p);
audioSelect.exec();
if (audioSelect.m_selected)
{
m_settings.m_audioDeviceName = audioSelect.m_audioDeviceName;
applySettings();
}
}
void FT8DemodGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_ft8Demod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(FT8DemodSettings::m_mminPowerThresholdDBf + powDbAvg) / FT8DemodSettings::m_mminPowerThresholdDBf,
(FT8DemodSettings::m_mminPowerThresholdDBf + powDbPeak) / FT8DemodSettings::m_mminPowerThresholdDBf,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(tr("%1 dB").arg(powDbAvg, 0, 'f', 1));
}
int audioSampleRate = m_ft8Demod->getAudioSampleRate();
bool squelchOpen = m_ft8Demod->getAudioActive();
if ((audioSampleRate != m_audioSampleRate) || (squelchOpen != m_squelchOpen))
{
if (audioSampleRate < 0) {
ui->audioMute->setStyleSheet("QToolButton { background-color : red; }");
} else if (squelchOpen) {
ui->audioMute->setStyleSheet("QToolButton { background-color : green; }");
} else {
ui->audioMute->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
}
m_audioSampleRate = audioSampleRate;
m_squelchOpen = squelchOpen;
}
m_tickCount++;
}
void FT8DemodGUI::makeUIConnections()
{
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FT8DemodGUI::on_deltaFrequency_changed);
QObject::connect(ui->audioBinaural, &QToolButton::toggled, this, &FT8DemodGUI::on_audioBinaural_toggled);
QObject::connect(ui->audioFlipChannels, &QToolButton::toggled, this, &FT8DemodGUI::on_audioFlipChannels_toggled);
QObject::connect(ui->dsb, &QToolButton::toggled, this, &FT8DemodGUI::on_dsb_toggled);
QObject::connect(ui->BW, &TickedSlider::valueChanged, this, &FT8DemodGUI::on_BW_valueChanged);
QObject::connect(ui->lowCut, &TickedSlider::valueChanged, this, &FT8DemodGUI::on_lowCut_valueChanged);
QObject::connect(ui->volume, &QDial::valueChanged, this, &FT8DemodGUI::on_volume_valueChanged);
QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &FT8DemodGUI::on_agc_toggled);
QObject::connect(ui->agcClamping, &ButtonSwitch::toggled, this, &FT8DemodGUI::on_agcClamping_toggled);
QObject::connect(ui->agcTimeLog2, &QDial::valueChanged, this, &FT8DemodGUI::on_agcTimeLog2_valueChanged);
QObject::connect(ui->agcPowerThreshold, &QDial::valueChanged, this, &FT8DemodGUI::on_agcPowerThreshold_valueChanged);
QObject::connect(ui->agcThresholdGate, &QDial::valueChanged, this, &FT8DemodGUI::on_agcThresholdGate_valueChanged);
QObject::connect(ui->audioMute, &QToolButton::toggled, this, &FT8DemodGUI::on_audioMute_toggled);
QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &FT8DemodGUI::on_spanLog2_valueChanged);
QObject::connect(ui->flipSidebands, &QPushButton::clicked, this, &FT8DemodGUI::on_flipSidebands_clicked);
QObject::connect(ui->fftWindow, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &FT8DemodGUI::on_fftWindow_currentIndexChanged);
QObject::connect(ui->filterIndex, &QDial::valueChanged, this, &FT8DemodGUI::on_filterIndex_valueChanged);
}
void FT8DemodGUI::updateAbsoluteCenterFrequency()
{
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
}

View File

@ -0,0 +1,135 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SSBDEMODGUI_H
#define INCLUDE_SSBDEMODGUI_H
#include <QIcon>
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "ft8demodsettings.h"
class PluginAPI;
class DeviceUISet;
class AudioFifo;
class FT8Demod;
class SpectrumVis;
class BasebandSampleSink;
namespace Ui {
class FT8DemodGUI;
}
class FT8DemodGUI : public ChannelGUI {
Q_OBJECT
public:
static FT8DemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = 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; };
virtual QString getTitle() const { return m_settings.m_title; };
virtual QColor getTitleColor() const { return m_settings.m_rgbColor; };
virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; }
virtual bool getHidden() const { return m_settings.m_hidden; }
virtual ChannelMarker& getChannelMarker() { return m_channelMarker; }
virtual int getStreamIndex() const { return m_settings.m_streamIndex; }
virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; }
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
private:
Ui::FT8DemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
FT8DemodSettings m_settings;
qint64 m_deviceCenterFrequency;
int m_basebandSampleRate;
bool m_doApplySettings;
int m_spectrumRate;
bool m_audioBinaural;
bool m_audioFlipChannels;
bool m_audioMute;
bool m_squelchOpen;
int m_audioSampleRate;
uint32_t m_tickCount;
FT8Demod* m_ft8Demod;
SpectrumVis* m_spectrumVis;
MessageQueue m_inputMessageQueue;
QIcon m_iconDSBUSB;
QIcon m_iconDSBLSB;
explicit FT8DemodGUI(PluginAPI* pluginAPI, DeviceUISet* deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~FT8DemodGUI();
bool blockApplySettings(bool block);
void applySettings(bool force = false);
void applyBandwidths(unsigned int spanLog2, bool force = false);
unsigned int spanLog2Max();
void displaySettings();
void displayAGCPowerThreshold(int value);
void displayAGCThresholdGate(int value);
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_audioBinaural_toggled(bool binaural);
void on_audioFlipChannels_toggled(bool flip);
void on_dsb_toggled(bool dsb);
void on_BW_valueChanged(int value);
void on_lowCut_valueChanged(int value);
void on_volume_valueChanged(int value);
void on_agc_toggled(bool checked);
void on_agcClamping_toggled(bool checked);
void on_agcTimeLog2_valueChanged(int value);
void on_agcPowerThreshold_valueChanged(int value);
void on_agcThresholdGate_valueChanged(int value);
void on_audioMute_toggled(bool checked);
void on_spanLog2_valueChanged(int value);
void on_flipSidebands_clicked(bool checked);
void on_fftWindow_currentIndexChanged(int index);
void on_filterIndex_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void audioSelect(const QPoint& p);
void tick();
};
#endif // INCLUDE_SSBDEMODGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,202 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "ft8demodsettings.h"
#ifdef SDR_RX_SAMPLE_24BIT
const int FT8DemodSettings::m_minPowerThresholdDB = -120;
const float FT8DemodSettings::m_mminPowerThresholdDBf = 120.0f;
#else
const int FT8DemodSettings::m_minPowerThresholdDB = -100;
const float FT8DemodSettings::m_mminPowerThresholdDBf = 100.0f;
#endif
FT8DemodSettings::FT8DemodSettings() :
m_channelMarker(nullptr),
m_spectrumGUI(nullptr),
m_rollupState(nullptr)
{
m_filterBank.resize(10);
resetToDefaults();
}
void FT8DemodSettings::resetToDefaults()
{
m_audioBinaural = false;
m_audioFlipChannels = false;
m_dsb = false;
m_audioMute = false;
m_agc = false;
m_agcClamping = false;
m_agcPowerThreshold = -100;
m_agcThresholdGate = 4;
m_agcTimeLog2 = 7;
m_volume = 1.0;
m_inputFrequencyOffset = 0;
m_rgbColor = QColor(0, 255, 0).rgb();
m_title = "FT8 Demodulator";
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
m_workspaceIndex = 0;
m_hidden = false;
m_filterIndex = 0;
}
QByteArray FT8DemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(3, m_volume * 10.0);
if (m_spectrumGUI) {
s.writeBlob(4, m_spectrumGUI->serialize());
}
s.writeU32(5, m_rgbColor);
s.writeBool(8, m_audioBinaural);
s.writeBool(9, m_audioFlipChannels);
s.writeBool(10, m_dsb);
s.writeBool(11, m_agc);
s.writeS32(12, m_agcTimeLog2);
s.writeS32(13, m_agcPowerThreshold);
s.writeS32(14, m_agcThresholdGate);
s.writeBool(15, m_agcClamping);
s.writeString(16, m_title);
s.writeString(17, m_audioDeviceName);
s.writeBool(18, m_useReverseAPI);
s.writeString(19, m_reverseAPIAddress);
s.writeU32(20, m_reverseAPIPort);
s.writeU32(21, m_reverseAPIDeviceIndex);
s.writeU32(22, m_reverseAPIChannelIndex);
s.writeS32(23, m_streamIndex);
if (m_rollupState) {
s.writeBlob(24, m_rollupState->serialize());
}
s.writeS32(25, m_workspaceIndex);
s.writeBlob(26, m_geometryBytes);
s.writeBool(27, m_hidden);
s.writeU32(29, m_filterIndex);
for (unsigned int i = 0; i < 10; i++)
{
s.writeS32(100 + 10*i, m_filterBank[i].m_spanLog2);
s.writeS32(101 + 10*i, m_filterBank[i].m_rfBandwidth / 100.0);
s.writeS32(102 + 10*i, m_filterBank[i].m_lowCutoff / 100.0);
s.writeS32(103 + 10*i, (int) m_filterBank[i].m_fftWindow);
}
return s.final();
}
bool FT8DemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
qint32 tmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(3, &tmp, 30);
m_volume = tmp / 10.0;
if (m_spectrumGUI)
{
d.readBlob(4, &bytetmp);
m_spectrumGUI->deserialize(bytetmp);
}
d.readU32(5, &m_rgbColor);
d.readBool(8, &m_audioBinaural, false);
d.readBool(9, &m_audioFlipChannels, false);
d.readBool(10, &m_dsb, false);
d.readBool(11, &m_agc, false);
d.readS32(12, &m_agcTimeLog2, 7);
d.readS32(13, &m_agcPowerThreshold, -40);
d.readS32(14, &m_agcThresholdGate, 4);
d.readBool(15, &m_agcClamping, false);
d.readString(16, &m_title, "SSB Demodulator");
d.readString(17, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readBool(18, &m_useReverseAPI, false);
d.readString(19, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(20, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(21, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(22, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readS32(23, &m_streamIndex, 0);
if (m_rollupState)
{
d.readBlob(24, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(25, &m_workspaceIndex, 0);
d.readBlob(26, &m_geometryBytes);
d.readBool(27, &m_hidden, false);
d.readU32(29, &utmp, 0);
m_filterIndex = utmp < 10 ? utmp : 0;
for (unsigned int i = 0; (i < 10); i++)
{
d.readS32(100 + 10*i, &m_filterBank[i].m_spanLog2, 3);
d.readS32(101 + 10*i, &tmp, 30);
m_filterBank[i].m_rfBandwidth = tmp * 100.0;
d.readS32(102+ 10*i, &tmp, 3);
m_filterBank[i].m_lowCutoff = tmp * 100.0;
d.readS32(103 + 10*i, &tmp, (int) FFTWindow::Blackman);
m_filterBank[i].m_fftWindow =
(FFTWindow::Function) (tmp < 0 ? 0 : tmp > (int) FFTWindow::BlackmanHarris7 ? (int) FFTWindow::BlackmanHarris7 : tmp);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_CHANNELRX_DEMODFT8_FT8DEMODSETTINGS_H_
#define PLUGINS_CHANNELRX_DEMODFT8_FT8DEMODSETTINGS_H_
#include <QByteArray>
#include <QString>
#include "dsp/fftwindow.h"
class Serializable;
struct FT8DemodFilterSettings
{
int m_spanLog2;
Real m_rfBandwidth;
Real m_lowCutoff;
FFTWindow::Function m_fftWindow;
FT8DemodFilterSettings() :
m_spanLog2(3),
m_rfBandwidth(3000),
m_lowCutoff(300),
m_fftWindow(FFTWindow::Blackman)
{}
};
struct FT8DemodSettings
{
qint32 m_inputFrequencyOffset;
// Real m_rfBandwidth;
// Real m_lowCutoff;
Real m_volume;
// int m_spanLog2;
bool m_audioBinaural;
bool m_audioFlipChannels;
bool m_dsb;
bool m_audioMute;
bool m_agc;
bool m_agcClamping;
int m_agcTimeLog2;
int m_agcPowerThreshold;
int m_agcThresholdGate;
quint32 m_rgbColor;
QString m_title;
QString m_audioDeviceName;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
// FFTWindow::Function m_fftWindow;
std::vector<FT8DemodFilterSettings> m_filterBank;
unsigned int m_filterIndex;
Serializable *m_channelMarker;
Serializable *m_spectrumGUI;
Serializable *m_rollupState;
FT8DemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setSpectrumGUI(Serializable *spectrumGUI) { m_spectrumGUI = spectrumGUI; }
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
static const int m_minPowerThresholdDB;
static const float m_mminPowerThresholdDBf;
};
#endif /* PLUGINS_CHANNELRX_DEMODFT8_FT8DEMODSETTINGS_H_ */

View File

@ -0,0 +1,454 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <QTime>
#include <QDebug>
#include "audio/audiooutputdevice.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/devicesamplemimo.h"
#include "dsp/spectrumvis.h"
#include "dsp/datafifo.h"
#include "device/deviceapi.h"
#include "util/db.h"
#include "util/messagequeue.h"
#include "maincore.h"
#include "ft8demodsink.h"
const int FT8DemodSink::m_ssbFftLen = 1024;
const int FT8DemodSink::m_agcTarget = 3276; // 32768/10 -10 dB amplitude => -20 dB power: center of normal signal
FT8DemodSink::FT8DemodSink() :
m_audioBinaual(false),
m_audioFlipChannels(false),
m_dsb(false),
m_audioMute(false),
m_agc(12000, m_agcTarget, 1e-2),
m_agcActive(false),
m_agcClamping(false),
m_agcNbSamples(12000),
m_agcPowerThreshold(1e-2),
m_agcThresholdGate(0),
m_squelchDelayLine(2*48000),
m_audioActive(false),
m_spectrumSink(nullptr),
m_audioFifo(24000),
m_audioSampleRate(48000)
{
m_Bandwidth = 5000;
m_LowCutoff = 300;
m_volume = 2.0;
m_spanLog2 = 3;
m_channelSampleRate = 48000;
m_channelFrequencyOffset = 0;
m_audioBuffer.resize(1<<14);
m_audioBufferFill = 0;
m_undersampleCount = 0;
m_sum = 0;
m_demodBuffer.resize(1<<12);
m_demodBufferFill = 0;
m_usb = true;
m_magsq = 0.0f;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
m_agc.setClampMax(SDR_RX_SCALED/100.0);
m_agc.setClamping(m_agcClamping);
SSBFilter = new fftfilt(m_LowCutoff / m_audioSampleRate, m_Bandwidth / m_audioSampleRate, m_ssbFftLen);
DSBFilter = new fftfilt((2.0f * m_Bandwidth) / m_audioSampleRate, 2 * m_ssbFftLen);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
applySettings(m_settings, true);
}
FT8DemodSink::~FT8DemodSink()
{
delete SSBFilter;
delete DSBFilter;
}
void FT8DemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
if (m_channelSampleRate == 0) {
return;
}
Complex ci;
for(SampleVector::const_iterator it = begin; it < end; ++it)
{
Complex c(it->real(), it->imag());
c *= m_nco.nextIQ();
if (m_interpolatorDistance < 1.0f) // interpolate
{
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
else
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
}
void FT8DemodSink::processOneSample(Complex &ci)
{
fftfilt::cmplx *sideband;
int n_out = 0;
int decim = 1<<(m_spanLog2 - 1);
unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
if (m_dsb) {
n_out = DSBFilter->runDSB(ci, &sideband);
} else {
n_out = SSBFilter->runSSB(ci, &sideband, m_usb);
}
for (int i = 0; i < n_out; i++)
{
// Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
// smart decimation with bit gain using float arithmetic (23 bits significand)
m_sum += sideband[i];
if (!(m_undersampleCount++ & decim_mask))
{
Real avgr = m_sum.real() / decim;
Real avgi = m_sum.imag() / decim;
m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED);
m_magsqSum += m_magsq;
if (m_magsq > m_magsqPeak)
{
m_magsqPeak = m_magsq;
}
m_magsqCount++;
if (!m_dsb & !m_usb)
{ // invert spectrum for LSB
m_sampleBuffer.push_back(Sample(avgi, avgr));
}
else
{
m_sampleBuffer.push_back(Sample(avgr, avgi));
}
m_sum.real(0.0);
m_sum.imag(0.0);
}
float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 0.1;
fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay());
m_audioActive = delayedSample.real() != 0.0;
m_squelchDelayLine.write(sideband[i]*agcVal);
if (m_audioMute)
{
m_audioBuffer[m_audioBufferFill].r = 0;
m_audioBuffer[m_audioBufferFill].l = 0;
}
else
{
fftfilt::cmplx z = m_agcActive ? delayedSample * m_agc.getStepValue() : delayedSample;
if (m_audioBinaual)
{
if (m_audioFlipChannels)
{
m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume);
m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume);
}
else
{
m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume);
m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume);
}
m_demodBuffer[m_demodBufferFill++] = z.real();
m_demodBuffer[m_demodBufferFill++] = z.imag();
}
else
{
Real demod = (z.real() + z.imag()) * 0.7;
qint16 sample = (qint16)(demod * m_volume);
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
m_demodBuffer[m_demodBufferFill++] = (z.real() + z.imag()) * 0.7;
}
if (m_demodBufferFill >= m_demodBuffer.size())
{
QList<ObjectPipe*> dataPipes;
MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes);
if (dataPipes.size() > 0)
{
QList<ObjectPipe*>::iterator it = dataPipes.begin();
for (; it != dataPipes.end(); ++it)
{
DataFifo *fifo = qobject_cast<DataFifo*>((*it)->m_element);
if (fifo)
{
fifo->write(
(quint8*) &m_demodBuffer[0],
m_demodBuffer.size() * sizeof(qint16),
m_audioBinaual ? DataFifo::DataTypeCI16 : DataFifo::DataTypeI16
);
}
}
}
m_demodBufferFill = 0;
}
}
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill) {
qDebug("FT8DemodSink::processOneSample: %u/%u samples written", res, m_audioBufferFill);
}
m_audioBufferFill = 0;
}
}
if (m_spectrumSink && (m_sampleBuffer.size() != 0))
{
m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), !m_dsb);
m_sampleBuffer.clear();
}
}
void FT8DemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "FT8DemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > channelSampleRate ? channelSampleRate : (m_Bandwidth * 1.5f);
m_interpolator.create(16, channelSampleRate, interpolatorBandwidth, 2.0f);
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) channelSampleRate / (Real) m_audioSampleRate;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void FT8DemodSink::applyAudioSampleRate(int sampleRate)
{
qDebug("FT8DemodSink::applyAudioSampleRate: %d", sampleRate);
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f);
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate;
SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow);
DSBFilter->create_dsb_filter(m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow);
int agcNbSamples = (sampleRate / 1000) * (1<<m_settings.m_agcTimeLog2);
int agcThresholdGate = (sampleRate / 1000) * m_settings.m_agcThresholdGate; // ms
if (m_agcNbSamples != agcNbSamples)
{
m_agc.resize(agcNbSamples, agcNbSamples/2, m_agcTarget);
m_agc.setStepDownDelay(agcNbSamples);
m_agcNbSamples = agcNbSamples;
}
if (m_agcThresholdGate != agcThresholdGate)
{
m_agc.setGate(agcThresholdGate);
m_agcThresholdGate = agcThresholdGate;
}
m_audioFifo.setSize(sampleRate);
m_audioSampleRate = sampleRate;
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes);
if (pipes.size() > 0)
{
for (const auto& pipe : pipes)
{
MessageQueue* messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
if (messageQueue)
{
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(m_channel, sampleRate);
messageQueue->push(msg);
}
}
}
}
void FT8DemodSink::applySettings(const FT8DemodSettings& settings, bool force)
{
qDebug() << "FT8DemodSink::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_filterIndex: " << settings.m_filterIndex
<< " [m_spanLog2: " << settings.m_filterBank[settings.m_filterIndex].m_spanLog2
<< " m_rfBandwidth: " << settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth
<< " m_lowCutoff: " << settings.m_filterBank[settings.m_filterIndex].m_lowCutoff
<< " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
<< " m_volume: " << settings.m_volume
<< " m_audioBinaual: " << settings.m_audioBinaural
<< " m_audioFlipChannels: " << settings.m_audioFlipChannels
<< " m_dsb: " << settings.m_dsb
<< " m_audioMute: " << settings.m_audioMute
<< " m_agcActive: " << settings.m_agc
<< " m_agcClamping: " << settings.m_agcClamping
<< " m_agcTimeLog2: " << settings.m_agcTimeLog2
<< " agcPowerThreshold: " << settings.m_agcPowerThreshold
<< " agcThresholdGate: " << settings.m_agcThresholdGate
<< " m_audioDeviceName: " << settings.m_audioDeviceName
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
if((m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth != settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth) ||
(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff != settings.m_filterBank[settings.m_filterIndex].m_lowCutoff) ||
(m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow != settings.m_filterBank[settings.m_filterIndex].m_fftWindow) || force)
{
float band, lowCutoff;
band = settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth;
lowCutoff = settings.m_filterBank[settings.m_filterIndex].m_lowCutoff;
if (band < 0) {
band = -band;
lowCutoff = -lowCutoff;
m_usb = false;
} else {
m_usb = true;
}
if (band < 100.0f)
{
band = 100.0f;
lowCutoff = 0;
}
m_Bandwidth = band;
m_LowCutoff = lowCutoff;
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f);
m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f);
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate;
SSBFilter->create_filter(m_LowCutoff / (float) m_audioSampleRate, m_Bandwidth / (float) m_audioSampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
DSBFilter->create_dsb_filter(m_Bandwidth / (float) m_audioSampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
}
if ((m_settings.m_volume != settings.m_volume) || force)
{
m_volume = settings.m_volume;
m_volume /= 4.0; // for 3276.8
}
if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) ||
(m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) ||
(m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) ||
(m_settings.m_agcClamping != settings.m_agcClamping) || force)
{
int agcNbSamples = (m_audioSampleRate / 1000) * (1<<settings.m_agcTimeLog2);
m_agc.setThresholdEnable(settings.m_agcPowerThreshold != -FT8DemodSettings::m_minPowerThresholdDB);
double agcPowerThreshold = CalcDb::powerFromdB(settings.m_agcPowerThreshold) * (SDR_RX_SCALED*SDR_RX_SCALED);
int agcThresholdGate = (m_audioSampleRate / 1000) * settings.m_agcThresholdGate; // ms
bool agcClamping = settings.m_agcClamping;
if (m_agcNbSamples != agcNbSamples)
{
m_agc.resize(agcNbSamples, agcNbSamples/2, m_agcTarget);
m_agc.setStepDownDelay(agcNbSamples);
m_agcNbSamples = agcNbSamples;
}
if (m_agcPowerThreshold != agcPowerThreshold)
{
m_agc.setThreshold(agcPowerThreshold);
m_agcPowerThreshold = agcPowerThreshold;
}
if (m_agcThresholdGate != agcThresholdGate)
{
m_agc.setGate(agcThresholdGate);
m_agcThresholdGate = agcThresholdGate;
}
if (m_agcClamping != agcClamping)
{
m_agc.setClamping(agcClamping);
m_agcClamping = agcClamping;
}
qDebug() << "FT8DemodSink::applySettings: AGC:"
<< " agcNbSamples: " << agcNbSamples
<< " agcPowerThreshold: " << agcPowerThreshold
<< " agcThresholdGate: " << agcThresholdGate
<< " agcClamping: " << agcClamping;
}
m_spanLog2 = settings.m_filterBank[settings.m_filterIndex].m_spanLog2;
m_audioBinaual = settings.m_audioBinaural;
m_audioFlipChannels = settings.m_audioFlipChannels;
m_dsb = settings.m_dsb;
m_audioMute = settings.m_audioMute;
m_agcActive = settings.m_agc;
m_settings = settings;
}

View File

@ -0,0 +1,138 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FT8DEMODSINK_H
#define INCLUDE_FT8DEMODSINK_H
#include <QVector>
#include "dsp/channelsamplesink.h"
#include "dsp/ncof.h"
#include "dsp/interpolator.h"
#include "dsp/fftfilt.h"
#include "dsp/agc.h"
#include "audio/audiofifo.h"
#include "util/doublebufferfifo.h"
#include "ft8demodsettings.h"
class SpectrumVis;
class ChannelAPI;
class FT8DemodSink : public ChannelSampleSink {
public:
FT8DemodSink();
~FT8DemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; }
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false);
void applySettings(const FT8DemodSettings& settings, bool force = false);
void applyAudioSampleRate(int sampleRate);
AudioFifo *getAudioFifo() { return &m_audioFifo; }
double getMagSq() const { return m_magsq; }
bool getAudioActive() const { return m_audioActive; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_magsqCount > 0)
{
m_magsq = m_magsqSum / m_magsqCount;
m_magSqLevelStore.m_magsq = m_magsq;
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
}
avg = m_magSqLevelStore.m_magsq;
peak = m_magSqLevelStore.m_magsqPeak;
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
}
private:
struct MagSqLevelsStore
{
MagSqLevelsStore() :
m_magsq(1e-12),
m_magsqPeak(1e-12)
{}
double m_magsq;
double m_magsqPeak;
};
FT8DemodSettings m_settings;
ChannelAPI *m_channel;
Real m_Bandwidth;
Real m_LowCutoff;
Real m_volume;
int m_spanLog2;
fftfilt::cmplx m_sum;
int m_undersampleCount;
int m_channelSampleRate;
int m_channelFrequencyOffset;
bool m_audioBinaual;
bool m_audioFlipChannels;
bool m_usb;
bool m_dsb;
bool m_audioMute;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MagAGC m_agc;
bool m_agcActive;
bool m_agcClamping;
int m_agcNbSamples; //!< number of audio (48 kHz) samples for AGC averaging
double m_agcPowerThreshold; //!< AGC power threshold (linear)
int m_agcThresholdGate; //!< Gate length in number of samples befor threshold triggers
DoubleBufferFIFO<fftfilt::cmplx> m_squelchDelayLine;
bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold)
NCOF m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
fftfilt* SSBFilter;
fftfilt* DSBFilter;
SpectrumVis* m_spectrumSink;
SampleVector m_sampleBuffer;
AudioVector m_audioBuffer;
uint m_audioBufferFill;
AudioFifo m_audioFifo;
quint32 m_audioSampleRate;
QVector<qint16> m_demodBuffer;
int m_demodBufferFill;
static const int m_ssbFftLen;
static const int m_agcTarget;
void processOneSample(Complex &ci);
};
#endif // INCLUDE_FT8DEMODSINK_H

View File

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGChannelSettings.h"
#include "ft8demod.h"
#include "ft8demodwebapiadapter.h"
FT8DemodWebAPIAdapter::FT8DemodWebAPIAdapter()
{}
FT8DemodWebAPIAdapter::~FT8DemodWebAPIAdapter()
{}
int FT8DemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSsbDemodSettings(new SWGSDRangel::SWGSSBDemodSettings());
response.getSsbDemodSettings()->init();
FT8Demod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int FT8DemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
FT8Demod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FT8DEMOD_WEBAPIADAPTER_H
#define INCLUDE_FT8DEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "ft8demodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class FT8DemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
FT8DemodWebAPIAdapter();
virtual ~FT8DemodWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
private:
FT8DemodSettings m_settings;
};
#endif // INCLUDE_FT8DEMOD_WEBAPIADAPTER_H

View File

@ -0,0 +1,76 @@
#include "ft8plugin.h"
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "ft8demodgui.h"
#endif
#include "ft8demod.h"
#include "ft8demodwebapiadapter.h"
#include "ft8plugin.h"
const PluginDescriptor FT8Plugin::m_pluginDescriptor = {
FT8Demod::m_channelId,
QStringLiteral("FT8 Demodulator"),
QStringLiteral("7.9.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
FT8Plugin::FT8Plugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& FT8Plugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void FT8Plugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register demodulator
m_pluginAPI->registerRxChannel(FT8Demod::m_channelIdURI, FT8Demod::m_channelId, this);
}
void FT8Plugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
FT8Demod *instance = new FT8Demod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* FT8Plugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return nullptr;
}
#else
ChannelGUI* FT8Plugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return FT8DemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* FT8Plugin::createChannelWebAPIAdapter() const
{
return new FT8DemodWebAPIAdapter();
}

View File

@ -0,0 +1,31 @@
#ifndef INCLUDE_FT8PLUGIN_H
#define INCLUDE_FT8PLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class FT8Plugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.ft8demod")
public:
explicit FT8Plugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FT8PLUGIN_H

View File

@ -0,0 +1,180 @@
<h1>SSB/DSB demodulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to listen to a single sideband or double sidebands modulated signal. This includes CW (Morse code) signals.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
![SSB Demodulator plugin GUI](../../../doc/img/SSBDemod_plugin.png)
&#9758; In order to toggle USB or LSB mode in SSB mode you have to set the "BW" in channel filter cutoff control (9) to a positive (USB) or negative (LSB) value. The above screenshot shows a LSB setup. See the (8) to (10) paragraphs below for details.
&#9758; The channel marker in the main spectrum display shows the actual band received taking in channel filtering into account.
<h3>1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>2: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>3: Monaural/binaural toggle</h3>
- Monaural: the scalar signal is routed to both left and right audio channels
- Binaural: the complex signal is fed with the real part on the left audio channel and the imaginary part to the right audio channel
<h3>4: Invert left and right channels</h3>
Inverts left and right audio channels. Useful in binaural mode only.
<h3>5: Sideband flip</h3>
Flip LSB/USB. Mirror filter bandwidth around zero frequency and change from LSB to USB or vice versa. Works in SSB mode only.
<h3>6: SSB/DSB demodulation</h3>
Toggles between SSB (icon with one sideband signal) and DSB (icon with double sidebands signal). In SSB mode the shape of the icon represents LSB or USB operation.
<h3>7: Level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>8: Spectrum display frequency span</h3>
The audio sample rate SR is further decimated by powers of two for the spectrum display and in channel filter limits. This effectively sets the total available bandwidth depending on the decimation:
- 1 (no decimation): SR/2 (SSB) or SR (DSB)
- 2: SR/4 (SSB) or SR/2 (DSB)
- 4: SR/8 (SSB) or SR/4 (DSB)
- 8: SR/16 (SSB) or SR/8 (DSB)
- 16: SR/32 (SSB) or SR/16 (DSB)
The span value display is set as follows depending on the SSB or DSB mode:
- In SSB mode: the span goes from zero to the upper (USB: positive frequencies) or lower (LSB: negative frequencies) limit and the absolute value of the limit is displayed.
- In DSB mode: the span goes from the lower to the upper limit of same absolute value and &#177; the absolute value of the limit is displayed.
This is how the Span (8) and bandpass (9, 10) filter controls look like in the 3 possible modes:
**DSB**:
![SSB Demodulator band controls DSB](../../../doc/img/SSBDemod_plugin_dsb.png)
- Decimation factor is 4 hence span is 6 kHz from -3 to +3 kHz and &#177;3.0k is displayed
- In channel filter bandwidth is 5.2 kHz from -2.6 to +2.6 kHz and &#177;2.6k is displayed
- In channel filter "low cut" is disabled and set to 0
**USB**:
![SSB Demodulator band controls USB](../../../doc/img/SSBDemod_plugin_usb.png)
- Decimation factor is 4 hence span is 3 kHz from 0 to 3 kHz and 3.0k is displayed
- In channel filter upper cutoff is 2.6 kHz and 2.6k is displayed
- In channel filter lower cutoff is 0.3 kHz and 0.3k is displayed
- Hence in channel filter bandwidth is 2.3 kHz
**LSB**:
![SSB Demodulator band controls LSB](../../../doc/img/SSBDemod_plugin_lsb.png)
- Decimation factor is 4 hence span is 3 kHz from 0 to -3 kHz and 3.0k is displayed
- In channel filter lower cutoff is -2.6 kHz and -2.6k is displayed
- In channel filter upper cutoff is -0.3 kHz and -0.3k is displayed
- Hence in channel filter bandwidth is 2.3 kHz
<h3>9: FFT filter window</h3>
The bandpass filter is a FFT filter. This controls the FFT window type:
- **Bart**: Bartlett
- **B-H**: 4 term Blackman-Harris
- **FT**: Flat top
- **Ham**: Hamming
- **Han**: Hanning
- **Rec**: Rectangular (no window)
- **Kai**: Kaiser with alpha = 2.15 (beta = 6.76) gives sidelobes &lt; -70dB
- **Blackman**: Blackman (3 term - default)
- **B-H7**: 7 term Blackman-Harris
<h3>10: Select filter in filter bank</h3>
There are 10 filters in the filter bank with indexes 0 to 9. This selects the current filter in the bank the filter index is displayed at the right of the button. The following controls are covered by the filter settings:
- Span (8)
- FFT window (9)
- BW (11)
- Low cut (12)
<h3>11: "BW": In channel bandpass filter cutoff frequency farthest from zero</h3>
Values are expressed in kHz and step is 100 Hz.
- In SSB mode this is the upper (USB: positive frequencies) or lower (LSB: negative frequencies) cutoff of the in channel single side band bandpass filter. The value triggers LSB mode when negative and USB when positive
- In DSB mode this is half the bandwidth of the double side band in channel bandpass filter therefore the value is prefixed with the &#177; sign.
<h3>12: "Low cut": In channel bandpass filter cutoff frequency closest to zero</h3>
Values are expressed in kHz and step is 100 Hz.
- In SSB mode this is the lower cutoff (USB: positive frequencies) or higher cutoff (LSB: negative frequencies) of the in channel single side band bandpass filter.
- In DSB mode it is inactive and set to zero (double side band filter).
<h3>13: Volume and AGC</h3>
![SSB volume and AGC controls](../../../doc/img/SSBDemod_plugin_vol.png)
<h4>13.1: Volume</h4>
This is the volume of the audio signal in dB from 0 (no gain) to 40 (10000). It can be varied continuously in 1 dB steps using the dial button. When AGC is engaged it is recommended to set a low value in dB not exceeding 3 db (gain 2). When AGC is not engaged the volume entirely depends on the RF power and can vary in large proportions. Hence setting the value in dB is more convenient to accommodate large differences.
<h4>13.2: AGC toggle</h4>
Use this checkbox to toggle AGC on and off.
If you are into digging weak signals out of the noise you probably will not turn the AGC on. AGC is intended for medium and large signals and help accommodate the signal power variations from a station to another or due to QSB.
This AGC is based on the calculated magnitude (square root of power of the filtered signal as I² + Q²) and will try to adjust audio volume as if a -20dB power signal was received.
<h4>13.2A: AGC clamping</h4>
When on this clamps signal at the maximum amplitude. Normally this is not needed for most signals as the AGC amplitude order is quite conservative at 10% of the maximum. You may switch it on if you notice a loud click when a transmission starts.
<h4>13.3: AGC time constant</h4>
This is the time window in milliseconds of the moving average used to calculate the signal power average. It can be varied in powers of two from 16 to 2048 ms that is: 16, 32, 64, 128, 256, 512, 1024 and 2048 ms. The most practical values are between 128 and 512 ms.
<h4>13.4: Signal power threshold (squelch)</h4>
Active only in AGC mode.
This threshold acts as a squelch and will mute the sound below this average signal power. To prevent short transient drop outs the squelch gets active only if the power has been below the threshold for a period equal to the AGC time constant (11.3)
This feature is mostly useful when more than one SSB channel is active. When there is no transmission the level of noise rises at the level of a normal signal due to the AGC and adds to the noise of other channels. Therefore it is desirable to shut down the audio when there is no signal in the channel.
To turn off the squelch completely move the knob all the way down (left). Then "---" will display as the value and the squelch will be disabled.
The signal power is calculated as the moving average over the AGC time constant (11.3) of the power of the filtered signal as I² + Q².
<h4>13.5: Signal power threshold (squelch) gate</h4>
Active only in AGC mode with squelch enabled.
To avoid unwanted squelch opening on short transient bursts only signals with power above threshold during this period in milliseconds will open the squelch.It can be varied from 0 to 20 ms in 1 ms steps then from 30 to 500 ms in 10 ms steps.
When the power threshold is close to the noise floor a few milliseconds help in preventing noise power wiggle to open the squelch.
<h3>14: Audio mute and audio output select</h3>
Left click on this button to toggle audio mute for this channel.
If you right click on it a dialog will open to select the audio output device. See [audio management documentation](../../../sdrgui/audio.md) for details.
<h3>15: Spectrum display</h3>
This is the spectrum display of the demodulated signal (SSB) or translated signal (DSB). Controls on the bottom of the panel are identical to the ones of the main spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md)

View File

@ -3517,6 +3517,9 @@ margin-bottom: 20px;
"FreqTrackerReport" : {
"$ref" : "#/definitions/FreqTrackerReport"
},
"FT8DemodReport" : {
"$ref" : "#/definitions/FT8DemodReport"
},
"M17DemodReport" : {
"$ref" : "#/definitions/M17DemodReport"
},
@ -3669,6 +3672,9 @@ margin-bottom: 20px;
"FreqTrackerSettings" : {
"$ref" : "#/definitions/FreqTrackerSettings"
},
"FT8DemodSettings" : {
"$ref" : "#/definitions/FT8DemodSettings"
},
"InterferometerSettings" : {
"$ref" : "#/definitions/InterferometerSettings"
},
@ -5555,6 +5561,131 @@ margin-bottom: 20px;
}
},
"description" : "FFT filter band definition"
};
defs.FT8DemodReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "power received in channel (dB)"
},
"squelch" : {
"type" : "integer",
"description" : "Audio squelch status (1 if open else 0)"
},
"audioSampleRate" : {
"type" : "integer"
},
"channelSampleRate" : {
"type" : "integer"
}
},
"description" : "FT8Demod"
};
defs.FT8DemodSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"filterIndex" : {
"type" : "integer"
},
"spanLog2" : {
"type" : "integer"
},
"rfBandwidth" : {
"type" : "number",
"format" : "float"
},
"lowCutoff" : {
"type" : "number",
"format" : "float"
},
"fftWindow" : {
"type" : "integer",
"description" : "FFT Window index (FFTWindow::Function):\n * 0 - Bartlett\n * 1 - Blackman-Harris 4 term\n * 2 - Flattop\n * 3 - Hamming\n * 4 - Hanning\n * 5 - Rectangle\n * 6 - Kaiser\n * 7 - Blackman 3 term\n * 8 - Blackman-Harris 7 term\n"
},
"volume" : {
"type" : "number",
"format" : "float"
},
"audioBinaural" : {
"type" : "integer",
"description" : "Audio binaural mode (1 if active else 0)"
},
"audioFlipChannels" : {
"type" : "integer",
"description" : "Flip audio channels (1 if flipped else 0)"
},
"dsb" : {
"type" : "integer",
"description" : "Double sidebands mode (1 if DSB else 0)"
},
"audioMute" : {
"type" : "integer",
"description" : "Mute audio (1 if muted else 0)"
},
"agc" : {
"type" : "integer",
"description" : "AGC (1 if AGC active else 0)"
},
"agcClamping" : {
"type" : "integer",
"description" : "AGC clamping (1 if AGC clampingactive else 0)"
},
"agcTimeLog2" : {
"type" : "integer",
"description" : "AGC averaging time log2 in milliseconds"
},
"agcPowerThreshold" : {
"type" : "integer",
"description" : "Audio open RF threshold (dB)"
},
"agcThresholdGate" : {
"type" : "integer",
"description" : "Audio squelch gate in ms"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"audioDeviceName" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
},
"spectrumConfig" : {
"$ref" : "#/definitions/GLSpectrum"
},
"channelMarker" : {
"$ref" : "#/definitions/ChannelMarker"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "FT8Demod"
};
defs.Feature = {
"required" : [ "id", "index", "title", "uid" ],
@ -56780,7 +56911,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2022-12-16T06:50:35.932+01:00
Generated 2023-01-14T23:57:54.939+01:00
</div>
</div>
</div>

View File

@ -49,6 +49,8 @@ ChannelReport:
$ref: "/doc/swagger/include/FreeDVMod.yaml#/FreeDVModReport"
FreqTrackerReport:
$ref: "/doc/swagger/include/FreqTracker.yaml#/FreqTrackerReport"
FT8DemodReport:
$ref: "/doc/swagger/include/FT8Demod.yaml#/FT8DemodReport"
M17DemodReport:
$ref: "/doc/swagger/include/M17Demod.yaml#/M17DemodReport"
M17ModReport:

View File

@ -63,6 +63,8 @@ ChannelSettings:
$ref: "/doc/swagger/include/FreeDVMod.yaml#/FreeDVModSettings"
FreqTrackerSettings:
$ref: "/doc/swagger/include/FreqTracker.yaml#/FreqTrackerSettings"
FT8DemodSettings:
$ref: "/doc/swagger/include/FT8Demod.yaml#/FT8DemodSettings"
InterferometerSettings:
$ref: "/doc/swagger/include/Interferometer.yaml#/InterferometerSettings"
IEEE_802_15_4_ModSettings:

View File

@ -0,0 +1,100 @@
FT8DemodSettings:
description: FT8Demod
properties:
inputFrequencyOffset:
type: integer
format: int64
filterIndex:
type: integer
spanLog2:
type: integer
rfBandwidth:
type: number
format: float
lowCutoff:
type: number
format: float
fftWindow:
type: integer
description: >
FFT Window index (FFTWindow::Function):
* 0 - Bartlett
* 1 - Blackman-Harris 4 term
* 2 - Flattop
* 3 - Hamming
* 4 - Hanning
* 5 - Rectangle
* 6 - Kaiser
* 7 - Blackman 3 term
* 8 - Blackman-Harris 7 term
volume:
type: number
format: float
audioBinaural:
description: Audio binaural mode (1 if active else 0)
type: integer
audioFlipChannels:
description: Flip audio channels (1 if flipped else 0)
type: integer
dsb:
description: Double sidebands mode (1 if DSB else 0)
type: integer
audioMute:
description: Mute audio (1 if muted else 0)
type: integer
agc:
description: AGC (1 if AGC active else 0)
type: integer
agcClamping:
description: AGC clamping (1 if AGC clampingactive else 0)
type: integer
agcTimeLog2:
description: AGC averaging time log2 in milliseconds
type: integer
agcPowerThreshold:
description: Audio open RF threshold (dB)
type: integer
agcThresholdGate:
description: Audio squelch gate in ms
type: integer
rgbColor:
type: integer
title:
type: string
audioDeviceName:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
spectrumConfig:
$ref: "/doc/swagger/include/GLSpectrum.yaml#/GLSpectrum"
channelMarker:
$ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "/doc/swagger/include/RollupState.yaml#/RollupState"
FT8DemodReport:
description: FT8Demod
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
squelch:
description: Audio squelch status (1 if open else 0)
type: integer
audioSampleRate:
type: integer
channelSampleRate:
type: integer

View File

@ -4523,6 +4523,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setFreqTrackerSettings(new SWGSDRangel::SWGFreqTrackerSettings());
channelSettings->getFreqTrackerSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "FT8DemodSettings")
{
channelSettings->setFt8DemodSettings(new SWGSDRangel::SWGFT8DemodSettings());
channelSettings->getFt8DemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "IEEE_802_15_4_ModSettings")
{
channelSettings->setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings());

View File

@ -67,6 +67,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channeltx.remotesource", "RemoteSourceSettings"},
{"sdrangel.channeltx.modssb", "SSBModSettings"},
{"sdrangel.channel.ssbdemod", "SSBDemodSettings"},
{"sdrangel.channel.ft8demod", "FT8DemodSettings"},
{"de.maintech.sdrangelove.channel.ssb", "SSBDemodSettings"}, // remap
{"sdrangel.channel.radioastronomy", "RadioAstronomySettings"},
{"sdrangel.channeltx.udpsource", "UDPSourceSettings"},
@ -175,6 +176,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"RemoteTCPSink", "RemoteTCPSinkSettings"},
{"SSBMod", "SSBModSettings"},
{"SSBDemod", "SSBDemodSettings"},
{"FT8Demod", "FT8DemodSettings"},
{"UDPSink", "UDPSinkSettings"},
{"UDPSource", "UDPSourceSettings"},
{"VORDemodMC", "VORDemodMCSettings"},
@ -530,7 +532,7 @@ bool WebAPIUtils::getSubObjectIntList(const QJsonObject &json, const QString &ke
{
QJsonValue value = subObject[key];
if (value.isArray())
{
{
QJsonArray array = value.toArray();
for (int i = 0; i < array.size(); i++)
{
@ -538,7 +540,7 @@ bool WebAPIUtils::getSubObjectIntList(const QJsonObject &json, const QString &ke
if (element.contains(subKey)) {
values.append(element[subKey].toInt());
}
}
}
return true;
}
}

View File

@ -49,6 +49,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/FreeDVMod.yaml#/FreeDVModReport"
FreqTrackerReport:
$ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerReport"
FT8DemodReport:
$ref: "http://swgserver:8081/api/swagger/include/FT8Demod.yaml#/FT8DemodReport"
M17DemodReport:
$ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodReport"
M17ModReport:

View File

@ -63,6 +63,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/FreeDVMod.yaml#/FreeDVModSettings"
FreqTrackerSettings:
$ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerSettings"
FT8DemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/FT8Demod.yaml#/FT8DemodSettings"
InterferometerSettings:
$ref: "http://swgserver:8081/api/swagger/include/Interferometer.yaml#/InterferometerSettings"
IEEE_802_15_4_ModSettings:

View File

@ -0,0 +1,100 @@
FT8DemodSettings:
description: FT8Demod
properties:
inputFrequencyOffset:
type: integer
format: int64
filterIndex:
type: integer
spanLog2:
type: integer
rfBandwidth:
type: number
format: float
lowCutoff:
type: number
format: float
fftWindow:
type: integer
description: >
FFT Window index (FFTWindow::Function):
* 0 - Bartlett
* 1 - Blackman-Harris 4 term
* 2 - Flattop
* 3 - Hamming
* 4 - Hanning
* 5 - Rectangle
* 6 - Kaiser
* 7 - Blackman 3 term
* 8 - Blackman-Harris 7 term
volume:
type: number
format: float
audioBinaural:
description: Audio binaural mode (1 if active else 0)
type: integer
audioFlipChannels:
description: Flip audio channels (1 if flipped else 0)
type: integer
dsb:
description: Double sidebands mode (1 if DSB else 0)
type: integer
audioMute:
description: Mute audio (1 if muted else 0)
type: integer
agc:
description: AGC (1 if AGC active else 0)
type: integer
agcClamping:
description: AGC clamping (1 if AGC clampingactive else 0)
type: integer
agcTimeLog2:
description: AGC averaging time log2 in milliseconds
type: integer
agcPowerThreshold:
description: Audio open RF threshold (dB)
type: integer
agcThresholdGate:
description: Audio squelch gate in ms
type: integer
rgbColor:
type: integer
title:
type: string
audioDeviceName:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
spectrumConfig:
$ref: "http://swgserver:8081/api/swagger/include/GLSpectrum.yaml#/GLSpectrum"
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
FT8DemodReport:
description: FT8Demod
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
squelch:
description: Audio squelch status (1 if open else 0)
type: integer
audioSampleRate:
type: integer
channelSampleRate:
type: integer

View File

@ -3517,6 +3517,9 @@ margin-bottom: 20px;
"FreqTrackerReport" : {
"$ref" : "#/definitions/FreqTrackerReport"
},
"FT8DemodReport" : {
"$ref" : "#/definitions/FT8DemodReport"
},
"M17DemodReport" : {
"$ref" : "#/definitions/M17DemodReport"
},
@ -3669,6 +3672,9 @@ margin-bottom: 20px;
"FreqTrackerSettings" : {
"$ref" : "#/definitions/FreqTrackerSettings"
},
"FT8DemodSettings" : {
"$ref" : "#/definitions/FT8DemodSettings"
},
"InterferometerSettings" : {
"$ref" : "#/definitions/InterferometerSettings"
},
@ -5555,6 +5561,131 @@ margin-bottom: 20px;
}
},
"description" : "FFT filter band definition"
};
defs.FT8DemodReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "power received in channel (dB)"
},
"squelch" : {
"type" : "integer",
"description" : "Audio squelch status (1 if open else 0)"
},
"audioSampleRate" : {
"type" : "integer"
},
"channelSampleRate" : {
"type" : "integer"
}
},
"description" : "FT8Demod"
};
defs.FT8DemodSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"filterIndex" : {
"type" : "integer"
},
"spanLog2" : {
"type" : "integer"
},
"rfBandwidth" : {
"type" : "number",
"format" : "float"
},
"lowCutoff" : {
"type" : "number",
"format" : "float"
},
"fftWindow" : {
"type" : "integer",
"description" : "FFT Window index (FFTWindow::Function):\n * 0 - Bartlett\n * 1 - Blackman-Harris 4 term\n * 2 - Flattop\n * 3 - Hamming\n * 4 - Hanning\n * 5 - Rectangle\n * 6 - Kaiser\n * 7 - Blackman 3 term\n * 8 - Blackman-Harris 7 term\n"
},
"volume" : {
"type" : "number",
"format" : "float"
},
"audioBinaural" : {
"type" : "integer",
"description" : "Audio binaural mode (1 if active else 0)"
},
"audioFlipChannels" : {
"type" : "integer",
"description" : "Flip audio channels (1 if flipped else 0)"
},
"dsb" : {
"type" : "integer",
"description" : "Double sidebands mode (1 if DSB else 0)"
},
"audioMute" : {
"type" : "integer",
"description" : "Mute audio (1 if muted else 0)"
},
"agc" : {
"type" : "integer",
"description" : "AGC (1 if AGC active else 0)"
},
"agcClamping" : {
"type" : "integer",
"description" : "AGC clamping (1 if AGC clampingactive else 0)"
},
"agcTimeLog2" : {
"type" : "integer",
"description" : "AGC averaging time log2 in milliseconds"
},
"agcPowerThreshold" : {
"type" : "integer",
"description" : "Audio open RF threshold (dB)"
},
"agcThresholdGate" : {
"type" : "integer",
"description" : "Audio squelch gate in ms"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"audioDeviceName" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
},
"spectrumConfig" : {
"$ref" : "#/definitions/GLSpectrum"
},
"channelMarker" : {
"$ref" : "#/definitions/ChannelMarker"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "FT8Demod"
};
defs.Feature = {
"required" : [ "id", "index", "title", "uid" ],
@ -56780,7 +56911,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2022-12-16T06:50:35.932+01:00
Generated 2023-01-14T23:57:54.939+01:00
</div>
</div>
</div>

View File

@ -70,6 +70,8 @@ SWGChannelReport::SWGChannelReport() {
m_free_dv_mod_report_isSet = false;
freq_tracker_report = nullptr;
m_freq_tracker_report_isSet = false;
ft8_demod_report = nullptr;
m_ft8_demod_report_isSet = false;
m17_demod_report = nullptr;
m_m17_demod_report_isSet = false;
m17_mod_report = nullptr;
@ -160,6 +162,8 @@ SWGChannelReport::init() {
m_free_dv_mod_report_isSet = false;
freq_tracker_report = new SWGFreqTrackerReport();
m_freq_tracker_report_isSet = false;
ft8_demod_report = new SWGFT8DemodReport();
m_ft8_demod_report_isSet = false;
m17_demod_report = new SWGM17DemodReport();
m_m17_demod_report_isSet = false;
m17_mod_report = new SWGM17ModReport();
@ -265,6 +269,9 @@ SWGChannelReport::cleanup() {
if(freq_tracker_report != nullptr) {
delete freq_tracker_report;
}
if(ft8_demod_report != nullptr) {
delete ft8_demod_report;
}
if(m17_demod_report != nullptr) {
delete m17_demod_report;
}
@ -380,6 +387,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&freq_tracker_report, pJson["FreqTrackerReport"], "SWGFreqTrackerReport", "SWGFreqTrackerReport");
::SWGSDRangel::setValue(&ft8_demod_report, pJson["FT8DemodReport"], "SWGFT8DemodReport", "SWGFT8DemodReport");
::SWGSDRangel::setValue(&m17_demod_report, pJson["M17DemodReport"], "SWGM17DemodReport", "SWGM17DemodReport");
::SWGSDRangel::setValue(&m17_mod_report, pJson["M17ModReport"], "SWGM17ModReport", "SWGM17ModReport");
@ -499,6 +508,9 @@ SWGChannelReport::asJsonObject() {
if((freq_tracker_report != nullptr) && (freq_tracker_report->isSet())){
toJsonValue(QString("FreqTrackerReport"), freq_tracker_report, obj, QString("SWGFreqTrackerReport"));
}
if((ft8_demod_report != nullptr) && (ft8_demod_report->isSet())){
toJsonValue(QString("FT8DemodReport"), ft8_demod_report, obj, QString("SWGFT8DemodReport"));
}
if((m17_demod_report != nullptr) && (m17_demod_report->isSet())){
toJsonValue(QString("M17DemodReport"), m17_demod_report, obj, QString("SWGM17DemodReport"));
}
@ -773,6 +785,16 @@ SWGChannelReport::setFreqTrackerReport(SWGFreqTrackerReport* freq_tracker_report
this->m_freq_tracker_report_isSet = true;
}
SWGFT8DemodReport*
SWGChannelReport::getFt8DemodReport() {
return ft8_demod_report;
}
void
SWGChannelReport::setFt8DemodReport(SWGFT8DemodReport* ft8_demod_report) {
this->ft8_demod_report = ft8_demod_report;
this->m_ft8_demod_report_isSet = true;
}
SWGM17DemodReport*
SWGChannelReport::getM17DemodReport() {
return m17_demod_report;
@ -1041,6 +1063,9 @@ SWGChannelReport::isSet(){
if(freq_tracker_report && freq_tracker_report->isSet()){
isObjectUpdated = true; break;
}
if(ft8_demod_report && ft8_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(m17_demod_report && m17_demod_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -35,6 +35,7 @@
#include "SWGDATVModReport.h"
#include "SWGDOA2Report.h"
#include "SWGDSDDemodReport.h"
#include "SWGFT8DemodReport.h"
#include "SWGFileSinkReport.h"
#include "SWGFileSourceReport.h"
#include "SWGFreeDVDemodReport.h"
@ -144,6 +145,9 @@ public:
SWGFreqTrackerReport* getFreqTrackerReport();
void setFreqTrackerReport(SWGFreqTrackerReport* freq_tracker_report);
SWGFT8DemodReport* getFt8DemodReport();
void setFt8DemodReport(SWGFT8DemodReport* ft8_demod_report);
SWGM17DemodReport* getM17DemodReport();
void setM17DemodReport(SWGM17DemodReport* m17_demod_report);
@ -271,6 +275,9 @@ private:
SWGFreqTrackerReport* freq_tracker_report;
bool m_freq_tracker_report_isSet;
SWGFT8DemodReport* ft8_demod_report;
bool m_ft8_demod_report_isSet;
SWGM17DemodReport* m17_demod_report;
bool m_m17_demod_report_isSet;

View File

@ -82,6 +82,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_free_dv_mod_settings_isSet = false;
freq_tracker_settings = nullptr;
m_freq_tracker_settings_isSet = false;
ft8_demod_settings = nullptr;
m_ft8_demod_settings_isSet = false;
interferometer_settings = nullptr;
m_interferometer_settings_isSet = false;
ieee_802_15_4_mod_settings = nullptr;
@ -196,6 +198,8 @@ SWGChannelSettings::init() {
m_free_dv_mod_settings_isSet = false;
freq_tracker_settings = new SWGFreqTrackerSettings();
m_freq_tracker_settings_isSet = false;
ft8_demod_settings = new SWGFT8DemodSettings();
m_ft8_demod_settings_isSet = false;
interferometer_settings = new SWGInterferometerSettings();
m_interferometer_settings_isSet = false;
ieee_802_15_4_mod_settings = new SWGIEEE_802_15_4_ModSettings();
@ -327,6 +331,9 @@ SWGChannelSettings::cleanup() {
if(freq_tracker_settings != nullptr) {
delete freq_tracker_settings;
}
if(ft8_demod_settings != nullptr) {
delete ft8_demod_settings;
}
if(interferometer_settings != nullptr) {
delete interferometer_settings;
}
@ -472,6 +479,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&freq_tracker_settings, pJson["FreqTrackerSettings"], "SWGFreqTrackerSettings", "SWGFreqTrackerSettings");
::SWGSDRangel::setValue(&ft8_demod_settings, pJson["FT8DemodSettings"], "SWGFT8DemodSettings", "SWGFT8DemodSettings");
::SWGSDRangel::setValue(&interferometer_settings, pJson["InterferometerSettings"], "SWGInterferometerSettings", "SWGInterferometerSettings");
::SWGSDRangel::setValue(&ieee_802_15_4_mod_settings, pJson["IEEE_802_15_4_ModSettings"], "SWGIEEE_802_15_4_ModSettings", "SWGIEEE_802_15_4_ModSettings");
@ -621,6 +630,9 @@ SWGChannelSettings::asJsonObject() {
if((freq_tracker_settings != nullptr) && (freq_tracker_settings->isSet())){
toJsonValue(QString("FreqTrackerSettings"), freq_tracker_settings, obj, QString("SWGFreqTrackerSettings"));
}
if((ft8_demod_settings != nullptr) && (ft8_demod_settings->isSet())){
toJsonValue(QString("FT8DemodSettings"), ft8_demod_settings, obj, QString("SWGFT8DemodSettings"));
}
if((interferometer_settings != nullptr) && (interferometer_settings->isSet())){
toJsonValue(QString("InterferometerSettings"), interferometer_settings, obj, QString("SWGInterferometerSettings"));
}
@ -973,6 +985,16 @@ SWGChannelSettings::setFreqTrackerSettings(SWGFreqTrackerSettings* freq_tracker_
this->m_freq_tracker_settings_isSet = true;
}
SWGFT8DemodSettings*
SWGChannelSettings::getFt8DemodSettings() {
return ft8_demod_settings;
}
void
SWGChannelSettings::setFt8DemodSettings(SWGFT8DemodSettings* ft8_demod_settings) {
this->ft8_demod_settings = ft8_demod_settings;
this->m_ft8_demod_settings_isSet = true;
}
SWGInterferometerSettings*
SWGChannelSettings::getInterferometerSettings() {
return interferometer_settings;
@ -1319,6 +1341,9 @@ SWGChannelSettings::isSet(){
if(freq_tracker_settings && freq_tracker_settings->isSet()){
isObjectUpdated = true; break;
}
if(ft8_demod_settings && ft8_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(interferometer_settings && interferometer_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -40,6 +40,7 @@
#include "SWGDATVModSettings.h"
#include "SWGDOA2Settings.h"
#include "SWGDSDDemodSettings.h"
#include "SWGFT8DemodSettings.h"
#include "SWGFileSinkSettings.h"
#include "SWGFileSourceSettings.h"
#include "SWGFreeDVDemodSettings.h"
@ -172,6 +173,9 @@ public:
SWGFreqTrackerSettings* getFreqTrackerSettings();
void setFreqTrackerSettings(SWGFreqTrackerSettings* freq_tracker_settings);
SWGFT8DemodSettings* getFt8DemodSettings();
void setFt8DemodSettings(SWGFT8DemodSettings* ft8_demod_settings);
SWGInterferometerSettings* getInterferometerSettings();
void setInterferometerSettings(SWGInterferometerSettings* interferometer_settings);
@ -335,6 +339,9 @@ private:
SWGFreqTrackerSettings* freq_tracker_settings;
bool m_freq_tracker_settings_isSet;
SWGFT8DemodSettings* ft8_demod_settings;
bool m_ft8_demod_settings_isSet;
SWGInterferometerSettings* interferometer_settings;
bool m_interferometer_settings_isSet;

View File

@ -0,0 +1,177 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGFT8DemodReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGFT8DemodReport::SWGFT8DemodReport(QString* json) {
init();
this->fromJson(*json);
}
SWGFT8DemodReport::SWGFT8DemodReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
squelch = 0;
m_squelch_isSet = false;
audio_sample_rate = 0;
m_audio_sample_rate_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGFT8DemodReport::~SWGFT8DemodReport() {
this->cleanup();
}
void
SWGFT8DemodReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
squelch = 0;
m_squelch_isSet = false;
audio_sample_rate = 0;
m_audio_sample_rate_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGFT8DemodReport::cleanup() {
}
SWGFT8DemodReport*
SWGFT8DemodReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGFT8DemodReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", "");
::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGFT8DemodReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGFT8DemodReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_squelch_isSet){
obj->insert("squelch", QJsonValue(squelch));
}
if(m_audio_sample_rate_isSet){
obj->insert("audioSampleRate", QJsonValue(audio_sample_rate));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGFT8DemodReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGFT8DemodReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGFT8DemodReport::getSquelch() {
return squelch;
}
void
SWGFT8DemodReport::setSquelch(qint32 squelch) {
this->squelch = squelch;
this->m_squelch_isSet = true;
}
qint32
SWGFT8DemodReport::getAudioSampleRate() {
return audio_sample_rate;
}
void
SWGFT8DemodReport::setAudioSampleRate(qint32 audio_sample_rate) {
this->audio_sample_rate = audio_sample_rate;
this->m_audio_sample_rate_isSet = true;
}
qint32
SWGFT8DemodReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGFT8DemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGFT8DemodReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_squelch_isSet){
isObjectUpdated = true; break;
}
if(m_audio_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,76 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGFT8DemodReport.h
*
* FT8Demod
*/
#ifndef SWGFT8DemodReport_H_
#define SWGFT8DemodReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGFT8DemodReport: public SWGObject {
public:
SWGFT8DemodReport();
SWGFT8DemodReport(QString* json);
virtual ~SWGFT8DemodReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGFT8DemodReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getSquelch();
void setSquelch(qint32 squelch);
qint32 getAudioSampleRate();
void setAudioSampleRate(qint32 audio_sample_rate);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 squelch;
bool m_squelch_isSet;
qint32 audio_sample_rate;
bool m_audio_sample_rate_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGFT8DemodReport_H_ */

View File

@ -0,0 +1,741 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGFT8DemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGFT8DemodSettings::SWGFT8DemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGFT8DemodSettings::SWGFT8DemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
filter_index = 0;
m_filter_index_isSet = false;
span_log2 = 0;
m_span_log2_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
low_cutoff = 0.0f;
m_low_cutoff_isSet = false;
fft_window = 0;
m_fft_window_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_binaural = 0;
m_audio_binaural_isSet = false;
audio_flip_channels = 0;
m_audio_flip_channels_isSet = false;
dsb = 0;
m_dsb_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
agc = 0;
m_agc_isSet = false;
agc_clamping = 0;
m_agc_clamping_isSet = false;
agc_time_log2 = 0;
m_agc_time_log2_isSet = false;
agc_power_threshold = 0;
m_agc_power_threshold_isSet = false;
agc_threshold_gate = 0;
m_agc_threshold_gate_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
audio_device_name = nullptr;
m_audio_device_name_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
spectrum_config = nullptr;
m_spectrum_config_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGFT8DemodSettings::~SWGFT8DemodSettings() {
this->cleanup();
}
void
SWGFT8DemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
filter_index = 0;
m_filter_index_isSet = false;
span_log2 = 0;
m_span_log2_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
low_cutoff = 0.0f;
m_low_cutoff_isSet = false;
fft_window = 0;
m_fft_window_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_binaural = 0;
m_audio_binaural_isSet = false;
audio_flip_channels = 0;
m_audio_flip_channels_isSet = false;
dsb = 0;
m_dsb_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
agc = 0;
m_agc_isSet = false;
agc_clamping = 0;
m_agc_clamping_isSet = false;
agc_time_log2 = 0;
m_agc_time_log2_isSet = false;
agc_power_threshold = 0;
m_agc_power_threshold_isSet = false;
agc_threshold_gate = 0;
m_agc_threshold_gate_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
audio_device_name = new QString("");
m_audio_device_name_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
spectrum_config = new SWGGLSpectrum();
m_spectrum_config_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGFT8DemodSettings::cleanup() {
if(title != nullptr) {
delete title;
}
if(audio_device_name != nullptr) {
delete audio_device_name;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(spectrum_config != nullptr) {
delete spectrum_config;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGFT8DemodSettings*
SWGFT8DemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&filter_index, pJson["filterIndex"], "qint32", "");
::SWGSDRangel::setValue(&span_log2, pJson["spanLog2"], "qint32", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&low_cutoff, pJson["lowCutoff"], "float", "");
::SWGSDRangel::setValue(&fft_window, pJson["fftWindow"], "qint32", "");
::SWGSDRangel::setValue(&volume, pJson["volume"], "float", "");
::SWGSDRangel::setValue(&audio_binaural, pJson["audioBinaural"], "qint32", "");
::SWGSDRangel::setValue(&audio_flip_channels, pJson["audioFlipChannels"], "qint32", "");
::SWGSDRangel::setValue(&dsb, pJson["dsb"], "qint32", "");
::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", "");
::SWGSDRangel::setValue(&agc, pJson["agc"], "qint32", "");
::SWGSDRangel::setValue(&agc_clamping, pJson["agcClamping"], "qint32", "");
::SWGSDRangel::setValue(&agc_time_log2, pJson["agcTimeLog2"], "qint32", "");
::SWGSDRangel::setValue(&agc_power_threshold, pJson["agcPowerThreshold"], "qint32", "");
::SWGSDRangel::setValue(&agc_threshold_gate, pJson["agcThresholdGate"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
::SWGSDRangel::setValue(&spectrum_config, pJson["spectrumConfig"], "SWGGLSpectrum", "SWGGLSpectrum");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGFT8DemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGFT8DemodSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_filter_index_isSet){
obj->insert("filterIndex", QJsonValue(filter_index));
}
if(m_span_log2_isSet){
obj->insert("spanLog2", QJsonValue(span_log2));
}
if(m_rf_bandwidth_isSet){
obj->insert("rfBandwidth", QJsonValue(rf_bandwidth));
}
if(m_low_cutoff_isSet){
obj->insert("lowCutoff", QJsonValue(low_cutoff));
}
if(m_fft_window_isSet){
obj->insert("fftWindow", QJsonValue(fft_window));
}
if(m_volume_isSet){
obj->insert("volume", QJsonValue(volume));
}
if(m_audio_binaural_isSet){
obj->insert("audioBinaural", QJsonValue(audio_binaural));
}
if(m_audio_flip_channels_isSet){
obj->insert("audioFlipChannels", QJsonValue(audio_flip_channels));
}
if(m_dsb_isSet){
obj->insert("dsb", QJsonValue(dsb));
}
if(m_audio_mute_isSet){
obj->insert("audioMute", QJsonValue(audio_mute));
}
if(m_agc_isSet){
obj->insert("agc", QJsonValue(agc));
}
if(m_agc_clamping_isSet){
obj->insert("agcClamping", QJsonValue(agc_clamping));
}
if(m_agc_time_log2_isSet){
obj->insert("agcTimeLog2", QJsonValue(agc_time_log2));
}
if(m_agc_power_threshold_isSet){
obj->insert("agcPowerThreshold", QJsonValue(agc_power_threshold));
}
if(m_agc_threshold_gate_isSet){
obj->insert("agcThresholdGate", QJsonValue(agc_threshold_gate));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(audio_device_name != nullptr && *audio_device_name != QString("")){
toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString"));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
if((spectrum_config != nullptr) && (spectrum_config->isSet())){
toJsonValue(QString("spectrumConfig"), spectrum_config, obj, QString("SWGGLSpectrum"));
}
if((channel_marker != nullptr) && (channel_marker->isSet())){
toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker"));
}
if((rollup_state != nullptr) && (rollup_state->isSet())){
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
}
return obj;
}
qint64
SWGFT8DemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGFT8DemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
qint32
SWGFT8DemodSettings::getFilterIndex() {
return filter_index;
}
void
SWGFT8DemodSettings::setFilterIndex(qint32 filter_index) {
this->filter_index = filter_index;
this->m_filter_index_isSet = true;
}
qint32
SWGFT8DemodSettings::getSpanLog2() {
return span_log2;
}
void
SWGFT8DemodSettings::setSpanLog2(qint32 span_log2) {
this->span_log2 = span_log2;
this->m_span_log2_isSet = true;
}
float
SWGFT8DemodSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGFT8DemodSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
float
SWGFT8DemodSettings::getLowCutoff() {
return low_cutoff;
}
void
SWGFT8DemodSettings::setLowCutoff(float low_cutoff) {
this->low_cutoff = low_cutoff;
this->m_low_cutoff_isSet = true;
}
qint32
SWGFT8DemodSettings::getFftWindow() {
return fft_window;
}
void
SWGFT8DemodSettings::setFftWindow(qint32 fft_window) {
this->fft_window = fft_window;
this->m_fft_window_isSet = true;
}
float
SWGFT8DemodSettings::getVolume() {
return volume;
}
void
SWGFT8DemodSettings::setVolume(float volume) {
this->volume = volume;
this->m_volume_isSet = true;
}
qint32
SWGFT8DemodSettings::getAudioBinaural() {
return audio_binaural;
}
void
SWGFT8DemodSettings::setAudioBinaural(qint32 audio_binaural) {
this->audio_binaural = audio_binaural;
this->m_audio_binaural_isSet = true;
}
qint32
SWGFT8DemodSettings::getAudioFlipChannels() {
return audio_flip_channels;
}
void
SWGFT8DemodSettings::setAudioFlipChannels(qint32 audio_flip_channels) {
this->audio_flip_channels = audio_flip_channels;
this->m_audio_flip_channels_isSet = true;
}
qint32
SWGFT8DemodSettings::getDsb() {
return dsb;
}
void
SWGFT8DemodSettings::setDsb(qint32 dsb) {
this->dsb = dsb;
this->m_dsb_isSet = true;
}
qint32
SWGFT8DemodSettings::getAudioMute() {
return audio_mute;
}
void
SWGFT8DemodSettings::setAudioMute(qint32 audio_mute) {
this->audio_mute = audio_mute;
this->m_audio_mute_isSet = true;
}
qint32
SWGFT8DemodSettings::getAgc() {
return agc;
}
void
SWGFT8DemodSettings::setAgc(qint32 agc) {
this->agc = agc;
this->m_agc_isSet = true;
}
qint32
SWGFT8DemodSettings::getAgcClamping() {
return agc_clamping;
}
void
SWGFT8DemodSettings::setAgcClamping(qint32 agc_clamping) {
this->agc_clamping = agc_clamping;
this->m_agc_clamping_isSet = true;
}
qint32
SWGFT8DemodSettings::getAgcTimeLog2() {
return agc_time_log2;
}
void
SWGFT8DemodSettings::setAgcTimeLog2(qint32 agc_time_log2) {
this->agc_time_log2 = agc_time_log2;
this->m_agc_time_log2_isSet = true;
}
qint32
SWGFT8DemodSettings::getAgcPowerThreshold() {
return agc_power_threshold;
}
void
SWGFT8DemodSettings::setAgcPowerThreshold(qint32 agc_power_threshold) {
this->agc_power_threshold = agc_power_threshold;
this->m_agc_power_threshold_isSet = true;
}
qint32
SWGFT8DemodSettings::getAgcThresholdGate() {
return agc_threshold_gate;
}
void
SWGFT8DemodSettings::setAgcThresholdGate(qint32 agc_threshold_gate) {
this->agc_threshold_gate = agc_threshold_gate;
this->m_agc_threshold_gate_isSet = true;
}
qint32
SWGFT8DemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGFT8DemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGFT8DemodSettings::getTitle() {
return title;
}
void
SWGFT8DemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
QString*
SWGFT8DemodSettings::getAudioDeviceName() {
return audio_device_name;
}
void
SWGFT8DemodSettings::setAudioDeviceName(QString* audio_device_name) {
this->audio_device_name = audio_device_name;
this->m_audio_device_name_isSet = true;
}
qint32
SWGFT8DemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGFT8DemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGFT8DemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGFT8DemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGFT8DemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGFT8DemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGFT8DemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGFT8DemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGFT8DemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGFT8DemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGFT8DemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGFT8DemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGGLSpectrum*
SWGFT8DemodSettings::getSpectrumConfig() {
return spectrum_config;
}
void
SWGFT8DemodSettings::setSpectrumConfig(SWGGLSpectrum* spectrum_config) {
this->spectrum_config = spectrum_config;
this->m_spectrum_config_isSet = true;
}
SWGChannelMarker*
SWGFT8DemodSettings::getChannelMarker() {
return channel_marker;
}
void
SWGFT8DemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGFT8DemodSettings::getRollupState() {
return rollup_state;
}
void
SWGFT8DemodSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGFT8DemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_filter_index_isSet){
isObjectUpdated = true; break;
}
if(m_span_log2_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(m_low_cutoff_isSet){
isObjectUpdated = true; break;
}
if(m_fft_window_isSet){
isObjectUpdated = true; break;
}
if(m_volume_isSet){
isObjectUpdated = true; break;
}
if(m_audio_binaural_isSet){
isObjectUpdated = true; break;
}
if(m_audio_flip_channels_isSet){
isObjectUpdated = true; break;
}
if(m_dsb_isSet){
isObjectUpdated = true; break;
}
if(m_audio_mute_isSet){
isObjectUpdated = true; break;
}
if(m_agc_isSet){
isObjectUpdated = true; break;
}
if(m_agc_clamping_isSet){
isObjectUpdated = true; break;
}
if(m_agc_time_log2_isSet){
isObjectUpdated = true; break;
}
if(m_agc_power_threshold_isSet){
isObjectUpdated = true; break;
}
if(m_agc_threshold_gate_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(audio_device_name && *audio_device_name != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
if(spectrum_config && spectrum_config->isSet()){
isObjectUpdated = true; break;
}
if(channel_marker && channel_marker->isSet()){
isObjectUpdated = true; break;
}
if(rollup_state && rollup_state->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,224 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGFT8DemodSettings.h
*
* FT8Demod
*/
#ifndef SWGFT8DemodSettings_H_
#define SWGFT8DemodSettings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGGLSpectrum.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGFT8DemodSettings: public SWGObject {
public:
SWGFT8DemodSettings();
SWGFT8DemodSettings(QString* json);
virtual ~SWGFT8DemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGFT8DemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
qint32 getFilterIndex();
void setFilterIndex(qint32 filter_index);
qint32 getSpanLog2();
void setSpanLog2(qint32 span_log2);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
float getLowCutoff();
void setLowCutoff(float low_cutoff);
qint32 getFftWindow();
void setFftWindow(qint32 fft_window);
float getVolume();
void setVolume(float volume);
qint32 getAudioBinaural();
void setAudioBinaural(qint32 audio_binaural);
qint32 getAudioFlipChannels();
void setAudioFlipChannels(qint32 audio_flip_channels);
qint32 getDsb();
void setDsb(qint32 dsb);
qint32 getAudioMute();
void setAudioMute(qint32 audio_mute);
qint32 getAgc();
void setAgc(qint32 agc);
qint32 getAgcClamping();
void setAgcClamping(qint32 agc_clamping);
qint32 getAgcTimeLog2();
void setAgcTimeLog2(qint32 agc_time_log2);
qint32 getAgcPowerThreshold();
void setAgcPowerThreshold(qint32 agc_power_threshold);
qint32 getAgcThresholdGate();
void setAgcThresholdGate(qint32 agc_threshold_gate);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
QString* getAudioDeviceName();
void setAudioDeviceName(QString* audio_device_name);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
SWGGLSpectrum* getSpectrumConfig();
void setSpectrumConfig(SWGGLSpectrum* spectrum_config);
SWGChannelMarker* getChannelMarker();
void setChannelMarker(SWGChannelMarker* channel_marker);
SWGRollupState* getRollupState();
void setRollupState(SWGRollupState* rollup_state);
virtual bool isSet() override;
private:
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
qint32 filter_index;
bool m_filter_index_isSet;
qint32 span_log2;
bool m_span_log2_isSet;
float rf_bandwidth;
bool m_rf_bandwidth_isSet;
float low_cutoff;
bool m_low_cutoff_isSet;
qint32 fft_window;
bool m_fft_window_isSet;
float volume;
bool m_volume_isSet;
qint32 audio_binaural;
bool m_audio_binaural_isSet;
qint32 audio_flip_channels;
bool m_audio_flip_channels_isSet;
qint32 dsb;
bool m_dsb_isSet;
qint32 audio_mute;
bool m_audio_mute_isSet;
qint32 agc;
bool m_agc_isSet;
qint32 agc_clamping;
bool m_agc_clamping_isSet;
qint32 agc_time_log2;
bool m_agc_time_log2_isSet;
qint32 agc_power_threshold;
bool m_agc_power_threshold_isSet;
qint32 agc_threshold_gate;
bool m_agc_threshold_gate_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
QString* audio_device_name;
bool m_audio_device_name_isSet;
qint32 stream_index;
bool m_stream_index_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
SWGGLSpectrum* spectrum_config;
bool m_spectrum_config_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGFT8DemodSettings_H_ */

View File

@ -114,6 +114,8 @@
#include "SWGFCDProPlusSettings.h"
#include "SWGFCDProSettings.h"
#include "SWGFFTBand.h"
#include "SWGFT8DemodReport.h"
#include "SWGFT8DemodSettings.h"
#include "SWGFeature.h"
#include "SWGFeatureActions.h"
#include "SWGFeatureConfig.h"
@ -843,6 +845,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGFT8DemodReport").compare(type) == 0) {
SWGFT8DemodReport *obj = new SWGFT8DemodReport();
obj->init();
return obj;
}
if(QString("SWGFT8DemodSettings").compare(type) == 0) {
SWGFT8DemodSettings *obj = new SWGFT8DemodSettings();
obj->init();
return obj;
}
if(QString("SWGFeature").compare(type) == 0) {
SWGFeature *obj = new SWGFeature();
obj->init();