WSJT-X/Network/wsprnet.cpp
2020-06-21 20:35:26 +01:00

233 lines
7.1 KiB
C++

// Interface to WSPRnet website
//
// by Edson Pereira - PY2SDR
#include "wsprnet.h"
#include <cmath>
#include <QTimer>
#include <QFile>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include "moc_wsprnet.cpp"
namespace
{
char const * const wsprNetUrl = "http://wsprnet.org/post?";
// char const * const wsprNetUrl = "http://127.0.0.1/post?";
};
WSPRNet::WSPRNet(QNetworkAccessManager * manager, QObject *parent)
: QObject{parent}
, networkManager {manager}
, uploadTimer {new QTimer {this}}
, m_urlQueueSize {0}
{
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReply(QNetworkReply*)));
connect( uploadTimer, SIGNAL(timeout()), this, SLOT(work()));
}
void WSPRNet::upload(QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq,
QString const& mode, QString const& tpct, QString const& dbm, QString const& version,
QString const& fileName)
{
m_call = call;
m_grid = grid;
m_rfreq = rfreq;
m_tfreq = tfreq;
m_mode = mode;
m_tpct = tpct;
m_dbm = dbm;
m_vers = version;
m_file = fileName;
// Open the wsprd.out file
QFile wsprdOutFile(fileName);
if (!wsprdOutFile.open(QIODevice::ReadOnly | QIODevice::Text) ||
wsprdOutFile.size() == 0) {
urlQueue.enqueue( wsprNetUrl + urlEncodeNoSpot());
m_uploadType = 1;
uploadTimer->start(200);
return;
}
// Read the contents
while (!wsprdOutFile.atEnd()) {
QHash<QString,QString> query;
if ( decodeLine(wsprdOutFile.readLine(), query) ) {
// Prevent reporting data ouside of the current frequency band
float f = fabs(m_rfreq.toFloat() - query["tqrg"].toFloat());
if (f < 0.0002) {
urlQueue.enqueue( wsprNetUrl + urlEncodeSpot(query));
m_uploadType = 2;
}
}
}
m_urlQueueSize = urlQueue.size();
uploadTimer->start(200);
}
void WSPRNet::networkReply(QNetworkReply *reply)
{
// check if request was ours
if (m_outstandingRequests.removeOne (reply)) {
if (QNetworkReply::NoError != reply->error ()) {
Q_EMIT uploadStatus (QString {"Error: %1"}.arg (reply->error ()));
// not clearing queue or halting queuing as it may be a transient
// one off request error
}
else {
QString serverResponse = reply->readAll();
if( m_uploadType == 2) {
if (!serverResponse.contains(QRegExp("spot\\(s\\) added"))) {
emit uploadStatus(QString {"Upload Failed: %1"}.arg (serverResponse));
urlQueue.clear();
uploadTimer->stop();
}
}
if (urlQueue.isEmpty()) {
emit uploadStatus("done");
QFile::remove(m_file);
uploadTimer->stop();
}
}
qDebug () << QString {"WSPRnet.org %1 outstanding requests"}.arg (m_outstandingRequests.size ());
// delete request object instance on return to the event loop otherwise it is leaked
reply->deleteLater ();
}
}
bool WSPRNet::decodeLine(QString const& line, QHash<QString,QString> &query)
{
// 130223 2256 7 -21 -0.3 14.097090 DU1MGA PK04 37 0 40 0
// Date Time Sync dBm DT Freq Msg
// 1 2 3 4 5 6 -------7------ 8 9 10
QRegExp rx("^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+\\.\\d+)\\s+(\\d+\\.\\d+)\\s+(.*)\\s+([+-]?\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+)");
if (rx.indexIn(line) != -1) {
int msgType = 0;
QString msg = rx.cap(7);
msg.remove(QRegExp("\\s+$"));
msg.remove(QRegExp("^\\s+"));
QString call, grid, dbm;
QRegExp msgRx;
// Check for Message Type 1
msgRx.setPattern("^([A-Z0-9]{3,6})\\s+([A-Z]{2}\\d{2})\\s+(\\d+)");
if (msgRx.indexIn(msg) != -1) {
msgType = 1;
call = msgRx.cap(1);
grid = msgRx.cap(2);
dbm = msgRx.cap(3);
}
// Check for Message Type 2
msgRx.setPattern("^([A-Z0-9/]+)\\s+(\\d+)");
if (msgRx.indexIn(msg) != -1) {
msgType = 2;
call = msgRx.cap(1);
grid = "";
dbm = msgRx.cap(2);
}
// Check for Message Type 3
msgRx.setPattern("^<([A-Z0-9/]+)>\\s+([A-Z]{2}\\d{2}[A-Z]{2})\\s+(\\d+)");
if (msgRx.indexIn(msg) != -1) {
msgType = 3;
call = msgRx.cap(1);
grid = msgRx.cap(2);
dbm = msgRx.cap(3);
}
// Unknown message format
if (!msgType) {
return false;
}
query["function"] = "wspr";
query["date"] = rx.cap(1);
query["time"] = rx.cap(2);
query["sig"] = rx.cap(4);
query["dt"] = rx.cap(5);
query["drift"] = rx.cap(8);
query["tqrg"] = rx.cap(6);
query["tcall"] = call;
query["tgrid"] = grid;
query["dbm"] = dbm;
} else {
return false;
}
return true;
}
QString WSPRNet::urlEncodeNoSpot()
{
QString queryString;
queryString += "function=wsprstat&";
queryString += "rcall=" + m_call + "&";
queryString += "rgrid=" + m_grid + "&";
queryString += "rqrg=" + m_rfreq + "&";
queryString += "tpct=" + m_tpct + "&";
queryString += "tqrg=" + m_tfreq + "&";
queryString += "dbm=" + m_dbm + "&";
queryString += "version=" + m_vers;
if(m_mode=="WSPR") queryString += "&mode=2";
if(m_mode=="WSPR-15") queryString += "&mode=15";
return queryString;;
}
QString WSPRNet::urlEncodeSpot(QHash<QString,QString> const& query)
{
QString queryString;
queryString += "function=" + query["function"] + "&";
queryString += "rcall=" + m_call + "&";
queryString += "rgrid=" + m_grid + "&";
queryString += "rqrg=" + m_rfreq + "&";
queryString += "date=" + query["date"] + "&";
queryString += "time=" + query["time"] + "&";
queryString += "sig=" + query["sig"] + "&";
queryString += "dt=" + query["dt"] + "&";
queryString += "drift=" + query["drift"] + "&";
queryString += "tqrg=" + query["tqrg"] + "&";
queryString += "tcall=" + query["tcall"] + "&";
queryString += "tgrid=" + query["tgrid"] + "&";
queryString += "dbm=" + query["dbm"] + "&";
queryString += "version=" + m_vers;
if(m_mode=="WSPR") queryString += "&mode=2";
if(m_mode=="WSPR-15") queryString += "&mode=15";
return queryString;
}
void WSPRNet::work()
{
if (!urlQueue.isEmpty()) {
#if QT_VERSION < QT_VERSION_CHECK (5, 15, 0)
if (QNetworkAccessManager::Accessible != networkManager->networkAccessible ()) {
// try and recover network access for QNAM
networkManager->setNetworkAccessible (QNetworkAccessManager::Accessible);
}
#endif
QUrl url(urlQueue.dequeue());
QNetworkRequest request(url);
m_outstandingRequests << networkManager->get(request);
emit uploadStatus(QString {"Uploading Spot %1/%2"}.arg (m_urlQueueSize - urlQueue.size()).arg (m_urlQueueSize));
} else {
uploadTimer->stop();
}
}
void WSPRNet::abortOutstandingRequests () {
urlQueue.clear ();
for (auto& request : m_outstandingRequests) {
request->abort ();
}
m_urlQueueSize = 0;
}