FT8 demod: OSD demodulation callsign verification

pull/1583/head
f4exb 2023-01-29 19:27:45 +01:00
rodzic f63f1270ba
commit 2299e5d115
18 zmienionych plików z 311 dodań i 7 usunięć

Wyświetl plik

@ -384,6 +384,7 @@ std::string Packing::unpack_5(int a77[], std::string& call1str, std::string& cal
ocall = "<...22>";
}
hashes_mu.unlock();
call2str = std::string(ocall);
// mext bit is alway for R

Wyświetl plik

@ -24,7 +24,7 @@
#include <string>
#include <map>
#include <QRecursiveMutex>
#include <QMutex>
#include "export.h"
@ -52,7 +52,7 @@ private:
std::string unpack_4(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
std::string unpack_5(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
QRecursiveMutex hashes_mu;
QMutex hashes_mu;
std::map<int, std::string> hashes10;
std::map<int, std::string> hashes12;
std::map<int, std::string> hashes22;

Wyświetl plik

@ -314,6 +314,18 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force)
if ((m_settings.m_decoderTimeBudget != settings.m_decoderTimeBudget) || force) {
reverseAPIKeys.append("decoderTimeBudget");
}
if ((m_settings.m_useOSD != settings.m_useOSD) || force) {
reverseAPIKeys.append("useOSD");
}
if ((m_settings.m_osdDepth != settings.m_osdDepth) || force) {
reverseAPIKeys.append("osdDepth");
}
if ((m_settings.m_osdLDPCThreshold != settings.m_osdLDPCThreshold) || force) {
reverseAPIKeys.append("osdLDPCThreshold");
}
if ((m_settings.m_verifyOSD != settings.m_verifyOSD) || force) {
reverseAPIKeys.append("verifyOSD");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
@ -494,6 +506,18 @@ void FT8Demod::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("decoderTimeBudget")) {
settings.m_decoderTimeBudget = response.getFt8DemodSettings()->getDecoderTimeBudget();
}
if (channelSettingsKeys.contains("useOSD")) {
settings.m_useOSD = response.getFt8DemodSettings()->getUseOsd() != 0;
}
if (channelSettingsKeys.contains("osdDepth")) {
settings.m_osdDepth = response.getFt8DemodSettings()->getOsdDepth();
}
if (channelSettingsKeys.contains("osdLDPCThreshold")) {
settings.m_osdLDPCThreshold = response.getFt8DemodSettings()->getOsdLdpcThreshold();
}
if (channelSettingsKeys.contains("verifyOSD")) {
settings.m_verifyOSD = response.getFt8DemodSettings()->getVerifyOsd() != 0;
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFt8DemodSettings()->getRgbColor();
}
@ -554,6 +578,10 @@ void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp
response.getFt8DemodSettings()->setLogMessages(settings.m_logMessages ? 1 : 0);
response.getFt8DemodSettings()->setNbDecoderThreads(settings.m_nbDecoderThreads);
response.getFt8DemodSettings()->setDecoderTimeBudget(settings.m_decoderTimeBudget);
response.getFt8DemodSettings()->setUseOsd(settings.m_useOSD ? 1 : 0);
response.getFt8DemodSettings()->setOsdDepth(settings.m_osdDepth);
response.getFt8DemodSettings()->setOsdLdpcThreshold(settings.m_osdLDPCThreshold);
response.getFt8DemodSettings()->setUseOsd(settings.m_verifyOSD ? 1 : 0);
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getFt8DemodSettings()->getTitle()) {
@ -737,6 +765,18 @@ void FT8Demod::webapiFormatChannelSettings(
if (channelSettingsKeys.contains("decoderTimeBudget") || force) {
swgFT8DemodSettings->setDecoderTimeBudget(settings.m_decoderTimeBudget);
}
if (channelSettingsKeys.contains("useOSD") || force) {
swgFT8DemodSettings->setUseOsd(settings.m_useOSD ? 1 : 0);
}
if (channelSettingsKeys.contains("osdDepth") || force) {
swgFT8DemodSettings->setOsdDepth(settings.m_osdDepth);
}
if (channelSettingsKeys.contains("osdLDPCThreshold") || force) {
swgFT8DemodSettings->setOsdLdpcThreshold(settings.m_osdLDPCThreshold);
}
if (channelSettingsKeys.contains("verifyOSD") || force) {
swgFT8DemodSettings->setVerifyOsd(settings.m_verifyOSD ? 1 : 0);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFT8DemodSettings->setRgbColor(settings.m_rgbColor);
}

Wyświetl plik

@ -257,6 +257,10 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc
m_ft8DemodWorker->setOSDLDPCThreshold(settings.m_osdLDPCThreshold);
}
if ((settings.m_verifyOSD != m_settings.m_verifyOSD) || force) {
m_ft8DemodWorker->setVerifyOSD(settings.m_verifyOSD);
}
m_sink.applySettings(settings, force);
m_settings = settings;
}

