mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2025-04-02 17:43:05 -04:00
Merge branch 'develop' of bitbucket.org:k1jt/wsjtx into develop
This commit is contained in:
commit
2905c95278
@ -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})
|
||||
|
@ -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 ();
|
||||
|
@ -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;
|
||||
|
@ -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><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></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable &PSK Reporter Spotting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="psk_reporter_tcpip_check_box">
|
||||
<property name="toolTip">
|
||||
<string><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></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>
|
||||
|
@ -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");
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
62
Network/NetworkAccessManager.cpp
Normal file
62
Network/NetworkAccessManager.cpp
Normal 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;
|
||||
}
|
@ -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
496
Network/PSKReporter.cpp
Normal 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
41
Network/PSKReporter.hpp
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
12
Network/tests/PSKReporter/PSKReporter.IESpec
Normal file
12
Network/tests/PSKReporter/PSKReporter.IESpec
Normal 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]
|
94
Network/tests/PSKReporter/listener.py
Normal file
94
Network/tests/PSKReporter/listener.py
Normal 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')
|
64
Network/tests/PSKReporter/python-ipfix.patch
Normal file
64
Network/tests/PSKReporter/python-ipfix.patch
Normal 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
|
@ -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);
|
||||
|
@ -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 ())
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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]
|
||||
|
@ -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?::
|
||||
|
@ -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
|
||||
|
49
main.cpp
49
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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
@ -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
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
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user