Merge branch 'develop' of bitbucket.org:k1jt/wsjtx into develop

This commit is contained in:
Joe Taylor 2020-07-27 08:34:58 -04:00
commit 2905c95278
38 changed files with 10817 additions and 2751 deletions

View File

@ -289,6 +289,7 @@ set (wsjt_qt_CXXSRCS
logbook/AD1CCty.cpp
logbook/WorkedBefore.cpp
logbook/Multiplier.cpp
Network/NetworkAccessManager.cpp
)
set (wsjt_qtmm_CXXSRCS
@ -302,7 +303,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
@ -846,6 +847,8 @@ endif (APPLE)
#
# find some useful tools
#
include (CheckSymbolExists)
find_program(CTAGS ctags)
find_program(ETAGS etags)
@ -881,6 +884,10 @@ message (STATUS "hamlib_INCLUDE_DIRS: ${hamlib_INCLUDE_DIRS}")
message (STATUS "hamlib_LIBRARIES: ${hamlib_LIBRARIES}")
message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}")
set (CMAKE_REQUIRED_INCLUDES "${hamlib_INCLUDE_DIRS}")
set (CMAKE_REQUIRED_LIBRARIES "${hamlib_LIBRARIES}")
check_symbol_exists (rig_set_cache_timeout_ms "hamlib/rig.h" HAVE_HAMLIB_CACHING)
#
# Qt5 setup
@ -1098,7 +1105,7 @@ add_custom_target (etags COMMAND ${ETAGS} -o ${CMAKE_SOURCE_DIR}/TAGS -R ${sourc
# Qt i18n - always include the country generic if any regional variant is included
set (LANGUAGES
ca # Catalan
#da # Danish
da # Danish
en # English (we need this to stop
# translation loaders loading the
# second preference UI languge, it
@ -1132,6 +1139,7 @@ if (UPDATE_TRANSLATIONS)
qt5_create_translation (
QM_FILES ${wsjt_qt_UISRCS} ${wsjtx_UISRCS} ${wsjt_qt_CXXSRCS} ${wsjtx_CXXSRCS}
${TS_FILES}
OPTIONS -I${CMAKE_CURRENT_SOURCE_DIR}
)
else ()
qt5_add_translation (QM_FILES ${TS_FILES})

View File

@ -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 ();

View File

@ -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;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>557</width>
<height>561</height>
<width>559</width>
<height>553</height>
</rect>
</property>
<property name="windowTitle">
@ -1800,20 +1800,27 @@ and DX Grid fields when a 73 or free text message is sent.</string>
<property name="title">
<string>Network Services</string>
</property>
<layout class="QGridLayout" name="gridLayout_17">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<widget class="QCheckBox" name="psk_reporter_check_box">
<property name="toolTip">
<string>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.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The program can send your station details and all decoded signals with grid squares as spots to the http://pskreporter.info web site.&lt;/p&gt;&lt;p&gt;This is used for reverse beacon analysis which is very useful for assessing propagation and system performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable &amp;PSK Reporter Spotting</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="psk_reporter_tcpip_check_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check this option if a reliable connection is needed&lt;/p&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use TCP/IP connection</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -3108,13 +3115,13 @@ Right click for insert and delete options.</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
</buttongroups>
</ui>

View File

@ -165,7 +165,13 @@ QString DecodedText::call() const
// get the second word, most likely the de call and the third word, most likely grid
void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) const
{
auto const& match = words_re.match (message_);
auto msg = message_;
auto p = msg.indexOf ("; ");
if (p >= 0)
{
msg = msg.mid (p + 2);
}
auto const& match = words_re.match (msg);
call = match.captured ("word2");
grid = match.captured ("word3");
if ("R" == grid) grid = match.captured ("word4");

View File

@ -292,7 +292,7 @@ EqualizationToolsDialog::impl::impl (EqualizationToolsDialog * self
| QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Close
, Qt::Vertical}
{
setWindowTitle (windowTitle () + ' ' + tr (title));
setWindowTitle (windowTitle () + ' ' + tr ("Equalization Tools"));
resize (500, 600);
{
SettingsGroup g {settings_, title};

View File

@ -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,25 +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<void (impl::*) (impl::SocketError)> (&impl::error)
, [this] (impl::SocketError e) {
, static_cast<void (impl::*) (impl::SocketError)> (&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);
}
@ -459,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);
}
}
@ -472,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;

View File

@ -76,15 +76,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);

View File

@ -0,0 +1,62 @@
#include "Network/NetworkAccessManager.hpp"
#include <QString>
#include <QNetworkReply>
#include "moc_NetworkAccessManager.cpp"
NetworkAccessManager::NetworkAccessManager (QWidget * parent)
: QNetworkAccessManager (parent)
, parent_widget_ {parent}
{
// handle SSL errors that have not been cached as allowed
// exceptions and offer them to the user to add to the ignored
// exception cache
connect (this, &QNetworkAccessManager::sslErrors, this, &NetworkAccessManager::filter_SSL_errors);
}
void NetworkAccessManager::filter_SSL_errors (QNetworkReply * reply, QList<QSslError> const& errors)
{
QString message;
QList<QSslError> new_errors;
for (auto const& error: errors)
{
if (!allowed_ssl_errors_.contains (error))
{
new_errors << error;
message += '\n' + reply->request ().url ().toDisplayString () + ": " + error.errorString ();
}
}
if (new_errors.size ())
{
QString certs;
for (auto const& cert : reply->sslConfiguration ().peerCertificateChain ())
{
certs += cert.toText () + '\n';
}
if (MessageBox::Ignore == MessageBox::query_message (parent_widget_
, tr ("Network SSL/TLS Errors")
, message, certs
, MessageBox::Abort | MessageBox::Ignore))
{
// accumulate new SSL error exceptions that have been allowed
allowed_ssl_errors_.append (new_errors);
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
else
{
// no new exceptions so silently ignore the ones already allowed
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
QNetworkReply * NetworkAccessManager::createRequest (Operation operation, QNetworkRequest const& request
, QIODevice * outgoing_data)
{
auto reply = QNetworkAccessManager::createRequest (operation, request, outgoing_data);
// errors are usually certificate specific so passing all cached
// exceptions here is ok
reply->ignoreSslErrors (allowed_ssl_errors_);
return reply;
}

View File

@ -4,8 +4,6 @@
#include <QNetworkAccessManager>
#include <QList>
#include <QSslError>
#include <QNetworkReply>
#include <QString>
#include "widgets/MessageBox.hpp"
@ -18,58 +16,18 @@ class QWidget;
class NetworkAccessManager
: public QNetworkAccessManager
{
Q_OBJECT
public:
NetworkAccessManager (QWidget * parent)
: QNetworkAccessManager (parent)
{
// handle SSL errors that have not been cached as allowed
// exceptions and offer them to the user to add to the ignored
// exception cache
connect (this, &QNetworkAccessManager::sslErrors, [this, &parent] (QNetworkReply * reply, QList<QSslError> const& errors) {
QString message;
QList<QSslError> new_errors;
for (auto const& error: errors)
{
if (!allowed_ssl_errors_.contains (error))
{
new_errors << error;
message += '\n' + reply->request ().url ().toDisplayString () + ": "
+ error.errorString ();
}
}
if (new_errors.size ())
{
QString certs;
for (auto const& cert : reply->sslConfiguration ().peerCertificateChain ())
{
certs += cert.toText () + '\n';
}
if (MessageBox::Ignore == MessageBox::query_message (parent, tr ("Network SSL Errors"), message, certs, MessageBox::Abort | MessageBox::Ignore))
{
// accumulate new SSL error exceptions that have been allowed
allowed_ssl_errors_.append (new_errors);
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
else
{
// no new exceptions so silently ignore the ones already allowed
reply->ignoreSslErrors (allowed_ssl_errors_);
}
});
}
explicit NetworkAccessManager (QWidget * parent);
protected:
QNetworkReply * createRequest (Operation operation, QNetworkRequest const& request, QIODevice * outgoing_data = nullptr) override
{
auto reply = QNetworkAccessManager::createRequest (operation, request, outgoing_data);
// errors are usually certificate specific so passing all cached
// exceptions here is ok
reply->ignoreSslErrors (allowed_ssl_errors_);
return reply;
}
QNetworkReply * createRequest (Operation, QNetworkRequest const&, QIODevice * = nullptr) override;
private:
void filter_SSL_errors (QNetworkReply * reply, QList<QSslError> const& errors);
QWidget * parent_widget_;
QList<QSslError> allowed_ssl_errors_;
};

496
Network/PSKReporter.cpp Normal file
View File

@ -0,0 +1,496 @@
#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 <cmath>
#include <QObject>
#include <QString>
#include <QDateTime>
#include <QSharedPointer>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QHostInfo>
#include <QQueue>
#include <QByteArray>
#include <QDataStream>
#include <QTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
#endif
#include <QDebug>
#include "Configuration.hpp"
#include "pimpl_impl.hpp"
#include "moc_PSKReporter.cpp"
namespace
{
QLatin1String HOST {"report.pskreporter.info"};
// QLatin1String HOST {"127.0.0.1"};
quint16 SERVICE_PORT {4739};
// quint16 SERVICE_PORT {14739};
int MIN_SEND_INTERVAL {15}; // in seconds
int FLUSH_INTERVAL {4 * 5}; // in send intervals
bool ALIGNMENT_PADDING {true};
int MIN_PAYLOAD_LENGTH {508};
int MAX_PAYLOAD_LENGTH {1400};
}
class PSKReporter::impl final
: public QObject
{
Q_OBJECT
public:
impl (PSKReporter * 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<QMetaObject::Connection>::create ();
*connection = connect (socket_.data (), &QAbstractSocket::disconnected, [this, connection] () {
qDebug () << "PSKReporter::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 () << "PSKReporter::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, &PSKReporter::impl::handle_socket_error);
#else
connect (socket_.data (), QOverload<QAbstractSocket::SocketError>::of (&QAbstractSocket::error), this, &PSKReporter::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);
}
PSKReporter * self_;
Configuration const * config_;
QSharedPointer<QAbstractSocket> 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<Spot> spots_;
QTimer report_timer_;
QTimer descriptor_timer_;
};
#include "PSKReporter.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 ());
}
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<quint16> (b.size ());
out.device ()->seek (pos);
}
}
void PSKReporter::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 () << "PSKReporter::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 () << "PSKReporter::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 PSKReporter::impl::send_report (bool send_residue)
{
check_connection ();
// qDebug () << "PSKReporter::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 () << "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;
while (spots_.size () || flush)
{
if (!payload_.size ())
{
// Build header, optional descriptors, and receiver information
build_preamble (message);
}
if (!tx_data_.size () && (spots_.size () || tx_residue_.size ()))
{
// Set Header
tx_out
<< quint16 (0x50e3) // Template ID
<< quint16 (0u); // Length (place-holder)
// qDebug () << "PSKReporter::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 () << "PSKReporter::impl::send_report: inserted data residue";
}
while (spots_.size () || flush)
{
auto tx_data_size = tx_data_.size ();
if (spots_.size ())
{
auto const& spot = spots_.dequeue ();
// qDebug () << "PSKReporter::impl::send_report: processing spotted call:" << spot.call_;
// Sender information
writeUtfString (tx_out, spot.call_);
tx_out
<< static_cast<quint32> (spot.freq_)
<< static_cast<qint8> (spot.snr_);
writeUtfString (tx_out, spot.mode_);
writeUtfString (tx_out, spot.grid_);
tx_out
<< quint8 (1u) // REPORTER_SOURCE_AUTOMATIC
<< static_cast<quint32> (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 ())) // send what we have, possibly no spots
{
if (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 ());
}
// insert Length and Export Time
set_length (message, payload_);
message.device ()->seek (2 * sizeof (quint16));
message << static_cast<quint32> (QDateTime::currentDateTime ().toSecsSinceEpoch ());
// 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
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 ();
break;
}
}
}
}
PSKReporter::PSKReporter (Configuration const * config, QString const& program_info)
: m_ {this, config, program_info}
{
}
PSKReporter::~PSKReporter ()
{
// m_->send_report (true); // send any pending spots
}
void PSKReporter::reconnect ()
{
m_->reconnect ();
}
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_)
{
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 PSKReporter::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 PSKReporter::sendReport ()
{
m_->send_report (true);
}

41
Network/PSKReporter.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef PSK_REPORTER_HPP_
#define PSK_REPORTER_HPP_
#include <QObject>
#include "Radio.hpp"
#include "pimpl_h.hpp"
class QString;
class Configuration;
class PSKReporter final
: public QObject
{
Q_OBJECT
public:
explicit PSKReporter (Configuration const *, QString const& program_info);
~PSKReporter ();
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<impl> m_;
};
#endif

View File

@ -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 <QHostInfo>
#include <QTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
#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<QString,QString> 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<QString,QString> 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);
}
}

View File

@ -1,54 +0,0 @@
// -*- Mode: C++ -*-
#ifndef PSK_REPORTER_H
#define PSK_REPORTER_H
#include <QObject>
#include <QString>
#include <QHostAddress>
#include <QQueue>
#include <QHash>
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<QString,QString> > m_spotQueue;
MessageClient * m_messageClient;
QTimer *reportTimer;
int m_sequenceNumber;
};
#endif // PSK_REPORTER_H

View File

@ -0,0 +1,12 @@
senderCallsign(30351/1)<string>[65535]
receiverCallsign(30351/2)<string>[65535]
senderLocator(30351/3)<string>[65535]
receiverLocator(30351/4)<string>[65535]
frequency(30351/5)<unsigned32>[4]
sNR(30351/6)<signed8>[1]
iMD(30351/7)<signed8>[1]
decoderSoftware(30351/8)<string>[65535]
antennaInformation(30351/9)<string>[65535]
mode(30351/10)<string>[65535]
informationSource(30351/11)<signed8>[1]
persistentIdentifier(30351/12)<string>[65535]

View File

@ -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')

View File

@ -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

View File

@ -88,7 +88,7 @@ SampleDownloader::impl::impl (QSettings * settings
, directory_ {configuration, network_manager}
, button_box_ {QDialogButtonBox::Close, Qt::Vertical}
{
setWindowTitle (windowTitle () + ' ' + tr (title));
setWindowTitle (windowTitle () + ' ' + tr ("Download Samples"));
resize (500, 600);
{
SettingsGroup g {settings_, title};
@ -111,7 +111,7 @@ SampleDownloader::impl::impl (QSettings * settings
details_layout_.setMargin (0);
details_layout_.addRow (tr ("Base URL for samples:"), &url_line_edit_);
details_layout_.addRow (tr ("Only use HTTP:"), &http_only_check_box_);
http_only_check_box_.setToolTip (tr ("Check this is you get SSL/TLS errors"));
http_only_check_box_.setToolTip (tr ("Check this if you get SSL/TLS errors"));
details_widget_.setLayout (&details_layout_);
main_layout_.addLayout (&left_layout_, 0, 0);

View File

@ -409,7 +409,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
tr ("DX Lab Suite Commander send command failed \"%1\": %2\n")
.arg (cmd)
.arg (commander_->errorString ())
};

View File

@ -606,6 +606,13 @@ int HamlibTransceiver::do_start ()
}
}
#if HAVE_HAMLIB_CACHING
// we must disable Hamlib caching because it lies about frequency
// for less than 1 Hz resolution rigs
auto orig_cache_timeout = rig_get_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL);
rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, 0);
#endif
int resolution {0};
if (freq_query_works_)
{
@ -646,6 +653,11 @@ int HamlibTransceiver::do_start ()
resolution = -1; // best guess
}
#if HAVE_HAMLIB_CACHING
// revert Hamlib cache timeout
rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, orig_cache_timeout);
#endif
do_poll ();
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
@ -672,7 +684,7 @@ void HamlibTransceiver::do_stop ()
TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_);
}
auto HamlibTransceiver::get_vfos (bool for_split) const -> std::tuple<vfo_t, vfo_t>
std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
{
if (get_vfo_works_ && rig_->caps->get_vfo)
{

View File

@ -5,10 +5,10 @@ Usage example: include::../common/links.adoc[]
Syntax: [link-id] [link] [displayed text]
Example:
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter]
:pskreporter: https://pskreporter.info/pskmap.html[PSK Reporter]
[link-id] = :pskreporter:
[link] http://pskreporter.info/pskmap.html
[link] https://pskreporter.info/pskmap.html
[displayed text] PSK Reporter
Perform searches from the doc root directory: doc
@ -42,53 +42,53 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
// General URL's
//:launchpadac6sl: https://launchpad.net/~jnogatch/+archive/wsjtx[WSJT-X Linux Packages]
:alarmejt: http://f5jmh.free.fr/index.php?page=english[AlarmeJT]
:asciidoc_cheatsheet: http://powerman.name/doc/asciidoc[AsciiDoc Cheatsheet]
:asciidoc_help: http://www.methods.co.nz/asciidoc/userguide.html[AsciiDoc User Guide]
:asciidoc_questions: http://www.methods.co.nz/asciidoc/faq.html[AsciiDoc FAQ]
:asciidoc_cheatsheet: https://powerman.name/doc/asciidoc[AsciiDoc Cheatsheet]
:asciidoc_help: https://www.methods.co.nz/asciidoc/userguide.html[AsciiDoc User Guide]
:asciidoc_questions: https://www.methods.co.nz/asciidoc/faq.html[AsciiDoc FAQ]
:asciidoc_syntax: http://xpt.sourceforge.net/techdocs/nix/tool/asciidoc-syn/ascs01-AsciiDocMarkupSyntaxQuickSummary/single/[AsciiDoc Syntax]
:asciidoctor_style: http://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Styles Guide]
:asciidoctor_syntax: http://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Syntax Guide]
:cc_by_sa: http://creativecommons.org/licenses/by-sa/3.0/[Commons Attribution-ShareAlike 3.0 Unported License]
:asciidoctor_style: https://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Styles Guide]
:asciidoctor_syntax: https://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Syntax Guide]
:cc_by_sa: https://creativecommons.org/licenses/by-sa/3.0/[Commons Attribution-ShareAlike 3.0 Unported License]
:debian32: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_i386.deb[wsjtx_{VERSION}_i386.deb]
:debian64: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_amd64.deb[wsjtx_{VERSION}_amd64.deb]
:raspbian: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_armhf.deb[wsjtx_{VERSION}_armhf.deb]
:debian: http://www.debian.org/[Debian]
:dev_guide: http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide]
:devsvn: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN]
:debian: https://www.debian.org/[Debian]
:dev_guide: https://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide]
:devsvn: https://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN]
:devrepo: https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/[SourceForge]
:dimension4: http://www.thinkman.com/dimension4/[Thinking Man Software]
:download: https://physics.princeton.edu/pulsar/K1JT/wsjtx.html[Download Page]
:dxatlas: http://www.dxatlas.com/[Afreet Software, Inc.]
:dxlcommander: http://www.dxlabsuite.com/commander/[Commander]
:dxlsuite: http://www.dxlabsuite.com/[DX Lab Suite]
:dxlcommander: https://www.dxlabsuite.com/commander/[Commander]
:dxlsuite: https://www.dxlabsuite.com/[DX Lab Suite]
:fedora32: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-i686.rpm[wsjtx-{VERSION}-i686.rpm]
:fedora64: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-x86_64.rpm[wsjtx-{VERSION}-x86_64.rpm]
:fmt_arrl: http://www.arrl.org/frequency-measuring-test[ARRL FMT Info]
:fmt_arrl: https://www.arrl.org/frequency-measuring-test[ARRL FMT Info]
:fmt_group: https://groups.yahoo.com/neo/groups/FMT-nuts/info[FMT Group]
:fmt_k5cm: http://www.k5cm.com/[FMT Event Info]
:fmt_wspr: http://www.physics.princeton.edu/pulsar/K1JT/FMT_User.pdf[Accurate Frequency Measurements with your WSPR Setup]
:fmt_wspr: https://www.physics.princeton.edu/pulsar/K1JT/FMT_User.pdf[Accurate Frequency Measurements with your WSPR Setup]
:ft4_protocol: https://physics.princeton.edu/pulsar/k1jt/FT4_Protocol.pdf[The FT4 Protocol for Digital Contesting]
:ft4_ft8_protocols: https://physics.princeton.edu/pulsar/k1jt/FT4_FT8_QEX.pdf[The FT4 and FT8 Communication Protocols]
:ft8_tips: http://www.g4ifb.com/FT8_Hinson_tips_for_HF_DXers.pdf[FT8 Operating Guide]
:ft8_tips: https://www.g4ifb.com/FT8_Hinson_tips_for_HF_DXers.pdf[FT8 Operating Guide]
:ft8_DXped: https://physics.princeton.edu/pulsar/k1jt/FT8_DXpedition_Mode.pdf[FT8 DXpedition Mode]
:gnu_gpl: http://www.gnu.org/licenses/gpl-3.0.txt[GNU General Public License]
:gnu_gpl: https://www.gnu.org/licenses/gpl-3.0.txt[GNU General Public License]
:homepage: https://physics.princeton.edu/pulsar/K1JT/[WSJT Home Page]
:hrd: http://www.hrdsoftwarellc.com/[Ham Radio Deluxe]
:jt4eme: https://physics.princeton.edu/pulsar/K1JT/WSJT-X_1.6.0_for_JT4_v7.pdf[Using WSJT-X for JT4 EME Operation]
:jt65protocol: https://physics.princeton.edu/pulsar/K1JT/JT65.pdf[QEX]
:jtalert: http://hamapps.com/[JTAlert]
:jtalert: https://hamapps.com/[JTAlert]
:launchpadki7mt: https://launchpad.net/~ki7mt[KI7MT PPA's]
:log4om: http://www.log4om.com[Log4OM]
:log4om: https://www.log4om.com[Log4OM]
:lunarEchoes: https://physics.princeton.edu/pulsar/K1JT/LunarEchoes_QEX.pdf[QEX]
:msk144: https://physics.princeton.edu/pulsar/k1jt/MSK144_Protocol_QEX.pdf[QEX]
:msvcpp_redist: https://www.microsoft.com/en-ph/download/details.aspx?id=40784[Microsoft VC++ 2013 Redistributable]
:msys_url: http://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/[MSYS Download]
:msys_url: https://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/[MSYS Download]
:n1mm_logger: https://n1mm.hamdocs.com/tiki-index.php[N1MM Logger+]
:ntpsetup: http://www.satsignal.eu/ntp/setup.html[Network Time Protocol Setup]
:ntpsetup: https://www.satsignal.eu/ntp/setup.html[Network Time Protocol Setup]
:osx_instructions: https://physics.princeton.edu/pulsar/K1JT/OSX_Readme[Mac OS X Install Instructions]
:ppa: http://en.wikipedia.org/wiki/Personal_Package_Archive[PPA]
:projsummary: http://sourceforge.net/projects/wsjt/[Project Summary]
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter]
:ppa: https://en.wikipedia.org/wiki/Personal_Package_Archive[PPA]
:projsummary: https://sourceforge.net/projects/wsjt/[Project Summary]
:pskreporter: https://pskreporter.info/pskmap.html[PSK Reporter]
:sourceforge: https://sourceforge.net/user/registration[SourceForge]
:sourceforge-jtsdk: https://sourceforge.net/projects/jtsdk[SourceForge JTSDK]
:ubuntu_sdk: https://launchpad.net/~ubuntu-sdk-team/+archive/ppa[Ubuntu SDK Notice]
@ -98,25 +98,25 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
:writelog: https://writelog.com/[Writelog]
:wsjtx_group: https://groups.io/g/WSJTX[WSJTX Group]
:wsjtx: https://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X]
:wspr0_guide: http://www.physics.princeton.edu/pulsar/K1JT/WSPR0_Instructions.TXT[WSPR0 Guide]
:wspr0_guide: https://www.physics.princeton.edu/pulsar/K1JT/WSPR0_Instructions.TXT[WSPR0 Guide]
:wspr: https://physics.princeton.edu/pulsar/K1JT/wspr.html[WSPR Home Page]
:wsprnet: http://wsprnet.org/drupal/[WSPRnet]
:wsprnet_activity: http://wsprnet.org/drupal/wsprnet/activity[WSPRnet Activity page]
:wsprnet: https://wsprnet.org/drupal/[WSPRnet]
:wsprnet_activity: https://wsprnet.org/drupal/wsprnet/activity[WSPRnet Activity page]
// Download Links
:cty_dat: http://www.country-files.com/cty/[Amateur Radio Country Files]
:jtbridge: http://jt-bridge.eller.nu/[JT-Bridge]
:cty_dat: https://www.country-files.com/cty/[Amateur Radio Country Files]
:jtbridge: https://jt-bridge.eller.nu/[JT-Bridge]
:jtsdk_doc: https://physics.princeton.edu/pulsar/K1JT/JTSDK-DOC.exe[Download]
:jtsdk_installer: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/JTSDK-2.0.0-B2-Win32.exe/download[Download]
:jtsdk_omnirig: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/OmniRig.zip/download[Download]
:jtsdk_installer: https://sourceforge.net/projects/jtsdk/files/win32/2.0.0/JTSDK-2.0.0-B2-Win32.exe/download[Download]
:jtsdk_omnirig: https://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/OmniRig.zip/download[Download]
:jtsdk_py: https://physics.princeton.edu/pulsar/K1JT/JTSDK-PY.exe[Download]
:jtsdk_qt: https://physics.princeton.edu/pulsar/K1JT/JTSDK-QT.exe[Download]
:jtsdk_vcredist: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/vcredist_x86.exe/download[Download]
:jtsdk_vcredist: https://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/vcredist_x86.exe/download[Download]
:nh6z: http://www.nh6z.net/Amatuer_Radio_Station_NH6Z/Other_Peoples_Software.html[here]
:omnirig: http://www.dxatlas.com/OmniRig/Files/OmniRig.zip[Omni-Rig]
:osx: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-Darwin.dmg[wsjtx-{VERSION}-Darwin.dmg]
:QRA64_EME: https://physics.princeton.edu/pulsar/K1JT/QRA64_EME.pdf[QRA64 for microwave EME]
:svn: http://subversion.apache.org/packages.html#windows[Subversion]
:svn: https://subversion.apache.org/packages.html#windows[Subversion]
:win32: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win32.exe[wsjtx-{VERSION}-win32.exe]
:win64: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win64.exe[wsjtx-{VERSION}-win64.exe]
:wsjt-devel: https://lists.sourceforge.net/lists/listinfo/wsjt-devel[here]
@ -127,7 +127,7 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
// MAIL-TO links
:alex_efros: mailto:powerman@powerman.name[Alex Efros]
:bill_somerville: mailto:g4wjs -at- c l a s s d e s i g n -dot- com [G4WJS]
:dev_mail_list: http://sourceforge.net/mailarchive/forum.php?forum_name=wsjt-devel[WSJT Developers Email List]
:dev_mail_list: https://sourceforge.net/mailarchive/forum.php?forum_name=wsjt-devel[WSJT Developers Email List]
:dev_mail_svn: https://sourceforge.net/auth/subscriptions/[WSJT SVN Archives]
:devmail: mailto:wsjt-devel@lists.sourceforge.net[wsjt-devel@lists.sourceforge.net]
:devmail1: mailto:wsjt-devel@lists.sourceforge.net[Post Message]

