1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-05-02 12:13:59 -04:00

FT8 Demodulator: added option to send reports to PSK reporter. Fixes #2561

This commit is contained in:
f4exb 2026-01-03 10:56:38 +01:00
parent c334ffeeec
commit 89a15d4d6a
24 changed files with 910 additions and 25 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

View File

@ -9,6 +9,7 @@ set(demodft8_SOURCES
ft8plugin.cpp
ft8buffer.cpp
ft8demodworker.cpp
pskreporterworker.cpp
)
set(demodft8_HEADERS
@ -19,7 +20,8 @@ set(demodft8_HEADERS
ft8demodwebapiadapter.h
ft8plugin.h
ft8buffer.h
ft8demodworker.h;
ft8demodworker.h
pskreporterworker.h
)
include_directories(

View File

@ -329,6 +329,18 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force)
if ((m_settings.m_verifyOSD != settings.m_verifyOSD) || force) {
reverseAPIKeys.append("verifyOSD");
}
if ((m_settings.m_enablePSKReporter != settings.m_enablePSKReporter) || force) {
reverseAPIKeys.append("enablePSKReporter");
}
if ((m_settings.m_pskReporterCallsign != settings.m_pskReporterCallsign) || force) {
reverseAPIKeys.append("pskReporterCallsign");
}
if ((m_settings.m_pskReporterLocator != settings.m_pskReporterLocator) || force) {
reverseAPIKeys.append("pskReporterLocator");
}
if ((m_settings.m_pskReporterSoftware != settings.m_pskReporterSoftware) || force) {
reverseAPIKeys.append("pskReporterSoftware");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
@ -525,6 +537,18 @@ void FT8Demod::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("verifyOSD")) {
settings.m_verifyOSD = response.getFt8DemodSettings()->getVerifyOsd() != 0;
}
if (channelSettingsKeys.contains("enablePSKReporter")) {
settings.m_enablePSKReporter = response.getFt8DemodSettings()->getEnablePskReporter() != 0;
}
if (channelSettingsKeys.contains("pskReporterCallsign")) {
settings.m_pskReporterCallsign = *response.getFt8DemodSettings()->getPskReporterCallsign();
}
if (channelSettingsKeys.contains("pskReporterLocator")) {
settings.m_pskReporterLocator = *response.getFt8DemodSettings()->getPskReporterLocator();
}
if (channelSettingsKeys.contains("pskReporterSoftware")) {
settings.m_pskReporterSoftware = *response.getFt8DemodSettings()->getPskReporterSoftware();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFt8DemodSettings()->getRgbColor();
}
@ -589,6 +613,10 @@ void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp
response.getFt8DemodSettings()->setOsdDepth(settings.m_osdDepth);
response.getFt8DemodSettings()->setOsdLdpcThreshold(settings.m_osdLDPCThreshold);
response.getFt8DemodSettings()->setUseOsd(settings.m_verifyOSD ? 1 : 0);
response.getFt8DemodSettings()->setEnablePskReporter(settings.m_enablePSKReporter ? 1 : 0);
response.getFt8DemodSettings()->setPskReporterCallsign(new QString(settings.m_pskReporterCallsign));
response.getFt8DemodSettings()->setPskReporterLocator(new QString(settings.m_pskReporterLocator));
response.getFt8DemodSettings()->setPskReporterSoftware(new QString(settings.m_pskReporterSoftware));
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getFt8DemodSettings()->getTitle()) {
@ -784,6 +812,18 @@ void FT8Demod::webapiFormatChannelSettings(
if (channelSettingsKeys.contains("verifyOSD") || force) {
swgFT8DemodSettings->setVerifyOsd(settings.m_verifyOSD ? 1 : 0);
}
if (channelSettingsKeys.contains("enablePSKReporter") || force) {
swgFT8DemodSettings->setEnablePskReporter(settings.m_enablePSKReporter ? 1 : 0);
}
if (channelSettingsKeys.contains("pskReporterCallsign") || force) {
swgFT8DemodSettings->setPskReporterCallsign(new QString(settings.m_pskReporterCallsign));
}
if (channelSettingsKeys.contains("pskReporterLocator") || force) {
swgFT8DemodSettings->setPskReporterLocator(new QString(settings.m_pskReporterLocator));
}
if (channelSettingsKeys.contains("pskReporterSoftware") || force) {
swgFT8DemodSettings->setPskReporterSoftware(new QString(settings.m_pskReporterSoftware));
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFT8DemodSettings->setRgbColor(settings.m_rgbColor);
}

View File

@ -24,6 +24,7 @@
#include "maincore.h"
#include "ft8demodworker.h"
#include "pskreporterworker.h"
#include "ft8demodbaseband.h"
MESSAGE_CLASS_DEFINITION(FT8DemodBaseband::MsgConfigureFT8DemodBaseband, Message)
@ -48,12 +49,7 @@ FT8DemodBaseband::FT8DemodBaseband() :
m_ft8DemodWorker,
&QObject::deleteLater
);
QObject::connect(
m_workerThread,
&QThread::finished,
m_ft8DemodWorker,
&QThread::deleteLater
);
QObject::connect(
this,
&FT8DemodBaseband::bufferReady,
@ -64,6 +60,20 @@ FT8DemodBaseband::FT8DemodBaseband() :
m_workerThread->start();
m_pskReporterThread = new QThread();
m_pskReporterWorker = new PskReporterWorker();
m_ft8DemodWorker->setPSKReportingMessageQueue(m_pskReporterWorker->getInputMessageQueue());
m_pskReporterWorker->moveToThread(m_pskReporterThread);
QObject::connect(
m_pskReporterThread,
&QThread::finished,
m_pskReporterWorker,
&QObject::deleteLater
);
m_pskReporterThread->start();
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
@ -84,6 +94,8 @@ FT8DemodBaseband::~FT8DemodBaseband()
m_workerThread->exit();
m_workerThread->wait();
delete[] m_ft8WorkerBuffer;
m_pskReporterThread->exit();
m_pskReporterThread->wait();
}
void FT8DemodBaseband::reset()
@ -96,7 +108,7 @@ void FT8DemodBaseband::reset()
void FT8DemodBaseband::setMessageQueueToGUI(MessageQueue *messageQueue)
{
m_messageQueueToGUI = messageQueue;
m_ft8DemodWorker->setReportingMessageQueue(m_messageQueueToGUI);
m_ft8DemodWorker->setGUIReportingMessageQueue(m_messageQueueToGUI);
}
void FT8DemodBaseband::setChannel(ChannelAPI *channel)
@ -237,6 +249,22 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc
m_ft8DemodWorker->setLogMessages(settings.m_logMessages);
}
if ((settings.m_enablePSKReporter != m_settings.m_enablePSKReporter) || force) {
m_ft8DemodWorker->setEnablePskReporter(settings.m_enablePSKReporter);
}
if ((settings.m_pskReporterCallsign != m_settings.m_pskReporterCallsign) || force) {
m_pskReporterWorker->setMyCallsign(settings.m_pskReporterCallsign);
}
if ((settings.m_pskReporterLocator != m_settings.m_pskReporterLocator) || force) {
m_pskReporterWorker->setMyLocator(settings.m_pskReporterLocator);
}
if ((settings.m_pskReporterSoftware != m_settings.m_pskReporterSoftware) || force) {
m_pskReporterWorker->setDecoderInfo(settings.m_pskReporterSoftware);
}
if ((settings.m_nbDecoderThreads != m_settings.m_nbDecoderThreads) || force) {
m_ft8DemodWorker->setNbDecoderThreads(settings.m_nbDecoderThreads);
}

