From ee2badb86f4cf6057dfb3bc70d71178546b5a24b Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 26 Jul 2017 21:18:59 +0000 Subject: [PATCH] Use the low confidence decode quality marker to elide spots and pass info via UDP The UDP decode and reply message have been augmented with a boolean flag denoting a low confidence decode when set. Existing clients can safely use the reply message without passing the flag as the default value will still action messages that have high confidence. If low confidence decodes are to be passed back via the reply message then the low confidence flag must be included and correctly set to match the original decode. See NetworkMessage.hpp for message fields and meanings. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@7957 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- MessageClient.cpp | 11 ++++++--- MessageClient.hpp | 4 +-- MessageServer.cpp | 12 ++++++--- MessageServer.hpp | 5 ++-- NetworkMessage.hpp | 11 +++++++-- UDPExamples/ClientWidget.cpp | 2 +- UDPExamples/ClientWidget.hpp | 2 +- UDPExamples/DecodesModel.cpp | 27 +++++++++++++++------ UDPExamples/DecodesModel.hpp | 5 ++-- UDPExamples/MessageAggregatorMainWindow.cpp | 4 +-- UDPExamples/UDPDaemon.cpp | 4 +-- decodedtext.cpp | 5 ++++ decodedtext.h | 1 + mainwindow.cpp | 19 +++++++-------- 14 files changed, 72 insertions(+), 40 deletions(-) diff --git a/MessageClient.cpp b/MessageClient.cpp index ea657d329..ac820f096 100644 --- a/MessageClient.cpp +++ b/MessageClient.cpp @@ -151,11 +151,13 @@ void MessageClient::impl::parse_message (QByteArray const& msg) quint32 delta_frequency; QByteArray mode; QByteArray message; - in >> time >> snr >> delta_time >> delta_frequency >> mode >> message; + bool low_confidence {false}; + in >> time >> snr >> delta_time >> delta_frequency >> mode >> message >> low_confidence; if (check_status (in) != Fail) { Q_EMIT self_->reply (time, snr, delta_time, delta_frequency - , QString::fromUtf8 (mode), QString::fromUtf8 (message)); + , QString::fromUtf8 (mode), QString::fromUtf8 (message) + , low_confidence); } } break; @@ -366,13 +368,14 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con } void MessageClient::decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency - , QString const& mode, QString const& message_text) + , QString const& mode, QString const& message_text, bool low_confidence) { if (m_->server_port_ && !m_->server_string_.isEmpty ()) { QByteArray message; NetworkMessage::Builder out {&message, NetworkMessage::Decode, m_->id_, m_->schema_}; - out << is_new << time << snr << delta_time << delta_frequency << mode.toUtf8 () << message_text.toUtf8 (); + out << is_new << time << snr << delta_time << delta_frequency << mode.toUtf8 () + << message_text.toUtf8 () << low_confidence; m_->send_message (out, message); } } diff --git a/MessageClient.hpp b/MessageClient.hpp index 9807c4d2f..0d3dd47ff 100644 --- a/MessageClient.hpp +++ b/MessageClient.hpp @@ -53,7 +53,7 @@ public: , QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode , bool fast_mode); Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency - , QString const& mode, QString const& message); + , QString const& mode, QString const& message, bool low_confidence); Q_SLOT void WSPR_decode (bool is_new, QTime time, qint32 snr, float delta_time, Frequency , qint32 drift, QString const& callsign, QString const& grid, qint32 power); Q_SLOT void clear_decodes (); @@ -70,7 +70,7 @@ public: // this signal is emitted if the server sends us a reply, the only // reply supported is reply to a prior CQ or QRZ message Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode - , QString const& message_text); + , QString const& message_text, bool low_confidence); // this signal is emitted if the server has requested a replay of // all decodes diff --git a/MessageServer.cpp b/MessageServer.cpp index e73203a40..caad76a9f 100644 --- a/MessageServer.cpp +++ b/MessageServer.cpp @@ -241,11 +241,14 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s quint32 delta_frequency; QByteArray mode; QByteArray message; - in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode >> message; + bool low_confidence; + in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode + >> message >> low_confidence; if (check_status (in) != Fail) { Q_EMIT self_->decode (is_new, id, time, snr, delta_time, delta_frequency - , QString::fromUtf8 (mode), QString::fromUtf8 (message)); + , QString::fromUtf8 (mode), QString::fromUtf8 (message) + , low_confidence); } } break; @@ -396,14 +399,15 @@ void MessageServer::start (port_type port, QHostAddress const& multicast_group_a } } -void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text) +void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text, bool low_confidence) { auto iter = m_->clients_.find (id); if (iter != std::end (m_->clients_)) { QByteArray message; NetworkMessage::Builder out {&message, NetworkMessage::Reply, id, (*iter).negotiated_schema_number_}; - out << time << snr << delta_time << delta_frequency << mode.toUtf8 () << message_text.toUtf8 (); + out << time << snr << delta_time << delta_frequency << mode.toUtf8 () + << message_text.toUtf8 () << low_confidence; m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); } } diff --git a/MessageServer.hpp b/MessageServer.hpp index 7338cbe4e..9a80aa0e7 100644 --- a/MessageServer.hpp +++ b/MessageServer.hpp @@ -46,7 +46,7 @@ public: // note that the client is not obliged to take any action and only // takes any action if the decode is present and is a CQ or QRZ message Q_SLOT void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency - , QString const& mode, QString const& message); + , QString const& mode, QString const& message, bool low_confidence); // ask the client with identification 'id' to replay all decodes Q_SLOT void replay (QString const& id); @@ -69,7 +69,8 @@ public: , bool watchdog_timeout, QString const& sub_mode, bool fast_mode); Q_SIGNAL void client_closed (QString const& id); Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time - , quint32 delta_frequency, QString const& mode, QString const& message); + , quint32 delta_frequency, QString const& mode, QString const& message + , bool low_confidence); Q_SIGNAL void WSPR_decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time, Frequency , qint32 drift, QString const& callsign, QString const& grid, qint32 power); Q_SIGNAL void qso_logged (QString const& id, QDateTime timeOff, QString const& dx_call, QString const& dx_grid diff --git a/NetworkMessage.hpp b/NetworkMessage.hpp index 00664b126..b248e40de 100644 --- a/NetworkMessage.hpp +++ b/NetworkMessage.hpp @@ -156,13 +156,19 @@ * Delta frequency (Hz) quint32 * Mode utf8 * Message utf8 + * Low confidence bool * * The decode message is sent when a new decode is completed, in * this case the 'New' field is true. It is also used in response * to a "Replay" message where each old decode in the "Band * activity" window, that has not been erased, is sent in order - * as a one of these messages with the 'New' field set to - * false. See the "Replay" message below for details of usage. + * as a one of these messages with the 'New' field set to false. + * See the "Replay" message below for details of usage. Low + * confidence decodes are flagged in protocols where the decoder + * has knows that a decode has a higher than normal probability + * of being false, they should not be reported on publicly + * accessible services without some attached warning or further + * validation. * * * Clear Out 3 quint32 @@ -184,6 +190,7 @@ * Delta frequency (Hz) quint32 * Mode utf8 * Message utf8 + * Low confidence bool * * In order for a server to provide a useful cooperative service * to WSJT-X it is possible for it to initiate a QSO by sending diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp index 3ff1a561f..d24c6d697 100644 --- a/UDPExamples/ClientWidget.cpp +++ b/UDPExamples/ClientWidget.cpp @@ -244,7 +244,7 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const& void ClientWidget::decode_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/ , float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/ - , QString const& /*message*/) + , QString const& /*message*/, bool /*low_confidence*/) { if (client_id == id_) { diff --git a/UDPExamples/ClientWidget.hpp b/UDPExamples/ClientWidget.hpp index 1e7779c87..8643df705 100644 --- a/UDPExamples/ClientWidget.hpp +++ b/UDPExamples/ClientWidget.hpp @@ -33,7 +33,7 @@ public: , bool watchdog_timeout, QString const& sub_mode, bool fast_mode); Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr , float delta_time, quint32 delta_frequency, QString const& mode - , QString const& message); + , QString const& message, bool low_confidence); Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime, qint32 snr , float delta_time, Frequency delta_frequency, qint32 drift , QString const& callsign, QString const& grid, qint32 power); diff --git a/UDPExamples/DecodesModel.cpp b/UDPExamples/DecodesModel.cpp index 72ab4b61f..3ca94dde1 100644 --- a/UDPExamples/DecodesModel.cpp +++ b/UDPExamples/DecodesModel.cpp @@ -17,13 +17,19 @@ namespace QT_TRANSLATE_NOOP ("DecodesModel", "DF"), QT_TRANSLATE_NOOP ("DecodesModel", "Md"), QT_TRANSLATE_NOOP ("DecodesModel", "Message"), + QT_TRANSLATE_NOOP ("DecodesModel", "Confidence"), }; + QString confidence_string (bool low_confidence) + { + return low_confidence ? QT_TRANSLATE_NOOP ("DecodesModel", "low") : QT_TRANSLATE_NOOP ("DecodesModel", "high"); + } + QFont text_font {"Courier", 10}; QList make_row (QString const& client_id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode, QString const& message - , bool is_fast) + , bool low_confidence, bool is_fast) { auto time_item = new QStandardItem {time.toString (is_fast || "~" == mode ? "hh:mm:ss" : "hh:mm")}; time_item->setData (time); @@ -44,8 +50,11 @@ namespace auto md = new QStandardItem {mode}; md->setTextAlignment (Qt::AlignHCenter); + auto confidence = new QStandardItem {confidence_string (low_confidence)}; + confidence->setTextAlignment (Qt::AlignHCenter); + QList row { - new QStandardItem {client_id}, time_item, snr_item, dt, df, md, new QStandardItem {message}}; + new QStandardItem {client_id}, time_item, snr_item, dt, df, md, new QStandardItem {message}, confidence}; Q_FOREACH (auto& item, row) { item->setEditable (false); @@ -57,7 +66,7 @@ namespace } DecodesModel::DecodesModel (QObject * parent) - : QStandardItemModel {0, 7, parent} + : QStandardItemModel {0, sizeof (headings) / sizeof (headings[0]), parent} { int column {0}; for (auto const& heading : headings) @@ -68,7 +77,7 @@ DecodesModel::DecodesModel (QObject * parent) void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode, QString const& message - , bool is_fast) + , bool low_confidence, bool is_fast) { if (!is_new) { @@ -83,7 +92,8 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time && item (row, 3)->data ().toFloat () == delta_time && item (row, 4)->data ().toUInt () == delta_frequency && data (index (row, 5)).toString () == mode - && data (index (row, 6)).toString () == message) + && data (index (row, 6)).toString () == message + && data (index (row, 7)).toString () == confidence_string (low_confidence)) { return; } @@ -96,12 +106,12 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time if (target_row >= 0) { insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, delta_frequency, mode - , message, is_fast)); + , message, low_confidence, is_fast)); return; } } - appendRow (make_row (client_id, time, snr, delta_time, delta_frequency, mode, message, is_fast)); + appendRow (make_row (client_id, time, snr, delta_time, delta_frequency, mode, message, low_confidence, is_fast)); } void DecodesModel::clear_decodes (QString const& client_id) @@ -124,7 +134,8 @@ void DecodesModel::do_reply (QModelIndex const& source) , item (row, 3)->data ().toFloat () , item (row, 4)->data ().toInt () , data (index (row, 5)).toString () - , data (index (row, 6)).toString ()); + , data (index (row, 6)).toString () + , confidence_string (true) == data (index (row, 7)).toString ()); } #include "moc_DecodesModel.cpp" diff --git a/UDPExamples/DecodesModel.hpp b/UDPExamples/DecodesModel.hpp index 03eaab39f..43bfd679d 100644 --- a/UDPExamples/DecodesModel.hpp +++ b/UDPExamples/DecodesModel.hpp @@ -32,12 +32,13 @@ public: explicit DecodesModel (QObject * parent = nullptr); Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time - , quint32 delta_frequency, QString const& mode, QString const& message, bool is_fast); + , quint32 delta_frequency, QString const& mode, QString const& message + , bool low_confidence, bool is_fast); Q_SLOT void clear_decodes (QString const& client_id); Q_SLOT void do_reply (QModelIndex const& source); Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency - , QString const& mode, QString const& message); + , QString const& mode, QString const& message, bool low_confidence); }; #endif diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp index 2cdb27015..ecb364141 100644 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ b/UDPExamples/MessageAggregatorMainWindow.cpp @@ -91,9 +91,9 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time , qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode - , QString const& message) { + , QString const& message, bool low_confidence) { decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message - , dock_widgets_[id]->fast_mode ());}); + , low_confidence, dock_widgets_[id]->fast_mode ());}); connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot); connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes); connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes); diff --git a/UDPExamples/UDPDaemon.cpp b/UDPExamples/UDPDaemon.cpp index 14a531bbf..7afc3d2f9 100644 --- a/UDPExamples/UDPDaemon.cpp +++ b/UDPExamples/UDPDaemon.cpp @@ -69,13 +69,13 @@ public: Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime time, qint32 snr , float delta_time, quint32 delta_frequency, QString const& mode - , QString const& message) + , QString const& message, bool low_confidence) { if (client_id == id_) { qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr << "Dt:" << delta_time << "Df:" << delta_frequency - << "mode:" << mode; + << "mode:" << mode << "Confidence:" << (low_confidence ? "low" : "high"); std::cout << tr ("%1: Decoded %2").arg (id_).arg (message).toStdString () << std::endl; } } diff --git a/decodedtext.cpp b/decodedtext.cpp index a44f697f4..e81ea1d39 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -56,6 +56,11 @@ bool DecodedText::isTX() return (i >= 0 && i < 15); // TODO guessing those numbers. Does Tx ever move? } +bool DecodedText::isLowConfidence () +{ + return QChar {'?'} == _string.mid (padding_ + column_qsoText + 21, 1); +} + int DecodedText::frequencyOffset() { return _string.mid(column_freq + padding_,4).toInt(); diff --git a/decodedtext.h b/decodedtext.h index 06fd28c9b..952120932 100644 --- a/decodedtext.h +++ b/decodedtext.h @@ -57,6 +57,7 @@ public: bool isJT65(); bool isJT9(); bool isTX(); + bool isLowConfidence (); int frequencyOffset(); // hertz offset from the tuned dial or rx frequency, aka audio frequency int snr(); float dt(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 516c89ca4..bcf132d21 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1440,7 +1440,7 @@ void MainWindow::fastSink(qint64 frames) bool stdMsg = decodedtext.report(m_baseCall, Radio::base_callsign(ui->dxCallEntry->text()),m_rptRcvd); decodedtext=message.mid(0,4) + message.mid(6,-1); - if(m_config.spot_to_psk_reporter() and stdMsg and !m_diskData) pskPost(decodedtext); + if (stdMsg) pskPost (decodedtext); } float fracTR=float(k)/(12000.0*m_TRperiod); @@ -2593,9 +2593,7 @@ void::MainWindow::fast_decode_done() Radio::base_callsign(ui->dxCallEntry->text()), m_rptRcvd); // extract details and send to PSKreporter - if(m_config.spot_to_psk_reporter() and stdMsg and !m_diskData) { - pskPost(decodedtext); - } + if (stdMsg) pskPost(decodedtext); } } m_startAnother=m_loopall; @@ -2774,9 +2772,7 @@ void MainWindow::readFromStdout() //readFromStdout // extract details and send to PSKreporter int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged; bool okToPost=(nsec>(4*m_TRperiod)/5); - if(m_config.spot_to_psk_reporter () and stdMsg and !m_diskData and okToPost) { - pskPost(decodedtext); - } + if (stdMsg && okToPost) pskPost(decodedtext); if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64") and m_msgAvgWidget!=NULL) { if(m_msgAvgWidget->isVisible()) { @@ -2830,8 +2826,10 @@ void MainWindow::auto_sequence (QString const& message, unsigned tolerance) } } -void MainWindow::pskPost(DecodedText decodedtext) +void MainWindow::pskPost (DecodedText decodedtext) { + if (m_diskData || !m_config.spot_to_psk_reporter() || decodedtext.isLowConfidence ()) return; + QString msgmode=m_mode; if(m_mode=="JT9+JT65") { msgmode="JT9"; @@ -6157,14 +6155,15 @@ void MainWindow::postDecode (bool is_new, QString const& message) { auto const& decode = message.trimmed (); auto const& parts = decode.left (22).split (' ', QString::SkipEmptyParts); - if (parts.size () >= 5) + if (!m_diskData && parts.size () >= 5) { auto has_seconds = parts[0].size () > 4; m_messageClient->decode (is_new , QTime::fromString (parts[0], has_seconds ? "hhmmss" : "hhmm") , parts[1].toInt () , parts[2].toFloat (), parts[3].toUInt (), parts[4][0] - , decode.mid (has_seconds ? 24 : 22)); + , decode.mid (has_seconds ? 24 : 22, 21) + , QChar {'?'} == decode.mid (has_seconds ? 24 + 21 : 22 + 21, 1)); } }