View File

@ -39,12 +39,12 @@ passband, if such control is available.
How should I configure _WSJT-X_ to run multiple instances?::
Start _WSJT-X_ from a command-prompt window, assigning each instance a
unique identifier as in the following two-instance example. This
procedure will isolate the *Settings* file and the writable file
location for each instance of _WSJT-X_.
Start _WSJT-X_ from a command-prompt window, assigning each a unique
identifier as in the following two-instance example. This procedure
will isolate the *Settings* file and the writable file location for
each instance of _WSJT-X_.
wsjtx --rig-name=TS2000
wsjtx --rig-name=TS590
wsjtx --rig-name=FT847
I am getting a "Network Error - SSL/TLS support not installed" message. What should I do?::

View File

@ -2,8 +2,8 @@ module packjt77
! These variables are accessible from outside via "use packjt77":
parameter (MAXHASH=1000,MAXRECENT=10)
character (len=13), dimension(1:1024) :: calls10=''
character (len=13), dimension(1:4096) :: calls12=''
character (len=13), dimension(0:1023) :: calls10=''
character (len=13), dimension(0:4095) :: calls12=''
character (len=13), dimension(1:MAXHASH) :: calls22=''
character (len=13), dimension(1:MAXRECENT) :: recent_calls=''
character (len=13) :: mycall13=''
@ -19,7 +19,7 @@ subroutine hash10(n10,c13)
character*13 c13
c13='<...>'
if(n10.lt.1 .or. n10.gt.1024) return
if(n10.lt.0 .or. n10.gt.1023) return
if(len(trim(calls10(n10))).gt.0) then
c13=calls10(n10)
c13='<'//trim(c13)//'>'
@ -33,7 +33,7 @@ subroutine hash12(n12,c13)
character*13 c13
c13='<...>'
if(n12.lt.1 .or. n12.gt.4096) return
if(n12.lt.0 .or. n12.gt.4095) return
if(len(trim(calls12(n12))).gt.0) then
c13=calls12(n12)
c13='<'//trim(c13)//'>'
@ -90,10 +90,10 @@ subroutine save_hash_call(c13,n10,n12,n22)
if(len(trim(cw)) .lt. 3) return
n10=ihashcall(cw,10)
if(n10.ge.1 .and. n10 .le. 1024 .and. cw.ne.mycall13) calls10(n10)=cw
if(n10.ge.0 .and. n10 .le. 1023 .and. cw.ne.mycall13) calls10(n10)=cw
n12=ihashcall(cw,12)
if(n12.ge.1 .and. n12 .le. 4096 .and. cw.ne.mycall13) calls12(n12)=cw
if(n12.ge.0 .and. n12 .le. 4095 .and. cw.ne.mycall13) calls12(n12)=cw
n22=ihashcall(cw,22)
if(any(ihash22.eq.n22)) then ! If entry exists, make sure callsign is the most recently received one

