From 553f2400e563df9c2b1e2a3f1fe60e87ee140822 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 19 Jul 2018 23:14:11 -0400 Subject: [PATCH] Updated text decoding to support more commands as well as numerical options for those commands --- decodedtext.cpp | 12 +++++++-- mainwindow.cpp | 71 ++++++++++++++++++++++++++++++++++++++----------- mainwindow.h | 1 + mainwindow.ui | 3 +++ varicode.cpp | 61 +++++++++++++++++++++++++++--------------- 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/decodedtext.cpp b/decodedtext.cpp index b076cd5..bed5a55 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -52,6 +52,12 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString , contest_mode_ , grid_c_string.constData () , 22, 6); + + // We're only going to unpack standard messages for CQs && beacons... + // TODO: jsherer - this is a hack for now... + if(is_standard_){ + is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch(); + } } if(!is_standard_){ @@ -63,7 +69,6 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString if(!unpacked){ unpacked = tryUnpackData(); } - } } @@ -82,8 +87,11 @@ bool DecodedText::tryUnpackDirected(){ } if(parts.length() == 3){ - // replace it with the correct unpacked (query) + // replace it with the correct unpacked (directed) message_ = QString("%1: %2%3").arg(parts.at(0), parts.at(1), parts.at(2)); + } else if(parts.length() == 4){ + // replace it with the correct unpacked (directed numeric) + message_ = QString("%1: %2%3 %4").arg(parts.at(0), parts.at(1), parts.at(2), parts.at(3)); } else { // replace it with the correct unpacked (freetext) message_ = QString(parts.join(QChar())); diff --git a/mainwindow.cpp b/mainwindow.cpp index 981ff61..7c7c44c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5473,6 +5473,9 @@ QPair MainWindow::buildFT8MessageFrames(QString const& int m = 0; QString datFrame = Varicode::packDataMessage(line.left(21) + "\x04", &m); // 63 / 3 = 21 (maximum number of 3bit chars we could possibly stuff in here) + qDebug() << "parsing message line" << line; + qDebug() << "-> isFree?" << isFree << n << m; + // if this parses to a standard FT8 free text message // but it can be parsed as a directed message, then we // should send the directed version @@ -5522,7 +5525,7 @@ QPair MainWindow::buildFT8MessageFrames(QString const& } } -#if 1 +#if 0 qDebug() << "parsed frames:"; foreach(auto frame, frames){ qDebug() << "->" << frame << Varicode::unpackDataMessage(frame); @@ -5573,11 +5576,17 @@ QString MainWindow::parseFT8Message(QString input, bool *isFree){ int c = msgbytes[11]; int d = ((a & 15) << 12) + (b << 6) + c; + QString output = QString::fromLatin1(msgsent); + if(isFree){ - *isFree = (d >= 32768); + // TODO: jsherer - lets figure out a better way to detect this for the case: + // KN4CRD 16 + // this gets encoded as a standard message, but doesn't seem to make sense as to why... + // see decodedtext.cpp for the alternate decoding side of this... + *isFree = (d >= 32768) || !QRegularExpression("^(CQ|DE|QRZ)\\s").match(output).hasMatch(); } - return QString::fromLatin1(msgsent).trimmed(); + return output.trimmed(); } bool MainWindow::prepareNextMessageFrame() @@ -6861,13 +6870,7 @@ void MainWindow::on_snrMacroButton_clicked(){ } } -void MainWindow::on_queryButton_pressed(){ - QMenu *menu = ui->queryButton->menu(); - if(!menu){ - menu = new QMenu(ui->queryButton); - } - menu->clear(); - +void MainWindow::buildQueryMenu(QMenu * menu){ QString call = callsignSelected(); if(call.isEmpty()){ return; @@ -6877,7 +6880,6 @@ void MainWindow::on_queryButton_pressed(){ auto snrAction = menu->addAction("? - What is my signal report?"); - // TODO: jsherer - this should be extracted connect(snrAction, &QAction::triggered, this, [this](){ QString selectedCall = callsignSelected(); @@ -6939,7 +6941,30 @@ void MainWindow::on_queryButton_pressed(){ addMessageText(QString("%1 RR").arg(selectedCall), true); }); - auto sevenThreeAction = menu->addAction("73 - Best regards / end of contact"); + + auto yesAction = menu->addAction("YES - I confirm your last inquiry"); + connect(yesAction, &QAction::triggered, this, [this](){ + + QString selectedCall = callsignSelected(); + if(selectedCall.isEmpty()){ + return; + } + + addMessageText(QString("%1 YES").arg(selectedCall), true); + }); + + auto noAction = menu->addAction("NO - I do not confirm your last inquiry"); + connect(noAction, &QAction::triggered, this, [this](){ + + QString selectedCall = callsignSelected(); + if(selectedCall.isEmpty()){ + return; + } + + addMessageText(QString("%1 NO").arg(selectedCall), true); + }); + + auto sevenThreeAction = menu->addAction("73 - I send my best regards / end of contact"); connect(sevenThreeAction, &QAction::triggered, this, [this](){ QString selectedCall = callsignSelected(); @@ -6949,6 +6974,16 @@ void MainWindow::on_queryButton_pressed(){ addMessageText(QString("%1 73").arg(selectedCall), true); }); +} + +void MainWindow::on_queryButton_pressed(){ + QMenu *menu = ui->queryButton->menu(); + if(!menu){ + menu = new QMenu(ui->queryButton); + } + menu->clear(); + + buildQueryMenu(menu); ui->queryButton->setMenu(menu); ui->queryButton->showMenu(); @@ -8194,7 +8229,9 @@ void MainWindow::displayActivity(bool force){ bool isAllCall = d.to == "ALLCALL"; +#if 0 qDebug() << "processing command" << d.from << d.to << d.cmd << d.freq; +#endif // we're only processing a subset of queries at this point if(!Varicode::isCommandAllowed(d.cmd)){ @@ -8217,7 +8254,8 @@ void MainWindow::displayActivity(bool force){ // SNR if(d.cmd == "?"){ // standard FT8 reply - reply = QString("%1 %2 %3").arg(d.from).arg(m_config.my_callsign()).arg(d.snr); + // reply = QString("%1 %2 %3").arg(d.from).arg(m_config.my_callsign()).arg(d.snr); + reply = QString("%1 %2").arg(d.from).arg(d.snr); } // QTH else if(d.cmd == "@" && !isAllCall){ @@ -8228,10 +8266,11 @@ void MainWindow::displayActivity(bool force){ continue; } // standard FT8 reply - reply = QString("%1 %2 %3").arg(d.from).arg(m_config.my_callsign()).arg(grid); - } else { - reply = QString("%1 %2").arg(d.from).arg(qth); + // reply = QString("%1 %2 %3").arg(d.from).arg(m_config.my_callsign()).arg(grid); + qth = grid; } + + reply = QString("%1 %2").arg(d.from).arg(qth); } // STATION MESSAGE else if(d.cmd == "&" && !isAllCall){ diff --git a/mainwindow.h b/mainwindow.h index f677372..b373776 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -240,6 +240,7 @@ private slots: void on_replyMacroButton_clicked(); void on_qthMacroButton_clicked(); void on_snrMacroButton_clicked(); + void buildQueryMenu(QMenu *); void on_queryButton_pressed(); void on_macrosMacroButton_pressed(); void on_tableWidgetRXAll_cellClicked(int row, int col); diff --git a/mainwindow.ui b/mainwindow.ui index 9df12bc..d497dcf 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1123,6 +1123,9 @@ background:yellow; + + false + 0 diff --git a/varicode.cpp b/varicode.cpp index 1001cea..dc11dfd 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -43,24 +43,26 @@ QMap directed_cmds = { {"&", 3 }, // query station message //{"|", 4 }, // relay message - //{"+", 5 }, // report +snr - //{"-", 6 }, // report -snr - // ... + {" NO", 26 }, // negative confirm + {" YES", 27 }, // confirm {" 73", 28 }, // best regards, end of contact {" RR", 29 }, // confirm message {" AGN?", 30 }, // repeat message {" ", 31 }, // send freetext }; -QSet allowed_cmds = {0, 2, 3, 28, 29, 30, 31}; +QSet allowed_cmds = {0, 2, 3, 26, 27, 28, 29, 30, 31}; -QRegularExpression directed_re(R"(^(?:(?[A-Z0-9/]+):\s?)?(?[A-Z0-9/]+)(?(\s?(?:RR|AGN[?]|73)|[?$@&| ])))"); +QRegularExpression directed_re("^" + "(?[A-Z0-9/]+)" + "(?\\s?(?:AGN[?]|RR|73|YES|NO|[?$@&| ]))" + "(?\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); QMap huff = { // char code weight - {' ' , "001" }, // 1300 - {'E' , "000" }, // 1270.2 + {' ' , "000" }, // 1300 + {'E' , "001" }, // 1270.2 {'T' , "1100" }, // 905.6 {'A' , "1010" }, // 816.7 {'O' , "0111" }, // 750.7 @@ -517,12 +519,20 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi auto match = directed_re.match(text); if(match.hasMatch()){ - QString from = match.captured("from"); - if(from.isEmpty()){ - from = callsign; - } + QString from = callsign; QString to = match.captured("to"); QString cmd = match.captured("cmd"); + QString num = match.captured("num").trimmed(); + + int inum = -31; + bool hasnum = false; + if(!num.isEmpty()){ + inum = qMax(-30, qMin(num.toInt(&hasnum, 10), 30)); + } + + qDebug() << "match" << match.captured(0); + qDebug() << "groups" << from << to << cmd << num; + qDebug() << "packed num" << num << inum << hasnum; if(to == callsign){ *n = 0; @@ -535,11 +545,12 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi return frame; } - auto fromBytes = from.toLocal8Bit(); - auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU()); + // TODO: jsherer - we don't need this CRC... the FT8 msg already has a 12 bit CRC... + //auto fromBytes = from.toLocal8Bit(); + //auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU()); quint8 packed_is_data = 0; - quint8 packed_flag = 0; + quint8 packed_flag = inum < 0 ? 1 : 0; quint32 packed_from = Varicode::packCallsign(from); quint32 packed_to = Varicode::packCallsign(to); @@ -549,7 +560,7 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi } quint8 packed_cmd = directed_cmds[cmd]; - quint8 packed_extra = fromCRC; + quint8 packed_extra = qAbs(inum); // [1][2][28][28][5],[5] = 69 auto bits = ( @@ -563,6 +574,8 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi *n = match.captured(0).length(); return frame; } + + return frame; } QStringList Varicode::unpackDirectedMessage(const QString &text){ @@ -587,23 +600,29 @@ QStringList Varicode::unpackDirectedMessage(const QString &text){ QString from = Varicode::unpackCallsign(packed_from).trimmed(); - auto fromBytes = from.toLocal8Bit(); - auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU()); - if(fromCRC != extra){ - return unpacked; - } + // TODO: jsherer - we don't need this CRC... the FT8 msg already has a 12 bit CRC... + //auto fromBytes = from.toLocal8Bit(); + //auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU()); + //if(fromCRC != extra){ + // return unpacked; + //} unpacked.append(from); unpacked.append(Varicode::unpackCallsign(packed_to).trimmed()); unpacked.append(directed_cmds.key(packed_cmd & 31)); + int num = (flag ? -1 : 1) * extra; + if(num != -31){ + unpacked.append(QString(num > 0 ? "+%1" : "%1").arg(num)); + } + return unpacked; } QString Varicode::packDataMessage(const QString &text, int *n){ QString frame; - // [1][63],[5] + // [1][63],[5] = 69 quint8 is_data = 1; auto frameBits = ( Varicode::intToBits(is_data, 1)