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)); } }