View File

@ -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;
}
}
@ -353,14 +353,41 @@ int main(int argc, char *argv[])
// Multiple instances: use rig_name as shared memory key
mem_jt9.setKey(a.applicationName ());
if(!mem_jt9.attach()) {
if (!mem_jt9.create(sizeof(struct dec_data))) {
splash.hide ();
MessageBox::critical_message (nullptr, a.translate ("main", "Shared memory error"),
a.translate ("main", "Unable to create shared memory segment"));
throw std::runtime_error {"Shared memory error"};
// try and shut down any orphaned jt9 process
for (int i = 3; i; --i) // three tries to close old jt9
{
if (mem_jt9.attach ()) // shared memory presence implies
// orphaned jt9 sub-process
{
dec_data_t * dd = reinterpret_cast<dec_data_t *> (mem_jt9.data());
mem_jt9.lock ();
dd->ipc[1] = 999; // tell jt9 to shut down
mem_jt9.unlock ();
mem_jt9.detach (); // start again
}
else
{
break; // good to go
}
QThread::sleep (1); // wait for jt9 to end
}
if (!mem_jt9.attach ())
{
if (!mem_jt9.create (sizeof (struct dec_data)))
{
splash.hide ();
MessageBox::critical_message (nullptr, a.translate ("main", "Shared memory error"),
a.translate ("main", "Unable to create shared memory segment"));
throw std::runtime_error {"Shared memory error"};
}
}
else
{
splash.hide ();
MessageBox::critical_message (nullptr, a.translate ("main", "Sub-process error"),
a.translate ("main", "Failed to close orphaned jt9 process"));
throw std::runtime_error {"Sub-process error"};
}
}
mem_jt9.lock ();
memset(mem_jt9.data(),0,sizeof(struct dec_data)); //Zero all decoding params in shared memory
mem_jt9.unlock ();
@ -411,12 +438,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
}

