diff --git a/CMakeLists.txt b/CMakeLists.txt index bba73af..c753f87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,7 @@ set (wsjtx_CXXSRCS messagereplydialog.cpp keyeater.cpp APRSISClient.cpp + SpotClient.cpp Inbox.cpp messagewindow.cpp mainwindow.cpp diff --git a/SpotClient.cpp b/SpotClient.cpp new file mode 100644 index 0000000..463cdd3 --- /dev/null +++ b/SpotClient.cpp @@ -0,0 +1,100 @@ +#include "SpotClient.h" +#include "Message.h" + +#include "moc_SpotClient.cpp" + +SpotClient::SpotClient(MessageClient *client, QObject *parent): + QObject(parent), + m_client { client } +{ + prepare(); + + connect(&m_timer, &QTimer::timeout, this, &SpotClient::processSpots); + m_timer.setInterval(60 * 1000); + m_timer.setSingleShot(false); + m_timer.start(); +} + +void SpotClient::prepare(){ + QHostInfo::lookupHost("spot.js8call.com", this, SLOT(dnsLookupResult(QHostInfo))); +} + +void SpotClient::dnsLookupResult(QHostInfo info){ + if (info.addresses().isEmpty()) { + qDebug() << "SpotClient Error:" << info.errorString(); + return; + } + + m_address = info.addresses().at(0); + qDebug() << "SpotClient Resolve:" << m_address.toString(); +} + +void SpotClient::setLocalStation(QString callsign, QString grid, QString info, QString version){ + bool changed = false; + + if(m_call != callsign){ + m_call = callsign; + changed = true; + } + + if(m_grid != grid){ + m_grid = grid; + changed = true; + } + + if(m_info != info){ + m_info = info; + changed = true; + } + + if(m_version != version){ + m_version = version; + changed = true; + } + + if(changed){ + // send local information to network + enqueueLocalSpot(callsign, grid, info, version); + } +} + +void SpotClient::enqueueLocalSpot(QString callsign, QString grid, QString info, QString version){ + auto m = Message("RX.LOCAL", "", { + {"CALLSIGN", QVariant(callsign)}, + {"GRID", QVariant(grid)}, + {"INFO", QVariant(info)}, + {"VERSION", QVariant(version)}, + }); + + m_queue.enqueue(m.toJson()); +} + +void SpotClient::enqueueSpot(QString callsign, QString grid, int frequency, int snr){ + auto m = Message("RX.SPOT", "", { + {"BY", QVariant(m_call)}, + {"CALLSIGN", QVariant(callsign)}, + {"GRID", QVariant(grid)}, + {"FREQ", QVariant(frequency)}, + {"SNR", QVariant(snr)}, + }); + + // queue these... + m_queue.enqueue(m.toJson()); +} + +void SpotClient::processSpots(){ + if(m_address.isNull()){ + prepare(); + return; + } + + while(!m_queue.isEmpty()){ + sendRawSpot(m_queue.dequeue()); + } +} + +void SpotClient::sendRawSpot(QByteArray payload){ + if(!m_address.isNull()){ + m_client->send_raw_datagram(payload, m_address, 50000); + } +} diff --git a/SpotClient.h b/SpotClient.h new file mode 100644 index 0000000..d0f949d --- /dev/null +++ b/SpotClient.h @@ -0,0 +1,39 @@ +#ifndef JS8SPOTCLIENT_H +#define JS8SPOTCLIENT_H + +#include "MessageClient.hpp" + +#include +#include +#include +#include + +class SpotClient : public QObject +{ + Q_OBJECT +public: + SpotClient(MessageClient *client, QObject *parent = nullptr); + + void prepare(); + void setLocalStation(QString callsign, QString grid, QString info, QString version); + void enqueueLocalSpot(QString callsign, QString grid, QString info, QString version); + void enqueueSpot(QString callsign, QString grid, int frequency, int snr); + void sendRawSpot(QByteArray payload); + +public slots: + void processSpots(); + void dnsLookupResult(QHostInfo); + +private: + QString m_call; + QString m_grid; + QString m_info; + QString m_version; + + QHostAddress m_address; + MessageClient *m_client; + QTimer m_timer; + QQueue m_queue; +}; + +#endif // JS8SPOTCLIENT_H diff --git a/js8call.pro b/js8call.pro index 11062d9..d2cb38b 100644 --- a/js8call.pro +++ b/js8call.pro @@ -81,7 +81,8 @@ SOURCES += \ jsc_checker.cpp \ Message.cpp \ Inbox.cpp \ - messagewindow.cpp + messagewindow.cpp \ + SpotClient.cpp HEADERS += qt_helpers.hpp \ pimpl_h.hpp pimpl_impl.hpp \ @@ -113,7 +114,8 @@ HEADERS += qt_helpers.hpp \ jsc_checker.h \ Message.h \ Inbox.h \ - messagewindow.h + messagewindow.h \ + SpotClient.h INCLUDEPATH += qmake_only diff --git a/mainwindow.cpp b/mainwindow.cpp index df0c08a..4f99a1f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -560,6 +560,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, version (), revision (), m_config.udp_server_name (), m_config.udp_server_port (), this}}, + m_spotClient { new SpotClient{m_messageClient, this}}, m_aprsClient {new APRSISClient{"rotate.aprs2.net", 14580, this}}, psk_Reporter {new PSK_Reporter {m_messageClient, this}}, m_i3bit {0}, @@ -1622,6 +1623,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, gridButtonLayout->setColumnStretch(1, 1); gridButtonLayout->setColumnStretch(2, 1); + spotSetLocal(); pskSetLocal(); aprsSetLocal(); @@ -2896,6 +2898,7 @@ void MainWindow::openSettings(int tab){ void MainWindow::prepareSpotting(){ if(m_config.spot_to_reporting_networks ()){ + spotSetLocal(); pskSetLocal(); aprsSetLocal(); m_aprsClient->setServer(m_config.aprs_server_name(), m_config.aprs_server_port()); @@ -4532,6 +4535,14 @@ QString MainWindow::lookupCallInCompoundCache(QString const &call){ return m_compoundCallCache.value(call, call); } +void MainWindow::spotReport(int offset, int snr, QString callsign, QString grid){ + if(!m_config.spot_to_reporting_networks()) return; + + Frequency frequency = m_freqNominal + offset; + + m_spotClient->enqueueSpot(callsign, grid, frequency, snr); +} + void MainWindow::pskLogReport(QString mode, int offset, int snr, QString callsign, QString grid){ if(!m_config.spot_to_reporting_networks()) return; @@ -8593,6 +8604,7 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const& } if (m_config.spot_to_reporting_networks ()) { + spotSetLocal(); pskSetLocal(); aprsSetLocal(); } @@ -8843,6 +8855,16 @@ bool MainWindow::shortList(QString callsign) return b; } +void MainWindow::spotSetLocal () +{ + auto call = m_config.my_callsign(); + auto grid = m_config.my_grid(); + auto info = replaceMacros(m_config.my_info(), buildMacroValues(), true); + auto ver = QString {"JS8Call v" + version() }.simplified (); + qDebug() << "SpotClient Set Local Station:" << call << grid << info << ver; + m_spotClient->setLocalStation(call, grid, info, ver); +} + void MainWindow::pskSetLocal () { auto info = replaceMacros(m_config.my_info(), buildMacroValues(), true); @@ -10791,6 +10813,7 @@ void MainWindow::processSpots() { auto dial = dialFrequency(); // Process spots to be sent... + spotSetLocal(); pskSetLocal(); aprsSetLocal(); @@ -10802,6 +10825,7 @@ void MainWindow::processSpots() { qDebug() << "spotting call to reporting networks" << d.call << d.snr << d.freq; + spotReport(d.freq, d.snr, d.call, d.grid); pskLogReport("JS8", d.freq, d.snr, d.call, d.grid); aprsLogReport(d.freq, d.snr, d.call, d.grid); diff --git a/mainwindow.h b/mainwindow.h index 3868686..b833447 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -44,6 +44,7 @@ #include "qpriorityqueue.h" #include "varicode.h" #include "MessageClient.hpp" +#include "SpotClient.h" #include "APRSISClient.h" #include "keyeater.h" @@ -896,6 +897,7 @@ private: QProgressDialog m_optimizingProgress; MessageClient * m_messageClient; PSK_Reporter *psk_Reporter; + SpotClient *m_spotClient; APRSISClient * m_aprsClient; DisplayManual m_manual; QHash m_pwrBandTxMemory; // Remembers power level by band @@ -920,8 +922,10 @@ private: bool shortList(QString callsign); void transmit (double snr = 99.); void rigFailure (QString const& reason); + void spotSetLocal(); void pskSetLocal (); void aprsSetLocal (); + void spotReport(int offset, int snr, QString callsign, QString grid); void pskLogReport(QString mode, int offset, int snr, QString callsign, QString grid); void aprsLogReport(int offset, int snr, QString callsign, QString grid); Radio::Frequency dialFrequency();