Added active/inactive flag and restructuring heartbeat

This commit is contained in:
Jordan Sherer 2018-11-25 22:12:54 -05:00
parent 09cea086c7
commit d9d3e6fba3
6 changed files with 335 additions and 351 deletions

View File

@ -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;
}

View File

@ -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<QPushButton*> 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<QString, QString> MainWindow::buildMacroValues(){
{"<MYQTH>", m_config.my_qth()},
{"<MYCQ>", m_config.cq_message()},
{"<MYREPLY>", m_config.reply_message()},
{"<MYSTATUS>", (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
{

View File

@ -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();

View File

@ -359,7 +359,7 @@ QPushButton[oob=&quot;true&quot;] {
</string>
</property>
<property name="text">
<string/>
<string>Callsign</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@ -411,29 +411,6 @@ color : white;
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labHeartbeat">
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
font-size: 11pt;
color : white;
}</string>
</property>
<property name="text">
<string>Next Heartbeat: disabled</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>5</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@ -499,7 +476,7 @@ QPushButton:checked {
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QGridLayout" name="gridLayout_8" columnstretch="1,1,1,1,0" columnminimumwidth="75,75,75,75,0">
<layout class="QGridLayout" name="gridLayout_8" columnstretch="1,1,1,1" columnminimumwidth="75,75,75,75">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
@ -573,43 +550,6 @@ QPushButton:checked {
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="activeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable automatic station replies to directed queries&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>ACTIVE</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="monitorButton">
<property name="enabled">
@ -776,7 +716,7 @@ QPushButton:checked {
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="heartbeatButton">
<widget class="QPushButton" name="activeButton">
<property name="enabled">
<bool>true</bool>
</property>
@ -804,7 +744,7 @@ QPushButton:checked {
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable the automatic heartbeat transmission&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Register your station as active or idle&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
@ -820,7 +760,7 @@ QPushButton:checked {
}</string>
</property>
<property name="text">
<string>HB</string>
<string>ACTIVE</string>
</property>
<property name="checkable">
<bool>true</bool>
@ -1361,192 +1301,7 @@ QTextEdit[transmitting=&quot;true&quot;] {
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="11">
<widget class="QPushButton" name="queryButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a directed message to another station&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Directed</string>
</property>
</widget>
</item>
<item row="1" column="16">
<widget class="QPushButton" name="stopTxButton">
<property name="minimumSize">
<size>
<width>75</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stop transmitting&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Halt</string>
</property>
</widget>
</item>
<item row="1" column="14">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="qtcMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send your station message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>QTC</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="snrMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send an SNR message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>SNR</string>
</property>
</widget>
</item>
<item row="1" column="7">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="replyMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reply to a CQ&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reply</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QPushButton" name="macrosMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a saved message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Saved</string>
</property>
</widget>
</item>
<item row="1" column="12">
<widget class="QPushButton" name="deselectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Deselect the current callsign for directed messaging&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Deselect</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="qthMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send your station location message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>QTH</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="cqMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a CQ message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>CQ</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="15">
<widget class="QPushButton" name="startTxButton">
<property name="enabled">
<bool>false</bool>
@ -1578,6 +1333,207 @@ color:#555;
</property>
</widget>
</item>
<item row="1" column="17">
<widget class="QPushButton" name="stopTxButton">
<property name="minimumSize">
<size>
<width>75</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stop transmitting&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Halt</string>
</property>
</widget>
</item>
<item row="1" column="15">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="qthMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send your station location message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>QTH</string>
</property>
</widget>
</item>
<item row="1" column="13">
<widget class="QPushButton" name="deselectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Deselect the current callsign for directed messaging&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Deselect</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="snrMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send an SNR message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>SNR</string>
</property>
</widget>
</item>
<item row="1" column="8">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="replyMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reply to a CQ&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reply</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="cqMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a CQ message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>CQ</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QPushButton" name="qtcMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send your station message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>QTC</string>
</property>
</widget>
</item>
<item row="1" column="7">
<widget class="QPushButton" name="macrosMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a saved message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Saved</string>
</property>
</widget>
</item>
<item row="1" column="12">
<widget class="QPushButton" name="queryButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send a directed message to another station&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Directed</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="hbMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;Send a Heartbeat message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>HB</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_5">
@ -4757,7 +4713,7 @@ list. The list can be maintained in Settings (F2).</string>
<string>C&amp;ontrol</string>
</property>
<addaction name="actionEnable_Spotting"/>
<addaction name="actionEnable_Heartbeat"/>
<addaction name="actionEnable_Active"/>
<addaction name="actionEnable_Auto_Reply"/>
<addaction name="actionEnable_Selcall"/>
</widget>
@ -5578,12 +5534,12 @@ list. The list can be maintained in Settings (F2).</string>
<string>Enable Spotting</string>
</property>
</action>
<action name="actionEnable_Heartbeat">
<action name="actionEnable_Active">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Enable Heartbeat</string>
<string>Enable Active</string>
</property>
</action>
<action name="actionEnable_Auto_Reply">

View File

@ -71,11 +71,12 @@ QMap<QString, int> 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<int> allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, 6, /*7,*/ 8, 9, 10, 11, 12, 13,
QSet<int> buffered_cmds = {3, 5, /*6,*/ /*7,*/ 8, 13, 14, 15};
QSet<int> snr_cmds = {12, 25};
QSet<int> snr_cmds = {25, 29};
QMap<int, int> checksum_cmds = {
{ 5, 16 },
@ -114,17 +115,17 @@ QMap<int, int> checksum_cmds = {
};
QString callsign_pattern = QString("(?<callsign>[@]?[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\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("(?<cmd>\\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("(?<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_num_pattern = QString("(?<num>(?<=SNR|HEARTBEAT ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
QString optional_num_pattern = QString("(?<num>(?<=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*(?<type>CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|HEARTBEAT)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)");
QRegularExpression heartbeat_re(R"(^\s*(?<type>CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|ACTIVE)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)");
QRegularExpression compound_re("^\\s*[`]" +
callsign_pattern +
@ -206,6 +207,12 @@ QMap<quint32, QString> cqs = {
{ 7, "CQ CQ CQ"},
};
QMap<quint32, QString> hbs = {
{ 0, "ACTIVE" },
{ 1, "INACTIVE" },
};
QMap<int, int> 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<QPair<QString, int>> 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

View File

@ -63,7 +63,9 @@ public:
static QMap<QString, QString> 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);