View File

@ -12,10 +12,10 @@ namespace
// human readable strings for each Region enumeration value
char const * const region_names[] =
{
"All",
"Region 1",
"Region 2",
"Region 3",
QT_TRANSLATE_NOOP ("IARURegions", "All"),
QT_TRANSLATE_NOOP ("IARURegions", "Region 1"),
QT_TRANSLATE_NOOP ("IARURegions", "Region 2"),
QT_TRANSLATE_NOOP ("IARURegions", "Region 3"),
};
std::size_t constexpr region_names_size = sizeof (region_names) / sizeof (region_names[0]);
}
@ -34,7 +34,7 @@ char const * IARURegions::name (Region r)
return region_names[static_cast<int> (r)];
}
auto IARURegions::value (QString const& s) -> Region
IARURegions::Region IARURegions::value (QString const& s)
{
auto end = region_names + region_names_size;
auto p = std::find_if (region_names, end

File diff suppressed because it is too large Load Diff

6439
translations/wsjtx_da.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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"));
}
}
}
@ -4607,6 +4609,13 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
return;
}
// ignore calls by other hounds
if (SpecOp::HOUND == m_config.special_op_id()
&& message.messageWords ().indexOf (QRegularExpression {R"(R\+-[0-9]+)"}) >= 0)
{
return;
}
QString firstcall = message.call();
if(firstcall.length()==5 and firstcall.mid(0,3)=="CQ ") firstcall="CQ";
if(!m_bFastMode and (!m_config.enable_VHF_features() or m_mode=="FT8")) {
@ -6635,7 +6644,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 +7323,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)

