diff --git a/decodedtext.cpp b/decodedtext.cpp index c68b403..0d572d0 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -130,7 +130,7 @@ bool DecodedText::tryUnpackBeacon(){ cmp.append(parts.at(1)); } compound_ = cmp.join("/"); - message_ = QString("%1: ALLCALL %2 %3 ").arg(compound_).arg(isAlt ? "CQ" : "BCN").arg(extra_); + message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? "CQCQCQ" : "BEACON").arg(extra_); frameType_ = type; return true; } diff --git a/mainwindow.cpp b/mainwindow.cpp index 0df15f5..6c0738b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5876,6 +5876,7 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ * -> One standard compound frame, followed by a compound directed frame * -> then **/ + bool shouldUseStandardFrame = true; if(compound || dirToCompound){ // Cases 1, 2, 3 all send a standard compound frame first... QString deCompoundMessage = QString("<%1 %2>").arg(mycall).arg(mygrid); @@ -5890,7 +5891,10 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ if(!dirCompoundFrame.isEmpty()){ frames.append(dirCompoundFrame); } - } else { + shouldUseStandardFrame = false; + } + + if(shouldUseStandardFrame) { // otherwise, just send the standard directed frame frames.append(frame); } @@ -5925,7 +5929,8 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ #if 1 qDebug() << "parsed frames:"; foreach(auto frame, frames){ - qDebug() << "->" << frame << DecodedText(frame).message(); + auto dt = DecodedText(frame); + qDebug() << "->" << frame << dt.message() << Varicode::frameTypeString(dt.frameType()); } #endif @@ -6134,7 +6139,7 @@ void MainWindow::prepareBacon(){ lines.append(beacon); // FT8Call Style - lines.append(QString("%1: BCN %2").arg(call).arg(grid)); + lines.append(QString("%1: BEACON %2").arg(call).arg(grid)); // Queue the beacon enqueueMessage(PriorityLow, lines.join(QChar('\n')), currentFreq(), nullptr); @@ -7262,11 +7267,7 @@ void MainWindow::on_clearAction_triggered(QObject * sender){ void MainWindow::on_cqMacroButton_clicked(){ QString call = m_config.my_callsign(); QString grid = m_config.my_grid().left(4); - if(call != Radio::base_callsign(call)){ - grid = ""; - } - - QString text = QString("CQ %1 %2").arg(call).arg(grid); + QString text = QString("%1: CQCQCQ %2").arg(call).arg(grid).trimmed(); addMessageText(text); } @@ -7475,7 +7476,7 @@ void MainWindow::buildQueryMenu(QMenu * menu){ toggleTx(true); }); - auto qslQueryAction = menu->addAction("QSL? - Did you copy my last transmission?"); + auto qslQueryAction = menu->addAction("QSL? - Did you receive my last transmission?"); connect(qslQueryAction, &QAction::triggered, this, [this](){ QString selectedCall = callsignSelected(); @@ -7487,31 +7488,18 @@ void MainWindow::buildQueryMenu(QMenu * menu){ toggleTx(true); }); - auto ackAction = menu->addAction("ACK - I acknowledge your last transmission"); - connect(ackAction, &QAction::triggered, this, [this](){ + auto qslAction = menu->addAction("QSL - I confirm I received your last transmission"); + connect(qslAction, &QAction::triggered, this, [this](){ QString selectedCall = callsignSelected(); if(selectedCall.isEmpty()){ return; } - addMessageText(QString("%1 ACK").arg(selectedCall), true); + addMessageText(QString("%1 QSL").arg(selectedCall), true); toggleTx(true); }); - auto rrAction = menu->addAction("RR - I received your last transmission"); - connect(rrAction, &QAction::triggered, this, [this](){ - - QString selectedCall = callsignSelected(); - if(selectedCall.isEmpty()){ - return; - } - - addMessageText(QString("%1 RR").arg(selectedCall), true); - toggleTx(true); - }); - - auto yesAction = menu->addAction("YES - I confirm your last inquiry"); connect(yesAction, &QAction::triggered, this, [this](){ diff --git a/varicode.cpp b/varicode.cpp index 027ad9e..2422f30 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -45,23 +45,23 @@ QMap directed_cmds = { {"$", 3 }, // query station(s) heard // {"^", 4 }, // query ack {"%", 5 }, // query pwr - {"|", 6 }, // relay message? - {"!", 7 }, // alert message? + {"|", 6 }, // retransmit message + {"!", 7 }, // alert message {"#", 8 }, // all or nothing message // {"=", 9 }, // unused? (can we even use equals?) // {"/", 10 }, // unused? (can we even use stroke?) // directed responses - {" QSL?", 21 }, // do you copy? - {" QSL", 22 }, // i copy - {" ACK", 23 }, // acknowledged + {" RR", 21 }, // roger roger (not visible in UI but still exists) + {" QSL?", 22 }, // do you copy? + {" QSL", 23 }, // i copy {" PWR", 24 }, // power level {" SNR", 25 }, // seen a station at the provided snr {" NO", 26 }, // negative confirm {" YES", 27 }, // confirm {" 73", 28 }, // best regards, end of contact - {" RR", 29 }, // confirm message + {" ACK", 29 }, // acknowledge {" AGN?", 30 }, // repeat message {" ", 31 }, // send freetext }; @@ -71,10 +71,10 @@ QSet allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 8, 21, 22, 23, 24, 25, 26, 27, QSet buffered_cmds = {6, 7, 8}; QString callsign_pattern = QString("(?[A-Z0-9/]+)"); -QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|QSL[?]?|RR|73|YES|NO|SNR|PWR|ACK|[?@&$^%|!# ]))?"); +QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|QSL[?]?|ACK|RR|73|YES|NO|SNR|PWR|[?@&$^%|!# ]))?"); QString optional_grid_pattern = QString("(?\\s?[A-R]{2}[0-9]{2})?"); -QString optional_pwr_pattern = QString("(?\\s?\\d+\\s?[KM]?W)?"); -QString optional_num_pattern = QString("(?\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); +QString optional_pwr_pattern = QString("(?(?<=PWR)\\s?\\d+\\s?[KM]?W)?"); +QString optional_num_pattern = QString("(?(?<=SNR)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); QRegularExpression directed_re("^" + callsign_pattern + @@ -82,14 +82,16 @@ QRegularExpression directed_re("^" + optional_pwr_pattern + optional_num_pattern); -QRegularExpression beacon_re(R"(^ALLCALL\s(?CQ|BCN)(?:\s(?[A-Z]{2}[0-9]{2}))?\b)"); +QRegularExpression beacon_re(R"(^(?CQCQCQ|BEACON)(?:\s(?[A-Z]{2}[0-9]{2}))?\b)"); -QRegularExpression compound_re("^[<]" + - callsign_pattern + - optional_grid_pattern + - optional_cmd_pattern + - optional_pwr_pattern + - optional_num_pattern + +QRegularExpression compound_re("^[<]" + + callsign_pattern + + "(?" + + optional_grid_pattern + + optional_cmd_pattern + + optional_pwr_pattern + + optional_num_pattern + + ")" + "[>]"); QMap hufftable = { @@ -323,8 +325,7 @@ quint16 nmaxgrid = (1<<15)-1; QMap basecalls = { { "<....>", nbasecall + 1 }, // incomplete callsign - { "CQCQCQ", nbasecall + 2 }, - { "ALLCALL", nbasecall + 3 }, + { "ALLCALL", nbasecall + 2 }, }; QMap dbm2mw = { @@ -1020,7 +1021,7 @@ quint8 Varicode::packPwr(QString const &pwr, bool *ok){ } // pack a reduced fidelity command and a number into the extra bits provided between nbasegrid and nmaxgrid -quint8 Varicode::packCmd(quint8 cmd, quint8 num){ +quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){ //quint8 allowed = nmaxgrid - nbasegrid - 1; // if cmd == pwr || cmd == snr @@ -1033,8 +1034,10 @@ quint8 Varicode::packCmd(quint8 cmd, quint8 num){ value = ((1 << 1) + ((int)cmd == directed_cmds[" PWR"])) << 6; value = value + num; + if(pPackedNum) *pPackedNum = true; } else { value = cmd & ((1<<7)-1); + if(pPackedNum) *pPackedNum = false; } return value; @@ -1067,8 +1070,8 @@ bool Varicode::isCommandBuffered(const QString &cmd){ return directed_cmds.contains(cmd) && buffered_cmds.contains(directed_cmds[cmd]); } -// ALLCALL CQ EM73 -// ALLCALL BCN EM73 +// CQCQCQ EM73 +// BEACON EM73 QString Varicode::packBeaconMessage(QString const &text, const QString &callsign, int *n){ QString frame; @@ -1082,11 +1085,11 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign // Beacon Alt Type // --------------- - // 1 0 BCN - // 1 1 CQ + // 1 0 BEACON + // 1 1 CQCQCQ auto type = parsedText.captured("type"); - auto isAlt = type == "CQ"; + auto isAlt = type.startsWith("CQ"); auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign); if(!parsedCall.hasMatch()){ @@ -1162,35 +1165,42 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){ QString num = parsedText.captured("num").trimmed(); QString pwr = parsedText.captured("pwr").trimmed().toUpper(); - auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign); - if(!parsedCall.hasMatch()){ - if(n) *n = 0; - return frame; - } - - auto base = parsedCall.captured("base"); - + QString base; + QString fix; bool isPrefix = false; - auto fix = parsedCall.captured("prefix"); - if(!fix.isEmpty()){ - isPrefix = true; - } - if(!isPrefix){ - fix = parsedCall.captured("suffix"); + if(basecalls.contains(callsign)){ + // if it's a basecall, use it verbatim with no prefix/suffix + base = callsign; + fix = ""; + } else { + // otherwise, parse the callsign for prefix/suffix + auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign); + if(!parsedCall.hasMatch()){ + if(n) *n = 0; + return frame; + } + + base = parsedCall.captured("base"); + fix = parsedCall.captured("prefix"); + if(!fix.isEmpty()){ + isPrefix = true; + } + if(!isPrefix){ + fix = parsedCall.captured("suffix"); + } } quint8 type = FrameCompound; quint16 extra = nmaxgrid; if (!cmd.isEmpty() && directed_cmds.contains(cmd) && Varicode::isCommandAllowed(cmd)){ - + bool packedNum = false; qint8 inum = Varicode::packNum(num, nullptr); - if(cmd.trimmed() == "PWR"){ + if(cmd == " PWR"){ inum = Varicode::packPwr(pwr, nullptr); } - - extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum); + extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum, &packedNum); type = FrameCompoundDirected; } else if(!grid.isEmpty()){ @@ -1343,7 +1353,9 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi return frame; } - if(parsedTo.hasMatch()){ + if(basecalls.contains(to)){ + if(pTo) *pTo = to; + } else if(parsedTo.hasMatch()){ if(pTo) *pTo = parsedTo.captured(0); auto parsedBase = parsedTo.captured("base"); @@ -1352,6 +1364,7 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi } } + // validate command if(!Varicode::isCommandAllowed(cmd)){ if(n) *n = 0; @@ -1435,7 +1448,7 @@ QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){ } else if(packed_cmd == directed_cmds[" SNR"]) { unpacked.append(Varicode::formatSNR((int)num-31)); } else { - unpacked.append(QString("%1").arg(num)); + unpacked.append(QString("%1").arg(num-31)); } } diff --git a/varicode.h b/varicode.h index b59b5d2..26d86c8 100644 --- a/varicode.h +++ b/varicode.h @@ -35,9 +35,29 @@ public: FrameDirectedNegative = 4, // [100] FrameDataUnpadded = 5, // [101] FrameDataPadded = 6, // [110] - FrameReservedX = 7, // [111] + FrameReserved = 7, // [111] }; + static const quint8 FrameTypeMax = 7; + + static QString frameTypeString(quint8 type) { + const char* FrameTypeStrings[] = { + "FrameBeacon", + "FrameCompound", + "FrameCompoundDirected", + "FrameDirectedPositive", + "FrameDirectedNegative", + "FrameDataUnpadded", + "FrameDataPadded", + "FrameReserved" + }; + + if(type > FrameTypeMax){ + return "FrameUnknown"; + } + return FrameTypeStrings[type]; + } + //Varicode(); static QString formatSNR(int snr); @@ -95,7 +115,7 @@ public: static quint8 packNum(QString const &num, bool *ok); static quint8 packPwr(QString const &pwr, bool *ok); - static quint8 packCmd(quint8 cmd, quint8 num); + static quint8 packCmd(quint8 cmd, quint8 num, bool *pPackedNum); static quint8 unpackCmd(quint8 value, quint8 *pNum); static bool isCommandAllowed(const QString &cmd);