From d9d3e6fba3fe018882e793789c233e7d31e32aed Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sun, 25 Nov 2018 22:12:54 -0500 Subject: [PATCH] Added active/inactive flag and restructuring heartbeat --- decodedtext.cpp | 2 +- mainwindow.cpp | 153 ++++++++-------- mainwindow.h | 9 +- mainwindow.ui | 462 ++++++++++++++++++++++-------------------------- varicode.cpp | 58 ++++-- varicode.h | 2 + 6 files changed, 335 insertions(+), 351 deletions(-) diff --git a/decodedtext.cpp b/decodedtext.cpp index 650f98f..1d26837 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -140,7 +140,7 @@ bool DecodedText::tryUnpackHeartbeat(){ cmp.append(parts.at(1)); } compound_ = cmp.join("/"); - message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "HEARTBEAT").arg(extra_); + message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : Varicode::hbString(bits3)).arg(extra_); frameType_ = type; return true; } diff --git a/mainwindow.cpp b/mainwindow.cpp index 52af221..b7317f9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1405,21 +1405,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, // Don't block heartbeat's first run... m_lastTxTime = DriftingDateTime::currentDateTimeUtc().addSecs(-300); - - auto heartbeatNow = new QAction(QString("Send Heartbeat Now"), ui->heartbeatButton); - connect(heartbeatNow, &QAction::triggered, this, [this](){ - if(m_transmitting){ - return; - } - if(!ui->heartbeatButton->isChecked()){ - ui->heartbeatButton->setChecked(true); - } - scheduleHeartbeat(true); - }); - ui->heartbeatButton->setContextMenuPolicy(Qt::ActionsContextMenu); - ui->heartbeatButton->addAction(heartbeatNow); - - int width = 75; /* QList btns; @@ -1564,12 +1549,6 @@ void MainWindow::initializeDummyData(){ displayTextForFreq("HELLO BRAVE NEW WORLD \u2301 ", 42, DriftingDateTime::currentDateTimeUtc().addSecs(-300), false, true, true); displayActivity(true); - - ui->heartbeatButton->click(); - QTimer::singleShot(10000, this, [this](){ - m_idleMinutes = 61; - scheduleHeartbeat(true); - }); } void MainWindow::initialize_fonts () @@ -2361,8 +2340,8 @@ void rebuildMacQAction(QMenu *menu, QAction *existingAction){ void MainWindow::on_menuControl_aboutToShow(){ ui->actionEnable_Spotting->setChecked(ui->spotButton->isChecked()); + ui->actionEnable_Active->setChecked(ui->activeButton->isChecked()); ui->actionEnable_Auto_Reply->setChecked(ui->autoReplyButton->isChecked()); - ui->actionEnable_Heartbeat->setChecked(ui->heartbeatButton->isChecked()); ui->actionEnable_Selcall->setChecked(ui->selcalButton->isChecked()); } @@ -2374,8 +2353,8 @@ void MainWindow::on_actionEnable_Auto_Reply_toggled(bool checked){ ui->autoReplyButton->setChecked(checked); } -void MainWindow::on_actionEnable_Heartbeat_toggled(bool checked){ - ui->heartbeatButton->setChecked(checked); +void MainWindow::on_actionEnable_Active_toggled(bool checked){ + ui->activeButton->setChecked(checked); } void MainWindow::on_actionEnable_Selcall_toggled(bool checked){ @@ -2715,6 +2694,27 @@ void MainWindow::on_spotButton_toggled(bool checked){ resetPushButtonToggleText(ui->spotButton); } +void MainWindow::on_activeButton_toggled(bool checked){ +#if 0 + // clear the ping queue when you toggle the button + m_txHeartbeatQueue.clear(); + displayBandActivity(); + + // then process the action + if(checked){ + scheduleHeartbeat(false); + } else { + pauseHeartbeat(); + } +#endif + + // we call this so hb button disabled state is updated + updateButtonDisplay(); + + resetPushButtonToggleText(ui->activeButton); +} + +#if 0 void MainWindow::on_heartbeatButton_toggled(bool checked){ // clear the ping queue when you toggle the button m_txHeartbeatQueue.clear(); @@ -2729,6 +2729,7 @@ void MainWindow::on_heartbeatButton_toggled(bool checked){ resetPushButtonToggleText(ui->heartbeatButton); } +#endif void MainWindow::auto_tx_mode (bool state) { @@ -3852,7 +3853,7 @@ void MainWindow::readFromStdout() //readFromStdout // convert HEARTBEAT to a directed command and process... cmd.from = cd.call; cmd.to = "@ALLCALL"; - cmd.cmd = " HEARTBEAT"; + cmd.cmd = " ACTIVE"; cmd.snr = cd.snr; cmd.bits = cd.bits; cmd.grid = cd.grid; @@ -4569,7 +4570,7 @@ void MainWindow::guiUpdate() } else if(m_monitoring) { if (m_tx_watchdog) { tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}"); - tx_status_label.setText ("Idle watchdog"); + tx_status_label.setText ("Inactive watchdog"); } else { tx_status_label.setStyleSheet("QLabel{background-color: #00ff00}"); QString t; @@ -4594,24 +4595,22 @@ void MainWindow::guiUpdate() parts << t.date().toString("yyyy MMM dd"); ui->labUTC->setText(parts.join("\n")); +#if 0 auto delta = t.secsTo(m_nextHeartbeat); QString ping; - if(ui->heartbeatButton->isChecked()){ - if(heartbeatTimer.isActive()){ - if(delta > 0){ - ping = QString("%1 s").arg(delta); - } else { - ping = "queued!"; - } + if(heartbeatTimer.isActive()){ + if(delta > 0){ + ping = QString("%1 s").arg(delta); } else { - ping = "on demand"; + ping = "queued!"; } } else if (m_nextHeartPaused) { ping = "paused"; } else { - ping = "disabled"; + ping = "on demand"; } ui->labHeartbeat->setText(QString("Next Heartbeat: %1").arg(ping)); +#endif auto callLabel = m_config.my_callsign(); if(m_config.use_dynamic_grid() && !m_config.my_grid().isEmpty()){ @@ -5446,7 +5445,6 @@ void MainWindow::enqueueMessage(int priority, QString message, int freq, Callbac void MainWindow::enqueueHeartbeat(QString message){ m_txHeartbeatQueue.enqueue(message); - scheduleHeartbeat(true); } void MainWindow::resetMessage(){ @@ -5664,10 +5662,7 @@ bool MainWindow::prepareNextMessageFrame() updateTxButtonDisplay(); - if(ui->heartbeatButton->isChecked()){ - // bump ping - scheduleHeartbeat(false); - } + // TODO: bump heartbeat return true; } @@ -5726,10 +5721,10 @@ int MainWindow::findFreeFreqOffset(int fmin, int fmax, int bw){ return fmin; } +#if 0 // schedulePing void MainWindow::scheduleHeartbeat(bool first){ auto timestamp = DriftingDateTime::currentDateTimeUtc(); - auto orig = timestamp; // if we have the heartbeat interval disabled, return early, unless this is a "heartbeat now" if(!m_config.heartbeat() && !first){ @@ -5765,7 +5760,6 @@ void MainWindow::scheduleHeartbeat(bool first){ // pausePing void MainWindow::pauseHeartbeat(){ - ui->heartbeatButton->setChecked(false); m_nextHeartPaused = true; if(heartbeatTimer.isActive()){ @@ -5775,13 +5769,12 @@ void MainWindow::pauseHeartbeat(){ // unpausePing void MainWindow::unpauseHeartbeat(){ - ui->heartbeatButton->setChecked(true); scheduleHeartbeat(false); } // checkPing void MainWindow::checkHeartbeat(){ - if(!ui->heartbeatButton->isChecked()){ + if(m_config.heartbeat() <= 0){ return; } auto secondsUntilHeartbeat = DriftingDateTime::currentDateTimeUtc().secsTo(m_nextHeartbeat); @@ -5835,8 +5828,11 @@ void MainWindow::prepareHeartbeat(){ m_nextHeartbeatQueued = true; } +#endif +void MainWindow::checkHeartbeat(){ +} QString MainWindow::calculateDistance(QString const& value, int *pDistance, int *pAzimuth) { @@ -6496,6 +6492,16 @@ void MainWindow::on_clearAction_triggered(QObject * sender){ } } +void MainWindow::on_hbMacroButton_clicked(){ + QString mycall = m_config.my_callsign(); + QString mygrid = m_config.my_grid().left(4); + QString message = QString("%1: ACTIVE %2").arg(mycall).arg(mygrid).trimmed(); + + addMessageText(message); + + if(m_config.transmit_directed()) toggleTx(true); +} + void MainWindow::on_cqMacroButton_clicked(){ auto message = m_config.cq_message(); if(message.isEmpty()){ @@ -6911,7 +6917,7 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){ addMessageText(QString("%1>[MESSAGE]").arg(selectedCall), true, true); }); - auto qsoQueryAction = menu->addAction(QString("%1 HEARTBEAT REQ [CALLSIGN]? - Please acknowledge you can communicate directly with [CALLSIGN]").arg(call).trimmed()); + auto qsoQueryAction = menu->addAction(QString("%1 QUERY [CALLSIGN]? - Please acknowledge you can communicate directly with [CALLSIGN]").arg(call).trimmed()); connect(qsoQueryAction, &QAction::triggered, this, [this](){ QString selectedCall = callsignSelected(); @@ -6919,7 +6925,7 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){ return; } - addMessageText(QString("%1 HEARTBEAT REQ [CALLSIGN]?").arg(selectedCall), true, true); + addMessageText(QString("%1 QUERY [CALLSIGN]?").arg(selectedCall), true, true); }); menu->addSeparator(); @@ -7135,6 +7141,7 @@ QMap MainWindow::buildMacroValues(){ {"", m_config.my_qth()}, {"", m_config.cq_message()}, {"", m_config.reply_message()}, + {"", (ui->activeButton->isChecked() ? "ACTIVE" : "INACTIVE")}, }; auto selectedCall = callsignSelected(); @@ -8260,7 +8267,9 @@ void MainWindow::updateButtonDisplay(){ auto selectedCallsign = callsignSelected(true); bool emptyCallsign = selectedCallsign.isEmpty(); + bool isActive = ui->activeButton->isChecked(); + ui->hbMacroButton->setDisabled(isTransmitting || !isActive); ui->cqMacroButton->setDisabled(isTransmitting); ui->replyMacroButton->setDisabled(isTransmitting || emptyCallsign); ui->snrMacroButton->setDisabled(isTransmitting || emptyCallsign); @@ -8603,7 +8612,7 @@ void MainWindow::processRxActivity() { continue; } - if(d.isDirected && d.text.contains(": HEARTBEAT")){ + if(d.isDirected && d.text.contains(": ACTIVE")){ // TODO: HEARTBEAT continue; } @@ -8885,7 +8894,7 @@ void MainWindow::processCommandActivity() { cd.snr = d.snr; cd.freq = d.freq; cd.bits = d.bits; - cd.ackTimestamp = d.text.contains("HEARTBEAT ACK") || toMe ? d.utcTimestamp : QDateTime{}; + cd.ackTimestamp = d.text.contains(": ACK") || toMe ? d.utcTimestamp : QDateTime{}; cd.utcTimestamp = d.utcTimestamp; cd.tdrift = d.tdrift; logCallActivity(cd, true); @@ -8927,7 +8936,7 @@ void MainWindow::processCommandActivity() { bool shouldDisplay = true; // don't display ping allcalls - if(isAllCall && (d.cmd != " " || ad.text.contains(": HEARTBEAT"))){ // || d.cmd == " HEARTBEAT")){ + if(isAllCall && (d.cmd != " " || ad.text.contains(": ACTIVE"))){ // || d.cmd == " HEARTBEAT")){ shouldDisplay = false; } @@ -8938,7 +8947,7 @@ void MainWindow::processCommandActivity() { // ACKs are the most likely source of items to be overwritten (multiple responses at once)... // so don't overwrite those (i.e., print each on a new line) - bool shouldOverwrite = (!d.cmd.contains("HEARTBEAT ACK")); /* && isRecentOffset(d.freq);*/ + bool shouldOverwrite = (!d.cmd.contains(" ACK")); /* && isRecentOffset(d.freq);*/ if(shouldOverwrite && ui->textEditRX->find(d.utcTimestamp.time().toString(), QTextDocument::FindBackward)){ // ... maybe we could delete the last line that had this message on this frequency... @@ -9006,10 +9015,10 @@ void MainWindow::processCommandActivity() { // QUERIED ACTIVE else if (d.cmd == " STATUS?" && !isAllCall) { - if(m_idleMinutes < 10){ + if(ui->activeButton->isChecked()){ reply = QString("%1 ACTIVE").arg(d.from); } else { - reply = QString("%1 IDLE").arg(d.from); + reply = QString("%1 INACTIVE").arg(d.from); } } @@ -9142,22 +9151,18 @@ void MainWindow::processCommandActivity() { reply = m_lastTxMessage; } - // PROCESS HEARTBEAT - else if (d.cmd == " HEARTBEAT" && ui->heartbeatButton->isChecked() && ui->autoReplyButton->isChecked() && !ui->selcalButton->isChecked()){ - reply = QString("%1 HEARTBEAT ACK %2").arg(d.from).arg(Varicode::formatSNR(d.snr)); - - enqueueHeartbeat(reply); + // PROCESS ACTIVE HEARTBEAT + else if (d.cmd == " ACTIVE" && ui->autoReplyButton->isChecked() && !ui->selcalButton->isChecked()){ + reply = QString("%1 ACK %2").arg(d.from).arg(Varicode::formatSNR(d.snr)); if(isAllCall){ // since all pings are technically @ALLCALL, let's bump the allcall cache here... m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 5); } - - continue; } - // PROCESS BUFFERED HEARTBEAT REQ QUERY - else if (d.cmd == " HEARTBEAT REQ" && ui->heartbeatButton->isChecked()){ + // PROCESS BUFFERED QUERY + else if (d.cmd == " QUERY" && ui->autoReplyButton->isChecked() && !ui->selcalButton->isChecked()){ auto who = d.text; if(who.isEmpty()){ continue; @@ -9184,15 +9189,11 @@ void MainWindow::processCommandActivity() { reply = replies.join("\n"); if(!reply.isEmpty()){ - enqueueHeartbeat(reply); - if(isAllCall){ // since all pings are technically @ALLCALL, let's bump the allcall cache here... m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 25); } } - - continue; } // PROCESS BUFFERED APRS: @@ -9246,7 +9247,7 @@ void MainWindow::processCommandActivity() { } // do not queue @ALLCALL replies if auto-reply is not checked or it's a ping reply - if(!ui->autoReplyButton->isChecked() && isAllCall && !d.cmd.contains("HEARTBEAT")){ + if(!ui->autoReplyButton->isChecked() && isAllCall && !d.cmd.contains("ACTIVE")){ continue; } @@ -9428,8 +9429,7 @@ void MainWindow::processTxQueue(){ // check to see if this is a high priority message, or if we have autoreply enabled, or if this is a ping and the ping button is enabled if(message.priority >= PriorityHigh || - (ui->autoReplyButton->isChecked()) || - (ui->heartbeatButton->isChecked() && message.message.contains("HEARTBEAT")) + (ui->autoReplyButton->isChecked()) ){ // then try to set the frequency... setFreqOffsetForRestore(f, true); @@ -9470,7 +9470,9 @@ void MainWindow::displayBandActivity() { selectedOffset = selectedItems.first()->text().toInt(); } - bool pingEnabled = ui->heartbeatButton->isChecked(); +#if 0 + bool pingEnabled = m_config.heartbeat() > 0; +#endif ui->tableWidgetRXAll->setUpdatesEnabled(false); { @@ -9562,10 +9564,12 @@ void MainWindow::displayBandActivity() { if (!isOffsetSelected && activityAging && item.utcTimestamp.secsTo(now) / 60 >= activityAging) { continue; } +#if 0 if (!pingEnabled && (item.text.contains(": HEARTBEAT") || item.text.contains("HEARTBEAT ACK"))){ // hide pings if we're not pinging. continue; } +#endif if (item.text.isEmpty()) { continue; } @@ -9933,7 +9937,7 @@ void MainWindow::networkMessage(Message const &message) auto type = message.type(); - if(type == "HEARTBEAT"){ + if(type == "PING"){ return; } @@ -10611,11 +10615,12 @@ void MainWindow::tx_watchdog (bool triggered) if (m_auto) auto_tx_mode (false); stopTx(); tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}"); - tx_status_label.setText ("Idle watchdog"); + tx_status_label.setText ("Inactive watchdog"); - pauseHeartbeat(); - MessageBox::warning_message(this, QString("Attempting to transmit heartbeat, but you have been idle for more than %1 minutes.").arg(m_config.watchdog())); - unpauseHeartbeat(); + // if the watchdog is triggered...we're no longer active + ui->activeButton->setChecked(false); + + MessageBox::warning_message(this, QString("Attempting to transmit, but you have been inactive for more than %1 minutes.").arg(m_config.watchdog())); } else { diff --git a/mainwindow.h b/mainwindow.h index 71e5e7c..ffe37ad 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -173,8 +173,8 @@ private slots: void on_tx6_editingFinished(); void on_menuControl_aboutToShow(); void on_actionEnable_Spotting_toggled(bool checked); + void on_actionEnable_Active_toggled(bool checked); void on_actionEnable_Auto_Reply_toggled(bool checked); - void on_actionEnable_Heartbeat_toggled(bool checked); void on_actionEnable_Selcall_toggled(bool checked); void on_menuWindow_aboutToShow(); void on_actionShow_Fullscreen_triggered(bool checked); @@ -274,6 +274,7 @@ private slots: void on_rbGenMsg_clicked(bool checked); void on_rbFreeText_clicked(bool checked); void on_clearAction_triggered(QObject * sender); + void on_hbMacroButton_clicked(); void on_cqMacroButton_clicked(); void on_replyMacroButton_clicked(); void on_snrMacroButton_clicked(); @@ -311,11 +312,7 @@ private slots: bool prepareNextMessageFrame(); bool isFreqOffsetFree(int f, int bw); int findFreeFreqOffset(int fmin, int fmax, int bw); - void scheduleHeartbeat(bool first=false); - void pauseHeartbeat(); - void unpauseHeartbeat(); void checkHeartbeat(); - void prepareHeartbeat(); QString calculateDistance(QString const& grid, int *pDistance=nullptr, int *pAzimuth=nullptr); void on_driftSpinBox_valueChanged(int n); void on_driftSyncButton_clicked(); @@ -355,7 +352,7 @@ private slots: void on_selcalButton_toggled(bool checked); void on_tuneButton_toggled(bool checked); void on_spotButton_toggled(bool checked); - void on_heartbeatButton_toggled(bool checked); + void on_activeButton_toggled(bool checked); void on_actionMessage_averaging_triggered(); void on_actionFox_Log_triggered(); diff --git a/mainwindow.ui b/mainwindow.ui index 83a673f..7d169d2 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -359,7 +359,7 @@ QPushButton[oob="true"] { - + Callsign Qt::AlignCenter @@ -411,29 +411,6 @@ color : white; - - - - QLabel { -font-family: MS Shell Dlg 2; -font-size: 11pt; -color : white; -} - - - Next Heartbeat: disabled - - - Qt::RichText - - - Qt::AlignCenter - - - 5 - - - @@ -499,7 +476,7 @@ QPushButton:checked { QFrame::Plain - + QLayout::SetMinimumSize @@ -573,43 +550,6 @@ QPushButton:checked { - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - false - - - <html><head/><body><p>Enable or disable automatic station replies to directed queries</p></body></html> - - - ACTIVE - - - true - - - @@ -776,7 +716,7 @@ QPushButton:checked { - + true @@ -804,7 +744,7 @@ QPushButton:checked { - <html><head/><body><p>Enable or disable the automatic heartbeat transmission</p></body></html> + <html><head/><body><p>Register your station as active or idle</p></body></html> QPushButton { @@ -820,7 +760,7 @@ QPushButton:checked { } - HB + ACTIVE true @@ -1361,192 +1301,7 @@ QTextEdit[transmitting="true"] { 0 - - - - - 0 - 30 - - - - <html><head/><body><p>Send a directed message to another station</p></body></html> - - - Directed - - - - - - - 75 - 30 - - - - <html><head/><body><p>Stop transmitting</p></body></html> - - - Halt - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 20 - 10 - - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Send your station message</p></body></html> - - - QTC - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Send an SNR message</p></body></html> - - - SNR - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 20 - 10 - - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Reply to a CQ</p></body></html> - - - Reply - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Send a saved message</p></body></html> - - - Saved - - - - - - - - 0 - 0 - - - - - 0 - 30 - - - - <html><head/><body><p>Deselect the current callsign for directed messaging</p></body></html> - - - Deselect - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Send your station location message</p></body></html> - - - QTH - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Send a CQ message</p></body></html> - - - CQ - - - false - - - - false @@ -1578,6 +1333,207 @@ color:#555; + + + + + 75 + 30 + + + + <html><head/><body><p>Stop transmitting</p></body></html> + + + Halt + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 10 + + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send your station location message</p></body></html> + + + QTH + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + <html><head/><body><p>Deselect the current callsign for directed messaging</p></body></html> + + + Deselect + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send an SNR message</p></body></html> + + + SNR + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 10 + + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Reply to a CQ</p></body></html> + + + Reply + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send a CQ message</p></body></html> + + + CQ + + + false + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send your station message</p></body></html> + + + QTC + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send a saved message</p></body></html> + + + Saved + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Send a directed message to another station</p></body></html> + + + Directed + + + + + + + + 0 + 30 + + + + <html><head/><body><p align="justify">Send a Heartbeat message</p></body></html> + + + HB + + + @@ -4757,7 +4713,7 @@ list. The list can be maintained in Settings (F2). C&ontrol - + @@ -5578,12 +5534,12 @@ list. The list can be maintained in Settings (F2). Enable Spotting - + true - Enable Heartbeat + Enable Active diff --git a/varicode.cpp b/varicode.cpp index 2a9c188..495bc63 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -71,11 +71,12 @@ QMap directed_cmds = { {" TU", 9 }, // thank you {" ACTIVE", 10 }, // i have been active in the past 10 minutes - {" IDLE", 11 }, // i have not been active in the past 10 minutes + {" INACTIVE", 11 }, // i have not been active in the past 10 minutes - {" HEARTBEAT", -1 }, // this is my ping (unused except for faux processing of pings as directed commands) - {" HEARTBEAT ACK", 12 }, // i acknowledge your ping at this SNR - {" HEARTBEAT REQ", 13 }, // can you transmit a ping to callsign? + //{" HEARTBEAT", -1 }, // this is my ping (unused except for faux processing of pings as directed commands) + //{" HEARTBEAT ACK", 12 }, // i acknowledge your ping at this SNR + + {" QUERY", 13 }, // can you transmit a ping to callsign? {" APRS:", 14 }, // send an aprs packet @@ -103,7 +104,7 @@ QSet allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, 6, /*7,*/ 8, 9, 10, 11, 12, 13, QSet buffered_cmds = {3, 5, /*6,*/ /*7,*/ 8, 13, 14, 15}; -QSet snr_cmds = {12, 25}; +QSet snr_cmds = {25, 29}; QMap checksum_cmds = { { 5, 16 }, @@ -114,17 +115,17 @@ QMap checksum_cmds = { }; QString callsign_pattern = QString("(?[@]?[A-Z0-9/]+)"); -QString optional_cmd_pattern = QString("(?\\s?(?:HEARTBEAT (ACK|REQ)|AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|QRZ[?]|SNR[?]|QTC[?]|QTH[?]|GRID[?]|STATUS[?]|(?:(?:ACK|73|YES|NO|SNR|QSL|RR|SK|FB|QTH|QTC|GRID|ACTIVE|IDLE|TU)(?=[ ]|$))|[?*^&@#> ]))?"); +QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|QRZ[?]|SNR[?]|QTC[?]|QTH[?]|GRID[?]|STATUS[?]|(?:(?:QUERY|ACK|73|YES|NO|SNR|QSL|RR|SK|FB|QTH|QTC|GRID|INACTIVE|ACTIVE|TU)(?=[ ]|$))|[?*^&@#> ]))?"); QString optional_grid_pattern = QString("(?\\s?[A-R]{2}[0-9]{2})?"); QString optional_extended_grid_pattern = QString("^(?\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?"); -QString optional_num_pattern = QString("(?(?<=SNR|HEARTBEAT ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); +QString optional_num_pattern = QString("(?(?<=SNR|ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); QRegularExpression directed_re("^" + callsign_pattern + optional_cmd_pattern + optional_num_pattern); -QRegularExpression heartbeat_re(R"(^\s*(?CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|HEARTBEAT)(?:\s(?[A-R]{2}[0-9]{2}))?\b)"); +QRegularExpression heartbeat_re(R"(^\s*(?CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|ACTIVE)(?:\s(?[A-R]{2}[0-9]{2}))?\b)"); QRegularExpression compound_re("^\\s*[`]" + callsign_pattern + @@ -206,6 +207,12 @@ QMap cqs = { { 7, "CQ CQ CQ"}, }; +QMap hbs = { + { 0, "ACTIVE" }, + { 1, "INACTIVE" }, +}; + + QMap dbm2mw = { {0 , 1}, // 1mW {3 , 2}, // 2mW @@ -292,6 +299,13 @@ QString Varicode::cqString(int number){ return cqs[number]; } +QString Varicode::hbString(int number){ + if(!hbs.contains(number)){ + return QString{}; + } + return hbs[number]; +} + bool Varicode::startsWithCQ(QString text){ foreach(auto cq, cqs.values()){ if(text.startsWith(cq)){ @@ -301,6 +315,15 @@ bool Varicode::startsWithCQ(QString text){ return false; } +bool Varicode::startsWithHB(QString text){ + foreach(auto cq, hbs.values()){ + if(text.startsWith(cq)){ + return true; + } + } + return false; +} + QString Varicode::formatSNR(int snr){ if(snr < -60 || snr > 60){ return QString(); @@ -994,7 +1017,7 @@ quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){ // [1][X][6] // X = 0 == SNR // X = 1 == ACK - value = ((1 << 1) | (int)(cmdStr == " HEARTBEAT ACK")) << 6; + value = ((1 << 1) | (int)(cmdStr == " ACK")) << 6; value = value + (num & ((1<<6)-1)); if(pPackedNum) *pPackedNum = true; } else { @@ -1012,7 +1035,7 @@ quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){ auto cmd = directed_cmds[" SNR"]; if(value & (1<<6)){ - cmd = directed_cmds[" HEARTBEAT ACK"]; + cmd = directed_cmds[" ACK"]; } return cmd; } else { @@ -1121,7 +1144,7 @@ bool Varicode::isCompoundCallsign(const QString &callsign){ // CQCQCQ EM73 // CQ DX EM73 // CQ QRP EM73 -// HEARTBEAT EM73 +// ACTIVE EM73 QString Varicode::packHeartbeatMessage(QString const &text, const QString &callsign, int *n){ QString frame; @@ -1135,7 +1158,7 @@ QString Varicode::packHeartbeatMessage(QString const &text, const QString &calls // Heartbeat Alt Type // --------------- - // 1 0 HEARTBEAT + // 1 0 ACTIVE // 1 1 CQCQCQ auto type = parsedText.captured("type"); @@ -1151,12 +1174,13 @@ QString Varicode::packHeartbeatMessage(QString const &text, const QString &calls packed_extra = Varicode::packGrid(extra); } + quint8 cqNumber = cqs.key(type, 0); + if(isAlt){ packed_extra |= (1<<15); + cqNumber = hbs.key(type, 0); } - quint8 cqNumber = cqs.key(type, 0); - frame = packCompoundFrame(callsign, FrameHeartbeat, packed_extra, cqNumber); if(frame.isEmpty()){ if(n) *n = 0; @@ -1657,9 +1681,9 @@ QList> Varicode::buildMessageFrames( // and if this isn't a raw message (starting with "`")... then... if(!selectedCall.isEmpty() && !line.startsWith(selectedCall) && !line.startsWith("`")){ bool lineStartsWithBaseCall = ( - line.startsWith("@ALLCALL") || - line.contains("HEARTBEAT") || - Varicode::startsWithCQ(line) + line.startsWith("@ALLCALL") || + Varicode::startsWithCQ(line) || + Varicode::startsWithHB(line) ); #if AUTO_PREPEND_DIRECTED_ALLOW_TEXT_CALLSIGNS diff --git a/varicode.h b/varicode.h index 994f6d7..136aa6b 100644 --- a/varicode.h +++ b/varicode.h @@ -63,7 +63,9 @@ public: static QMap defaultHuffTable(); static QString cqString(int number); + static QString hbString(int number); static bool startsWithCQ(QString text); + static bool startsWithHB(QString text); static QString formatSNR(int snr); static QString formatPWR(int dbm);