Added spotting to APRS-IS for grids larger than 4 characters. Added supporting commands for QTH QTC and GRID
This commit is contained in:
parent
2fa1fcd4f8
commit
adcc728492
250
APRSISClient.cpp
Normal file
250
APRSISClient.cpp
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
#include "APRSISClient.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "Radio.hpp"
|
||||||
|
#include "varicode.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
APRSISClient::APRSISClient(QString host, quint16 port, QObject *parent):
|
||||||
|
QTcpSocket(parent),
|
||||||
|
m_host(host),
|
||||||
|
m_port(port)
|
||||||
|
{
|
||||||
|
connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports);
|
||||||
|
m_timer.setInterval(60*1000); // every minute
|
||||||
|
m_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 APRSISClient::hashCallsign(QString callsign){
|
||||||
|
// based on: https://github.com/hessu/aprsc/blob/master/src/passcode.c
|
||||||
|
QByteArray rootCall = QString(callsign.split("-").first().toUpper()).toLocal8Bit() + '\0';
|
||||||
|
quint32 hash = 0x73E2;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int len = rootCall.length();
|
||||||
|
|
||||||
|
while(i+1 < len){
|
||||||
|
hash ^= rootCall.at(i) << 8;
|
||||||
|
hash ^= rootCall.at(i+1);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash & 0x7FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString APRSISClient::loginFrame(QString callsign){
|
||||||
|
auto loginFrame = QString("user %1 pass %2 ver %3\n");
|
||||||
|
loginFrame = loginFrame.arg(callsign);
|
||||||
|
loginFrame = loginFrame.arg(hashCallsign(callsign));
|
||||||
|
loginFrame = loginFrame.arg("FT8Call");
|
||||||
|
return loginFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QStringList> findall(QRegularExpression re, QString content){
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
QList<QStringList> all;
|
||||||
|
while(pos < content.length()){
|
||||||
|
auto match = re.match(content, pos);
|
||||||
|
if(!match.hasMatch()){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
all.append(match.capturedTexts());
|
||||||
|
pos = match.capturedEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline long
|
||||||
|
floordiv (long num, long den)
|
||||||
|
{
|
||||||
|
if (0 < (num^den))
|
||||||
|
return num/den;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ldiv_t res = ldiv(num,den);
|
||||||
|
return (res.rem)? res.quot-1
|
||||||
|
: res.quot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert an arbitrary length grid locator to a high precision lat/lon
|
||||||
|
QPair<float, float> APRSISClient::grid2deg(QString locator){
|
||||||
|
QString grid = locator.toUpper();
|
||||||
|
|
||||||
|
float lat = -90;
|
||||||
|
float lon = -90;
|
||||||
|
|
||||||
|
auto lats = findall(QRegularExpression("([A-X])([A-X])"), grid);
|
||||||
|
auto lons = findall(QRegularExpression("(\\d)(\\d)"), grid);
|
||||||
|
|
||||||
|
int valx[22];
|
||||||
|
int valy[22];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int tot = 0;
|
||||||
|
char A = 'A';
|
||||||
|
foreach(QStringList matched, lats){
|
||||||
|
char x = matched.at(1).at(0).toLatin1();
|
||||||
|
char y = matched.at(2).at(0).toLatin1();
|
||||||
|
|
||||||
|
valx[i*2] = x-A;
|
||||||
|
valy[i*2] = y-A;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
tot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
foreach(QStringList matched, lons){
|
||||||
|
int x = matched.at(1).toInt();
|
||||||
|
int y = matched.at(2).toInt();
|
||||||
|
valx[i*2+1]=x;
|
||||||
|
valy[i*2+1]=y;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
tot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < tot; i++){
|
||||||
|
int x = valx[i];
|
||||||
|
int y = valy[i];
|
||||||
|
|
||||||
|
int z = i - 1;
|
||||||
|
float scale = pow(10, floordiv(-(z-1), 2)) * pow(24, floordiv(-z, 2));
|
||||||
|
|
||||||
|
lon += scale * x;
|
||||||
|
lat += scale * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
lon *= 2;
|
||||||
|
|
||||||
|
return {lat, lon};
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert an arbitrary length grid locator to a high precision lat/lon in aprs format
|
||||||
|
QPair<QString, QString> APRSISClient::grid2aprs(QString grid){
|
||||||
|
auto geo = APRSISClient::grid2deg(grid);
|
||||||
|
auto lat = geo.first;
|
||||||
|
auto lon = geo.second;
|
||||||
|
|
||||||
|
QString latDir = "N";
|
||||||
|
if(lat < 0){
|
||||||
|
lat *= -1;
|
||||||
|
latDir = "S";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString lonDir = "E";
|
||||||
|
if(lon < 0){
|
||||||
|
lon *= -1;
|
||||||
|
lonDir = "W";
|
||||||
|
}
|
||||||
|
|
||||||
|
double iLat, fLat, iLon, fLon, iLatMin, fLatMin, iLonMin, fLonMin, iLatSec, iLonSec;
|
||||||
|
fLat = modf(lat, &iLat);
|
||||||
|
fLon = modf(lon, &iLon);
|
||||||
|
|
||||||
|
fLatMin = modf(fLat * 60, &iLatMin);
|
||||||
|
fLonMin = modf(fLon * 60, &iLonMin);
|
||||||
|
|
||||||
|
iLatSec = round(fLatMin * 60);
|
||||||
|
iLonSec = round(fLonMin * 60);
|
||||||
|
|
||||||
|
if(iLatSec == 60){
|
||||||
|
iLatMin += 1;
|
||||||
|
iLatSec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iLonSec == 60){
|
||||||
|
iLonMin += 1;
|
||||||
|
iLonSec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iLatMin == 60){
|
||||||
|
iLat += 1;
|
||||||
|
iLatMin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iLonMin == 60){
|
||||||
|
iLon += 1;
|
||||||
|
iLonMin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double aprsLat = iLat * 100 + iLatMin + (iLatSec / 60.0);
|
||||||
|
double aprsLon = iLon * 100 + iLonMin + (iLonSec / 60.0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
QString().sprintf("%07.2f%%1", aprsLat).arg(latDir),
|
||||||
|
QString().sprintf("%08.2f%%1", aprsLon).arg(lonDir)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void APRSISClient::enqueueSpot(QString theircall, QString grid, quint64 frequency, int snr){
|
||||||
|
if(m_localCall.isEmpty()) return;
|
||||||
|
|
||||||
|
auto geo = APRSISClient::grid2aprs(grid);
|
||||||
|
auto spotFrame = QString("%1>APRS,TCPIP*:=%2/%3nFT8CALL %4 %5 %6MHz %7dB\n");
|
||||||
|
spotFrame = spotFrame.arg(theircall);
|
||||||
|
spotFrame = spotFrame.arg(geo.first);
|
||||||
|
spotFrame = spotFrame.arg(geo.second);
|
||||||
|
spotFrame = spotFrame.arg(m_localCall);
|
||||||
|
spotFrame = spotFrame.arg(m_localGrid.left(4));
|
||||||
|
spotFrame = spotFrame.arg(Radio::frequency_MHz_string(frequency));
|
||||||
|
spotFrame = spotFrame.arg(Varicode::formatSNR(snr));
|
||||||
|
enqueueRaw(spotFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APRSISClient::enqueueMessage(QString tocall, QString message){
|
||||||
|
if(m_localCall.isEmpty()) return;
|
||||||
|
|
||||||
|
auto messageFrame = QString("%1>APRS,TCPIP*::%2:%3\n");
|
||||||
|
messageFrame = messageFrame.arg(m_localCall);
|
||||||
|
messageFrame = messageFrame.arg(tocall + QString(" ").repeated(9-tocall.length()));
|
||||||
|
messageFrame = messageFrame.arg(message);
|
||||||
|
enqueueRaw(messageFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APRSISClient::enqueueRaw(QString aprsFrame){
|
||||||
|
m_frameQueue.enqueue(aprsFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APRSISClient::processQueue(bool disconnect){
|
||||||
|
if(m_localCall.isEmpty()) return;
|
||||||
|
|
||||||
|
// 1. connect (and read)
|
||||||
|
// 2. login (and read)
|
||||||
|
// 3. for each raw frame in queue, send
|
||||||
|
// 4. disconnect
|
||||||
|
|
||||||
|
if(state() != QTcpSocket::ConnectedState){
|
||||||
|
connectToHost(m_host, m_port);
|
||||||
|
if(!waitForConnected(5000)){
|
||||||
|
qDebug() << "APRSISClient Connection Error:" << errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(write(loginFrame(m_localCall).toLocal8Bit()) == -1){
|
||||||
|
qDebug() << "APRSISClient Write Login Error:" << errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!m_frameQueue.isEmpty()){
|
||||||
|
if(write(m_frameQueue.head().toLocal8Bit()) == -1){
|
||||||
|
qDebug() << "APRSISClient Write Error:" << errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto frame = m_frameQueue.dequeue();
|
||||||
|
qDebug() << "APRISISClient Write:" << frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disconnect){
|
||||||
|
disconnectFromHost();
|
||||||
|
}
|
||||||
|
}
|
44
APRSISClient.h
Normal file
44
APRSISClient.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef APRSISCLIENT_H
|
||||||
|
#define APRSISCLIENT_H
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class APRSISClient : public QTcpSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
APRSISClient(QString host, quint16 port, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static quint32 hashCallsign(QString callsign);
|
||||||
|
static QString loginFrame(QString callsign);
|
||||||
|
static QPair<float, float> grid2deg(QString grid);
|
||||||
|
static QPair<QString, QString> grid2aprs(QString grid);
|
||||||
|
|
||||||
|
void setLocalStation(QString mycall, QString mygrid){
|
||||||
|
m_localCall = mycall;
|
||||||
|
m_localGrid = mygrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enqueueSpot(QString theircall, QString grid, quint64 frequency, int snr);
|
||||||
|
void enqueueMessage(QString tocall, QString message);
|
||||||
|
void enqueueMail(QString email, QString message);
|
||||||
|
void enqueueRaw(QString aprsFrame);
|
||||||
|
|
||||||
|
void processQueue(bool disconnect=false);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sendReports(){ processQueue(true); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_localCall;
|
||||||
|
QString m_localGrid;
|
||||||
|
|
||||||
|
QQueue<QString> m_frameQueue;
|
||||||
|
QString m_host;
|
||||||
|
quint16 m_port;
|
||||||
|
QTimer m_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APRSISCLIENT_H
|
@ -305,6 +305,7 @@ set (wsjtx_CXXSRCS
|
|||||||
WsprTxScheduler.cpp
|
WsprTxScheduler.cpp
|
||||||
varicode.cpp
|
varicode.cpp
|
||||||
SelfDestructMessageBox.cpp
|
SelfDestructMessageBox.cpp
|
||||||
|
APRSISClient.cpp
|
||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
Configuration.cpp
|
Configuration.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
@ -576,7 +576,7 @@ private:
|
|||||||
double txDelay_;
|
double txDelay_;
|
||||||
bool id_after_73_;
|
bool id_after_73_;
|
||||||
bool tx_QSY_allowed_;
|
bool tx_QSY_allowed_;
|
||||||
bool spot_to_psk_reporter_;
|
bool spot_to_reporting_networks_;
|
||||||
bool transmit_directed_;
|
bool transmit_directed_;
|
||||||
bool autoreply_off_at_startup_;
|
bool autoreply_off_at_startup_;
|
||||||
bool monitor_off_at_startup_;
|
bool monitor_off_at_startup_;
|
||||||
@ -678,15 +678,15 @@ double Configuration::txDelay() const {return m_->txDelay_;}
|
|||||||
qint32 Configuration::RxBandwidth() const {return m_->RxBandwidth_;}
|
qint32 Configuration::RxBandwidth() const {return m_->RxBandwidth_;}
|
||||||
bool Configuration::id_after_73 () const {return m_->id_after_73_;}
|
bool Configuration::id_after_73 () const {return m_->id_after_73_;}
|
||||||
bool Configuration::tx_QSY_allowed () const {return m_->tx_QSY_allowed_;}
|
bool Configuration::tx_QSY_allowed () const {return m_->tx_QSY_allowed_;}
|
||||||
bool Configuration::spot_to_psk_reporter () const
|
bool Configuration::spot_to_reporting_networks () const
|
||||||
{
|
{
|
||||||
// rig must be open and working to spot externally
|
// rig must be open and working to spot externally
|
||||||
return is_transceiver_online () && m_->spot_to_psk_reporter_;
|
return is_transceiver_online () && m_->spot_to_reporting_networks_;
|
||||||
}
|
}
|
||||||
void Configuration::set_spot_to_psk_reporter (bool spot)
|
void Configuration::set_spot_to_reporting_networks (bool spot)
|
||||||
{
|
{
|
||||||
if(m_->spot_to_psk_reporter_ != spot){
|
if(m_->spot_to_reporting_networks_ != spot){
|
||||||
m_->spot_to_psk_reporter_ = spot;
|
m_->spot_to_reporting_networks_ = spot;
|
||||||
m_->write_settings();
|
m_->write_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1264,7 +1264,7 @@ void Configuration::impl::initialize_models ()
|
|||||||
ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
|
ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
|
||||||
ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
|
ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
|
||||||
ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_);
|
ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_);
|
||||||
ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_);
|
ui_->psk_reporter_check_box->setChecked (spot_to_reporting_networks_);
|
||||||
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
||||||
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
||||||
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
||||||
@ -1473,7 +1473,7 @@ void Configuration::impl::read_settings ()
|
|||||||
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
||||||
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
||||||
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
||||||
spot_to_psk_reporter_ = settings_->value ("PSKReporter", true).toBool ();
|
spot_to_reporting_networks_ = settings_->value ("PSKReporter", true).toBool ();
|
||||||
id_after_73_ = settings_->value ("After73", false).toBool ();
|
id_after_73_ = settings_->value ("After73", false).toBool ();
|
||||||
tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool ();
|
tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool ();
|
||||||
use_dynamic_info_ = settings_->value ("AutoGrid", false).toBool ();
|
use_dynamic_info_ = settings_->value ("AutoGrid", false).toBool ();
|
||||||
@ -1613,7 +1613,7 @@ void Configuration::impl::write_settings ()
|
|||||||
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
||||||
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
||||||
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
||||||
settings_->setValue ("PSKReporter", spot_to_psk_reporter_);
|
settings_->setValue ("PSKReporter", spot_to_reporting_networks_);
|
||||||
settings_->setValue ("After73", id_after_73_);
|
settings_->setValue ("After73", id_after_73_);
|
||||||
settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_);
|
settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_);
|
||||||
settings_->setValue ("Macros", macros_.stringList ());
|
settings_->setValue ("Macros", macros_.stringList ());
|
||||||
@ -2033,7 +2033,7 @@ void Configuration::impl::accept ()
|
|||||||
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
||||||
callsign_aging_ = ui_->callsign_aging_spin_box->value();
|
callsign_aging_ = ui_->callsign_aging_spin_box->value();
|
||||||
activity_aging_ = ui_->activity_aging_spin_box->value();
|
activity_aging_ = ui_->activity_aging_spin_box->value();
|
||||||
spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked ();
|
spot_to_reporting_networks_ = ui_->psk_reporter_check_box->isChecked ();
|
||||||
id_interval_ = ui_->CW_id_interval_spin_box->value ();
|
id_interval_ = ui_->CW_id_interval_spin_box->value ();
|
||||||
ntrials_ = ui_->sbNtrials->value ();
|
ntrials_ = ui_->sbNtrials->value ();
|
||||||
txDelay_ = ui_->sbTxDelay->value ();
|
txDelay_ = ui_->sbTxDelay->value ();
|
||||||
|
@ -112,8 +112,8 @@ public:
|
|||||||
double txDelay() const;
|
double txDelay() const;
|
||||||
bool id_after_73 () const;
|
bool id_after_73 () const;
|
||||||
bool tx_QSY_allowed () const;
|
bool tx_QSY_allowed () const;
|
||||||
bool spot_to_psk_reporter () const;
|
bool spot_to_reporting_networks () const;
|
||||||
void set_spot_to_psk_reporter (bool);
|
void set_spot_to_reporting_networks (bool);
|
||||||
bool transmit_directed() const;
|
bool transmit_directed() const;
|
||||||
bool autoreply_off_at_startup () const;
|
bool autoreply_off_at_startup () const;
|
||||||
bool monitor_off_at_startup () const;
|
bool monitor_off_at_startup () const;
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
<string><html><head/><body><p>4-digit Maidenhead Locator</p></body></html></string>
|
<string><html><head/><body><p>4-digit Maidenhead Locator</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="maxLength">
|
<property name="maxLength">
|
||||||
<number>16</number>
|
<number>12</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
64
aprs.py
Normal file
64
aprs.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
KKEY = 0x73e2
|
||||||
|
|
||||||
|
def do_hash(callsign):
|
||||||
|
rootCall = callsign.split("-")[0].upper() + '\0'
|
||||||
|
|
||||||
|
hash = KKEY
|
||||||
|
i = 0
|
||||||
|
length = len(rootCall)
|
||||||
|
|
||||||
|
while (i+1 < length):
|
||||||
|
hash ^= ord(rootCall[i])<<8
|
||||||
|
hash ^= ord(rootCall[i+1])
|
||||||
|
i += 2
|
||||||
|
|
||||||
|
return int(hash & 0x7fff)
|
||||||
|
|
||||||
|
HOST = 'rotate.aprs2.net'
|
||||||
|
PORT = 14580
|
||||||
|
|
||||||
|
print "Connecting..."
|
||||||
|
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((HOST, PORT))
|
||||||
|
|
||||||
|
print "Connected..."
|
||||||
|
|
||||||
|
data = s.recv(1024)
|
||||||
|
print data
|
||||||
|
|
||||||
|
call = 'KN4CRD'
|
||||||
|
pw = do_hash(call)
|
||||||
|
ver = "FT8Call"
|
||||||
|
|
||||||
|
login = "user {} pass {} ver {}\n".format(call, pw, ver)
|
||||||
|
s.send(login)
|
||||||
|
|
||||||
|
print "Login sent...", login
|
||||||
|
|
||||||
|
data = s.recv(1024)
|
||||||
|
print data
|
||||||
|
|
||||||
|
if 1:
|
||||||
|
message = "{}>APRS,TCPIP*::EMAIL-2 :kn4crd@gmail.com testing456{{AA}}\n".format(call)
|
||||||
|
s.send(message)
|
||||||
|
|
||||||
|
if 0:
|
||||||
|
payload = ":This is a test message"
|
||||||
|
message = "{}>APRS,TCPIP*::{} {}\n".format(call, call, payload)
|
||||||
|
s.send(message)
|
||||||
|
if 0:
|
||||||
|
position = "=3352.45N/08422.71Wn"
|
||||||
|
status = "FT8CALL VIA XX9XXX/XXXX 14.082500MHz -20dB"
|
||||||
|
payload = "".join((position, status))
|
||||||
|
message = "{}>APRS,TCPIP*:{}\n".format(call, payload)
|
||||||
|
s.send(message)
|
||||||
|
|
||||||
|
print "Spot sent...", message
|
||||||
|
|
||||||
|
data = s.recv(1024)
|
||||||
|
print data
|
||||||
|
|
||||||
|
s.close()
|
185
mainwindow.cpp
185
mainwindow.cpp
@ -1,5 +1,6 @@
|
|||||||
//---------------------------------------------------------- MainWindow
|
//---------------------------------------------------------- MainWindow
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
#include <cmath>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -25,6 +26,7 @@
|
|||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
#include <QSplashScreen>
|
#include <QSplashScreen>
|
||||||
|
|
||||||
|
#include "APRSISClient.h"
|
||||||
#include "revision_utils.hpp"
|
#include "revision_utils.hpp"
|
||||||
#include "qt_helpers.hpp"
|
#include "qt_helpers.hpp"
|
||||||
#include "NetworkAccessManager.hpp"
|
#include "NetworkAccessManager.hpp"
|
||||||
@ -466,6 +468,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
version (), revision (),
|
version (), revision (),
|
||||||
m_config.udp_server_name (), m_config.udp_server_port (),
|
m_config.udp_server_name (), m_config.udp_server_port (),
|
||||||
this}},
|
this}},
|
||||||
|
m_aprsClient {new APRSISClient{"rotate.aprs2.net", 14580, this}},
|
||||||
psk_Reporter {new PSK_Reporter {m_messageClient, this}},
|
psk_Reporter {new PSK_Reporter {m_messageClient, this}},
|
||||||
m_i3bit {0},
|
m_i3bit {0},
|
||||||
m_manual {&m_network_manager},
|
m_manual {&m_network_manager},
|
||||||
@ -1014,7 +1017,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
setFreqOffsetForRestore(f, false);
|
setFreqOffsetForRestore(f, false);
|
||||||
ui->cbVHFcontest->setChecked(false); // this needs to always be false
|
ui->cbVHFcontest->setChecked(false); // this needs to always be false
|
||||||
|
|
||||||
ui->spotButton->setChecked(m_config.spot_to_psk_reporter());
|
ui->spotButton->setChecked(m_config.spot_to_reporting_networks());
|
||||||
|
|
||||||
auto enterFilter = new EnterKeyPressEater();
|
auto enterFilter = new EnterKeyPressEater();
|
||||||
connect(enterFilter, &EnterKeyPressEater::enterKeyPressed, this, [this](QObject *, QKeyEvent *, bool *pProcessed){
|
connect(enterFilter, &EnterKeyPressEater::enterKeyPressed, this, [this](QObject *, QKeyEvent *, bool *pProcessed){
|
||||||
@ -1279,6 +1282,17 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
quint8 r = 0;
|
quint8 r = 0;
|
||||||
quint64 v = Varicode::unpack72bits(Varicode::pack72bits((((quint64)1)<<62)-1, (1<<7)-1), &r);
|
quint64 v = Varicode::unpack72bits(Varicode::pack72bits((((quint64)1)<<62)-1, (1<<7)-1), &r);
|
||||||
qDebug() << "packing" << Varicode::pack72bits((((quint64)1)<<62)-1, (1<<7)-1) << v << r;
|
qDebug() << "packing" << Varicode::pack72bits((((quint64)1)<<62)-1, (1<<7)-1) << v << r;
|
||||||
|
|
||||||
|
qDebug() << APRSISClient::grid2deg("EM73");
|
||||||
|
qDebug() << APRSISClient::grid2deg("EM73TU");
|
||||||
|
qDebug() << APRSISClient::grid2deg("EM73TU49NT");
|
||||||
|
|
||||||
|
qDebug() << APRSISClient::grid2aprs("EM73");
|
||||||
|
qDebug() << APRSISClient::grid2aprs("EM73TU");
|
||||||
|
qDebug() << APRSISClient::grid2aprs("EM73TU49NT");
|
||||||
|
|
||||||
|
qDebug() << APRSISClient::grid2aprs("FI08VE49");
|
||||||
|
qDebug() << APRSISClient::grid2aprs("OM25CU");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// this must be the last statement of constructor
|
// this must be the last statement of constructor
|
||||||
@ -2035,7 +2049,7 @@ void MainWindow::fastSink(qint64 frames)
|
|||||||
writeAllTxt(message);
|
writeAllTxt(message);
|
||||||
bool stdMsg = decodedtext.report(m_baseCall,
|
bool stdMsg = decodedtext.report(m_baseCall,
|
||||||
Radio::base_callsign(ui->dxCallEntry->text()),m_rptRcvd);
|
Radio::base_callsign(ui->dxCallEntry->text()),m_rptRcvd);
|
||||||
if (stdMsg) pskPost (decodedtext);
|
//if (stdMsg) pskPost (decodedtext);
|
||||||
}
|
}
|
||||||
|
|
||||||
float fracTR=float(k)/(12000.0*m_TRperiod);
|
float fracTR=float(k)/(12000.0*m_TRperiod);
|
||||||
@ -2183,7 +2197,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
|
|||||||
on_dxGridEntry_textChanged (m_hisGrid); // recalculate distances in case of units change
|
on_dxGridEntry_textChanged (m_hisGrid); // recalculate distances in case of units change
|
||||||
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
|
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
|
||||||
|
|
||||||
preparePSKReporter();
|
prepareSpotting();
|
||||||
|
|
||||||
if(m_config.restart_audio_input ()) {
|
if(m_config.restart_audio_input ()) {
|
||||||
Q_EMIT startAudioInputStream (m_config.audio_input_device (),
|
Q_EMIT startAudioInputStream (m_config.audio_input_device (),
|
||||||
@ -2233,9 +2247,10 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::preparePSKReporter(){
|
void MainWindow::prepareSpotting(){
|
||||||
if(m_config.spot_to_psk_reporter ()){
|
if(m_config.spot_to_reporting_networks ()){
|
||||||
pskSetLocal ();
|
pskSetLocal();
|
||||||
|
aprsSetLocal();
|
||||||
ui->spotButton->setChecked(true);
|
ui->spotButton->setChecked(true);
|
||||||
} else {
|
} else {
|
||||||
ui->spotButton->setChecked(false);
|
ui->spotButton->setChecked(false);
|
||||||
@ -2244,10 +2259,10 @@ void MainWindow::preparePSKReporter(){
|
|||||||
|
|
||||||
void MainWindow::on_spotButton_clicked(bool checked){
|
void MainWindow::on_spotButton_clicked(bool checked){
|
||||||
// 1. save setting
|
// 1. save setting
|
||||||
m_config.set_spot_to_psk_reporter(checked);
|
m_config.set_spot_to_reporting_networks(checked);
|
||||||
|
|
||||||
// 2. prepare
|
// 2. prepare
|
||||||
preparePSKReporter();
|
prepareSpotting();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_monitorButton_clicked (bool checked)
|
void MainWindow::on_monitorButton_clicked (bool checked)
|
||||||
@ -3247,12 +3262,12 @@ void::MainWindow::fast_decode_done()
|
|||||||
writeAllTxt(message);
|
writeAllTxt(message);
|
||||||
|
|
||||||
if(m_mode=="JT9" or m_mode=="MSK144") {
|
if(m_mode=="JT9" or m_mode=="MSK144") {
|
||||||
// find and extract any report for myCall
|
// find and extract any report for myCall
|
||||||
bool stdMsg = decodedtext.report(m_baseCall,
|
bool stdMsg = decodedtext.report(m_baseCall,
|
||||||
Radio::base_callsign(ui->dxCallEntry->text()), m_rptRcvd);
|
Radio::base_callsign(ui->dxCallEntry->text()), m_rptRcvd);
|
||||||
|
|
||||||
// extract details and send to PSKreporter
|
// extract details and send to PSKreporter
|
||||||
if (stdMsg) pskPost (decodedtext);
|
//if (stdMsg) pskPost (decodedtext);
|
||||||
}
|
}
|
||||||
if (tmax >= 0.0) auto_sequence (decodedtext, ui->sbFtol->value (), ui->sbFtol->value ());
|
if (tmax >= 0.0) auto_sequence (decodedtext, ui->sbFtol->value (), ui->sbFtol->value ());
|
||||||
}
|
}
|
||||||
@ -3688,7 +3703,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
m_mode=="JT9") auto_sequence (decodedtext, 25, 50);
|
m_mode=="JT9") auto_sequence (decodedtext, 25, 50);
|
||||||
postDecode (true, decodedtext.string ());
|
postDecode (true, decodedtext.string ());
|
||||||
|
|
||||||
// find and extract any report for myCall, but save in m_rptRcvd only if it's from DXcall
|
// find and extract any report for myCall, but save in m_rptRcvd only if it's from DXcall
|
||||||
QString rpt;
|
QString rpt;
|
||||||
bool stdMsg = decodedtext.report(m_baseCall,
|
bool stdMsg = decodedtext.report(m_baseCall,
|
||||||
Radio::base_callsign(ui->dxCallEntry->text()), rpt);
|
Radio::base_callsign(ui->dxCallEntry->text()), rpt);
|
||||||
@ -3699,10 +3714,11 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
QString t=Radio::base_callsign(ui->dxCallEntry->text());
|
QString t=Radio::base_callsign(ui->dxCallEntry->text());
|
||||||
if((t==deCall or t=="") and rpt!="") m_rptRcvd=rpt;
|
if((t==deCall or t=="") and rpt!="") m_rptRcvd=rpt;
|
||||||
}
|
}
|
||||||
// extract details and send to PSKreporter
|
// extract details and send to PSKreporter
|
||||||
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
|
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
|
||||||
bool okToPost=(nsec>(4*m_TRperiod)/5);
|
bool okToPost=(nsec>(4*m_TRperiod)/5);
|
||||||
if (stdMsg && okToPost) pskPost(decodedtext);
|
|
||||||
|
//if (stdMsg && okToPost) pskPost(decodedtext);
|
||||||
|
|
||||||
if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64") and m_msgAvgWidget!=NULL) {
|
if((m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64") and m_msgAvgWidget!=NULL) {
|
||||||
if(m_msgAvgWidget->isVisible()) {
|
if(m_msgAvgWidget->isVisible()) {
|
||||||
@ -3811,7 +3827,8 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler
|
|||||||
|
|
||||||
void MainWindow::pskPost (DecodedText const& decodedtext)
|
void MainWindow::pskPost (DecodedText const& decodedtext)
|
||||||
{
|
{
|
||||||
if (m_diskData || !m_config.spot_to_psk_reporter() || decodedtext.isLowConfidence ()) return;
|
#if 0
|
||||||
|
if (m_diskData || !m_config.spot_to_reporting_networks() || decodedtext.isLowConfidence ()) return;
|
||||||
|
|
||||||
QString msgmode=m_mode;
|
QString msgmode=m_mode;
|
||||||
if(m_mode=="JT9+JT65") {
|
if(m_mode=="JT9+JT65") {
|
||||||
@ -3826,6 +3843,7 @@ void MainWindow::pskPost (DecodedText const& decodedtext)
|
|||||||
audioFrequency=decodedtext.string().mid(16,4).toInt();
|
audioFrequency=decodedtext.string().mid(16,4).toInt();
|
||||||
}
|
}
|
||||||
int snr = decodedtext.snr();
|
int snr = decodedtext.snr();
|
||||||
|
|
||||||
pskSetLocal ();
|
pskSetLocal ();
|
||||||
if(grid.contains (grid_regexp)) {
|
if(grid.contains (grid_regexp)) {
|
||||||
// qDebug() << "To PSKreporter:" << deCall << grid << frequency << msgmode << snr;
|
// qDebug() << "To PSKreporter:" << deCall << grid << frequency << msgmode << snr;
|
||||||
@ -3833,10 +3851,11 @@ void MainWindow::pskPost (DecodedText const& decodedtext)
|
|||||||
// QString::number(snr),QString::number(QDateTime::currentDateTime().toTime_t()));
|
// QString::number(snr),QString::number(QDateTime::currentDateTime().toTime_t()));
|
||||||
pskLogReport(msgmode, audioFrequency, snr, deCall, grid);
|
pskLogReport(msgmode, audioFrequency, snr, deCall, grid);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::pskLogReport(QString mode, int offset, int snr, QString callsign, QString grid){
|
void MainWindow::pskLogReport(QString mode, int offset, int snr, QString callsign, QString grid){
|
||||||
if(!m_config.spot_to_psk_reporter()) return;
|
if(!m_config.spot_to_reporting_networks()) return;
|
||||||
|
|
||||||
Frequency frequency = m_freqNominal + offset;
|
Frequency frequency = m_freqNominal + offset;
|
||||||
|
|
||||||
@ -3849,6 +3868,19 @@ void MainWindow::pskLogReport(QString mode, int offset, int snr, QString callsig
|
|||||||
QString::number(QDateTime::currentDateTime().toTime_t()));
|
QString::number(QDateTime::currentDateTime().toTime_t()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::aprsLogReport(int offset, int snr, QString callsign, QString grid){
|
||||||
|
if(!m_config.spot_to_reporting_networks()) return;
|
||||||
|
|
||||||
|
Frequency frequency = m_freqNominal + offset;
|
||||||
|
|
||||||
|
if(grid.length() < 6){
|
||||||
|
qDebug() << "APRSISClient Spot Skipped:" << callsign << grid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_aprsClient->enqueueSpot(Radio::base_callsign(callsign), grid, frequency, snr);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::killFile ()
|
void MainWindow::killFile ()
|
||||||
{
|
{
|
||||||
if (m_fnameWE.size () &&
|
if (m_fnameWE.size () &&
|
||||||
@ -6946,6 +6978,7 @@ void MainWindow::band_changed (Frequency f)
|
|||||||
m_bandEdited = false;
|
m_bandEdited = false;
|
||||||
|
|
||||||
psk_Reporter->sendReport(); // Upload any queued spots before changing band
|
psk_Reporter->sendReport(); // Upload any queued spots before changing band
|
||||||
|
m_aprsClient->processQueue();
|
||||||
if (!m_transmitting) monitor (true);
|
if (!m_transmitting) monitor (true);
|
||||||
if ("FreqCal" == m_mode)
|
if ("FreqCal" == m_mode)
|
||||||
{
|
{
|
||||||
@ -7122,18 +7155,15 @@ void MainWindow::on_qtcMacroButton_clicked(){
|
|||||||
if(qtc.isEmpty()){
|
if(qtc.isEmpty()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addMessageText(qtc);
|
addMessageText(QString("QTC %1").arg(qtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_qthMacroButton_clicked(){
|
void MainWindow::on_qthMacroButton_clicked(){
|
||||||
QString qth = m_config.my_qth();
|
QString qth = m_config.my_qth();
|
||||||
if(qth.isEmpty()){
|
|
||||||
qth = m_config.my_grid();
|
|
||||||
}
|
|
||||||
if(qth.isEmpty()){
|
if(qth.isEmpty()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addMessageText(qth);
|
addMessageText(QString("QTH %1").arg(qth));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setSortBy(QString key, QString value){
|
void MainWindow::setSortBy(QString key, QString value){
|
||||||
@ -7284,6 +7314,20 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
if(m_config.transmit_directed()) toggleTx(true);
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto gridQueryAction = menu->addAction(QString("%1^ - What is your current grid locator?").arg(call));
|
||||||
|
gridQueryAction->setDisabled(isAllCall);
|
||||||
|
connect(gridQueryAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
if(selectedCall.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessageText(QString("%1^").arg(selectedCall), true);
|
||||||
|
|
||||||
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
|
});
|
||||||
|
|
||||||
auto stationMessageQueryAction = menu->addAction(QString("%1&& - What is your station message?").arg(call).trimmed());
|
auto stationMessageQueryAction = menu->addAction(QString("%1&& - What is your station message?").arg(call).trimmed());
|
||||||
stationMessageQueryAction->setDisabled(isAllCall);
|
stationMessageQueryAction->setDisabled(isAllCall);
|
||||||
connect(stationMessageQueryAction, &QAction::triggered, this, [this](){
|
connect(stationMessageQueryAction, &QAction::triggered, this, [this](){
|
||||||
@ -7362,6 +7406,49 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
addMessageText(QString("%1!").arg(selectedCall), true);
|
addMessageText(QString("%1!").arg(selectedCall), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
auto qtcAction = menu->addAction(QString("%1 QTC message - Send my station message").arg(call).trimmed());
|
||||||
|
connect(qtcAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
if(selectedCall.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessageText(QString("%1 QTC %2").arg(selectedCall).arg(m_config.my_station()), true);
|
||||||
|
|
||||||
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto qthAction = menu->addAction(QString("%1 QTH message - Send my station location message").arg(call).trimmed());
|
||||||
|
connect(qthAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
if(selectedCall.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessageText(QString("%1 QTH %2").arg(selectedCall).arg(m_config.my_qth()), true);
|
||||||
|
|
||||||
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto grid = m_config.my_grid();
|
||||||
|
auto gridAction = menu->addAction(QString("%1 GRID %2 - Send my current station grid location").arg(call).arg(grid).trimmed());
|
||||||
|
connect(gridAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
if(selectedCall.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessageText(QString("%1 GRID %2").arg(selectedCall).arg(m_config.my_grid()), true);
|
||||||
|
|
||||||
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
auto agnAction = menu->addAction(QString("%1 AGN? - Please repeat your last transmission").arg(call).trimmed());
|
auto agnAction = menu->addAction(QString("%1 AGN? - Please repeat your last transmission").arg(call).trimmed());
|
||||||
@ -7908,8 +7995,9 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.spot_to_psk_reporter ()) {
|
if (m_config.spot_to_reporting_networks ()) {
|
||||||
pskSetLocal ();
|
pskSetLocal();
|
||||||
|
aprsSetLocal();
|
||||||
}
|
}
|
||||||
statusChanged();
|
statusChanged();
|
||||||
m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
|
m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
|
||||||
@ -8167,6 +8255,11 @@ void MainWindow::pskSetLocal ()
|
|||||||
m_config.my_station(), QString {"FT8Call v" + version() }.simplified ());
|
m_config.my_station(), QString {"FT8Call v" + version() }.simplified ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::aprsSetLocal ()
|
||||||
|
{
|
||||||
|
m_aprsClient->setLocalStation(Radio::base_callsign(m_config.my_callsign()), m_config.my_grid());
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::transmitDisplay (bool transmitting)
|
void MainWindow::transmitDisplay (bool transmitting)
|
||||||
{
|
{
|
||||||
if (transmitting == m_transmitting) {
|
if (transmitting == m_transmitting) {
|
||||||
@ -8979,15 +9072,20 @@ void MainWindow::processCommandActivity() {
|
|||||||
else if (d.cmd == "@" && !isAllCall) {
|
else if (d.cmd == "@" && !isAllCall) {
|
||||||
QString qth = m_config.my_qth();
|
QString qth = m_config.my_qth();
|
||||||
if (qth.isEmpty()) {
|
if (qth.isEmpty()) {
|
||||||
QString grid = m_config.my_grid();
|
continue;
|
||||||
if (grid.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
qth = grid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = QString("%1 QTH %2").arg(d.from).arg(qth);
|
reply = QString("%1 QTH %2").arg(d.from).arg(qth);
|
||||||
}
|
}
|
||||||
|
// QUERIED GRID
|
||||||
|
else if (d.cmd == "^" && !isAllCall) {
|
||||||
|
QString grid = m_config.my_grid();
|
||||||
|
if (grid.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = QString("%1 GRID %2").arg(d.from).arg(grid);
|
||||||
|
}
|
||||||
// QUERIED STATION MESSAGE
|
// QUERIED STATION MESSAGE
|
||||||
else if (d.cmd == "&" && !isAllCall) {
|
else if (d.cmd == "&" && !isAllCall) {
|
||||||
reply = QString("%1 QTC %2").arg(d.from).arg(m_config.my_station());
|
reply = QString("%1 QTC %2").arg(d.from).arg(m_config.my_station());
|
||||||
@ -9031,9 +9129,30 @@ void MainWindow::processCommandActivity() {
|
|||||||
else if (d.cmd == "#" && !isAllCall) {
|
else if (d.cmd == "#" && !isAllCall) {
|
||||||
reply = QString("%1 ACK").arg(d.from);
|
reply = QString("%1 ACK").arg(d.from);
|
||||||
}
|
}
|
||||||
|
// PROCESS AGN
|
||||||
|
else if (d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()) {
|
||||||
|
reply = m_lastTxMessage;
|
||||||
|
}
|
||||||
|
// PROCESS BUFFERED QTH
|
||||||
|
else if (d.cmd == " GRID"){
|
||||||
|
// 1. parse grids
|
||||||
|
// 2. log it to reporting networks
|
||||||
|
auto grids = Varicode::parseGrids(d.text);
|
||||||
|
foreach(auto grid, grids){
|
||||||
|
CallDetail cd = {};
|
||||||
|
cd.bits = d.bits;
|
||||||
|
cd.call = d.from;
|
||||||
|
cd.freq = d.freq;
|
||||||
|
cd.grid = grid;
|
||||||
|
cd.snr = d.snr;
|
||||||
|
cd.utcTimestamp = d.utcTimestamp;
|
||||||
|
logCallActivity(cd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = QString("%1 ACK").arg(d.from);
|
||||||
|
}
|
||||||
// PROCESS ALERT
|
// PROCESS ALERT
|
||||||
else if (d.cmd == "!" && !isAllCall) {
|
else if (d.cmd == "!" && !isAllCall) {
|
||||||
|
|
||||||
QMessageBox * msgBox = new QMessageBox(this);
|
QMessageBox * msgBox = new QMessageBox(this);
|
||||||
msgBox->setIcon(QMessageBox::Information);
|
msgBox->setIcon(QMessageBox::Information);
|
||||||
|
|
||||||
@ -9056,10 +9175,6 @@ void MainWindow::processCommandActivity() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
msgBox->show();
|
msgBox->show();
|
||||||
|
|
||||||
continue;
|
|
||||||
} else if (d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()) {
|
|
||||||
reply = m_lastTxMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply.isEmpty()) {
|
if (reply.isEmpty()) {
|
||||||
@ -9098,14 +9213,16 @@ void MainWindow::processSpots() {
|
|||||||
|
|
||||||
// Process spots to be sent...
|
// Process spots to be sent...
|
||||||
pskSetLocal();
|
pskSetLocal();
|
||||||
|
aprsSetLocal();
|
||||||
|
|
||||||
while(!m_rxCallQueue.isEmpty()){
|
while(!m_rxCallQueue.isEmpty()){
|
||||||
CallDetail d = m_rxCallQueue.dequeue();
|
CallDetail d = m_rxCallQueue.dequeue();
|
||||||
if(d.call.isEmpty()){
|
if(d.call.isEmpty()){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
qDebug() << "spotting call to psk reporter" << d.call << d.snr << d.freq;
|
qDebug() << "spotting call to reporting networks" << d.call << d.snr << d.freq;
|
||||||
pskLogReport("FT8CALL", d.freq, d.snr, d.call, d.grid);
|
pskLogReport("FT8CALL", d.freq, d.snr, d.call, d.grid);
|
||||||
|
aprsLogReport(d.freq, d.snr, d.call, d.grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "qpriorityqueue.h"
|
#include "qpriorityqueue.h"
|
||||||
#include "varicode.h"
|
#include "varicode.h"
|
||||||
#include "MessageClient.hpp"
|
#include "MessageClient.hpp"
|
||||||
|
#include "APRSISClient.h"
|
||||||
|
|
||||||
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
|
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
|
||||||
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
|
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
|
||||||
@ -167,7 +168,7 @@ private slots:
|
|||||||
void on_actionShow_Waterfall_triggered(bool checked);
|
void on_actionShow_Waterfall_triggered(bool checked);
|
||||||
void on_actionReset_Window_Sizes_triggered();
|
void on_actionReset_Window_Sizes_triggered();
|
||||||
void on_actionSettings_triggered();
|
void on_actionSettings_triggered();
|
||||||
void preparePSKReporter();
|
void prepareSpotting();
|
||||||
void on_spotButton_clicked(bool checked);
|
void on_spotButton_clicked(bool checked);
|
||||||
void on_monitorButton_clicked (bool);
|
void on_monitorButton_clicked (bool);
|
||||||
void on_actionAbout_triggered();
|
void on_actionAbout_triggered();
|
||||||
@ -805,6 +806,7 @@ private:
|
|||||||
QTimer m_heartbeat;
|
QTimer m_heartbeat;
|
||||||
MessageClient * m_messageClient;
|
MessageClient * m_messageClient;
|
||||||
PSK_Reporter *psk_Reporter;
|
PSK_Reporter *psk_Reporter;
|
||||||
|
APRSISClient * m_aprsClient;
|
||||||
DisplayManual m_manual;
|
DisplayManual m_manual;
|
||||||
QHash<QString, QVariant> m_pwrBandTxMemory; // Remembers power level by band
|
QHash<QString, QVariant> m_pwrBandTxMemory; // Remembers power level by band
|
||||||
QHash<QString, QVariant> m_pwrBandTuneMemory; // Remembers power level by band for tuning
|
QHash<QString, QVariant> m_pwrBandTuneMemory; // Remembers power level by band for tuning
|
||||||
@ -831,8 +833,10 @@ private:
|
|||||||
void transmit (double snr = 99.);
|
void transmit (double snr = 99.);
|
||||||
void rigFailure (QString const& reason);
|
void rigFailure (QString const& reason);
|
||||||
void pskSetLocal ();
|
void pskSetLocal ();
|
||||||
|
void aprsSetLocal ();
|
||||||
void pskPost(DecodedText const& decodedtext);
|
void pskPost(DecodedText const& decodedtext);
|
||||||
void pskLogReport(QString mode, 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();
|
Radio::Frequency dialFrequency();
|
||||||
void displayDialFrequency ();
|
void displayDialFrequency ();
|
||||||
void transmitDisplay (bool);
|
void transmitDisplay (bool);
|
||||||
|
@ -1250,6 +1250,9 @@ QTextEdit[transmitting="true"] {
|
|||||||
</item>
|
</item>
|
||||||
<item row="1" column="4">
|
<item row="1" column="4">
|
||||||
<widget class="QPushButton" name="qthMacroButton">
|
<widget class="QPushButton" name="qthMacroButton">
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
@ -1301,15 +1304,15 @@ QTextEdit[transmitting="true"] {
|
|||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="1" column="2">
|
||||||
<widget class="QPushButton" name="qtcMacroButton">
|
<widget class="QPushButton" name="qtcMacroButton">
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>30</height>
|
<height>30</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="visible">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Send your station message</p></body></html></string>
|
<string><html><head/><body><p>Send your station message</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
|
24
varicode.cpp
24
varicode.cpp
@ -30,7 +30,7 @@
|
|||||||
const int nalphabet = 41;
|
const int nalphabet = 41;
|
||||||
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"}; // alphabet to encode _into_ for FT8 freetext transmission
|
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"}; // alphabet to encode _into_ for FT8 freetext transmission
|
||||||
QString alphabet72 = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+/?."};
|
QString alphabet72 = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+/?."};
|
||||||
QString grid_pattern = {R"((?<grid>[A-R]{2}[0-9]{2})+)"};
|
QString grid_pattern = {R"((?<grid>[A-X]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*)+)"};
|
||||||
QString orig_compound_callsign_pattern = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){2,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"};
|
QString orig_compound_callsign_pattern = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){2,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"};
|
||||||
QString compound_callsign_pattern = {R"((?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
|
QString compound_callsign_pattern = {R"((?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
|
||||||
QString pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
|
QString pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
|
||||||
@ -44,6 +44,7 @@ QMap<QString, int> directed_cmds = {
|
|||||||
{"@", 1 }, // query qth
|
{"@", 1 }, // query qth
|
||||||
{"&", 2 }, // query station message
|
{"&", 2 }, // query station message
|
||||||
{"$", 3 }, // query station(s) heard
|
{"$", 3 }, // query station(s) heard
|
||||||
|
{"^", 4 }, // query grid
|
||||||
|
|
||||||
{"%", 5 }, // query pwr
|
{"%", 5 }, // query pwr
|
||||||
{"|", 6 }, // retransmit message
|
{"|", 6 }, // retransmit message
|
||||||
@ -54,10 +55,11 @@ QMap<QString, int> directed_cmds = {
|
|||||||
// {"/", 10 }, // unused? (can we even use stroke?)
|
// {"/", 10 }, // unused? (can we even use stroke?)
|
||||||
|
|
||||||
// directed responses
|
// directed responses
|
||||||
{" QTC", 16 }, // this is my qtc
|
{" GRID", 15 }, // this is my current grid locator
|
||||||
{" QTH", 17 }, // this is my qth
|
{" QTC", 16 }, // this is my qtc message
|
||||||
|
{" QTH", 17 }, // this is my qth message
|
||||||
{" FB", 18 }, // fine business
|
{" FB", 18 }, // fine business
|
||||||
{" HW CPY?", 19 }, // how do you copy?
|
{" HW CPY?", 19 }, // how do you copy?
|
||||||
{" HEARING", 20 }, // i am hearing the following stations
|
{" HEARING", 20 }, // i am hearing the following stations
|
||||||
{" RR", 21 }, // roger roger
|
{" RR", 21 }, // roger roger
|
||||||
{" QSL?", 22 }, // do you copy?
|
{" QSL?", 22 }, // do you copy?
|
||||||
@ -72,12 +74,12 @@ QMap<QString, int> directed_cmds = {
|
|||||||
{" ", 31 }, // send freetext
|
{" ", 31 }, // send freetext
|
||||||
};
|
};
|
||||||
|
|
||||||
QSet<int> allowed_cmds = {0, 1, 2, 3, /*4,*/ 5, 6, 7, 8, /*...*/ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 8, /*...*/ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
||||||
|
|
||||||
QSet<int> buffered_cmds = {6, 7, 8};
|
QSet<int> buffered_cmds = {6, 7, 8, 15};
|
||||||
|
|
||||||
QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)");
|
QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)");
|
||||||
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|PWR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|[?@&$%|!# ]))?");
|
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|PWR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|[?@&$%|!#^ ]))?");
|
||||||
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
|
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
|
||||||
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
|
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
|
||||||
QString optional_pwr_pattern = QString("(?<pwr>(?<=PWR)\\s?\\d+\\s?[KM]?W)?");
|
QString optional_pwr_pattern = QString("(?<pwr>(?<=PWR)\\s?\\d+\\s?[KM]?W)?");
|
||||||
@ -951,7 +953,7 @@ QString Varicode::unpackCallsign(quint32 value){
|
|||||||
return callsign;
|
return callsign;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString deg2grid(float dlong, float dlat){
|
QString Varicode::deg2grid(float dlong, float dlat){
|
||||||
QChar grid[6];
|
QChar grid[6];
|
||||||
|
|
||||||
if(dlong < -180){
|
if(dlong < -180){
|
||||||
@ -984,7 +986,7 @@ QString deg2grid(float dlong, float dlat){
|
|||||||
return QString(grid, 6);
|
return QString(grid, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<float, float> grid2deg(QString const &grid){
|
QPair<float, float> Varicode::grid2deg(QString const &grid){
|
||||||
QPair<float, float> longLat;
|
QPair<float, float> longLat;
|
||||||
|
|
||||||
QString g = grid;
|
QString g = grid;
|
||||||
@ -1016,7 +1018,7 @@ quint16 Varicode::packGrid(QString const& value){
|
|||||||
return (1<<15)-1;
|
return (1<<15)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pair = grid2deg(grid.left(4));
|
auto pair = Varicode::grid2deg(grid.left(4));
|
||||||
int ilong = pair.first;
|
int ilong = pair.first;
|
||||||
int ilat = pair.second + 90;
|
int ilat = pair.second + 90;
|
||||||
|
|
||||||
@ -1031,7 +1033,7 @@ QString Varicode::unpackGrid(quint16 value){
|
|||||||
float dlat = value % 180 - 90;
|
float dlat = value % 180 - 90;
|
||||||
float dlong = value / 180 * 2 - 180 + 2;
|
float dlong = value / 180 * 2 - 180 + 2;
|
||||||
|
|
||||||
return deg2grid(dlong, dlat).left(4);
|
return Varicode::deg2grid(dlong, dlat).left(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pack a number or snr into an integer between 0 & 62
|
// pack a number or snr into an integer between 0 & 62
|
||||||
|
@ -109,6 +109,8 @@ public:
|
|||||||
static quint32 packCallsign(QString const& value);
|
static quint32 packCallsign(QString const& value);
|
||||||
static QString unpackCallsign(quint32 value);
|
static QString unpackCallsign(quint32 value);
|
||||||
|
|
||||||
|
static QString deg2grid(float dlong, float dlat);
|
||||||
|
static QPair<float, float> grid2deg(QString const &grid);
|
||||||
static quint16 packGrid(QString const& value);
|
static quint16 packGrid(QString const& value);
|
||||||
static QString unpackGrid(quint16 value);
|
static QString unpackGrid(quint16 value);
|
||||||
|
|
||||||
|
@ -71,7 +71,8 @@ SOURCES += \
|
|||||||
varicode.cpp \
|
varicode.cpp \
|
||||||
NetworkMessage.cpp \
|
NetworkMessage.cpp \
|
||||||
MessageClient.cpp \
|
MessageClient.cpp \
|
||||||
SelfDestructMessageBox.cpp
|
SelfDestructMessageBox.cpp \
|
||||||
|
APRSISClient.cpp
|
||||||
|
|
||||||
HEADERS += qt_helpers.hpp \
|
HEADERS += qt_helpers.hpp \
|
||||||
pimpl_h.hpp pimpl_impl.hpp \
|
pimpl_h.hpp pimpl_impl.hpp \
|
||||||
@ -94,7 +95,8 @@ HEADERS += qt_helpers.hpp \
|
|||||||
crc.h \
|
crc.h \
|
||||||
NetworkMessage.hpp \
|
NetworkMessage.hpp \
|
||||||
MessageClient.hpp \
|
MessageClient.hpp \
|
||||||
SelfDestructMessageBox.h
|
SelfDestructMessageBox.h \
|
||||||
|
APRSISClient.h
|
||||||
|
|
||||||
|
|
||||||
INCLUDEPATH += qmake_only
|
INCLUDEPATH += qmake_only
|
||||||
|
Loading…
Reference in New Issue
Block a user