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
|
||||
varicode.cpp
|
||||
SelfDestructMessageBox.cpp
|
||||
APRSISClient.cpp
|
||||
mainwindow.cpp
|
||||
Configuration.cpp
|
||||
main.cpp
|
||||
|
@ -576,7 +576,7 @@ private:
|
||||
double txDelay_;
|
||||
bool id_after_73_;
|
||||
bool tx_QSY_allowed_;
|
||||
bool spot_to_psk_reporter_;
|
||||
bool spot_to_reporting_networks_;
|
||||
bool transmit_directed_;
|
||||
bool autoreply_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_;}
|
||||
bool Configuration::id_after_73 () const {return m_->id_after_73_;}
|
||||
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
|
||||
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){
|
||||
m_->spot_to_psk_reporter_ = spot;
|
||||
if(m_->spot_to_reporting_networks_ != spot){
|
||||
m_->spot_to_reporting_networks_ = spot;
|
||||
m_->write_settings();
|
||||
}
|
||||
}
|
||||
@ -1264,7 +1264,7 @@ void Configuration::impl::initialize_models ()
|
||||
ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
|
||||
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_check_box->setChecked (spot_to_reporting_networks_);
|
||||
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
||||
ui_->autoreply_off_check_box->setChecked (autoreply_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 ();
|
||||
monitor_off_at_startup_ = settings_->value ("MonitorOFF", 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 ();
|
||||
tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", 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 ("MonitorOFF", monitor_off_at_startup_);
|
||||
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 ("TxQSYAllowed", tx_QSY_allowed_);
|
||||
settings_->setValue ("Macros", macros_.stringList ());
|
||||
@ -2033,7 +2033,7 @@ void Configuration::impl::accept ()
|
||||
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
||||
callsign_aging_ = ui_->callsign_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 ();
|
||||
ntrials_ = ui_->sbNtrials->value ();
|
||||
txDelay_ = ui_->sbTxDelay->value ();
|
||||
|
@ -112,8 +112,8 @@ public:
|
||||
double txDelay() const;
|
||||
bool id_after_73 () const;
|
||||
bool tx_QSY_allowed () const;
|
||||
bool spot_to_psk_reporter () const;
|
||||
void set_spot_to_psk_reporter (bool);
|
||||
bool spot_to_reporting_networks () const;
|
||||
void set_spot_to_reporting_networks (bool);
|
||||
bool transmit_directed() const;
|
||||
bool autoreply_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>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>16</number>
|
||||
<number>12</number>
|
||||
</property>
|
||||
</widget>
|
||||
</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
|
||||
#include "mainwindow.h"
|
||||
#include <cmath>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
@ -25,6 +26,7 @@
|
||||
#include <QActionGroup>
|
||||
#include <QSplashScreen>
|
||||
|
||||
#include "APRSISClient.h"
|
||||
#include "revision_utils.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
#include "NetworkAccessManager.hpp"
|
||||
@ -466,6 +468,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
version (), revision (),
|
||||
m_config.udp_server_name (), m_config.udp_server_port (),
|
||||
this}},
|
||||
m_aprsClient {new APRSISClient{"rotate.aprs2.net", 14580, this}},
|
||||
psk_Reporter {new PSK_Reporter {m_messageClient, this}},
|
||||
m_i3bit {0},
|
||||
m_manual {&m_network_manager},
|
||||
@ -1014,7 +1017,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
setFreqOffsetForRestore(f, 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();
|
||||
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;
|
||||
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() << 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
|
||||
|
||||
// this must be the last statement of constructor
|
||||
@ -2035,7 +2049,7 @@ void MainWindow::fastSink(qint64 frames)
|
||||
writeAllTxt(message);
|
||||
bool stdMsg = decodedtext.report(m_baseCall,
|
||||
Radio::base_callsign(ui->dxCallEntry->text()),m_rptRcvd);
|
||||
if (stdMsg) pskPost (decodedtext);
|
||||
//if (stdMsg) pskPost (decodedtext);
|
||||
}
|
||||
|
||||
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
|
||||
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
|
||||
|
||||
preparePSKReporter();
|
||||
prepareSpotting();
|
||||
|
||||
if(m_config.restart_audio_input ()) {
|
||||
Q_EMIT startAudioInputStream (m_config.audio_input_device (),
|
||||
@ -2233,9 +2247,10 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::preparePSKReporter(){
|
||||
if(m_config.spot_to_psk_reporter ()){
|
||||
pskSetLocal ();
|
||||
void MainWindow::prepareSpotting(){
|
||||
if(m_config.spot_to_reporting_networks ()){
|
||||
pskSetLocal();
|
||||
aprsSetLocal();
|
||||
ui->spotButton->setChecked(true);
|
||||
} else {
|
||||
ui->spotButton->setChecked(false);
|
||||
@ -2244,10 +2259,10 @@ void MainWindow::preparePSKReporter(){
|
||||
|
||||
void MainWindow::on_spotButton_clicked(bool checked){
|
||||
// 1. save setting
|
||||
m_config.set_spot_to_psk_reporter(checked);
|
||||
m_config.set_spot_to_reporting_networks(checked);
|
||||
|
||||
// 2. prepare
|
||||
preparePSKReporter();
|
||||
prepareSpotting();
|
||||
}
|
||||
|
||||
void MainWindow::on_monitorButton_clicked (bool checked)
|
||||
@ -3247,12 +3262,12 @@ void::MainWindow::fast_decode_done()
|
||||
writeAllTxt(message);
|
||||
|
||||
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,
|
||||
Radio::base_callsign(ui->dxCallEntry->text()), m_rptRcvd);
|
||||
|
||||
// extract details and send to PSKreporter
|
||||
if (stdMsg) pskPost (decodedtext);
|
||||
// extract details and send to PSKreporter
|
||||
//if (stdMsg) pskPost (decodedtext);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
bool stdMsg = decodedtext.report(m_baseCall,
|
||||
Radio::base_callsign(ui->dxCallEntry->text()), rpt);
|
||||
@ -3699,10 +3714,11 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
QString t=Radio::base_callsign(ui->dxCallEntry->text());
|
||||
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;
|
||||
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_msgAvgWidget->isVisible()) {
|
||||
@ -3811,7 +3827,8 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler
|
||||
|
||||
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;
|
||||
if(m_mode=="JT9+JT65") {
|
||||
@ -3826,6 +3843,7 @@ void MainWindow::pskPost (DecodedText const& decodedtext)
|
||||
audioFrequency=decodedtext.string().mid(16,4).toInt();
|
||||
}
|
||||
int snr = decodedtext.snr();
|
||||
|
||||
pskSetLocal ();
|
||||
if(grid.contains (grid_regexp)) {
|
||||
// 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()));
|
||||
pskLogReport(msgmode, audioFrequency, snr, deCall, grid);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -3849,6 +3868,19 @@ void MainWindow::pskLogReport(QString mode, int offset, int snr, QString callsig
|
||||
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 ()
|
||||
{
|
||||
if (m_fnameWE.size () &&
|
||||
@ -6946,6 +6978,7 @@ void MainWindow::band_changed (Frequency f)
|
||||
m_bandEdited = false;
|
||||
|
||||
psk_Reporter->sendReport(); // Upload any queued spots before changing band
|
||||
m_aprsClient->processQueue();
|
||||
if (!m_transmitting) monitor (true);
|
||||
if ("FreqCal" == m_mode)
|
||||
{
|
||||
@ -7122,18 +7155,15 @@ void MainWindow::on_qtcMacroButton_clicked(){
|
||||
if(qtc.isEmpty()){
|
||||
return;
|
||||
}
|
||||
addMessageText(qtc);
|
||||
addMessageText(QString("QTC %1").arg(qtc));
|
||||
}
|
||||
|
||||
void MainWindow::on_qthMacroButton_clicked(){
|
||||
QString qth = m_config.my_qth();
|
||||
if(qth.isEmpty()){
|
||||
qth = m_config.my_grid();
|
||||
}
|
||||
if(qth.isEmpty()){
|
||||
return;
|
||||
}
|
||||
addMessageText(qth);
|
||||
addMessageText(QString("QTH %1").arg(qth));
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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());
|
||||
stationMessageQueryAction->setDisabled(isAllCall);
|
||||
connect(stationMessageQueryAction, &QAction::triggered, this, [this](){
|
||||
@ -7362,6 +7406,49 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
||||
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();
|
||||
|
||||
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 ()) {
|
||||
pskSetLocal ();
|
||||
if (m_config.spot_to_reporting_networks ()) {
|
||||
pskSetLocal();
|
||||
aprsSetLocal();
|
||||
}
|
||||
statusChanged();
|
||||
m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
|
||||
@ -8167,6 +8255,11 @@ void MainWindow::pskSetLocal ()
|
||||
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)
|
||||
{
|
||||
if (transmitting == m_transmitting) {
|
||||
@ -8979,15 +9072,20 @@ void MainWindow::processCommandActivity() {
|
||||
else if (d.cmd == "@" && !isAllCall) {
|
||||
QString qth = m_config.my_qth();
|
||||
if (qth.isEmpty()) {
|
||||
QString grid = m_config.my_grid();
|
||||
if (grid.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
qth = grid;
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
else if (d.cmd == "&" && !isAllCall) {
|
||||
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) {
|
||||
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
|
||||
else if (d.cmd == "!" && !isAllCall) {
|
||||
|
||||
QMessageBox * msgBox = new QMessageBox(this);
|
||||
msgBox->setIcon(QMessageBox::Information);
|
||||
|
||||
@ -9056,10 +9175,6 @@ void MainWindow::processCommandActivity() {
|
||||
});
|
||||
|
||||
msgBox->show();
|
||||
|
||||
continue;
|
||||
} else if (d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()) {
|
||||
reply = m_lastTxMessage;
|
||||
}
|
||||
|
||||
if (reply.isEmpty()) {
|
||||
@ -9098,14 +9213,16 @@ void MainWindow::processSpots() {
|
||||
|
||||
// Process spots to be sent...
|
||||
pskSetLocal();
|
||||
aprsSetLocal();
|
||||
|
||||
while(!m_rxCallQueue.isEmpty()){
|
||||
CallDetail d = m_rxCallQueue.dequeue();
|
||||
if(d.call.isEmpty()){
|
||||
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);
|
||||
aprsLogReport(d.freq, d.snr, d.call, d.grid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "qpriorityqueue.h"
|
||||
#include "varicode.h"
|
||||
#include "MessageClient.hpp"
|
||||
#include "APRSISClient.h"
|
||||
|
||||
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded 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_actionReset_Window_Sizes_triggered();
|
||||
void on_actionSettings_triggered();
|
||||
void preparePSKReporter();
|
||||
void prepareSpotting();
|
||||
void on_spotButton_clicked(bool checked);
|
||||
void on_monitorButton_clicked (bool);
|
||||
void on_actionAbout_triggered();
|
||||
@ -805,6 +806,7 @@ private:
|
||||
QTimer m_heartbeat;
|
||||
MessageClient * m_messageClient;
|
||||
PSK_Reporter *psk_Reporter;
|
||||
APRSISClient * m_aprsClient;
|
||||
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
|
||||
@ -831,8 +833,10 @@ private:
|
||||
void transmit (double snr = 99.);
|
||||
void rigFailure (QString const& reason);
|
||||
void pskSetLocal ();
|
||||
void aprsSetLocal ();
|
||||
void pskPost(DecodedText const& decodedtext);
|
||||
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();
|
||||
void displayDialFrequency ();
|
||||
void transmitDisplay (bool);
|
||||
|
@ -1250,6 +1250,9 @@ QTextEdit[transmitting="true"] {
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="qthMacroButton">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
@ -1301,15 +1304,15 @@ QTextEdit[transmitting="true"] {
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="qtcMacroButton">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Send your station message</p></body></html></string>
|
||||
</property>
|
||||
|
24
varicode.cpp
24
varicode.cpp
@ -30,7 +30,7 @@
|
||||
const int nalphabet = 41;
|
||||
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"}; // alphabet to encode _into_ for FT8 freetext transmission
|
||||
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 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 ]))"};
|
||||
@ -44,6 +44,7 @@ QMap<QString, int> directed_cmds = {
|
||||
{"@", 1 }, // query qth
|
||||
{"&", 2 }, // query station message
|
||||
{"$", 3 }, // query station(s) heard
|
||||
{"^", 4 }, // query grid
|
||||
|
||||
{"%", 5 }, // query pwr
|
||||
{"|", 6 }, // retransmit message
|
||||
@ -54,10 +55,11 @@ QMap<QString, int> directed_cmds = {
|
||||
// {"/", 10 }, // unused? (can we even use stroke?)
|
||||
|
||||
// directed responses
|
||||
{" QTC", 16 }, // this is my qtc
|
||||
{" QTH", 17 }, // this is my qth
|
||||
{" GRID", 15 }, // this is my current grid locator
|
||||
{" QTC", 16 }, // this is my qtc message
|
||||
{" QTH", 17 }, // this is my qth message
|
||||
{" 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
|
||||
{" RR", 21 }, // roger roger
|
||||
{" QSL?", 22 }, // do you copy?
|
||||
@ -72,12 +74,12 @@ QMap<QString, int> directed_cmds = {
|
||||
{" ", 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 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_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)?");
|
||||
@ -951,7 +953,7 @@ QString Varicode::unpackCallsign(quint32 value){
|
||||
return callsign;
|
||||
}
|
||||
|
||||
QString deg2grid(float dlong, float dlat){
|
||||
QString Varicode::deg2grid(float dlong, float dlat){
|
||||
QChar grid[6];
|
||||
|
||||
if(dlong < -180){
|
||||
@ -984,7 +986,7 @@ QString deg2grid(float dlong, float dlat){
|
||||
return QString(grid, 6);
|
||||
}
|
||||
|
||||
QPair<float, float> grid2deg(QString const &grid){
|
||||
QPair<float, float> Varicode::grid2deg(QString const &grid){
|
||||
QPair<float, float> longLat;
|
||||
|
||||
QString g = grid;
|
||||
@ -1016,7 +1018,7 @@ quint16 Varicode::packGrid(QString const& value){
|
||||
return (1<<15)-1;
|
||||
}
|
||||
|
||||
auto pair = grid2deg(grid.left(4));
|
||||
auto pair = Varicode::grid2deg(grid.left(4));
|
||||
int ilong = pair.first;
|
||||
int ilat = pair.second + 90;
|
||||
|
||||
@ -1031,7 +1033,7 @@ QString Varicode::unpackGrid(quint16 value){
|
||||
float dlat = value % 180 - 90;
|
||||
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
|
||||
|
@ -109,6 +109,8 @@ public:
|
||||
static quint32 packCallsign(QString const& 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 QString unpackGrid(quint16 value);
|
||||
|
||||
|
@ -71,7 +71,8 @@ SOURCES += \
|
||||
varicode.cpp \
|
||||
NetworkMessage.cpp \
|
||||
MessageClient.cpp \
|
||||
SelfDestructMessageBox.cpp
|
||||
SelfDestructMessageBox.cpp \
|
||||
APRSISClient.cpp
|
||||
|
||||
HEADERS += qt_helpers.hpp \
|
||||
pimpl_h.hpp pimpl_impl.hpp \
|
||||
@ -94,7 +95,8 @@ HEADERS += qt_helpers.hpp \
|
||||
crc.h \
|
||||
NetworkMessage.hpp \
|
||||
MessageClient.hpp \
|
||||
SelfDestructMessageBox.h
|
||||
SelfDestructMessageBox.h \
|
||||
APRSISClient.h
|
||||
|
||||
|
||||
INCLUDEPATH += qmake_only
|
||||
|
Loading…
Reference in New Issue
Block a user