1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-25 04:53:27 -04:00

Merge pull request #1610 from srcejon/navtex_rtty

Navtex and RTTY Demodulators
This commit is contained in:
Edouard Griffiths
2023-03-04 06:39:00 +01:00
committed by GitHub
77 changed files with 13621 additions and 12 deletions
+2
View File
@@ -81,6 +81,8 @@ 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)
option(ENABLE_CHANNELRX_DEMODNAVTEX "Enable channelrx demodnavtex plugin" ON)
option(ENABLE_CHANNELRX_DEMODRTTY "Enable channelrx demodrtty plugin" ON)
# Channel Tx enablers
option(ENABLE_CHANNELTX "Enable channeltx plugins" ON)
Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+8
View File
@@ -113,6 +113,14 @@ if (ENABLE_CHANNELRX_DEMODFT8 AND FT8_SUPPORT)
add_subdirectory(demodft8)
endif()
if (ENABLE_CHANNELRX_DEMODNAVTEX)
add_subdirectory(demodnavtex)
endif()
if (ENABLE_CHANNELRX_DEMODRTTY)
add_subdirectory(demodrtty)
endif()
if(NOT SERVER_MODE)
add_subdirectory(heatmap)
@@ -783,6 +783,8 @@ void AISDemodGUI::makeUIConnections()
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &AISDemodGUI::on_logEnable_clicked);
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &AISDemodGUI::on_logFilename_clicked);
QObject::connect(ui->logOpen, &QToolButton::clicked, this, &AISDemodGUI::on_logOpen_clicked);
QObject::connect(ui->channel1, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AISDemodGUI::on_channel1_currentIndexChanged);
QObject::connect(ui->channel2, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AISDemodGUI::on_channel2_currentIndexChanged);
}
void AISDemodGUI::updateAbsoluteCenterFrequency()
@@ -0,0 +1,63 @@
project(demodnavtex)
set(demodnavtex_SOURCES
navtexdemod.cpp
navtexdemodsettings.cpp
navtexdemodbaseband.cpp
navtexdemodsink.cpp
navtexdemodplugin.cpp
navtexdemodwebapiadapter.cpp
)
set(demodnavtex_HEADERS
navtexdemod.h
navtexdemodsettings.h
navtexdemodbaseband.h
navtexdemodsink.h
navtexdemodplugin.h
navtexdemodwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(demodnavtex_SOURCES
${demodnavtex_SOURCES}
navtexdemodgui.cpp
navtexdemodgui.ui
)
set(demodnavtex_HEADERS
${demodnavtex_HEADERS}
navtexdemodgui.h
)
set(TARGET_NAME demodnavtex)
set(TARGET_LIB "Qt::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demodnavtexsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${demodnavtex_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()
@@ -0,0 +1,751 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "navtexdemod.h"
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGNavtexDemodSettings.h"
#include "SWGChannelReport.h"
#include "SWGMapItem.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "settings/serializable.h"
#include "util/db.h"
#include "maincore.h"
MESSAGE_CLASS_DEFINITION(NavtexDemod::MsgConfigureNavtexDemod, Message)
MESSAGE_CLASS_DEFINITION(NavtexDemod::MsgCharacter, Message)
MESSAGE_CLASS_DEFINITION(NavtexDemod::MsgMessage, Message)
const char * const NavtexDemod::m_channelIdURI = "sdrangel.channel.navtexdemod";
const char * const NavtexDemod::m_channelId = "NavtexDemod";
NavtexDemod::NavtexDemod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
m_basebandSink = new NavtexDemodBaseband(this);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&NavtexDemod::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&NavtexDemod::handleIndexInDeviceSetChanged
);
}
NavtexDemod::~NavtexDemod()
{
qDebug("NavtexDemod::~NavtexDemod");
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&NavtexDemod::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
delete m_basebandSink;
}
void NavtexDemod::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 NavtexDemod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void NavtexDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
m_basebandSink->feed(begin, end);
}
void NavtexDemod::start()
{
qDebug("NavtexDemod::start");
m_basebandSink->reset();
m_basebandSink->startWork();
m_thread.start();
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
NavtexDemodBaseband::MsgConfigureNavtexDemodBaseband *msg = NavtexDemodBaseband::MsgConfigureNavtexDemodBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
}
void NavtexDemod::stop()
{
qDebug("NavtexDemod::stop");
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
bool NavtexDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureNavtexDemod::match(cmd))
{
MsgConfigureNavtexDemod& cfg = (MsgConfigureNavtexDemod&) cmd;
qDebug() << "NavtexDemod::handleMessage: MsgConfigureNavtexDemod";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "NavtexDemod::handleMessage: DSPSignalNotification";
m_basebandSink->getInputMessageQueue()->push(rep);
// Forward to GUI if any
if (m_guiMessageQueue) {
m_guiMessageQueue->push(new DSPSignalNotification(notif));
}
return true;
}
else if (NavtexDemod::MsgCharacter::match(cmd))
{
// Forward to GUI
NavtexDemod::MsgCharacter& report = (NavtexDemod::MsgCharacter&)cmd;
if (getMessageQueueToGUI())
{
NavtexDemod::MsgCharacter *msg = new NavtexDemod::MsgCharacter(report);
getMessageQueueToGUI()->push(msg);
}
return true;
}
else if (NavtexDemod::MsgMessage::match(cmd))
{
// Forward to GUI
NavtexDemod::MsgMessage& report = (NavtexDemod::MsgMessage&)cmd;
if (getMessageQueueToGUI())
{
NavtexDemod::MsgMessage *msg = new NavtexDemod::MsgMessage(report);
getMessageQueueToGUI()->push(msg);
}
// Forward via UDP
if (m_settings.m_udpEnabled)
{
qDebug() << "Forwarding to " << m_settings.m_udpAddress << ":" << m_settings.m_udpPort;
QByteArray bytes = report.getMessage().m_message.toUtf8();
m_udpSocket.writeDatagram(bytes, bytes.size(),
QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort);
}
// Write to log file
if (m_logFile.isOpen())
{
const NavtexMessage &navtexMsg = report.getMessage();
if (navtexMsg.m_valid)
{
m_logStream << navtexMsg.m_dateTime.date().toString() << ","
<< navtexMsg.m_dateTime.time().toString() << ","
<< navtexMsg.m_stationId << ","
<< navtexMsg.m_typeId << ","
<< navtexMsg.m_id << ","
<< "\"" << navtexMsg.m_message << "\","
<< report.getErrors() << ","
<< report.getRSSI()
<< "\n";
}
}
return true;
}
else if (MainCore::MsgChannelDemodQuery::match(cmd))
{
qDebug() << "NavtexDemod::handleMessage: MsgChannelDemodQuery";
sendSampleRateToDemodAnalyzer();
return true;
}
else
{
return false;
}
}
ScopeVis *NavtexDemod::getScopeSink()
{
return m_basebandSink->getScopeSink();
}
void NavtexDemod::setCenterFrequency(qint64 frequency)
{
NavtexDemodSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureNavtexDemod *msgToGUI = MsgConfigureNavtexDemod::create(settings, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void NavtexDemod::applySettings(const NavtexDemodSettings& settings, bool force)
{
qDebug() << "NavtexDemod::applySettings:"
<< " m_logEnabled: " << settings.m_logEnabled
<< " m_logFilename: " << settings.m_logFilename
<< " 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 ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((settings.m_navArea != m_settings.m_navArea) || force) {
reverseAPIKeys.append("navArea");
}
if ((settings.m_filterStation != m_settings.m_filterStation) || force) {
reverseAPIKeys.append("filterStation");
}
if ((settings.m_filterType != m_settings.m_filterType) || force) {
reverseAPIKeys.append("filterType");
}
if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) {
reverseAPIKeys.append("udpEnabled");
}
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) {
reverseAPIKeys.append("udpAddress");
}
if ((settings.m_udpPort != m_settings.m_udpPort) || force) {
reverseAPIKeys.append("udpPort");
}
if ((settings.m_logFilename != m_settings.m_logFilename) || force) {
reverseAPIKeys.append("logFilename");
}
if ((settings.m_logEnabled != m_settings.m_logEnabled) || force) {
reverseAPIKeys.append("logEnabled");
}
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");
}
NavtexDemodBaseband::MsgConfigureNavtexDemodBaseband *msg = NavtexDemodBaseband::MsgConfigureNavtexDemodBaseband::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);
}
if ((settings.m_logEnabled != m_settings.m_logEnabled)
|| (settings.m_logFilename != m_settings.m_logFilename)
|| force)
{
if (m_logFile.isOpen())
{
m_logStream.flush();
m_logFile.close();
}
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
{
m_logFile.setFileName(settings.m_logFilename);
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
qDebug() << "NavtexDemod::applySettings - Logging to: " << settings.m_logFilename;
bool newFile = m_logFile.size() == 0;
m_logStream.setDevice(&m_logFile);
if (newFile)
{
// Write header
m_logStream << "Date,Time,SID,TID,MID,Message,Errors,RSSI\n";
}
}
else
{
qDebug() << "NavtexDemod::applySettings - Unable to open log file: " << settings.m_logFilename;
}
}
}
m_settings = settings;
}
void NavtexDemod::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);
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
this,
NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE
);
messageQueue->push(msg);
}
}
}
QByteArray NavtexDemod::serialize() const
{
return m_settings.serialize();
}
bool NavtexDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureNavtexDemod *msg = MsgConfigureNavtexDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureNavtexDemod *msg = MsgConfigureNavtexDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int NavtexDemod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setNavtexDemodSettings(new SWGSDRangel::SWGNavtexDemodSettings());
response.getNavtexDemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int NavtexDemod::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int NavtexDemod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
NavtexDemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureNavtexDemod *msg = MsgConfigureNavtexDemod::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("NavtexDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureNavtexDemod *msgToGUI = MsgConfigureNavtexDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int NavtexDemod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setNavtexDemodReport(new SWGSDRangel::SWGNavtexDemodReport());
response.getNavtexDemodReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void NavtexDemod::webapiUpdateChannelSettings(
NavtexDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getNavtexDemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getNavtexDemodSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("navArea")) {
settings.m_navArea = response.getNavtexDemodSettings()->getNavArea();
}
if (channelSettingsKeys.contains("filterStation")) {
settings.m_filterStation = *response.getNavtexDemodSettings()->getFilterStation();
}
if (channelSettingsKeys.contains("filterType")) {
settings.m_filterType = *response.getNavtexDemodSettings()->getFilterType();
}
if (channelSettingsKeys.contains("udpEnabled")) {
settings.m_udpEnabled = response.getNavtexDemodSettings()->getUdpEnabled();
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getNavtexDemodSettings()->getUdpAddress();
}
if (channelSettingsKeys.contains("udpPort")) {
settings.m_udpPort = response.getNavtexDemodSettings()->getUdpPort();
}
if (channelSettingsKeys.contains("logFilename")) {
settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename();
}
if (channelSettingsKeys.contains("logEnabled")) {
settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getNavtexDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getNavtexDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getNavtexDemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getNavtexDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getNavtexDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getNavtexDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getNavtexDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getNavtexDemodSettings()->getReverseApiChannelIndex();
}
if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) {
settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.getNavtexDemodSettings()->getScopeConfig());
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getNavtexDemodSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getNavtexDemodSettings()->getRollupState());
}
}
void NavtexDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const NavtexDemodSettings& settings)
{
response.getNavtexDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getNavtexDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getNavtexDemodSettings()->setNavArea(settings.m_navArea);
response.getNavtexDemodSettings()->setFilterStation(new QString(settings.m_filterStation));
response.getNavtexDemodSettings()->setFilterType(new QString(settings.m_filterType));
response.getNavtexDemodSettings()->setUdpEnabled(settings.m_udpEnabled);
response.getNavtexDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
response.getNavtexDemodSettings()->setUdpPort(settings.m_udpPort);
response.getNavtexDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
response.getNavtexDemodSettings()->setLogEnabled(settings.m_logEnabled);
response.getNavtexDemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getNavtexDemodSettings()->getTitle()) {
*response.getNavtexDemodSettings()->getTitle() = settings.m_title;
} else {
response.getNavtexDemodSettings()->setTitle(new QString(settings.m_title));
}
response.getNavtexDemodSettings()->setStreamIndex(settings.m_streamIndex);
response.getNavtexDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getNavtexDemodSettings()->getReverseApiAddress()) {
*response.getNavtexDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getNavtexDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getNavtexDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getNavtexDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getNavtexDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_scopeGUI)
{
if (response.getNavtexDemodSettings()->getScopeConfig())
{
settings.m_scopeGUI->formatTo(response.getNavtexDemodSettings()->getScopeConfig());
}
else
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
response.getNavtexDemodSettings()->setScopeConfig(swgGLScope);
}
}
if (settings.m_channelMarker)
{
if (response.getNavtexDemodSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getNavtexDemodSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getNavtexDemodSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getNavtexDemodSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getNavtexDemodSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getNavtexDemodSettings()->setRollupState(swgRollupState);
}
}
}
void NavtexDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
response.getNavtexDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
response.getNavtexDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
}
void NavtexDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const NavtexDemodSettings& 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 NavtexDemod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const NavtexDemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("NavtexDemod"));
swgChannelSettings->setNavtexDemodSettings(new SWGSDRangel::SWGNavtexDemodSettings());
SWGSDRangel::SWGNavtexDemodSettings *swgNavtexDemodSettings = swgChannelSettings->getNavtexDemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgNavtexDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgNavtexDemodSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("navArea") || force) {
swgNavtexDemodSettings->setNavArea(settings.m_navArea);
}
if (channelSettingsKeys.contains("filterStation") || force) {
swgNavtexDemodSettings->setFilterStation(new QString(settings.m_filterStation));
}
if (channelSettingsKeys.contains("filterType") || force) {
swgNavtexDemodSettings->setFilterType(new QString(settings.m_filterType));
}
if (channelSettingsKeys.contains("udpEnabled") || force) {
swgNavtexDemodSettings->setUdpEnabled(settings.m_udpEnabled);
}
if (channelSettingsKeys.contains("udpAddress") || force) {
swgNavtexDemodSettings->setUdpAddress(new QString(settings.m_udpAddress));
}
if (channelSettingsKeys.contains("udpPort") || force) {
swgNavtexDemodSettings->setUdpPort(settings.m_udpPort);
}
if (channelSettingsKeys.contains("logFilename") || force) {
swgNavtexDemodSettings->setLogFilename(new QString(settings.m_logFilename));
}
if (channelSettingsKeys.contains("logEnabled") || force) {
swgNavtexDemodSettings->setLogEnabled(settings.m_logEnabled);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgNavtexDemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgNavtexDemodSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgNavtexDemodSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force))
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
swgNavtexDemodSettings->setScopeConfig(swgGLScope);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgNavtexDemodSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgNavtexDemodSettings->setRollupState(swgRollupState);
}
}
void NavtexDemod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "NavtexDemod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("NavtexDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void NavtexDemod::handleIndexInDeviceSetChanged(int index)
{
if (index < 0) {
return;
}
QString fifoLabel = QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(index);
m_basebandSink->setFifoLabel(fifoLabel);
}
+224
View File
@@ -0,0 +1,224 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMOD_H
#define INCLUDE_NAVTEXDEMOD_H
#include <vector>
#include <QNetworkRequest>
#include <QUdpSocket>
#include <QThread>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include <QDateTime>
#include "dsp/basebandsamplesink.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "util/navtex.h"
#include "navtexdemodbaseband.h"
#include "navtexdemodsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class ScopeVis;
class NavtexDemod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureNavtexDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const NavtexDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureNavtexDemod* create(const NavtexDemodSettings& settings, bool force)
{
return new MsgConfigureNavtexDemod(settings, force);
}
private:
NavtexDemodSettings m_settings;
bool m_force;
MsgConfigureNavtexDemod(const NavtexDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgCharacter : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getCharacter() const { return m_character; }
static MsgCharacter* create(const QString& character)
{
return new MsgCharacter(character);
}
private:
QString m_character;
MsgCharacter(const QString& character) :
m_character(character)
{}
};
class MsgMessage : public Message {
MESSAGE_CLASS_DECLARATION
public:
const NavtexMessage& getMessage() const { return m_message; }
int getErrors() const { return m_errors; }
float getRSSI() const { return m_rssi; }
static MsgMessage* create(const NavtexMessage& message, int errors, float rssi)
{
return new MsgMessage(message, errors, rssi);
}
private:
NavtexMessage m_message;
int m_errors;
float m_rssi;
MsgMessage(const NavtexMessage& message, int errors, float rssi) :
m_message(message),
m_errors(errors),
m_rssi(rssi)
{}
};
NavtexDemod(DeviceAPI *deviceAPI);
virtual ~NavtexDemod();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
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 const QString& getURI() const { return getName(); }
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 0;
}
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 NavtexDemodSettings& settings);
static void webapiUpdateChannelSettings(
NavtexDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
ScopeVis *getScopeSink();
double getMagSq() const { return m_basebandSink->getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
}
/* void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}*/
uint32_t getNumberOfDeviceStreams() const;
static const char * const m_channelIdURI;
static const char * const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
NavtexDemodBaseband* m_basebandSink;
NavtexDemodSettings m_settings;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
qint64 m_centerFrequency;
QUdpSocket m_udpSocket;
QFile m_logFile;
QTextStream m_logStream;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
virtual bool handleMessage(const Message& cmd);
void applySettings(const NavtexDemodSettings& settings, bool force = false);
void sendSampleRateToDemodAnalyzer();
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const NavtexDemodSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const NavtexDemodSettings& settings,
bool force
);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);
};
#endif // INCLUDE_NAVTEXDEMOD_H
@@ -0,0 +1,181 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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/downchannelizer.h"
#include "navtexdemodbaseband.h"
MESSAGE_CLASS_DEFINITION(NavtexDemodBaseband::MsgConfigureNavtexDemodBaseband, Message)
NavtexDemodBaseband::NavtexDemodBaseband(NavtexDemod *packetDemod) :
m_sink(packetDemod),
m_running(false)
{
qDebug("NavtexDemodBaseband::NavtexDemodBaseband");
m_sink.setScopeSink(&m_scopeSink);
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
}
NavtexDemodBaseband::~NavtexDemodBaseband()
{
m_inputMessageQueue.clear();
delete m_channelizer;
}
void NavtexDemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_sampleFifo.reset();
}
void NavtexDemodBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&NavtexDemodBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_running = true;
}
void NavtexDemodBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&NavtexDemodBaseband::handleData
);
m_running = false;
}
void NavtexDemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void NavtexDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void NavtexDemodBaseband::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 NavtexDemodBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool NavtexDemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureNavtexDemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureNavtexDemodBaseband& cfg = (MsgConfigureNavtexDemodBaseband&) cmd;
qDebug() << "NavtexDemodBaseband::handleMessage: MsgConfigureNavtexDemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "NavtexDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
return true;
}
else
{
return false;
}
}
void NavtexDemodBaseband::applySettings(const NavtexDemodSettings& settings, bool force)
{
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
m_sink.applySettings(settings, force);
m_settings = settings;
}
int NavtexDemodBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}
void NavtexDemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
@@ -0,0 +1,103 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMODBASEBAND_H
#define INCLUDE_NAVTEXDEMODBASEBAND_H
#include <QObject>
#include <QRecursiveMutex>
#include "dsp/samplesinkfifo.h"
#include "dsp/scopevis.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "navtexdemodsink.h"
class DownChannelizer;
class ChannelAPI;
class NavtexDemod;
class ScopeVis;
class NavtexDemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureNavtexDemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const NavtexDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureNavtexDemodBaseband* create(const NavtexDemodSettings& settings, bool force)
{
return new MsgConfigureNavtexDemodBaseband(settings, force);
}
private:
NavtexDemodSettings m_settings;
bool m_force;
MsgConfigureNavtexDemodBaseband(const NavtexDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
NavtexDemodBaseband(NavtexDemod *packetDemod);
~NavtexDemodBaseband();
void reset();
void startWork();
void stopWork();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_sink.getMagSqLevels(avg, peak, nbSamples);
}
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
int getChannelSampleRate() const;
ScopeVis *getScopeSink() { return &m_scopeSink; }
void setChannel(ChannelAPI *channel);
double getMagSq() const { return m_sink.getMagSq(); }
bool isRunning() const { return m_running; }
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
NavtexDemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
NavtexDemodSettings m_settings;
ScopeVis m_scopeSink;
bool m_running;
QRecursiveMutex m_mutex;
bool handleMessage(const Message& cmd);
void calculateOffset(NavtexDemodSink *sink);
void applySettings(const NavtexDemodSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_NAVTEXDEMODBASEBAND_H
@@ -0,0 +1,882 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 <QDockWidget>
#include <QMainWindow>
#include <QDebug>
#include <QMessageBox>
#include <QAction>
#include <QClipboard>
#include <QFileDialog>
#include <QScrollBar>
#include <QMenu>
#include "navtexdemodgui.h"
#include "device/deviceuiset.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "ui_navtexdemodgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/csv.h"
#include "util/db.h"
#include "util/morse.h"
#include "util/units.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "gui/decimaldelegate.h"
#include "dsp/dspengine.h"
#include "dsp/glscopesettings.h"
#include "gui/crightclickenabler.h"
#include "gui/tabletapandhold.h"
#include "gui/dialogpositioner.h"
#include "channel/channelwebapiutils.h"
#include "feature/featurewebapiutils.h"
#include "maincore.h"
#include "navtexdemod.h"
#include "navtexdemodsink.h"
void NavtexDemodGUI::resizeTable()
{
// Fill table with a row of dummy data that will size the columns nicely
// Trailing spaces are for sort arrow
int row = ui->messages->rowCount();
ui->messages->setRowCount(row + 1);
ui->messages->setItem(row, MESSAGE_COL_DATE, new QTableWidgetItem("15/04/2016-"));
ui->messages->setItem(row, MESSAGE_COL_TIME, new QTableWidgetItem("10:17"));
ui->messages->setItem(row, MESSAGE_COL_STATION_ID, new QTableWidgetItem("A"));
ui->messages->setItem(row, MESSAGE_COL_STATION, new QTableWidgetItem("Netherlands"));
ui->messages->setItem(row, MESSAGE_COL_TYPE_ID, new QTableWidgetItem("B"));
ui->messages->setItem(row, MESSAGE_COL_TYPE, new QTableWidgetItem("Meteorological\nwarning"));
ui->messages->setItem(row, MESSAGE_COL_MESSAGE_ID, new QTableWidgetItem("12"));
ui->messages->setItem(row, MESSAGE_COL_MESSAGE, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ\n123456789"));
ui->messages->setItem(row, MESSAGE_COL_ERRORS, new QTableWidgetItem("100"));
ui->messages->setItem(row, MESSAGE_COL_ERROR_PERCENT, new QTableWidgetItem("10.0%"));
ui->messages->resizeColumnsToContents();
ui->messages->removeRow(row);
}
// Columns in table reordered
void NavtexDemodGUI::messages_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{
(void) oldVisualIndex;
m_settings.m_columnIndexes[logicalIndex] = newVisualIndex;
}
// Column in table resized (when hidden size is 0)
void NavtexDemodGUI::messages_sectionResized(int logicalIndex, int oldSize, int newSize)
{
(void) oldSize;
m_settings.m_columnSizes[logicalIndex] = newSize;
}
// Right click in table header - show column select menu
void NavtexDemodGUI::columnSelectMenu(QPoint pos)
{
menu->popup(ui->messages->horizontalHeader()->viewport()->mapToGlobal(pos));
}
// Hide/show column when menu selected
void NavtexDemodGUI::columnSelectMenuChecked(bool checked)
{
(void) checked;
QAction* action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
int idx = action->data().toInt(nullptr);
ui->messages->setColumnHidden(idx, !action->isChecked());
}
}
// Create column select menu item
QAction *NavtexDemodGUI::createCheckableItem(QString &text, int idx, bool checked)
{
QAction *action = new QAction(text, this);
action->setCheckable(true);
action->setChecked(checked);
action->setData(QVariant(idx));
connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked()));
return action;
}
NavtexDemodGUI* NavtexDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
NavtexDemodGUI* gui = new NavtexDemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void NavtexDemodGUI::destroy()
{
delete this;
}
void NavtexDemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray NavtexDemodGUI::serialize() const
{
return m_settings.serialize();
}
bool NavtexDemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
qint64 NavtexDemodGUI::getFrequency()
{
qint64 frequency;
// m_deviceCenterFrequency may sometimes be 0 if using file source
frequency = (m_deviceCenterFrequency ? m_deviceCenterFrequency : 518000) + m_settings.m_inputFrequencyOffset;
return ((frequency + 500) / 1000) * 1000; // Round to nearest kHz
}
// Add row to table
void NavtexDemodGUI::messageReceived(const NavtexMessage& message, int errors, float rssi)
{
// Is scroll bar at bottom
QScrollBar *sb = ui->messages->verticalScrollBar();
bool scrollToBottom = sb->value() == sb->maximum();
ui->messages->setSortingEnabled(false);
int row = ui->messages->rowCount();
ui->messages->setRowCount(row + 1);
QTableWidgetItem *dateItem = new QTableWidgetItem();
QTableWidgetItem *timeItem = new QTableWidgetItem();
QTableWidgetItem *stationIdItem = new QTableWidgetItem();
QTableWidgetItem *stationItem = new QTableWidgetItem();
QTableWidgetItem *typeIdItem = new QTableWidgetItem();
QTableWidgetItem *typeItem = new QTableWidgetItem();
QTableWidgetItem *messageIdItem = new QTableWidgetItem();
QTableWidgetItem *messageItem = new QTableWidgetItem();
QTableWidgetItem *errorsItem = new QTableWidgetItem();
QTableWidgetItem *errorPercentItem = new QTableWidgetItem();
QTableWidgetItem *rssiItem = new QTableWidgetItem();
ui->messages->setItem(row, MESSAGE_COL_DATE, dateItem);
ui->messages->setItem(row, MESSAGE_COL_TIME, timeItem);
ui->messages->setItem(row, MESSAGE_COL_STATION_ID, stationIdItem);
ui->messages->setItem(row, MESSAGE_COL_STATION, stationItem);
ui->messages->setItem(row, MESSAGE_COL_TYPE_ID, typeIdItem);
ui->messages->setItem(row, MESSAGE_COL_TYPE, typeItem);
ui->messages->setItem(row, MESSAGE_COL_MESSAGE_ID, messageIdItem);
ui->messages->setItem(row, MESSAGE_COL_MESSAGE, messageItem);
ui->messages->setItem(row, MESSAGE_COL_ERRORS, errorsItem);
ui->messages->setItem(row, MESSAGE_COL_ERROR_PERCENT, errorPercentItem);
ui->messages->setItem(row, MESSAGE_COL_RSSI, rssiItem);
dateItem->setData(Qt::DisplayRole, message.m_dateTime.date());
timeItem->setData(Qt::DisplayRole, message.m_dateTime.time());
if (message.m_valid)
{
QString station = message.getStation(m_settings.m_navArea, getFrequency());
QString type = message.getType();
stationIdItem->setText(message.m_stationId);
stationItem->setText(station);
typeIdItem->setText(message.m_typeId);
typeItem->setText(type);
messageIdItem->setText(message.m_id);
// Add to filter comboboxes
if (!station.isEmpty() && (ui->filterStation->findText(station) == -1)) {
ui->filterStation->addItem(station);
}
if (!type.isEmpty() && (ui->filterType->findText(type) == -1)) {
ui->filterType->addItem(type);
}
errorsItem->setData(Qt::DisplayRole, errors);
float errorPC = 100.0f * errors / (message.m_message.size() * 2.0f); // SITOR-B sends each character twice
errorPercentItem->setData(Qt::DisplayRole, errorPC);
rssiItem->setData(Qt::DisplayRole, rssi);
}
messageItem->setText(message.m_message);
ui->messages->setSortingEnabled(true);
ui->messages->resizeRowToContents(row);
if (scrollToBottom) {
ui->messages->scrollToBottom();
}
filterRow(row);
}
bool NavtexDemodGUI::handleMessage(const Message& message)
{
if (NavtexDemod::MsgConfigureNavtexDemod::match(message))
{
qDebug("NavtexDemodGUI::handleMessage: NavtexDemod::MsgConfigureNavtexDemod");
const NavtexDemod::MsgConfigureNavtexDemod& cfg = (NavtexDemod::MsgConfigureNavtexDemod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
ui->scopeGUI->updateSettings();
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (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 if (NavtexDemod::MsgCharacter::match(message))
{
NavtexDemod::MsgCharacter& report = (NavtexDemod::MsgCharacter&) message;
QString c = report.getCharacter();
// Is the scroll bar at the bottom?
int scrollPos = ui->text->verticalScrollBar()->value();
bool atBottom = scrollPos >= ui->text->verticalScrollBar()->maximum();
// Move cursor to end where we want to append new text
// (user may have moved it by clicking / highlighting text)
ui->text->moveCursor(QTextCursor::End);
// Restore scroll position
ui->text->verticalScrollBar()->setValue(scrollPos);
if (c == '\b') {
ui->text->textCursor().deletePreviousChar();
} else {
ui->text->insertPlainText(c);
}
// Scroll to bottom, if we we're previously at the bottom
if (atBottom) {
ui->text->verticalScrollBar()->setValue(ui->text->verticalScrollBar()->maximum());
}
return true;
}
else if (NavtexDemod::MsgMessage::match(message))
{
NavtexDemod::MsgMessage& textMsg = (NavtexDemod::MsgMessage&) message;
messageReceived(textMsg.getMessage(), textMsg.getErrors(), textMsg.getRSSI());
return true;
}
return false;
}
void NavtexDemodGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void NavtexDemodGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void NavtexDemodGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void NavtexDemodGUI::updateTxStation()
{
const NavtexTransmitter *transmitter = NavtexTransmitter::getTransmitter(QDateTime::currentDateTimeUtc().time(),
m_settings.m_navArea,
getFrequency());
if (transmitter)
{
ui->txStation->setText(transmitter->m_station);
}
else
{
ui->txStation->setText("");
}
}
void NavtexDemodGUI::on_findOnMapFeature_clicked()
{
QString station = ui->txStation->text();
if (!station.isEmpty()) {
FeatureWebAPIUtils::mapFind(station);
}
}
void NavtexDemodGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
updateAbsoluteCenterFrequency();
applySettings();
}
void NavtexDemodGUI::on_rfBW_valueChanged(int value)
{
float bw = value;
ui->rfBWText->setText(QString("%1 Hz").arg((int)bw));
m_channelMarker.setBandwidth(bw);
m_settings.m_rfBandwidth = bw;
applySettings();
}
void NavtexDemodGUI::on_filterStation_currentIndexChanged(int index)
{
(void) index;
m_settings.m_filterStation = ui->filterStation->currentText();
filter();
applySettings();
}
void NavtexDemodGUI::on_filterType_currentIndexChanged(int index)
{
(void) index;
m_settings.m_filterType = ui->filterType->currentText();
filter();
applySettings();
}
void NavtexDemodGUI::on_clearTable_clicked()
{
ui->messages->setRowCount(0);
ui->text->clear();
}
void NavtexDemodGUI::on_udpEnabled_clicked(bool checked)
{
m_settings.m_udpEnabled = checked;
applySettings();
}
void NavtexDemodGUI::on_udpAddress_editingFinished()
{
m_settings.m_udpAddress = ui->udpAddress->text();
applySettings();
}
void NavtexDemodGUI::on_udpPort_editingFinished()
{
m_settings.m_udpPort = ui->udpPort->text().toInt();
applySettings();
}
void NavtexDemodGUI::filterRow(int row)
{
bool hidden = false;
if ((m_settings.m_filterStation != "") && (m_settings.m_filterStation != "All"))
{
QTableWidgetItem *stationItem = ui->messages->item(row, MESSAGE_COL_STATION);
if (m_settings.m_filterStation != stationItem->text()) {
hidden = true;
}
}
if ((m_settings.m_filterType != "") && (m_settings.m_filterType != "All"))
{
QTableWidgetItem *typeItem = ui->messages->item(row, MESSAGE_COL_TYPE);
if (m_settings.m_filterType != typeItem->text()) {
hidden = true;
}
}
ui->messages->setRowHidden(row, hidden);
}
void NavtexDemodGUI::filter()
{
for (int i = 0; i < ui->messages->rowCount(); i++) {
filterRow(i);
}
}
void NavtexDemodGUI::on_navArea_currentIndexChanged(int index)
{
m_settings.m_navArea = index + 1;
updateTxStation();
applySettings();
}
void NavtexDemodGUI::on_channel1_currentIndexChanged(int index)
{
m_settings.m_scopeCh1 = index;
applySettings();
}
void NavtexDemodGUI::on_channel2_currentIndexChanged(int index)
{
m_settings.m_scopeCh2 = index;
applySettings();
}
void NavtexDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
getRollupContents()->saveState(m_rollupState);
applySettings();
}
void NavtexDemodGUI::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_navtexDemod->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();
}
NavtexDemodGUI::NavtexDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::NavtexDemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_deviceCenterFrequency(0),
m_doApplySettings(true),
m_tickCount(0)
{
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/channelrx/demodnavtex/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_navtexDemod = reinterpret_cast<NavtexDemod*>(rxChannel);
m_navtexDemod->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
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->messages->setItemDelegateForColumn(MESSAGE_COL_ERROR_PERCENT, new DecimalDelegate(1));
ui->messages->setItemDelegateForColumn(MESSAGE_COL_RSSI, new DecimalDelegate(1));
m_scopeVis = m_navtexDemod->getScopeSink();
m_scopeVis->setGLScope(ui->glScope);
ui->glScope->connectTimer(MainCore::instance()->getMasterTimer());
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
// Scope settings to display the IQ waveforms
ui->scopeGUI->setPreTrigger(1);
GLScopeSettings::TraceData traceDataI, traceDataQ;
traceDataI.m_projectionType = Projector::ProjectionReal;
traceDataI.m_amp = 1.0; // for -1 to +1
traceDataI.m_ofs = 0.0; // vertical offset
traceDataQ.m_projectionType = Projector::ProjectionImag;
traceDataQ.m_amp = 1.0;
traceDataQ.m_ofs = 0.0;
ui->scopeGUI->changeTrace(0, traceDataI);
ui->scopeGUI->addTrace(traceDataQ);
ui->scopeGUI->setDisplayMode(GLScopeSettings::DisplayXYV);
ui->scopeGUI->focusOnTrace(0); // re-focus to take changes into account in the GUI
GLScopeSettings::TriggerData triggerData;
triggerData.m_triggerLevel = 0.1;
triggerData.m_triggerLevelCoarse = 10;
triggerData.m_triggerPositiveEdge = true;
ui->scopeGUI->changeTrigger(0, triggerData);
ui->scopeGUI->focusOnTrigger(0); // re-focus to take changes into account in the GUI
m_scopeVis->setLiveRate(NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE);
m_scopeVis->configure(500, 1, 0, 0, true); // not working!
//m_scopeVis->setFreeRun(false); // FIXME: add method rather than call m_scopeVis->configure()
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle("Navtex 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.setScopeGUI(ui->scopeGUI);
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()));
// Resize the table using dummy data
resizeTable();
// Allow user to reorder columns
ui->messages->horizontalHeader()->setSectionsMovable(true);
// Allow user to sort table by clicking on headers
ui->messages->setSortingEnabled(true);
// Add context menu to allow hiding/showing of columns
menu = new QMenu(ui->messages);
for (int i = 0; i < ui->messages->horizontalHeader()->count(); i++)
{
QString text = ui->messages->horizontalHeaderItem(i)->text();
menu->addAction(createCheckableItem(text, i, true));
}
ui->messages->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->messages->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint)));
// Get signals when columns change
connect(ui->messages->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(messages_sectionMoved(int, int, int)));
connect(ui->messages->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(messages_sectionResized(int, int, int)));
ui->messages->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->messages, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customContextMenuRequested(QPoint)));
TableTapAndHold *tableTapAndHold = new TableTapAndHold(ui->messages);
connect(tableTapAndHold, &TableTapAndHold::tapAndHold, this, &NavtexDemodGUI::customContextMenuRequested);
ui->scopeContainer->setVisible(false);
displaySettings();
makeUIConnections();
applySettings(true);
}
void NavtexDemodGUI::customContextMenuRequested(QPoint pos)
{
QTableWidgetItem *item = ui->messages->itemAt(pos);
if (item)
{
int row = item->row();
QString station = ui->messages->item(row, MESSAGE_COL_STATION)->text();
QMenu* tableContextMenu = new QMenu(ui->messages);
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
QAction* copyAction = new QAction("Copy", tableContextMenu);
const QString text = item->text();
connect(copyAction, &QAction::triggered, this, [text]()->void {
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(text);
});
tableContextMenu->addAction(copyAction);
if (!station.isEmpty())
{
tableContextMenu->addSeparator();
QAction* findOnMapAction = new QAction(QString("Find %1 on map").arg(station), tableContextMenu);
connect(findOnMapAction, &QAction::triggered, this, [station]()->void {
FeatureWebAPIUtils::mapFind(station);
});
tableContextMenu->addAction(findOnMapAction);
}
tableContextMenu->popup(ui->messages->viewport()->mapToGlobal(pos));
}
}
NavtexDemodGUI::~NavtexDemodGUI()
{
delete ui;
}
void NavtexDemodGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void NavtexDemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
NavtexDemod::MsgConfigureNavtexDemod* message = NavtexDemod::MsgConfigureNavtexDemod::create( m_settings, force);
m_navtexDemod->getInputMessageQueue()->push(message);
}
}
void NavtexDemodGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
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->rfBWText->setText(QString("%1 Hz").arg((int)m_settings.m_rfBandwidth));
ui->rfBW->setValue(m_settings.m_rfBandwidth);
ui->navArea->setCurrentIndex(m_settings.m_navArea - 1);
updateIndexLabel();
ui->filterStation->setCurrentText(m_settings.m_filterStation);
ui->filterType->setCurrentText(m_settings.m_filterType);
ui->udpEnabled->setChecked(m_settings.m_udpEnabled);
ui->udpAddress->setText(m_settings.m_udpAddress);
ui->udpPort->setText(QString::number(m_settings.m_udpPort));
ui->channel1->setCurrentIndex(m_settings.m_scopeCh1);
ui->channel2->setCurrentIndex(m_settings.m_scopeCh2);
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
ui->logEnable->setChecked(m_settings.m_logEnabled);
// Order and size columns
QHeaderView *header = ui->messages->horizontalHeader();
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++)
{
bool hidden = m_settings.m_columnSizes[i] == 0;
header->setSectionHidden(i, hidden);
menu->actions().at(i)->setChecked(!hidden);
if (m_settings.m_columnSizes[i] > 0)
ui->messages->setColumnWidth(i, m_settings.m_columnSizes[i]);
header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]);
}
filter();
getRollupContents()->restoreState(m_rollupState);
updateAbsoluteCenterFrequency();
blockApplySettings(false);
}
void NavtexDemodGUI::leaveEvent(QEvent* event)
{
m_channelMarker.setHighlighted(false);
ChannelGUI::leaveEvent(event);
}
void NavtexDemodGUI::enterEvent(EnterEventType* event)
{
m_channelMarker.setHighlighted(true);
ChannelGUI::enterEvent(event);
}
void NavtexDemodGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_navtexDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(100.0f + powDbAvg) / 100.0f,
(100.0f + powDbPeak) / 100.0f,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
}
if (m_tickCount % (50*10) == 0) {
updateTxStation();
}
m_tickCount++;
}
void NavtexDemodGUI::on_logEnable_clicked(bool checked)
{
m_settings.m_logEnabled = checked;
applySettings();
}
void NavtexDemodGUI::on_logFilename_clicked()
{
// Get filename to save to
QFileDialog fileDialog(nullptr, "Select file to log received messages to", "", "*.csv");
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
if (fileDialog.exec())
{
QStringList fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
m_settings.m_logFilename = fileNames[0];
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
applySettings();
}
}
}
// Read .csv log and process as received messages
void NavtexDemodGUI::on_logOpen_clicked()
{
QFileDialog fileDialog(nullptr, "Select .csv log file to read", "", "*.csv");
if (fileDialog.exec())
{
QStringList fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
QFile file(fileNames[0]);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file);
QString error;
QHash<QString, int> colIndexes = CSV::readHeader(in, {"Date", "Time", "SID", "TID", "MID", "Message"}, error);
if (error.isEmpty())
{
int dateCol = colIndexes.value("Date");
int timeCol = colIndexes.value("Time");
int sidCol = colIndexes.value("SID");
int tidCol = colIndexes.value("TID");
int midCol = colIndexes.value("MID");
int messageCol = colIndexes.value("Message");
int errorsCol = colIndexes.value("Errors");
int rssiCol = colIndexes.value("RSSI");
int maxCol = std::max({dateCol, timeCol, sidCol, tidCol, midCol, messageCol});
QMessageBox dialog(this);
dialog.setText("Reading message data");
dialog.addButton(QMessageBox::Cancel);
dialog.show();
QApplication::processEvents();
int count = 0;
bool cancelled = false;
QStringList cols;
while (!cancelled && CSV::readRow(in, &cols))
{
if (cols.size() > maxCol)
{
QDate date = QDate::fromString(cols[dateCol]);
QTime time = QTime::fromString(cols[timeCol]);
QDateTime dateTime(date, time);
NavtexMessage message(dateTime,
cols[sidCol],
cols[tidCol],
cols[midCol],
cols[messageCol]);
int errors = cols[errorsCol].toInt();
float rssi = cols[rssiCol].toFloat();
messageReceived(message, errors, rssi);
if (count % 1000 == 0)
{
QApplication::processEvents();
if (dialog.clickedButton()) {
cancelled = true;
}
}
count++;
}
}
dialog.close();
}
else
{
QMessageBox::critical(this, "Navtex Demod", error);
}
}
else
{
QMessageBox::critical(this, "Navtex Demod", QString("Failed to open file %1").arg(fileNames[0]));
}
}
}
}
void NavtexDemodGUI::makeUIConnections()
{
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &NavtexDemodGUI::on_deltaFrequency_changed);
QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &NavtexDemodGUI::on_rfBW_valueChanged);
QObject::connect(ui->navArea, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavtexDemodGUI::on_navArea_currentIndexChanged);
QObject::connect(ui->findOnMapFeature, &QPushButton::clicked, this, &NavtexDemodGUI::on_findOnMapFeature_clicked);
QObject::connect(ui->filterStation, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavtexDemodGUI::on_filterStation_currentIndexChanged);
QObject::connect(ui->filterType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavtexDemodGUI::on_filterType_currentIndexChanged);
QObject::connect(ui->clearTable, &QPushButton::clicked, this, &NavtexDemodGUI::on_clearTable_clicked);
QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &NavtexDemodGUI::on_udpEnabled_clicked);
QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &NavtexDemodGUI::on_udpAddress_editingFinished);
QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &NavtexDemodGUI::on_udpPort_editingFinished);
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &NavtexDemodGUI::on_logEnable_clicked);
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &NavtexDemodGUI::on_logFilename_clicked);
QObject::connect(ui->logOpen, &QToolButton::clicked, this, &NavtexDemodGUI::on_logOpen_clicked);
QObject::connect(ui->channel1, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavtexDemodGUI::on_channel1_currentIndexChanged);
QObject::connect(ui->channel2, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NavtexDemodGUI::on_channel2_currentIndexChanged);
}
void NavtexDemodGUI::updateAbsoluteCenterFrequency()
{
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
updateTxStation();
}
@@ -0,0 +1,150 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMODGUI_H
#define INCLUDE_NAVTEXDEMODGUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "navtexdemod.h"
#include "navtexdemodsettings.h"
class PluginAPI;
class DeviceUISet;
class BasebandSampleSink;
class ScopeVis;
class NavtexDemod;
class NavtexDemodGUI;
namespace Ui {
class NavtexDemodGUI;
}
class NavtexDemodGUI;
class NavtexDemodGUI : public ChannelGUI {
Q_OBJECT
public:
static NavtexDemodGUI* 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::NavtexDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
NavtexDemodSettings m_settings;
qint64 m_deviceCenterFrequency;
bool m_doApplySettings;
ScopeVis* m_scopeVis;
NavtexDemod* m_navtexDemod;
int m_basebandSampleRate;
uint32_t m_tickCount;
MessageQueue m_inputMessageQueue;
QMenu *menu; // Column select context menu
enum MessageCol {
MESSAGE_COL_DATE,
MESSAGE_COL_TIME,
MESSAGE_COL_STATION_ID,
MESSAGE_COL_STATION,
MESSAGE_COL_TYPE_ID,
MESSAGE_COL_TYPE,
MESSAGE_COL_MESSAGE_ID,
MESSAGE_COL_MESSAGE,
MESSAGE_COL_ERRORS,
MESSAGE_COL_ERROR_PERCENT,
MESSAGE_COL_RSSI
};
explicit NavtexDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~NavtexDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void messageReceived(const NavtexMessage& message, int errors, float rssi);
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
void updateTxStation();
qint64 getFrequency();
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
void resizeTable();
QAction *createCheckableItem(QString& text, int idx, bool checked);
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_rfBW_valueChanged(int index);
void on_navArea_currentIndexChanged(int index);
void on_findOnMapFeature_clicked();
void on_filterStation_currentIndexChanged(int index);
void on_filterType_currentIndexChanged(int index);
void on_clearTable_clicked();
void on_udpEnabled_clicked(bool checked);
void on_udpAddress_editingFinished();
void on_udpPort_editingFinished();
void on_logEnable_clicked(bool checked=false);
void on_logFilename_clicked();
void on_logOpen_clicked();
void on_channel1_currentIndexChanged(int index);
void on_channel2_currentIndexChanged(int index);
void filterRow(int row);
void filter();
void messages_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void messages_sectionResized(int logicalIndex, int oldSize, int newSize);
void columnSelectMenu(QPoint pos);
void columnSelectMenuChecked(bool checked = false);
void customContextMenuRequested(QPoint point);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
};
#endif // INCLUDE_NAVTEXDEMODGUI_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,93 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "navtexdemodgui.h"
#endif
#include "navtexdemod.h"
#include "navtexdemodwebapiadapter.h"
#include "navtexdemodplugin.h"
const PluginDescriptor NavtexDemodPlugin::m_pluginDescriptor = {
NavtexDemod::m_channelId,
QStringLiteral("Navtex Demodulator"),
QStringLiteral("7.11.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
NavtexDemodPlugin::NavtexDemodPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& NavtexDemodPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void NavtexDemodPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerRxChannel(NavtexDemod::m_channelIdURI, NavtexDemod::m_channelId, this);
}
void NavtexDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
NavtexDemod *instance = new NavtexDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* NavtexDemodPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return 0;
}
#else
ChannelGUI* NavtexDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return NavtexDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* NavtexDemodPlugin::createChannelWebAPIAdapter() const
{
return new NavtexDemodWebAPIAdapter();
}
@@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMODPLUGIN_H
#define INCLUDE_NAVTEXDEMODPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class NavtexDemodPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.navtexdemod")
public:
explicit NavtexDemodPlugin(QObject* parent = NULL);
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_NAVTEXDEMODPLUGIN_H
@@ -0,0 +1,209 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "navtexdemodsettings.h"
NavtexDemodSettings::NavtexDemodSettings() :
m_channelMarker(nullptr),
m_scopeGUI(nullptr),
m_rollupState(nullptr)
{
resetToDefaults();
}
void NavtexDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 450.0f; // OBW for 2FSK = 2 * deviation + data rate. Then add a bit for carrier frequency offset
m_navArea = 1;
m_filterStation = "All";
m_filterType = "All";
m_udpEnabled = false;
m_udpAddress = "127.0.0.1";
m_udpPort = 9999;
m_logFilename = "navtex_log.csv";
m_logEnabled = false;
m_scopeCh1 = 0;
m_scopeCh2 = 1;
m_rgbColor = QColor(100, 25, 207).rgb();
m_title = "Navtex Demodulator";
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;
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++)
{
m_columnIndexes[i] = i;
m_columnSizes[i] = -1; // Autosize
}
}
QByteArray NavtexDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_streamIndex);
s.writeS32(3, m_navArea);
s.writeString(4, m_filterStation);
s.writeString(5, m_filterType);
if (m_channelMarker) {
s.writeBlob(6, m_channelMarker->serialize());
}
s.writeFloat(7, m_rfBandwidth);
s.writeBool(9, m_udpEnabled);
s.writeString(10, m_udpAddress);
s.writeU32(11, m_udpPort);
s.writeString(12, m_logFilename);
s.writeBool(13, m_logEnabled);
s.writeS32(14, m_scopeCh1);
s.writeS32(15, m_scopeCh2);
s.writeU32(20, m_rgbColor);
s.writeString(21, m_title);
s.writeBool(22, m_useReverseAPI);
s.writeString(23, m_reverseAPIAddress);
s.writeU32(24, m_reverseAPIPort);
s.writeU32(25, m_reverseAPIDeviceIndex);
s.writeU32(26, m_reverseAPIChannelIndex);
if (m_rollupState) {
s.writeBlob(27, m_rollupState->serialize());
}
s.writeS32(28, m_workspaceIndex);
s.writeBlob(29, m_geometryBytes);
s.writeBool(30, m_hidden);
s.writeBlob(31, m_scopeGUI->serialize());
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++) {
s.writeS32(100 + i, m_columnIndexes[i]);
}
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++) {
s.writeS32(200 + i, m_columnSizes[i]);
}
return s.final();
}
bool NavtexDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_streamIndex, 0);
d.readS32(3, &m_navArea, 1);
d.readString(4, &m_filterStation, "All");
d.readString(5, &m_filterType, "All");
if (m_channelMarker)
{
d.readBlob(6, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readFloat(7, &m_rfBandwidth, 450.0f);
d.readBool(9, &m_udpEnabled);
d.readString(10, &m_udpAddress);
d.readU32(11, &utmp);
if ((utmp > 1023) && (utmp < 65535)) {
m_udpPort = utmp;
} else {
m_udpPort = 9999;
}
d.readString(12, &m_logFilename, "navtex_log.csv");
d.readBool(13, &m_logEnabled, false);
d.readS32(14, &m_scopeCh1, 0);
d.readS32(15, &m_scopeCh2, 0);
d.readU32(20, &m_rgbColor, QColor(100, 25, 207).rgb());
d.readString(21, &m_title, "Navtex Demodulator");
d.readBool(22, &m_useReverseAPI, false);
d.readString(23, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(24, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(25, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(26, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
if (m_rollupState)
{
d.readBlob(27, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(28, &m_workspaceIndex, 0);
d.readBlob(29, &m_geometryBytes);
d.readBool(30, &m_hidden, false);
if (m_scopeGUI)
{
d.readBlob(31, &bytetmp);
m_scopeGUI->deserialize(bytetmp);
}
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++) {
d.readS32(100 + i, &m_columnIndexes[i], i);
}
for (int i = 0; i < NAVTEXDEMOD_COLUMNS; i++) {
d.readS32(200 + i, &m_columnSizes[i], -1);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}
@@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMODSETTINGS_H
#define INCLUDE_NAVTEXDEMODSETTINGS_H
#include <QByteArray>
class Serializable;
// Number of columns in the table
#define NAVTEXDEMOD_COLUMNS 11
struct NavtexDemodSettings
{
qint32 m_inputFrequencyOffset;
Real m_rfBandwidth;
int m_navArea;
QString m_filterStation;
QString m_filterType;
bool m_udpEnabled;
QString m_udpAddress;
uint16_t m_udpPort;
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
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_scopeCh1;
int m_scopeCh2;
QString m_logFilename;
bool m_logEnabled;
Serializable *m_scopeGUI;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
int m_columnIndexes[NAVTEXDEMOD_COLUMNS];//!< How the columns are ordered in the table
int m_columnSizes[NAVTEXDEMOD_COLUMNS]; //!< Size of the columns in the table
static const int NAVTEXDEMOD_CHANNEL_SAMPLE_RATE = 1000; // Must be integer multiple of baud rate (x10)
static const int NAVTEXDEMOD_BAUD_RATE = 100;
static const int NAVTEXDEMOD_SAMPLES_PER_BIT = NAVTEXDEMOD_CHANNEL_SAMPLE_RATE / NAVTEXDEMOD_BAUD_RATE;
static const int NAVTEXDEMOD_FREQUENCY_SHIFT = 170;
NavtexDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* INCLUDE_NAVTEXDEMODSETTINGS_H */
@@ -0,0 +1,493 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 <QRegularExpression>
#include <complex.h>
#include "dsp/dspengine.h"
#include "dsp/scopevis.h"
#include "util/db.h"
#include "maincore.h"
#include "navtexdemod.h"
#include "navtexdemodsink.h"
NavtexDemodSink::NavtexDemodSink(NavtexDemod *packetDemod) :
m_navtexDemod(packetDemod),
m_channelSampleRate(NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE),
m_channelFrequencyOffset(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_exp(nullptr),
m_sampleBufferIndex(0)
{
m_magsq = 0.0;
m_sampleBuffer.resize(m_sampleBufferSize);
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
m_lowpassComplex1.create(301, NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE, NavtexDemodSettings::NAVTEXDEMOD_BAUD_RATE * 1.1);
m_lowpassComplex2.create(301, NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE, NavtexDemodSettings::NAVTEXDEMOD_BAUD_RATE * 1.1);
}
NavtexDemodSink::~NavtexDemodSink()
{
delete[] m_exp;
}
void NavtexDemodSink::sampleToScope(Complex sample)
{
if (m_scopeSink)
{
Real r = std::real(sample) * SDR_RX_SCALEF;
Real i = std::imag(sample) * SDR_RX_SCALEF;
m_sampleBuffer[m_sampleBufferIndex++] = Sample(r, i);
if (m_sampleBufferIndex == m_sampleBufferSize)
{
std::vector<SampleVector::const_iterator> vbegin;
vbegin.push_back(m_sampleBuffer.begin());
m_scopeSink->feed(vbegin, m_sampleBufferSize);
m_sampleBufferIndex = 0;
}
}
}
void NavtexDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
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 // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
}
void NavtexDemodSink::eraseChars(int n)
{
if (getMessageQueueToChannel())
{
QString msg = QString("%1").arg(QChar(0x8)); // Backspace
for (int i = 0; i < n; i++)
{
NavtexDemod::MsgCharacter *msg = NavtexDemod::MsgCharacter::create(QChar(0x8));
getMessageQueueToChannel()->push(msg);
}
}
}
void NavtexDemodSink::processOneSample(Complex &ci)
{
// Calculate average and peak levels for level meter
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();;
Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
// Sum power while data is being received
if (m_gotSOP)
{
m_rssiMagSqSum += magsq;
m_rssiMagSqCount++;
}
ci /= SDR_RX_SCALEF;
// Correlate with expected frequencies
Complex exp = m_exp[m_expIdx];
m_expIdx = (m_expIdx + 1) % m_expLength;
Complex corr1 = ci * exp;
Complex corr2 = ci * std::conj(exp);
// Low pass filter
Real abs1Filt = std::abs(m_lowpassComplex1.filter(corr1));
Real abs2Filt = std::abs(m_lowpassComplex2.filter(corr2));
// Envelope calculation
m_movMax1(abs1Filt);
m_movMax2(abs2Filt);
Real env1 = m_movMax1.getMaximum();
Real env2 = m_movMax2.getMaximum();
// Automatic threshold correction to compensate for frequency selective fading
// http://www.w7ay.net/site/Technical/ATC/index.html
Real bias1 = abs1Filt - 0.5 * env1;
Real bias2 = abs2Filt - 0.5 * env2;
Real unbiasedData = abs1Filt - abs2Filt;
Real biasedData = bias1 - bias2;
// Save current data for edge detection
m_dataPrev = m_data;
// Set data according to stongest correlation
m_data = biasedData < 0;
// Generate sampling clock by aligning to correlator zero-crossing
if (m_data && !m_dataPrev)
{
if ((m_clockCount > 2) && (m_clockCount < m_samplesPerBit*3/4) && m_gotSOP)
{
//qDebug() << "Clock toggle ignored at " << m_clockCount;
}
else
{
m_clockCount = 0;
m_clock = false;
}
}
else
{
// Sample in middle of symbol
if (m_clockCount == m_samplesPerBit/2)
{
receiveBit(m_data);
m_clock = true;
}
m_clockCount = (m_clockCount + 1) % m_samplesPerBit;
if (m_clockCount == 0) {
m_clock = false;
}
}
// Select signals to feed to scope
Complex scopeSample;
switch (m_settings.m_scopeCh1)
{
case 0:
scopeSample.real(ci.real());
break;
case 1:
scopeSample.real(ci.imag());
break;
case 2:
scopeSample.real(real(exp));
break;
case 3:
scopeSample.real(imag(exp));
break;
case 4:
scopeSample.real(real(corr1));
break;
case 5:
scopeSample.real(imag(corr1));
break;
case 6:
scopeSample.real(real(corr2));
break;
case 7:
scopeSample.real(imag(corr2));
break;
case 8:
scopeSample.real(abs1Filt);
break;
case 9:
scopeSample.real(abs2Filt);
break;
case 10:
scopeSample.real(env1);
break;
case 11:
scopeSample.real(env2);
break;
case 12:
scopeSample.real(unbiasedData);
break;
case 13:
scopeSample.real(biasedData);
break;
case 14:
scopeSample.real(m_data);
break;
case 15:
scopeSample.real(m_clock);
break;
case 16:
scopeSample.real(m_bit);
break;
case 17:
scopeSample.real(m_gotSOP);
break;
}
switch (m_settings.m_scopeCh2)
{
case 0:
scopeSample.imag(ci.real());
break;
case 1:
scopeSample.imag(ci.imag());
break;
case 2:
scopeSample.imag(real(exp));
break;
case 3:
scopeSample.imag(imag(exp));
break;
case 4:
scopeSample.imag(real(corr1));
break;
case 5:
scopeSample.imag(imag(corr1));
break;
case 6:
scopeSample.imag(real(corr2));
break;
case 7:
scopeSample.imag(imag(corr2));
break;
case 8:
scopeSample.imag(abs1Filt);
break;
case 9:
scopeSample.imag(abs2Filt);
break;
case 10:
scopeSample.imag(env1);
break;
case 11:
scopeSample.imag(env2);
break;
case 12:
scopeSample.imag(unbiasedData);
break;
case 13:
scopeSample.imag(biasedData);
break;
case 14:
scopeSample.imag(m_data);
break;
case 15:
scopeSample.imag(m_clock);
break;
case 16:
scopeSample.imag(m_bit);
break;
case 17:
scopeSample.imag(m_gotSOP);
break;
}
sampleToScope(scopeSample);
}
void NavtexDemodSink::receiveBit(bool bit)
{
m_bit = bit;
// Store in shift reg
m_bits = (m_bits << 1) | m_bit;
m_bitCount++;
if (!m_gotSOP)
{
if (m_bitCount == 14)
{
if ((m_bits & 0x3fff) == 0x19f8) // phase 2 followed by phase 1
{
m_gotSOP = true;
m_bitCount = 0;
m_sitorBDecoder.init();
}
else
{
m_bitCount--;
}
}
}
else
{
if (m_bitCount == 7)
{
char c = m_sitorBDecoder.decode(m_bits & 0x7f);
if (c != -1)
{
//qDebug() << "Out: " << SitorBDecoder::printable(c);
m_consecutiveErrors = 0;
if ((c != '<') && (c != '>') && (c != 0x2))
{
// 7 bytes per second, so may as well send individually to be displayed
if (getMessageQueueToChannel())
{
NavtexDemod::MsgCharacter *msg = NavtexDemod::MsgCharacter::create(SitorBDecoder::printable(c));
getMessageQueueToChannel()->push(msg);
}
// Add character to message buffer
m_messageBuffer.append(c);
}
else
{
if (m_messageBuffer.size() > 0)
{
QRegularExpression re("[Z*][C*][Z*][C*](.|\n|\r)*[N*][N*][N*][N*]");
QRegularExpressionMatch match = re.match(m_messageBuffer);
if (match.hasMatch())
{
if (getMessageQueueToChannel())
{
NavtexMessage navtexMsg = NavtexMessage(match.captured(0));
float rssi = CalcDb::dbPower(m_rssiMagSqSum / m_rssiMagSqCount);
NavtexDemod::MsgMessage *msg = NavtexDemod::MsgMessage::create(navtexMsg, m_sitorBDecoder.getErrors(), rssi);
getMessageQueueToChannel()->push(msg);
}
// Navtex messages can span multiple blocks?
m_messageBuffer = "";
}
}
if (c == 0x2) // End of text
{
// Reset demod
init();
}
}
}
if (c == '*')
{
m_errorCount++;
m_consecutiveErrors++;
// ITU 476-5 just says return to standby after the percentage of
// mutilated signals received has reached a predetermined value
// without saying what that value is
if (m_messageBuffer.size() >= 12)
{
float errorPC = m_errorCount / (float)(m_messageBuffer.size() + m_errorCount);
if (errorPC >= 0.2f)
{
//qDebug() << "Too many errors" << m_errorCount << m_messageBuffer.size();
init();
}
}
else if (m_errorCount >= 3)
{
//qDebug() << "Too many errors" << m_errorCount << m_messageBuffer.size();
eraseChars(m_messageBuffer.size());
init();
}
if (m_consecutiveErrors >= 5)
{
//qDebug() << "Too many consequtive errors";
init();
}
}
m_bitCount = 0;
}
}
}
void NavtexDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "NavtexDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) channelSampleRate / (Real) NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void NavtexDemodSink::init()
{
m_expIdx = 0;
m_bit = 0;
m_bits = 0;
m_bitCount = 0;
m_gotSOP = false;
m_errorCount = 0;
m_clockCount = 0;
m_clock = 0;
m_rssiMagSqSum = 0.0;
m_rssiMagSqCount = 0;
m_consecutiveErrors = 0;
m_sitorBDecoder.init();
m_messageBuffer = "";
}
void NavtexDemodSink::applySettings(const NavtexDemodSettings& settings, bool force)
{
qDebug() << "NavtexDemodSink::applySettings:"
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " force: " << force;
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
{
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
if (force)
{
delete[] m_exp;
m_exp = new Complex[m_expLength];
Real f0 = 0.0f;
for (int i = 0; i < m_expLength; i++)
{
m_exp[i] = Complex(cos(f0), sin(f0));
f0 += 2.0f * (Real)M_PI * (NavtexDemodSettings::NAVTEXDEMOD_FREQUENCY_SHIFT/2.0f) / NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE;
}
init();
// Due to start and stop bits, we should get mark and space at least every 8 bits
// while something is being transmitted
m_movMax1.setSize(m_samplesPerBit * 8);
m_movMax2.setSize(m_samplesPerBit * 8);
}
m_settings = settings;
}
@@ -0,0 +1,145 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMODSINK_H
#define INCLUDE_NAVTEXDEMODSINK_H
#include <QVector>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/firfilter.h"
#include "util/movingaverage.h"
#include "util/movingmaximum.h"
#include "util/messagequeue.h"
#include "util/navtex.h"
#include "navtexdemodsettings.h"
class ChannelAPI;
class NavtexDemod;
class ScopeVis;
class NavtexDemodSink : public ChannelSampleSink {
public:
NavtexDemodSink(NavtexDemod *packetDemod);
~NavtexDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setScopeSink(ScopeVis* scopeSink) { m_scopeSink = scopeSink; }
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const NavtexDemodSettings& settings, bool force = false);
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
double getMagSq() const { return m_magsq; }
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;
};
ScopeVis* m_scopeSink; // Scope GUI to display baseband waveform
NavtexDemod *m_navtexDemod;
NavtexDemodSettings m_settings;
ChannelAPI *m_channel;
int m_channelSampleRate;
int m_channelFrequencyOffset;
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MessageQueue *m_messageQueueToChannel;
MovingAverageUtil<Real, double, 16> m_movingAverage;
Lowpass<Complex> m_lowpassComplex1;
Lowpass<Complex> m_lowpassComplex2;
MovingMaximum<Real> m_movMax1;
MovingMaximum<Real> m_movMax2;
static const int m_expLength = 600;
static const int m_samplesPerBit = NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE / NavtexDemodSettings::NAVTEXDEMOD_BAUD_RATE;
Complex *m_exp;
int m_expIdx;
int m_bit;
bool m_data;
bool m_dataPrev;
int m_clockCount;
bool m_clock;
double m_rssiMagSqSum;
int m_rssiMagSqCount;
unsigned short m_bits;
int m_bitCount;
bool m_gotSOP;
int m_errorCount;
int m_consecutiveErrors;
QString m_messageBuffer;
SitorBDecoder m_sitorBDecoder;
SampleVector m_sampleBuffer;
static const int m_sampleBufferSize = NavtexDemodSettings::NAVTEXDEMOD_CHANNEL_SAMPLE_RATE / 20;
int m_sampleBufferIndex;
void processOneSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
void sampleToScope(Complex sample);
void eraseChars(int n);
void init();
void receiveBit(bool bit);
};
#endif // INCLUDE_NAVTEXDEMODSINK_H
@@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "navtexdemod.h"
#include "navtexdemodwebapiadapter.h"
NavtexDemodWebAPIAdapter::NavtexDemodWebAPIAdapter()
{}
NavtexDemodWebAPIAdapter::~NavtexDemodWebAPIAdapter()
{}
int NavtexDemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setNavtexDemodSettings(new SWGSDRangel::SWGNavtexDemodSettings());
response.getNavtexDemodSettings()->init();
NavtexDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int NavtexDemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force;
(void) errorMessage;
NavtexDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}
@@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_NAVTEXDEMOD_WEBAPIADAPTER_H
#define INCLUDE_NAVTEXDEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "navtexdemodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class NavtexDemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
NavtexDemodWebAPIAdapter();
virtual ~NavtexDemodWebAPIAdapter();
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:
NavtexDemodSettings m_settings;
};
#endif // INCLUDE_NAVTEXDEMOD_WEBAPIADAPTER_H
+101
View File
@@ -0,0 +1,101 @@
<h1>Navtex Demodulator Plugin</h1>
<h2>Introduction</h2>
This plugin can be used to demodulate Navtex (Navigational Telex) transmissions, which contain marine navigational and meteorological warnings and forecasts.
These are broadcast worldwide on 518kHz (in English for international messages), 490kHz (often in the local language for national messages) and 4209.5kHz.
Navtex messages are transmitted using FSK with 170Hz separation at 100 baud, using [SITOR-B](https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.625-4-201203-I!!PDF-E.pdf]) encoding.
The [Map](../../feature/map/readme.md) feature can display the location of Navtex transmitters, along with their transmission times and frequencies.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
![Navtex Demodulator plugin GUI](../../../doc/img/NavtexDemod_plugin.png)
<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: 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>4: Navarea</h3>
Specifies the geographical area in which the receiver is in. This enables the plugin to decode transmitter station identifiers, and display which transmitter the current transmission timeslot is assigned to (5).
Note that with good propagation conditions, it is possible to receive messages from another area, so the station indicated in the message table (17) should be checked against the location given in the recevied message text.
<h3>5: TX</h3>
Displays which transmitter is assigned the current 10 minute timeslot.
<h3>6: Find TX On Map</h3>
If the [Map](../../feature/map/readme.md) feature is open, when clicked, the Map will be centered on the current transmitter (5).
<h3>7: RF Bandwidth</h3>
This specifies the bandwidth of a filter that is applied to the input signal to limit the RF bandwidth.
<h3>8: UDP</h3>
When checked, received messages are forwarded to the specified UDP address (9) and port (10).
<h3>9: UDP address</h3>
IP address of the host to forward received messages to via UDP.
<h3>10: UDP port</h3>
UDP port number to forward received messages to.
<h3>11: Station Filter</h3>
This drop down displays a list of all stations which messages have been received from. When a station other than "All" is selected, only messages from that station will be displayed in the table.
<h3>12: Message Type Filter</h3>
This drop down displays a list of all message types that have been received. When a type other than "All" is selected, only messages with that type will be displayed in the table.
<h3>13: Start/stop Logging Messages to .csv File</h3>
When checked, writes all received messages to a .csv file, specified by (14).
<h3>14: .csv Log Filename</h3>
Click to specify the name of the .csv file which received messasges are logged to.
<h3>15: Read Data from .csv File</h3>
Click to specify a previously written .csv log file, which is read and used to update the table.
<h3>16: Received Text</h3>
The received text area shows text as it is received.
<h3>17: Received Messages Table</h3>
The received messages table displays the contents of the messages that have been received.
* Date - Date the message was received.
* Time - Time the message was received.
* SID - Station identifer of the transmitting station.
* Station - SID decoded according to the currently selected navarea (4).
* TID - Message type identifier.
* MID - Message identifier.
* Message - The message text.
* Errors - The number of characters that were received with detected errors.
* Error % - The percentage of characters that were received with errors.
* RSSI - Average channel power in dB, while receiving the message.
Right clicking on the header will open a menu allowing you to select which columns are visible, or locate the station in the selected row on the [Map](../../feature/map/readme.md).
To reorder the columns, left click and drag left or right a column header.
Left click on a header to sort the table by the data in that column.
@@ -170,7 +170,7 @@ bool PacketDemodSettings::deserialize(const QByteArray& data)
m_udpPort = 9999;
}
d.readString(25, &m_logFilename, "pager_log.csv");
d.readString(25, &m_logFilename, "packet_log.csv");
d.readBool(26, &m_logEnabled, false);
if (m_rollupState)
@@ -302,7 +302,6 @@ void PacketDemodSink::applySettings(const PacketDemodSettings& settings, bool fo
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) PacketDemodSettings::PACKETDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
m_lowpass.create(301, PacketDemodSettings::PACKETDEMOD_CHANNEL_SAMPLE_RATE, settings.m_rfBandwidth / 2.0f);
}
if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force)
{
@@ -105,7 +105,6 @@ private:
MovingAverageUtil<Real, double, 16> m_movingAverage;
Lowpass<Complex> m_lowpass;
PhaseDiscriminators m_phaseDiscri;
int m_correlationLength;
@@ -0,0 +1,63 @@
project(demodrtty)
set(demodrtty_SOURCES
rttydemod.cpp
rttydemodsettings.cpp
rttydemodbaseband.cpp
rttydemodsink.cpp
rttydemodplugin.cpp
rttydemodwebapiadapter.cpp
)
set(demodrtty_HEADERS
rttydemod.h
rttydemodsettings.h
rttydemodbaseband.h
rttydemodsink.h
rttydemodplugin.h
rttydemodwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(demodrtty_SOURCES
${demodrtty_SOURCES}
rttydemodgui.cpp
rttydemodgui.ui
)
set(demodrtty_HEADERS
${demodrtty_HEADERS}
rttydemodgui.h
)
set(TARGET_NAME demodrtty)
set(TARGET_LIB "Qt::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demodrttysrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${demodrtty_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()
+103
View File
@@ -0,0 +1,103 @@
<h1>RTTY Demodulator Plugin</h1>
<h2>Introduction</h2>
This plugin can be used to demodulate RTTY (Radioteletype) transmissions.
RTTY uses BFSK (Binary Frequency Shift Keying), where transmission of data alternates between two frequencies,
the mark frequency and the space frequency. The RTTY Demodulor should be centered in between these frequencies.
The baud rate, frequency shift (difference between mark and space frequencies), bandwidth and baudot character set are configurable.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
![RTTY Demodulator plugin GUI](../../../doc/img/RTTYDemod_plugin.png)
<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: 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>4: RTTY Presets</h3>
From the presets dropdown, you can select common baud rate and frequency shift settings, or choose Custom to set these individually.
<h3>5: Baud rate</h3>
Specifies the baud rate, in symbols per second.
The tooltip will display an estimate of the received baud rate (Which will be accurate to around 5 baud), providing that the frequency shift has been set correctly.
<h3>6: Frequency shift</h3>
Specifies the frequency shift in Hertz between the mark frequency and the space frequency.
The tooltip will display an estimate of the frequency shift (Which will be accurate to around 10-20Hz), assuming that the bandwidth has been set wide enough to contain the signal.
<h3>7: RF Bandwidth</h3>
This specifies the bandwidth of a filter that is applied to the input signal to limit the RF bandwidth. This should be set wide enough to contain the mark and space frequencies and sidebands,
but not so wide to accept noise or adjacent signals.
<h3>8: UDP</h3>
When checked, received characters are forwarded to the specified UDP address (9) and port (10).
<h3>9: UDP address</h3>
IP address of the host to forward received characters to via UDP.
<h3>10: UDP port</h3>
UDP port number to forward received characters to.
<h3>11: Squelch</h3>
Sets the squelch power level in dB. Characters received with average power lower than this setting will be discarded.
<h3>12: Baudot Character Set</h3>
The baudot character set dropdown determines how the received Baudot encodings will be mapped to Unicode characters. The following character sets are supported:
* ITA 2
* UK
* European
* US
* Russian
* Murray
<h3>13: Bit ordering</h3>
Specifies whether bits are transmitted least-significant-bit first (LSB) or most-significant-bit first (MSB).
<h3>14: Mark/Space Frequency</h3>
When unchecked, the mark frequency is the higher frequency, when checked the space frequency is higher.
<h3>15: Suppress CR LF</h3>
When checked the CR CR LF sequence is just displayed as CR. This can help make more received text visible in (19).
<h3>16: Unshift on Space</h3>
When checked, the Baudot character set will shift to letters when a space character (' ') is received.
<h3>17: Start/stop Logging Messages to .txt File</h3>
When checked, writes all received characters to the .txt file specified by (16).
<h3>18: .txt Log Filename</h3>
Click to specify the name of the .txt file which received characters are logged to.
<h3>19: Received Text</h3>
The received text area shows characters as they are received.
+780
View File
@@ -0,0 +1,780 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "rttydemod.h"
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGRTTYDemodSettings.h"
#include "SWGChannelReport.h"
#include "SWGMapItem.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "settings/serializable.h"
#include "util/db.h"
#include "maincore.h"
MESSAGE_CLASS_DEFINITION(RttyDemod::MsgConfigureRttyDemod, Message)
MESSAGE_CLASS_DEFINITION(RttyDemod::MsgCharacter, Message)
MESSAGE_CLASS_DEFINITION(RttyDemod::MsgModeEstimate, Message)
const char * const RttyDemod::m_channelIdURI = "sdrangel.channel.rttydemod";
const char * const RttyDemod::m_channelId = "RTTYDemod";
RttyDemod::RttyDemod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
m_basebandSink = new RttyDemodBaseband(this);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RttyDemod::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&RttyDemod::handleIndexInDeviceSetChanged
);
}
RttyDemod::~RttyDemod()
{
qDebug("RttyDemod::~RttyDemod");
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RttyDemod::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
delete m_basebandSink;
}
void RttyDemod::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 RttyDemod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void RttyDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
m_basebandSink->feed(begin, end);
}
void RttyDemod::start()
{
qDebug("RttyDemod::start");
m_basebandSink->reset();
m_basebandSink->startWork();
m_thread.start();
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
RttyDemodBaseband::MsgConfigureRttyDemodBaseband *msg = RttyDemodBaseband::MsgConfigureRttyDemodBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
}
void RttyDemod::stop()
{
qDebug("RttyDemod::stop");
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
bool RttyDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureRttyDemod::match(cmd))
{
MsgConfigureRttyDemod& cfg = (MsgConfigureRttyDemod&) cmd;
qDebug() << "RttyDemod::handleMessage: MsgConfigureRttyDemod";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "RttyDemod::handleMessage: DSPSignalNotification";
m_basebandSink->getInputMessageQueue()->push(rep);
// Forward to GUI if any
if (m_guiMessageQueue) {
m_guiMessageQueue->push(new DSPSignalNotification(notif));
}
return true;
}
else if (RttyDemod::MsgCharacter::match(cmd))
{
// Forward to GUI
RttyDemod::MsgCharacter& report = (RttyDemod::MsgCharacter&)cmd;
if (getMessageQueueToGUI())
{
RttyDemod::MsgCharacter *msg = new RttyDemod::MsgCharacter(report);
getMessageQueueToGUI()->push(msg);
}
// Forward via UDP
if (m_settings.m_udpEnabled)
{
QByteArray bytes = report.getCharacter().toUtf8();
m_udpSocket.writeDatagram(bytes, bytes.size(),
QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort);
}
// Write to log file
if (m_logFile.isOpen()) {
m_logStream << report.getCharacter();
}
return true;
}
else if (RttyDemod::MsgModeEstimate::match(cmd))
{
// Forward to GUI
RttyDemod::MsgModeEstimate& report = (RttyDemod::MsgModeEstimate&)cmd;
if (getMessageQueueToGUI())
{
RttyDemod::MsgModeEstimate *msg = new RttyDemod::MsgModeEstimate(report);
getMessageQueueToGUI()->push(msg);
}
return true;
}
else if (MainCore::MsgChannelDemodQuery::match(cmd))
{
qDebug() << "RttyDemod::handleMessage: MsgChannelDemodQuery";
sendSampleRateToDemodAnalyzer();
return true;
}
else
{
return false;
}
}
ScopeVis *RttyDemod::getScopeSink()
{
return m_basebandSink->getScopeSink();
}
void RttyDemod::setCenterFrequency(qint64 frequency)
{
RttyDemodSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureRttyDemod *msgToGUI = MsgConfigureRttyDemod::create(settings, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void RttyDemod::applySettings(const RttyDemodSettings& settings, bool force)
{
qDebug() << "RttyDemod::applySettings:"
<< " m_logEnabled: " << settings.m_logEnabled
<< " m_logFilename: " << settings.m_logFilename
<< " 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 ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((settings.m_baudRate != m_settings.m_baudRate) || force) {
reverseAPIKeys.append("baudRate");
}
if ((settings.m_frequencyShift != m_settings.m_frequencyShift) || force) {
reverseAPIKeys.append("frequencyShift");
}
if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) {
reverseAPIKeys.append("udpEnabled");
}
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) {
reverseAPIKeys.append("udpAddress");
}
if ((settings.m_udpPort != m_settings.m_udpPort) || force) {
reverseAPIKeys.append("udpPort");
}
if ((settings.m_characterSet != m_settings.m_characterSet) || force) {
reverseAPIKeys.append("characterSet");
}
if ((settings.m_suppressCRLF != m_settings.m_suppressCRLF) || force) {
reverseAPIKeys.append("suppressCRLF");
}
if ((settings.m_unshiftOnSpace != m_settings.m_unshiftOnSpace) || force) {
reverseAPIKeys.append("unshiftOnSpace");
}
if ((settings.m_msbFirst != m_settings.m_msbFirst) || force) {
reverseAPIKeys.append("msbFirst");
}
if ((settings.m_spaceHigh != m_settings.m_spaceHigh) || force) {
reverseAPIKeys.append("spaceHigh");
}
if ((settings.m_squelch != m_settings.m_squelch) || force) {
reverseAPIKeys.append("squelch");
}
if ((settings.m_logFilename != m_settings.m_logFilename) || force) {
reverseAPIKeys.append("logFilename");
}
if ((settings.m_logEnabled != m_settings.m_logEnabled) || force) {
reverseAPIKeys.append("logEnabled");
}
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");
}
RttyDemodBaseband::MsgConfigureRttyDemodBaseband *msg = RttyDemodBaseband::MsgConfigureRttyDemodBaseband::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);
}
if ((settings.m_logEnabled != m_settings.m_logEnabled)
|| (settings.m_logFilename != m_settings.m_logFilename)
|| force)
{
if (m_logFile.isOpen())
{
m_logStream.flush();
m_logFile.close();
}
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
{
m_logFile.setFileName(settings.m_logFilename);
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
qDebug() << "RttyDemod::applySettings - Logging to: " << settings.m_logFilename;
m_logStream.setDevice(&m_logFile);
}
else
{
qDebug() << "RttyDemod::applySettings - Unable to open log file: " << settings.m_logFilename;
}
}
}
m_settings = settings;
}
void RttyDemod::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);
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
this,
RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE
);
messageQueue->push(msg);
}
}
}
QByteArray RttyDemod::serialize() const
{
return m_settings.serialize();
}
bool RttyDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureRttyDemod *msg = MsgConfigureRttyDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureRttyDemod *msg = MsgConfigureRttyDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int RttyDemod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRttyDemodSettings(new SWGSDRangel::SWGRTTYDemodSettings());
response.getRttyDemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int RttyDemod::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int RttyDemod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
RttyDemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureRttyDemod *msg = MsgConfigureRttyDemod::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("RttyDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureRttyDemod *msgToGUI = MsgConfigureRttyDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int RttyDemod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRttyDemodReport(new SWGSDRangel::SWGRTTYDemodReport());
response.getRttyDemodReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void RttyDemod::webapiUpdateChannelSettings(
RttyDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getRttyDemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getRttyDemodSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("baudRate")) {
settings.m_baudRate = response.getRttyDemodSettings()->getBaudRate();
}
if (channelSettingsKeys.contains("frequencyShift")) {
settings.m_frequencyShift = response.getRttyDemodSettings()->getFrequencyShift();
}
if (channelSettingsKeys.contains("udpEnabled")) {
settings.m_udpEnabled = response.getRttyDemodSettings()->getUdpEnabled();
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getRttyDemodSettings()->getUdpAddress();
}
if (channelSettingsKeys.contains("udpPort")) {
settings.m_udpPort = response.getRttyDemodSettings()->getUdpPort();
}
if (channelSettingsKeys.contains("characterSet")) {
settings.m_characterSet = (Baudot::CharacterSet)response.getRttyDemodSettings()->getCharacterSet();
}
if (channelSettingsKeys.contains("suppressCRLF")) {
settings.m_suppressCRLF = response.getRttyDemodSettings()->getSuppressCrlf();
}
if (channelSettingsKeys.contains("unshiftOnSpace")) {
settings.m_unshiftOnSpace = response.getRttyDemodSettings()->getUnshiftOnSpace();
}
if (channelSettingsKeys.contains("msbFirst")) {
settings.m_msbFirst = response.getRttyDemodSettings()->getMsbFirst();
}
if (channelSettingsKeys.contains("spaceHigh")) {
settings.m_spaceHigh = response.getRttyDemodSettings()->getSpaceHigh();
}
if (channelSettingsKeys.contains("squelch")) {
settings.m_squelch = response.getRttyDemodSettings()->getSquelch();
}
if (channelSettingsKeys.contains("logFilename")) {
settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename();
}
if (channelSettingsKeys.contains("logEnabled")) {
settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getRttyDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getRttyDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getRttyDemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getRttyDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getRttyDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getRttyDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getRttyDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getRttyDemodSettings()->getReverseApiChannelIndex();
}
if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) {
settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.getRttyDemodSettings()->getScopeConfig());
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getRttyDemodSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getRttyDemodSettings()->getRollupState());
}
}
void RttyDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const RttyDemodSettings& settings)
{
response.getRttyDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getRttyDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getRttyDemodSettings()->setBaudRate(settings.m_baudRate);
response.getRttyDemodSettings()->setFrequencyShift(settings.m_frequencyShift);
response.getRttyDemodSettings()->setUdpEnabled(settings.m_udpEnabled);
response.getRttyDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
response.getRttyDemodSettings()->setUdpPort(settings.m_udpPort);
response.getRttyDemodSettings()->setCharacterSet(settings.m_characterSet);
response.getRttyDemodSettings()->setSuppressCrlf(settings.m_suppressCRLF);
response.getRttyDemodSettings()->setUnshiftOnSpace(settings.m_unshiftOnSpace);
response.getRttyDemodSettings()->setMsbFirst(settings.m_msbFirst);
response.getRttyDemodSettings()->setSpaceHigh(settings.m_spaceHigh);
response.getRttyDemodSettings()->setSquelch(settings.m_squelch);
response.getRttyDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
response.getRttyDemodSettings()->setLogEnabled(settings.m_logEnabled);
response.getRttyDemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getRttyDemodSettings()->getTitle()) {
*response.getRttyDemodSettings()->getTitle() = settings.m_title;
} else {
response.getRttyDemodSettings()->setTitle(new QString(settings.m_title));
}
response.getRttyDemodSettings()->setStreamIndex(settings.m_streamIndex);
response.getRttyDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getRttyDemodSettings()->getReverseApiAddress()) {
*response.getRttyDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getRttyDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getRttyDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getRttyDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getRttyDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_scopeGUI)
{
if (response.getRttyDemodSettings()->getScopeConfig())
{
settings.m_scopeGUI->formatTo(response.getRttyDemodSettings()->getScopeConfig());
}
else
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
response.getRttyDemodSettings()->setScopeConfig(swgGLScope);
}
}
if (settings.m_channelMarker)
{
if (response.getRttyDemodSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getRttyDemodSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getRttyDemodSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getRttyDemodSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getRttyDemodSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getRttyDemodSettings()->setRollupState(swgRollupState);
}
}
}
void RttyDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
response.getRttyDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
response.getRttyDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
}
void RttyDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RttyDemodSettings& 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 RttyDemod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const RttyDemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("RttyDemod"));
swgChannelSettings->setRttyDemodSettings(new SWGSDRangel::SWGRTTYDemodSettings());
SWGSDRangel::SWGRTTYDemodSettings *swgRttyDemodSettings = swgChannelSettings->getRttyDemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgRttyDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgRttyDemodSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("baudRate") || force) {
swgRttyDemodSettings->setBaudRate(settings.m_baudRate);
}
if (channelSettingsKeys.contains("frequencyShift") || force) {
swgRttyDemodSettings->setFrequencyShift(settings.m_frequencyShift);
}
if (channelSettingsKeys.contains("udpEnabled") || force) {
swgRttyDemodSettings->setUdpEnabled(settings.m_udpEnabled);
}
if (channelSettingsKeys.contains("udpAddress") || force) {
swgRttyDemodSettings->setUdpAddress(new QString(settings.m_udpAddress));
}
if (channelSettingsKeys.contains("udpPort") || force) {
swgRttyDemodSettings->setUdpPort(settings.m_udpPort);
}
if (channelSettingsKeys.contains("characterSet") || force) {
swgRttyDemodSettings->setCharacterSet(settings.m_characterSet);
}
if (channelSettingsKeys.contains("suppressCRLF") || force) {
swgRttyDemodSettings->setSuppressCrlf(settings.m_suppressCRLF);
}
if (channelSettingsKeys.contains("unshiftOnSpace") || force) {
swgRttyDemodSettings->setUnshiftOnSpace(settings.m_unshiftOnSpace);
}
if (channelSettingsKeys.contains("msbFirst") || force) {
swgRttyDemodSettings->setMsbFirst(settings.m_msbFirst);
}
if (channelSettingsKeys.contains("spaceHigh") || force) {
swgRttyDemodSettings->setSpaceHigh(settings.m_spaceHigh);
}
if (channelSettingsKeys.contains("squelch") || force) {
swgRttyDemodSettings->setSquelch(settings.m_squelch);
}
if (channelSettingsKeys.contains("logFilename") || force) {
swgRttyDemodSettings->setLogFilename(new QString(settings.m_logFilename));
}
if (channelSettingsKeys.contains("logEnabled") || force) {
swgRttyDemodSettings->setLogEnabled(settings.m_logEnabled);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgRttyDemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgRttyDemodSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgRttyDemodSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force))
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
swgRttyDemodSettings->setScopeConfig(swgGLScope);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgRttyDemodSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgRttyDemodSettings->setRollupState(swgRollupState);
}
}
void RttyDemod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "RttyDemod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("RttyDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void RttyDemod::handleIndexInDeviceSetChanged(int index)
{
if (index < 0) {
return;
}
QString fifoLabel = QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(index);
m_basebandSink->setFifoLabel(fifoLabel);
}
+218
View File
@@ -0,0 +1,218 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMOD_H
#define INCLUDE_RTTYDEMOD_H
#include <QNetworkRequest>
#include <QUdpSocket>
#include <QThread>
#include <QFile>
#include <QTextStream>
#include "dsp/basebandsamplesink.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "rttydemodbaseband.h"
#include "rttydemodsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class ScopeVis;
class RttyDemod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureRttyDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const RttyDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureRttyDemod* create(const RttyDemodSettings& settings, bool force)
{
return new MsgConfigureRttyDemod(settings, force);
}
private:
RttyDemodSettings m_settings;
bool m_force;
MsgConfigureRttyDemod(const RttyDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
// Sent from Sink when character is decoded
class MsgCharacter : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getCharacter() const { return m_character; }
static MsgCharacter* create(const QString& character)
{
return new MsgCharacter(character);
}
private:
QString m_character;
MsgCharacter(const QString& character) :
m_character(character)
{}
};
// Sent from Sink when an estimate is made of the baud rate
class MsgModeEstimate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getBaudRate() const { return m_baudRate; }
int getFrequencyShift() const { return m_frequencyShift; }
static MsgModeEstimate* create(int baudRate, int frequencyShift)
{
return new MsgModeEstimate(baudRate, frequencyShift);
}
private:
int m_baudRate;
int m_frequencyShift;
MsgModeEstimate(int baudRate, int frequencyShift) :
m_baudRate(baudRate),
m_frequencyShift(frequencyShift)
{}
};
RttyDemod(DeviceAPI *deviceAPI);
virtual ~RttyDemod();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
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 const QString& getURI() const { return getName(); }
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 0;
}
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 RttyDemodSettings& settings);
static void webapiUpdateChannelSettings(
RttyDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
ScopeVis *getScopeSink();
double getMagSq() const { return m_basebandSink->getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
}
/* void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}*/
uint32_t getNumberOfDeviceStreams() const;
static const char * const m_channelIdURI;
static const char * const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
RttyDemodBaseband* m_basebandSink;
RttyDemodSettings m_settings;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
qint64 m_centerFrequency;
QUdpSocket m_udpSocket;
QFile m_logFile;
QTextStream m_logStream;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
virtual bool handleMessage(const Message& cmd);
void applySettings(const RttyDemodSettings& settings, bool force = false);
void sendSampleRateToDemodAnalyzer();
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RttyDemodSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const RttyDemodSettings& settings,
bool force
);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);
};
#endif // INCLUDE_RTTYDEMOD_H
@@ -0,0 +1,181 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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/downchannelizer.h"
#include "rttydemodbaseband.h"
MESSAGE_CLASS_DEFINITION(RttyDemodBaseband::MsgConfigureRttyDemodBaseband, Message)
RttyDemodBaseband::RttyDemodBaseband(RttyDemod *packetDemod) :
m_sink(packetDemod),
m_running(false)
{
qDebug("RttyDemodBaseband::RttyDemodBaseband");
m_sink.setScopeSink(&m_scopeSink);
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
}
RttyDemodBaseband::~RttyDemodBaseband()
{
m_inputMessageQueue.clear();
delete m_channelizer;
}
void RttyDemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_sampleFifo.reset();
}
void RttyDemodBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&RttyDemodBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_running = true;
}
void RttyDemodBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&RttyDemodBaseband::handleData
);
m_running = false;
}
void RttyDemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void RttyDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void RttyDemodBaseband::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 RttyDemodBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool RttyDemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureRttyDemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureRttyDemodBaseband& cfg = (MsgConfigureRttyDemodBaseband&) cmd;
qDebug() << "RttyDemodBaseband::handleMessage: MsgConfigureRttyDemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "RttyDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
return true;
}
else
{
return false;
}
}
void RttyDemodBaseband::applySettings(const RttyDemodSettings& settings, bool force)
{
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
m_sink.applySettings(settings, force);
m_settings = settings;
}
int RttyDemodBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}
void RttyDemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
@@ -0,0 +1,103 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMODBASEBAND_H
#define INCLUDE_RTTYDEMODBASEBAND_H
#include <QObject>
#include <QRecursiveMutex>
#include "dsp/samplesinkfifo.h"
#include "dsp/scopevis.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "rttydemodsink.h"
class DownChannelizer;
class ChannelAPI;
class RttyDemod;
class ScopeVis;
class RttyDemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureRttyDemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const RttyDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureRttyDemodBaseband* create(const RttyDemodSettings& settings, bool force)
{
return new MsgConfigureRttyDemodBaseband(settings, force);
}
private:
RttyDemodSettings m_settings;
bool m_force;
MsgConfigureRttyDemodBaseband(const RttyDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
RttyDemodBaseband(RttyDemod *packetDemod);
~RttyDemodBaseband();
void reset();
void startWork();
void stopWork();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_sink.getMagSqLevels(avg, peak, nbSamples);
}
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
int getChannelSampleRate() const;
ScopeVis *getScopeSink() { return &m_scopeSink; }
void setChannel(ChannelAPI *channel);
double getMagSq() const { return m_sink.getMagSq(); }
bool isRunning() const { return m_running; }
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
RttyDemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
RttyDemodSettings m_settings;
ScopeVis m_scopeSink;
bool m_running;
QRecursiveMutex m_mutex;
bool handleMessage(const Message& cmd);
void calculateOffset(RttyDemodSink *sink);
void applySettings(const RttyDemodSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_RTTYDEMODBASEBAND_H
@@ -0,0 +1,671 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 <QDockWidget>
#include <QMainWindow>
#include <QDebug>
#include <QAction>
#include <QClipboard>
#include <QFileDialog>
#include <QScrollBar>
#include <QMenu>
#include "rttydemodgui.h"
#include "device/deviceuiset.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "ui_rttydemodgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "dsp/dspengine.h"
#include "dsp/glscopesettings.h"
#include "gui/crightclickenabler.h"
#include "gui/tabletapandhold.h"
#include "gui/dialogpositioner.h"
#include "channel/channelwebapiutils.h"
#include "feature/featurewebapiutils.h"
#include "maincore.h"
#include "rttydemod.h"
#include "rttydemodsink.h"
RttyDemodGUI* RttyDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
RttyDemodGUI* gui = new RttyDemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void RttyDemodGUI::destroy()
{
delete this;
}
void RttyDemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray RttyDemodGUI::serialize() const
{
return m_settings.serialize();
}
bool RttyDemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
void RttyDemodGUI::characterReceived(QString c)
{
// Is the scroll bar at the bottom?
int scrollPos = ui->text->verticalScrollBar()->value();
bool atBottom = scrollPos >= ui->text->verticalScrollBar()->maximum();
// Move cursor to end where we want to append new text
// (user may have moved it by clicking / highlighting text)
ui->text->moveCursor(QTextCursor::End);
// Restore scroll position
ui->text->verticalScrollBar()->setValue(scrollPos);
if ((c == '\r') && (m_previousChar[1] == '\r') && m_settings.m_suppressCRLF)
{
// Don't insert yet
}
else if ((c == '\n') && (m_previousChar[0] == '\r') && (m_previousChar[1] == '\r') && m_settings.m_suppressCRLF)
{
// Change \r\r\n to \r
}
else if ((c != '\n') && (m_previousChar[0] == '\r') && (m_previousChar[1] == '\r') && m_settings.m_suppressCRLF)
{
ui->text->insertPlainText("\r"); // Insert \r we skipped
ui->text->insertPlainText(c);
}
else if (c == '\b')
{
ui->text->textCursor().deletePreviousChar();
}
else
{
ui->text->insertPlainText(c);
}
// Scroll to bottom, if we we're previously at the bottom
if (atBottom) {
ui->text->verticalScrollBar()->setValue(ui->text->verticalScrollBar()->maximum());
}
// Save last 2 previous characters
m_previousChar[0] = m_previousChar[1];
m_previousChar[1] = c;
}
bool RttyDemodGUI::handleMessage(const Message& message)
{
if (RttyDemod::MsgConfigureRttyDemod::match(message))
{
qDebug("RttyDemodGUI::handleMessage: RttyDemod::MsgConfigureRttyDemod");
const RttyDemod::MsgConfigureRttyDemod& cfg = (RttyDemod::MsgConfigureRttyDemod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
ui->scopeGUI->updateSettings();
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (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 if (RttyDemod::MsgCharacter::match(message))
{
RttyDemod::MsgCharacter& report = (RttyDemod::MsgCharacter&) message;
QString c = report.getCharacter();
characterReceived(c);
return true;
}
else if (RttyDemod::MsgModeEstimate::match(message))
{
RttyDemod::MsgModeEstimate& report = (RttyDemod::MsgModeEstimate&) message;
ui->baudRate->setToolTip(QString("Baud rate (symbols per second)\n\nEstimate: %1 baud").arg(report.getBaudRate()));
ui->frequencyShift->setToolTip(QString("Frequency shift in Hz (Difference between mark and space frequency)\n\nEstimate: %1 Hz").arg(report.getFrequencyShift()));
ui->modeEst->setText(QString("%1/%2").arg(report.getBaudRate()).arg(report.getFrequencyShift()));
return true;
}
return false;
}
void RttyDemodGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void RttyDemodGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void RttyDemodGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void RttyDemodGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
updateAbsoluteCenterFrequency();
applySettings();
}
void RttyDemodGUI::on_rfBW_valueChanged(int value)
{
float bw = value;
ui->rfBWText->setText(formatFrequency((int)bw));
m_channelMarker.setBandwidth(bw);
m_settings.m_rfBandwidth = bw;
applySettings();
}
void RttyDemodGUI::on_baudRate_currentIndexChanged(int index)
{
(void) index;
m_settings.m_baudRate = ui->baudRate->currentText().toFloat();
applySettings();
}
void RttyDemodGUI::on_frequencyShift_valueChanged(int value)
{
ui->frequencyShiftText->setText(formatFrequency(value));
m_settings.m_frequencyShift = value;
applySettings();
}
void RttyDemodGUI::on_squelch_valueChanged(int value)
{
ui->squelchText->setText(QString("%1 dB").arg(value));
m_settings.m_squelch = value;
applySettings();
}
void RttyDemodGUI::on_characterSet_currentIndexChanged(int index)
{
m_settings.m_characterSet = (Baudot::CharacterSet) index;
applySettings();
}
void RttyDemodGUI::on_suppressCRLF_clicked(bool checked)
{
m_settings.m_suppressCRLF = checked;
applySettings();
}
void RttyDemodGUI::on_mode_currentIndexChanged(int index)
{
(void) index;
QString mode = ui->mode->currentText();
bool custom = mode == "Custom";
if (!custom)
{
QStringList settings = mode.split("/");
int baudRate = settings[0].toInt();
int frequencyShift = settings[1].toInt();
int bandwidth = frequencyShift * 2 + baudRate;
ui->baudRate->setCurrentText(settings[0]);
ui->frequencyShift->setValue(frequencyShift);
ui->rfBW->setValue(bandwidth);
}
ui->baudRateLabel->setEnabled(custom);
ui->baudRate->setEnabled(custom);
ui->frequencyShiftLabel->setEnabled(custom);
ui->frequencyShift->setEnabled(custom);
ui->frequencyShiftText->setEnabled(custom);
ui->rfBWLabel->setEnabled(custom);
ui->rfBW->setEnabled(custom);
ui->rfBWText->setEnabled(custom);
//m_settings.m_mode = index;
applySettings();
}
void RttyDemodGUI::on_filter_currentIndexChanged(int index)
{
m_settings.m_filter = (RttyDemodSettings::FilterType)index;
applySettings();
}
void RttyDemodGUI::on_atc_clicked(bool checked)
{
m_settings.m_atc = checked;
applySettings();
}
void RttyDemodGUI::on_endian_clicked(bool checked)
{
m_settings.m_msbFirst = checked;
if (checked) {
ui->endian->setText("MSB");
} else {
ui->endian->setText("LSB");
}
applySettings();
}
void RttyDemodGUI::on_spaceHigh_clicked(bool checked)
{
m_settings.m_spaceHigh = checked;
if (checked) {
ui->spaceHigh->setText("M-S");
} else {
ui->spaceHigh->setText("S-M");
}
applySettings();
}
void RttyDemodGUI::on_clearTable_clicked()
{
ui->text->clear();
}
void RttyDemodGUI::on_udpEnabled_clicked(bool checked)
{
m_settings.m_udpEnabled = checked;
applySettings();
}
void RttyDemodGUI::on_udpAddress_editingFinished()
{
m_settings.m_udpAddress = ui->udpAddress->text();
applySettings();
}
void RttyDemodGUI::on_udpPort_editingFinished()
{
m_settings.m_udpPort = ui->udpPort->text().toInt();
applySettings();
}
void RttyDemodGUI::on_channel1_currentIndexChanged(int index)
{
m_settings.m_scopeCh1 = index;
applySettings();
}
void RttyDemodGUI::on_channel2_currentIndexChanged(int index)
{
m_settings.m_scopeCh2 = index;
applySettings();
}
void RttyDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
getRollupContents()->saveState(m_rollupState);
applySettings();
}
void RttyDemodGUI::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_rttyDemod->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();
}
RttyDemodGUI::RttyDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::RttyDemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_deviceCenterFrequency(0),
m_doApplySettings(true),
m_tickCount(0)
{
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/channelrx/demodrtty/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_rttyDemod = reinterpret_cast<RttyDemod*>(rxChannel);
m_rttyDemod->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
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);
m_scopeVis = m_rttyDemod->getScopeSink();
m_scopeVis->setGLScope(ui->glScope);
ui->glScope->connectTimer(MainCore::instance()->getMasterTimer());
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
// Scope settings to display the IQ waveforms
ui->scopeGUI->setPreTrigger(1);
GLScopeSettings::TraceData traceDataI, traceDataQ;
traceDataI.m_projectionType = Projector::ProjectionReal;
traceDataI.m_amp = 1.0; // for -1 to +1
traceDataI.m_ofs = 0.0; // vertical offset
traceDataQ.m_projectionType = Projector::ProjectionImag;
traceDataQ.m_amp = 1.0;
traceDataQ.m_ofs = 0.0;
ui->scopeGUI->changeTrace(0, traceDataI);
ui->scopeGUI->addTrace(traceDataQ);
ui->scopeGUI->setDisplayMode(GLScopeSettings::DisplayXYV);
ui->scopeGUI->focusOnTrace(0); // re-focus to take changes into account in the GUI
GLScopeSettings::TriggerData triggerData;
triggerData.m_triggerLevel = 0.1;
triggerData.m_triggerLevelCoarse = 10;
triggerData.m_triggerPositiveEdge = true;
ui->scopeGUI->changeTrigger(0, triggerData);
ui->scopeGUI->focusOnTrigger(0); // re-focus to take changes into account in the GUI
m_scopeVis->setLiveRate(RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE);
m_scopeVis->configure(500, 1, 0, 0, true); // not working!
//m_scopeVis->setFreeRun(false); // FIXME: add method rather than call m_scopeVis->configure()
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle("RTTY 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.setScopeGUI(ui->scopeGUI);
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->scopeContainer->setVisible(false);
// Hide developer only settings
ui->filterSettingsWidget->setVisible(false);
ui->filterLine->setVisible(false);
displaySettings();
makeUIConnections();
applySettings(true);
}
RttyDemodGUI::~RttyDemodGUI()
{
delete ui;
}
void RttyDemodGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void RttyDemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
RttyDemod::MsgConfigureRttyDemod* message = RttyDemod::MsgConfigureRttyDemod::create( m_settings, force);
m_rttyDemod->getInputMessageQueue()->push(message);
}
}
QString RttyDemodGUI::formatFrequency(int frequency) const
{
QString suffix = "";
if (width() > 450) {
suffix = " Hz";
}
return QString("%1%2").arg(frequency).arg(suffix);
}
void RttyDemodGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
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->mode->setCurrentText("Custom");
ui->rfBWText->setText(formatFrequency((int)m_settings.m_rfBandwidth));
ui->rfBW->setValue(m_settings.m_rfBandwidth);
QString baudRate;
if (m_settings.m_baudRate < 46.0f && m_settings.m_baudRate > 45.0f) {
baudRate = "45.45";
} else {
baudRate = QString("%1").arg(m_settings.m_baudRate);
}
ui->baudRate->setCurrentIndex(ui->baudRate->findText(baudRate));
ui->frequencyShiftText->setText(formatFrequency(m_settings.m_frequencyShift));
ui->frequencyShift->setValue(m_settings.m_frequencyShift);
ui->squelchText->setText(QString("%1 dB").arg(m_settings.m_squelch));
ui->squelch->setValue(m_settings.m_squelch);
ui->characterSet->setCurrentIndex((int)m_settings.m_characterSet);
ui->suppressCRLF->setChecked(m_settings.m_suppressCRLF);
ui->filter->setCurrentIndex((int)m_settings.m_filter);
ui->atc->setChecked(m_settings.m_atc);
ui->endian->setChecked(m_settings.m_msbFirst);
if (m_settings.m_msbFirst) {
ui->endian->setText("MSB");
} else {
ui->endian->setText("LSB");
}
ui->spaceHigh->setChecked(m_settings.m_spaceHigh);
if (m_settings.m_spaceHigh) {
ui->spaceHigh->setText("M-S");
} else {
ui->spaceHigh->setText("S-M");
}
updateIndexLabel();
ui->udpEnabled->setChecked(m_settings.m_udpEnabled);
ui->udpAddress->setText(m_settings.m_udpAddress);
ui->udpPort->setText(QString::number(m_settings.m_udpPort));
ui->channel1->setCurrentIndex(m_settings.m_scopeCh1);
ui->channel2->setCurrentIndex(m_settings.m_scopeCh2);
ui->logFilename->setToolTip(QString(".txt log filename: %1").arg(m_settings.m_logFilename));
ui->logEnable->setChecked(m_settings.m_logEnabled);
getRollupContents()->restoreState(m_rollupState);
updateAbsoluteCenterFrequency();
blockApplySettings(false);
}
void RttyDemodGUI::leaveEvent(QEvent* event)
{
m_channelMarker.setHighlighted(false);
ChannelGUI::leaveEvent(event);
}
void RttyDemodGUI::enterEvent(EnterEventType* event)
{
m_channelMarker.setHighlighted(true);
ChannelGUI::enterEvent(event);
}
void RttyDemodGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_rttyDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(100.0f + powDbAvg) / 100.0f,
(100.0f + powDbPeak) / 100.0f,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
}
m_tickCount++;
}
void RttyDemodGUI::on_logEnable_clicked(bool checked)
{
m_settings.m_logEnabled = checked;
applySettings();
}
void RttyDemodGUI::on_logFilename_clicked()
{
// Get filename to save to
QFileDialog fileDialog(nullptr, "Select file to log received text to", "", "*.txt");
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
if (fileDialog.exec())
{
QStringList fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
m_settings.m_logFilename = fileNames[0];
ui->logFilename->setToolTip(QString(".txt log filename: %1").arg(m_settings.m_logFilename));
applySettings();
}
}
}
void RttyDemodGUI::makeUIConnections()
{
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RttyDemodGUI::on_deltaFrequency_changed);
QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &RttyDemodGUI::on_rfBW_valueChanged);
QObject::connect(ui->baudRate, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_baudRate_currentIndexChanged);
QObject::connect(ui->frequencyShift, &QSlider::valueChanged, this, &RttyDemodGUI::on_frequencyShift_valueChanged);
QObject::connect(ui->squelch, &QDial::valueChanged, this, &RttyDemodGUI::on_squelch_valueChanged);
QObject::connect(ui->characterSet, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_characterSet_currentIndexChanged);
QObject::connect(ui->suppressCRLF, &ButtonSwitch::clicked, this, &RttyDemodGUI::on_suppressCRLF_clicked);
QObject::connect(ui->mode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_mode_currentIndexChanged);
QObject::connect(ui->filter, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_filter_currentIndexChanged);
QObject::connect(ui->atc, &QCheckBox::clicked, this, &RttyDemodGUI::on_atc_clicked);
QObject::connect(ui->endian, &QCheckBox::clicked, this, &RttyDemodGUI::on_endian_clicked);
QObject::connect(ui->spaceHigh, &QCheckBox::clicked, this, &RttyDemodGUI::on_spaceHigh_clicked);
QObject::connect(ui->clearTable, &QPushButton::clicked, this, &RttyDemodGUI::on_clearTable_clicked);
QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &RttyDemodGUI::on_udpEnabled_clicked);
QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &RttyDemodGUI::on_udpAddress_editingFinished);
QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &RttyDemodGUI::on_udpPort_editingFinished);
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &RttyDemodGUI::on_logEnable_clicked);
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &RttyDemodGUI::on_logFilename_clicked);
QObject::connect(ui->channel1, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_channel1_currentIndexChanged);
QObject::connect(ui->channel2, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RttyDemodGUI::on_channel2_currentIndexChanged);
}
void RttyDemodGUI::updateAbsoluteCenterFrequency()
{
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
}
+130
View File
@@ -0,0 +1,130 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMODGUI_H
#define INCLUDE_RTTYDEMODGUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "rttydemod.h"
#include "rttydemodsettings.h"
class PluginAPI;
class DeviceUISet;
class BasebandSampleSink;
class ScopeVis;
class RttyDemod;
class RttyDemodGUI;
namespace Ui {
class RttyDemodGUI;
}
class RttyDemodGUI;
class RttyDemodGUI : public ChannelGUI {
Q_OBJECT
public:
static RttyDemodGUI* 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::RttyDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
RttyDemodSettings m_settings;
qint64 m_deviceCenterFrequency;
bool m_doApplySettings;
ScopeVis* m_scopeVis;
RttyDemod* m_rttyDemod;
int m_basebandSampleRate;
uint32_t m_tickCount;
MessageQueue m_inputMessageQueue;
QString m_previousChar[2];
explicit RttyDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~RttyDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
qint64 getFrequency();
QString formatFrequency(int frequency) const;
void characterReceived(QString c);
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_rfBW_valueChanged(int index);
void on_baudRate_currentIndexChanged(int index);
void on_frequencyShift_valueChanged(int value);
void on_squelch_valueChanged(int value);
void on_characterSet_currentIndexChanged(int index);
void on_suppressCRLF_clicked(bool checked=false);
void on_mode_currentIndexChanged(int index);
void on_filter_currentIndexChanged(int index);
void on_atc_clicked(bool checked);
void on_endian_clicked(bool checked);
void on_spaceHigh_clicked(bool checked);
void on_clearTable_clicked();
void on_udpEnabled_clicked(bool checked);
void on_udpAddress_editingFinished();
void on_udpPort_editingFinished();
void on_logEnable_clicked(bool checked=false);
void on_logFilename_clicked();
void on_channel1_currentIndexChanged(int index);
void on_channel2_currentIndexChanged(int index);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
};
#endif // INCLUDE_RTTYDEMODGUI_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,93 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "rttydemodgui.h"
#endif
#include "rttydemod.h"
#include "rttydemodwebapiadapter.h"
#include "rttydemodplugin.h"
const PluginDescriptor RttyDemodPlugin::m_pluginDescriptor = {
RttyDemod::m_channelId,
QStringLiteral("RTTY Demodulator"),
QStringLiteral("7.11.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
RttyDemodPlugin::RttyDemodPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& RttyDemodPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void RttyDemodPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerRxChannel(RttyDemod::m_channelIdURI, RttyDemod::m_channelId, this);
}
void RttyDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
RttyDemod *instance = new RttyDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* RttyDemodPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return 0;
}
#else
ChannelGUI* RttyDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return RttyDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* RttyDemodPlugin::createChannelWebAPIAdapter() const
{
return new RttyDemodWebAPIAdapter();
}
@@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMODPLUGIN_H
#define INCLUDE_RTTYDEMODPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class RttyDemodPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.rttydemod")
public:
explicit RttyDemodPlugin(QObject* parent = NULL);
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_RTTYDEMODPLUGIN_H
@@ -0,0 +1,213 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "rttydemodsettings.h"
RttyDemodSettings::RttyDemodSettings() :
m_channelMarker(nullptr),
m_scopeGUI(nullptr),
m_rollupState(nullptr)
{
resetToDefaults();
}
void RttyDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 400.0f; // OBW for 2FSK = 2 * deviation + data rate. Then add a bit for carrier frequency offset
m_baudRate = 45.45;
m_frequencyShift = 170;
m_udpEnabled = false;
m_udpAddress = "127.0.0.1";
m_udpPort = 9999;
m_characterSet = Baudot::ITA2;
m_suppressCRLF = false;
m_filter = LOWPASS;
m_atc = true;
m_msbFirst = false;
m_spaceHigh = false;
m_squelch = -70;
m_logFilename = "rtty_log.csv";
m_logEnabled = false;
m_scopeCh1 = 0;
m_scopeCh2 = 1;
m_rgbColor = QColor(180, 205, 130).rgb();
m_title = "RTTY Demodulator";
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;
}
QByteArray RttyDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_streamIndex);
s.writeFloat(3, m_rfBandwidth);
s.writeFloat(4, m_baudRate);
s.writeS32(5, m_frequencyShift);
s.writeS32(6, (int)m_characterSet);
s.writeBool(7, m_suppressCRLF);
s.writeBool(8, m_unshiftOnSpace);
s.writeS32(9, (int)m_filter);
s.writeBool(10, m_atc);
s.writeBool(34, m_msbFirst);
s.writeBool(35, m_spaceHigh);
s.writeS32(36, m_squelch);
if (m_channelMarker) {
s.writeBlob(11, m_channelMarker->serialize());
}
s.writeU32(12, m_rgbColor);
s.writeString(13, m_title);
s.writeBool(14, m_useReverseAPI);
s.writeString(15, m_reverseAPIAddress);
s.writeU32(16, m_reverseAPIPort);
s.writeU32(17, m_reverseAPIDeviceIndex);
s.writeU32(18, m_reverseAPIChannelIndex);
s.writeBool(22, m_udpEnabled);
s.writeString(23, m_udpAddress);
s.writeU32(24, m_udpPort);
s.writeS32(31, m_scopeCh1);
s.writeS32(32, m_scopeCh2);
s.writeBlob(33, m_scopeGUI->serialize());
s.writeString(25, m_logFilename);
s.writeBool(26, m_logEnabled);
if (m_rollupState) {
s.writeBlob(27, m_rollupState->serialize());
}
s.writeS32(28, m_workspaceIndex);
s.writeBlob(29, m_geometryBytes);
s.writeBool(30, m_hidden);
return s.final();
}
bool RttyDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_streamIndex, 0);
d.readFloat(3, &m_rfBandwidth, 450.0f);
d.readFloat(4, &m_baudRate, 45.45f);
d.readS32(5, &m_frequencyShift, 170);
d.readS32(6, (int *)&m_characterSet, (int)Baudot::ITA2);
d.readBool(7, &m_suppressCRLF, false);
d.readBool(8, &m_unshiftOnSpace, false);
d.readS32(9, (int *)&m_filter, (int) LOWPASS);
d.readBool(10, &m_atc, true);
d.readBool(34, &m_msbFirst, false);
d.readBool(35, &m_spaceHigh, false);
d.readS32(36, &m_squelch, -70);
if (m_channelMarker)
{
d.readBlob(11, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readU32(12, &m_rgbColor, QColor(180, 205, 130).rgb());
d.readString(13, &m_title, "RTTY Demodulator");
d.readBool(14, &m_useReverseAPI, false);
d.readString(15, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(16, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(17, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(18, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readBool(22, &m_udpEnabled);
d.readString(23, &m_udpAddress);
d.readU32(24, &utmp);
if ((utmp > 1023) && (utmp < 65535)) {
m_udpPort = utmp;
} else {
m_udpPort = 9999;
}
d.readS32(31, &m_scopeCh1, 0);
d.readS32(32, &m_scopeCh2, 0);
if (m_scopeGUI)
{
d.readBlob(33, &bytetmp);
m_scopeGUI->deserialize(bytetmp);
}
d.readString(25, &m_logFilename, "rtty_log.csv");
d.readBool(26, &m_logEnabled, false);
if (m_rollupState)
{
d.readBlob(27, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(28, &m_workspaceIndex, 0);
d.readBlob(29, &m_geometryBytes);
d.readBool(30, &m_hidden, false);
return true;
}
else
{
resetToDefaults();
return false;
}
}
@@ -0,0 +1,88 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMODSETTINGS_H
#define INCLUDE_RTTYDEMODSETTINGS_H
#include <QByteArray>
#include "util/baudot.h"
class Serializable;
struct RttyDemodSettings
{
qint32 m_inputFrequencyOffset;
Real m_rfBandwidth;
Real m_baudRate;
int m_frequencyShift;
bool m_udpEnabled;
QString m_udpAddress;
uint16_t m_udpPort;
Baudot::CharacterSet m_characterSet;
bool m_suppressCRLF;
bool m_unshiftOnSpace;
enum FilterType {
LOWPASS,
COSINE_B_1,
COSINE_B_0_75,
COSINE_B_0_5,
COSINE_B_1_BW_0_75,
COSINE_B_1_BW_1_25,
MAV,
FILTERED_MAV
} m_filter;
bool m_atc;
bool m_msbFirst; // false = LSB first, true = MSB first
bool m_spaceHigh; // false = mark high frequency, true = space high frequency
int m_squelch; // In dB
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
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_scopeCh1;
int m_scopeCh2;
QString m_logFilename;
bool m_logEnabled;
Serializable *m_scopeGUI;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
static const int RTTYDEMOD_CHANNEL_SAMPLE_RATE = 1000;
RttyDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* INCLUDE_RTTYDEMODSETTINGS_H */
@@ -0,0 +1,670 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 <QRegularExpression>
#include <complex.h>
#include "dsp/dspengine.h"
#include "dsp/scopevis.h"
#include "util/db.h"
#include "maincore.h"
#include "rttydemod.h"
#include "rttydemodsink.h"
RttyDemodSink::RttyDemodSink(RttyDemod *packetDemod) :
m_rttyDemod(packetDemod),
m_channelSampleRate(RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE),
m_channelFrequencyOffset(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_expLength(600),
m_prods1(nullptr),
m_prods2(nullptr),
m_exp(nullptr),
m_sampleIdx(0),
m_clockHistogram(100),
m_shiftEstMag(m_fftSize),
m_fftSequence(-1),
m_fft(nullptr),
m_fftCounter(0),
m_sampleBufferIndex(0)
{
m_magsq = 0.0;
m_sampleBuffer.resize(m_sampleBufferSize);
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0) {
fftFactory->releaseEngine(m_fftSize, false, m_fftSequence);
}
m_fftSequence = fftFactory->getEngine(m_fftSize, false, &m_fft);
m_fftCounter = 0;
}
RttyDemodSink::~RttyDemodSink()
{
delete[] m_exp;
delete[] m_prods1;
delete[] m_prods2;
}
void RttyDemodSink::sampleToScope(Complex sample)
{
if (m_scopeSink)
{
Real r = std::real(sample) * SDR_RX_SCALEF;
Real i = std::imag(sample) * SDR_RX_SCALEF;
m_sampleBuffer[m_sampleBufferIndex++] = Sample(r, i);
if (m_sampleBufferIndex == m_sampleBufferSize)
{
std::vector<SampleVector::const_iterator> vbegin;
vbegin.push_back(m_sampleBuffer.begin());
m_scopeSink->feed(vbegin, m_sampleBufferSize);
m_sampleBufferIndex = 0;
}
}
}
void RttyDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
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 // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
}
void RttyDemodSink::processOneSample(Complex &ci)
{
// Calculate average and peak levels for level meter
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();;
Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
// Sum power while data is being received
if (m_gotSOP)
{
m_rssiMagSqSum += magsq;
m_rssiMagSqCount++;
}
ci /= SDR_RX_SCALEF;
// Use FFT to estimate frequency shift
m_fft->in()[m_fftCounter] = ci;
m_fftCounter++;
if (m_fftCounter == m_fftSize)
{
estimateFrequencyShift();
m_fftCounter = 0;
}
// Correlate with expected mark and space frequencies
Complex exp = m_exp[m_expIdx];
m_expIdx = (m_expIdx + 1) % m_expLength;
//Complex exp = m_exp[m_sampleIdx];
//qDebug() << "IQ " << real(ci) << imag(ci);
Complex corr1 = ci * exp;
Complex corr2 = ci * std::conj(exp);
// Filter
Real abs1, abs2;
Real abs1Filt, abs2Filt;
if (m_settings.m_filter == RttyDemodSettings::LOWPASS)
{
// Low pass filter
abs1Filt = abs1 = std::abs(m_lowpassComplex1.filter(corr1));
abs2Filt = abs2 = std::abs(m_lowpassComplex2.filter(corr2));
}
else if ( (m_settings.m_filter == RttyDemodSettings::COSINE_B_1)
|| (m_settings.m_filter == RttyDemodSettings::COSINE_B_0_75)
|| (m_settings.m_filter == RttyDemodSettings::COSINE_B_0_5)
)
{
// Rasised cosine filter
abs1Filt = abs1 = std::abs(m_raisedCosine1.filter(corr1));
abs2Filt = abs2 = std::abs(m_raisedCosine2.filter(corr2));
}
else
{
// Moving average
// Calculating moving average (well windowed sum)
Complex old1 = m_prods1[m_sampleIdx];
Complex old2 = m_prods2[m_sampleIdx];
m_prods1[m_sampleIdx] = corr1;
m_prods2[m_sampleIdx] = corr2;
m_sum1 += m_prods1[m_sampleIdx] - old1;
m_sum2 += m_prods2[m_sampleIdx] - old2;
m_sampleIdx = (m_sampleIdx + 1) % m_samplesPerBit;
// Square signals (by calculating absolute value of complex signal)
abs1 = std::abs(m_sum1);
abs2 = std::abs(m_sum2);
// Apply optional low-pass filter to try to avoid extra zero-crassings above the baud rate
if (m_settings.m_filter == RttyDemodSettings::FILTERED_MAV)
{
abs1Filt = m_lowpass1.filter(abs1);
abs2Filt = m_lowpass2.filter(abs2);
}
else
{
abs1Filt = abs1;
abs2Filt = abs2;
}
}
// Envelope calculation
m_movMax1(abs1Filt);
m_movMax2(abs2Filt);
Real env1 = m_movMax1.getMaximum();
Real env2 = m_movMax2.getMaximum();
// Automatic threshold correction to compensate for frequency selective fading
// http://www.w7ay.net/site/Technical/ATC/index.html
Real bias1 = abs1Filt - 0.5 * env1;
Real bias2 = abs2Filt - 0.5 * env2;
Real unbiasedData = abs1Filt - abs2Filt;
Real biasedData = bias1 - bias2;
// Save current data for edge detection
m_dataPrev = m_data;
// Set data according to stongest correlation
if (m_settings.m_spaceHigh) {
m_data = m_settings.m_atc ? biasedData < 0 : unbiasedData < 0;
} else {
m_data = m_settings.m_atc ? biasedData > 0 : unbiasedData > 0;
}
if (!m_gotSOP)
{
// Look for falling edge which indicates start bit
if (!m_data && m_dataPrev)
{
m_gotSOP = true;
m_bits = 0;
m_bitCount = 0;
m_clockCount = 0;
m_clock = false;
m_cycleCount = 0;
}
}
else
{
// Sample in middle of symbol
if (m_clockCount == m_samplesPerBit/2)
{
receiveBit(m_data);
m_clock = true;
}
m_clockCount = (m_clockCount + 1) % m_samplesPerBit;
if (m_clockCount == 0) {
m_clock = false;
}
// Count cycles between edges, to estimate baud rate
m_cycleCount++;
if (m_data != m_dataPrev)
{
if (m_cycleCount < m_clockHistogram.size())
{
m_clockHistogram[m_cycleCount]++;
m_edgeCount++;
// Every 100 edges, calculate estimate
if (m_edgeCount == 100) {
estimateBaudRate();
}
}
m_cycleCount = 0;
}
}
// Select signals to feed to scope
Complex scopeSample;
switch (m_settings.m_scopeCh1)
{
case 0:
scopeSample.real(ci.real());
break;
case 1:
scopeSample.real(ci.imag());
break;
case 2:
scopeSample.real(magsq);
break;
case 3:
scopeSample.real(m_sampleIdx);
break;
case 4:
scopeSample.real(abs(m_sum1));
break;
case 5:
scopeSample.real(abs(m_sum2));
break;
case 6:
scopeSample.real(m_bit);
break;
case 7:
scopeSample.real(m_bitCount);
break;
case 8:
scopeSample.real(m_gotSOP);
break;
case 9:
scopeSample.real(real(exp));
break;
case 10:
scopeSample.real(imag(exp));
break;
case 11:
scopeSample.real(abs1Filt);
break;
case 12:
scopeSample.real(abs2Filt);
break;
case 13:
scopeSample.real(abs2 - abs1);
break;
case 14:
scopeSample.real(abs2Filt - abs1Filt);
break;
case 15:
scopeSample.real(m_data);
break;
case 16:
scopeSample.real(m_clock);
break;
case 17:
scopeSample.real(env1);
break;
case 18:
scopeSample.real(env2);
break;
case 19:
scopeSample.real(bias1);
break;
case 20:
scopeSample.real(bias2);
break;
case 21:
scopeSample.real(unbiasedData);
break;
case 22:
scopeSample.real(biasedData);
break;
}
switch (m_settings.m_scopeCh2)
{
case 0:
scopeSample.imag(ci.real());
break;
case 1:
scopeSample.imag(ci.imag());
break;
case 2:
scopeSample.imag(magsq);
break;
case 3:
scopeSample.imag(m_sampleIdx);
break;
case 4:
scopeSample.imag(abs(m_sum1));
break;
case 5:
scopeSample.imag(abs(m_sum2));
break;
case 6:
scopeSample.imag(m_bit);
break;
case 7:
scopeSample.imag(m_bitCount);
break;
case 8:
scopeSample.imag(m_gotSOP);
break;
case 9:
scopeSample.imag(real(exp));
break;
case 10:
scopeSample.imag(imag(exp));
break;
case 11:
scopeSample.imag(abs1Filt);
break;
case 12:
scopeSample.imag(abs2Filt);
break;
case 13:
scopeSample.imag(abs2 - abs1);
break;
case 14:
scopeSample.imag(abs2Filt - abs1Filt);
break;
case 15:
scopeSample.imag(m_data);
break;
case 16:
scopeSample.imag(m_clock);
break;
case 17:
scopeSample.imag(env1);
break;
case 18:
scopeSample.imag(env2);
break;
case 19:
scopeSample.imag(bias1);
break;
case 20:
scopeSample.imag(bias2);
break;
case 21:
scopeSample.imag(unbiasedData);
break;
case 22:
scopeSample.imag(biasedData);
break;
}
sampleToScope(scopeSample);
}
void RttyDemodSink::estimateFrequencyShift()
{
// Perform FFT
m_fft->transform();
// Calculate magnitude
for (int i = 0; i < m_fftSize; i++)
{
Complex c = m_fft->out()[i];
Real v = c.real() * c.real() + c.imag() * c.imag();
Real magsq = v / (m_fftSize * m_fftSize);
m_shiftEstMag[i] = magsq;
}
// Fink peaks in each half
Real peak1 = m_shiftEstMag[0];
int peak1Bin = 0;
for (int i = 1; i < m_fftSize/2; i++)
{
if (m_shiftEstMag[i] > peak1)
{
peak1 = m_shiftEstMag[i];
peak1Bin = i;
}
}
Real peak2 = m_shiftEstMag[m_fftSize/2];
int peak2Bin = m_fftSize/2;
for (int i = m_fftSize/2+1; i < m_fftSize; i++)
{
if (m_shiftEstMag[i] > peak2)
{
peak2 = m_shiftEstMag[i];
peak2Bin = i;
}
}
// Convert bin to frequency offset
double frequencyResolution = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / (double)m_fftSize;
double freq1 = frequencyResolution * peak1Bin;
double freq2 = -frequencyResolution * (m_fftSize - peak2Bin);
m_freq1Average(freq1);
m_freq2Average(freq2);
//int shift = m_freq1Average.instantAverage() - m_freq2Average.instantAverage();
//qDebug() << "Freq est " << freq1 << freq2 << shift;
}
int RttyDemodSink::estimateBaudRate()
{
// Find most frequent entry in histogram
auto histMax = max_element(m_clockHistogram.begin(), m_clockHistogram.end());
int index = std::distance(m_clockHistogram.begin(), histMax);
// Calculate baud rate as weighted average
Real baud1 = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / (Real)(index-1);
int count1 = m_clockHistogram[index-1];
Real baud2 = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / (Real)(index);
int count2 = m_clockHistogram[index];
Real baud3 = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / (Real)(index+1);
int count3 = m_clockHistogram[index+1];
Real total = count1 + count2 + count3;
Real estBaud = count1/total*baud1 + count2/total*baud2 + count3/total*baud3;
m_baudRateAverage(estBaud);
// Send estimate to GUI
if (getMessageQueueToChannel())
{
int estFrequencyShift = m_freq1Average.instantAverage() - m_freq2Average.instantAverage();
RttyDemod::MsgModeEstimate *msg = RttyDemod::MsgModeEstimate::create(m_baudRateAverage.instantAverage(), estFrequencyShift);
getMessageQueueToChannel()->push(msg);
}
// Restart estimation
std::fill(m_clockHistogram.begin(), m_clockHistogram.end(), 0);
m_edgeCount = 0;
return estBaud;
}
void RttyDemodSink::receiveBit(bool bit)
{
m_bit = bit;
// Store in shift reg.
if (m_settings.m_msbFirst) {
m_bits = (m_bit & 0x1) | (m_bits << 1);
} else {
m_bits = (m_bit << 6) | (m_bits >> 1);
}
m_bitCount++;
if (m_bitCount == 7)
{
if ( (!m_settings.m_msbFirst && ((m_bits & 0x40) != 0x40))
|| (m_settings.m_msbFirst && ((m_bits & 0x01) != 0x01)))
{
//qDebug() << "No stop bit";
}
else
{
QString c = m_rttyDecoder.decode((m_bits >> 1) & 0x1f);
if ((c != '\0') && (c != '<') && (c != '>') && (c != '^'))
{
// Calculate average power over received byte
float rssi = CalcDb::dbPower(m_rssiMagSqSum / m_rssiMagSqCount);
if (rssi > m_settings.m_squelch)
{
// Slow enough to send individually to be displayed
if (getMessageQueueToChannel())
{
RttyDemod::MsgCharacter *msg = RttyDemod::MsgCharacter::create(c);
getMessageQueueToChannel()->push(msg);
}
}
}
}
m_gotSOP = false;
}
}
void RttyDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "RttyDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) channelSampleRate / (Real) RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void RttyDemodSink::init()
{
m_sampleIdx = 0;
m_expIdx = 0;
m_sum1 = 0.0;
m_sum2 = 0.0;
for (int i = 0; i < m_samplesPerBit; i++)
{
m_prods1[i] = 0.0f;
m_prods2[i] = 0.0f;
}
m_bit = 0;
m_bits = 0;
m_bitCount = 0;
m_gotSOP = false;
m_clockCount = 0;
m_clock = 0;
m_rssiMagSqSum = 0.0;
m_rssiMagSqCount = 0;
m_rttyDecoder.init();
}
void RttyDemodSink::applySettings(const RttyDemodSettings& settings, bool force)
{
qDebug() << "RttyDemodSink::applySettings:"
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_baudRate: " << settings.m_baudRate
<< " m_frequencyShift: " << settings.m_frequencyShift
<< " m_characterSet: " << settings.m_characterSet
<< " m_unshiftOnSpace: " << settings.m_unshiftOnSpace
<< " force: " << force;
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
{
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
if ((settings.m_baudRate != m_settings.m_baudRate) || (settings.m_filter != m_settings.m_filter) || force)
{
m_envelope1.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, 2);
m_envelope2.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, 2);
m_lowpass1.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, m_settings.m_baudRate * 1.1);
m_lowpass2.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, m_settings.m_baudRate * 1.1);
//m_lowpass1.printTaps("lpf");
m_lowpassComplex1.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, m_settings.m_baudRate * 1.1);
m_lowpassComplex2.create(301, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE, m_settings.m_baudRate * 1.1);
//m_lowpass1.printTaps("lpfc");
// http://w7ay.net/site/Technical/Extended%20Nyquist%20Filters/index.html
// http://w7ay.net/site/Technical/EqualizedRaisedCosine/index.html
float beta = 1.0f;
float bw = 1.0f;
if (settings.m_filter == RttyDemodSettings::COSINE_B_0_5) {
beta = 0.5f;
} else if (settings.m_filter == RttyDemodSettings::COSINE_B_0_75) {
beta = 0.75f;
} else if (settings.m_filter == RttyDemodSettings::COSINE_B_1_BW_0_75) {
bw = 0.75f;
} else if (settings.m_filter == RttyDemodSettings::COSINE_B_1_BW_1_25) {
bw = 1.25f;
}
m_raisedCosine1.create(beta, 7, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE/(m_settings.m_baudRate/bw), false);
m_raisedCosine2.create(beta, 7, RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE/(m_settings.m_baudRate/bw), false);
//m_raisedCosine1.printTaps("rcos");
}
if ((settings.m_characterSet != m_settings.m_characterSet) || force) {
m_rttyDecoder.setCharacterSet(settings.m_characterSet);
}
if ((settings.m_unshiftOnSpace != m_settings.m_unshiftOnSpace) || force) {
m_rttyDecoder.setUnshiftOnSpace(settings.m_unshiftOnSpace);
}
if ((settings.m_baudRate != m_settings.m_baudRate) || (settings.m_frequencyShift != m_settings.m_frequencyShift) || force)
{
delete[] m_exp;
delete[] m_prods1;
delete[] m_prods2;
m_samplesPerBit = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / settings.m_baudRate;
m_exp = new Complex[m_expLength];
m_prods1 = new Complex[m_samplesPerBit];
m_prods2 = new Complex[m_samplesPerBit];
Real f0 = 0.0f;
for (int i = 0; i < m_expLength; i++)
{
m_exp[i] = Complex(cos(f0), sin(f0));
f0 += 2.0f * (Real)M_PI * (settings.m_frequencyShift/2.0f) / RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE;
}
init();
// Due to start and stop bits, we should get mark and space at least every 8 bits
// while something is being transmitted
m_movMax1.setSize(m_samplesPerBit * 8);
m_movMax2.setSize(m_samplesPerBit * 8);
m_edgeCount = 0;
std::fill(m_clockHistogram.begin(), m_clockHistogram.end(), 0);
m_baudRateAverage.reset();
m_freq1Average.reset();
m_freq2Average.reset();
}
m_settings = settings;
}
+170
View File
@@ -0,0 +1,170 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMODSINK_H
#define INCLUDE_RTTYDEMODSINK_H
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/firfilter.h"
#include "dsp/raisedcosine.h"
#include "dsp/fftfactory.h"
#include "dsp/fftengine.h"
#include "util/movingaverage.h"
#include "util/movingmaximum.h"
#include "util/doublebufferfifo.h"
#include "util/messagequeue.h"
#include "rttydemodsettings.h"
class ChannelAPI;
class RttyDemod;
class ScopeVis;
class RttyDemodSink : public ChannelSampleSink {
public:
RttyDemodSink(RttyDemod *packetDemod);
~RttyDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setScopeSink(ScopeVis* scopeSink) { m_scopeSink = scopeSink; }
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const RttyDemodSettings& settings, bool force = false);
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
double getMagSq() const { return m_magsq; }
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;
};
ScopeVis* m_scopeSink; // Scope GUI to display baseband waveform
RttyDemod *m_rttyDemod;
RttyDemodSettings m_settings;
ChannelAPI *m_channel;
int m_channelSampleRate;
int m_channelFrequencyOffset;
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MessageQueue *m_messageQueueToChannel;
MovingAverageUtil<Real, double, 16> m_movingAverage;
Lowpass<Real> m_envelope1;
Lowpass<Real> m_envelope2;
Lowpass<Real> m_lowpass1;
Lowpass<Real> m_lowpass2;
Lowpass<Complex> m_lowpassComplex1;
Lowpass<Complex> m_lowpassComplex2;
RaisedCosine<Complex> m_raisedCosine1;
RaisedCosine<Complex> m_raisedCosine2;
MovingMaximum<Real> m_movMax1;
MovingMaximum<Real> m_movMax2;
int m_expLength;
int m_samplesPerBit;
Complex *m_prods1;
Complex *m_prods2;
Complex *m_exp;
Complex m_sum1;
Complex m_sum2;
int m_sampleIdx;
int m_expIdx;
int m_bit;
bool m_data;
bool m_dataPrev;
int m_clockCount;
bool m_clock;
double m_rssiMagSqSum;
int m_rssiMagSqCount;
unsigned short m_bits;
int m_bitCount;
bool m_gotSOP;
BaudotDecoder m_rttyDecoder;
// For baud rate estimation
unsigned int m_cycleCount;
std::vector<int> m_clockHistogram;
int m_edgeCount;
MovingAverageUtil<Real, Real, 5> m_baudRateAverage;
// For frequency shift estimation
std::vector<Real> m_shiftEstMag;
int m_fftSequence;
FFTEngine *m_fft;
int m_fftCounter;
static const int m_fftSize = 128; // ~7Hz res
MovingAverageUtil<Real, Real, 16> m_freq1Average;
MovingAverageUtil<Real, Real, 16> m_freq2Average;
SampleVector m_sampleBuffer;
static const int m_sampleBufferSize = RttyDemodSettings::RTTYDEMOD_CHANNEL_SAMPLE_RATE / 20;
int m_sampleBufferIndex;
void processOneSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
void sampleToScope(Complex sample);
void init();
void receiveBit(bool bit);
int estimateBaudRate();
void estimateFrequencyShift();
};
#endif // INCLUDE_RTTYDEMODSINK_H
@@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "rttydemod.h"
#include "rttydemodwebapiadapter.h"
RttyDemodWebAPIAdapter::RttyDemodWebAPIAdapter()
{}
RttyDemodWebAPIAdapter::~RttyDemodWebAPIAdapter()
{}
int RttyDemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRttyDemodSettings(new SWGSDRangel::SWGRTTYDemodSettings());
response.getRttyDemodSettings()->init();
RttyDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int RttyDemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force;
(void) errorMessage;
RttyDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}
@@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_RTTYDEMOD_WEBAPIADAPTER_H
#define INCLUDE_RTTYDEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "rttydemodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class RttyDemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
RttyDemodWebAPIAdapter();
virtual ~RttyDemodWebAPIAdapter();
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:
RttyDemodSettings m_settings;
};
#endif // INCLUDE_RTTYDEMOD_WEBAPIADAPTER_H
+185
View File
@@ -3476,3 +3476,188 @@ DAB,Wolv'ton & Shrop 3702,Wolv'ton & Shrop,218640000,52.878444,-3.091941,305,0.6
DAB,Wolv'ton & Shrop 3708,Wolv'ton & Shrop,218640000,52.547739,-2.115424,225,0.6,37,08
DAB,Wolv'ton & Shrop 3709,Wolv'ton & Shrop,218640000,52.670935,-2.550228,396,1.95447,37,09
DAB,Wolv'ton & Shrop 3711,Wolv'ton & Shrop,218640000,52.496364,-2.048417,267,4,37,11
DAB,Marseille intermédiaire 1 2310,Marseille intermédiaire 1,195936000,43.3878,5.41167,66,10,17,0a
DAB,Marseille intermédiaire 1 2302,Marseille intermédiaire 1,195936000,43.5272,5.40194,25,3.5,17,02
DAB,Marseille intermédiaire 2 2410,Marseille intermédiaire 2,188928000,43.3839,5.42556,148,10,18,0a
DAB,Nice intermédiaire 2 1704,Nice intermédiaire 2,220352000,43.7217,7.32083,56,6,11,04
DAB,Nice local 2 0404,Nice local 2,208064000,43.7675,7.29556,30,12,04,04
DAB,Paris intermédiaire 1 1210,Paris intermédiaire 1,181936000,48.8853,2.42222,144,8,0c,0a
DAB,Paris local 2 2610,Paris local 2,204640000,48.8853,2.42222,144,8,1a,0a
DAB,Paris local 1 2210,Paris local 1,202928000,48.8872,2.34167,48,4,16,0a
DAB,Marseille local 3 0710,Marseille local 3,201072000,43.3878,5.41167,66,10,07,0a
DAB,Marseille local 2 1110,Marseille local 2,199360000,43.3892,5.4125,45,4,0b,0a
DAB,Paris local 3 2710,Paris local 3,216928000,48.8853,2.42222,144,4,1b,0a
DAB,Nice étendu 1 3104,Nice étendu 1,216928000,43.7142,7.30722,18,10,1f,04
DAB,Nice local 3 0604,Nice local 3,201072000,43.7217,7.32083,38,2,06,04
DAB,Lille local 1 0510,Lille local 1,192352000,50.6556,3.03056,70,7,05,0a
DAB,Douai-Lens local 1 2701,Douai-Lens local 1,222064000,50.4336,2.6075,30,4,1b,01
DAB,Lille étendu 1 0010,Lille étendu 1,195936000,50.6425,3.12389,143,4,00,0a
DAB,Calais-Boulogne-sur-Mer local 1 0102,Calais-Boulogne-sur-Mer local 1,176640000,50.9506,1.89083,63,3,01,02
DAB,Dunkerque local 1 0303,Dunkerque local 1,201072000,51.0331,2.35889,55,3,03,03
DAB,Valenciennes local 1 2204,Valenciennes local 1,188928000,50.3394,3.57389,46,8,16,04
DAB,Lille local 2 0610,Lille local 2,194064000,50.6425,3.12389,143,4,06,0a
DAB,Marseille local 2 1101,Marseille local 2,199360000,43.3586,5.57444,22,2,0b,01
DAB,Lyon étendu 1 1710,Lyon étendu 1,181936000,45.8225,4.82083,32,13,11,0a
DAB,Lyon étendu 1 1704,Lyon étendu 1,181936000,45.965,4.70361,19,2.1,11,04
DAB,Strasbourg local 2 0910,Strasbourg local 2,192352000,48.5867,7.74111,90,2.5,09,0a
DAB,Strasbourg étendu 1 6710,Strasbourg étendu 1,187072000,48.5867,7.74111,90,4,43,0a
DAB,Strasbourg étendu 1 6701,Strasbourg étendu 1,187072000,48.0767,7.32667,40,1.2,43,01
DAB,Strasbourg étendu 1 6702,Strasbourg étendu 1,187072000,47.7344,7.34556,15,2.5,43,02
DAB,Colmar local 1 1801,Colmar local 1,220352000,48.0786,7.33444,56,1,12,01
DAB,Mulhouse local 1 1302,Mulhouse local 1,222064000,47.7503,7.31472,55,4,0d,02
DAB,Bourg-en-Bresse local 1 1601,Bourg-en-Bresse local 1,187072000,46.1917,5.33833,32,1.5,10,01
DAB,Bourgoin-Jallieu local 1 2802,Bourgoin-Jallieu local 1,178352000,45.5911,5.36583,35,3,1c,02
DAB,Strasbourg local 1 0510,Strasbourg local 1,185360000,48.5806,7.76583,54,4,05,0a
DAB,Lyon local 2 2504,Lyon local 2,176640000,45.965,4.70361,19,1.8,19,04
DAB,Lyon local 2 2510,Lyon local 2,176640000,45.8225,4.82083,32,6,19,0a
DAB,Nantes local 1 1710,Nantes local 1,190640000,47.2447,-1.60833,125,6,11,0a
DAB,La Roche-sur-Yon local 1 1902,La Roche-sur-Yon local 1,202928000,46.6892,-1.43722,47,6,13,02
DAB,Saint-Nazaire local 1 1401,Saint-Nazaire local 1,201072000,47.3253,-2.40639,63,3,0e,01
DAB,Saint-Nazaire local 1 1403,Saint-Nazaire local 1,201072000,47.1914,-2.07667,32,3,0e,03
DAB,Nantes étendu 1 0801,Nantes étendu 1,180064000,47.3253,-2.40639,63,2,08,01
DAB,Nantes étendu 1 0810,Nantes étendu 1,180064000,47.2447,-1.60833,125,9,08,0a
DAB,Nantes local 2 2010,Nantes local 2,223936000,47.2236,-1.615,98,4,14,0a
DAB,Rouen étendu 1 1610,Rouen étendu 1,209936000,49.4428,1.03306,66,10,10,0a
DAB,Rouen étendu 1 1601,Rouen étendu 1,209936000,49.5047,0.139722,49,10,10,01
DAB,Rouen local 1 0410,Rouen local 1,206352000,49.4472,1.13667,63,6,04,0a
DAB,Rouen local 2 2310,Rouen local 2,213360000,49.4472,1.13667,63,6,17,0a
DAB,Le Havre local 1 2901,Le Havre local 1,215072000,49.5072,0.0894444,30,4,1d,01
DAB,Paris intermédiaire 2 1310,Paris intermédiaire 2,187072000,48.8853,2.42222,144,8,0d,0a
DAB,Mâcon local 1 1401,Mâcon local 1,195936000,46.3517,4.78333,32,1.8,0e,01
DAB,Bordeaux étendu 1 1310,Bordeaux étendu 1,199360000,44.8203,-0.505556,163,6,0d,0a
DAB,Bordeaux étendu 1 1301,Bordeaux étendu 1,199360000,44.4328,0.07,37,4,0d,01
DAB,Bordeaux local 1 3010,Bordeaux local 1,188928000,44.8719,-0.515278,78,10,1e,0a
DAB,Bordeaux local 1 3010,Bordeaux local 1,188928000,44.8614,-0.558611,35,2,1e,0a
DAB,Bordeaux local 2 3110,Bordeaux local 2,197648000,44.8203,-0.505556,163,4,1f,0a
DAB,Arcachon local 1 2501,Arcachon local 1,220352000,44.6469,-1.16361,91,4,19,01
DAB,Toulouse étendu 1 1810,Toulouse étendu 1,183648000,43.5589,1.44639,27,10,12,0a
DAB,Toulouse étendu 1 0301,Toulouse étendu 1,183648000,43.9875,1.35917,34,5,03,01
DAB,Toulouse local 1 3010,Toulouse local 1,192352000,43.6058,1.46722,20,6,1e,0a
DAB,Toulouse local 2 3110,Toulouse local 2,199360000,43.5589,1.44639,27,5,1f,0a
DAB,métropole métropolitain 1 6708,métropole métropolitain 1,188928000,45.3156,4.74611,15,2.6,43,08
DAB,métropole métropolitain 1 6706,métropole métropolitain 1,188928000,44.9642,4.80083,25,10,43,06
DAB,métropole métropolitain 1 2701,métropole métropolitain 1,192352000,43.3586,5.57444,27,2.6,1b,01
DAB,métropole métropolitain 1 0604,métropole métropolitain 1,208064000,47.1456,4.66167,52,2.6,06,04
DAB,métropole métropolitain 1 0601,métropole métropolitain 1,208064000,47.0347,4.81389,42,4,06,01
DAB,métropole métropolitain 1 0610,métropole métropolitain 1,208064000,47.3003,4.99056,29,5,06,0a
DAB,métropole métropolitain 1 0605,métropole métropolitain 1,208064000,47.485,4.1575,32,1.7,06,05
DAB,métropole métropolitain 1 0606,métropole métropolitain 1,208064000,47.335,4.81083,21,2.6,06,06
DAB,métropole métropolitain 1 0607,métropole métropolitain 1,208064000,47.3469,4.45889,45,1.7,06,07
DAB,métropole métropolitain 1 0407,métropole métropolitain 1,211648000,44.6131,4.77917,26,3.6,04,07
DAB,métropole métropolitain 1 0402,métropole métropolitain 1,211648000,44.1814,4.66222,47,4,04,02
DAB,métropole métropolitain 1 1404,métropole métropolitain 1,185360000,48.0333,3.00278,78,1.5,0e,04
DAB,métropole métropolitain 1 1910,métropole métropolitain 1,188928000,45.8225,4.82083,34,13,13,0a
DAB,métropole métropolitain 1 6705,métropole métropolitain 1,188928000,45.5325,4.80861,45,3.4,43,05
DAB,métropole métropolitain 1 0602,métropole métropolitain 1,208064000,46.7364,4.665,27,6.5,06,02
DAB,métropole métropolitain 1 1903,métropole métropolitain 1,188928000,46.2817,4.68056,76,10,13,03
DAB,métropole métropolitain 1 1101,métropole métropolitain 1,185360000,48.88,2.28389,167,10,0b,01
DAB,métropole métropolitain 1 1110,métropole métropolitain 1,185360000,48.8675,2.41528,139,14,0b,0a
DAB,métropole métropolitain 1 1403,métropole métropolitain 1,185360000,48.1647,2.88139,38,1.8,0e,03
DAB,métropole métropolitain 1 1410,métropole métropolitain 1,185360000,48.4267,2.71083,46,5.6,0e,0a
DAB,métropole métropolitain 1 6702,métropole métropolitain 1,194064000,48.2911,2.68194,28,1.7,43,02
DAB,métropole métropolitain 1 0405,métropole métropolitain 1,211648000,43.97,4.85972,88,4,04,05
DAB,métropole métropolitain 1 0608,métropole métropolitain 1,208064000,47.5394,3.89389,20,5,06,08
DAB,métropole métropolitain 1 1010,métropole métropolitain 1,208064000,47.8406,3.66639,101,7,0a,0a
DAB,métropole métropolitain 1 1401,métropole métropolitain 1,185360000,48.6,2.44278,86,3,0e,01
DAB,métropole métropolitain 1 1402,métropole métropolitain 1,185360000,48.4783,2.42417,93,6,0e,02
DAB,métropole métropolitain 2 0808,métropole métropolitain 2,190640000,45.3156,4.74611,15,2.6,08,08
DAB,métropole métropolitain 2 0806,métropole métropolitain 2,190640000,44.9642,4.80083,25,5,08,06
DAB,métropole métropolitain 2 2807,métropole métropolitain 2,197648000,43.6319,5.09611,23,1.9,1c,07
DAB,métropole métropolitain 2 2808,métropole métropolitain 2,197648000,43.5403,5.23833,14,2,1c,08
DAB,métropole métropolitain 2 0010,métropole métropolitain 2,206352000,47.1456,4.66167,52,2.6,00,0a
DAB,métropole métropolitain 2 1310,métropole métropolitain 2,206352000,47.0347,4.81389,42,4,0d,0a
DAB,métropole métropolitain 2 0001,métropole métropolitain 2,206352000,47.485,4.1575,32,1.7,00,01
DAB,métropole métropolitain 2 0002,métropole métropolitain 2,206352000,47.3469,4.45889,45,1.7,00,02
DAB,métropole métropolitain 2 0607,métropole métropolitain 2,213360000,44.6131,4.77917,26,4,06,07
DAB,métropole métropolitain 2 0602,métropole métropolitain 2,213360000,44.1814,4.66222,47,4,06,02
DAB,métropole métropolitain 2 0504,métropole métropolitain 2,199360000,48.0333,3.00278,78,4,05,04
DAB,métropole métropolitain 2 2010,métropole métropolitain 2,190640000,45.8225,4.82083,34,10,14,0a
DAB,métropole métropolitain 2 0805,métropole métropolitain 2,190640000,45.5325,4.80861,45,10,08,05
DAB,métropole métropolitain 2 2004,métropole métropolitain 2,190640000,45.965,4.70361,19,10,14,04
DAB,métropole métropolitain 2 1302,métropole métropolitain 2,206352000,46.7364,4.665,27,6.5,0d,02
DAB,métropole métropolitain 2 0101,métropole métropolitain 2,199360000,46.3517,4.78333,32,5,01,01
DAB,métropole métropolitain 2 1301,métropole métropolitain 2,206352000,46.5656,4.87639,29,5,0d,01
DAB,métropole métropolitain 2 0501,métropole métropolitain 2,199360000,48.88,2.28389,167,10,05,01
DAB,métropole métropolitain 2 0510,métropole métropolitain 2,199360000,48.8675,2.41528,139,14,05,0a
DAB,métropole métropolitain 2 0505,métropole métropolitain 2,199360000,48.1647,2.88139,38,14,05,05
DAB,métropole métropolitain 2 0506,métropole métropolitain 2,199360000,48.4286,2.54028,36,1.7,05,06
DAB,métropole métropolitain 2 0507,métropole métropolitain 2,199360000,48.2911,2.68194,28,1.7,05,07
DAB,métropole métropolitain 2 0605,métropole métropolitain 2,213360000,43.97,4.85972,88,4.2,06,05
DAB,métropole métropolitain 2 0606,métropole métropolitain 2,213360000,43.8036,5.04694,39,4.2,06,06
DAB,métropole métropolitain 2 0003,métropole métropolitain 2,206352000,47.5394,3.89389,20,2.6,00,03
DAB,métropole métropolitain 2 0004,métropole métropolitain 2,206352000,47.8406,3.66639,101,7,00,04
DAB,métropole métropolitain 2 0503,métropole métropolitain 2,199360000,48.6286,2.42806,52,7,05,03
DAB,Dijon étendu 1 0410,Dijon étendu 1,204640000,47.3003,4.99056,29,7.5,04,0a
DAB,Dijon étendu 1 0402,Dijon étendu 1,204640000,46.7364,4.665,27,6.5,04,02
DAB,Avignon étendu 1 2505,Avignon étendu 1,208064000,43.97,4.85972,88,7.7,19,05
DAB,Avignon étendu 1 2502,Avignon étendu 1,208064000,44.1814,4.66222,47,6,19,02
DAB,Paris étendu 1 0701,Paris étendu 1,218640000,48.88,2.28389,167,10,07,01
DAB,Paris étendu 1 0710,Paris étendu 1,218640000,48.8675,2.41528,139,14,07,0a
DAB,Avignon local 1 3605,Avignon local 1,178352000,43.97,4.85972,88,4.6,24,05
DAB,Dijon local 1 6710,Dijon local 1,218640000,47.315,4.98667,27,7.7,43,0a
DAB,Lille étendu 1 0001,Lille étendu 1,195936000,50.4186,2.65944,74,10.5,00,01
DAB,Lyon local 1 2410,Lyon local 1,218640000,45.8181,4.90667,56,7,18,0a
DAB,Marseille étendu 1 3210,Marseille étendu 1,176640000,43.3839,5.42556,106,10,20,0a
DAB,Marseille étendu 1 3201,Marseille étendu 1,176640000,43.2747,5.30833,27,4,20,01
DAB,Paris intermédiaire 2 1301,Paris intermédiaire 2,187072000,48.8025,2.20444,103,10,0d,01
DAB,Nice intermédiaire 1 1004,Nice intermédiaire 1,218640000,43.7217,7.32083,53,8,0a,04
DAB,Nice intermédiaire 1 1005,Nice intermédiaire 1,218640000,43.5783,7.03556,45,4,0a,05
DAB,métropole métropolitain 1 2710,métropole métropolitain 1,192352000,43.3892,5.4125,71,20,1b,0a
DAB,Toulon local 1 0010,Toulon local 1,220352000,43.0531,5.84556,72,4,00,0a
DAB,Toulon étendu 1 2610,Toulon étendu 1,202928000,43.0531,5.84556,72,10,1a,0a
DAB,Toulon étendu 1 2601,Toulon étendu 1,202928000,43.3686,5.99194,49,4.8,1a,01
DAB,Toulon étendu 1 2602,Toulon étendu 1,202928000,43.2814,6.29417,70,4.8,1a,02
DAB,Toulon étendu 1 2603,Toulon étendu 1,202928000,43.1033,6.34889,22,5,1a,03
DAB,métropole métropolitain 2 2810,métropole métropolitain 2,197648000,43.3892,5.4125,71,20,1c,0a
DAB,Orléans local 1 2310,Orléans local 1,178352000,47.9433,1.9275,89,6.4,17,0a
DAB,Poitiers local 1 1410,Poitiers local 1,192352000,46.5628,0.348889,74,8.2,0e,0a
DAB,Tours local 1 2610,Tours local 1,204640000,47.4144,0.684722,52,9.4,1a,0a
DAB,Saint-Étienne étendu 1 0510,Saint-Étienne étendu 1,185360000,45.4006,4.38778,62,10,05,0a
DAB,Saint-Étienne étendu 1 0501,Saint-Étienne étendu 1,185360000,45.9844,3.91944,21,5,05,01
DAB,Nice intermédiaire 2 1705,Nice intermédiaire 2,220352000,43.5783,7.03556,46,4,11,05
DAB,Nice intermédiaire 2 1706,Nice intermédiaire 2,220352000,43.6611,6.9175,17,3.5,11,06
DAB,Nice local 2 0405,Nice local 2,208064000,43.5783,7.03556,46,4,04,05
DAB,Nice local 2 0404,Nice local 2,208064000,43.7217,7.32083,54,5,04,04
DAB,Paris intermédiaire 1 1201,Paris intermédiaire 1,181936000,48.8025,2.20444,103,10,0c,01
DAB,Lille local 1 0505,Lille local 1,192352000,50.6897,3.1825,78,0.4,05,05
DAB,Annecy étendu 1 1801,Annecy étendu 1,192352000,45.9164,6.17222,32,4,12,01
DAB,Annecy étendu 1 1002,Annecy étendu 1,192352000,46.1453,6.18861,24,4,0a,02
DAB,Annecy étendu 1 1803,Annecy étendu 1,192352000,45.66,5.82139,32,4,12,03
DAB,Annemasse local 1 3402,Annemasse local 1,194064000,46.1681,6.21806,23,3,22,02
DAB,Chambéry local 1 2103,Chambéry local 1,187072000,45.6633,5.82222,18,5,15,03
DAB,Grenoble local 1 2601,Grenoble local 1,206352000,45.1508,5.665,37,6.7,1a,01
DAB,Grenoble local 1 2602,Grenoble local 1,206352000,45.2603,5.54611,33,3.5,1a,02
DAB,Saint-Étienne local 1 3610,Saint-Étienne local 1,195936000,45.4,4.39389,22,8.6,24,0a
DAB,Orléans étendu 1 0810,Orléans étendu 1,209936000,47.9433,1.9275,88,9,08,0a
DAB,Orléans étendu 1 0801,Orléans étendu 1,209936000,47.6083,1.30306,58,3,08,01
DAB,Orléans étendu 1 0802,Orléans étendu 1,209936000,47.9983,2.74194,49,4,08,02
DAB,Poitiers étendu 1 0110,Poitiers étendu 1,206352000,46.5914,0.3475,61,6,01,0a
DAB,Poitiers étendu 1 0101,Poitiers étendu 1,206352000,46.9047,0.526389,70,5.5,01,01
DAB,Poitiers étendu 1 0102,Poitiers étendu 1,206352000,46.3489,-0.430556,82,4,01,02
DAB,Tours étendu 1 2710,Tours étendu 1,185360000,47.4058,0.724167,36,10,1b,0a
DAB,Tours étendu 1 2701,Tours étendu 1,185360000,46.9644,0.684444,27,1.7,1b,01
DAB,Tours étendu 1 2702,Tours étendu 1,185360000,47.1361,0.228333,20,2.6,1b,02
DAB,Annecy local 1 3101,Annecy local 1,222064000,45.9161,6.16944,25,8,1f,01
DAB,Grenoble étendu 1 0301,Grenoble étendu 1,180064000,45.1508,5.665,37,6.7,03,01
DAB,Grenoble étendu 1 0302,Grenoble étendu 1,180064000,45.2603,5.54611,33,3.5,03,02
DAB,Grenoble étendu 1 0303,Grenoble étendu 1,180064000,45.5578,5.45361,12,2.6,03,03
DAB,Valenciennes local 1 2206,Valenciennes local 1,188928000,50.2675,3.92194,46,9.5,16,06
DAB,Nice étendu 1 3105,Nice étendu 1,216928000,43.5694,7.035,48,3.5,1f,05
DAB,Nice étendu 1 3106,Nice étendu 1,216928000,43.4328,6.81167,24,4,1f,06
DAB,Besançon local 1 2410,Besançon local 1,202928000,47.2422,6.08361,79,3,18,0a
DAB,métropole métropolitain 1 2702,métropole métropolitain 1,192352000,43.8386,5.03028,13,1.7,1b,02
DAB,métropole métropolitain 1 0410,métropole métropolitain 1,211648000,43.8247,4.34083,73,5,04,0a
DAB,métropole métropolitain 1 1904,métropole métropolitain 1,188928000,45.7983,4.70361,20,0.1,13,04
DAB,Montpellier étendu 1 0802,Montpellier étendu 1,220352000,43.1642,2.97306,24,3.3,08,02
DAB,Montpellier étendu 1 0801,Montpellier étendu 1,220352000,43.3631,3.22917,27,3.3,08,01
DAB,Montpellier étendu 1 0810,Montpellier étendu 1,220352000,43.5242,3.64444,65,3.5,08,0a
DAB,Nîmes étendu 1 1701,Nîmes étendu 1,209936000,44.1164,4.05944,14,5,11,01
DAB,Perpignan local 1 0310,Perpignan local 1,216928000,42.7681,2.77917,26,5,03,0a
DAB,métropole métropolitain 2 2809,métropole métropolitain 2,197648000,43.3586,5.57444,27,2.6,1c,09
DAB,métropole métropolitain 2 1303,métropole métropolitain 2,206352000,47.3003,4.99056,29,5,0d,03
DAB,métropole métropolitain 2 0005,métropole métropolitain 2,206352000,47.335,4.81083,22,2.6,00,05
DAB,Amiens étendu 1 0810,Amiens étendu 1,0,49.8597,2.28222,,0,08,0a
DAB,Amiens local 1 3010,Amiens local 1,0,49.8597,2.28222,,0,1e,0a
DAB,Besançon étendu 1 1202,Besançon étendu 1,180064000,46.6519,5.58222,36,2.6,0c,02
DAB,Besançon étendu 1 1203,Besançon étendu 1,180064000,47.1169,5.47,35,2.6,0c,03
DAB,Annemasse local 1 3404,Annemasse local 1,194064000,46.2908,5.98528,11,8.7,22,04
1 Type Id Name Frequency (Hz) Latitude Longitude Altitude (m) Power TII Main TII Sub
3476 DAB Wolv'ton & Shrop 3708 Wolv'ton & Shrop 218640000 52.547739 -2.115424 225 0.6 37 08
3477 DAB Wolv'ton & Shrop 3709 Wolv'ton & Shrop 218640000 52.670935 -2.550228 396 1.95447 37 09
3478 DAB Wolv'ton & Shrop 3711 Wolv'ton & Shrop 218640000 52.496364 -2.048417 267 4 37 11
3479 DAB Marseille intermdiaire 1 2310 Marseille intermdiaire 1 195936000 43.3878 5.41167 66 10 17 0a
3480 DAB Marseille intermdiaire 1 2302 Marseille intermdiaire 1 195936000 43.5272 5.40194 25 3.5 17 02
3481 DAB Marseille intermdiaire 2 2410 Marseille intermdiaire 2 188928000 43.3839 5.42556 148 10 18 0a
3482 DAB Nice intermdiaire 2 1704 Nice intermdiaire 2 220352000 43.7217 7.32083 56 6 11 04
3483 DAB Nice local 2 0404 Nice local 2 208064000 43.7675 7.29556 30 12 04 04
3484 DAB Paris intermdiaire 1 1210 Paris intermdiaire 1 181936000 48.8853 2.42222 144 8 0c 0a
3485 DAB Paris local 2 2610 Paris local 2 204640000 48.8853 2.42222 144 8 1a 0a
3486 DAB Paris local 1 2210 Paris local 1 202928000 48.8872 2.34167 48 4 16 0a
3487 DAB Marseille local 3 0710 Marseille local 3 201072000 43.3878 5.41167 66 10 07 0a
3488 DAB Marseille local 2 1110 Marseille local 2 199360000 43.3892 5.4125 45 4 0b 0a
3489 DAB Paris local 3 2710 Paris local 3 216928000 48.8853 2.42222 144 4 1b 0a
3490 DAB Nice tendu 1 3104 Nice tendu 1 216928000 43.7142 7.30722 18 10 1f 04
3491 DAB Nice local 3 0604 Nice local 3 201072000 43.7217 7.32083 38 2 06 04
3492 DAB Lille local 1 0510 Lille local 1 192352000 50.6556 3.03056 70 7 05 0a
3493 DAB Douai-Lens local 1 2701 Douai-Lens local 1 222064000 50.4336 2.6075 30 4 1b 01
3494 DAB Lille tendu 1 0010 Lille tendu 1 195936000 50.6425 3.12389 143 4 00 0a
3495 DAB Calais-Boulogne-sur-Mer local 1 0102 Calais-Boulogne-sur-Mer local 1 176640000 50.9506 1.89083 63 3 01 02
3496 DAB Dunkerque local 1 0303 Dunkerque local 1 201072000 51.0331 2.35889 55 3 03 03
3497 DAB Valenciennes local 1 2204 Valenciennes local 1 188928000 50.3394 3.57389 46 8 16 04
3498 DAB Lille local 2 0610 Lille local 2 194064000 50.6425 3.12389 143 4 06 0a
3499 DAB Marseille local 2 1101 Marseille local 2 199360000 43.3586 5.57444 22 2 0b 01
3500 DAB Lyon tendu 1 1710 Lyon tendu 1 181936000 45.8225 4.82083 32 13 11 0a
3501 DAB Lyon tendu 1 1704 Lyon tendu 1 181936000 45.965 4.70361 19 2.1 11 04
3502 DAB Strasbourg local 2 0910 Strasbourg local 2 192352000 48.5867 7.74111 90 2.5 09 0a
3503 DAB Strasbourg tendu 1 6710 Strasbourg tendu 1 187072000 48.5867 7.74111 90 4 43 0a
3504 DAB Strasbourg tendu 1 6701 Strasbourg tendu 1 187072000 48.0767 7.32667 40 1.2 43 01
3505 DAB Strasbourg tendu 1 6702 Strasbourg tendu 1 187072000 47.7344 7.34556 15 2.5 43 02
3506 DAB Colmar local 1 1801 Colmar local 1 220352000 48.0786 7.33444 56 1 12 01
3507 DAB Mulhouse local 1 1302 Mulhouse local 1 222064000 47.7503 7.31472 55 4 0d 02
3508 DAB Bourg-en-Bresse local 1 1601 Bourg-en-Bresse local 1 187072000 46.1917 5.33833 32 1.5 10 01
3509 DAB Bourgoin-Jallieu local 1 2802 Bourgoin-Jallieu local 1 178352000 45.5911 5.36583 35 3 1c 02
3510 DAB Strasbourg local 1 0510 Strasbourg local 1 185360000 48.5806 7.76583 54 4 05 0a
3511 DAB Lyon local 2 2504 Lyon local 2 176640000 45.965 4.70361 19 1.8 19 04
3512 DAB Lyon local 2 2510 Lyon local 2 176640000 45.8225 4.82083 32 6 19 0a
3513 DAB Nantes local 1 1710 Nantes local 1 190640000 47.2447 -1.60833 125 6 11 0a
3514 DAB La Roche-sur-Yon local 1 1902 La Roche-sur-Yon local 1 202928000 46.6892 -1.43722 47 6 13 02
3515 DAB Saint-Nazaire local 1 1401 Saint-Nazaire local 1 201072000 47.3253 -2.40639 63 3 0e 01
3516 DAB Saint-Nazaire local 1 1403 Saint-Nazaire local 1 201072000 47.1914 -2.07667 32 3 0e 03
3517 DAB Nantes tendu 1 0801 Nantes tendu 1 180064000 47.3253 -2.40639 63 2 08 01
3518 DAB Nantes tendu 1 0810 Nantes tendu 1 180064000 47.2447 -1.60833 125 9 08 0a
3519 DAB Nantes local 2 2010 Nantes local 2 223936000 47.2236 -1.615 98 4 14 0a
3520 DAB Rouen tendu 1 1610 Rouen tendu 1 209936000 49.4428 1.03306 66 10 10 0a
3521 DAB Rouen tendu 1 1601 Rouen tendu 1 209936000 49.5047 0.139722 49 10 10 01
3522 DAB Rouen local 1 0410 Rouen local 1 206352000 49.4472 1.13667 63 6 04 0a
3523 DAB Rouen local 2 2310 Rouen local 2 213360000 49.4472 1.13667 63 6 17 0a
3524 DAB Le Havre local 1 2901 Le Havre local 1 215072000 49.5072 0.0894444 30 4 1d 01
3525 DAB Paris intermdiaire 2 1310 Paris intermdiaire 2 187072000 48.8853 2.42222 144 8 0d 0a
3526 DAB Mcon local 1 1401 Mcon local 1 195936000 46.3517 4.78333 32 1.8 0e 01
3527 DAB Bordeaux tendu 1 1310 Bordeaux tendu 1 199360000 44.8203 -0.505556 163 6 0d 0a
3528 DAB Bordeaux tendu 1 1301 Bordeaux tendu 1 199360000 44.4328 0.07 37 4 0d 01
3529 DAB Bordeaux local 1 3010 Bordeaux local 1 188928000 44.8719 -0.515278 78 10 1e 0a
3530 DAB Bordeaux local 1 3010 Bordeaux local 1 188928000 44.8614 -0.558611 35 2 1e 0a
3531 DAB Bordeaux local 2 3110 Bordeaux local 2 197648000 44.8203 -0.505556 163 4 1f 0a
3532 DAB Arcachon local 1 2501 Arcachon local 1 220352000 44.6469 -1.16361 91 4 19 01
3533 DAB Toulouse tendu 1 1810 Toulouse tendu 1 183648000 43.5589 1.44639 27 10 12 0a
3534 DAB Toulouse tendu 1 0301 Toulouse tendu 1 183648000 43.9875 1.35917 34 5 03 01
3535 DAB Toulouse local 1 3010 Toulouse local 1 192352000 43.6058 1.46722 20 6 1e 0a
3536 DAB Toulouse local 2 3110 Toulouse local 2 199360000 43.5589 1.44639 27 5 1f 0a
3537 DAB mtropole mtropolitain 1 6708 mtropole mtropolitain 1 188928000 45.3156 4.74611 15 2.6 43 08
3538 DAB mtropole mtropolitain 1 6706 mtropole mtropolitain 1 188928000 44.9642 4.80083 25 10 43 06
3539 DAB mtropole mtropolitain 1 2701 mtropole mtropolitain 1 192352000 43.3586 5.57444 27 2.6 1b 01
3540 DAB mtropole mtropolitain 1 0604 mtropole mtropolitain 1 208064000 47.1456 4.66167 52 2.6 06 04
3541 DAB mtropole mtropolitain 1 0601 mtropole mtropolitain 1 208064000 47.0347 4.81389 42 4 06 01
3542 DAB mtropole mtropolitain 1 0610 mtropole mtropolitain 1 208064000 47.3003 4.99056 29 5 06 0a
3543 DAB mtropole mtropolitain 1 0605 mtropole mtropolitain 1 208064000 47.485 4.1575 32 1.7 06 05
3544 DAB mtropole mtropolitain 1 0606 mtropole mtropolitain 1 208064000 47.335 4.81083 21 2.6 06 06
3545 DAB mtropole mtropolitain 1 0607 mtropole mtropolitain 1 208064000 47.3469 4.45889 45 1.7 06 07
3546 DAB mtropole mtropolitain 1 0407 mtropole mtropolitain 1 211648000 44.6131 4.77917 26 3.6 04 07
3547 DAB mtropole mtropolitain 1 0402 mtropole mtropolitain 1 211648000 44.1814 4.66222 47 4 04 02
3548 DAB mtropole mtropolitain 1 1404 mtropole mtropolitain 1 185360000 48.0333 3.00278 78 1.5 0e 04
3549 DAB mtropole mtropolitain 1 1910 mtropole mtropolitain 1 188928000 45.8225 4.82083 34 13 13 0a
3550 DAB mtropole mtropolitain 1 6705 mtropole mtropolitain 1 188928000 45.5325 4.80861 45 3.4 43 05
3551 DAB mtropole mtropolitain 1 0602 mtropole mtropolitain 1 208064000 46.7364 4.665 27 6.5 06 02
3552 DAB mtropole mtropolitain 1 1903 mtropole mtropolitain 1 188928000 46.2817 4.68056 76 10 13 03
3553 DAB mtropole mtropolitain 1 1101 mtropole mtropolitain 1 185360000 48.88 2.28389 167 10 0b 01
3554 DAB mtropole mtropolitain 1 1110 mtropole mtropolitain 1 185360000 48.8675 2.41528 139 14 0b 0a
3555 DAB mtropole mtropolitain 1 1403 mtropole mtropolitain 1 185360000 48.1647 2.88139 38 1.8 0e 03
3556 DAB mtropole mtropolitain 1 1410 mtropole mtropolitain 1 185360000 48.4267 2.71083 46 5.6 0e 0a
3557 DAB mtropole mtropolitain 1 6702 mtropole mtropolitain 1 194064000 48.2911 2.68194 28 1.7 43 02
3558 DAB mtropole mtropolitain 1 0405 mtropole mtropolitain 1 211648000 43.97 4.85972 88 4 04 05
3559 DAB mtropole mtropolitain 1 0608 mtropole mtropolitain 1 208064000 47.5394 3.89389 20 5 06 08
3560 DAB mtropole mtropolitain 1 1010 mtropole mtropolitain 1 208064000 47.8406 3.66639 101 7 0a 0a
3561 DAB mtropole mtropolitain 1 1401 mtropole mtropolitain 1 185360000 48.6 2.44278 86 3 0e 01
3562 DAB mtropole mtropolitain 1 1402 mtropole mtropolitain 1 185360000 48.4783 2.42417 93 6 0e 02
3563 DAB mtropole mtropolitain 2 0808 mtropole mtropolitain 2 190640000 45.3156 4.74611 15 2.6 08 08
3564 DAB mtropole mtropolitain 2 0806 mtropole mtropolitain 2 190640000 44.9642 4.80083 25 5 08 06
3565 DAB mtropole mtropolitain 2 2807 mtropole mtropolitain 2 197648000 43.6319 5.09611 23 1.9 1c 07
3566 DAB mtropole mtropolitain 2 2808 mtropole mtropolitain 2 197648000 43.5403 5.23833 14 2 1c 08
3567 DAB mtropole mtropolitain 2 0010 mtropole mtropolitain 2 206352000 47.1456 4.66167 52 2.6 00 0a
3568 DAB mtropole mtropolitain 2 1310 mtropole mtropolitain 2 206352000 47.0347 4.81389 42 4 0d 0a
3569 DAB mtropole mtropolitain 2 0001 mtropole mtropolitain 2 206352000 47.485 4.1575 32 1.7 00 01
3570 DAB mtropole mtropolitain 2 0002 mtropole mtropolitain 2 206352000 47.3469 4.45889 45 1.7 00 02
3571 DAB mtropole mtropolitain 2 0607 mtropole mtropolitain 2 213360000 44.6131 4.77917 26 4 06 07
3572 DAB mtropole mtropolitain 2 0602 mtropole mtropolitain 2 213360000 44.1814 4.66222 47 4 06 02
3573 DAB mtropole mtropolitain 2 0504 mtropole mtropolitain 2 199360000 48.0333 3.00278 78 4 05 04
3574 DAB mtropole mtropolitain 2 2010 mtropole mtropolitain 2 190640000 45.8225 4.82083 34 10 14 0a
3575 DAB mtropole mtropolitain 2 0805 mtropole mtropolitain 2 190640000 45.5325 4.80861 45 10 08 05
3576 DAB mtropole mtropolitain 2 2004 mtropole mtropolitain 2 190640000 45.965 4.70361 19 10 14 04
3577 DAB mtropole mtropolitain 2 1302 mtropole mtropolitain 2 206352000 46.7364 4.665 27 6.5 0d 02
3578 DAB mtropole mtropolitain 2 0101 mtropole mtropolitain 2 199360000 46.3517 4.78333 32 5 01 01
3579 DAB mtropole mtropolitain 2 1301 mtropole mtropolitain 2 206352000 46.5656 4.87639 29 5 0d 01
3580 DAB mtropole mtropolitain 2 0501 mtropole mtropolitain 2 199360000 48.88 2.28389 167 10 05 01
3581 DAB mtropole mtropolitain 2 0510 mtropole mtropolitain 2 199360000 48.8675 2.41528 139 14 05 0a
3582 DAB mtropole mtropolitain 2 0505 mtropole mtropolitain 2 199360000 48.1647 2.88139 38 14 05 05
3583 DAB mtropole mtropolitain 2 0506 mtropole mtropolitain 2 199360000 48.4286 2.54028 36 1.7 05 06
3584 DAB mtropole mtropolitain 2 0507 mtropole mtropolitain 2 199360000 48.2911 2.68194 28 1.7 05 07
3585 DAB mtropole mtropolitain 2 0605 mtropole mtropolitain 2 213360000 43.97 4.85972 88 4.2 06 05
3586 DAB mtropole mtropolitain 2 0606 mtropole mtropolitain 2 213360000 43.8036 5.04694 39 4.2 06 06
3587 DAB mtropole mtropolitain 2 0003 mtropole mtropolitain 2 206352000 47.5394 3.89389 20 2.6 00 03
3588 DAB mtropole mtropolitain 2 0004 mtropole mtropolitain 2 206352000 47.8406 3.66639 101 7 00 04
3589 DAB mtropole mtropolitain 2 0503 mtropole mtropolitain 2 199360000 48.6286 2.42806 52 7 05 03
3590 DAB Dijon tendu 1 0410 Dijon tendu 1 204640000 47.3003 4.99056 29 7.5 04 0a
3591 DAB Dijon tendu 1 0402 Dijon tendu 1 204640000 46.7364 4.665 27 6.5 04 02
3592 DAB Avignon tendu 1 2505 Avignon tendu 1 208064000 43.97 4.85972 88 7.7 19 05
3593 DAB Avignon tendu 1 2502 Avignon tendu 1 208064000 44.1814 4.66222 47 6 19 02
3594 DAB Paris tendu 1 0701 Paris tendu 1 218640000 48.88 2.28389 167 10 07 01
3595 DAB Paris tendu 1 0710 Paris tendu 1 218640000 48.8675 2.41528 139 14 07 0a
3596 DAB Avignon local 1 3605 Avignon local 1 178352000 43.97 4.85972 88 4.6 24 05
3597 DAB Dijon local 1 6710 Dijon local 1 218640000 47.315 4.98667 27 7.7 43 0a
3598 DAB Lille tendu 1 0001 Lille tendu 1 195936000 50.4186 2.65944 74 10.5 00 01
3599 DAB Lyon local 1 2410 Lyon local 1 218640000 45.8181 4.90667 56 7 18 0a
3600 DAB Marseille tendu 1 3210 Marseille tendu 1 176640000 43.3839 5.42556 106 10 20 0a
3601 DAB Marseille tendu 1 3201 Marseille tendu 1 176640000 43.2747 5.30833 27 4 20 01
3602 DAB Paris intermdiaire 2 1301 Paris intermdiaire 2 187072000 48.8025 2.20444 103 10 0d 01
3603 DAB Nice intermdiaire 1 1004 Nice intermdiaire 1 218640000 43.7217 7.32083 53 8 0a 04
3604 DAB Nice intermdiaire 1 1005 Nice intermdiaire 1 218640000 43.5783 7.03556 45 4 0a 05
3605 DAB mtropole mtropolitain 1 2710 mtropole mtropolitain 1 192352000 43.3892 5.4125 71 20 1b 0a
3606 DAB Toulon local 1 0010 Toulon local 1 220352000 43.0531 5.84556 72 4 00 0a
3607 DAB Toulon tendu 1 2610 Toulon tendu 1 202928000 43.0531 5.84556 72 10 1a 0a
3608 DAB Toulon tendu 1 2601 Toulon tendu 1 202928000 43.3686 5.99194 49 4.8 1a 01
3609 DAB Toulon tendu 1 2602 Toulon tendu 1 202928000 43.2814 6.29417 70 4.8 1a 02
3610 DAB Toulon tendu 1 2603 Toulon tendu 1 202928000 43.1033 6.34889 22 5 1a 03
3611 DAB mtropole mtropolitain 2 2810 mtropole mtropolitain 2 197648000 43.3892 5.4125 71 20 1c 0a
3612 DAB Orlans local 1 2310 Orlans local 1 178352000 47.9433 1.9275 89 6.4 17 0a
3613 DAB Poitiers local 1 1410 Poitiers local 1 192352000 46.5628 0.348889 74 8.2 0e 0a
3614 DAB Tours local 1 2610 Tours local 1 204640000 47.4144 0.684722 52 9.4 1a 0a
3615 DAB Saint-tienne tendu 1 0510 Saint-tienne tendu 1 185360000 45.4006 4.38778 62 10 05 0a
3616 DAB Saint-tienne tendu 1 0501 Saint-tienne tendu 1 185360000 45.9844 3.91944 21 5 05 01
3617 DAB Nice intermdiaire 2 1705 Nice intermdiaire 2 220352000 43.5783 7.03556 46 4 11 05
3618 DAB Nice intermdiaire 2 1706 Nice intermdiaire 2 220352000 43.6611 6.9175 17 3.5 11 06
3619 DAB Nice local 2 0405 Nice local 2 208064000 43.5783 7.03556 46 4 04 05
3620 DAB Nice local 2 0404 Nice local 2 208064000 43.7217 7.32083 54 5 04 04
3621 DAB Paris intermdiaire 1 1201 Paris intermdiaire 1 181936000 48.8025 2.20444 103 10 0c 01
3622 DAB Lille local 1 0505 Lille local 1 192352000 50.6897 3.1825 78 0.4 05 05
3623 DAB Annecy tendu 1 1801 Annecy tendu 1 192352000 45.9164 6.17222 32 4 12 01
3624 DAB Annecy tendu 1 1002 Annecy tendu 1 192352000 46.1453 6.18861 24 4 0a 02
3625 DAB Annecy tendu 1 1803 Annecy tendu 1 192352000 45.66 5.82139 32 4 12 03
3626 DAB Annemasse local 1 3402 Annemasse local 1 194064000 46.1681 6.21806 23 3 22 02
3627 DAB Chambry local 1 2103 Chambry local 1 187072000 45.6633 5.82222 18 5 15 03
3628 DAB Grenoble local 1 2601 Grenoble local 1 206352000 45.1508 5.665 37 6.7 1a 01
3629 DAB Grenoble local 1 2602 Grenoble local 1 206352000 45.2603 5.54611 33 3.5 1a 02
3630 DAB Saint-tienne local 1 3610 Saint-tienne local 1 195936000 45.4 4.39389 22 8.6 24 0a
3631 DAB Orlans tendu 1 0810 Orlans tendu 1 209936000 47.9433 1.9275 88 9 08 0a
3632 DAB Orlans tendu 1 0801 Orlans tendu 1 209936000 47.6083 1.30306 58 3 08 01
3633 DAB Orlans tendu 1 0802 Orlans tendu 1 209936000 47.9983 2.74194 49 4 08 02
3634 DAB Poitiers tendu 1 0110 Poitiers tendu 1 206352000 46.5914 0.3475 61 6 01 0a
3635 DAB Poitiers tendu 1 0101 Poitiers tendu 1 206352000 46.9047 0.526389 70 5.5 01 01
3636 DAB Poitiers tendu 1 0102 Poitiers tendu 1 206352000 46.3489 -0.430556 82 4 01 02
3637 DAB Tours tendu 1 2710 Tours tendu 1 185360000 47.4058 0.724167 36 10 1b 0a
3638 DAB Tours tendu 1 2701 Tours tendu 1 185360000 46.9644 0.684444 27 1.7 1b 01
3639 DAB Tours tendu 1 2702 Tours tendu 1 185360000 47.1361 0.228333 20 2.6 1b 02
3640 DAB Annecy local 1 3101 Annecy local 1 222064000 45.9161 6.16944 25 8 1f 01
3641 DAB Grenoble tendu 1 0301 Grenoble tendu 1 180064000 45.1508 5.665 37 6.7 03 01
3642 DAB Grenoble tendu 1 0302 Grenoble tendu 1 180064000 45.2603 5.54611 33 3.5 03 02
3643 DAB Grenoble tendu 1 0303 Grenoble tendu 1 180064000 45.5578 5.45361 12 2.6 03 03
3644 DAB Valenciennes local 1 2206 Valenciennes local 1 188928000 50.2675 3.92194 46 9.5 16 06
3645 DAB Nice tendu 1 3105 Nice tendu 1 216928000 43.5694 7.035 48 3.5 1f 05
3646 DAB Nice tendu 1 3106 Nice tendu 1 216928000 43.4328 6.81167 24 4 1f 06
3647 DAB Besanon local 1 2410 Besanon local 1 202928000 47.2422 6.08361 79 3 18 0a
3648 DAB mtropole mtropolitain 1 2702 mtropole mtropolitain 1 192352000 43.8386 5.03028 13 1.7 1b 02
3649 DAB mtropole mtropolitain 1 0410 mtropole mtropolitain 1 211648000 43.8247 4.34083 73 5 04 0a
3650 DAB mtropole mtropolitain 1 1904 mtropole mtropolitain 1 188928000 45.7983 4.70361 20 0.1 13 04
3651 DAB Montpellier tendu 1 0802 Montpellier tendu 1 220352000 43.1642 2.97306 24 3.3 08 02
3652 DAB Montpellier tendu 1 0801 Montpellier tendu 1 220352000 43.3631 3.22917 27 3.3 08 01
3653 DAB Montpellier tendu 1 0810 Montpellier tendu 1 220352000 43.5242 3.64444 65 3.5 08 0a
3654 DAB Nmes tendu 1 1701 Nmes tendu 1 209936000 44.1164 4.05944 14 5 11 01
3655 DAB Perpignan local 1 0310 Perpignan local 1 216928000 42.7681 2.77917 26 5 03 0a
3656 DAB mtropole mtropolitain 2 2809 mtropole mtropolitain 2 197648000 43.3586 5.57444 27 2.6 1c 09
3657 DAB mtropole mtropolitain 2 1303 mtropole mtropolitain 2 206352000 47.3003 4.99056 29 5 0d 03
3658 DAB mtropole mtropolitain 2 0005 mtropole mtropolitain 2 206352000 47.335 4.81083 22 2.6 00 05
3659 DAB Amiens tendu 1 0810 Amiens tendu 1 0 49.8597 2.28222 0 08 0a
3660 DAB Amiens local 1 3010 Amiens local 1 0 49.8597 2.28222 0 1e 0a
3661 DAB Besanon tendu 1 1202 Besanon tendu 1 180064000 46.6519 5.58222 36 2.6 0c 02
3662 DAB Besanon tendu 1 1203 Besanon tendu 1 180064000 47.1169 5.47 35 2.6 0c 03
3663 DAB Annemasse local 1 3404 Annemasse local 1 194064000 46.2908 5.98528 11 8.7 22 04
+46
View File
@@ -38,6 +38,7 @@
#include "util/units.h"
#include "util/maidenhead.h"
#include "util/morse.h"
#include "util/navtex.h"
#include "maplocationdialog.h"
#include "mapmaidenheaddialog.h"
#include "mapsettingsdialog.h"
@@ -307,6 +308,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
addNavAids();
addAirspace();
addAirports();
addNavtex();
displaySettings();
applySettings(true);
@@ -985,6 +987,50 @@ void MapGUI::airportsUpdated()
addAirports();
}
void MapGUI::addNavtex()
{
for (int i = 0; i < NavtexTransmitter::m_navtexTransmitters.size(); i++)
{
SWGSDRangel::SWGMapItem navtexMapItem;
QString name = QString("%1").arg(NavtexTransmitter::m_navtexTransmitters[i].m_station);
navtexMapItem.setName(new QString(name));
navtexMapItem.setLatitude(NavtexTransmitter::m_navtexTransmitters[i].m_latitude);
navtexMapItem.setLongitude(NavtexTransmitter::m_navtexTransmitters[i].m_longitude);
navtexMapItem.setAltitude(0.0);
navtexMapItem.setImage(new QString("antenna.png"));
navtexMapItem.setImageRotation(0);
QString text = QString("Navtex Transmitter\nStation: %1\nArea: %2")
.arg(NavtexTransmitter::m_navtexTransmitters[i].m_station)
.arg(NavtexTransmitter::m_navtexTransmitters[i].m_area);
QStringList schedules;
for (const auto& schedule : NavtexTransmitter::m_navtexTransmitters[i].m_schedules)
{
QString scheduleText = QString("\nFrequency: %1 kHz\nID: %2").arg(schedule.m_frequency / 1000).arg(schedule.m_id);
if (schedule.m_times.size() > 0)
{
QStringList times;
for (const auto& time : schedule.m_times) {
times.append(time.toString("hh:mm"));
}
scheduleText.append("\nTimes: ");
scheduleText.append(times.join(" "));
scheduleText.append(" UTC");
}
schedules.append(scheduleText);
}
text.append(schedules.join(""));
navtexMapItem.setText(new QString(text));
navtexMapItem.setModel(new QString("antenna.glb"));
navtexMapItem.setFixedPosition(true);
navtexMapItem.setOrientation(0);
navtexMapItem.setLabel(new QString(name));
navtexMapItem.setLabelAltitudeOffset(4.5);
navtexMapItem.setAltitudeReference(1);
update(m_map, &navtexMapItem, "Navtex");
}
}
void MapGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
+1
View File
@@ -160,6 +160,7 @@ public:
void addAirspace(const Airspace *airspace, const QString& group, int cnt);
void addAirspace();
void addAirports();
void addNavtex();
void find(const QString& target);
void track3D(const QString& target);
Q_INVOKABLE void supportedMapsChanged();
+2
View File
@@ -94,6 +94,8 @@ MapSettings::MapSettings() :
dabSettings->m_filterDistance = 75000;
m_itemSettings.insert("DAB", dabSettings);
m_itemSettings.insert("Navtex", new MapItemSettings("Navtex", false, QColor(255, 0, 255), false, true, 8));
MapItemSettings *navAidSettings = new MapItemSettings("NavAid", false, QColor(255, 0, 255), false, true, 11);
navAidSettings->m_filterDistance = 500000;
m_itemSettings.insert("NavAid", navAidSettings);
+5 -3
View File
@@ -12,16 +12,18 @@ On top of this, it can plot data from other plugins, such as:
* Weather imagery from APT Demodulator,
* The Sun, Moon and Stars from the Star Tracker,
* Weather ballons from the RadioSonde feature,
* RF Heat Maps from the Heap Map channel,
* Radials and estimated position from the VOR localizer feature.
As well as other data sources:
As well as internet data sources:
* AM, FM and DAB transmitters in the UK,
* AM, FM and DAB transmitters in the UK and DAB transmitters in France,
* Airports, NavAids and airspaces,
* Beacons based on the IARU Region 1 beacon database and International Beacon Project,
* Radio time transmitters,
* GRAVES radar,
* Ionosonde station data.
* Ionosonde station data,
* Navtex transmitters.
It can also create tracks showing the path aircraft, ships and APRS objects have taken, as well as predicted paths for satellites.
+5
View File
@@ -176,6 +176,7 @@ set(sdrbase_SOURCES
util/aprs.cpp
util/astronomy.cpp
util/azel.cpp
util/baudot.cpp
util/colormap.cpp
util/coordinates.cpp
util/crc.cpp
@@ -195,6 +196,7 @@ set(sdrbase_SOURCES
util/message.cpp
util/messagequeue.cpp
util/morse.cpp
util/navtex.cpp
util/openaip.cpp
util/osndb.cpp
util/ourairportsdb.cpp
@@ -399,6 +401,7 @@ set(sdrbase_HEADERS
util/aprs.h
util/astronomy.h
util/azel.h
util/baudot.h
util/colormap.h
util/coordinates.h
util/CRC64.h
@@ -423,6 +426,8 @@ set(sdrbase_HEADERS
util/messagequeue.h
util/morse.h
util/movingaverage.h
util/movingmaximum.h
util/navtex.h
util/openaip.h
util/osndb.h
util/outairportsdb.h
+14
View File
@@ -19,6 +19,7 @@
#pragma once
#include <cmath>
#include <cstdio>
#include "dsp/dsptypes.h"
#include "dsp/misc.h"
#include "export.h"
@@ -57,6 +58,19 @@ public:
return acc;
}
// Print taps as a Matlab vector
void printTaps(const char *name)
{
printf("%s = [", name);
for (int i = 0; i <= m_taps.size() - 1; ++i) {
printf("%g ", m_taps[i]);
}
for (int i = m_taps.size() - 2; i >= 0; --i) {
printf("%g ", m_taps[i]);
}
printf("];\n");
}
protected:
void init(int nTaps)
{
+14
View File
@@ -20,6 +20,7 @@
#define INCLUDE_RAISEDCOSINE_H
#include <cmath>
#include <cstdio>
#include "dsp/dsptypes.h"
// Raised-cosine low-pass filter for pulse shaping, without intersymbol interference (ISI)
@@ -131,6 +132,19 @@ public:
return acc;
}
// Print taps as a Matlab vector
void printTaps(const char *name)
{
printf("%s = [", name);
for (int i = 0; i <= m_taps.size() - 1; ++i) {
printf("%g ", m_taps[i]);
}
for (int i = m_taps.size() - 2; i >= 0; --i) {
printf("%g ", m_taps[i]);
}
printf("];\n");
}
private:
std::vector<Real> m_taps;
std::vector<Type> m_samples;
+192
View File
@@ -0,0 +1,192 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "baudot.h"
// https://en.wikipedia.org/wiki/Baudot_code
// We use < for FIGS and > for LTRS and ^ for Cyrillic
// Unicode used for source file encoding
const QString Baudot::m_ita2Letter[] = {
"\0", "E", "\n", "A", " ", "S", "I", "U",
"\r", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", ">"
};
const QString Baudot::m_ita2Figure[] = {
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "\x5", "4", "\a", ",", "!", ":", "(",
"5", "+", ")", "2", "£", "6", "0", "1",
"9", "?", "&", "<", ".", "/", "=", ">"
};
const QString Baudot::m_ukLetter[] = {
"\0", "A", "E", "/", "Y", "U", "I", "O",
"<", "J", "G", "H", "B", "C", "F", "D",
" ", "-", "X", "Z", "S", "T", "W", "V",
"\b", "K", "M", "L", "R", "Q", "N", "P"
};
const QString Baudot::m_ukFigure[] = {
"\0", "1", "2", "", "3", "4", "³⁄", "5",
" ", "6", "7", "¹", "8", "9", "⁵⁄", "0",
">", ".", "⁹⁄", ":", "⁷⁄", "²", "?", "\'",
"\b", "(", ")", "=", "-", "/", "£", "+"
};
const QString Baudot::m_europeanLetter[] = {
"\0", "A", "E", "É", "Y", "U", "I", "O",
"<", "J", "G", "H", "B", "C", "F", "D",
" ", "t", "X", "Z", "S", "T", "W", "V",
"\b", "K", "M", "L", "R", "Q", "N", "P"
};
const QString Baudot::m_europeanFigure[] = {
"\0", "1", "2", "&", "3", "4", "º", "5",
" ", "6", "7", "", "8", "9", "", "0",
">", ".", ",", ":", ";", "!", "?", "\'",
"\b", "(", ")", "=", "-", "/", "", "%"
};
const QString Baudot::m_usLetter[] = {
"\0", "E", "\n", "A", " ", "S", "I", "U",
"\r", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", ">"
};
const QString Baudot::m_usFigure[] = {
"\0", "3", "\n", "-", " ", "\a", "8", "7",
"\r", "\x5", "4", "\'", ",", "!", ":", "(",
"5", "\"", ")", "2", "#", "6", "0", "1",
"9", "?", "&", "<", ".", "/", ";", ">"
};
const QString Baudot::m_russianLetter[] = {
"\0", "Е", "\n", "А", " ", "С", "И", "У",
"\r", "Д", "П", "Й", "Н", "Ф", "Ц", "К",
"Т", "З", "Л", "В", "Х", "Ы", "P", "Я",
"О", "Б", "Г", "<", "М", "Ь", "Ж", ">"
};
const QString Baudot::m_russianFigure[] = {
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "Ч", "4", "Ю", ",", "Э", ":", "(",
"5", "+", ")", "2", "Щ", "6", "0", "1",
"9", "?", "Ш", "<", ".", "/", ";", ">"
};
const QString Baudot::m_murrayLetter[] = {
" ", "E", "?", "A", ">", "S", "I", "U",
"\n", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", "\b"
};
const QString Baudot::m_murrayFigure[] = {
" ", "3", "?", " ", ">", "'", "8", "7",
"\n", "²", "4", "⁷⁄", "-", "", "(", "⁹⁄",
"5", ".", "/", "2", "⁵⁄", "6", "0", "1",
"9", "?", "³⁄", "<", ",", "£", ")", "\b"
};
BaudotDecoder::BaudotDecoder()
{
setCharacterSet(Baudot::ITA2);
setUnshiftOnSpace(false);
init();
}
void BaudotDecoder::setCharacterSet(Baudot::CharacterSet characterSet)
{
m_characterSet = characterSet;
switch (m_characterSet)
{
case Baudot::ITA2:
m_letters = Baudot::m_ita2Letter;
m_figures = Baudot::m_ita2Figure;
break;
case Baudot::UK:
m_letters = Baudot::m_ukLetter;
m_figures = Baudot::m_ukFigure;
break;
case Baudot::EUROPEAN:
m_letters = Baudot::m_europeanLetter;
m_figures = Baudot::m_europeanFigure;
break;
case Baudot::US:
m_letters = Baudot::m_usLetter;
m_figures = Baudot::m_usFigure;
break;
case Baudot::RUSSIAN:
m_letters = Baudot::m_russianLetter;
m_figures = Baudot::m_russianFigure;
break;
case Baudot::MURRAY:
m_letters = Baudot::m_murrayLetter;
m_figures = Baudot::m_murrayFigure;
break;
default:
qDebug() << "BaudotDecoder::BaudotDecoder: Unsupported character set " << m_characterSet;
m_letters = Baudot::m_ita2Letter;
m_figures = Baudot::m_ita2Figure;
m_characterSet = Baudot::ITA2;
break;
}
}
void BaudotDecoder::setUnshiftOnSpace(bool unshiftOnSpace)
{
m_unshiftOnSpace = unshiftOnSpace;
}
void BaudotDecoder::init()
{
m_figure = false;
}
QString BaudotDecoder::decode(char bits)
{
QString c = m_figure ? m_figures[(int)bits] : m_letters[(int)bits];
if ((c == '>') || (m_unshiftOnSpace && (c == " ")))
{
// Switch to letters
m_figure = false;
if (m_characterSet == Baudot::RUSSIAN) {
m_letters = Baudot::m_ita2Letter;
}
}
if (c == '<')
{
// Switch to figures
m_figure = true;
}
if ((m_characterSet == Baudot::RUSSIAN) && (c == '\0'))
{
// Switch to Cyrillic
m_figure = false;
m_letters = Baudot::m_russianLetter;
c = '^';
}
return c;
}
+77
View File
@@ -0,0 +1,77 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_UTIL_BAUDOT_H
#define INCLUDE_UTIL_BAUDOT_H
#include <QString>
#include <QDateTime>
#include <QMap>
#include "export.h"
class SDRBASE_API Baudot {
public:
enum CharacterSet {
ITA2,
UK,
EUROPEAN,
US,
RUSSIAN, // MTK-2
MURRAY
};
// QString used for fractions in figure set
static const QString m_ita2Letter[];
static const QString m_ita2Figure[];
static const QString m_ukLetter[];
static const QString m_ukFigure[];
static const QString m_europeanLetter[];
static const QString m_europeanFigure[];
static const QString m_usLetter[];
static const QString m_usFigure[];
static const QString m_russianLetter[];
static const QString m_russianFigure[];
static const QString m_murrayLetter[];
static const QString m_murrayFigure[];
};
class SDRBASE_API BaudotDecoder {
public:
BaudotDecoder();
void setCharacterSet(Baudot::CharacterSet characterSet=Baudot::ITA2);
void setUnshiftOnSpace(bool unshiftOnSpace);
void init();
QString decode(char bits);
private:
Baudot::CharacterSet m_characterSet;
bool m_unshiftOnSpace;
const QString *m_letters;
const QString *m_figures;
bool m_figure;
};
#endif // INCLUDE_UTIL_BAUDOT_H
+4 -4
View File
@@ -70,7 +70,7 @@ QHash<QString, QString> *CSV::hash(const QString& filename, int reserve)
// Read a row from a CSV file (handling quotes)
// https://stackoverflow.com/questions/27318631/parsing-through-a-csv-file-in-qt
bool CSV::readRow(QTextStream &in, QStringList *row)
bool CSV::readRow(QTextStream &in, QStringList *row, char separator)
{
static const int delta[][5] = {
// , " \n ? eof
@@ -101,7 +101,7 @@ bool CSV::readRow(QTextStream &in, QStringList *row)
else
{
in >> ch;
if (ch == ',') {
if (ch == separator) {
t = 0;
} else if (ch == '\"') {
t = 1;
@@ -137,13 +137,13 @@ bool CSV::readRow(QTextStream &in, QStringList *row)
// Read header row from CSV file and return a hash mapping names to column numbers
// Returns error if header row can't be read, or if all of requiredColumns aren't found
QHash<QString, int> CSV::readHeader(QTextStream &in, QStringList requiredColumns, QString &error)
QHash<QString, int> CSV::readHeader(QTextStream &in, QStringList requiredColumns, QString &error, char separator)
{
QHash<QString, int> colNumbers;
QStringList row;
// Read column names
if (CSV::readRow(in, &row))
if (CSV::readRow(in, &row, separator))
{
// Create hash mapping column names to indices
for (int i = 0; i < row.size(); i++) {
+2 -2
View File
@@ -46,8 +46,8 @@ struct SDRBASE_API CSV {
static QHash<QString, QString> *hash(const QString& filename, int reserve=0);
static bool readRow(QTextStream &in, QStringList *row);
static QHash<QString, int> readHeader(QTextStream &in, QStringList requiredColumns, QString &error);
static bool readRow(QTextStream &in, QStringList *row, char seperator=',');
static QHash<QString, int> readHeader(QTextStream &in, QStringList requiredColumns, QString &error, char seperator=',');
};
+99
View File
@@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////////////
// //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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_UTIL_MOVINGMAXIMUM_H
#define INCLUDE_UTIL_MOVINGMAXIMUM_H
#include <algorithm>
#include <QDebug>
// Calculates moving maximum over a number of samples
template <typename T>
class MovingMaximum
{
public:
MovingMaximum() :
m_samples(nullptr),
m_size(0)
{
reset();
}
~MovingMaximum()
{
delete[] m_samples;
}
void reset()
{
m_count = 0;
m_index = 0;
m_max = NAN;
}
void setSize(int size)
{
delete[] m_samples;
m_samples = new T[size]();
m_size = size;
reset();
}
void operator()(T sample)
{
if (m_count < m_size)
{
m_samples[m_count++] = sample;
if (m_count == 1) {
m_max = sample;
} else {
m_max = std::max(m_max, sample);
}
}
else
{
T oldest = m_samples[m_index];
m_samples[m_index] = sample;
m_index = (m_index + 1) % m_size;
m_max = std::max(m_max, sample);
if (oldest >= m_max)
{
// Find new maximum, that will be lower than the oldest sample
m_max = m_samples[0];
for (unsigned int i = 1; i < m_size; i++) {
m_max = std::max(m_max, m_samples[i]);
}
}
}
}
T getMaximum() const {
return m_max;
}
private:
T *m_samples;
unsigned int m_size; // Max number of samples
unsigned int m_count; // Number of samples used
unsigned int m_index; // Current index
T m_max;
};
#endif /* INCLUDE_UTIL_MOVINGMAXIMUM_H */
+728
View File
@@ -0,0 +1,728 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 <QRegularExpression>
#include "navtex.h"
// From https://en.wikipedia.org/wiki/List_of_Navtex_stations
const QList<NavtexTransmitter> NavtexTransmitter::m_navtexTransmitters = {
{1, "Svalbard", 78.056944, 13.609722, {Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{1, "Bodo", 67.266667, 14.383333, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{1, "Vardo", 70.370889, 31.097389, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{1, "Torshavn", 62.014944, -6.800056, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)})}},
{1, "Niton", 50.586297, -1.254756, {NavtexTransmitter::Schedule('E', 518000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(12, 40), QTime(16, 40), QTime(20, 40)}),
NavtexTransmitter::Schedule('K', 518000, {QTime(1, 40), QTime(5, 40), QTime(6, 40), QTime(13, 40), QTime(17, 40), QTime(21, 40)}),
NavtexTransmitter::Schedule('I', 490000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}}, // Have seen this broadcast at 9:10
{1, "Talinn", 59.4644, 24.357294, {NavtexTransmitter::Schedule('F', 518000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)})}},
{1, "Cullercoats", 55.0732, -1.463233, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)}),
NavtexTransmitter::Schedule('U', 490000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)})}},
{1, "Bjuroklubb", 64.461639, 21.591833, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}},
{1, "Grimeton", 57.103056, 12.385556, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}},
{1, "Gislovshammer", 55.488917, 14.314222, {NavtexTransmitter::Schedule('J', 518000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(17, 30), QTime(21, 30)})}},
{1, "Rogaland", 58.658817, 5.603778, {NavtexTransmitter::Schedule('L', 518000, {QTime(1, 50), QTime(5, 50), QTime(9, 50), QTime(13, 50), QTime(17, 50), QTime(21, 50)})}},
{1, "Jeloy", 59.435833, 10.589444, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{1, "Orlandet", 63.661194, 9.5455, {NavtexTransmitter::Schedule('N', 518000, {QTime(2, 10), QTime(6, 10), QTime(10, 10), QTime(14, 10), QTime(18, 10), QTime(22, 10)})}},
{1, "Portpatrick", 54.844044, -5.124478, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)}),
NavtexTransmitter::Schedule('C', 490000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(16, 20), QTime(20, 20)})}},
{1, "Netherlands Coastguard", 52.095128, 4.257975, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{1, "Malin Head", 55.363278, -7.33925, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)})}},
{1, "Saudanes", 66.18625, -18.951867, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)}),
NavtexTransmitter::Schedule('E', 490000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(16, 40), QTime(20, 40)})}},
{1, "Hamburg", 53.673333, 9.808611, {NavtexTransmitter::Schedule('S', 518000, {QTime(3, 0), QTime(7, 0), QTime(12, 0), QTime(15, 0), QTime(19, 0), QTime(23, 0)}),
NavtexTransmitter::Schedule('L', 490000, {QTime(1, 50), QTime(5, 50), QTime(9, 50), QTime(17, 50), QTime(21, 50)})}}, // Transmitter is at Pinneberg (used on wiki), but messages give location as Hamburg
{1, "Oostende", 51.182278, 2.806539, {NavtexTransmitter::Schedule('T', 518000, {QTime(3, 10), QTime(7, 10), QTime(11, 10), QTime(15, 10), QTime(19, 10), QTime(23, 10)}),
NavtexTransmitter::Schedule('V', 518000, {QTime(3, 30), QTime(7, 30), QTime(11, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)}),
NavtexTransmitter::Schedule('B', 490000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(16, 10), QTime(20, 10)})}},
{1, "Valentia", 51.929756, -10.349028, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)})}},
{1, "Grindavik", 63.833208, -22.450786, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)}),
NavtexTransmitter::Schedule('K', 490000, {QTime(1, 40), QTime(5, 40), QTime(9, 40), QTime(17, 40), QTime(21, 40)})}},
{2, "Cross Corsen", 48.476031, -5.053697, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)}),
NavtexTransmitter::Schedule('E', 490000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(12, 40), QTime(16, 40), QTime(20, 40)})}},
{2, "Coruna", 43.367028, -8.451861, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)}),
NavtexTransmitter::Schedule('W', 490000, {QTime(3, 40), QTime(7, 40), QTime(11, 40), QTime(15, 40), QTime(17, 40), QTime(23, 40)})}},
{2, "Horta", 38.529872, -28.628922, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)}),
NavtexTransmitter::Schedule('J', 490000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(15, 30), QTime(21, 30)})}},
{2, "Tarifa", 36.042, -5.556606, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)})}},
{2, "Las Palmas", 27.758522, -15.605361, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)}),
NavtexTransmitter::Schedule('A', 490000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{2, "Casablanca", 33.6, -7.633333, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{2, "Porto Santo", 33.066278, -16.355417, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{2, "Monsanto", 38.731611, -9.190611, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)}),
NavtexTransmitter::Schedule('G', 490000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(15, 0), QTime(21, 0)})}},
{2, "Ribeira de Vinha", 16.853228, -25.003197, {NavtexTransmitter::Schedule('U', 518000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)})}},
{2, "Cabo La Nao", 38.723258, 0.161367, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 520), QTime(11, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)}),
NavtexTransmitter::Schedule('M', 490000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(16, 0), QTime(22, 0)})}},
{2, "Sao Vicente", 16.853228, -25.003197, {NavtexTransmitter::Schedule('P', 490000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(16, 30), QTime(22, 30)})}},
// {2, "Niton", 50.586297, -1.254756, {NavtexTransmitter::Schedule('T', 490000, {QTime(3, 10), QTime(7, 10), QTime(11, 10), QTime(15, 10), QTime(17, 10), QTime(23, 10)})}},
{2, "Tarifa", 36.042, -5.556606, {NavtexTransmitter::Schedule('T', 490000, {QTime(3, 10), QTime(7, 10), QTime(11, 10), QTime(15, 10), QTime(17, 10), QTime(23, 10)})}},
{3, "Novorossijsk", 44.599111, 37.951442, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{3, "Algier", 36.733333, 3.18, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{3, "Odessa", 46.377611, 30.748222, {NavtexTransmitter::Schedule('C', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{3, "Istanbul", 41.066667, 28.95, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)}),
NavtexTransmitter::Schedule('B', 490000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(16, 10), QTime(20, 10)}),
NavtexTransmitter::Schedule('M', 4209500, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(16, 0), QTime(22, 0)})}},
{3, "Samsun", 41.386667, 36.188333, {NavtexTransmitter::Schedule('E', 518000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(12, 40), QTime(16, 40), QTime(20, 40)}),
NavtexTransmitter::Schedule('A', 490000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{3, "Antalya", 36.1525, 32.44, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)}),
NavtexTransmitter::Schedule('D', 490000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(16, 30), QTime(20, 30)})}},
{3, "Iraklio", 35.322861, 25.748986, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)}),
NavtexTransmitter::Schedule('Q', 490000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(16, 40), QTime(22, 40)}),
NavtexTransmitter::Schedule('S', 4209500, {QTime(3, 0), QTime(7, 0), QTime(11, 0), QTime(15, 0), QTime(19, 0), QTime(3, 20)})}},
{3, "Izmir", 38.275833, 26.2675, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)}),
NavtexTransmitter::Schedule('C', 490000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(16, 20), QTime(20, 20)})}},
{3, "Varna", 43.068056, 27.786111, {NavtexTransmitter::Schedule('J', 518000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(17, 30), QTime(21, 30)})}},
{3, "Kerkyra", 39.607222, 19.890833, {NavtexTransmitter::Schedule('K', 518000, {QTime(1, 40), QTime(5, 40), QTime(9, 40), QTime(13, 40), QTime(17, 40), QTime(21, 40)}),
NavtexTransmitter::Schedule('P', 490000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(16, 30), QTime(22, 30)})}},
{3, "Limnos", 39.906389, 25.181389, {NavtexTransmitter::Schedule('L', 518000, {QTime(1, 50), QTime(5, 50), QTime(9, 50), QTime(13, 50), QTime(17, 50), QTime(21, 50)}),
NavtexTransmitter::Schedule('R', 490000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(16, 50), QTime(22, 50)})}},
{3, "Cyprus", 35.048278, 33.283628, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{3, "Alexandria", 31.198089, 29.864494, {NavtexTransmitter::Schedule('N', 518000, {QTime(2, 10), QTime(6, 10), QTime(10, 10), QTime(14, 10), QTime(18, 10), QTime(22, 10)})}},
{3, "Malta", 35.815211, 14.526911, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{3, "Haifa", 32.827806, 34.969306, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{3, "Split", 43.181861, 16.422333, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)})}},
{3, "La Maddalena", 41.222778, 9.398889, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)}),
NavtexTransmitter::Schedule('I', 490000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}},
{3, "Kelibia", 36.801819, 11.037372, {NavtexTransmitter::Schedule('T', 518000, {QTime(3, 10), QTime(7, 10), QTime(12, 10), QTime(15, 10), QTime(19, 10), QTime(23, 10)})}},
{3, "Mondolfo", 43.747778, 13.141667, {NavtexTransmitter::Schedule('U', 518000, {QTime(3, 20), QTime(7, 20), QTime(12, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)}),
NavtexTransmitter::Schedule('E', 490000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(12, 40), QTime(16, 40), QTime(20, 40)})}},
{3, "Sellia Marina", 38.873056, 16.719722, {NavtexTransmitter::Schedule('V', 518000, {QTime(3, 30), QTime(7, 30), QTime(12, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)}),
NavtexTransmitter::Schedule('W', 490000, {QTime(3, 40), QTime(7, 40), QTime(11, 40), QTime(15, 40), QTime(17, 40), QTime(23, 40)})}},
{3, "Cross La Garde", 43.104306, 5.991389, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)}),
NavtexTransmitter::Schedule('S', 490000, {QTime(3, 0), QTime(7, 0), QTime(11, 0), QTime(15, 0), QTime(19, 0), QTime(3, 20)})}},
{3, "Cabo de la Nao", 38.723258, 0.161367, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)}),
NavtexTransmitter::Schedule('M', 490000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(16, 0), QTime(22, 0)})}},
{4, "Miami", 25.626225, -80.383411, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{4, "Bermuda Harbour", 32.380389, -64.682778, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{4, "Riviere-au-Renard", 50.195, -66.109889, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)}),
NavtexTransmitter::Schedule('D', 490000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(16, 30), QTime(20, 30)})}},
{4, "Boston", 41.709833, -70.498353, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 20), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)})}},
{4, "New Orleans", 29.884625, -89.945611, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)}),
NavtexTransmitter::Schedule('G', 4209500, {QTime(3, 0), QTime(7, 0), QTime(11, 0), QTime(15, 0), QTime(19, 0), QTime(23, 0)})}},
{4, "Wiarton", 44.937111, -81.233467, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}},
{4, "Curacao", 12.173197, -68.864919, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}}, // Duplicate Id
{4, "Portsmouth", 36.726342, -76.007894, {NavtexTransmitter::Schedule('N', 518000, {QTime(2, 10), QTime(6, 10), QTime(10, 10), QTime(14, 10), QTime(18, 10), QTime(22, 10)})}},
{4, "St. John's", 47.611111, -52.666944, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{4, "Thunder Bay", 48.563514, -88.656311, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{4, "Sydney", 46.185556, -59.893611, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)}),
NavtexTransmitter::Schedule('J', 490000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(15, 30), QTime(21, 30)})}},
{4, "Isabela", 18.466683, -67.071819, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)})}},
{4, "Iqaluit", 63.731389, -68.543167, {NavtexTransmitter::Schedule('T', 518000, {QTime(3, 10), QTime(7, 10), QTime(11, 10), QTime(15, 10), QTime(19, 10), QTime(23, 10)}),
NavtexTransmitter::Schedule('S', 490000, {QTime(3, 0), QTime(7, 0), QTime(11, 0), QTime(15, 0), QTime(19, 0), QTime(3, 20)})}},
{4, "Saint John", 43.744256, -66.121786, {NavtexTransmitter::Schedule('U', 518000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)}),
NavtexTransmitter::Schedule('V', 490000, {QTime(3, 30), QTime(7, 30), QTime(11, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)})}},
{4, "Kook Island", 64.067017, -52.012611, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)})}},
{4, "Labrador", 53.708611, -57.021667, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)})}},
{6, "La Paloma", -34.666667, -54.15, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)})}},
{6, "Ushuaia", -54.8, -68.3, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{6, "Rio Gallegos", -51.616667, -69.216667, {NavtexTransmitter::Schedule('N', 518000, {QTime(2, 10), QTime(6, 10), QTime(10, 10), QTime(14, 10), QTime(18, 10), QTime(22, 10)})}},
{6, "Comodoro Rivadavia", -45.85, -67.416667, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{6, "Bahía Blanca", -38.716667, -62.1, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{6, "Mar del Plata", -38.05, -57.533333, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)})}},
{6, "Buenos Aires", -34.6, -58.366667, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)})}},
{7, "Walvis Bay", -23.05665, 14.624333, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{7, "Cape Town", -33.685128, 18.712961, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{7, "Port Elizabeth", -34.036722, 25.555833, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}},
{7, "Durban", -29.804833, 30.815633, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{8, "Mauritius", -20.167089, 57.478161, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{8, "Bombay", 19.083239, 72.834033, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)})}},
{8, "Madras", 13.082778, 80.287222, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{9, "Bushehr", 28.962225, 50.822794, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{9, "Hamala", 26.157167, 50.47665, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{9, "Bandar Abbas", 27.161022, 56.225378, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)})}},
{9, "Jeddah", 21.342222, 39.155833, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}},
{9, "Muscat", 23.6, 58.5, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{9, "Karachi", 24.851944, 67.0425, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{9, "Quseir", 26.110889, 34.280083, {NavtexTransmitter::Schedule('V', 518000, {QTime(3, 30), QTime(7, 30), QTime(11, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)})}},
{9, "Serapeum ", 30.470311, 32.36675, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)})}},
{11, "Jayapura", -2.516667, 140.716667, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{11, "Ambon", -3.7, 128.2, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{11, "Singapore", 1.333333, 103.7, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{11, "Makassar", -5.1, 119.433333, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)})}},
{11, "Jakarta", -6.116667, 106.866667, {NavtexTransmitter::Schedule('E', 518000, {QTime(0, 40), QTime(4, 40), QTime(8, 40), QTime(12, 40), QTime(16, 40), QTime(20, 40)})}},
{11, "Bangkok", 13.024444, 100.019733, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)})}},
{11, "Naha", 26.15, 127.766667, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)})}},
{11, "Moji", 33.95, 130.966667, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}},
{11, "Puerto Princesa", 9.733333, 118.716667, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}},
{11, "Yokohama", 35.433333, 139.633333, {NavtexTransmitter::Schedule('I', 518000, {QTime(1, 20), QTime(5, 20), QTime(9, 20), QTime(13, 20), QTime(17, 20), QTime(21, 20)})}}, // Duplicate Id
{11, "Manila", 14.583333, 121.05, {NavtexTransmitter::Schedule('J', 518000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(17, 30), QTime(21, 30)})}},
{11, "Otaru", 43.2, 141, {NavtexTransmitter::Schedule('J', 518000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(17, 30), QTime(21, 30)})}}, // Duplicate Id
{11, "Davao City", 7.066667, 125.6, {NavtexTransmitter::Schedule('K', 518000, {QTime(1, 40), QTime(5, 40), QTime(9, 40), QTime(13, 40), QTime(17, 40), QTime(21, 40)})}},
{11, "Kushiro", 42.983333, 144.383333, {NavtexTransmitter::Schedule('K', 518000, {QTime(1, 40), QTime(5, 40), QTime(9, 40), QTime(13, 40), QTime(17, 40), QTime(21, 40)})}}, // Duplicate Id
{11, "Hongkong", 22.209167, 114.256111, {NavtexTransmitter::Schedule('L', 518000, {QTime(1, 50), QTime(5, 50), QTime(9, 50), QTime(13, 50), QTime(17, 50), QTime(21, 50)})}},
{11, "Sanya", 18.232222, 109.495833, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{11, "Guangzhou", 23.15, 113.483333, {NavtexTransmitter::Schedule('N', 518000, {QTime(2, 10), QTime(6, 10), QTime(10, 10), QTime(14, 10), QTime(18, 10), QTime(22, 10)})}},
{11, "Fuzhou", 26.028544, 119.305444, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{11, "Da Nang", 16.083333, 108.233333, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}},
{11, "Chilung", 25.15, 121.733333, {NavtexTransmitter::Schedule('P', 518000, {QTime(2, 30), QTime(6, 30), QTime(10, 30), QTime(14, 30), QTime(18, 30), QTime(22, 30)})}}, // Duplicate Id
{11, "Shanghai", 31.108889, 121.544167, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)})}},
{11, "Dalian", 38.845244, 121.518056, {NavtexTransmitter::Schedule('R', 518000, {QTime(2, 50), QTime(6, 50), QTime(10, 50), QTime(14, 50), QTime(18, 50), QTime(22, 50)})}},
{11, "Sandakan", 5.895886, 118.00305, {NavtexTransmitter::Schedule('S', 518000, {QTime(3, 0), QTime(7, 0), QTime(12, 0), QTime(15, 0), QTime(19, 0), QTime(23, 0)})}},
{11, "Miri", 4.438, 114.020889, {NavtexTransmitter::Schedule('T', 518000, {QTime(3, 10), QTime(7, 10), QTime(11, 10), QTime(15, 10), QTime(19, 10), QTime(23, 10)})}},
{11, "Penang", 5.425, 100.403056, {NavtexTransmitter::Schedule('U', 518000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)})}},
{11, "Guam", 13.47445, 144.844389, {NavtexTransmitter::Schedule('V', 518000, {QTime(3, 30), QTime(7, 30), QTime(11, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)})}},
{11, "Jukbyeon", 37.05, 129.416667, {NavtexTransmitter::Schedule('V', 518000, {QTime(3, 30), QTime(7, 30), QTime(11, 30), QTime(15, 30), QTime(19, 30), QTime(23, 30)}), // Duplicate Id
NavtexTransmitter::Schedule('J', 490000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(15, 30), QTime(21, 30)})}},
{11, "Byeonsan", 35.6, 126.483333, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)}),
NavtexTransmitter::Schedule('K', 490000, {QTime(1, 40), QTime(5, 40), QTime(9, 40), QTime(17, 40), QTime(21, 40)})}},
{11, "Ho-Chi-Minh City", 10.703317, 106.729139, {NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)})}},
{12, "San Francisco", 37.925739, -122.734056, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{12, "Prince Rupert", 54.298519, -130.417669, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)})}},
{12, "Tofino", 48.925478, -125.540306, {NavtexTransmitter::Schedule('H', 518000, {QTime(1, 10), QTime(5, 10), QTime(9, 10), QTime(13, 10), QTime(17, 10), QTime(21, 10)})}},
{12, "Kodiak", 57.781606, -152.537583, {NavtexTransmitter::Schedule('J', 518000, {QTime(1, 30), QTime(5, 30), QTime(9, 30), QTime(13, 30), QTime(17, 30), QTime(21, 30)}),
NavtexTransmitter::Schedule('X', 518000, {QTime(3, 50), QTime(7, 50), QTime(12, 50), QTime(15, 50), QTime(19, 50), QTime(23, 50)})}},
{12, "Ayora", -0.75, -90.316667, {NavtexTransmitter::Schedule('L', 518000, {QTime(1, 50), QTime(5, 50), QTime(9, 50), QTime(13, 50), QTime(17, 50), QTime(21, 50)}),
NavtexTransmitter::Schedule('A', 490000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{12, "Guayaquil", -2.283333, -80.016667, {NavtexTransmitter::Schedule('M', 518000, {QTime(2, 0), QTime(6, 0), QTime(10, 0), QTime(14, 0), QTime(18, 0), QTime(22, 0)})}},
{12, "Honolulu", 21.437019, -158.143239, {NavtexTransmitter::Schedule('O', 518000, {QTime(2, 20), QTime(6, 20), QTime(10, 20), QTime(14, 20), QTime(18, 20), QTime(22, 20)})}},
{12, "Cambria", 35.524297, -121.061922, {NavtexTransmitter::Schedule('Q', 518000, {QTime(2, 40), QTime(6, 40), QTime(10, 40), QTime(14, 40), QTime(18, 40), QTime(22, 40)})}},
{12, "Astoria", 46.203989, -123.955639, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)})}},
{13, "Vladivostok", 43.381472, 131.899861, {NavtexTransmitter::Schedule('A', 518000, {QTime(0, 0), QTime(4, 0), QTime(8, 0), QTime(12, 0), QTime(16, 0), QTime(20, 0)})}},
{13, "Kholmsk", 47.023556, 142.045056, {NavtexTransmitter::Schedule('B', 518000, {QTime(0, 10), QTime(4, 10), QTime(8, 10), QTime(12, 10), QTime(16, 10), QTime(20, 10)})}},
{13, "Murmansk", 68.865803, 33.070761, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}},
{13, "Petropavlosk", 53.247778, 158.419472, {NavtexTransmitter::Schedule('C', 518000, {QTime(0, 20), QTime(4, 20), QTime(8, 20), QTime(12, 20), QTime(16, 20), QTime(20, 20)})}}, // Duplicate
{13, "Magadan", 59.683333, 150.15, {NavtexTransmitter::Schedule('D', 518000, {QTime(0, 30), QTime(4, 30), QTime(8, 30), QTime(12, 30), QTime(16, 30), QTime(20, 30)})}},
{13, "Archangelsk", 64.556278, 40.550028, {NavtexTransmitter::Schedule('F', 518000, {QTime(0, 50), QTime(4, 50), QTime(8, 50), QTime(12, 50), QTime(16, 50), QTime(20, 50)})}},
{13, "Okhotsk", 59.366667, 143.2, {NavtexTransmitter::Schedule('G', 518000, {QTime(1, 0), QTime(5, 0), QTime(9, 0), QTime(13, 0), QTime(17, 0), QTime(21, 0)})}},
{13, "Astrakhan", 46.296694, 47.997778, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)})}},
{15, "Antofagasta", -23.491333, -70.424778, {NavtexTransmitter::Schedule('A', 518000, {QTime(4, 0), QTime(12, 0), QTime(20, 0)})}},
{15, "Valparaíso", -32.802222, -71.485, {NavtexTransmitter::Schedule('B', 518000, {QTime(4, 10), QTime(12, 10), QTime(20, 10)})}},
{15, "Talcahuano", -36.715056, -73.108, {NavtexTransmitter::Schedule('C', 518000, {QTime(4, 20), QTime(12, 20), QTime(20, 20)})}},
{15, "Puerto Montt", -41.489983, -72.957744, {NavtexTransmitter::Schedule('D', 518000, {QTime(4, 30), QTime(12, 30), QTime(20, 30)})}},
{15, "Punta Arenas", -52.948111, -71.056944, {NavtexTransmitter::Schedule('E', 518000, {QTime(4, 40), QTime(12, 40), QTime(20, 40)})}},
{15, "Easter Island", -27.15, -109.416667, {NavtexTransmitter::Schedule('F', 518000, {QTime(4, 50), QTime(12, 50), QTime(20, 50)})}},
{16, "Paita", -5.083333, -81.116667, {NavtexTransmitter::Schedule('S', 518000, {QTime(3, 0), QTime(7, 0), QTime(12, 0), QTime(15, 0), QTime(19, 0), QTime(23, 0)})}},
{16, "Callao", -12.5, -77.15, {NavtexTransmitter::Schedule('U', 518000, {QTime(3, 20), QTime(7, 20), QTime(11, 20), QTime(15, 20), QTime(19, 20), QTime(23, 20)})}},
{16, "Mollendo", -17.016667, -72.016667, {NavtexTransmitter::Schedule('W', 518000, {QTime(3, 40), QTime(7, 40), QTime(12, 40), QTime(15, 40), QTime(19, 40), QTime(23, 40)})}},
};
const QMap<QString,QString> NavtexMessage::m_types = {
{"A", "Navigational warning"},
{"B", "Meteorological warning"},
{"C", "Ice reports"},
{"D", "Search and rescue"},
{"E", "Meteorological forecasts"},
{"F", "Pilot service messages"},
{"G", "AIS"},
{"H", "LORAN"},
{"J", "SATNAV"},
{"K", "Navaid messages"},
{"L", "Navigational warning"},
{"T", "Test transmissions"},
{"X", "Special services"},
{"Y", "Special services"},
{"Z", "No message"}
};
const NavtexTransmitter* NavtexTransmitter::getTransmitter(QTime time, int area, qint64 frequency)
{
for (const auto& transmitter : NavtexTransmitter::m_navtexTransmitters)
{
if (transmitter.m_area == area)
{
for (const auto& schedule : transmitter.m_schedules)
{
if (schedule.m_frequency == frequency)
{
for (const auto& txStartTime : schedule.m_times)
{
// Transmitters have 10 minute windows for transmission
int secs = txStartTime.secsTo(time);
if ((secs >= 0) && (secs < 10*60)) {
return &transmitter;
}
}
}
}
}
}
return nullptr;
}
NavtexMessage::NavtexMessage(QDateTime dateTime, const QString& stationId, const QString& typeId, const QString& id, const QString& message) :
m_stationId(stationId),
m_typeId(typeId),
m_id(id),
m_message(message),
m_dateTime(dateTime),
m_valid(true)
{
}
NavtexMessage::NavtexMessage(const QString& text)
{
m_dateTime = QDateTime::currentDateTime();
QRegularExpression re("[Z*][C*][Z*][C*][ *]([A-Z])([A-Z])(\\d\\d)((.|\n|\r)*)[N*][N*][N*][N*]");
QRegularExpressionMatch match = re.match(text);
if (match.hasMatch())
{
m_stationId = match.captured(1);
m_typeId = match.captured(2);
m_id = match.captured(3);
m_message = match.captured(4).trimmed();
m_valid = true;
}
else
{
m_message = text;
m_valid = false;
}
}
QString NavtexMessage::getStation(int area, qint64 frequency) const
{
for (const auto& transmitter : NavtexTransmitter::m_navtexTransmitters)
{
if (transmitter.m_area == area)
{
for (const auto& schedule : transmitter.m_schedules)
{
if ((schedule.m_id == m_stationId) && (schedule.m_frequency == frequency)) {
return transmitter.m_station;
}
}
}
}
return "";
}
QString NavtexMessage::getType() const
{
if (m_valid && m_types.contains(m_typeId)) {
return m_types.value(m_typeId);
}
return "";
}
void SitorBDecoder::init()
{
m_state = PHASING;
m_idx = 0;
m_figureSet = false;
m_errors = 0;
}
// In:
// Received 7-bit CCIR476 sequence
// Returns:
// Decoded ASCII character
// ETX end of text
// '*' both chars invalid
// -1 no character available yet
char SitorBDecoder::decode(char c)
{
char ret = -1;
//qDebug() << "In: " << printable(ccir476Decode(c));
switch (m_state)
{
case PHASING:
// Wait until we get a valid non-phasing character
if ((c != PHASING_1) && (c != PHASING_2) && (ccir476Decode(c) != -1))
{
m_buf[m_idx++] = c;
m_state = FILL_RX;
}
break;
case FILL_DX:
// Fill up buffer
m_buf[m_idx++] = c;
if (m_idx == BUFFER_SIZE)
{
m_state = RX;
m_idx = 0;
}
else
{
m_state = FILL_RX;
}
break;
case FILL_RX:
// Should be phasing 1
if (c != PHASING_1) {
m_errors++;
}
m_state = FILL_DX;
break;
case RX:
{
// Try to decode a character
char dx = ccir476Decode(m_buf[m_idx]);
char rx = ccir476Decode(c);
char a;
// Idle alpha (phasing 1) in both dx and rx means end of signal
if ((dx == '<') && (rx == '<'))
{
a = 0x2; // ETX - End of text
}
else if (dx != -1)
{
a = dx; // First received character has no detectable error
if ((dx != rx) && !((dx == '<') && (rx == '>')) && !((dx == '>') && (rx == '<'))) {
m_errors++;
}
}
else if (rx != -1)
{
a = rx; // Second received character has no detectable error
m_errors++;
}
else
{
a = '*'; // Both received characters have errors
m_errors += 2;
}
if (a == 0xf) {
m_figureSet = false;
} else if (a == 0xe) {
m_figureSet = true;
} else {
ret = a;
}
m_state = DX;
}
break;
case DX:
// Save received character in buffer
m_buf[m_idx] = c;
m_idx = (m_idx + 1) % BUFFER_SIZE;
m_state = RX;
break;
}
return ret;
}
QString SitorBDecoder::printable(char c)
{
if (c == -1) {
return "Unknown";
} else if (c == 0x2) {
return "End of transmission";
} else if (c == 0xf) {
return "Letter";
} else if (c == 0xe) {
return "Figure";
} else if (c == 0x5) {
return "Cross";
} else if (c == 0x7) {
return "Bell";
} else {
return QString("%1").arg(c);
}
}
// https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.476-5-199510-I!!PDF-E.pdf - Table 1
// https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.625-4-201203-I!!PDF-E.pdf
const char SitorBDecoder::m_ccir476LetterSetDecode[128] = {
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
0x0d,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'T',
-1,
-1,
-1,
0x0a,
-1,
' ',
'V',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'B',
-1,
-1,
-1,
-1,
-1,
0x0f,
'X',
-1,
-1,
-1,
-1,
'>',
-1,
'E',
0x0e,
-1,
-1,
'U',
'Q',
-1,
'K',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'O',
-1,
-1,
-1,
'H',
-1,
'N',
'M',
-1,
-1,
-1,
-1,
'L',
-1,
'R',
'G',
-1,
-1,
'I',
'P',
-1,
'C',
-1,
-1,
-1,
-1,
-1,
-1,
'Z',
-1,
'D',
-1,
-1,
-1,
'S',
'Y',
-1,
'F',
-1,
-1,
-1,
-1,
'A',
'W',
-1,
'J',
-1,
-1,
-1,
'<',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
};
const char SitorBDecoder::m_ccir476FigureSetDecode[128] = {
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
0x0d,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'5',
-1,
-1,
-1,
0x0a,
-1,
' ',
'=',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'?',
-1,
-1,
-1,
-1,
-1,
0x0f,
'/',
-1,
-1,
-1,
-1,
'>',
-1,
'3',
0x0e,
-1,
-1,
'7',
'1',
-1,
'(',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
'9',
-1,
-1,
-1,
-93,
-1,
',',
'.',
-1,
-1,
-1,
-1,
')',
-1,
'4',
'&',
-1,
-1,
'8',
'0',
-1,
':',
-1,
-1,
-1,
-1,
-1,
-1,
'+',
-1,
0x05,
-1,
-1,
-1,
'\'',
'6',
-1,
'!',
-1,
-1,
-1,
-1,
'-',
'2',
-1,
0x07,
-1,
-1,
-1,
'<',
-1,
-1,
-1,
-1,
-1,
-1,
-1,
};
char SitorBDecoder::ccir476Decode(char c)
{
if (m_figureSet) {
return m_ccir476FigureSetDecode[(int)c];
} else {
return m_ccir476LetterSetDecode[(int)c];
}
}
+111
View File
@@ -0,0 +1,111 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// 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_UTIL_NAVTEX_H
#define INCLUDE_UTIL_NAVTEX_H
#include <QString>
#include <QDateTime>
#include <QMap>
#include "export.h"
class SDRBASE_API NavtexTransmitter {
public:
struct Schedule {
char m_id;
qint64 m_frequency;
QList<QTime> m_times;
Schedule(char id, qint64 frequency) :
m_id(id),
m_frequency(frequency)
{
}
Schedule(char id, qint64 frequency, QList<QTime> times) :
m_id(id),
m_frequency(frequency),
m_times(times)
{
}
};
int m_area;
QString m_station;
float m_latitude;
float m_longitude;
QList<Schedule> m_schedules;
static const QList<NavtexTransmitter> m_navtexTransmitters;
static const NavtexTransmitter* getTransmitter(QTime time, int area, qint64 frequency);
};
class SDRBASE_API NavtexMessage {
public:
QString m_stationId;
QString m_typeId;
QString m_id;
QString m_message;
QDateTime m_dateTime;
bool m_valid;
static const QMap<QString,QString> m_types;
NavtexMessage(const QString& text);
NavtexMessage(QDateTime dataTime, const QString& stationId, const QString& typeId, const QString& id, const QString& message);
QString getStation(int area, qint64 frequency) const;
QString getType() const;
};
class SDRBASE_API SitorBDecoder {
public:
void init();
char decode(char c);
int getErrors() const { return m_errors; }
static QString printable(char c);
private:
static const char PHASING_1 = 0x78;
static const char PHASING_2 = 0x33;
static const int BUFFER_SIZE = 3;
char m_buf[3];
bool m_figureSet;
enum State {
PHASING,
FILL_DX,
FILL_RX,
DX,
RX
} m_state;
int m_idx;
int m_errors;
static const char m_ccir476LetterSetDecode[128];
static const char m_ccir476FigureSetDecode[128];
char ccir476Decode(char c);
};
#endif // INCLUDE_UTIL_NAVTEX_H
+19
View File
@@ -294,6 +294,25 @@ public:
longitude = -longitude;
return true;
}
QRegExp dms2(QString("([0-9]+)([NS])([0-9]{2})([0-9]{2}) *,?([0-9]+)([EW])([0-9]{2})([0-9]{2})"));
if (dms2.exactMatch(string))
{
float latD = dms2.capturedTexts()[1].toFloat();
bool north = dms2.capturedTexts()[2] == "N";
float latM = dms2.capturedTexts()[3].toFloat();
float latS = dms2.capturedTexts()[4].toFloat();
float lonD = dms2.capturedTexts()[5].toFloat();
bool east = dms2.capturedTexts()[6] == "E";
float lonM = dms2.capturedTexts()[7].toFloat();
float lonS = dms2.capturedTexts()[8].toFloat();
latitude = latD + latM/60.0 + latS/(60.0*60.0);
if (!north)
latitude = -latitude;
longitude = lonD + lonM/60.0 + lonS/(60.0*60.0);
if (!east)
longitude = -longitude;
return true;
}
return false;
}
+14
View File
@@ -4543,6 +4543,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setInterferometerSettings(new SWGSDRangel::SWGInterferometerSettings());
channelSettings->getInterferometerSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "NavtexDemodSettings")
{
channelSettings->setNavtexDemodSettings(new SWGSDRangel::SWGNavtexDemodSettings());
channelSettings->getNavtexDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "M17DemodSettings")
{
channelSettings->setM17DemodSettings(new SWGSDRangel::SWGM17DemodSettings());
@@ -4624,6 +4629,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setRemoteTcpSinkSettings(new SWGSDRangel::SWGRemoteTCPSinkSettings());
channelSettings->getRemoteTcpSinkSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "RTTYDemodSettings")
{
channelSettings->setRttyDemodSettings(new SWGSDRangel::SWGRTTYDemodSettings());
channelSettings->getRttyDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "SigMFFileSinkSettings")
{
channelSettings->setSigMfFileSinkSettings(new SWGSDRangel::SWGSigMFFileSinkSettings());
@@ -5382,6 +5392,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setDsdDemodSettings(nullptr);
channelSettings.setHeatMapSettings(nullptr);
channelSettings.setIeee802154ModSettings(nullptr);
channelSettings.setNavtexDemodSettings(nullptr);
channelSettings.setNfmDemodSettings(nullptr);
channelSettings.setNfmModSettings(nullptr);
channelSettings.setNoiseFigureSettings(nullptr);
@@ -5394,6 +5405,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setRemoteSinkSettings(nullptr);
channelSettings.setRemoteSourceSettings(nullptr);
channelSettings.setRemoteTcpSinkSettings(nullptr);
channelSettings.setRttyDemodSettings(nullptr);
channelSettings.setSsbDemodSettings(nullptr);
channelSettings.setSsbModSettings(nullptr);
channelSettings.setUdpSourceSettings(nullptr);
@@ -5418,6 +5430,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
channelReport.setDatvModReport(nullptr);
channelReport.setDsdDemodReport(nullptr);
channelReport.setHeatMapReport(nullptr);
channelReport.setNavtexDemodReport(nullptr);
channelReport.setNfmDemodReport(nullptr);
channelReport.setNfmModReport(nullptr);
channelReport.setNoiseFigureReport(nullptr);
@@ -5427,6 +5440,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
channelReport.setRadioClockReport(nullptr);
channelReport.setRadiosondeDemodReport(nullptr);
channelReport.setRemoteSourceReport(nullptr);
channelReport.setRttyDemodReport(nullptr);
channelReport.setSsbDemodReport(nullptr);
channelReport.setSsbModReport(nullptr);
channelReport.setUdpSourceReport(nullptr);
+4
View File
@@ -48,6 +48,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channeltx.freedvmod", "FreeDVModSettings"},
{"sdrangel.channel.freqtracker", "FreqTrackerSettings"},
{"sdrangel.channel.heatmap", "HeatMapSettings"},
{"sdrangel.channel.navtexemod", "NavtexDemodSettings"},
{"sdrangel.channel.m17demod", "M17DemodSettings"},
{"sdrangel.channeltx.modm17", "M17ModSettings"},
{"sdrangel.channel.nfmdemod", "NFMDemodSettings"},
@@ -66,6 +67,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.demod.remotesink", "RemoteSinkSettings"},
{"sdrangel.demod.remotetcpsink", "RemoteTCPSinkSettings"},
{"sdrangel.channeltx.remotesource", "RemoteSourceSettings"},
{"sdrangel.channel.rttydemod", "RTTYDemodSettings"},
{"sdrangel.channeltx.modssb", "SSBModSettings"},
{"sdrangel.channel.ssbdemod", "SSBDemodSettings"},
{"sdrangel.channel.ft8demod", "FT8DemodSettings"},
@@ -162,6 +164,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModSettings"},
{"M17Demod", "M17DemodSettings"},
{"M17Mod", "M17ModSettings"},
{"NavtexDemod", "NavtexDemodSettings"},
{"NFMDemod", "NFMDemodSettings"},
{"NFMMod", "NFMModSettings"},
{"NoiseFigure", "NoiseFigureSettings"},
@@ -176,6 +179,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"RemoteSink", "RemoteSinkSettings"},
{"RemoteSource", "RemoteSourceSettings"},
{"RemoteTCPSink", "RemoteTCPSinkSettings"},
{"RTTYDemodSettings", "RTTYDemodSettings"},
{"SSBMod", "SSBModSettings"},
{"SSBDemod", "SSBDemodSettings"},
{"FT8Demod", "FT8DemodSettings"},
@@ -51,12 +51,16 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerReport"
FT8DemodReport:
$ref: "http://swgserver:8081/api/swagger/include/FT8Demod.yaml#/FT8DemodReport"
RTTYDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/RTTYDemod.yaml#/RTTYDemodReport"
HeatMapReport:
$ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapReport"
M17DemodReport:
$ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodReport"
M17ModReport:
$ref: "http://swgserver:8081/api/swagger/include/M17Mod.yaml#/M17ModReport"
NavtexDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/NavtexDemod.yaml#/NavtexDemodReport"
NFMDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodReport"
NFMModReport:
@@ -65,6 +65,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerSettings"
FT8DemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/FT8Demod.yaml#/FT8DemodSettings"
RTTYDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/RTTYDemod.yaml#/RTTYDemodSettings"
HeatMapSettings:
$ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapSettings"
InterferometerSettings:
@@ -75,6 +77,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodSettings"
M17ModSettings:
$ref: "http://swgserver:8081/api/swagger/include/M17Mod.yaml#/M17ModSettings"
NavtexDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/NavtexDemod.yaml#/NavtexDemodSettings"
NFMDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodSettings"
NFMModSettings:
@@ -0,0 +1,62 @@
NavtexDemodSettings:
description: ACARSDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
rfBandwidth:
type: number
format: float
navArea:
type: integer
filterStation:
type: string
filterType:
type: string
udpEnabled:
description: "Whether to forward received messages to specified UDP port"
type: integer
udpAddress:
description: "UDP address to forward received messages to"
type: string
udpPort:
description: "UDP port to forward received messages to"
type: integer
logFilename:
type: string
logEnabled:
type: integer
rgbColor:
type: integer
title:
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
scopeConfig:
$ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope"
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
NavtexDemodReport:
description: ACARSDemod
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
channelSampleRate:
type: integer
@@ -0,0 +1,73 @@
RTTYDemodSettings:
description: ACARSDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
rfBandwidth:
type: number
format: float
baudRate:
type: number
format: float
frequencyShift:
type: integer
udpEnabled:
description: "Whether to forward received messages to specified UDP port"
type: integer
udpAddress:
description: "UDP address to forward received messages to"
type: string
udpPort:
description: "UDP port to forward received messages to"
type: integer
characterSet:
type: integer
suppressCRLF:
type: integer
unshiftOnSpace:
type: integer
msbFirst:
type: integer
spaceHigh:
type: integer
squelch:
type: integer
logFilename:
type: string
logEnabled:
type: integer
rgbColor:
type: integer
title:
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
scopeConfig:
$ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope"
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
RTTYDemodReport:
description: ACARSDemod
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
channelSampleRate:
type: integer
@@ -72,12 +72,16 @@ SWGChannelReport::SWGChannelReport() {
m_freq_tracker_report_isSet = false;
ft8_demod_report = nullptr;
m_ft8_demod_report_isSet = false;
rtty_demod_report = nullptr;
m_rtty_demod_report_isSet = false;
heat_map_report = nullptr;
m_heat_map_report_isSet = false;
m17_demod_report = nullptr;
m_m17_demod_report_isSet = false;
m17_mod_report = nullptr;
m_m17_mod_report_isSet = false;
navtex_demod_report = nullptr;
m_navtex_demod_report_isSet = false;
nfm_demod_report = nullptr;
m_nfm_demod_report_isSet = false;
nfm_mod_report = nullptr;
@@ -166,12 +170,16 @@ SWGChannelReport::init() {
m_freq_tracker_report_isSet = false;
ft8_demod_report = new SWGFT8DemodReport();
m_ft8_demod_report_isSet = false;
rtty_demod_report = new SWGRTTYDemodReport();
m_rtty_demod_report_isSet = false;
heat_map_report = new SWGHeatMapReport();
m_heat_map_report_isSet = false;
m17_demod_report = new SWGM17DemodReport();
m_m17_demod_report_isSet = false;
m17_mod_report = new SWGM17ModReport();
m_m17_mod_report_isSet = false;
navtex_demod_report = new SWGNavtexDemodReport();
m_navtex_demod_report_isSet = false;
nfm_demod_report = new SWGNFMDemodReport();
m_nfm_demod_report_isSet = false;
nfm_mod_report = new SWGNFMModReport();
@@ -276,6 +284,9 @@ SWGChannelReport::cleanup() {
if(ft8_demod_report != nullptr) {
delete ft8_demod_report;
}
if(rtty_demod_report != nullptr) {
delete rtty_demod_report;
}
if(heat_map_report != nullptr) {
delete heat_map_report;
}
@@ -285,6 +296,9 @@ SWGChannelReport::cleanup() {
if(m17_mod_report != nullptr) {
delete m17_mod_report;
}
if(navtex_demod_report != nullptr) {
delete navtex_demod_report;
}
if(nfm_demod_report != nullptr) {
delete nfm_demod_report;
}
@@ -396,12 +410,16 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&ft8_demod_report, pJson["FT8DemodReport"], "SWGFT8DemodReport", "SWGFT8DemodReport");
::SWGSDRangel::setValue(&rtty_demod_report, pJson["RTTYDemodReport"], "SWGRTTYDemodReport", "SWGRTTYDemodReport");
::SWGSDRangel::setValue(&heat_map_report, pJson["HeatMapReport"], "SWGHeatMapReport", "SWGHeatMapReport");
::SWGSDRangel::setValue(&m17_demod_report, pJson["M17DemodReport"], "SWGM17DemodReport", "SWGM17DemodReport");
::SWGSDRangel::setValue(&m17_mod_report, pJson["M17ModReport"], "SWGM17ModReport", "SWGM17ModReport");
::SWGSDRangel::setValue(&navtex_demod_report, pJson["NavtexDemodReport"], "SWGNavtexDemodReport", "SWGNavtexDemodReport");
::SWGSDRangel::setValue(&nfm_demod_report, pJson["NFMDemodReport"], "SWGNFMDemodReport", "SWGNFMDemodReport");
::SWGSDRangel::setValue(&nfm_mod_report, pJson["NFMModReport"], "SWGNFMModReport", "SWGNFMModReport");
@@ -520,6 +538,9 @@ SWGChannelReport::asJsonObject() {
if((ft8_demod_report != nullptr) && (ft8_demod_report->isSet())){
toJsonValue(QString("FT8DemodReport"), ft8_demod_report, obj, QString("SWGFT8DemodReport"));
}
if((rtty_demod_report != nullptr) && (rtty_demod_report->isSet())){
toJsonValue(QString("RTTYDemodReport"), rtty_demod_report, obj, QString("SWGRTTYDemodReport"));
}
if((heat_map_report != nullptr) && (heat_map_report->isSet())){
toJsonValue(QString("HeatMapReport"), heat_map_report, obj, QString("SWGHeatMapReport"));
}
@@ -529,6 +550,9 @@ SWGChannelReport::asJsonObject() {
if((m17_mod_report != nullptr) && (m17_mod_report->isSet())){
toJsonValue(QString("M17ModReport"), m17_mod_report, obj, QString("SWGM17ModReport"));
}
if((navtex_demod_report != nullptr) && (navtex_demod_report->isSet())){
toJsonValue(QString("NavtexDemodReport"), navtex_demod_report, obj, QString("SWGNavtexDemodReport"));
}
if((nfm_demod_report != nullptr) && (nfm_demod_report->isSet())){
toJsonValue(QString("NFMDemodReport"), nfm_demod_report, obj, QString("SWGNFMDemodReport"));
}
@@ -807,6 +831,16 @@ SWGChannelReport::setFt8DemodReport(SWGFT8DemodReport* ft8_demod_report) {
this->m_ft8_demod_report_isSet = true;
}
SWGRTTYDemodReport*
SWGChannelReport::getRttyDemodReport() {
return rtty_demod_report;
}
void
SWGChannelReport::setRttyDemodReport(SWGRTTYDemodReport* rtty_demod_report) {
this->rtty_demod_report = rtty_demod_report;
this->m_rtty_demod_report_isSet = true;
}
SWGHeatMapReport*
SWGChannelReport::getHeatMapReport() {
return heat_map_report;
@@ -837,6 +871,16 @@ SWGChannelReport::setM17ModReport(SWGM17ModReport* m17_mod_report) {
this->m_m17_mod_report_isSet = true;
}
SWGNavtexDemodReport*
SWGChannelReport::getNavtexDemodReport() {
return navtex_demod_report;
}
void
SWGChannelReport::setNavtexDemodReport(SWGNavtexDemodReport* navtex_demod_report) {
this->navtex_demod_report = navtex_demod_report;
this->m_navtex_demod_report_isSet = true;
}
SWGNFMDemodReport*
SWGChannelReport::getNfmDemodReport() {
return nfm_demod_report;
@@ -1088,6 +1132,9 @@ SWGChannelReport::isSet(){
if(ft8_demod_report && ft8_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(rtty_demod_report && rtty_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(heat_map_report && heat_map_report->isSet()){
isObjectUpdated = true; break;
}
@@ -1097,6 +1144,9 @@ SWGChannelReport::isSet(){
if(m17_mod_report && m17_mod_report->isSet()){
isObjectUpdated = true; break;
}
if(navtex_demod_report && navtex_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(nfm_demod_report && nfm_demod_report->isSet()){
isObjectUpdated = true; break;
}
@@ -47,10 +47,12 @@
#include "SWGM17ModReport.h"
#include "SWGNFMDemodReport.h"
#include "SWGNFMModReport.h"
#include "SWGNavtexDemodReport.h"
#include "SWGNoiseFigureReport.h"
#include "SWGPacketDemodReport.h"
#include "SWGPacketModReport.h"
#include "SWGPagerDemodReport.h"
#include "SWGRTTYDemodReport.h"
#include "SWGRadioAstronomyReport.h"
#include "SWGRadioClockReport.h"
#include "SWGRadiosondeDemodReport.h"
@@ -149,6 +151,9 @@ public:
SWGFT8DemodReport* getFt8DemodReport();
void setFt8DemodReport(SWGFT8DemodReport* ft8_demod_report);
SWGRTTYDemodReport* getRttyDemodReport();
void setRttyDemodReport(SWGRTTYDemodReport* rtty_demod_report);
SWGHeatMapReport* getHeatMapReport();
void setHeatMapReport(SWGHeatMapReport* heat_map_report);
@@ -158,6 +163,9 @@ public:
SWGM17ModReport* getM17ModReport();
void setM17ModReport(SWGM17ModReport* m17_mod_report);
SWGNavtexDemodReport* getNavtexDemodReport();
void setNavtexDemodReport(SWGNavtexDemodReport* navtex_demod_report);
SWGNFMDemodReport* getNfmDemodReport();
void setNfmDemodReport(SWGNFMDemodReport* nfm_demod_report);
@@ -282,6 +290,9 @@ private:
SWGFT8DemodReport* ft8_demod_report;
bool m_ft8_demod_report_isSet;
SWGRTTYDemodReport* rtty_demod_report;
bool m_rtty_demod_report_isSet;
SWGHeatMapReport* heat_map_report;
bool m_heat_map_report_isSet;
@@ -291,6 +302,9 @@ private:
SWGM17ModReport* m17_mod_report;
bool m_m17_mod_report_isSet;
SWGNavtexDemodReport* navtex_demod_report;
bool m_navtex_demod_report_isSet;
SWGNFMDemodReport* nfm_demod_report;
bool m_nfm_demod_report_isSet;
@@ -84,6 +84,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_freq_tracker_settings_isSet = false;
ft8_demod_settings = nullptr;
m_ft8_demod_settings_isSet = false;
rtty_demod_settings = nullptr;
m_rtty_demod_settings_isSet = false;
heat_map_settings = nullptr;
m_heat_map_settings_isSet = false;
interferometer_settings = nullptr;
@@ -94,6 +96,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_m17_demod_settings_isSet = false;
m17_mod_settings = nullptr;
m_m17_mod_settings_isSet = false;
navtex_demod_settings = nullptr;
m_navtex_demod_settings_isSet = false;
nfm_demod_settings = nullptr;
m_nfm_demod_settings_isSet = false;
nfm_mod_settings = nullptr;
@@ -202,6 +206,8 @@ SWGChannelSettings::init() {
m_freq_tracker_settings_isSet = false;
ft8_demod_settings = new SWGFT8DemodSettings();
m_ft8_demod_settings_isSet = false;
rtty_demod_settings = new SWGRTTYDemodSettings();
m_rtty_demod_settings_isSet = false;
heat_map_settings = new SWGHeatMapSettings();
m_heat_map_settings_isSet = false;
interferometer_settings = new SWGInterferometerSettings();
@@ -212,6 +218,8 @@ SWGChannelSettings::init() {
m_m17_demod_settings_isSet = false;
m17_mod_settings = new SWGM17ModSettings();
m_m17_mod_settings_isSet = false;
navtex_demod_settings = new SWGNavtexDemodSettings();
m_navtex_demod_settings_isSet = false;
nfm_demod_settings = new SWGNFMDemodSettings();
m_nfm_demod_settings_isSet = false;
nfm_mod_settings = new SWGNFMModSettings();
@@ -338,6 +346,9 @@ SWGChannelSettings::cleanup() {
if(ft8_demod_settings != nullptr) {
delete ft8_demod_settings;
}
if(rtty_demod_settings != nullptr) {
delete rtty_demod_settings;
}
if(heat_map_settings != nullptr) {
delete heat_map_settings;
}
@@ -353,6 +364,9 @@ SWGChannelSettings::cleanup() {
if(m17_mod_settings != nullptr) {
delete m17_mod_settings;
}
if(navtex_demod_settings != nullptr) {
delete navtex_demod_settings;
}
if(nfm_demod_settings != nullptr) {
delete nfm_demod_settings;
}
@@ -488,6 +502,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&ft8_demod_settings, pJson["FT8DemodSettings"], "SWGFT8DemodSettings", "SWGFT8DemodSettings");
::SWGSDRangel::setValue(&rtty_demod_settings, pJson["RTTYDemodSettings"], "SWGRTTYDemodSettings", "SWGRTTYDemodSettings");
::SWGSDRangel::setValue(&heat_map_settings, pJson["HeatMapSettings"], "SWGHeatMapSettings", "SWGHeatMapSettings");
::SWGSDRangel::setValue(&interferometer_settings, pJson["InterferometerSettings"], "SWGInterferometerSettings", "SWGInterferometerSettings");
@@ -498,6 +514,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&m17_mod_settings, pJson["M17ModSettings"], "SWGM17ModSettings", "SWGM17ModSettings");
::SWGSDRangel::setValue(&navtex_demod_settings, pJson["NavtexDemodSettings"], "SWGNavtexDemodSettings", "SWGNavtexDemodSettings");
::SWGSDRangel::setValue(&nfm_demod_settings, pJson["NFMDemodSettings"], "SWGNFMDemodSettings", "SWGNFMDemodSettings");
::SWGSDRangel::setValue(&nfm_mod_settings, pJson["NFMModSettings"], "SWGNFMModSettings", "SWGNFMModSettings");
@@ -642,6 +660,9 @@ SWGChannelSettings::asJsonObject() {
if((ft8_demod_settings != nullptr) && (ft8_demod_settings->isSet())){
toJsonValue(QString("FT8DemodSettings"), ft8_demod_settings, obj, QString("SWGFT8DemodSettings"));
}
if((rtty_demod_settings != nullptr) && (rtty_demod_settings->isSet())){
toJsonValue(QString("RTTYDemodSettings"), rtty_demod_settings, obj, QString("SWGRTTYDemodSettings"));
}
if((heat_map_settings != nullptr) && (heat_map_settings->isSet())){
toJsonValue(QString("HeatMapSettings"), heat_map_settings, obj, QString("SWGHeatMapSettings"));
}
@@ -657,6 +678,9 @@ SWGChannelSettings::asJsonObject() {
if((m17_mod_settings != nullptr) && (m17_mod_settings->isSet())){
toJsonValue(QString("M17ModSettings"), m17_mod_settings, obj, QString("SWGM17ModSettings"));
}
if((navtex_demod_settings != nullptr) && (navtex_demod_settings->isSet())){
toJsonValue(QString("NavtexDemodSettings"), navtex_demod_settings, obj, QString("SWGNavtexDemodSettings"));
}
if((nfm_demod_settings != nullptr) && (nfm_demod_settings->isSet())){
toJsonValue(QString("NFMDemodSettings"), nfm_demod_settings, obj, QString("SWGNFMDemodSettings"));
}
@@ -1007,6 +1031,16 @@ SWGChannelSettings::setFt8DemodSettings(SWGFT8DemodSettings* ft8_demod_settings)
this->m_ft8_demod_settings_isSet = true;
}
SWGRTTYDemodSettings*
SWGChannelSettings::getRttyDemodSettings() {
return rtty_demod_settings;
}
void
SWGChannelSettings::setRttyDemodSettings(SWGRTTYDemodSettings* rtty_demod_settings) {
this->rtty_demod_settings = rtty_demod_settings;
this->m_rtty_demod_settings_isSet = true;
}
SWGHeatMapSettings*
SWGChannelSettings::getHeatMapSettings() {
return heat_map_settings;
@@ -1057,6 +1091,16 @@ SWGChannelSettings::setM17ModSettings(SWGM17ModSettings* m17_mod_settings) {
this->m_m17_mod_settings_isSet = true;
}
SWGNavtexDemodSettings*
SWGChannelSettings::getNavtexDemodSettings() {
return navtex_demod_settings;
}
void
SWGChannelSettings::setNavtexDemodSettings(SWGNavtexDemodSettings* navtex_demod_settings) {
this->navtex_demod_settings = navtex_demod_settings;
this->m_navtex_demod_settings_isSet = true;
}
SWGNFMDemodSettings*
SWGChannelSettings::getNfmDemodSettings() {
return nfm_demod_settings;
@@ -1366,6 +1410,9 @@ SWGChannelSettings::isSet(){
if(ft8_demod_settings && ft8_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(rtty_demod_settings && rtty_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(heat_map_settings && heat_map_settings->isSet()){
isObjectUpdated = true; break;
}
@@ -1381,6 +1428,9 @@ SWGChannelSettings::isSet(){
if(m17_mod_settings && m17_mod_settings->isSet()){
isObjectUpdated = true; break;
}
if(navtex_demod_settings && navtex_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(nfm_demod_settings && nfm_demod_settings->isSet()){
isObjectUpdated = true; break;
}
@@ -55,10 +55,12 @@
#include "SWGM17ModSettings.h"
#include "SWGNFMDemodSettings.h"
#include "SWGNFMModSettings.h"
#include "SWGNavtexDemodSettings.h"
#include "SWGNoiseFigureSettings.h"
#include "SWGPacketDemodSettings.h"
#include "SWGPacketModSettings.h"
#include "SWGPagerDemodSettings.h"
#include "SWGRTTYDemodSettings.h"
#include "SWGRadioAstronomySettings.h"
#include "SWGRadioClockSettings.h"
#include "SWGRadiosondeDemodSettings.h"
@@ -177,6 +179,9 @@ public:
SWGFT8DemodSettings* getFt8DemodSettings();
void setFt8DemodSettings(SWGFT8DemodSettings* ft8_demod_settings);
SWGRTTYDemodSettings* getRttyDemodSettings();
void setRttyDemodSettings(SWGRTTYDemodSettings* rtty_demod_settings);
SWGHeatMapSettings* getHeatMapSettings();
void setHeatMapSettings(SWGHeatMapSettings* heat_map_settings);
@@ -192,6 +197,9 @@ public:
SWGM17ModSettings* getM17ModSettings();
void setM17ModSettings(SWGM17ModSettings* m17_mod_settings);
SWGNavtexDemodSettings* getNavtexDemodSettings();
void setNavtexDemodSettings(SWGNavtexDemodSettings* navtex_demod_settings);
SWGNFMDemodSettings* getNfmDemodSettings();
void setNfmDemodSettings(SWGNFMDemodSettings* nfm_demod_settings);
@@ -346,6 +354,9 @@ private:
SWGFT8DemodSettings* ft8_demod_settings;
bool m_ft8_demod_settings_isSet;
SWGRTTYDemodSettings* rtty_demod_settings;
bool m_rtty_demod_settings_isSet;
SWGHeatMapSettings* heat_map_settings;
bool m_heat_map_settings_isSet;
@@ -361,6 +372,9 @@ private:
SWGM17ModSettings* m17_mod_settings;
bool m_m17_mod_settings_isSet;
SWGNavtexDemodSettings* navtex_demod_settings;
bool m_navtex_demod_settings_isSet;
SWGNFMDemodSettings* nfm_demod_settings;
bool m_nfm_demod_settings_isSet;
@@ -205,6 +205,8 @@
#include "SWGNFMModReport.h"
#include "SWGNFMModSettings.h"
#include "SWGNamedEnum.h"
#include "SWGNavtexDemodReport.h"
#include "SWGNavtexDemodSettings.h"
#include "SWGNoiseFigureReport.h"
#include "SWGNoiseFigureSettings.h"
#include "SWGPERTesterActions.h"
@@ -238,6 +240,8 @@
#include "SWGPresets.h"
#include "SWGRDSReport.h"
#include "SWGRDSReport_altFrequencies.h"
#include "SWGRTTYDemodReport.h"
#include "SWGRTTYDemodSettings.h"
#include "SWGRadioAstronomyActions.h"
#include "SWGRadioAstronomyReport.h"
#include "SWGRadioAstronomySettings.h"
@@ -1302,6 +1306,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGNavtexDemodReport").compare(type) == 0) {
SWGNavtexDemodReport *obj = new SWGNavtexDemodReport();
obj->init();
return obj;
}
if(QString("SWGNavtexDemodSettings").compare(type) == 0) {
SWGNavtexDemodSettings *obj = new SWGNavtexDemodSettings();
obj->init();
return obj;
}
if(QString("SWGNoiseFigureReport").compare(type) == 0) {
SWGNoiseFigureReport *obj = new SWGNoiseFigureReport();
obj->init();
@@ -1467,6 +1481,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGRTTYDemodReport").compare(type) == 0) {
SWGRTTYDemodReport *obj = new SWGRTTYDemodReport();
obj->init();
return obj;
}
if(QString("SWGRTTYDemodSettings").compare(type) == 0) {
SWGRTTYDemodSettings *obj = new SWGRTTYDemodSettings();
obj->init();
return obj;
}
if(QString("SWGRadioAstronomyActions").compare(type) == 0) {
SWGRadioAstronomyActions *obj = new SWGRadioAstronomyActions();
obj->init();
@@ -0,0 +1,131 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGNavtexDemodReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGNavtexDemodReport::SWGNavtexDemodReport(QString* json) {
init();
this->fromJson(*json);
}
SWGNavtexDemodReport::SWGNavtexDemodReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGNavtexDemodReport::~SWGNavtexDemodReport() {
this->cleanup();
}
void
SWGNavtexDemodReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGNavtexDemodReport::cleanup() {
}
SWGNavtexDemodReport*
SWGNavtexDemodReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGNavtexDemodReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGNavtexDemodReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGNavtexDemodReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGNavtexDemodReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGNavtexDemodReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGNavtexDemodReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGNavtexDemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGNavtexDemodReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}
@@ -0,0 +1,64 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGNavtexDemodReport.h
*
* ACARSDemod
*/
#ifndef SWGNavtexDemodReport_H_
#define SWGNavtexDemodReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGNavtexDemodReport: public SWGObject {
public:
SWGNavtexDemodReport();
SWGNavtexDemodReport(QString* json);
virtual ~SWGNavtexDemodReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGNavtexDemodReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGNavtexDemodReport_H_ */
@@ -0,0 +1,586 @@
/**
* 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 "SWGNavtexDemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGNavtexDemodSettings::SWGNavtexDemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGNavtexDemodSettings::SWGNavtexDemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
nav_area = 0;
m_nav_area_isSet = false;
filter_station = nullptr;
m_filter_station_isSet = false;
filter_type = nullptr;
m_filter_type_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = nullptr;
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
log_filename = nullptr;
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_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;
scope_config = nullptr;
m_scope_config_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGNavtexDemodSettings::~SWGNavtexDemodSettings() {
this->cleanup();
}
void
SWGNavtexDemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
nav_area = 0;
m_nav_area_isSet = false;
filter_station = new QString("");
m_filter_station_isSet = false;
filter_type = new QString("");
m_filter_type_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = new QString("");
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
log_filename = new QString("");
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_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;
scope_config = new SWGGLScope();
m_scope_config_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGNavtexDemodSettings::cleanup() {
if(filter_station != nullptr) {
delete filter_station;
}
if(filter_type != nullptr) {
delete filter_type;
}
if(udp_address != nullptr) {
delete udp_address;
}
if(log_filename != nullptr) {
delete log_filename;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(scope_config != nullptr) {
delete scope_config;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGNavtexDemodSettings*
SWGNavtexDemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGNavtexDemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&nav_area, pJson["navArea"], "qint32", "");
::SWGSDRangel::setValue(&filter_station, pJson["filterStation"], "QString", "QString");
::SWGSDRangel::setValue(&filter_type, pJson["filterType"], "QString", "QString");
::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", "");
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString");
::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "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(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGNavtexDemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGNavtexDemodSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_rf_bandwidth_isSet){
obj->insert("rfBandwidth", QJsonValue(rf_bandwidth));
}
if(m_nav_area_isSet){
obj->insert("navArea", QJsonValue(nav_area));
}
if(filter_station != nullptr && *filter_station != QString("")){
toJsonValue(QString("filterStation"), filter_station, obj, QString("QString"));
}
if(filter_type != nullptr && *filter_type != QString("")){
toJsonValue(QString("filterType"), filter_type, obj, QString("QString"));
}
if(m_udp_enabled_isSet){
obj->insert("udpEnabled", QJsonValue(udp_enabled));
}
if(udp_address != nullptr && *udp_address != QString("")){
toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString"));
}
if(m_udp_port_isSet){
obj->insert("udpPort", QJsonValue(udp_port));
}
if(log_filename != nullptr && *log_filename != QString("")){
toJsonValue(QString("logFilename"), log_filename, obj, QString("QString"));
}
if(m_log_enabled_isSet){
obj->insert("logEnabled", QJsonValue(log_enabled));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_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((scope_config != nullptr) && (scope_config->isSet())){
toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope"));
}
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
SWGNavtexDemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGNavtexDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
float
SWGNavtexDemodSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGNavtexDemodSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
qint32
SWGNavtexDemodSettings::getNavArea() {
return nav_area;
}
void
SWGNavtexDemodSettings::setNavArea(qint32 nav_area) {
this->nav_area = nav_area;
this->m_nav_area_isSet = true;
}
QString*
SWGNavtexDemodSettings::getFilterStation() {
return filter_station;
}
void
SWGNavtexDemodSettings::setFilterStation(QString* filter_station) {
this->filter_station = filter_station;
this->m_filter_station_isSet = true;
}
QString*
SWGNavtexDemodSettings::getFilterType() {
return filter_type;
}
void
SWGNavtexDemodSettings::setFilterType(QString* filter_type) {
this->filter_type = filter_type;
this->m_filter_type_isSet = true;
}
qint32
SWGNavtexDemodSettings::getUdpEnabled() {
return udp_enabled;
}
void
SWGNavtexDemodSettings::setUdpEnabled(qint32 udp_enabled) {
this->udp_enabled = udp_enabled;
this->m_udp_enabled_isSet = true;
}
QString*
SWGNavtexDemodSettings::getUdpAddress() {
return udp_address;
}
void
SWGNavtexDemodSettings::setUdpAddress(QString* udp_address) {
this->udp_address = udp_address;
this->m_udp_address_isSet = true;
}
qint32
SWGNavtexDemodSettings::getUdpPort() {
return udp_port;
}
void
SWGNavtexDemodSettings::setUdpPort(qint32 udp_port) {
this->udp_port = udp_port;
this->m_udp_port_isSet = true;
}
QString*
SWGNavtexDemodSettings::getLogFilename() {
return log_filename;
}
void
SWGNavtexDemodSettings::setLogFilename(QString* log_filename) {
this->log_filename = log_filename;
this->m_log_filename_isSet = true;
}
qint32
SWGNavtexDemodSettings::getLogEnabled() {
return log_enabled;
}
void
SWGNavtexDemodSettings::setLogEnabled(qint32 log_enabled) {
this->log_enabled = log_enabled;
this->m_log_enabled_isSet = true;
}
qint32
SWGNavtexDemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGNavtexDemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGNavtexDemodSettings::getTitle() {
return title;
}
void
SWGNavtexDemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGNavtexDemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGNavtexDemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGNavtexDemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGNavtexDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGNavtexDemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGNavtexDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGNavtexDemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGNavtexDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGNavtexDemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGNavtexDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGNavtexDemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGNavtexDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGGLScope*
SWGNavtexDemodSettings::getScopeConfig() {
return scope_config;
}
void
SWGNavtexDemodSettings::setScopeConfig(SWGGLScope* scope_config) {
this->scope_config = scope_config;
this->m_scope_config_isSet = true;
}
SWGChannelMarker*
SWGNavtexDemodSettings::getChannelMarker() {
return channel_marker;
}
void
SWGNavtexDemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGNavtexDemodSettings::getRollupState() {
return rollup_state;
}
void
SWGNavtexDemodSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGNavtexDemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(m_nav_area_isSet){
isObjectUpdated = true; break;
}
if(filter_station && *filter_station != QString("")){
isObjectUpdated = true; break;
}
if(filter_type && *filter_type != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_enabled_isSet){
isObjectUpdated = true; break;
}
if(udp_address && *udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_port_isSet){
isObjectUpdated = true; break;
}
if(log_filename && *log_filename != QString("")){
isObjectUpdated = true; break;
}
if(m_log_enabled_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != 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(scope_config && scope_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;
}
}
@@ -0,0 +1,182 @@
/**
* 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.
*/
/*
* SWGNavtexDemodSettings.h
*
* ACARSDemod
*/
#ifndef SWGNavtexDemodSettings_H_
#define SWGNavtexDemodSettings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGGLScope.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGNavtexDemodSettings: public SWGObject {
public:
SWGNavtexDemodSettings();
SWGNavtexDemodSettings(QString* json);
virtual ~SWGNavtexDemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGNavtexDemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
qint32 getNavArea();
void setNavArea(qint32 nav_area);
QString* getFilterStation();
void setFilterStation(QString* filter_station);
QString* getFilterType();
void setFilterType(QString* filter_type);
qint32 getUdpEnabled();
void setUdpEnabled(qint32 udp_enabled);
QString* getUdpAddress();
void setUdpAddress(QString* udp_address);
qint32 getUdpPort();
void setUdpPort(qint32 udp_port);
QString* getLogFilename();
void setLogFilename(QString* log_filename);
qint32 getLogEnabled();
void setLogEnabled(qint32 log_enabled);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
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);
SWGGLScope* getScopeConfig();
void setScopeConfig(SWGGLScope* scope_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;
float rf_bandwidth;
bool m_rf_bandwidth_isSet;
qint32 nav_area;
bool m_nav_area_isSet;
QString* filter_station;
bool m_filter_station_isSet;
QString* filter_type;
bool m_filter_type_isSet;
qint32 udp_enabled;
bool m_udp_enabled_isSet;
QString* udp_address;
bool m_udp_address_isSet;
qint32 udp_port;
bool m_udp_port_isSet;
QString* log_filename;
bool m_log_filename_isSet;
qint32 log_enabled;
bool m_log_enabled_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_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;
SWGGLScope* scope_config;
bool m_scope_config_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGNavtexDemodSettings_H_ */
@@ -0,0 +1,131 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGRTTYDemodReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGRTTYDemodReport::SWGRTTYDemodReport(QString* json) {
init();
this->fromJson(*json);
}
SWGRTTYDemodReport::SWGRTTYDemodReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGRTTYDemodReport::~SWGRTTYDemodReport() {
this->cleanup();
}
void
SWGRTTYDemodReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGRTTYDemodReport::cleanup() {
}
SWGRTTYDemodReport*
SWGRTTYDemodReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGRTTYDemodReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGRTTYDemodReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGRTTYDemodReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGRTTYDemodReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGRTTYDemodReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGRTTYDemodReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGRTTYDemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGRTTYDemodReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}
@@ -0,0 +1,64 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGRTTYDemodReport.h
*
* ACARSDemod
*/
#ifndef SWGRTTYDemodReport_H_
#define SWGRTTYDemodReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGRTTYDemodReport: public SWGObject {
public:
SWGRTTYDemodReport();
SWGRTTYDemodReport(QString* json);
virtual ~SWGRTTYDemodReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRTTYDemodReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGRTTYDemodReport_H_ */
@@ -0,0 +1,697 @@
/**
* 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 "SWGRTTYDemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGRTTYDemodSettings::SWGRTTYDemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGRTTYDemodSettings::SWGRTTYDemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
baud_rate = 0.0f;
m_baud_rate_isSet = false;
frequency_shift = 0;
m_frequency_shift_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = nullptr;
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
character_set = 0;
m_character_set_isSet = false;
suppress_crlf = 0;
m_suppress_crlf_isSet = false;
unshift_on_space = 0;
m_unshift_on_space_isSet = false;
msb_first = 0;
m_msb_first_isSet = false;
space_high = 0;
m_space_high_isSet = false;
squelch = 0;
m_squelch_isSet = false;
log_filename = nullptr;
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_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;
scope_config = nullptr;
m_scope_config_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGRTTYDemodSettings::~SWGRTTYDemodSettings() {
this->cleanup();
}
void
SWGRTTYDemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
baud_rate = 0.0f;
m_baud_rate_isSet = false;
frequency_shift = 0;
m_frequency_shift_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = new QString("");
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
character_set = 0;
m_character_set_isSet = false;
suppress_crlf = 0;
m_suppress_crlf_isSet = false;
unshift_on_space = 0;
m_unshift_on_space_isSet = false;
msb_first = 0;
m_msb_first_isSet = false;
space_high = 0;
m_space_high_isSet = false;
squelch = 0;
m_squelch_isSet = false;
log_filename = new QString("");
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_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;
scope_config = new SWGGLScope();
m_scope_config_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGRTTYDemodSettings::cleanup() {
if(udp_address != nullptr) {
delete udp_address;
}
if(log_filename != nullptr) {
delete log_filename;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(scope_config != nullptr) {
delete scope_config;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGRTTYDemodSettings*
SWGRTTYDemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGRTTYDemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&baud_rate, pJson["baudRate"], "float", "");
::SWGSDRangel::setValue(&frequency_shift, pJson["frequencyShift"], "qint32", "");
::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", "");
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
::SWGSDRangel::setValue(&character_set, pJson["characterSet"], "qint32", "");
::SWGSDRangel::setValue(&suppress_crlf, pJson["suppressCRLF"], "qint32", "");
::SWGSDRangel::setValue(&unshift_on_space, pJson["unshiftOnSpace"], "qint32", "");
::SWGSDRangel::setValue(&msb_first, pJson["msbFirst"], "qint32", "");
::SWGSDRangel::setValue(&space_high, pJson["spaceHigh"], "qint32", "");
::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", "");
::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString");
::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "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(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGRTTYDemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGRTTYDemodSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_rf_bandwidth_isSet){
obj->insert("rfBandwidth", QJsonValue(rf_bandwidth));
}
if(m_baud_rate_isSet){
obj->insert("baudRate", QJsonValue(baud_rate));
}
if(m_frequency_shift_isSet){
obj->insert("frequencyShift", QJsonValue(frequency_shift));
}
if(m_udp_enabled_isSet){
obj->insert("udpEnabled", QJsonValue(udp_enabled));
}
if(udp_address != nullptr && *udp_address != QString("")){
toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString"));
}
if(m_udp_port_isSet){
obj->insert("udpPort", QJsonValue(udp_port));
}
if(m_character_set_isSet){
obj->insert("characterSet", QJsonValue(character_set));
}
if(m_suppress_crlf_isSet){
obj->insert("suppressCRLF", QJsonValue(suppress_crlf));
}
if(m_unshift_on_space_isSet){
obj->insert("unshiftOnSpace", QJsonValue(unshift_on_space));
}
if(m_msb_first_isSet){
obj->insert("msbFirst", QJsonValue(msb_first));
}
if(m_space_high_isSet){
obj->insert("spaceHigh", QJsonValue(space_high));
}
if(m_squelch_isSet){
obj->insert("squelch", QJsonValue(squelch));
}
if(log_filename != nullptr && *log_filename != QString("")){
toJsonValue(QString("logFilename"), log_filename, obj, QString("QString"));
}
if(m_log_enabled_isSet){
obj->insert("logEnabled", QJsonValue(log_enabled));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_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((scope_config != nullptr) && (scope_config->isSet())){
toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope"));
}
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
SWGRTTYDemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGRTTYDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
float
SWGRTTYDemodSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGRTTYDemodSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
float
SWGRTTYDemodSettings::getBaudRate() {
return baud_rate;
}
void
SWGRTTYDemodSettings::setBaudRate(float baud_rate) {
this->baud_rate = baud_rate;
this->m_baud_rate_isSet = true;
}
qint32
SWGRTTYDemodSettings::getFrequencyShift() {
return frequency_shift;
}
void
SWGRTTYDemodSettings::setFrequencyShift(qint32 frequency_shift) {
this->frequency_shift = frequency_shift;
this->m_frequency_shift_isSet = true;
}
qint32
SWGRTTYDemodSettings::getUdpEnabled() {
return udp_enabled;
}
void
SWGRTTYDemodSettings::setUdpEnabled(qint32 udp_enabled) {
this->udp_enabled = udp_enabled;
this->m_udp_enabled_isSet = true;
}
QString*
SWGRTTYDemodSettings::getUdpAddress() {
return udp_address;
}
void
SWGRTTYDemodSettings::setUdpAddress(QString* udp_address) {
this->udp_address = udp_address;
this->m_udp_address_isSet = true;
}
qint32
SWGRTTYDemodSettings::getUdpPort() {
return udp_port;
}
void
SWGRTTYDemodSettings::setUdpPort(qint32 udp_port) {
this->udp_port = udp_port;
this->m_udp_port_isSet = true;
}
qint32
SWGRTTYDemodSettings::getCharacterSet() {
return character_set;
}
void
SWGRTTYDemodSettings::setCharacterSet(qint32 character_set) {
this->character_set = character_set;
this->m_character_set_isSet = true;
}
qint32
SWGRTTYDemodSettings::getSuppressCrlf() {
return suppress_crlf;
}
void
SWGRTTYDemodSettings::setSuppressCrlf(qint32 suppress_crlf) {
this->suppress_crlf = suppress_crlf;
this->m_suppress_crlf_isSet = true;
}
qint32
SWGRTTYDemodSettings::getUnshiftOnSpace() {
return unshift_on_space;
}
void
SWGRTTYDemodSettings::setUnshiftOnSpace(qint32 unshift_on_space) {
this->unshift_on_space = unshift_on_space;
this->m_unshift_on_space_isSet = true;
}
qint32
SWGRTTYDemodSettings::getMsbFirst() {
return msb_first;
}
void
SWGRTTYDemodSettings::setMsbFirst(qint32 msb_first) {
this->msb_first = msb_first;
this->m_msb_first_isSet = true;
}
qint32
SWGRTTYDemodSettings::getSpaceHigh() {
return space_high;
}
void
SWGRTTYDemodSettings::setSpaceHigh(qint32 space_high) {
this->space_high = space_high;
this->m_space_high_isSet = true;
}
qint32
SWGRTTYDemodSettings::getSquelch() {
return squelch;
}
void
SWGRTTYDemodSettings::setSquelch(qint32 squelch) {
this->squelch = squelch;
this->m_squelch_isSet = true;
}
QString*
SWGRTTYDemodSettings::getLogFilename() {
return log_filename;
}
void
SWGRTTYDemodSettings::setLogFilename(QString* log_filename) {
this->log_filename = log_filename;
this->m_log_filename_isSet = true;
}
qint32
SWGRTTYDemodSettings::getLogEnabled() {
return log_enabled;
}
void
SWGRTTYDemodSettings::setLogEnabled(qint32 log_enabled) {
this->log_enabled = log_enabled;
this->m_log_enabled_isSet = true;
}
qint32
SWGRTTYDemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGRTTYDemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGRTTYDemodSettings::getTitle() {
return title;
}
void
SWGRTTYDemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGRTTYDemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGRTTYDemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGRTTYDemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGRTTYDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGRTTYDemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGRTTYDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGRTTYDemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGRTTYDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGRTTYDemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGRTTYDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGRTTYDemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGRTTYDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGGLScope*
SWGRTTYDemodSettings::getScopeConfig() {
return scope_config;
}
void
SWGRTTYDemodSettings::setScopeConfig(SWGGLScope* scope_config) {
this->scope_config = scope_config;
this->m_scope_config_isSet = true;
}
SWGChannelMarker*
SWGRTTYDemodSettings::getChannelMarker() {
return channel_marker;
}
void
SWGRTTYDemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGRTTYDemodSettings::getRollupState() {
return rollup_state;
}
void
SWGRTTYDemodSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGRTTYDemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(m_baud_rate_isSet){
isObjectUpdated = true; break;
}
if(m_frequency_shift_isSet){
isObjectUpdated = true; break;
}
if(m_udp_enabled_isSet){
isObjectUpdated = true; break;
}
if(udp_address && *udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_port_isSet){
isObjectUpdated = true; break;
}
if(m_character_set_isSet){
isObjectUpdated = true; break;
}
if(m_suppress_crlf_isSet){
isObjectUpdated = true; break;
}
if(m_unshift_on_space_isSet){
isObjectUpdated = true; break;
}
if(m_msb_first_isSet){
isObjectUpdated = true; break;
}
if(m_space_high_isSet){
isObjectUpdated = true; break;
}
if(m_squelch_isSet){
isObjectUpdated = true; break;
}
if(log_filename && *log_filename != QString("")){
isObjectUpdated = true; break;
}
if(m_log_enabled_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != 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(scope_config && scope_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;
}
}
@@ -0,0 +1,212 @@
/**
* 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.
*/
/*
* SWGRTTYDemodSettings.h
*
* ACARSDemod
*/
#ifndef SWGRTTYDemodSettings_H_
#define SWGRTTYDemodSettings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGGLScope.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGRTTYDemodSettings: public SWGObject {
public:
SWGRTTYDemodSettings();
SWGRTTYDemodSettings(QString* json);
virtual ~SWGRTTYDemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRTTYDemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
float getBaudRate();
void setBaudRate(float baud_rate);
qint32 getFrequencyShift();
void setFrequencyShift(qint32 frequency_shift);
qint32 getUdpEnabled();
void setUdpEnabled(qint32 udp_enabled);
QString* getUdpAddress();
void setUdpAddress(QString* udp_address);
qint32 getUdpPort();
void setUdpPort(qint32 udp_port);
qint32 getCharacterSet();
void setCharacterSet(qint32 character_set);
qint32 getSuppressCrlf();
void setSuppressCrlf(qint32 suppress_crlf);
qint32 getUnshiftOnSpace();
void setUnshiftOnSpace(qint32 unshift_on_space);
qint32 getMsbFirst();
void setMsbFirst(qint32 msb_first);
qint32 getSpaceHigh();
void setSpaceHigh(qint32 space_high);
qint32 getSquelch();
void setSquelch(qint32 squelch);
QString* getLogFilename();
void setLogFilename(QString* log_filename);
qint32 getLogEnabled();
void setLogEnabled(qint32 log_enabled);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
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);
SWGGLScope* getScopeConfig();
void setScopeConfig(SWGGLScope* scope_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;
float rf_bandwidth;
bool m_rf_bandwidth_isSet;
float baud_rate;
bool m_baud_rate_isSet;
qint32 frequency_shift;
bool m_frequency_shift_isSet;
qint32 udp_enabled;
bool m_udp_enabled_isSet;
QString* udp_address;
bool m_udp_address_isSet;
qint32 udp_port;
bool m_udp_port_isSet;
qint32 character_set;
bool m_character_set_isSet;
qint32 suppress_crlf;
bool m_suppress_crlf_isSet;
qint32 unshift_on_space;
bool m_unshift_on_space_isSet;
qint32 msb_first;
bool m_msb_first_isSet;
qint32 space_high;
bool m_space_high_isSet;
qint32 squelch;
bool m_squelch_isSet;
QString* log_filename;
bool m_log_filename_isSet;
qint32 log_enabled;
bool m_log_enabled_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_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;
SWGGLScope* scope_config;
bool m_scope_config_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGRTTYDemodSettings_H_ */