Added automatic BEACON acknowledgment and BEACON REQ commands
This commit is contained in:
parent
fe1509c94b
commit
25e7631c09
@ -769,7 +769,7 @@ bool Configuration::clear_DX () const {return m_->clear_DX_;}
|
|||||||
bool Configuration::miles () const {return m_->miles_;}
|
bool Configuration::miles () const {return m_->miles_;}
|
||||||
bool Configuration::quick_call () const {return m_->quick_call_;}
|
bool Configuration::quick_call () const {return m_->quick_call_;}
|
||||||
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
|
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
|
||||||
int Configuration::beacon () const {return m_->beacon_;}
|
int Configuration::beacon () const {return qMax(10, qMin(m_->beacon_, 1440));}
|
||||||
int Configuration::watchdog () const {return m_->watchdog_;}
|
int Configuration::watchdog () const {return m_->watchdog_;}
|
||||||
bool Configuration::TX_messages () const {return m_->TX_messages_;}
|
bool Configuration::TX_messages () const {return m_->TX_messages_;}
|
||||||
bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;}
|
bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;}
|
||||||
|
@ -530,10 +530,10 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>15</number>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>60</number>
|
<number>1440</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
|
141
mainwindow.cpp
141
mainwindow.cpp
@ -904,7 +904,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
connect(&TxAgainTimer, SIGNAL(timeout()), this, SLOT(TxAgain()));
|
connect(&TxAgainTimer, SIGNAL(timeout()), this, SLOT(TxAgain()));
|
||||||
|
|
||||||
beaconTimer.setSingleShot(false);
|
beaconTimer.setSingleShot(false);
|
||||||
connect(&beaconTimer, &QTimer::timeout, this, &MainWindow::checkBacon);
|
connect(&beaconTimer, &QTimer::timeout, this, &MainWindow::checkBeacon);
|
||||||
|
|
||||||
connect(m_wideGraph.data (), SIGNAL(setFreq3(int,int)),this,
|
connect(m_wideGraph.data (), SIGNAL(setFreq3(int,int)),this,
|
||||||
SLOT(setFreq4(int,int)));
|
SLOT(setFreq4(int,int)));
|
||||||
@ -3719,7 +3719,18 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
cd.bits = decodedtext.bits();
|
cd.bits = decodedtext.bits();
|
||||||
|
|
||||||
if(decodedtext.isBeacon()){
|
if(decodedtext.isBeacon()){
|
||||||
logCallActivity(cd, true);
|
// convert BEACON to a directed command and process...
|
||||||
|
CommandDetail d = {};
|
||||||
|
d.from = cd.call;
|
||||||
|
d.to = "ALLCALL";
|
||||||
|
d.cmd = " BEACON";
|
||||||
|
d.snr = cd.snr;
|
||||||
|
d.bits = cd.bits;
|
||||||
|
d.extra = cd.grid;
|
||||||
|
d.freq = cd.freq;
|
||||||
|
d.utcTimestamp = cd.utcTimestamp;
|
||||||
|
m_rxCommandQueue.append(d);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "buffering compound call" << cd.call << cd.bits;
|
qDebug() << "buffering compound call" << cd.call << cd.bits;
|
||||||
m_messageBuffer[cd.freq/10*10].compound.append(cd);
|
m_messageBuffer[cd.freq/10*10].compound.append(cd);
|
||||||
@ -6200,6 +6211,11 @@ void MainWindow::enqueueMessage(int priority, QString message, int freq, Callbac
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::enqueueBeacon(QString message){
|
||||||
|
m_txBeaconQueue.enqueue(message);
|
||||||
|
scheduleBeacon(true);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::resetMessage(){
|
void MainWindow::resetMessage(){
|
||||||
resetMessageUI();
|
resetMessageUI();
|
||||||
resetMessageTransmitQueue();
|
resetMessageTransmitQueue();
|
||||||
@ -6356,13 +6372,19 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
|
|||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
|
|
||||||
// remove our callsign from the start of the line...
|
// remove our callsign from the start of the line...
|
||||||
if(line.startsWith(mycall + ":")){
|
if(line.startsWith(mycall + ":") || line.startsWith(mycall + " ")){
|
||||||
line = lstrip(line.mid(mycall.length() + 1));
|
line = lstrip(line.mid(mycall.length() + 1));
|
||||||
}
|
}
|
||||||
if(line.startsWith(basecall + ":")){
|
if(line.startsWith(basecall + ":") || line.startsWith(basecall + " ")){
|
||||||
line = lstrip(line.mid(basecall.length() + 1));
|
line = lstrip(line.mid(basecall.length() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove trailing whitespace as long as there are characters left afterwards
|
||||||
|
auto rline = rstrip(line);
|
||||||
|
if(!rline.isEmpty()){
|
||||||
|
line = rline;
|
||||||
|
}
|
||||||
|
|
||||||
#if AUTO_PREPEND_DIRECTED
|
#if AUTO_PREPEND_DIRECTED
|
||||||
// see if we need to prepend the directed call to the line...
|
// see if we need to prepend the directed call to the line...
|
||||||
// if we have a selected call and the text doesn't start with that call...
|
// if we have a selected call and the text doesn't start with that call...
|
||||||
@ -6650,7 +6672,7 @@ bool MainWindow::prepareNextMessageFrame()
|
|||||||
|
|
||||||
if(ui->beaconButton->isChecked()){
|
if(ui->beaconButton->isChecked()){
|
||||||
// bump beacon
|
// bump beacon
|
||||||
scheduleBacon(false);
|
scheduleBeacon(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -6711,7 +6733,7 @@ int MainWindow::findFreeFreqOffset(int fmin, int fmax, int bw){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// scheduleBeacon
|
// scheduleBeacon
|
||||||
void MainWindow::scheduleBacon(bool first){
|
void MainWindow::scheduleBeacon(bool first){
|
||||||
auto timestamp = DriftingDateTime::currentDateTimeUtc();
|
auto timestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
auto orig = timestamp;
|
auto orig = timestamp;
|
||||||
|
|
||||||
@ -6742,7 +6764,7 @@ void MainWindow::scheduleBacon(bool first){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pauseBeacon
|
// pauseBeacon
|
||||||
void MainWindow::pauseBacon(){
|
void MainWindow::pauseBeacon(){
|
||||||
ui->beaconButton->setChecked(false);
|
ui->beaconButton->setChecked(false);
|
||||||
m_nextBeaconPaused = true;
|
m_nextBeaconPaused = true;
|
||||||
|
|
||||||
@ -6752,40 +6774,52 @@ void MainWindow::pauseBacon(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkBeacon
|
// checkBeacon
|
||||||
void MainWindow::checkBacon(){
|
void MainWindow::checkBeacon(){
|
||||||
if(!ui->beaconButton->isChecked()){
|
if(!ui->beaconButton->isChecked()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(DriftingDateTime::currentDateTimeUtc().secsTo(m_nextBeacon) > 5){
|
auto secondsUntilBeacon = DriftingDateTime::currentDateTimeUtc().secsTo(m_nextBeacon);
|
||||||
|
if(secondsUntilBeacon > 5 && m_txBeaconQueue.isEmpty()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(m_nextBeaconQueued){
|
if(m_nextBeaconQueued){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareBacon();
|
prepareBeacon();
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareBeacon
|
// prepareBeacon
|
||||||
void MainWindow::prepareBacon(){
|
void MainWindow::prepareBeacon(){
|
||||||
QStringList lines;
|
QStringList lines;
|
||||||
|
|
||||||
QString mycall = m_config.my_callsign();
|
QString mycall = m_config.my_callsign();
|
||||||
QString mygrid = m_config.my_grid().left(4);
|
QString mygrid = m_config.my_grid().left(4);
|
||||||
|
|
||||||
// FT8Call Style
|
// FT8Call Style
|
||||||
|
if(m_txBeaconQueue.isEmpty()){
|
||||||
lines.append(QString("%1: BEACON %2").arg(mycall).arg(mygrid));
|
lines.append(QString("%1: BEACON %2").arg(mycall).arg(mygrid));
|
||||||
|
|
||||||
bool shouldTransmitTwoBeacons = true;
|
bool shouldTransmitTwoBeacons = true;
|
||||||
if(shouldTransmitTwoBeacons){
|
if(shouldTransmitTwoBeacons){
|
||||||
lines.append(QString("%1: BEACON %2").arg(mycall).arg(mygrid));
|
lines.append(QString("%1: BEACON %2").arg(mycall).arg(mygrid));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
while(!m_txBeaconQueue.isEmpty() && lines.length() < 2){
|
||||||
|
lines.append(m_txBeaconQueue.dequeue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Choose a beacon frequency
|
// Choose a beacon frequency
|
||||||
auto f = findFreeFreqOffset(500, 1000, 50);
|
auto f = findFreeFreqOffset(500, 1000, 50);
|
||||||
|
|
||||||
|
auto text = lines.join(QChar('\n'));
|
||||||
|
if(text.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Queue the beacon
|
// Queue the beacon
|
||||||
enqueueMessage(PriorityLow, lines.join(QChar('\n')), f, [this](){
|
enqueueMessage(PriorityLow, text, f, [this](){
|
||||||
m_nextBeaconQueued = false;
|
m_nextBeaconQueued = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -7616,6 +7650,8 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
// for now, we're going to omit displaying the call...delete this if we want the other functionality
|
// for now, we're going to omit displaying the call...delete this if we want the other functionality
|
||||||
call = "";
|
call = "";
|
||||||
|
|
||||||
|
auto grid = m_config.my_grid();
|
||||||
|
|
||||||
auto callAction = menu->addAction(QString("Send a directed message to selected callsign"));
|
auto callAction = menu->addAction(QString("Send a directed message to selected callsign"));
|
||||||
connect(callAction, &QAction::triggered, this, [this](){
|
connect(callAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
@ -7729,17 +7765,6 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
if(m_config.transmit_directed()) toggleTx(true);
|
if(m_config.transmit_directed()) toggleTx(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto qsoQueryAction = menu->addAction(QString("%1 QSO [CALLSIGN]? - Can you communicate directly with [CALLSIGN]?").arg(call).trimmed());
|
|
||||||
connect(qsoQueryAction, &QAction::triggered, this, [this](){
|
|
||||||
|
|
||||||
QString selectedCall = callsignSelected();
|
|
||||||
if(selectedCall.isEmpty()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessageText(QString("%1 QSO [CALLSIGN]?").arg(selectedCall), true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto hashAction = menu->addAction(QString("%1#[MESSAGE] - Please ACK if you receive this message in its entirety").arg(call).trimmed());
|
auto hashAction = menu->addAction(QString("%1#[MESSAGE] - Please ACK if you receive this message in its entirety").arg(call).trimmed());
|
||||||
hashAction->setDisabled(isAllCall);
|
hashAction->setDisabled(isAllCall);
|
||||||
connect(hashAction, &QAction::triggered, this, [this](){
|
connect(hashAction, &QAction::triggered, this, [this](){
|
||||||
@ -7778,10 +7803,22 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
addMessageText(QString("%1>[MESSAGE]").arg(selectedCall), true, true);
|
addMessageText(QString("%1>[MESSAGE]").arg(selectedCall), true, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto qsoQueryAction = menu->addAction(QString("%1 BEACON REQ [CALLSIGN]? - Please acknowledge you can communicate directly with [CALLSIGN]").arg(call).trimmed());
|
||||||
|
connect(qsoQueryAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
if(selectedCall.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessageText(QString("%1 BEACON REQ [CALLSIGN]?").arg(selectedCall), true, true);
|
||||||
|
});
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
bool emptyQTC = m_config.my_station().isEmpty();
|
bool emptyQTC = m_config.my_station().isEmpty();
|
||||||
bool emptyQTH = m_config.my_qth().isEmpty() && m_config.my_grid().isEmpty();
|
bool emptyQTH = m_config.my_qth().isEmpty();
|
||||||
|
bool emptyGrid = m_config.my_grid().isEmpty();
|
||||||
|
|
||||||
auto qtcAction = menu->addAction(QString("%1 QTC - Send my station message").arg(call).trimmed());
|
auto qtcAction = menu->addAction(QString("%1 QTC - Send my station message").arg(call).trimmed());
|
||||||
qtcAction->setDisabled(emptyQTC);
|
qtcAction->setDisabled(emptyQTC);
|
||||||
@ -7811,8 +7848,9 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
|||||||
if(m_config.transmit_directed()) toggleTx(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 Maidenhead grid locator").arg(call).arg(grid).trimmed());
|
auto gridAction = menu->addAction(QString("%1 GRID %2 - Send my current station Maidenhead grid locator").arg(call).arg(grid).trimmed());
|
||||||
|
gridAction->setDisabled(emptyGrid);
|
||||||
connect(gridAction, &QAction::triggered, this, [this](){
|
connect(gridAction, &QAction::triggered, this, [this](){
|
||||||
|
|
||||||
QString selectedCall = callsignSelected();
|
QString selectedCall = callsignSelected();
|
||||||
@ -8294,9 +8332,9 @@ void MainWindow::on_pbT2R_clicked()
|
|||||||
void MainWindow::on_beaconButton_clicked()
|
void MainWindow::on_beaconButton_clicked()
|
||||||
{
|
{
|
||||||
if(ui->beaconButton->isChecked()){
|
if(ui->beaconButton->isChecked()){
|
||||||
scheduleBacon(true);
|
scheduleBeacon(true);
|
||||||
} else {
|
} else {
|
||||||
pauseBacon();
|
pauseBeacon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9653,17 +9691,11 @@ void MainWindow::processCommandActivity() {
|
|||||||
|
|
||||||
// construct a reply, if needed
|
// construct a reply, if needed
|
||||||
QString reply;
|
QString reply;
|
||||||
|
int priority = PriorityNormal;
|
||||||
|
int freq = -1;
|
||||||
|
|
||||||
// QUERIED SNR
|
// QUERIED SNR
|
||||||
if (d.cmd == "?") {
|
if (d.cmd == "?" && !isAllCall) {
|
||||||
// do not respond to allcall ? if:
|
|
||||||
// 1. we recently responded to one
|
|
||||||
// 2. or, we are in a directed qso...(i.e., we have a callsign selected that isn't ALLCALL)
|
|
||||||
auto selectedCall = callsignSelected();
|
|
||||||
if(isAllCall && !selectedCall.isEmpty() && selectedCall != "ALLCALL" && selectedCall != d.from){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
|
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9810,8 +9842,22 @@ void MainWindow::processCommandActivity() {
|
|||||||
reply = m_lastTxMessage;
|
reply = m_lastTxMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROCESS BUFFERED QSO QUERY
|
// PROCESS BEACON
|
||||||
else if (d.cmd == " QSO"){
|
else if (d.cmd == " BEACON" && ui->beaconButton->isChecked()){
|
||||||
|
reply = QString("%1 BEACON ACK %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
|
||||||
|
|
||||||
|
enqueueBeacon(reply);
|
||||||
|
|
||||||
|
if(isAllCall){
|
||||||
|
// since all beacons are technically ALLCALL, let's bump the allcall cache here...
|
||||||
|
m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PROCESS BUFFERED BEACON REQ QUERY
|
||||||
|
else if (d.cmd == " BEACON REQ" && ui->beaconButton->isChecked()){
|
||||||
auto who = d.text;
|
auto who = d.text;
|
||||||
if(who.isEmpty()){
|
if(who.isEmpty()){
|
||||||
continue;
|
continue;
|
||||||
@ -9831,11 +9877,22 @@ void MainWindow::processCommandActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(baseCall == cd.call || baseCall == Radio::base_callsign(cd.call)){
|
if(baseCall == cd.call || baseCall == Radio::base_callsign(cd.call)){
|
||||||
auto r = QString("%1 ACK %2 %3 (%4)").arg(d.from).arg(cd.call).arg(Varicode::formatSNR(cd.snr)).arg(since(cd.utcTimestamp));
|
auto r = QString("%1 BEACON ACK %2").arg(cd.call).arg(Varicode::formatSNR(cd.snr));
|
||||||
replies.append(r);
|
replies.append(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reply = replies.join("\n");
|
reply = replies.join("\n");
|
||||||
|
|
||||||
|
if(!reply.isEmpty()){
|
||||||
|
enqueueBeacon(reply);
|
||||||
|
|
||||||
|
if(isAllCall){
|
||||||
|
// since all beacons are technically ALLCALL, let's bump the allcall cache here...
|
||||||
|
m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROCESS BUFFERED APRS:
|
// PROCESS BUFFERED APRS:
|
||||||
@ -9882,8 +9939,8 @@ void MainWindow::processCommandActivity() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not queue ALLCALL replies if auto-reply is not checked
|
// do not queue ALLCALL replies if auto-reply is not checked or it's a beacon reply
|
||||||
if(!ui->autoReplyButton->isChecked() && isAllCall){
|
if(!ui->autoReplyButton->isChecked() && isAllCall && !d.cmd.contains("BEACON")){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9896,7 +9953,7 @@ void MainWindow::processCommandActivity() {
|
|||||||
// unless, this is an allcall, to which we should be responding on a clear frequency offset
|
// unless, this is an allcall, to which we should be responding on a clear frequency offset
|
||||||
// we always want to make sure that the directed cache has been updated at this point so we have the
|
// we always want to make sure that the directed cache has been updated at this point so we have the
|
||||||
// most information available to make a frequency selection.
|
// most information available to make a frequency selection.
|
||||||
enqueueMessage(PriorityNormal, reply, -1, nullptr);
|
enqueueMessage(priority, reply, freq, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
mainwindow.h
10
mainwindow.h
@ -145,6 +145,7 @@ public slots:
|
|||||||
void prependMessageText(QString text);
|
void prependMessageText(QString text);
|
||||||
void addMessageText(QString text, bool clear=false, bool selectFirstPlaceholder=false);
|
void addMessageText(QString text, bool clear=false, bool selectFirstPlaceholder=false);
|
||||||
void enqueueMessage(int priority, QString message, int freq, Callback c);
|
void enqueueMessage(int priority, QString message, int freq, Callback c);
|
||||||
|
void enqueueBeacon(QString message);
|
||||||
void resetMessage();
|
void resetMessage();
|
||||||
void resetMessageUI();
|
void resetMessageUI();
|
||||||
void restoreMessage();
|
void restoreMessage();
|
||||||
@ -297,10 +298,10 @@ private slots:
|
|||||||
bool prepareNextMessageFrame();
|
bool prepareNextMessageFrame();
|
||||||
bool isFreqOffsetFree(int f, int bw);
|
bool isFreqOffsetFree(int f, int bw);
|
||||||
int findFreeFreqOffset(int fmin, int fmax, int bw);
|
int findFreeFreqOffset(int fmin, int fmax, int bw);
|
||||||
void scheduleBacon(bool first=false);
|
void scheduleBeacon(bool first=false);
|
||||||
void pauseBacon();
|
void pauseBeacon();
|
||||||
void checkBacon();
|
void checkBeacon();
|
||||||
void prepareBacon();
|
void prepareBeacon();
|
||||||
QString calculateDistance(QString const& grid, int *pDistance=nullptr);
|
QString calculateDistance(QString const& grid, int *pDistance=nullptr);
|
||||||
void on_driftSpinBox_valueChanged(int n);
|
void on_driftSpinBox_valueChanged(int n);
|
||||||
void on_driftSyncButton_clicked();
|
void on_driftSyncButton_clicked();
|
||||||
@ -772,6 +773,7 @@ private:
|
|||||||
QMap<int, QList<ActivityDetail>> m_bandActivity; // freq -> [(text, last timestamp), ...]
|
QMap<int, QList<ActivityDetail>> m_bandActivity; // freq -> [(text, last timestamp), ...]
|
||||||
QMap<int, MessageBuffer> m_messageBuffer; // freq -> (cmd, [frames, ...])
|
QMap<int, MessageBuffer> m_messageBuffer; // freq -> (cmd, [frames, ...])
|
||||||
QMap<QString, CallDetail> m_callActivity; // call -> (last freq, last timestamp)
|
QMap<QString, CallDetail> m_callActivity; // call -> (last freq, last timestamp)
|
||||||
|
QQueue<QString> m_txBeaconQueue; // beacon frames to be sent
|
||||||
|
|
||||||
QMap<QString, QMap<QString, CallDetail>> m_callActivityCache; // band -> call activity
|
QMap<QString, QMap<QString, CallDetail>> m_callActivityCache; // band -> call activity
|
||||||
QMap<QString, QMap<int, QList<ActivityDetail>>> m_bandActivityCache; // band -> band activity
|
QMap<QString, QMap<int, QList<ActivityDetail>>> m_bandActivityCache; // band -> band activity
|
||||||
|
47
varicode.cpp
47
varicode.cpp
@ -52,16 +52,18 @@ QMap<QString, int> directed_cmds = {
|
|||||||
|
|
||||||
// {"=", 9 }, // unused
|
// {"=", 9 }, // unused
|
||||||
// {"/", 10 }, // unused
|
// {"/", 10 }, // unused
|
||||||
// {"/", 11 }, // unused
|
|
||||||
// {"/", 12 }, // unused
|
|
||||||
// {"/", 13 }, // unused
|
|
||||||
|
|
||||||
// directed responses
|
// directed responses
|
||||||
{" QSO", 13 }, // can you communicate with? i can communicate with
|
{" BEACON", -1 }, // this is my beacon (unused except for faux processing of beacons as directed commands)
|
||||||
|
{" BEACON ACK", 12 }, // i received your beacon at this snr
|
||||||
|
{" BEACON REQ", 13 }, // can you transmit a beacon to callsign?
|
||||||
|
|
||||||
{" APRS:", 14 }, // send an aprs packet
|
{" APRS:", 14 }, // send an aprs packet
|
||||||
|
|
||||||
{" GRID", 15 }, // this is my current grid locator
|
{" GRID", 15 }, // this is my current grid locator
|
||||||
{" QTC", 16 }, // this is my qtc message
|
{" QTC", 16 }, // this is my qtc message
|
||||||
{" QTH", 17 }, // this is my qth 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
|
||||||
@ -78,14 +80,14 @@ QMap<QString, int> directed_cmds = {
|
|||||||
{" ", 31 }, // send freetext
|
{" ", 31 }, // send freetext
|
||||||
};
|
};
|
||||||
|
|
||||||
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, /*6,*/ /*7,*/ 8, /*...*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
|
QSet<int> allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, /*6,*/ /*7,*/ 8, /*...*/ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
|
||||||
|
|
||||||
QSet<int> buffered_cmds = {5, /*6,*/ /*7,*/ 8, 13, 14, 15};
|
QSet<int> buffered_cmds = {3, 5, /*6,*/ /*7,*/ 8, 13, 14, 15};
|
||||||
|
|
||||||
|
QSet<int> snr_cmds = {12, 25};
|
||||||
|
|
||||||
QMap<int, int> checksum_cmds = {
|
QMap<int, int> checksum_cmds = {
|
||||||
{ 5, 16 },
|
{ 5, 16 },
|
||||||
/*{ 6, 16 },*/
|
|
||||||
/*{ 7, 16 },*/
|
|
||||||
{ 8, 32 },
|
{ 8, 32 },
|
||||||
{ 13, 16 },
|
{ 13, 16 },
|
||||||
{ 14, 16 },
|
{ 14, 16 },
|
||||||
@ -93,10 +95,10 @@ QMap<int, int> checksum_cmds = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%#^> ]))?");
|
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|BEACON (ACK|REQ)|[?@&$%#^> ]))?");
|
||||||
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_num_pattern = QString("(?<num>(?<=SNR|HEARING)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
|
QString optional_num_pattern = QString("(?<num>(?<=SNR|HEARING|BEACON ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
|
||||||
|
|
||||||
QRegularExpression directed_re("^" +
|
QRegularExpression directed_re("^" +
|
||||||
callsign_pattern +
|
callsign_pattern +
|
||||||
@ -1084,11 +1086,13 @@ quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){
|
|||||||
|
|
||||||
// if cmd == snr
|
// if cmd == snr
|
||||||
quint8 value = 0;
|
quint8 value = 0;
|
||||||
if(cmd == directed_cmds[" SNR"]){
|
auto cmdStr = directed_cmds.key(cmd);
|
||||||
|
if(Varicode::isSNRCommand(cmdStr)){
|
||||||
// 8 bits - 1 bit flag + 1 bit type + 6 bit number
|
// 8 bits - 1 bit flag + 1 bit type + 6 bit number
|
||||||
// [1][X][6]
|
// [1][X][6]
|
||||||
// X = 0 == SNR
|
// X = 0 == SNR
|
||||||
value = (1 << 1) << 6;
|
// X = 1 == BEACON ACK
|
||||||
|
value = ((1 << 1) | (int)(cmdStr == " BEACON ACK")) << 6;
|
||||||
value = value + (num & ((1<<6)-1));
|
value = value + (num & ((1<<6)-1));
|
||||||
if(pPackedNum) *pPackedNum = true;
|
if(pPackedNum) *pPackedNum = true;
|
||||||
} else {
|
} else {
|
||||||
@ -1103,13 +1107,22 @@ quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){
|
|||||||
// if the first bit is 1, this is an SNR with a number encoded in the lower 6 bits
|
// if the first bit is 1, this is an SNR with a number encoded in the lower 6 bits
|
||||||
if(value & (1<<7)){
|
if(value & (1<<7)){
|
||||||
if(pNum) *pNum = value & ((1<<6)-1);
|
if(pNum) *pNum = value & ((1<<6)-1);
|
||||||
return directed_cmds[" SNR"];
|
|
||||||
|
auto cmd = directed_cmds[" SNR"];
|
||||||
|
if(value & (1<<6)){
|
||||||
|
cmd = directed_cmds[" BEACON ACK"];
|
||||||
|
}
|
||||||
|
return cmd;
|
||||||
} else {
|
} else {
|
||||||
if(pNum) *pNum = 0;
|
if(pNum) *pNum = 0;
|
||||||
return value & ((1<<7)-1);
|
return value & ((1<<7)-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Varicode::isSNRCommand(const QString &cmd){
|
||||||
|
return directed_cmds.contains(cmd) && snr_cmds.contains(directed_cmds[cmd]);
|
||||||
|
}
|
||||||
|
|
||||||
bool Varicode::isCommandAllowed(const QString &cmd){
|
bool Varicode::isCommandAllowed(const QString &cmd){
|
||||||
return directed_cmds.contains(cmd) && allowed_cmds.contains(directed_cmds[cmd]);
|
return directed_cmds.contains(cmd) && allowed_cmds.contains(directed_cmds[cmd]);
|
||||||
}
|
}
|
||||||
@ -1288,10 +1301,11 @@ QStringList Varicode::unpackCompoundMessage(const QString &text, quint8 *pType,
|
|||||||
} else if (nusergrid <= extra && extra < nmaxgrid) {
|
} else if (nusergrid <= extra && extra < nmaxgrid) {
|
||||||
quint8 num = 0;
|
quint8 num = 0;
|
||||||
auto cmd = Varicode::unpackCmd(extra - nusergrid, &num);
|
auto cmd = Varicode::unpackCmd(extra - nusergrid, &num);
|
||||||
|
auto cmdStr = directed_cmds.key(cmd);
|
||||||
|
|
||||||
unpacked.append(directed_cmds.key(cmd));
|
unpacked.append(cmdStr);
|
||||||
|
|
||||||
if(cmd == directed_cmds[" SNR"]){
|
if(Varicode::isSNRCommand(cmdStr)){
|
||||||
unpacked.append(Varicode::formatSNR(num - 31));
|
unpacked.append(Varicode::formatSNR(num - 31));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1498,8 +1512,7 @@ QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){
|
|||||||
unpacked.append(cmd);
|
unpacked.append(cmd);
|
||||||
|
|
||||||
if(extra != 0){
|
if(extra != 0){
|
||||||
// TODO: jsherer - should we decide which format to use on the command, or something else?
|
if(Varicode::isSNRCommand(cmd)){
|
||||||
if(packed_cmd == directed_cmds[" SNR"]) {
|
|
||||||
unpacked.append(Varicode::formatSNR((int)extra-31));
|
unpacked.append(Varicode::formatSNR((int)extra-31));
|
||||||
} else {
|
} else {
|
||||||
unpacked.append(QString("%1").arg(extra-31));
|
unpacked.append(QString("%1").arg(extra-31));
|
||||||
|
@ -123,6 +123,7 @@ public:
|
|||||||
static quint8 packCmd(quint8 cmd, quint8 num, bool *pPackedNum);
|
static quint8 packCmd(quint8 cmd, quint8 num, bool *pPackedNum);
|
||||||
static quint8 unpackCmd(quint8 value, quint8 *pNum);
|
static quint8 unpackCmd(quint8 value, quint8 *pNum);
|
||||||
|
|
||||||
|
static bool isSNRCommand(const QString &cmd);
|
||||||
static bool isCommandAllowed(const QString &cmd);
|
static bool isCommandAllowed(const QString &cmd);
|
||||||
static bool isCommandBuffered(const QString &cmd);
|
static bool isCommandBuffered(const QString &cmd);
|
||||||
static int isCommandChecksumed(const QString &cmd);
|
static int isCommandChecksumed(const QString &cmd);
|
||||||
|
Loading…
Reference in New Issue
Block a user