Wyświetl plik

@ -502,6 +502,12 @@ void FT8DemodGUI::on_settings_clicked()
changed = true;
}
if (settingsKeys.contains("verifyOSD"))
{
m_settings.m_verifyOSD = settings.m_verifyOSD;
changed = true;
}
if (settingsKeys.contains("bandPresets"))
{
m_settings.m_bandPresets = settings.m_bandPresets;

Wyświetl plik

@ -50,6 +50,7 @@ void FT8DemodSettings::resetToDefaults()
m_useOSD = false;
m_osdDepth = 0;
m_osdLDPCThreshold = 70;
m_verifyOSD = false;
m_volume = 1.0;
m_inputFrequencyOffset = 0;
m_rgbColor = QColor(0, 192, 255).rgb();
@ -113,6 +114,7 @@ QByteArray FT8DemodSettings::serialize() const
s.writeBool(12, m_useOSD);
s.writeS32(13, m_osdDepth);
s.writeS32(14, m_osdLDPCThreshold);
s.writeBool(15, m_verifyOSD);
s.writeString(16, m_title);
s.writeBool(18, m_useReverseAPI);
s.writeString(19, m_reverseAPIAddress);
@ -181,6 +183,7 @@ bool FT8DemodSettings::deserialize(const QByteArray& data)
d.readBool(12, &m_useOSD, false);
d.readS32(13, &m_osdDepth, 0);
d.readS32(14, &m_osdLDPCThreshold, 70);
d.readBool(15, &m_verifyOSD, false);
d.readString(16, &m_title, "SSB Demodulator");
d.readBool(18, &m_useReverseAPI, false);
d.readString(19, &m_reverseAPIAddress, "127.0.0.1");

Wyświetl plik

@ -76,6 +76,7 @@ struct FT8DemodSettings
bool m_useOSD;
int m_osdDepth;
int m_osdLDPCThreshold;
bool m_verifyOSD;
quint32 m_rgbColor;
QString m_title;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).

Wyświetl plik

