From b48b23450d7d5e07fcda37c1151db472cd68dd2b Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 21 Jun 2020 01:36:30 +0100 Subject: [PATCH 1/6] First iteration of optional TCP/IP client for PSK Reporter --- CMakeLists.txt | 2 +- Configuration.cpp | 6 + Configuration.hpp | 1 + Configuration.ui | 33 ++- Network/MessageClient.cpp | 60 ++--- Network/MessageClient.hpp | 9 - Network/PSK_Reporter.cpp | 495 ++++++++++++++++++++++++++++++++++++++ Network/PSK_Reporter.hpp | 41 ++++ Network/psk_reporter.cpp | 140 ----------- Network/psk_reporter.h | 54 ----- main.cpp | 8 +- widgets/mainwindow.cpp | 14 +- widgets/mainwindow.h | 5 +- wsjtx.pro | 4 +- 14 files changed, 598 insertions(+), 274 deletions(-) create mode 100644 Network/PSK_Reporter.cpp create mode 100644 Network/PSK_Reporter.hpp delete mode 100644 Network/psk_reporter.cpp delete mode 100644 Network/psk_reporter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ca25c3a08..8690c562f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -302,7 +302,7 @@ set (jt9_FSRCS set (wsjtx_CXXSRCS logbook/logbook.cpp - Network/psk_reporter.cpp + Network/PSK_Reporter.cpp Modulator/Modulator.cpp Detector/Detector.cpp widgets/logqso.cpp diff --git a/Configuration.cpp b/Configuration.cpp index 4afa9dfe7..c27c5591f 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -604,6 +604,7 @@ private: bool id_after_73_; bool tx_QSY_allowed_; bool spot_to_psk_reporter_; + bool psk_reporter_tcpip_; bool monitor_off_at_startup_; bool monitor_last_used_; bool log_as_RTTY_; @@ -701,6 +702,7 @@ bool Configuration::spot_to_psk_reporter () const // rig must be open and working to spot externally return is_transceiver_online () && m_->spot_to_psk_reporter_; } +bool Configuration::psk_reporter_tcpip () const {return m_->psk_reporter_tcpip_;} bool Configuration::monitor_off_at_startup () const {return m_->monitor_off_at_startup_;} bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;} bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;} @@ -1241,6 +1243,7 @@ void Configuration::impl::initialize_models () ui_->CW_id_after_73_check_box->setChecked (id_after_73_); ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_); ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_); + ui_->psk_reporter_tcpip_check_box->setChecked (psk_reporter_tcpip_); ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_); ui_->monitor_last_used_check_box->setChecked (monitor_last_used_); ui_->log_as_RTTY_check_box->setChecked (log_as_RTTY_); @@ -1448,6 +1451,7 @@ void Configuration::impl::read_settings () monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool (); monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool (); spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool (); + psk_reporter_tcpip_ = settings_->value ("PSKReporterTCPIP", false).toBool (); id_after_73_ = settings_->value ("After73", false).toBool (); tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool (); use_dynamic_grid_ = settings_->value ("AutoGrid", false).toBool (); @@ -1587,6 +1591,7 @@ void Configuration::impl::write_settings () settings_->setValue ("MonitorOFF", monitor_off_at_startup_); settings_->setValue ("MonitorLastUsed", monitor_last_used_); settings_->setValue ("PSKReporter", spot_to_psk_reporter_); + settings_->setValue ("PSKReporterTCPIP", psk_reporter_tcpip_); settings_->setValue ("After73", id_after_73_); settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_); settings_->setValue ("Macros", macros_.stringList ()); @@ -2041,6 +2046,7 @@ void Configuration::impl::accept () FD_exchange_= ui_->Field_Day_Exchange->text ().toUpper (); RTTY_exchange_= ui_->RTTY_Exchange->text ().toUpper (); spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked (); + psk_reporter_tcpip_ = ui_->psk_reporter_tcpip_check_box->isChecked (); id_interval_ = ui_->CW_id_interval_spin_box->value (); ntrials_ = ui_->sbNtrials->value (); txDelay_ = ui_->sbTxDelay->value (); diff --git a/Configuration.hpp b/Configuration.hpp index 43267d6ce..882a78629 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -113,6 +113,7 @@ public: bool id_after_73 () const; bool tx_QSY_allowed () const; bool spot_to_psk_reporter () const; + bool psk_reporter_tcpip () const; bool monitor_off_at_startup () const; bool monitor_last_used () const; bool log_as_RTTY () const; diff --git a/Configuration.ui b/Configuration.ui index fa16476e9..16a248223 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -6,8 +6,8 @@ 0 0 - 557 - 561 + 559 + 553 @@ -1800,20 +1800,27 @@ and DX Grid fields when a 73 or free text message is sent. Network Services - - + + - The program can send your station details and all -decoded signals as spots to the http://pskreporter.info web site. -This is used for reverse beacon analysis which is very useful -for assessing propagation and system performance. + <html><head/><body><p>The program can send your station details and all decoded signals with grid squares as spots to the http://pskreporter.info web site.</p><p>This is used for reverse beacon analysis which is very useful for assessing propagation and system performance.</p></body></html> Enable &PSK Reporter Spotting + + + + <html><head/><body><p>Check this option if a reliable connection is needed</p><p>Most users do not need this, the default uses UDP which is more efficient. Only check this if you have evidence that UDP traffic from you to PSK Reporter is being lost.</p></body></html> + + + Use TCP/IP connection + + + @@ -3108,13 +3115,13 @@ Right click for insert and delete options. + + - - - - - + + + diff --git a/Network/MessageClient.cpp b/Network/MessageClient.cpp index d33b2a636..d2de5867e 100644 --- a/Network/MessageClient.cpp +++ b/Network/MessageClient.cpp @@ -36,6 +36,7 @@ public: impl (QString const& id, QString const& version, QString const& revision, port_type server_port, MessageClient * self) : self_ {self} + , dns_lookup_id_ {0} , enabled_ {false} , id_ {id} , version_ {version} @@ -81,6 +82,7 @@ public: Q_SLOT void host_info_results (QHostInfo); MessageClient * self_; + int dns_lookup_id_; bool enabled_; QString id_; QString version_; @@ -101,6 +103,7 @@ public: void MessageClient::impl::host_info_results (QHostInfo host_info) { + if (host_info.lookupId () != dns_lookup_id_) return; if (QHostInfo::NoError != host_info.error ()) { Q_EMIT self_->error ("UDP server lookup failed:\n" + host_info.errorString ()); @@ -423,30 +426,24 @@ MessageClient::MessageClient (QString const& id, QString const& version, QString : QObject {self} , m_ {id, version, revision, server_port, this} { + connect (&*m_ #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - connect (&*m_, static_cast (&impl::error) - , [this] (impl::SocketError e) - { + , static_cast (&impl::error), [this] (impl::SocketError e) +#else + , &impl::errorOccurred, [this] (impl::SocketError e) +#endif + { #if defined (Q_OS_WIN) - if (e != impl::NetworkError // take this out when Qt 5.5 - // stops doing this - // spuriously - && e != impl::ConnectionRefusedError) // not - // interested - // in this with - // UDP socket + if (e != impl::NetworkError // take this out when Qt 5.5 stops doing this spuriously + && e != impl::ConnectionRefusedError) // not interested in this with UDP socket + { #else - Q_UNUSED (e); + { + Q_UNUSED (e); #endif - { - Q_EMIT error (m_->errorString ()); - } - }); -#else - connect (&*m_, &impl::errorOccurred, [this] (impl::SocketError) { Q_EMIT error (m_->errorString ()); - }); -#endif + } + }); set_server (server); } @@ -464,11 +461,11 @@ void MessageClient::set_server (QString const& server) { m_->server_.clear (); m_->server_string_ = server; - if (!server.isEmpty ()) + if (server.size ()) { // queue a host address lookup TRACE_UDP ("server host DNS lookup:" << server); - QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo))); + m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, &MessageClient::impl::host_info_results); } } @@ -477,27 +474,6 @@ void MessageClient::set_server_port (port_type server_port) m_->server_port_ = server_port; } -qint64 MessageClient::send_raw_datagram (QByteArray const& message, QHostAddress const& dest_address - , port_type dest_port) -{ - if (dest_port && !dest_address.isNull ()) - { - return m_->writeDatagram (message, dest_address, dest_port); - } - return 0; -} - -void MessageClient::add_blocked_destination (QHostAddress const& a) -{ - m_->blocked_addresses_.push_back (a); - if (a == m_->server_) - { - m_->server_.clear (); - Q_EMIT error ("UDP server blocked, please try another"); - m_->pending_messages_.clear (); // discard - } -} - void MessageClient::enable (bool flag) { m_->enabled_ = flag; diff --git a/Network/MessageClient.hpp b/Network/MessageClient.hpp index 20f39b019..29730f22c 100644 --- a/Network/MessageClient.hpp +++ b/Network/MessageClient.hpp @@ -75,15 +75,6 @@ public: // of record marker Q_SLOT void logged_ADIF (QByteArray const& ADIF_record); - // this may be used to send arbitrary UDP datagrams to and - // destination allowing the underlying socket to be used for general - // UDP messaging if desired - qint64 send_raw_datagram (QByteArray const&, QHostAddress const& dest_address, port_type dest_port); - - // disallowed message destination (does not block datagrams sent - // with send_raw_datagram() above) - Q_SLOT void add_blocked_destination (QHostAddress const&); - // this signal is emitted if the server has requested a decode // window clear action Q_SIGNAL void clear_decodes (quint8 window); diff --git a/Network/PSK_Reporter.cpp b/Network/PSK_Reporter.cpp new file mode 100644 index 000000000..ae3a41043 --- /dev/null +++ b/Network/PSK_Reporter.cpp @@ -0,0 +1,495 @@ +// Interface for posting spots to PSK Reporter web site +// Implemented by Edson Pereira PY2SDR +// Updated by Bill Somerville, G4WJS +// +// Reports will be sent in batch mode every 5 minutes. + +#include "PSK_Reporter.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +#include +#endif +#include + +#include "Configuration.hpp" +#include "pimpl_impl.hpp" + + +#include "moc_PSK_Reporter.cpp" + +namespace +{ + constexpr QLatin1String HOST {"report.pskreporter.info"}; + // constexpr QLatin1String HOST {"127.0.0.1"}; + // constexpr QLatin1String HOST {"192.168.1.195"}; + constexpr quint16 SERVICE_PORT {4739}; + // constexpr quint16 SERVICE_PORT {14739}; + constexpr int MIN_SEND_INTERVAL {60}; // in seconds + constexpr int FLUSH_INTERVAL {5}; // in send intervals + constexpr bool ALIGNMENT_PADDING {true}; + constexpr int MIN_PAYLOAD_LENGTH {508}; + constexpr int MAX_PAYLOAD_LENGTH {1400}; +} + +class PSK_Reporter::impl final + : public QObject +{ + Q_OBJECT + +public: + impl (PSK_Reporter * self, Configuration const * config, QString const& program_info) + : self_ {self} + , config_ {config} + , sequence_number_ {0u} + , send_descriptors_ {0} + , send_receiver_data_ {0} + , flush_counter_ {0u} + , prog_id_ {program_info} + { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + observation_id_ = qrand(); +#else + observation_id_ = QRandomGenerator::global ()->generate (); +#endif + + // This timer sets the interval to check for spots to send. + connect (&report_timer_, &QTimer::timeout, [this] () {send_report ();}); + report_timer_.start (MIN_SEND_INTERVAL * 1000); + + // This timer repeats the sending of IPFIX templates and receiver + // information if we are using UDP, in case server has been + // restarted ans lost cached information. + connect (&descriptor_timer_, &QTimer::timeout, [this] () { + if (socket_ + && QAbstractSocket::UdpSocket == socket_->socketType ()) + { + // send templates again + send_descriptors_ = 3; // three times + // send receiver data set again + send_receiver_data_ = 3; // three times + } + }); + descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly + } + + void check_connection () + { + if (!socket_ + || QAbstractSocket::UnconnectedState == socket_->state () + || (socket_->socketType () != config_->psk_reporter_tcpip () ? QAbstractSocket::TcpSocket : QAbstractSocket::UdpSocket)) + { + // we need to create the appropriate socket + if (socket_ + && QAbstractSocket::UnconnectedState != socket_->state () + && QAbstractSocket::ClosingState != socket_->state ()) + { + // handle re-opening asynchronously + auto connection = QSharedPointer::create (); + *connection = connect (socket_.get (), &QAbstractSocket::disconnected, [this, connection] () { + qDebug () << "PSK_Reporter::impl::check_connection: disconnected, socket state:" << socket_->state (); + disconnect (*connection); + check_connection (); + }); + // close gracefully + socket_->close (); + } + else + { + reconnect (); + } + } + } + + void handle_socket_error (QAbstractSocket::SocketError e) + { + switch (e) + { + case QAbstractSocket::RemoteHostClosedError: + socket_->disconnectFromHost (); + break; + + case QAbstractSocket::TemporaryError: + break; + + default: + spots_.clear (); + qDebug () << "PSK_Reporter::impl::handle_socket_error:" << socket_->errorString (); + Q_EMIT self_->errorOccurred (socket_->errorString ()); + break; + } + } + + void reconnect () + { + // Using deleteLater for the deleter as we may eventually + // be called from the disconnected handler above. + if (config_->psk_reporter_tcpip ()) + { + socket_.reset (new QTcpSocket, &QObject::deleteLater); + send_descriptors_ = 1; + send_receiver_data_ = 1; + } + else + { + socket_.reset (new QUdpSocket, &QObject::deleteLater); + send_descriptors_ = 3; + send_receiver_data_ = 3; + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + connect (socket_.get (), &QAbstractSocket::errorOccurred, this, &PSK_Reporter::impl::handle_socket_error); +#else + connect (socket_.get (), QOverload::of (&QAbstractSocket::error), this, &PSK_Reporter::impl::handle_socket_error); +#endif + + // use this for pseudo connection with UDP, allows us to use + // QIODevice::write() instead of QUDPSocket::writeDatagram() + socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly); + } + + void send_report (bool send_residue = false); + void build_preamble (QDataStream&); + + bool flushing () + { + return FLUSH_INTERVAL && !(++flush_counter_ % FLUSH_INTERVAL); + } + + PSK_Reporter * self_; + Configuration const * config_; + QSharedPointer socket_; + int dns_lookup_id_; + QByteArray payload_; + quint32 sequence_number_; + int send_descriptors_; + + // Currently PSK Reporter requires that a receiver data set is sent + // in every data flow. This memeber variable can be used to only + // send that information at session start (3 times for UDP), when it + // changes (3 times for UDP), or once per hour (3 times) if using + // UDP. Uncomment the relevant code to enable that fuctionality. + int send_receiver_data_; + + unsigned flush_counter_; + quint32 observation_id_; + QString rx_call_; + QString rx_grid_; + QString rx_ant_; + QString prog_id_; + QByteArray tx_data_; + QByteArray tx_residue_; + struct Spot + { + bool operator == (Spot const& rhs) + { + return + call_ == rhs.call_ + && grid_ == rhs.grid_ + && mode_ == rhs.mode_ + && std::abs (Radio::FrequencyDelta (freq_ - rhs.freq_)) < 50; + } + + QString call_; + QString grid_; + int snr_; + Radio::Frequency freq_; + QString mode_; + QDateTime time_; + }; + QQueue spots_; + QTimer report_timer_; + QTimer descriptor_timer_; +}; + +#include "PSK_Reporter.moc" + +namespace +{ + void writeUtfString (QDataStream& out, QString const& s) + { + auto const& utf = s.toUtf8 ().left (254); + out << quint8 (utf.size ()); + out.writeRawData (utf, utf.size ()); + } + + constexpr + int num_pad_bytes (int len) + { + return ALIGNMENT_PADDING ? (4 - len % 4) % 4 : 0; + } + + void set_length (QDataStream& out, QByteArray& b) + { + // pad with nulls modulo 4 + auto pad_len = num_pad_bytes (b.size ()); + out.writeRawData (QByteArray {pad_len, '\0'}.constData (), pad_len); + auto pos = out.device ()->pos (); + out.device ()->seek (sizeof (quint16)); + // insert length + out << static_cast (b.size ()); + out.device ()->seek (pos); + } +} + +void PSK_Reporter::impl::build_preamble (QDataStream& message) +{ + // Message Header + message + << quint16 (10u) // Version Number + << quint16 (0u) // Length (place-holder filled in later) + << quint32 (0u) // Export Time (place-holder filled in later) + << ++sequence_number_ // Sequence Number + << observation_id_; // Observation Domain ID + + qDebug () << "PSK_Reporter::impl::build_preamble: send_descriptors_:" << send_descriptors_; + if (send_descriptors_) + { + --send_descriptors_; + { + // Sender Information descriptor + QByteArray descriptor; + QDataStream out {&descriptor, QIODevice::WriteOnly}; + out + << quint16 (2u) // Template Set ID + << quint16 (0u) // Length (place-holder) + << quint16 (0x50e3) // Link ID + << quint16 (7u) // Field Count + << quint16 (0x8000 + 1u) // Option 1 Information Element ID (senderCallsign) + << quint16 (0xffff) // Option 1 Field Length (variable) + << quint32 (30351u) // Option 1 Enterprise Number + << quint16 (0x8000 + 5u) // Option 2 Information Element ID (frequency) + << quint16 (4u) // Option 2 Field Length + << quint32 (30351u) // Option 2 Enterprise Number + << quint16 (0x8000 + 6u) // Option 3 Information Element ID (sNR) + << quint16 (1u) // Option 3 Field Length + << quint32 (30351u) // Option 3 Enterprise Number + << quint16 (0x8000 + 10u) // Option 4 Information Element ID (mode) + << quint16 (0xffff) // Option 4 Field Length (variable) + << quint32 (30351u) // Option 4 Enterprise Number + << quint16 (0x8000 + 3u) // Option 5 Information Element ID (senderLocator) + << quint16 (0xffff) // Option 5 Field Length (variable) + << quint32 (30351u) // Option 5 Enterprise Number + << quint16 (0x8000 + 11u) // Option 6 Information Element ID (informationSource) + << quint16 (1u) // Option 6 Field Length + << quint32 (30351u) // Option 6 Enterprise Number + << quint16 (150u) // Option 7 Information Element ID (dateTimeSeconds) + << quint16 (4u); // Option 7 Field Length + // insert Length and move to payload + set_length (out, descriptor); + message.writeRawData (descriptor.constData (), descriptor.size ()); + } + { + // Receiver Information descriptor + QByteArray descriptor; + QDataStream out {&descriptor, QIODevice::WriteOnly}; + out + << quint16 (3u) // Options Template Set ID + << quint16 (0u) // Length (place-holder) + << quint16 (0x50e2) // Link ID + << quint16 (4u) // Field Count + << quint16 (0u) // Scope Field Count + << quint16 (0x8000 + 2u) // Option 1 Information Element ID (receiverCallsign) + << quint16 (0xffff) // Option 1 Field Length (variable) + << quint32 (30351u) // Option 1 Enterprise Number + << quint16 (0x8000 + 4u) // Option 2 Information Element ID (receiverLocator) + << quint16 (0xffff) // Option 2 Field Length (variable) + << quint32 (30351u) // Option 2 Enterprise Number + << quint16 (0x8000 + 8u) // Option 3 Information Element ID (decodingSoftware) + << quint16 (0xffff) // Option 3 Field Length (variable) + << quint32 (30351u) // Option 3 Enterprise Number + << quint16 (0x8000 + 9u) // Option 4 Information Element ID (antennaInformation) + << quint16 (0xffff) // Option 4 Field Length (variable) + << quint32 (30351u); // Option 4 Enterprise Number + // insert Length + set_length (out, descriptor); + message.writeRawData (descriptor.constData (), descriptor.size ()); + } + } + + qDebug () << "PSK_Reporter::impl::build_preamble: send_receiver_data_:" << send_receiver_data_; + // if (send_receiver_data_) + { + // --send_receiver_data_; + + // Receiver information + QByteArray data; + QDataStream out {&data, QIODevice::WriteOnly}; + + // Set Header + out + << quint16 (0x50e2) // Template ID + << quint16 (0u); // Length (place-holder) + + // Set data + writeUtfString (out, rx_call_); + writeUtfString (out, rx_grid_); + writeUtfString (out, prog_id_); + writeUtfString (out, rx_ant_); + + // insert Length and move to payload + set_length (out, data); + message.writeRawData (data.constData (), data.size ()); + } +} + +void PSK_Reporter::impl::send_report (bool send_residue) +{ + check_connection (); + qDebug () << "PSK_Reporter::impl::send_report: send_residue:" << send_residue; + if (QAbstractSocket::ConnectedState != socket_->state ()) return; + + QDataStream message {&payload_, QIODevice::WriteOnly | QIODevice::Append}; + QDataStream tx_out {&tx_data_, QIODevice::WriteOnly | QIODevice::Append}; + + if (!payload_.size ()) + { + qDebug () << "PSK_Reporter::impl::send_report: building header"; + // Build header, optional descriptors, and receiver information + build_preamble (message); + } + + auto flush = flushing () || send_residue; + qDebug () << "PSK_Reporter::impl::send_report: flush:" << flush; + while (spots_.size () || (flush && (tx_data_.size () || tx_residue_.size ()))) + { + if (!payload_.size ()) + { + // Build header, optional descriptors, and receiver information + build_preamble (message); + } + + if (!tx_data_.size ()) + { + // Set Header + tx_out + << quint16 (0x50e3) // Template ID + << quint16 (0u); // Length (place-holder) + qDebug () << "PSK_Reporter::impl::send_report: set data set header"; + } + + // insert any residue + if (tx_residue_.size ()) + { + tx_out.writeRawData (tx_residue_.constData (), tx_residue_.size ()); + tx_residue_.clear (); + qDebug () << "PSK_Reporter::impl::send_report: inserted data residue"; + } + + while (spots_.size () || (flush && tx_data_.size ())) + { + auto tx_data_size = tx_data_.size (); + if (spots_.size ()) + { + auto const& spot = spots_.dequeue (); + qDebug () << "PSK_Reporter::impl::send_report: processing spotted call:" << spot.call_; + + // Sender information + writeUtfString (tx_out, spot.call_); + tx_out + << static_cast (spot.freq_) + << static_cast (spot.snr_); + writeUtfString (tx_out, spot.mode_); + writeUtfString (tx_out, spot.grid_); + tx_out + << quint8 (1u) // REPORTER_SOURCE_AUTOMATIC + << static_cast (spot.time_.toSecsSinceEpoch ()); + } + + auto len = payload_.size () + tx_data_.size (); + len += num_pad_bytes (tx_data_.size ()); + len += num_pad_bytes (len); + if (len > MAX_PAYLOAD_LENGTH // our upper datagram size limit + || (!spots_.size () && len > MIN_PAYLOAD_LENGTH) // spots drained and above lower datagram size limit + || (flush && !spots_.size () && tx_data_.size ())) // send what we have + { + if (len <= MAX_PAYLOAD_LENGTH) + { + tx_data_size = tx_data_.size (); + qDebug () << "PSK_Reporter::impl::send_report: sending short payload:" << tx_data_size; + } + QByteArray tx {tx_data_.left (tx_data_size)}; + QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append}; + // insert Length + set_length (out, tx); + message.writeRawData (tx.constData (), tx.size ()); + + // insert Length and Export Time + set_length (message, payload_); + message.device ()->seek (2 * sizeof (quint16)); + message << static_cast (QDateTime::currentDateTime ().toSecsSinceEpoch ()); + + // Send data to PSK Reporter site + socket_->write (payload_); // TODO: handle errors + qDebug () << "PSK_Reporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; + message.device ()->seek (0u); + payload_.clear (); // Fresh message + // Save unsent spots + tx_residue_ = tx_data_.right (tx_data_.size () - tx_data_size); + tx_out.device ()->seek (0u); + tx_data_.clear (); + qDebug () << "PSK_Reporter::impl::send_report: payload sent residue length:" << tx_residue_.size (); + break; + } + } + } +} + +PSK_Reporter::PSK_Reporter (Configuration const * config, QString const& program_info) + : m_ {this, config, program_info} +{ +} + +PSK_Reporter::~PSK_Reporter () +{ + // m_->send_report (true); // send any pending spots +} + +void PSK_Reporter::reconnect () +{ + m_->reconnect (); +} + +void PSK_Reporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna) +{ + if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_) + { + m_->send_receiver_data_ = m_->socket_ + && QAbstractSocket::UdpSocket == m_->socket_->socketType () ? 3 : 1; + m_->rx_call_ = call; + m_->rx_grid_ = gridSquare; + m_->rx_ant_ = antenna; + } +} + +bool PSK_Reporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq + , QString const& mode, int snr) +{ + if (m_->socket_ && m_->socket_->isValid ()) + { + if (QAbstractSocket::UnconnectedState == m_->socket_->state ()) + { + reconnect (); + } + m_->spots_.enqueue ({call, grid, snr, freq, mode, QDateTime::currentDateTimeUtc ()}); + return true; + } + return false; +} + +void PSK_Reporter::sendReport () +{ + m_->send_report (true); +} diff --git a/Network/PSK_Reporter.hpp b/Network/PSK_Reporter.hpp new file mode 100644 index 000000000..5a8534521 --- /dev/null +++ b/Network/PSK_Reporter.hpp @@ -0,0 +1,41 @@ +#ifndef PSK_REPORTER_HPP_ +#define PSK_REPORTER_HPP_ + +#include +#include "Radio.hpp" +#include "pimpl_h.hpp" + +class QString; +class Configuration; + +class PSK_Reporter final + : public QObject +{ + Q_OBJECT + +public: + explicit PSK_Reporter (Configuration const *, QString const& program_info); + ~PSK_Reporter (); + + void reconnect (); + + void setLocalStation (QString const& call, QString const& grid, QString const& antenna); + + // + // Returns false if PSK Reporter connection is not available + // + bool addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq, QString const& mode, int snr); + + // + // Flush any pending spots to PSK Reporter + // + void sendReport (); + + Q_SIGNAL void errorOccurred (QString const& reason); + +private: + class impl; + pimpl m_; +}; + +#endif diff --git a/Network/psk_reporter.cpp b/Network/psk_reporter.cpp deleted file mode 100644 index f84da5ecd..000000000 --- a/Network/psk_reporter.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// KISS Interface for posting spots to PSK Reporter web site -// Implemented by Edson Pereira PY2SDR -// -// Reports will be sent in batch mode every 5 minutes. - -#include "psk_reporter.h" - -#include -#include -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) -#include -#endif - -#include "Network/MessageClient.hpp" - -#include "moc_psk_reporter.cpp" - -namespace -{ - int constexpr MAX_PAYLOAD_LENGTH {1400}; -} - -PSK_Reporter::PSK_Reporter(MessageClient * message_client, QObject *parent) : - QObject {parent}, - m_messageClient {message_client}, - reportTimer {new QTimer {this}}, - m_sequenceNumber {0} -{ - m_header_h = "000Allllttttttttssssssssiiiiiiii"; - - // We use 50E2 and 50E3 for link Id - m_rxInfoDescriptor_h = "0003002C50E200040000" - "8002FFFF0000768F" // 2. Rx Call - "8004FFFF0000768F" // 4. Rx Grid - "8008FFFF0000768F" // 8. Rx Soft - "8009FFFF0000768F" // 9. Rx Antenna - "0000"; - - m_txInfoDescriptor_h = "0002003C50E30007" - "8001FFFF0000768F" // 1. Tx Call - "800500040000768F" // 5. Tx Freq - "800600010000768F" // 6. Tx snr - "800AFFFF0000768F" // 10. Tx Mode - "8003FFFF0000768F" // 3. Tx Grid - "800B00010000768F" // 11. Tx info src - "00960004"; // Report time - - -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0')); -#else - m_randomId_h = QString("%1").arg(QRandomGenerator::global ()->generate (), 8, 16, QChar('0')); -#endif - - QHostInfo::lookupHost("report.pskreporter.info", this, SLOT(dnsLookupResult(QHostInfo))); - - connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport())); - reportTimer->start(5*60*1000); // 5 minutes; -} - -void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo) -{ - m_rxCall = call; - m_rxGrid = gridSquare; - m_rxAnt = antenna; - m_progId = programInfo; -} - -void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time ) -{ - QHash spot; - spot["call"] = call; - spot["grid"] = grid; - spot["snr"] = snr; - spot["freq"] = freq; - spot["mode"] = mode; - spot["time"] = time; - m_spotQueue.enqueue(spot); -} - -void PSK_Reporter::sendReport() -{ - while (!m_spotQueue.isEmpty()) { - QString report_h; - - // Header - QString header_h = m_header_h; - header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0'))); - header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0'))); - header_h.replace("iiiiiiii", m_randomId_h); - - // Receiver information - QString rxInfoData_h = "50E2llll"; - rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex(); - rxInfoData_h += "0000"; - rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0'))); - - // Sender information - QString txInfoData_h = "50E3llll"; - while (!m_spotQueue.isEmpty() - && (header_h.size () + m_rxInfoDescriptor_h.size () + m_txInfoDescriptor_h.size () + rxInfoData_h.size () + txInfoData_h.size ()) / 2 < MAX_PAYLOAD_LENGTH) { - QHash spot = m_spotQueue.dequeue(); - txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0')); - txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2); - txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC - txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0')); - } - txInfoData_h += "0000"; - txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0'))); - report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h; - //qDebug() << "Sending Report TX: "; - - report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0'))); - QByteArray report = QByteArray::fromHex(report_h.toUtf8()); - - // Send data to PSK Reporter site - if (!m_pskReporterAddress.isNull()) { - m_messageClient->send_raw_datagram (report, m_pskReporterAddress, 4739); - } - } -} - -void PSK_Reporter::dnsLookupResult(QHostInfo info) -{ - if (!info.addresses().isEmpty()) { - m_pskReporterAddress = info.addresses().at(0); - // qDebug() << "PSK Reporter IP: " << m_pskReporterAddress; - - // deal with miss-configured settings that attempt to set a - // Pskreporter Internet address for the WSJT-X UDP protocol - // server address - m_messageClient->add_blocked_destination (m_pskReporterAddress); - } -} diff --git a/Network/psk_reporter.h b/Network/psk_reporter.h deleted file mode 100644 index 16afcf437..000000000 --- a/Network/psk_reporter.h +++ /dev/null @@ -1,54 +0,0 @@ -// -*- Mode: C++ -*- -#ifndef PSK_REPORTER_H -#define PSK_REPORTER_H - -#include -#include -#include -#include -#include - -class MessageClient; -class QTimer; -class QHostInfo; - -class PSK_Reporter : public QObject -{ - Q_OBJECT -public: - explicit PSK_Reporter(MessageClient *, QObject *parent = nullptr); - void setLocalStation(QString call, QString grid, QString antenna, QString programInfo); - void addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time); - -signals: - -public slots: - void sendReport(); - -private slots: - void dnsLookupResult(QHostInfo info); - -private: - QString m_header_h; - QString m_rxInfoDescriptor_h; - QString m_txInfoDescriptor_h; - QString m_randomId_h; - QString m_linkId_h; - - QString m_rxCall; - QString m_rxGrid; - QString m_rxAnt; - QString m_progId; - - QHostAddress m_pskReporterAddress; - - QQueue< QHash > m_spotQueue; - - MessageClient * m_messageClient; - - QTimer *reportTimer; - - int m_sequenceNumber; -}; - -#endif // PSK_REPORTER_H diff --git a/main.cpp b/main.cpp index 4943f2f02..3c743bfa2 100644 --- a/main.cpp +++ b/main.cpp @@ -82,12 +82,12 @@ namespace } catch (std::exception const& e) { - MessageBox::critical_message (nullptr, translate ("main", "Fatal error"), e.what ()); + MessageBox::critical_message (nullptr, "Fatal error", e.what ()); throw; } catch (...) { - MessageBox::critical_message (nullptr, translate ("main", "Unexpected fatal error")); + MessageBox::critical_message (nullptr, "Unexpected fatal error"); throw; } } @@ -407,12 +407,12 @@ int main(int argc, char *argv[]) } catch (std::exception const& e) { - MessageBox::critical_message (nullptr, QApplication::translate ("main", "Fatal error"), e.what ()); + MessageBox::critical_message (nullptr, "Fatal error", e.what ()); std::cerr << "Error: " << e.what () << '\n'; } catch (...) { - MessageBox::critical_message (nullptr, QApplication::translate ("main", "Unexpected fatal error")); + MessageBox::critical_message (nullptr, "Unexpected fatal error"); std::cerr << "Unexpected fatal error\n"; throw; // hoping the runtime might tell us more about the exception } diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 0ed391faa..3c9c56b59 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -412,7 +412,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, version (), revision (), m_config.udp_server_name (), m_config.udp_server_port (), this}}, - psk_Reporter {new PSK_Reporter {m_messageClient, this}}, + m_psk_Reporter {&m_config, QString {"WSJT-X v" + version () + " " + m_revision}.simplified ()}, m_manual {&m_network_manager}, m_block_udp_status_updates {false} { @@ -3481,8 +3481,10 @@ void MainWindow::pskPost (DecodedText const& decodedtext) pskSetLocal (); if(grid.contains (grid_regexp)) { // qDebug() << "To PSKreporter:" << deCall << grid << frequency << msgmode << snr; - psk_Reporter->addRemoteStation(deCall,grid,QString::number(frequency),msgmode, - QString::number(snr),QString::number(QDateTime::currentDateTimeUtc ().toTime_t())); + if (!m_psk_Reporter.addRemoteStation (deCall, grid, frequency, msgmode, snr)) + { + showStatusMessage (tr ("Spotting to PSK Reporter unavailable")); + } } } @@ -6635,7 +6637,7 @@ void MainWindow::band_changed (Frequency f) } m_lastBand.clear (); m_bandEdited = false; - psk_Reporter->sendReport(); // Upload any queued spots before changing band + m_psk_Reporter.sendReport(); // Upload any queued spots before changing band if (!m_transmitting) monitor (true); if ("FreqCal" == m_mode) { @@ -7314,9 +7316,7 @@ void MainWindow::pskSetLocal () , StationList::description_column).data ().toString (); } // qDebug() << "To PSKreporter: local station details"; - psk_Reporter->setLocalStation(m_config.my_callsign (), m_config.my_grid (), - antenna_description, QString {"WSJT-X v" + version() + " " + - m_revision}.simplified ()); + m_psk_Reporter.setLocalStation(m_config.my_callsign (), m_config.my_grid (), antenna_description); } void MainWindow::transmitDisplay (bool transmitting) diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 6da871b37..d00b9d9c1 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -33,7 +34,7 @@ #include "WSPR/WSPRBandHopping.hpp" #include "Transceiver/Transceiver.hpp" #include "DisplayManual.hpp" -#include "Network/psk_reporter.h" +#include "Network/PSK_Reporter.hpp" #include "logbook/logbook.h" #include "astro.h" #include "MessageBox.hpp" @@ -683,7 +684,7 @@ private: QProgressDialog m_optimizingProgress; QTimer m_heartbeat; MessageClient * m_messageClient; - PSK_Reporter *psk_Reporter; + PSK_Reporter m_psk_Reporter; DisplayManual m_manual; QHash m_pwrBandTxMemory; // Remembers power level by band QHash m_pwrBandTuneMemory; // Remembers power level by band for tuning diff --git a/wsjtx.pro b/wsjtx.pro index 3248fefd7..0bbe8b565 100644 --- a/wsjtx.pro +++ b/wsjtx.pro @@ -60,7 +60,7 @@ SOURCES += \ PollingTransceiver.cpp EmulateSplitTransceiver.cpp \ HRDTransceiver.cpp DXLabSuiteCommanderTransceiver.cpp \ HamlibTransceiver.cpp FrequencyLineEdit.cpp \ - Configuration.cpp psk_reporter.cpp AudioDevice.cpp \ + Configuration.cpp PSK_Reporter.cpp AudioDevice.cpp \ Modulator.cpp Detector.cpp \ getfile.cpp soundout.cpp soundin.cpp \ WFPalette.cpp \ @@ -77,7 +77,7 @@ HEADERS += qt_helpers.hpp qt_db_helpers.hpp \ soundin.h soundout.h \ WFPalette.hpp getfile.h decodedtext.h \ commons.h sleep.h \ - AudioDevice.hpp Detector.hpp Modulator.hpp psk_reporter.h \ + AudioDevice.hpp Detector.hpp Modulator.hpp PSK_Reporter.hpp \ Transceiver.hpp TransceiverBase.hpp TransceiverFactory.hpp PollingTransceiver.hpp \ EmulateSplitTransceiver.hpp DXLabSuiteCommanderTransceiver.hpp HamlibTransceiver.hpp \ Configuration.hpp wsprnet.h \ From af894c6039241c5064ff42a7f3900a5b77d84051 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 21 Jun 2020 02:15:31 +0100 Subject: [PATCH 2/6] Rename files to match class name Avoiding case insensitive file system issues. --- CMakeLists.txt | 2 +- Network/{PSK_Reporter.cpp => PSKReporter.cpp} | 60 +++++++++---------- Network/{PSK_Reporter.hpp => PSKReporter.hpp} | 6 +- widgets/mainwindow.h | 4 +- 4 files changed, 36 insertions(+), 36 deletions(-) rename Network/{PSK_Reporter.cpp => PSKReporter.cpp} (88%) rename Network/{PSK_Reporter.hpp => PSKReporter.hpp} (84%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8690c562f..18d632ebd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -302,7 +302,7 @@ set (jt9_FSRCS set (wsjtx_CXXSRCS logbook/logbook.cpp - Network/PSK_Reporter.cpp + Network/PSKReporter.cpp Modulator/Modulator.cpp Detector/Detector.cpp widgets/logqso.cpp diff --git a/Network/PSK_Reporter.cpp b/Network/PSKReporter.cpp similarity index 88% rename from Network/PSK_Reporter.cpp rename to Network/PSKReporter.cpp index ae3a41043..b981188b7 100644 --- a/Network/PSK_Reporter.cpp +++ b/Network/PSKReporter.cpp @@ -1,11 +1,11 @@ +#include "PSKReporter.hpp" + // Interface for posting spots to PSK Reporter web site // Implemented by Edson Pereira PY2SDR // Updated by Bill Somerville, G4WJS // // Reports will be sent in batch mode every 5 minutes. -#include "PSK_Reporter.hpp" - #include #include #include @@ -28,7 +28,7 @@ #include "pimpl_impl.hpp" -#include "moc_PSK_Reporter.cpp" +#include "moc_PSKReporter.cpp" namespace { @@ -44,13 +44,13 @@ namespace constexpr int MAX_PAYLOAD_LENGTH {1400}; } -class PSK_Reporter::impl final +class PSKReporter::impl final : public QObject { Q_OBJECT public: - impl (PSK_Reporter * self, Configuration const * config, QString const& program_info) + impl (PSKReporter * self, Configuration const * config, QString const& program_info) : self_ {self} , config_ {config} , sequence_number_ {0u} @@ -99,7 +99,7 @@ public: // handle re-opening asynchronously auto connection = QSharedPointer::create (); *connection = connect (socket_.get (), &QAbstractSocket::disconnected, [this, connection] () { - qDebug () << "PSK_Reporter::impl::check_connection: disconnected, socket state:" << socket_->state (); + qDebug () << "PSKReporter::impl::check_connection: disconnected, socket state:" << socket_->state (); disconnect (*connection); check_connection (); }); @@ -126,7 +126,7 @@ public: default: spots_.clear (); - qDebug () << "PSK_Reporter::impl::handle_socket_error:" << socket_->errorString (); + qDebug () << "PSKReporter::impl::handle_socket_error:" << socket_->errorString (); Q_EMIT self_->errorOccurred (socket_->errorString ()); break; } @@ -150,9 +150,9 @@ public: } #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - connect (socket_.get (), &QAbstractSocket::errorOccurred, this, &PSK_Reporter::impl::handle_socket_error); + connect (socket_.get (), &QAbstractSocket::errorOccurred, this, &PSKReporter::impl::handle_socket_error); #else - connect (socket_.get (), QOverload::of (&QAbstractSocket::error), this, &PSK_Reporter::impl::handle_socket_error); + connect (socket_.get (), QOverload::of (&QAbstractSocket::error), this, &PSKReporter::impl::handle_socket_error); #endif // use this for pseudo connection with UDP, allows us to use @@ -168,7 +168,7 @@ public: return FLUSH_INTERVAL && !(++flush_counter_ % FLUSH_INTERVAL); } - PSK_Reporter * self_; + PSKReporter * self_; Configuration const * config_; QSharedPointer socket_; int dns_lookup_id_; @@ -214,7 +214,7 @@ public: QTimer descriptor_timer_; }; -#include "PSK_Reporter.moc" +#include "PSKReporter.moc" namespace { @@ -244,7 +244,7 @@ namespace } } -void PSK_Reporter::impl::build_preamble (QDataStream& message) +void PSKReporter::impl::build_preamble (QDataStream& message) { // Message Header message @@ -254,7 +254,7 @@ void PSK_Reporter::impl::build_preamble (QDataStream& message) << ++sequence_number_ // Sequence Number << observation_id_; // Observation Domain ID - qDebug () << "PSK_Reporter::impl::build_preamble: send_descriptors_:" << send_descriptors_; + qDebug () << "PSKReporter::impl::build_preamble: send_descriptors_:" << send_descriptors_; if (send_descriptors_) { --send_descriptors_; @@ -319,7 +319,7 @@ void PSK_Reporter::impl::build_preamble (QDataStream& message) } } - qDebug () << "PSK_Reporter::impl::build_preamble: send_receiver_data_:" << send_receiver_data_; + qDebug () << "PSKReporter::impl::build_preamble: send_receiver_data_:" << send_receiver_data_; // if (send_receiver_data_) { // --send_receiver_data_; @@ -345,10 +345,10 @@ void PSK_Reporter::impl::build_preamble (QDataStream& message) } } -void PSK_Reporter::impl::send_report (bool send_residue) +void PSKReporter::impl::send_report (bool send_residue) { check_connection (); - qDebug () << "PSK_Reporter::impl::send_report: send_residue:" << send_residue; + qDebug () << "PSKReporter::impl::send_report: send_residue:" << send_residue; if (QAbstractSocket::ConnectedState != socket_->state ()) return; QDataStream message {&payload_, QIODevice::WriteOnly | QIODevice::Append}; @@ -356,13 +356,13 @@ void PSK_Reporter::impl::send_report (bool send_residue) if (!payload_.size ()) { - qDebug () << "PSK_Reporter::impl::send_report: building header"; + qDebug () << "PSKReporter::impl::send_report: building header"; // Build header, optional descriptors, and receiver information build_preamble (message); } auto flush = flushing () || send_residue; - qDebug () << "PSK_Reporter::impl::send_report: flush:" << flush; + qDebug () << "PSKReporter::impl::send_report: flush:" << flush; while (spots_.size () || (flush && (tx_data_.size () || tx_residue_.size ()))) { if (!payload_.size ()) @@ -377,7 +377,7 @@ void PSK_Reporter::impl::send_report (bool send_residue) tx_out << quint16 (0x50e3) // Template ID << quint16 (0u); // Length (place-holder) - qDebug () << "PSK_Reporter::impl::send_report: set data set header"; + qDebug () << "PSKReporter::impl::send_report: set data set header"; } // insert any residue @@ -385,7 +385,7 @@ void PSK_Reporter::impl::send_report (bool send_residue) { tx_out.writeRawData (tx_residue_.constData (), tx_residue_.size ()); tx_residue_.clear (); - qDebug () << "PSK_Reporter::impl::send_report: inserted data residue"; + qDebug () << "PSKReporter::impl::send_report: inserted data residue"; } while (spots_.size () || (flush && tx_data_.size ())) @@ -394,7 +394,7 @@ void PSK_Reporter::impl::send_report (bool send_residue) if (spots_.size ()) { auto const& spot = spots_.dequeue (); - qDebug () << "PSK_Reporter::impl::send_report: processing spotted call:" << spot.call_; + qDebug () << "PSKReporter::impl::send_report: processing spotted call:" << spot.call_; // Sender information writeUtfString (tx_out, spot.call_); @@ -418,7 +418,7 @@ void PSK_Reporter::impl::send_report (bool send_residue) if (len <= MAX_PAYLOAD_LENGTH) { tx_data_size = tx_data_.size (); - qDebug () << "PSK_Reporter::impl::send_report: sending short payload:" << tx_data_size; + qDebug () << "PSKReporter::impl::send_report: sending short payload:" << tx_data_size; } QByteArray tx {tx_data_.left (tx_data_size)}; QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append}; @@ -433,36 +433,36 @@ void PSK_Reporter::impl::send_report (bool send_residue) // Send data to PSK Reporter site socket_->write (payload_); // TODO: handle errors - qDebug () << "PSK_Reporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; + qDebug () << "PSKReporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; message.device ()->seek (0u); payload_.clear (); // Fresh message // Save unsent spots tx_residue_ = tx_data_.right (tx_data_.size () - tx_data_size); tx_out.device ()->seek (0u); tx_data_.clear (); - qDebug () << "PSK_Reporter::impl::send_report: payload sent residue length:" << tx_residue_.size (); + qDebug () << "PSKReporter::impl::send_report: payload sent residue length:" << tx_residue_.size (); break; } } } } -PSK_Reporter::PSK_Reporter (Configuration const * config, QString const& program_info) +PSKReporter::PSKReporter (Configuration const * config, QString const& program_info) : m_ {this, config, program_info} { } -PSK_Reporter::~PSK_Reporter () +PSKReporter::~PSKReporter () { // m_->send_report (true); // send any pending spots } -void PSK_Reporter::reconnect () +void PSKReporter::reconnect () { m_->reconnect (); } -void PSK_Reporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna) +void PSKReporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna) { if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_) { @@ -474,7 +474,7 @@ void PSK_Reporter::setLocalStation (QString const& call, QString const& gridSqua } } -bool PSK_Reporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq +bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq , QString const& mode, int snr) { if (m_->socket_ && m_->socket_->isValid ()) @@ -489,7 +489,7 @@ bool PSK_Reporter::addRemoteStation (QString const& call, QString const& grid, R return false; } -void PSK_Reporter::sendReport () +void PSKReporter::sendReport () { m_->send_report (true); } diff --git a/Network/PSK_Reporter.hpp b/Network/PSKReporter.hpp similarity index 84% rename from Network/PSK_Reporter.hpp rename to Network/PSKReporter.hpp index 5a8534521..3582b5850 100644 --- a/Network/PSK_Reporter.hpp +++ b/Network/PSKReporter.hpp @@ -8,14 +8,14 @@ class QString; class Configuration; -class PSK_Reporter final +class PSKReporter final : public QObject { Q_OBJECT public: - explicit PSK_Reporter (Configuration const *, QString const& program_info); - ~PSK_Reporter (); + explicit PSKReporter (Configuration const *, QString const& program_info); + ~PSKReporter (); void reconnect (); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index d00b9d9c1..f24b4f2af 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -34,7 +34,7 @@ #include "WSPR/WSPRBandHopping.hpp" #include "Transceiver/Transceiver.hpp" #include "DisplayManual.hpp" -#include "Network/PSK_Reporter.hpp" +#include "Network/PSKReporter.hpp" #include "logbook/logbook.h" #include "astro.h" #include "MessageBox.hpp" @@ -684,7 +684,7 @@ private: QProgressDialog m_optimizingProgress; QTimer m_heartbeat; MessageClient * m_messageClient; - PSK_Reporter m_psk_Reporter; + PSKReporter m_psk_Reporter; DisplayManual m_manual; QHash m_pwrBandTxMemory; // Remembers power level by band QHash m_pwrBandTuneMemory; // Remembers power level by band for tuning From 147f29e27191d8c9d683796f4f5d1504a06cc53d Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 21 Jun 2020 16:11:36 +0100 Subject: [PATCH 3/6] Allow flush to send a receiver info data set without any spots --- Network/PSKReporter.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/Network/PSKReporter.cpp b/Network/PSKReporter.cpp index b981188b7..5ffd87113 100644 --- a/Network/PSKReporter.cpp +++ b/Network/PSKReporter.cpp @@ -32,13 +32,13 @@ namespace { - constexpr QLatin1String HOST {"report.pskreporter.info"}; + // constexpr QLatin1String HOST {"report.pskreporter.info"}; // constexpr QLatin1String HOST {"127.0.0.1"}; - // constexpr QLatin1String HOST {"192.168.1.195"}; + constexpr QLatin1String HOST {"192.168.1.195"}; constexpr quint16 SERVICE_PORT {4739}; // constexpr quint16 SERVICE_PORT {14739}; - constexpr int MIN_SEND_INTERVAL {60}; // in seconds - constexpr int FLUSH_INTERVAL {5}; // in send intervals + constexpr int MIN_SEND_INTERVAL {15}; // in seconds + constexpr int FLUSH_INTERVAL {4 * 5}; // in send intervals constexpr bool ALIGNMENT_PADDING {true}; constexpr int MIN_PAYLOAD_LENGTH {508}; constexpr int MAX_PAYLOAD_LENGTH {1400}; @@ -363,7 +363,7 @@ void PSKReporter::impl::send_report (bool send_residue) auto flush = flushing () || send_residue; qDebug () << "PSKReporter::impl::send_report: flush:" << flush; - while (spots_.size () || (flush && (tx_data_.size () || tx_residue_.size ()))) + while (spots_.size () || flush) { if (!payload_.size ()) { @@ -371,7 +371,7 @@ void PSKReporter::impl::send_report (bool send_residue) build_preamble (message); } - if (!tx_data_.size ()) + if (!tx_data_.size () && (spots_.size () || tx_residue_.size ())) { // Set Header tx_out @@ -388,7 +388,7 @@ void PSKReporter::impl::send_report (bool send_residue) qDebug () << "PSKReporter::impl::send_report: inserted data residue"; } - while (spots_.size () || (flush && tx_data_.size ())) + while (spots_.size () || flush) { auto tx_data_size = tx_data_.size (); if (spots_.size ()) @@ -413,18 +413,21 @@ void PSKReporter::impl::send_report (bool send_residue) len += num_pad_bytes (len); if (len > MAX_PAYLOAD_LENGTH // our upper datagram size limit || (!spots_.size () && len > MIN_PAYLOAD_LENGTH) // spots drained and above lower datagram size limit - || (flush && !spots_.size () && tx_data_.size ())) // send what we have + || (flush && !spots_.size ())) // send what we have, possibly no spots { - if (len <= MAX_PAYLOAD_LENGTH) + if (tx_data_.size ()) { - tx_data_size = tx_data_.size (); - qDebug () << "PSKReporter::impl::send_report: sending short payload:" << tx_data_size; + if (len <= MAX_PAYLOAD_LENGTH) + { + tx_data_size = tx_data_.size (); + qDebug () << "PSKReporter::impl::send_report: sending short payload:" << tx_data_size; + } + QByteArray tx {tx_data_.left (tx_data_size)}; + QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append}; + // insert Length + set_length (out, tx); + message.writeRawData (tx.constData (), tx.size ()); } - QByteArray tx {tx_data_.left (tx_data_size)}; - QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append}; - // insert Length - set_length (out, tx); - message.writeRawData (tx.constData (), tx.size ()); // insert Length and Export Time set_length (message, payload_); @@ -434,6 +437,7 @@ void PSKReporter::impl::send_report (bool send_residue) // Send data to PSK Reporter site socket_->write (payload_); // TODO: handle errors qDebug () << "PSKReporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; + flush = false; // break loop message.device ()->seek (0u); payload_.clear (); // Fresh message // Save unsent spots From 843d491fdbb58bd9812e6e87393bec083e5a2aa8 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 26 Jul 2020 12:31:33 +0100 Subject: [PATCH 4/6] Mock PSKReporter.info Python script --- Network/tests/PSKReporter/PSKReporter.IESpec | 12 +++ Network/tests/PSKReporter/listener.py | 94 ++++++++++++++++++++ Network/tests/PSKReporter/python-ipfix.patch | 64 +++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 Network/tests/PSKReporter/PSKReporter.IESpec create mode 100644 Network/tests/PSKReporter/listener.py create mode 100644 Network/tests/PSKReporter/python-ipfix.patch diff --git a/Network/tests/PSKReporter/PSKReporter.IESpec b/Network/tests/PSKReporter/PSKReporter.IESpec new file mode 100644 index 000000000..8e95ffd08 --- /dev/null +++ b/Network/tests/PSKReporter/PSKReporter.IESpec @@ -0,0 +1,12 @@ +senderCallsign(30351/1)[65535] +receiverCallsign(30351/2)[65535] +senderLocator(30351/3)[65535] +receiverLocator(30351/4)[65535] +frequency(30351/5)[4] +sNR(30351/6)[1] +iMD(30351/7)[1] +decoderSoftware(30351/8)[65535] +antennaInformation(30351/9)[65535] +mode(30351/10)[65535] +informationSource(30351/11)[1] +persistentIdentifier(30351/12)[65535] diff --git a/Network/tests/PSKReporter/listener.py b/Network/tests/PSKReporter/listener.py new file mode 100644 index 000000000..b2e830e34 --- /dev/null +++ b/Network/tests/PSKReporter/listener.py @@ -0,0 +1,94 @@ +from __future__ import unicode_literals +import sys +import argparse +import logging +import time +import threading +import ipfix.ie +import ipfix.reader +import ipfix.message +import socketserver +import socket + +class IPFixDatagramHandler (socketserver.DatagramRequestHandler): + + def handle (self): + logging.info (f'Connection from {self.client_address}') + try: + self.server.msg_buffer.from_bytes (self.packet) + for rec in self.server.msg_buffer.namedict_iterator (): + logging.info (f't: {self.server.msg_buffer.get_export_time()}: {rec}') + except: + logging.error ('Unexpected exception:', sys.exc_info ()[0]) + + +class IPFixUdpServer (socketserver.UDPServer): + + def __init__ (self, *args, **kwargs): + + self.msg_buffer = ipfix.message.MessageBuffer () + super ().__init__ (*args, **kwargs) + +class IPFixStreamHandler (socketserver.StreamRequestHandler): + + def handle (self): + logging.info (f'Connection from {self.client_address}') + try: + msg_reader = ipfix.reader.from_stream (self.rfile) + for rec in msg_reader.namedict_iterator (): + logging.info (f't: {msg_reader.msg.get_export_time()}: {rec}') + logging.info (f'{self.client_address} closed their connection') + except ConnectionResetError: + logging.info (f'{self.client_address} connection reset') + except TimeoutError: + logging.info (f'{self.client_address} connection timed out') + except: + logging.error ('Unexpected exception:', sys.exc_info ()[0]) + + +if __name__ == "__main__": + INTERFACE, PORT = '', 4739 + + ap = argparse.ArgumentParser (description='Dump IPFIX data collectedover UDP') + ap.add_argument ('-l', '--log', metavar='loglevel', default='WARNING', help='logging level') + ap.add_argument ('-s', '--spec', metavar='specfile', help='iespec file to read') + args = ap.parse_args () + + log_level = getattr (logging, args.log.upper (), None) + if not isinstance (log_level, int): + raise ValueError (f'Invalif log level: {log_level}') + logging.basicConfig ( + level=log_level + , format='[%(levelname)s] (%(threadName)-10s) %(message)s' + , + ) + + logging.info (f'Starting IPFix servers on service port: {PORT}') + + ipfix.ie.use_iana_default () + ipfix.ie.use_5103_default () + if args.spec: + ipfix.ie.use_specfile (args.spec) + + udp_server = IPFixUdpServer ((INTERFACE, PORT), IPFixDatagramHandler) + udp_thread = threading.Thread (name='UDP Server', target=udp_server.serve_forever) + + tcp_server = socketserver.TCPServer ((INTERFACE, PORT), IPFixStreamHandler) + tcp_server.allow_reuse_address = True + tcp_server.socket.setsockopt (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + tcp_thread = threading.Thread (name='TCP/IP Server', target=tcp_server.serve_forever) + + udp_thread.start () + tcp_thread.start () + + try: + while True: + time.sleep (1000) + except KeyboardInterrupt: + logging.warning ('Closing down servers') + udp_server.shutdown () + tcp_server.shutdown () + + udp_thread.join () + tcp_thread.join () + logging.info ('Servers closed') diff --git a/Network/tests/PSKReporter/python-ipfix.patch b/Network/tests/PSKReporter/python-ipfix.patch new file mode 100644 index 000000000..cb6797094 --- /dev/null +++ b/Network/tests/PSKReporter/python-ipfix.patch @@ -0,0 +1,64 @@ +The following changes since commit e487dfbead9965c1a251d110dc55039a7ba4afef: + + sphinx doc update (2017-09-20 16:07:22 +0200) + +are available in the Git repository at: + + git@github.com:g4wjs/python-ipfix.git varlen-and-padding + +for you to fetch changes up to 6dcc106f22d25ac21b27f8ccb9b82be12e7eb18e: + + Parse and emit data elements correctly when variable length items included (2020-06-19 19:53:33 +0100) + +---------------------------------------------------------------- +Bill Somerville (2): + Allow for alignment padding when parsing sets + Parse and emit data elements correctly when variable length items included + + ipfix/message.py | 3 ++- + ipfix/template.py | 4 ++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/ipfix/message.py b/ipfix/message.py +index 0af2fad..71c1328 100644 +--- a/ipfix/message.py ++++ b/ipfix/message.py +@@ -398,7 +398,7 @@ class MessageBuffer(object): + offset += _sethdr_st.size # skip set header in decode + if setid == template.TEMPLATE_SET_ID or\ + setid == template.OPTIONS_SET_ID: +- while offset < setend: ++ while offset + 4 < setend: # allow for padding up to 4 byte alignment + (tmpl, offset) = template.decode_template_from( + self.mbuf, offset, setid) + # FIXME handle withdrawal +@@ -431,6 +431,7 @@ class MessageBuffer(object): + # KeyError on template lookup - unknown data set + self.unknown_data_set_hook(self, + self.mbuf[offset-_sethdr_st.size:setend]) ++ offset = setend # real end may be greater that accumulated offset + + def namedict_iterator(self): + """ +diff --git a/ipfix/template.py b/ipfix/template.py +index 1203e55..26cd8c1 100644 +--- a/ipfix/template.py ++++ b/ipfix/template.py +@@ -187,7 +187,7 @@ class Template(object): + offset += packplan.st.size + + # short circuit on no varlen +- if not self.varlenslice: ++ if self.varlenslice is None: + return (vals, offset) + + # direct iteration over remaining IEs +@@ -239,7 +239,7 @@ class Template(object): + offset += packplan.st.size + + # shortcircuit no varlen +- if not self.varlenslice: ++ if self.varlenslice is None: + return offset + + # direct iteration over remaining IEs From 1f524026f912a9ded4ca3aa5291348313fa7dd12 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 26 Jul 2020 15:55:17 +0100 Subject: [PATCH 5/6] Fix merging issue --- Network/MessageClient.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Network/MessageClient.cpp b/Network/MessageClient.cpp index 54abd9252..b545dd44e 100644 --- a/Network/MessageClient.cpp +++ b/Network/MessageClient.cpp @@ -428,24 +428,22 @@ MessageClient::MessageClient (QString const& id, QString const& version, QString { connect (&*m_ #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - connect (&*m_, static_cast (&impl::error) - , [this] (impl::SocketError e) { + , static_cast (&impl::error), [this] (impl::SocketError e) #else - connect (&*m_, &impl::errorOccurred, [this] (impl::SocketError e) { + , &impl::errorOccurred, [this] (impl::SocketError e) #endif + { #if defined (Q_OS_WIN) - // take this out when Qt 5.5 stops doing this spuriously - if (e != impl::NetworkError - // not interested in this with UDP socket - && e != impl::ConnectionRefusedError) + if (e != impl::NetworkError // take this out when Qt 5.5 stops doing this spuriously + && e != impl::ConnectionRefusedError) // not interested in this with UDP socket + { #else - Q_UNUSED (e); + { + Q_UNUSED (e); #endif - { - Q_EMIT error (m_->errorString ()); - } - }); - + Q_EMIT error (m_->errorString ()); + } + }); set_server (server); } From 8b0b2c15b23af322998ed4c66edac817f6a69039 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 26 Jul 2020 15:55:54 +0100 Subject: [PATCH 6/6] Final adjustments for moving from test to live PSKReporter spotting --- Network/PSKReporter.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Network/PSKReporter.cpp b/Network/PSKReporter.cpp index 5ffd87113..56f773896 100644 --- a/Network/PSKReporter.cpp +++ b/Network/PSKReporter.cpp @@ -32,9 +32,8 @@ namespace { - // constexpr QLatin1String HOST {"report.pskreporter.info"}; + constexpr QLatin1String HOST {"report.pskreporter.info"}; // constexpr QLatin1String HOST {"127.0.0.1"}; - constexpr QLatin1String HOST {"192.168.1.195"}; constexpr quint16 SERVICE_PORT {4739}; // constexpr quint16 SERVICE_PORT {14739}; constexpr int MIN_SEND_INTERVAL {15}; // in seconds @@ -254,7 +253,7 @@ void PSKReporter::impl::build_preamble (QDataStream& message) << ++sequence_number_ // Sequence Number << observation_id_; // Observation Domain ID - qDebug () << "PSKReporter::impl::build_preamble: send_descriptors_:" << send_descriptors_; + // qDebug () << "PSKReporter::impl::build_preamble: send_descriptors_:" << send_descriptors_; if (send_descriptors_) { --send_descriptors_; @@ -319,7 +318,7 @@ void PSKReporter::impl::build_preamble (QDataStream& message) } } - qDebug () << "PSKReporter::impl::build_preamble: send_receiver_data_:" << send_receiver_data_; + // qDebug () << "PSKReporter::impl::build_preamble: send_receiver_data_:" << send_receiver_data_; // if (send_receiver_data_) { // --send_receiver_data_; @@ -348,7 +347,7 @@ void PSKReporter::impl::build_preamble (QDataStream& message) void PSKReporter::impl::send_report (bool send_residue) { check_connection (); - qDebug () << "PSKReporter::impl::send_report: send_residue:" << send_residue; + // qDebug () << "PSKReporter::impl::send_report: send_residue:" << send_residue; if (QAbstractSocket::ConnectedState != socket_->state ()) return; QDataStream message {&payload_, QIODevice::WriteOnly | QIODevice::Append}; @@ -356,13 +355,13 @@ void PSKReporter::impl::send_report (bool send_residue) if (!payload_.size ()) { - qDebug () << "PSKReporter::impl::send_report: building header"; + // qDebug () << "PSKReporter::impl::send_report: building header"; // Build header, optional descriptors, and receiver information build_preamble (message); } auto flush = flushing () || send_residue; - qDebug () << "PSKReporter::impl::send_report: flush:" << flush; + // qDebug () << "PSKReporter::impl::send_report: flush:" << flush; while (spots_.size () || flush) { if (!payload_.size ()) @@ -377,7 +376,7 @@ void PSKReporter::impl::send_report (bool send_residue) tx_out << quint16 (0x50e3) // Template ID << quint16 (0u); // Length (place-holder) - qDebug () << "PSKReporter::impl::send_report: set data set header"; + // qDebug () << "PSKReporter::impl::send_report: set data set header"; } // insert any residue @@ -385,7 +384,7 @@ void PSKReporter::impl::send_report (bool send_residue) { tx_out.writeRawData (tx_residue_.constData (), tx_residue_.size ()); tx_residue_.clear (); - qDebug () << "PSKReporter::impl::send_report: inserted data residue"; + // qDebug () << "PSKReporter::impl::send_report: inserted data residue"; } while (spots_.size () || flush) @@ -394,7 +393,7 @@ void PSKReporter::impl::send_report (bool send_residue) if (spots_.size ()) { auto const& spot = spots_.dequeue (); - qDebug () << "PSKReporter::impl::send_report: processing spotted call:" << spot.call_; + // qDebug () << "PSKReporter::impl::send_report: processing spotted call:" << spot.call_; // Sender information writeUtfString (tx_out, spot.call_); @@ -420,7 +419,7 @@ void PSKReporter::impl::send_report (bool send_residue) if (len <= MAX_PAYLOAD_LENGTH) { tx_data_size = tx_data_.size (); - qDebug () << "PSKReporter::impl::send_report: sending short payload:" << tx_data_size; + // qDebug () << "PSKReporter::impl::send_report: sending short payload:" << tx_data_size; } QByteArray tx {tx_data_.left (tx_data_size)}; QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append}; @@ -436,7 +435,7 @@ void PSKReporter::impl::send_report (bool send_residue) // Send data to PSK Reporter site socket_->write (payload_); // TODO: handle errors - qDebug () << "PSKReporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; + // qDebug () << "PSKReporter::impl::send_report: sent payload:" << payload_.size () << "observation id:" << observation_id_; flush = false; // break loop message.device ()->seek (0u); payload_.clear (); // Fresh message @@ -444,7 +443,7 @@ void PSKReporter::impl::send_report (bool send_residue) tx_residue_ = tx_data_.right (tx_data_.size () - tx_data_size); tx_out.device ()->seek (0u); tx_data_.clear (); - qDebug () << "PSKReporter::impl::send_report: payload sent residue length:" << tx_residue_.size (); + // qDebug () << "PSKReporter::impl::send_report: payload sent residue length:" << tx_residue_.size (); break; } }