View File

@ -21,6 +21,7 @@
#include <QPointer>
#include <QSet>
#include <QVector>
#include <QQueue>
#include <QFuture>
#include <QFutureWatcher>
@ -33,7 +34,7 @@
#include "WSPR/WSPRBandHopping.hpp"
#include "Transceiver/Transceiver.hpp"
#include "DisplayManual.hpp"
#include "Network/psk_reporter.h"
#include "Network/PSKReporter.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;
PSKReporter m_psk_Reporter;
DisplayManual m_manual;
QHash<QString, QVariant> m_pwrBandTxMemory; // Remembers power level by band
QHash<QString, QVariant> m_pwrBandTuneMemory; // Remembers power level by band for tuning

View File

@ -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 \

View File

@ -19,6 +19,8 @@ extern "C" {
#cmakedefine PROJECT_SAMPLES_URL "@PROJECT_SAMPLES_URL@"
#cmakedefine PROJECT_SUMMARY_DESCRIPTION "@PROJECT_SUMMARY_DESCRIPTION@"
#cmakedefine01 HAVE_HAMLIB_CACHING
#cmakedefine01 WSJT_SHARED_RUNTIME
#cmakedefine01 WSJT_QDEBUG_TO_FILE
#cmakedefine01 WSJT_QDEBUG_IN_RELEASE