@ -34,6 +34,7 @@ FT8DemodSettingsDialog::FT8DemodSettingsDialog(FT8DemodSettings& settings, QStri
ui->osdDepthText->setText(tr("%1").arg(m_settings.m_osdDepth));
ui->osdLDPCThreshold->setValue(m_settings.m_osdLDPCThreshold);
ui->osdLDPCThresholdText->setText(tr("%1").arg(m_settings.m_osdLDPCThreshold));
ui->verifyOSD->setChecked(m_settings.m_verifyOSD);
resizeBandsTable();
populateBandsTable();
connect(ui->bands, &QTableWidget::cellChanged, this, &FT8DemodSettingsDialog::textCellChanged);
@ -152,6 +153,15 @@ void FT8DemodSettingsDialog::on_osdLDPCThreshold_valueChanged(int value)
}
}
void FT8DemodSettingsDialog::on_verifyOSD_stateChanged(int state)
{
m_settings.m_verifyOSD = state == Qt::Checked;
if (!m_settingsKeys.contains("verifyOSD")) {
m_settingsKeys.append("verifyOSD");
}
}
void FT8DemodSettingsDialog::on_addBand_clicked()
{
int currentRow = ui->bands->currentRow();

Wyświetl plik

@ -54,6 +54,7 @@ private slots:
void on_osdEnable_toggled(bool checked);
void on_osdDepth_valueChanged(int value);
void on_osdLDPCThreshold_valueChanged(int value);
void on_verifyOSD_stateChanged(int state);
void on_addBand_clicked();
void on_deleteBand_clicked();
void on_moveBandUp_clicked();

Wyświetl plik

@ -198,6 +198,19 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="verifyOSD">
<property name="toolTip">
<string>Verify OSD decoded message against valid callsigns</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Vfy</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">

Wyświetl plik

@ -18,6 +18,7 @@
#include <QStandardPaths>
#include <QDir>
#include <QDateTime>
#include <QMutableListIterator>
#include "channel/channelapi.h"
#include "dsp/wavfilerecord.h"
@ -39,7 +40,8 @@ FT8DemodWorker::FT8Callback::FT8Callback(
m_packing(packing),
m_periodTS(periodTS),
m_baseFrequency(baseFrequency),
m_name(name)
m_name(name),
m_validCallsigns(nullptr)
{
m_msgReportFT8Messages = MsgReportFT8Messages::create();
m_msgReportFT8Messages->setBaseFrequency(baseFrequency);
@ -71,6 +73,9 @@ int FT8DemodWorker::FT8Callback::hcb(
}
cycle_already[msg] = true;
QString info(comment);
QString call2QStr(call2.c_str());
QList<FT8Message>& ft8Messages = m_msgReportFT8Messages->getFT8Messages();
FT8Message baseMessage{
m_periodTS,
@ -81,9 +86,9 @@ int FT8DemodWorker::FT8Callback::hcb(
off - 0.5f,
hz0,
QString(call1.c_str()).simplified(),
QString(call2.c_str()).simplified(),
call2QStr,
QString(loc.c_str()).simplified(),
QString(comment)
info
};
// DXpedition packs two messages in one with the two callees in the first call area separated by a semicolon
@ -135,6 +140,7 @@ FT8DemodWorker::FT8DemodWorker() :
m_useOSD(false),
m_osdDepth(0),
m_osdLDPCThreshold(70),
m_verifyOSD(false),
m_lowFreq(200),
m_highFreq(3000),
m_invalidSequence(true),
@ -181,6 +187,7 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
int hints[2] = { 2, 0 }; // CQ
FT8Callback ft8Callback(periodTS, m_baseFrequency, m_packing, channelReference);
ft8Callback.setValidCallsigns((m_useOSD && m_verifyOSD) ? &m_validCallsigns : nullptr);
m_ft8Decoder.getParams().nthreads = m_nbDecoderThreads;
m_ft8Decoder.getParams().use_osd = m_useOSD ? 1 : 0;
m_ft8Decoder.getParams().osd_depth = m_osdDepth;
@ -214,6 +221,20 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
qDebug("FT8DemodWorker::processBuffer: done: at %6.3f %d messages",
m_baseFrequency / 1000000.0, ft8Callback.getReportMessage()->getFT8Messages().size());
if (m_useOSD && m_verifyOSD)
{
QMutableListIterator<FT8Message> i(ft8Callback.getReportMessage()->getFT8Messages());
while (i.hasNext())
{
const auto& ft8Message = i.next();
if (ft8Message.decoderInfo.startsWith("OSD") && !m_validCallsigns.contains(ft8Message.call2)) {
i.remove();
}
}
}
if (m_reportingMessageQueue) {
m_reportingMessageQueue->push(new MsgReportFT8Messages(*ft8Callback.getReportMessage()));
}
@ -298,6 +319,20 @@ void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
}
}
}
if (m_verifyOSD && !ft8Message.decoderInfo.startsWith("OSD"))
{
if ((ft8Message.type == "1") || (ft8Message.type == "2"))
{
if (!ft8Message.call2.startsWith("<")) {
m_validCallsigns.insert(ft8Message.call2);
}
if (!ft8Message.call1.startsWith("CQ") && !ft8Message.call1.startsWith("<")) {
m_validCallsigns.insert(ft8Message.call1);
}
}
}
}
delete ft8Callback.getReportMessage();

Wyświetl plik

@ -19,6 +19,7 @@
#define INCLUDE_FT8DEMODWORKER_H
#include <QObject>
#include <QSet>
#include "ft8.h"
#include "unpack.h"
@ -43,6 +44,7 @@ public:
void setUseOSD(bool useOSD) { m_useOSD = useOSD; }
void setOSDDepth(int osdDepth) { m_osdDepth = osdDepth; }
void setOSDLDPCThreshold(int osdLDPCThreshold) { m_osdLDPCThreshold = osdLDPCThreshold; }
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; }
@ -72,6 +74,7 @@ private:
virtual QString get_name();
const std::map<std::string, bool>& getMsgMap() { return cycle_already; }
MsgReportFT8Messages *getReportMessage() { return m_msgReportFT8Messages; }
void setValidCallsigns(const QSet<QString> *validCallsigns) { m_validCallsigns = validCallsigns; }
private:
QMutex cycle_mu;
@ -81,6 +84,7 @@ private:
const QDateTime& m_periodTS;
qint64 m_baseFrequency;
QString m_name;
const QSet<QString> *m_validCallsigns;
};
QString m_samplesPath;
@ -92,6 +96,7 @@ private:
bool m_useOSD;
int m_osdDepth;
int m_osdLDPCThreshold;
bool m_verifyOSD;
int m_lowFreq;
int m_highFreq;
bool m_invalidSequence;
@ -100,6 +105,7 @@ private:
FT8::Packing m_packing;
MessageQueue *m_reportingMessageQueue;
ChannelAPI *m_channel;
QSet<QString> m_validCallsigns;
};
#endif // INCLUDE_FT8DEMODWORKER_H