View File

@ -36,6 +36,7 @@ class ChannelAPI;
class SpectrumVis;
class QThread;
class FT8DemodWorker;
class PskReporterWorker;
class FT8DemodBaseband : public QObject
{
@ -102,6 +103,8 @@ private:
int m_tickCount;
QThread *m_workerThread;
FT8DemodWorker *m_ft8DemodWorker;
QThread *m_pskReporterThread;
PskReporterWorker *m_pskReporterWorker;
int16_t *m_ft8WorkerBuffer;
qint64 m_deviceCenterFrequency;
QRecursiveMutex m_mutex;

View File

@ -528,6 +528,30 @@ void FT8DemodGUI::on_settings_clicked()
changed = true;
}
if (settingsKeys.contains("enablePSKReporter"))
{
m_settings.m_enablePSKReporter = settings.m_enablePSKReporter;
changed = true;
}
if (settingsKeys.contains("pskReporterCallsign"))
{
m_settings.m_pskReporterCallsign = settings.m_pskReporterCallsign;
changed = true;
}
if (settingsKeys.contains("pskReporterLocator"))
{
m_settings.m_pskReporterLocator = settings.m_pskReporterLocator;
changed = true;
}
if (settingsKeys.contains("pskReporterSoftware"))
{
m_settings.m_pskReporterSoftware = settings.m_pskReporterSoftware;
changed = true;
}
if (settingsKeys.contains("bandPresets"))
{
m_settings.m_bandPresets = settings.m_bandPresets;

View File

@ -19,9 +19,12 @@
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include <QCoreApplication>
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "maincore.h"
#include "util/maidenhead.h"
#include "ft8demodsettings.h"
const int FT8DemodSettings::m_ft8SampleRate = 12000;
@ -66,6 +69,10 @@ void FT8DemodSettings::resetToDefaults()
m_workspaceIndex = 0;
m_hidden = false;
m_filterIndex = 0;
m_enablePSKReporter = false;
m_pskReporterCallsign = getDefaultReporterCallsign();
m_pskReporterLocator = getDefaultReporterLocator();
m_pskReporterSoftware = "SDRangel FT8 Demod";
resetBandPresets();
}
@ -133,6 +140,10 @@ QByteArray FT8DemodSettings::serialize() const
s.writeBlob(26, m_geometryBytes);
s.writeBool(27, m_hidden);
s.writeU32(29, m_filterIndex);
s.writeBool(30, m_enablePSKReporter);
s.writeString(31, m_pskReporterCallsign);
s.writeString(32, m_pskReporterLocator);
s.writeString(33, m_pskReporterSoftware);
for (unsigned int i = 0; i < 10; i++)
{
@ -214,6 +225,10 @@ bool FT8DemodSettings::deserialize(const QByteArray& data)
d.readBool(27, &m_hidden, false);
d.readU32(29, &utmp, 0);
m_filterIndex = utmp < 10 ? utmp : 0;
d.readBool(30, &m_enablePSKReporter, false);
d.readString(31, &m_pskReporterCallsign, getDefaultReporterCallsign());
d.readString(32, &m_pskReporterLocator, getDefaultReporterLocator());
d.readString(33, &m_pskReporterSoftware, getDefaultReporterSoftware());
for (unsigned int i = 0; (i < 10); i++)
{
@ -252,3 +267,20 @@ QDataStream& operator>>(QDataStream& in, FT8DemodBandPreset& bandPreset)
in >> bandPreset.m_channelOffset;
return in;
}
QString FT8DemodSettings::getDefaultReporterCallsign() const
{
return MainCore::instance()->getSettings().getStationName();
}
QString FT8DemodSettings::getDefaultReporterLocator() const
{
float lat = MainCore::instance()->getSettings().getLatitude();
float lon = MainCore::instance()->getSettings().getLongitude();
return Maidenhead::toMaidenhead(lat, lon);
}
QString FT8DemodSettings::getDefaultReporterSoftware() const
{
return QCoreApplication::applicationName() + " " + QCoreApplication::applicationVersion();
}

View File

@ -97,6 +97,10 @@ public:
std::vector<FT8DemodFilterSettings> m_filterBank;
unsigned int m_filterIndex;
QList<FT8DemodBandPreset> m_bandPresets;
bool m_enablePSKReporter;
QString m_pskReporterCallsign;
QString m_pskReporterLocator;
QString m_pskReporterSoftware;
Serializable *m_channelMarker;
Serializable *m_spectrumGUI;
@ -110,6 +114,9 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void resetBandPresets();
QString getDefaultReporterCallsign() const;
QString getDefaultReporterLocator() const;
QString getDefaultReporterSoftware() const;
static const int m_ft8SampleRate;
static const int m_minPowerThresholdDB;

View File

@ -35,6 +35,10 @@ FT8DemodSettingsDialog::FT8DemodSettingsDialog(FT8DemodSettings& settings, QStri
ui->osdLDPCThreshold->setValue(m_settings.m_osdLDPCThreshold);
ui->osdLDPCThresholdText->setText(tr("%1").arg(m_settings.m_osdLDPCThreshold));
ui->verifyOSD->setChecked(m_settings.m_verifyOSD);
ui->enablePSKReporter->setChecked(m_settings.m_enablePSKReporter);
ui->reportingStation->setText(m_settings.m_pskReporterCallsign);
ui->reportingLocator->setText(m_settings.m_pskReporterLocator);
ui->reportingSoftware->setText(m_settings.m_pskReporterSoftware);
resizeBandsTable();
populateBandsTable();
connect(ui->bands, &QTableWidget::cellChanged, this, &FT8DemodSettingsDialog::textCellChanged);
@ -162,6 +166,65 @@ void FT8DemodSettingsDialog::on_verifyOSD_stateChanged(int state)
}
}
void FT8DemodSettingsDialog::on_enablePSKReporter_toggled(bool checked)
{
m_settings.m_enablePSKReporter = checked;
if (!m_settingsKeys.contains("enablePSKReporter")) {
m_settingsKeys.append("enablePSKReporter");
}
}
void FT8DemodSettingsDialog::on_pskReporterCallsign_editingFinished()
{
m_settings.m_pskReporterCallsign = ui->reportingStation->text();
if (!m_settingsKeys.contains("pskReporterCallsign")) {
m_settingsKeys.append("pskReporterCallsign");
}
}
void FT8DemodSettingsDialog::on_pskReporterLocator_editingFinished()
{
m_settings.m_pskReporterLocator = ui->reportingLocator->text();
if (!m_settingsKeys.contains("pskReporterLocator")) {
m_settingsKeys.append("pskReporterLocator");
}
}
void FT8DemodSettingsDialog::on_pskReporterSoftware_editingFinished()
{
m_settings.m_pskReporterSoftware = ui->reportingSoftware->text();
if (!m_settingsKeys.contains("pskReporterSoftware")) {
m_settingsKeys.append("pskReporterSoftware");
}
}
void FT8DemodSettingsDialog::on_reportingDefaults_clicked()
{
ui->reportingStation->setText(m_settings.getDefaultReporterCallsign());
ui->reportingLocator->setText(m_settings.getDefaultReporterLocator());
ui->reportingSoftware->setText(m_settings.getDefaultReporterSoftware());
m_settings.m_pskReporterCallsign = ui->reportingStation->text();
m_settings.m_pskReporterLocator = ui->reportingLocator->text();
m_settings.m_pskReporterSoftware = ui->reportingSoftware->text();
if (!m_settingsKeys.contains("pskReporterCallsign")) {
m_settingsKeys.append("pskReporterCallsign");
}
if (!m_settingsKeys.contains("pskReporterLocator")) {
m_settingsKeys.append("pskReporterLocator");
}
if (!m_settingsKeys.contains("pskReporterSoftware")) {
m_settingsKeys.append("pskReporterSoftware");
}
}
void FT8DemodSettingsDialog::on_addBand_clicked()
{
int currentRow = ui->bands->currentRow();

View File

@ -62,6 +62,11 @@ private slots:
void on_osdDepth_valueChanged(int value);
void on_osdLDPCThreshold_valueChanged(int value);
void on_verifyOSD_stateChanged(int state);
void on_enablePSKReporter_toggled(bool checked);
void on_pskReporterCallsign_editingFinished();
void on_pskReporterLocator_editingFinished();
void on_pskReporterSoftware_editingFinished();
void on_reportingDefaults_clicked();
void on_addBand_clicked();
void on_deleteBand_clicked();
void on_moveBandUp_clicked();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>349</width>
<height>333</height>
<width>343</width>
<height>423</height>
</rect>
</property>
<property name="font">
@ -229,6 +229,104 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="reporterGroup">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>PSK reporter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="enablePSKReporter">
<property name="toolTip">
<string>Enable sending reception reports to PSK reporter</string>
</property>
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="reportingStationLabel">
<property name="text">
<string>Sta</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="reportingStation">
<property name="toolTip">
<string>Reporting (your) station</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="reportingLocatorLabel">
<property name="text">
<string>Loc</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="reportingLocator">
<property name="toolTip">
<string>Reporting (your) locator</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="reportingSoftwareLabel">
<property name="text">
<string>Soft</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="reportingSoftware">
<property name="toolTip">
<string>Reporting (your) software</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reportingDefaults">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Reset reporter values to defaults</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/recycle.png</normaloff>:/recycle.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="bandsGroup">
<property name="title">

View File

@ -141,6 +141,8 @@ QString FT8DemodWorker::FT8Callback::get_name()
FT8DemodWorker::FT8DemodWorker() :
m_recordSamples(false),
m_logMessages(false),
m_enablePskReporter(false),
m_nbDecoderThreads(6),
m_decoderTimeBudget(0.5),
m_useOSD(false),
@ -151,7 +153,8 @@ FT8DemodWorker::FT8DemodWorker() :
m_highFreq(3000),
m_invalidSequence(true),
m_baseFrequency(0),
m_reportingMessageQueue(nullptr),
m_guiReportingMessageQueue(nullptr),
m_pskReportingMessageQueue(nullptr),
m_channel(nullptr)
{
QString relPath = "ft8/save";
@ -237,8 +240,12 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
qDebug("FT8DemodWorker::processBuffer: done: at %6.3f %d messages",
m_baseFrequency / 1000000.0, (int)ft8Callback.getReportMessage()->getFT8Messages().size());
if (m_reportingMessageQueue) {
m_reportingMessageQueue->push(new MsgReportFT8Messages(*ft8Callback.getReportMessage()));
if (m_guiReportingMessageQueue) {
m_guiReportingMessageQueue->push(new MsgReportFT8Messages(*ft8Callback.getReportMessage()));
}
if (m_enablePskReporter && m_pskReportingMessageQueue) {
m_pskReportingMessageQueue->push(new MsgReportFT8Messages(*ft8Callback.getReportMessage()));
}
QList<ObjectPipe*> mapPipes;

View File

@ -41,6 +41,7 @@ public:
void processBuffer(int16_t *buffer, QDateTime periodTS);
void setRecordSamples(bool recordSamples) { m_recordSamples = recordSamples; }
void setLogMessages(bool logMessages) { m_logMessages = logMessages; }
void setEnablePskReporter(bool enablePskReporter) { m_enablePskReporter = enablePskReporter; }
void setNbDecoderThreads(int nbDecoderThreads) { m_nbDecoderThreads = nbDecoderThreads; }
void setDecoderTimeBudget(float decoderTimeBudget) { m_decoderTimeBudget = decoderTimeBudget; }
void setUseOSD(bool useOSD) { m_useOSD = useOSD; }
@ -49,7 +50,8 @@ public:
void setVerifyOSD(bool verifyOSD) { m_verifyOSD = verifyOSD; }
void setLowFrequency(int lowFreq) { m_lowFreq = lowFreq; }
void setHighFrequency(int highFreq) { m_highFreq = highFreq; }
void setReportingMessageQueue(MessageQueue *messageQueue) { m_reportingMessageQueue = messageQueue; }
void setGUIReportingMessageQueue(MessageQueue *messageQueue) { m_guiReportingMessageQueue = messageQueue; }
void setPSKReportingMessageQueue(MessageQueue *messageQueue) { m_pskReportingMessageQueue = messageQueue; }
void invalidateSequence() { m_invalidSequence = true; }
void setBaseFrequency(qint64 baseFrequency) { m_baseFrequency = baseFrequency; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
@ -92,6 +94,7 @@ private:
QString m_logsPath;
bool m_recordSamples;
bool m_logMessages;
bool m_enablePskReporter;
int m_nbDecoderThreads;
float m_decoderTimeBudget;
bool m_useOSD;
@ -104,7 +107,8 @@ private:
qint64 m_baseFrequency;
FT8::FT8Decoder m_ft8Decoder;
FT8::Packing m_packing;
MessageQueue *m_reportingMessageQueue;
MessageQueue *m_guiReportingMessageQueue;
MessageQueue *m_pskReportingMessageQueue;
ChannelAPI *m_channel;
QSet<QString> m_validCallsigns;
};

View File

@ -0,0 +1,286 @@
///////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////////
#include <QHostInfo>
#include <QRandomGenerator>
#include "util/ft8message.h"
#include "pskreporterworker.h"
const char PskReporterWorker::hostname[] = "report.pskreporter.info";
const char PskReporterWorker::service[] = "4739";
const char PskReporterWorker::test_service[] = "14739";
const char PskReporterWorker::txMode[] = "FT8";
const unsigned char PskReporterWorker::rxDescriptor[] = {
0x00, 0x03, // Template Set ID
0x00, 0x24, // Length
0x99, 0x92, // Link ID
0x00, 0x03, // Field Count
0x00, 0x00, // Scope Field Count
0x80, 0x02, // Receiver Callsign ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x04, // Receiver Locator ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x08, // Receiver Decoder Software ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x00, 0x00 // Padding
}; // "PSKREPORTER_RX"
const unsigned char PskReporterWorker::txDescriptor[] = {
0x00, 0x02, // Template Set ID
0x00, 0x3C, // Length
0x99, 0x93, // Link ID
0x00, 0x07, // Field Count
0x80, 0x01, // Sender Callsign ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x05, // Sender Frequency ID
0x00, 0x04, // Fixed length (4)
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x06, // Sender SNR ID
0x00, 0x01, // Fixed length (1)
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x0A, // Sender Mode ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x03, // Sender Locator ID
0xFF, 0xFF, // Variable field length
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x80, 0x0B, // Information Source ID
0x00, 0x01, // Fixed length (1)
0x00, 0x00, 0x76, 0x8F, // Enterprise number
0x00, 0x96, // DateTimeSeconds ID
0x00, 0x04 // Field Length
}; // "PSKREPORTER_TX"
PskReporterWorker::PskReporterWorker() :
m_udpSocket(new QUdpSocket(this))
{
connect(&m_reportQueue, &MessageQueue::messageEnqueued,
this, &PskReporterWorker::handleInputMessages);
m_lastReportTime = QDateTime::currentDateTimeUtc();
m_identifier = QRandomGenerator::global()->generate(); // random number for the identifier for this session
}
void PskReporterWorker::processFT8Messages(const QList<FT8Message>& ft8Messages, qint64 baseFrequency)
{
m_ft8MessageQueue.append(ft8Messages); // Queue messages for processing
qDebug("PskReporterWorker::processFT8Messages: queued %d messages", ft8Messages.size());
// Avoid reporting too frequently - 5 minutes interval is the recommended value
if (m_lastReportTime.secsTo(QDateTime::currentDateTimeUtc()) < 5*60) {
return;
}
m_lastReportTime = QDateTime::currentDateTimeUtc();
uint32_t txPtr = 4;
std::fill(std::begin(txInfoData), std::end(txInfoData), 0);
while (!m_ft8MessageQueue.isEmpty())
{
FT8Message msg = m_ft8MessageQueue.dequeue();
if (m_reportedCalls.contains(msg.call2)) {
continue;
}
m_reportedCalls.insert(msg.call2);
if (txPtr > 1200)
{
sendMessageToPskReporter(txPtr);
txPtr = 4;
std::fill(std::begin(txInfoData), std::end(txInfoData), 0);
}
// Station callsign
*(uint8_t *)&txInfoData[txPtr] = (uint8_t) msg.call2.size();
txPtr += 1;
memcpy(&txInfoData[txPtr], msg.call2.toStdString().c_str(), msg.call2.size());
txPtr += msg.call2.size();
// Station frequency
uint32_t freq = static_cast<uint32_t>(baseFrequency + msg.df);
freq = SwapEndian32(freq);
memcpy(&txInfoData[txPtr], &freq, 4);
txPtr +=4;
// Station SNR
int8_t snr = static_cast<int8_t>(msg.snr);
memcpy(&txInfoData[txPtr], &snr, 1);
txPtr +=1;
// Station Mode
*(uint8_t *)&txInfoData[txPtr] = (uint8_t)strlen(txMode);
txPtr += 1;
strncpy((char *)&txInfoData[txPtr], txMode, strlen(txMode));
txPtr += strlen(txMode);
// Station locator
*(uint8_t *)&txInfoData[txPtr] = (uint8_t) msg.loc.size();
txPtr += 1;
memcpy(&txInfoData[txPtr], msg.loc.toStdString().c_str(), msg.loc.size());
txPtr += msg.loc.size();
/* Station Info -- Static length (1) */
*(uint8_t *)&txInfoData[txPtr] = (uint8_t)1;
txPtr += 1;
// Message timestamp -- Static length (4)
uint32_t timestamp = static_cast<uint32_t>(msg.ts.toSecsSinceEpoch());
timestamp = SwapEndian32(timestamp);
memcpy(&txInfoData[txPtr], &timestamp, 4);
txPtr +=4;
}
sendMessageToPskReporter(txPtr);
m_reportedCalls.clear();
m_reportSequenceNumber++;
}
void PskReporterWorker::sendMessageToPskReporter(uint32_t txPtr)
{
// Implement PSK Reporter message sending here
if (txInfoData[4] == 0) {
return; // No data to send
}
// Prepare and send the UDP message to PSK Reporter server
const uint32_t headerSize = 16;
char headerData[headerSize] = {0};
uint32_t hPtr = 0;
*(uint16_t *)&headerData[hPtr] = SwapEndian16(0x000A);
hPtr += 2;
hPtr += 2; // Skip the size block, adjust later
uint32_t timestamp = QDateTime::currentDateTimeUtc().toSecsSinceEpoch();
*(uint32_t *)&headerData[hPtr] = SwapEndian32(timestamp);
hPtr += 4;
*(uint32_t *)&headerData[hPtr] = SwapEndian32(m_reportSequenceNumber);
hPtr += 4;
*(uint32_t *)&headerData[hPtr] = SwapEndian32(m_identifier);
hPtr += 4;
char rxInfoData[256] = {0};
uint32_t rxPtr = 0;
*(uint16_t *)&rxInfoData[rxPtr] = SwapEndian16(0x9992);
rxPtr += 2;
rxPtr += 2; // Skip the size block, adjust later
// Receiver callsign
*(uint8_t *)&rxInfoData[rxPtr] = (uint8_t) m_myCallsign.size();
rxPtr += 1;
memcpy(&rxInfoData[rxPtr], m_myCallsign.toStdString().c_str(), m_myCallsign.size());
rxPtr += m_myCallsign.size();
// Receiver locator
*(uint8_t *)&rxInfoData[rxPtr] = (uint8_t) m_myLocator.size();
rxPtr += 1;
memcpy(&rxInfoData[rxPtr], m_myLocator.toStdString().c_str(), m_myLocator.size());
rxPtr += m_myLocator.size();
// Receiver decoder software
*(uint8_t *)&rxInfoData[rxPtr] = (uint8_t) m_decoderInfo.size();
rxPtr += 1;
memcpy(&rxInfoData[rxPtr], m_decoderInfo.toStdString().c_str(), m_decoderInfo.size());
rxPtr += m_decoderInfo.size();
// Padding to 4-byte boundary
if ((rxPtr % 4) > 0)
rxPtr += (4 - (rxPtr % 4));
// Padding to 4-byte boundary
if ((txPtr % 4) > 0)
txPtr += (4 - (txPtr % 4));
*(uint16_t *)&txInfoData[0] = SwapEndian16(0x9993);
/* Adjust the block sizes */
uint32_t fullBlockSize = headerSize + sizeof(rxDescriptor) + sizeof(txDescriptor) + rxPtr + txPtr;
*(uint16_t *)&rxInfoData[2] = SwapEndian16(rxPtr);
*(uint16_t *)&txInfoData[2] = SwapEndian16(txPtr);
*(uint16_t *)&headerData[2] = SwapEndian16(fullBlockSize);
/* Assemble the block to send over UDP */
char *fullBlockData = new char[fullBlockSize];
uint32_t ptrBlock = 0;
memcpy(&fullBlockData[ptrBlock], headerData, headerSize); ptrBlock += headerSize;
memcpy(&fullBlockData[ptrBlock], rxDescriptor, sizeof(rxDescriptor)); ptrBlock += sizeof(rxDescriptor);
memcpy(&fullBlockData[ptrBlock], txDescriptor, sizeof(txDescriptor)); ptrBlock += sizeof(txDescriptor);
memcpy(&fullBlockData[ptrBlock], rxInfoData, rxPtr); ptrBlock += rxPtr;
memcpy(&fullBlockData[ptrBlock], txInfoData, txPtr); ptrBlock += txPtr;
/* Send via UDP */
const char* servicePort = m_isTestMode ? test_service : service;
QHostAddress hostAddress;
// Resolve hostname
QHostInfo hostInfo = QHostInfo::fromName(hostname);
if (!hostInfo.addresses().isEmpty()) {
hostAddress = hostInfo.addresses().first();
// Send the datagram
qint64 bytesSent = m_udpSocket->writeDatagram(fullBlockData, fullBlockSize, hostAddress, QString(servicePort).toUInt());
if (bytesSent == -1) {
qWarning("PskReporterWorker::sendMessageToPskReporter: Failed to send UDP datagram: %s",
qPrintable(m_udpSocket->errorString()));
} else {
qDebug("PskReporterWorker::sendMessageToPskReporter: Sent %lld bytes to %s:%s",
bytesSent, hostname, servicePort);
}
} else {
qWarning("PskReporterWorker::sendMessageToPskReporter: Failed to resolve hostname: %s", hostname);
}
delete[] fullBlockData;
}
bool PskReporterWorker::handleMessage(const Message& message)
{
if (MsgReportFT8Messages::match(message))
{
const MsgReportFT8Messages& ft8Msg = static_cast<const MsgReportFT8Messages&>(message);
const QList<FT8Message>& ft8Messages = ft8Msg.getFT8Messages();
qint64 baseFrequency = ft8Msg.getBaseFrequency();
// Process FT8 messages for PSK Reporter here
processFT8Messages(ft8Messages, baseFrequency);
return true;
}
return false;
}
void PskReporterWorker::handleInputMessages()
{
Message* message = m_reportQueue.pop();
if (message == nullptr) {
return;
}
if (!handleMessage(*message)) {
delete message;
}
}

View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2026 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_PSKREPORTERWORKER_H
#define INCLUDE_PSKREPORTERWORKER_H
#include <QObject>
#include <QList>
#include <QSet>
#include <QQueue>
#include <QUdpSocket>
#include "util/ft8message.h"
#include "util/message.h"
#include "util/messagequeue.h"
class PskReporterWorker : public QObject
{
Q_OBJECT
public:
PskReporterWorker();
~PskReporterWorker() = default;
MessageQueue* getInputMessageQueue() { return &m_reportQueue; }
void setMyCallsign(const QString& callsign) { m_myCallsign = callsign; }
void setMyLocator(const QString& locator) { m_myLocator = locator; }
void setDecoderInfo(const QString& decoderInfo) { m_decoderInfo = decoderInfo; }
void setTestMode(bool isTestMode) { m_isTestMode = isTestMode; }
private:
struct decoder_results {
char call[13];
char loc[7];
int32_t freq;
int32_t snr;
};
QString m_myCallsign;
QString m_myLocator;
QString m_decoderInfo;
bool m_isTestMode = false;
MessageQueue m_reportQueue;
QQueue<FT8Message> m_ft8MessageQueue;
QSet<QString> m_reportedCalls;
uint32_t m_reportSequenceNumber = 1;
QDateTime m_lastReportTime;
QUdpSocket* m_udpSocket;
uint32_t m_identifier;
char txInfoData[1500];
static const char hostname[];
static const char service[];
static const char test_service[];
static const char txMode[];
static const unsigned char rxDescriptor[];
static const unsigned char txDescriptor[];
void processFT8Messages(const QList<FT8Message>& ft8Messages, qint64 baseFrequency);
void sendMessageToPskReporter(uint32_t txPtr);
bool handleMessage(const Message& message);
inline uint16_t SwapEndian16(uint16_t val) {
return (val<<8) | (val>>8);
}
inline uint32_t SwapEndian32(uint32_t val) {
return (val<<24) | ((val<<8) & 0x00ff0000) | ((val>>8) & 0x0000ff00) | (val>>24);
}
private slots:
void handleInputMessages();
};
#endif // INCLUDE_PSKREPORTERWORKER_H

View File

@ -235,7 +235,27 @@ Sets the minimum number of correct LDPC bits (out of 83) necessary to trigger OS
OSD search may find invalid solutions as mentioned above. When checking this option the callsigns in messages not passed through OSD (thus very certainly valid) are stored for the life of the plugin. When OSD is engaged the second callsign field which is always a callsign is checked against this list and if the callsign is not found the message is rejected. This is quite efficient in removing false messages when OSD is engaged although some valid messages may be removed.
<h4>C.1.7: Band presets table</h4>
<h4>C.1.7: Report to PSK reporter</h4>
Sends received calls details to the PSK reporter (report.pskreporter.info port 4739) via UDP packets. Reports are sent every 5 minutes.
<h4>C.1.8: Reporting station callsign</h4>
This is the callsign of your station as it will be reported (may be an Amateur Radio callsign or something else). By default it takes the value for "Sation name" in the "Preferences > My Position..." in the main window.
<h4>C.1.9: Reporting station Maindenhead locator</h4>
This is the Maidenhead locator as it will be reported. By default it is calculated from the "Latitude" and "Longitude" values in the "Preferences > My Position..." in the main window.
<h4>C.1.10: Reporting station software</h4>
This is the decoding Software name and version as it will be reported in the "Using" field. By default it takes the value shown at the bottom right of the main SDRangel window.
<h4>C.1.11: Revert to default values</h4>
Use this push button to revert to default values for the reporting station details.
<h4>C.1.12: Band presets table</h4>
This table shows the band presets values that will appear in (C.5)
@ -245,23 +265,23 @@ This table shows the band presets values that will appear in (C.5)
You can edit these values by clicking on the cell in the table.
<h4>C.1.8: Add preset</h4>
<h4>C.1.13: Add preset</h4>
Use this button to create a new preset. It will take the values from the row of the selected cell in the table (if selected) and put the new preset at the bottom of the table
<h4>C.1.9: Delete preset</h4>
<h4>C.1.14: Delete preset</h4>
Delete the preset designated by the selected cell in the table.
<h4>C.1.10: Move up preset</h4>
<h4>C.1.15: Move up preset</h4>
Move up the preset designated by the selected cell in the table.
<h4>C.1.11: Move down preset</h4>
<h4>C.1.16: Move down preset</h4>
Move down the preset designated by the selected cell in the table.
<h4>C.1.12: Restore defaults</h4>
<h4>C.1.17: Restore defaults</h4>
This restores the default band preset values:
@ -283,10 +303,10 @@ This restores the default band preset values:
Channel offsets are all set to 0 kHz.
<h4>C.1.13 Commit changes</h4>
<h4>C.1.18 Commit changes</h4>
Click on the "OK" button to commit changes and close dialog.
<h4>C.1.14 Cancel changes</h4>
<h4>C.1.19 Cancel changes</h4>
Click on the "Cancel" button to close dialog without making changes.

View File

@ -6380,6 +6380,19 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "Verify OSD decoded message against a list of validated callsigns\n * 0 - Disable\n * 1 - Enable\n"
},
"enablePSKReporter" : {
"type" : "integer",
"description" : "Enable reporting of decoded messages to PSK Reporter server\n * 0 - Disable\n * 1 - Enable\n"
},
"pskReporterCallsign" : {
"type" : "string"
},
"pskReporterLocator" : {
"type" : "string"
},
"pskReporterSoftware" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
@ -59634,7 +59647,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2025-12-31T21:12:50.336+01:00
Generated 2026-01-03T10:36:00.091+01:00
</div>
</div>
</div>

View File

@ -70,6 +70,18 @@ FT8DemodSettings:
Verify OSD decoded message against a list of validated callsigns
* 0 - Disable
* 1 - Enable
enablePSKReporter:
type: integer
description: >
Enable reporting of decoded messages to PSK Reporter server
* 0 - Disable
* 1 - Enable
pskReporterCallsign:
type: string
pskReporterLocator:
type: string
pskReporterSoftware:
type: string
rgbColor:
type: integer
title:

View File

@ -44,7 +44,9 @@ struct SDRBASE_API FT8Message
class SDRBASE_API MsgReportFT8Messages : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QList<FT8Message>& getFT8Messages() const { return m_ft8Messages; }
QList<FT8Message>& getFT8Messages() { return m_ft8Messages; }
qint64 getBaseFrequency() const { return m_baseFrequency; }
void setBaseFrequency(qint64 baseFrequency) { m_baseFrequency = baseFrequency; }
static MsgReportFT8Messages* create() {

View File

@ -70,6 +70,18 @@ FT8DemodSettings:
Verify OSD decoded message against a list of validated callsigns
* 0 - Disable
* 1 - Enable
enablePSKReporter:
type: integer
description: >
Enable reporting of decoded messages to PSK Reporter server
* 0 - Disable
* 1 - Enable
pskReporterCallsign:
type: string
pskReporterLocator:
type: string
pskReporterSoftware:
type: string
rgbColor:
type: integer
title:

View File

@ -6380,6 +6380,19 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "Verify OSD decoded message against a list of validated callsigns\n * 0 - Disable\n * 1 - Enable\n"
},
"enablePSKReporter" : {
"type" : "integer",
"description" : "Enable reporting of decoded messages to PSK Reporter server\n * 0 - Disable\n * 1 - Enable\n"
},
"pskReporterCallsign" : {
"type" : "string"
},
"pskReporterLocator" : {
"type" : "string"
},
"pskReporterSoftware" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
@ -59634,7 +59647,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2025-12-31T21:12:50.336+01:00
Generated 2026-01-03T10:36:00.091+01:00
</div>
</div>
</div>

View File

@ -60,6 +60,14 @@ SWGFT8DemodSettings::SWGFT8DemodSettings() {
m_osd_ldpc_threshold_isSet = false;
verify_osd = 0;
m_verify_osd_isSet = false;
enable_psk_reporter = 0;
m_enable_psk_reporter_isSet = false;
psk_reporter_callsign = nullptr;
m_psk_reporter_callsign_isSet = false;
psk_reporter_locator = nullptr;
m_psk_reporter_locator_isSet = false;
psk_reporter_software = nullptr;
m_psk_reporter_software_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
@ -122,6 +130,14 @@ SWGFT8DemodSettings::init() {
m_osd_ldpc_threshold_isSet = false;
verify_osd = 0;
m_verify_osd_isSet = false;
enable_psk_reporter = 0;
m_enable_psk_reporter_isSet = false;
psk_reporter_callsign = new QString("");
m_psk_reporter_callsign_isSet = false;
psk_reporter_locator = new QString("");
m_psk_reporter_locator_isSet = false;
psk_reporter_software = new QString("");
m_psk_reporter_software_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
@ -165,6 +181,16 @@ SWGFT8DemodSettings::cleanup() {
if(psk_reporter_callsign != nullptr) {
delete psk_reporter_callsign;
}
if(psk_reporter_locator != nullptr) {
delete psk_reporter_locator;
}
if(psk_reporter_software != nullptr) {
delete psk_reporter_software;
}
if(title != nullptr) {
delete title;
}
@ -230,6 +256,14 @@ SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&verify_osd, pJson["verifyOSD"], "qint32", "");
::SWGSDRangel::setValue(&enable_psk_reporter, pJson["enablePSKReporter"], "qint32", "");
::SWGSDRangel::setValue(&psk_reporter_callsign, pJson["pskReporterCallsign"], "QString", "QString");
::SWGSDRangel::setValue(&psk_reporter_locator, pJson["pskReporterLocator"], "QString", "QString");
::SWGSDRangel::setValue(&psk_reporter_software, pJson["pskReporterSoftware"], "QString", "QString");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
@ -316,6 +350,18 @@ SWGFT8DemodSettings::asJsonObject() {
if(m_verify_osd_isSet){
obj->insert("verifyOSD", QJsonValue(verify_osd));
}
if(m_enable_psk_reporter_isSet){
obj->insert("enablePSKReporter", QJsonValue(enable_psk_reporter));
}
if(psk_reporter_callsign != nullptr && *psk_reporter_callsign != QString("")){
toJsonValue(QString("pskReporterCallsign"), psk_reporter_callsign, obj, QString("QString"));
}
if(psk_reporter_locator != nullptr && *psk_reporter_locator != QString("")){
toJsonValue(QString("pskReporterLocator"), psk_reporter_locator, obj, QString("QString"));
}
if(psk_reporter_software != nullptr && *psk_reporter_software != QString("")){
toJsonValue(QString("pskReporterSoftware"), psk_reporter_software, obj, QString("QString"));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
@ -513,6 +559,46 @@ SWGFT8DemodSettings::setVerifyOsd(qint32 verify_osd) {
this->m_verify_osd_isSet = true;
}
qint32
SWGFT8DemodSettings::getEnablePskReporter() {
return enable_psk_reporter;
}
void
SWGFT8DemodSettings::setEnablePskReporter(qint32 enable_psk_reporter) {
this->enable_psk_reporter = enable_psk_reporter;
this->m_enable_psk_reporter_isSet = true;
}
QString*
SWGFT8DemodSettings::getPskReporterCallsign() {
return psk_reporter_callsign;
}
void
SWGFT8DemodSettings::setPskReporterCallsign(QString* psk_reporter_callsign) {
this->psk_reporter_callsign = psk_reporter_callsign;
this->m_psk_reporter_callsign_isSet = true;
}
QString*
SWGFT8DemodSettings::getPskReporterLocator() {
return psk_reporter_locator;
}
void
SWGFT8DemodSettings::setPskReporterLocator(QString* psk_reporter_locator) {
this->psk_reporter_locator = psk_reporter_locator;
this->m_psk_reporter_locator_isSet = true;
}
QString*
SWGFT8DemodSettings::getPskReporterSoftware() {
return psk_reporter_software;
}
void
SWGFT8DemodSettings::setPskReporterSoftware(QString* psk_reporter_software) {
this->psk_reporter_software = psk_reporter_software;
this->m_psk_reporter_software_isSet = true;
}
qint32
SWGFT8DemodSettings::getRgbColor() {
return rgb_color;
@ -676,6 +762,18 @@ SWGFT8DemodSettings::isSet(){
if(m_verify_osd_isSet){
isObjectUpdated = true; break;
}
if(m_enable_psk_reporter_isSet){
isObjectUpdated = true; break;
}
if(psk_reporter_callsign && *psk_reporter_callsign != QString("")){
isObjectUpdated = true; break;
}
if(psk_reporter_locator && *psk_reporter_locator != QString("")){
isObjectUpdated = true; break;
}
if(psk_reporter_software && *psk_reporter_software != QString("")){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}

View File

@ -93,6 +93,18 @@ public:
qint32 getVerifyOsd();
void setVerifyOsd(qint32 verify_osd);
qint32 getEnablePskReporter();
void setEnablePskReporter(qint32 enable_psk_reporter);
QString* getPskReporterCallsign();
void setPskReporterCallsign(QString* psk_reporter_callsign);
QString* getPskReporterLocator();
void setPskReporterLocator(QString* psk_reporter_locator);
QString* getPskReporterSoftware();
void setPskReporterSoftware(QString* psk_reporter_software);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
@ -178,6 +190,18 @@ private:
qint32 verify_osd;
bool m_verify_osd_isSet;
qint32 enable_psk_reporter;
bool m_enable_psk_reporter_isSet;
QString* psk_reporter_callsign;
bool m_psk_reporter_callsign_isSet;
QString* psk_reporter_locator;
bool m_psk_reporter_locator_isSet;
QString* psk_reporter_software;
bool m_psk_reporter_software_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;