Wyświetl plik

@ -5627,6 +5627,22 @@ margin-bottom: 20px;
"type" : "number",
"format" : "float"
},
"useOSD" : {
"type" : "integer",
"description" : "Use Ordered Statistics Decoding (OSD) to decode messages if some LDPC bits are invalid\n * 0 - Disable\n * 1 - Enable\n"
},
"osdDepth" : {
"type" : "integer",
"description" : "OSD depth (recommended between 0 and 6)"
},
"osdLDPCThreshold" : {
"type" : "integer",
"description" : "minimum of valid LDPC bits (out of 83) necessary to activate OSD"
},
"verifyOSD" : {
"type" : "integer",
"description" : "Verify OSD decoded message against a list of validated callsigns\n * 0 - Disable\n * 1 - Enable\n"
},
"rgbColor" : {
"type" : "integer"
},
@ -56889,7 +56905,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-01-19T23:18:51.935+01:00
Generated 2023-01-28T23:27:13.480+01:00
</div>
</div>
</div>

Wyświetl plik

@ -52,6 +52,24 @@ FT8DemodSettings:
type: number
format: float
desctiption: Decoder time budget in seconds (will stop after running this time)
useOSD:
type: integer
description: >
Use Ordered Statistics Decoding (OSD) to decode messages if some LDPC bits are invalid
* 0 - Disable
* 1 - Enable
osdDepth:
type: integer
description: OSD depth (recommended between 0 and 6)
osdLDPCThreshold:
type: integer
description: minimum of valid LDPC bits (out of 83) necessary to activate OSD
verifyOSD:
type: integer
description: >
Verify OSD decoded message against a list of validated callsigns
* 0 - Disable
* 1 - Enable
rgbColor:
type: integer
title:

Wyświetl plik

@ -52,6 +52,24 @@ FT8DemodSettings:
type: number
format: float
desctiption: Decoder time budget in seconds (will stop after running this time)
useOSD:
type: integer
description: >
Use Ordered Statistics Decoding (OSD) to decode messages if some LDPC bits are invalid
* 0 - Disable
* 1 - Enable
osdDepth:
type: integer
description: OSD depth (recommended between 0 and 6)
osdLDPCThreshold:
type: integer
description: minimum of valid LDPC bits (out of 83) necessary to activate OSD
verifyOSD:
type: integer
description: >
Verify OSD decoded message against a list of validated callsigns
* 0 - Disable
* 1 - Enable
rgbColor:
type: integer
title:

Wyświetl plik

@ -5627,6 +5627,22 @@ margin-bottom: 20px;
"type" : "number",
"format" : "float"
},
"useOSD" : {
"type" : "integer",
"description" : "Use Ordered Statistics Decoding (OSD) to decode messages if some LDPC bits are invalid\n * 0 - Disable\n * 1 - Enable\n"
},
"osdDepth" : {
"type" : "integer",
"description" : "OSD depth (recommended between 0 and 6)"
},
"osdLDPCThreshold" : {
"type" : "integer",
"description" : "minimum of valid LDPC bits (out of 83) necessary to activate OSD"
},
"verifyOSD" : {
"type" : "integer",
"description" : "Verify OSD decoded message against a list of validated callsigns\n * 0 - Disable\n * 1 - Enable\n"
},
"rgbColor" : {
"type" : "integer"
},
@ -56889,7 +56905,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-01-19T23:18:51.935+01:00
Generated 2023-01-28T23:27:13.480+01:00
</div>
</div>
</div>

Wyświetl plik

@ -52,6 +52,14 @@ SWGFT8DemodSettings::SWGFT8DemodSettings() {
m_nb_decoder_threads_isSet = false;
decoder_time_budget = 0.0f;
m_decoder_time_budget_isSet = false;
use_osd = 0;
m_use_osd_isSet = false;
osd_depth = 0;
m_osd_depth_isSet = false;
osd_ldpc_threshold = 0;
m_osd_ldpc_threshold_isSet = false;
verify_osd = 0;
m_verify_osd_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
@ -106,6 +114,14 @@ SWGFT8DemodSettings::init() {
m_nb_decoder_threads_isSet = false;
decoder_time_budget = 0.0f;
m_decoder_time_budget_isSet = false;
use_osd = 0;
m_use_osd_isSet = false;
osd_depth = 0;
m_osd_depth_isSet = false;
osd_ldpc_threshold = 0;
m_osd_ldpc_threshold_isSet = false;
verify_osd = 0;
m_verify_osd_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
@ -145,6 +161,10 @@ SWGFT8DemodSettings::cleanup() {
if(title != nullptr) {
delete title;
}
@ -202,6 +222,14 @@ SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&decoder_time_budget, pJson["decoderTimeBudget"], "float", "");
::SWGSDRangel::setValue(&use_osd, pJson["useOSD"], "qint32", "");
::SWGSDRangel::setValue(&osd_depth, pJson["osdDepth"], "qint32", "");
::SWGSDRangel::setValue(&osd_ldpc_threshold, pJson["osdLDPCThreshold"], "qint32", "");
::SWGSDRangel::setValue(&verify_osd, pJson["verifyOSD"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
@ -276,6 +304,18 @@ SWGFT8DemodSettings::asJsonObject() {
if(m_decoder_time_budget_isSet){
obj->insert("decoderTimeBudget", QJsonValue(decoder_time_budget));
}
if(m_use_osd_isSet){
obj->insert("useOSD", QJsonValue(use_osd));
}
if(m_osd_depth_isSet){
obj->insert("osdDepth", QJsonValue(osd_depth));
}
if(m_osd_ldpc_threshold_isSet){
obj->insert("osdLDPCThreshold", QJsonValue(osd_ldpc_threshold));
}
if(m_verify_osd_isSet){
obj->insert("verifyOSD", QJsonValue(verify_osd));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
@ -433,6 +473,46 @@ SWGFT8DemodSettings::setDecoderTimeBudget(float decoder_time_budget) {
this->m_decoder_time_budget_isSet = true;
}
qint32
SWGFT8DemodSettings::getUseOsd() {
return use_osd;
}
void
SWGFT8DemodSettings::setUseOsd(qint32 use_osd) {
this->use_osd = use_osd;
this->m_use_osd_isSet = true;
}
qint32
SWGFT8DemodSettings::getOsdDepth() {
return osd_depth;
}
void
SWGFT8DemodSettings::setOsdDepth(qint32 osd_depth) {
this->osd_depth = osd_depth;
this->m_osd_depth_isSet = true;
}
qint32
SWGFT8DemodSettings::getOsdLdpcThreshold() {
return osd_ldpc_threshold;
}
void
SWGFT8DemodSettings::setOsdLdpcThreshold(qint32 osd_ldpc_threshold) {
this->osd_ldpc_threshold = osd_ldpc_threshold;
this->m_osd_ldpc_threshold_isSet = true;
}
qint32
SWGFT8DemodSettings::getVerifyOsd() {
return verify_osd;
}
void
SWGFT8DemodSettings::setVerifyOsd(qint32 verify_osd) {
this->verify_osd = verify_osd;
this->m_verify_osd_isSet = true;
}
qint32
SWGFT8DemodSettings::getRgbColor() {
return rgb_color;
@ -584,6 +664,18 @@ SWGFT8DemodSettings::isSet(){
if(m_decoder_time_budget_isSet){
isObjectUpdated = true; break;
}
if(m_use_osd_isSet){
isObjectUpdated = true; break;
}
if(m_osd_depth_isSet){
isObjectUpdated = true; break;
}
if(m_osd_ldpc_threshold_isSet){
isObjectUpdated = true; break;
}
if(m_verify_osd_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}

Wyświetl plik

@ -81,6 +81,18 @@ public:
float getDecoderTimeBudget();
void setDecoderTimeBudget(float decoder_time_budget);
qint32 getUseOsd();
void setUseOsd(qint32 use_osd);
qint32 getOsdDepth();
void setOsdDepth(qint32 osd_depth);
qint32 getOsdLdpcThreshold();
void setOsdLdpcThreshold(qint32 osd_ldpc_threshold);
qint32 getVerifyOsd();
void setVerifyOsd(qint32 verify_osd);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
@ -154,6 +166,18 @@ private:
float decoder_time_budget;
bool m_decoder_time_budget_isSet;
qint32 use_osd;
bool m_use_osd_isSet;
qint32 osd_depth;
bool m_osd_depth_isSet;
qint32 osd_ldpc_threshold;
bool m_osd_ldpc_threshold_isSet;
qint32 verify_osd;
bool m_verify_osd_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;