diff --git a/decodedtext.cpp b/decodedtext.cpp index 8d61527..821225b 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -21,6 +21,9 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString , contest_mode_ {contest_mode} , message_ {string_.mid (column_qsoText + padding_).trimmed ()} , is_standard_ {false} + , frameType_(Varicode::FrameUnknown) + , isBeacon_(false) + , isAlt_(false) { if(message_.length() >= 1) { message_ = message_.left (21).remove (QRegularExpression {"[<>]"}); @@ -58,8 +61,12 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString tryUnpack(); } -DecodedText::DecodedText (QString const& ft8callmessage){ - message_ = ft8callmessage; +DecodedText::DecodedText (QString const& ft8callmessage): + frameType_(Varicode::FrameUnknown), + message_(ft8callmessage), + isBeacon_(false), + isAlt_(false) +{ is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch(); tryUnpack(); @@ -80,7 +87,6 @@ bool DecodedText::tryUnpack(){ unpacked = tryUnpackCompound(); } - if(!unpacked){ unpacked = tryUnpackDirected(); } @@ -101,7 +107,8 @@ bool DecodedText::tryUnpackBeacon(){ } bool isAlt = false; - QStringList parts = Varicode::unpackBeaconMessage(m, &isAlt); + quint8 type = Varicode::FrameUnknown; + QStringList parts = Varicode::unpackBeaconMessage(m, &type, &isAlt); if(parts.isEmpty() || parts.length() < 2){ return false; @@ -113,7 +120,7 @@ bool DecodedText::tryUnpackBeacon(){ // 1 1 CQ isBeacon_ = true; isAlt_ = isAlt; - extra_ = parts.at(2); + extra_ = parts.length() < 3 ? "" : parts.at(2); QStringList cmp; if(!parts.at(0).isEmpty()){ @@ -124,7 +131,7 @@ bool DecodedText::tryUnpackBeacon(){ } compound_ = cmp.join("/"); message_ = QString("%1: ALLCALL %2 %3 ").arg(compound_).arg(isAlt ? "CQ" : "BCN").arg(extra_); - + frameType_ = type; return true; } @@ -135,15 +142,12 @@ bool DecodedText::tryUnpackCompound(){ return false; } - auto parts = Varicode::unpackCompoundMessage(m); + quint8 type = Varicode::FrameUnknown; + auto parts = Varicode::unpackCompoundMessage(m, &type); if(parts.isEmpty() || parts.length() < 2){ return false; } - isBeacon_ = false; - - extra_ = parts.at(2); - QStringList cmp; if(!parts.at(0).isEmpty()){ cmp.append(parts.at(0)); @@ -152,13 +156,29 @@ bool DecodedText::tryUnpackCompound(){ cmp.append(parts.at(1)); } compound_ = cmp.join("/"); + extra_ = parts.length() < 3 ? "" : parts.mid(2).join(" "); - if(extra_.isEmpty()){ - message_ = QString("<%1> ").arg(compound_); - } else { - message_ = QString("<%1 %2> ").arg(compound_).arg(extra_); + bool hasPrefixSuffix = compound_.contains("/"); + + if(type == Varicode::FrameCompound){ +#if COMPOUND_SHOW_GRID + message_ = QString("<%1%2>:").arg(compound_).arg(extra_); +#endif + if(hasPrefixSuffix){ + message_ = QString("<%1>: ").arg(compound_); + } else { + message_ = QString("%1: ").arg(compound_); + } + } else if(type == Varicode::FrameCompoundDirected){ + if(hasPrefixSuffix){ + message_ = QString("<%1>%2").arg(compound_).arg(extra_); + } else { + message_ = QString("%1%2").arg(compound_).arg(extra_); + } + directed_ = QStringList{ "<....>", compound_ } + parts.mid(2); } + frameType_ = type; return true; } @@ -170,7 +190,8 @@ bool DecodedText::tryUnpackDirected(){ return false; } - QStringList parts = Varicode::unpackDirectedMessage(m); + quint8 type = Varicode::FrameUnknown; + QStringList parts = Varicode::unpackDirectedMessage(m, &type); if(parts.isEmpty()){ return false; @@ -188,6 +209,7 @@ bool DecodedText::tryUnpackDirected(){ } directed_ = parts; + frameType_ = type; return true; } @@ -199,13 +221,15 @@ bool DecodedText::tryUnpackData(){ return false; } - QString data = Varicode::unpackDataMessage(m); + quint8 type = Varicode::FrameUnknown; + QString data = Varicode::unpackDataMessage(m, &type); if(data.isEmpty()){ return false; } message_ = data; + frameType_ = type; return true; } diff --git a/decodedtext.h b/decodedtext.h index 29dc025..a75e7c6 100644 --- a/decodedtext.h +++ b/decodedtext.h @@ -39,10 +39,12 @@ public: bool tryUnpackDirected(); bool tryUnpackData(); + quint8 frameType() const { return frameType_; } + + QString extra() const { return extra_; } QString compoundCall() const { return compound_; } bool isCompound() const { return !compound_.isEmpty(); } - QString extra() const { return extra_; } bool isBeacon() const { return isBeacon_; } bool isAlt() const { return isAlt_; } @@ -96,6 +98,7 @@ private: column_mode = 19, column_qsoText = 22 }; + quint8 frameType_; bool isBeacon_; bool isAlt_; QString compound_; diff --git a/mainwindow.cpp b/mainwindow.cpp index cf1ed27..b91382a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3392,8 +3392,9 @@ void MainWindow::readFromStdout() //readFromStdout // Process compound callsign commands (put them in cache)" #if 1 + qDebug() << "decoded" << decodedtext.frameType() << decodedtext.isCompound() << decodedtext.isDirectedMessage() << decodedtext.isBeacon(); bool shouldProcessCompound = true; - if(shouldProcessCompound && decodedtext.isCompound()){ + if(shouldProcessCompound && decodedtext.isCompound() && !decodedtext.isDirectedMessage()){ CallDetail cd; cd.call = decodedtext.compoundCall(); cd.grid = decodedtext.extra(); // compound calls via beacons may contain grid... @@ -5905,7 +5906,9 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ bool useStd = false; bool useBcn = false; +#if ALLOW_SEND_COMPOUND bool useCmp = false; +#endif bool useDir = false; bool useDat = false; bool isFree = false; @@ -5914,18 +5917,20 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ int l = 0; QString bcnFrame = Varicode::packBeaconMessage(line, mycall, &l); +#if ALLOW_SEND_COMPOUND int o = 0; QString cmpFrame = Varicode::packCompoundMessage(line, &o); +#endif int n = 0; QString dirCmd; QString dirTo; - QString dirFrame = Varicode::packDirectedMessage(line, basecall, &dirTo, &dirCmd, &n); + QString dirNum; + QString dirFrame = Varicode::packDirectedMessage(line, basecall, &dirTo, &dirCmd, &dirNum, &n); + bool dirToCompound = dirTo.contains("/"); - // packDataMessage can output a new line to datLineOut (huff escaping special characters) int m = 0; - QString datLineOut; - QString datFrame = Varicode::packDataMessage(line.left(24) + "\x04", &datLineOut, &m); // 66 / 3 + 2 = 22 (maximum number of 3bit chars we could possibly stuff in here plus 2 for good measure :P) + QString datFrame = Varicode::packDataMessage(line.left(24) + "\x04", &m); // 66 / 3 + 2 = 22 (maximum number of 3bit chars we could possibly stuff in here plus 2 for good measure :P) // if this parses to a standard FT8 free text message // but it can be parsed as a directed message, then we @@ -5938,11 +5943,13 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ hasDirected = false; frame = bcnFrame; } +#if ALLOW_SEND_COMPOUND else if(isFree && !hasDirected && !hasData && o > 0){ useCmp = true; hasDirected = false; frame = cmpFrame; } +#endif else if(isFree && !hasDirected && !hasData && n > 0){ useDir = true; hasDirected = true; @@ -5952,9 +5959,6 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ useDat = true; hasData = true; frame = datFrame; - if(!datLineOut.isEmpty()){ - line = datLineOut; - } } else { useStd = true; frame = stdFrame; @@ -5981,32 +5985,52 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ line = line.mid(l); } +#if ALLOW_SEND_COMPOUND if(useCmp){ frames.append(frame); line = line.mid(o); } +#endif if(useDir){ - // from compound callsign - if(compound){ - QString compoundMessage = QString("<%1 %2>").arg(mycall).arg(mygrid); - QString compoundFrame = Varicode::packCompoundMessage(compoundMessage, nullptr); - if(!compoundFrame.isEmpty()){ - frames.append(compoundFrame); + /** + * We have a few special cases when we are sending to a compound call, or our call is a compound call, or both. + * CASE 0: Non-compound: KN4CRD: J1Y ACK + * -> One standard directed message frame + * + * CASE 1: Compound From: KN4CRD/P: J1Y ACK + * -> One standard compound frame, followed by a standard directed message frame with placeholder + * -> The second standard directed frame _could_ be replaced with a compound directed frame + * -> then <....>: J1Y ACK + * -> then + * + * CASE 2: Compound To: KN4CRD: J1Y/P ACK + * -> One standard compound frame, followed by a compound directed frame + * -> then + * + * CASE 3: Compound From & To: KN4CRD/P: J1Y/P ACK + * -> One standard compound frame, followed by a compound directed frame + * -> then + **/ + if(compound || dirToCompound){ + // Cases 1, 2, 3 all send a standard compound frame first... + QString deCompoundMessage = QString("<%1 %2>").arg(mycall).arg(mygrid); + QString deCompoundFrame = Varicode::packCompoundMessage(deCompoundMessage, nullptr); + if(!deCompoundFrame.isEmpty()){ + frames.append(deCompoundFrame); } - } - // to compound callsign - if(!dirTo.isEmpty() && dirTo.contains("/")){ - QString compoundMessage = QString("<%1>").arg(dirTo); - QString compoundFrame = Varicode::packCompoundMessage(compoundMessage, nullptr); - if(!compoundFrame.isEmpty()){ - frames.append(compoundFrame); + // Followed, by a standard OR compound directed message... + QString dirCompoundMessage = QString("<%1%2%3>").arg(dirTo).arg(dirCmd).arg(dirNum); + QString dirCompoundFrame = Varicode::packCompoundMessage(dirCompoundMessage, nullptr); + if(!dirCompoundFrame.isEmpty()){ + frames.append(dirCompoundFrame); } + } else { + // otherwise, just send the standard directed frame + frames.append(frame); } - frames.append(frame); - line = line.mid(n); // generate a checksum for buffered commands with line data @@ -6037,7 +6061,7 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ #if 1 qDebug() << "parsed frames:"; foreach(auto frame, frames){ - qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundFrame(frame, nullptr, nullptr) << Varicode::unpackBeaconMessage(frame, nullptr); + qDebug() << "->" << frame << DecodedText(frame).message(); } #endif @@ -6283,10 +6307,11 @@ void MainWindow::prepareBacon(){ } -QString MainWindow::calculateDistance(QString const& grid) +QString MainWindow::calculateDistance(QString const& value) { - if(grid.isEmpty()){ - return QString(); + QString grid = value.trimmed(); + if(grid.isEmpty() || grid.length() < 4){ + return QString{}; } qint64 nsec = (QDateTime::currentMSecsSinceEpoch()/1000) % 86400; diff --git a/varicode.cpp b/varicode.cpp index 9e3ea70..32d2660 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -68,16 +68,27 @@ QSet allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 8, 23, 24, 25, 26, 27, 28, 29, QSet buffered_cmds = {6, 7, 8}; -QRegularExpression directed_re("^" - "(?[A-Z0-9/]+)" - "(?\\s?(?:AGN[?]|RR|73|YES|NO|SNR|PWR|ACK|[?@&$^%|!# ]))" - "(?\\s?\\d+\\s?[KM]?W)?" - "(?\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?" - ); +QString callsign_pattern = QString("(?[A-Z0-9/]+)"); +QString optional_cmd_pattern = QString("(?\\s?(?:AGN[?]|RR|73|YES|NO|SNR|PWR|ACK|[?@&$^%|!# ]))?"); +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]))?"); + +QRegularExpression directed_re("^" + + callsign_pattern + + optional_cmd_pattern + + optional_pwr_pattern + + optional_num_pattern); QRegularExpression beacon_re(R"(^ALLCALL\s(?CQ|BCN)(?:\s(?[A-Z]{2}[0-9]{2}))?\b)"); -QRegularExpression compound_re(R"(^[<](?[A-Z0-9/]+)(?:\s(?[A-Z]{2}[0-9]{2}))?[>])"); +QRegularExpression compound_re("^[<]" + + callsign_pattern + + optional_grid_pattern + + optional_cmd_pattern + + optional_pwr_pattern + + optional_num_pattern + + "[>]"); QMap hufftable = { // char code weight @@ -305,6 +316,7 @@ QChar EOT = '\x04'; // EOT char quint32 nbasecall = 37 * 36 * 10 * 27 * 27 * 27; quint16 nbasegrid = 180 * 180; +quint16 nusergrid = nbasegrid + 10; quint16 nmaxgrid = (1<<15)-1; QMap basecalls = { @@ -955,14 +967,12 @@ QPair grid2deg(QString const &grid){ } // pack a 4-digit maidenhead grid locator into a 15-bit value -quint16 Varicode::packGrid(QString const& grid){ - // TODO: validate grid... +quint16 Varicode::packGrid(QString const& value){ + QString grid = QString(value).trimmed(); if(grid.length() < 4){ return (1<<15)-1; } - // TODO: encode non-grid data... - auto pair = grid2deg(grid.left(4)); int ilong = pair.first; int ilat = pair.second + 90; @@ -971,8 +981,7 @@ quint16 Varicode::packGrid(QString const& grid){ } QString Varicode::unpackGrid(quint16 value){ - if(value > 180*180){ - // TODO: decode non-grid data... for now just return an empty string... + if(value > nbasegrid){ return ""; } @@ -982,6 +991,81 @@ QString Varicode::unpackGrid(quint16 value){ return deg2grid(dlong, dlat).left(4); } +// pack a number or snr into an integer between 0 & 62 +quint8 Varicode::packNum(QString const &num, bool *ok){ + int inum = 0; + if(num.isEmpty()){ + if(ok) *ok = false; + return inum; + } + + inum = qMax(-30, qMin(num.toInt(ok, 10), 31)); + return inum + 30 + 1; +} + +// pack pwr string into a dbm between 0 and 62 +quint8 Varicode::packPwr(QString const &pwr, bool *ok){ + int ipwr = 0; + if(pwr.isEmpty()){ + if(ok) *ok = false; + return ipwr; + } + + int factor = 1000; + if(pwr.endsWith("KW")){ + factor = 1000000; + } + else if(pwr.endsWith("MW")){ + factor = 1; + } + + ipwr = QString(pwr).replace(QRegExp("[KM]?W", Qt::CaseInsensitive), "").toInt() * factor; + ipwr = mwattsToDbm(ipwr); + + if(ok) *ok = true; + return ipwr + 1; +} + +// 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 allowed = nmaxgrid - nbasegrid - 1; + + // if cmd == pwr || cmd == snr + quint8 value = 0; + if(cmd == directed_cmds[" PWR"] || cmd == directed_cmds[" SNR"]){ + // 8 bits - 1 bit flag + 1 bit type + 6 bit number + // [1][X][6] + // X = 0 == SNR + // X = 1 == PWR + + value = ((1 << 1) + ((int)cmd == directed_cmds[" PWR"])) << 6; + value = value + num; + } else { + value = cmd & ((1<<7)-1); + } + + return value; +} + +quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){ + // if the first bit is 1, the second bit will indicate if its pwr or snr... + if(value & (1<<7)){ + // either pwr or snr... + bool pwr = value & (1<<6); + + if(pNum) *pNum = value & ((1<<6)-1); + + if(pwr){ + return directed_cmds[" PWR"]; + } else { + return directed_cmds[" SNR"]; + } + } else { + if(pNum) *pNum = 0; + return value & ((1<<7)-1); + } +} + bool Varicode::isCommandAllowed(const QString &cmd){ return directed_cmds.contains(cmd) && allowed_cmds.contains(directed_cmds[cmd]); } @@ -1049,7 +1133,7 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign return frame; } -QStringList Varicode::unpackBeaconMessage(const QString &text, bool * isAlt){ +QStringList Varicode::unpackBeaconMessage(const QString &text, quint8 *pType, bool * isAlt){ quint8 type = FrameBeacon; quint16 num = nmaxgrid; @@ -1061,6 +1145,7 @@ QStringList Varicode::unpackBeaconMessage(const QString &text, bool * isAlt){ unpacked.append(Varicode::unpackGrid(num & ((1<<15)-1))); if(isAlt) *isAlt = (num & (1<<15)); + if(pType) *pType = type; return unpacked; } @@ -1078,8 +1163,11 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){ return frame; } - auto callsign = parsedText.captured("callsign"); - auto grid = parsedText.captured("grid"); + QString callsign = parsedText.captured("callsign"); + QString grid = parsedText.captured("grid"); + QString cmd = parsedText.captured("cmd"); + QString num = parsedText.captured("num").trimmed(); + QString pwr = parsedText.captured("pwr").trimmed().toUpper(); auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign); if(!parsedCall.hasMatch()){ @@ -1099,24 +1187,54 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){ fix = parsedCall.captured("suffix"); } - quint16 extra = Varicode::packGrid(grid); + quint8 type = FrameCompound; + quint16 extra = nmaxgrid; - frame = Varicode::packCompoundFrame(base, fix, isPrefix, FrameCompound, extra); + if (!cmd.isEmpty() && directed_cmds.contains(cmd) && Varicode::isCommandAllowed(cmd)){ + + qint8 inum = Varicode::packNum(num, nullptr); + if(cmd.trimmed() == "PWR"){ + inum = Varicode::packPwr(pwr, nullptr); + } + + extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum); + + type = FrameCompoundDirected; + } else if(!grid.isEmpty()){ + extra = Varicode::packGrid(grid); + } + + frame = Varicode::packCompoundFrame(base, fix, isPrefix, type, extra); if(n) *n = parsedText.captured(0).length(); return frame; } -QStringList Varicode::unpackCompoundMessage(const QString &text){ +QStringList Varicode::unpackCompoundMessage(const QString &text, quint8 *pType){ quint8 type = FrameCompound; - quint16 num = nmaxgrid; + quint16 extra = nmaxgrid; - QStringList unpacked = unpackCompoundFrame(text, &type, &num); - if(unpacked.isEmpty() || type != FrameCompound){ + QStringList unpacked = unpackCompoundFrame(text, &type, &extra); + if(unpacked.isEmpty() || (type != FrameCompound && type != FrameCompoundDirected)){ return QStringList {}; } - unpacked.append(Varicode::unpackGrid(num & ((1<<15)-1))); + if(extra <= nbasegrid){ + unpacked.append(" " + Varicode::unpackGrid(extra)); + } else if (nusergrid <= extra && extra < nmaxgrid) { + quint8 num = 0; + auto cmd = Varicode::unpackCmd(extra - nusergrid, &num); + + unpacked.append(directed_cmds.key(cmd)); + + if(cmd == directed_cmds[" PWR"]){ + unpacked.append(Varicode::formatPWR(num - 1)); + } else if(cmd == directed_cmds[" SNR"]){ + unpacked.append(Varicode::formatSNR(num - 31)); + } + } + + if(pType) *pType = type; return unpacked; } @@ -1124,7 +1242,7 @@ QStringList Varicode::unpackCompoundMessage(const QString &text){ QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, quint8 type, quint16 num){ QString frame; - // needs to be a beacon type... + // needs to be a compound type... if(type == FrameDataPadded || type == FrameDataUnpadded || type == FrameDirectedPositive || type == FrameDirectedNegative){ return frame; } @@ -1203,7 +1321,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu // J1Y? // KN4CRD: J1Y$ // KN4CRD: J1Y! HELLO WORLD -QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, QString *pTo, QString * pCmd, int *n){ +QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, QString *pTo, QString * pCmd, QString *pNum, int *n){ QString frame; auto match = directed_re.match(text); @@ -1213,11 +1331,17 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi } QString from = callsign; - QString to = match.captured("to"); + QString to = match.captured("callsign"); QString cmd = match.captured("cmd"); QString num = match.captured("num").trimmed(); QString pwr = match.captured("pwr").trimmed().toUpper(); + // ensure we have a directed command + if(cmd.isEmpty()){ + if(n) *n = 0; + return frame; + } + // validate "to" callsign auto parsedTo = QRegularExpression(compound_callsign_pattern).match(to); bool validToCallsign = (to != callsign) && (basecalls.contains(to) || parsedTo.hasMatch()); @@ -1242,27 +1366,16 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi } // packing general number... - int inum = -31; - bool hasnum = false; - if(!num.isEmpty()){ - inum = qMax(-30, qMin(num.toInt(&hasnum, 10), 30)); + quint8 inum = 0; + + if(cmd.trimmed() == "PWR"){ + inum = Varicode::packPwr(pwr, nullptr); + if(pNum) *pNum = pwr; + } else { + inum = Varicode::packNum(num, nullptr); + if(pNum) *pNum = num; } - // if we are packing a PWR command, pack pwr as dbm - int ipwr = -31; - if(!pwr.isEmpty() && cmd.trimmed() == "PWR"){ - int factor = 1000; - if(pwr.endsWith("KW")){ - factor = 1000000; - } - else if(pwr.endsWith("MW")){ - factor = 1; - } - ipwr = pwr.replace(QRegExp("[KM]?W", Qt::CaseInsensitive), "").toInt() * factor; - inum = mwattsToDbm(ipwr) - 30; - } - - quint8 packed_flag = inum < 0 ? FrameDirectedNegative : FrameDirectedPositive; quint32 packed_from = Varicode::packCallsign(from); quint32 packed_to = Varicode::packCallsign(to); @@ -1272,7 +1385,8 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi } quint8 packed_cmd = directed_cmds[cmd]; - quint8 packed_extra = qAbs(inum); + quint8 packed_flag = inum < 31 ? FrameDirectedNegative : FrameDirectedPositive; + quint8 packed_extra = inum < 31 ? inum : inum - 31; // [3][28][28][5],[5] = 69 auto bits = ( @@ -1287,7 +1401,7 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi return Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_extra % 32); } -QStringList Varicode::unpackDirectedMessage(const QString &text){ +QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){ QStringList unpacked; if(text.length() < 13 || text.contains(" ")){ @@ -1301,9 +1415,9 @@ QStringList Varicode::unpackDirectedMessage(const QString &text){ int numSign = 0; quint8 packed_flag = Varicode::bitsToInt(Varicode::strToBits(bits.left(3))); if(packed_flag == FrameDirectedPositive){ - numSign = 1; + numSign = 31; } else if(packed_flag == FrameDirectedNegative){ - numSign = -1; + numSign = 0; } else { return unpacked; } @@ -1320,22 +1434,23 @@ QStringList Varicode::unpackDirectedMessage(const QString &text){ unpacked.append(to); unpacked.append(cmd); - int num = numSign * extra; - if(num != -31){ + quint8 num = extra + numSign; + if(num != 0){ // TODO: jsherer - should we decide which format to use on the command, or something else? if(packed_cmd == directed_cmds[" PWR"]){ - unpacked.append(Varicode::formatPWR(num + 30)); + unpacked.append(Varicode::formatPWR(num-1)); } else if(packed_cmd == directed_cmds[" SNR"]) { - unpacked.append(Varicode::formatSNR(num)); + unpacked.append(Varicode::formatSNR((int)num-31)); } else { unpacked.append(QString("%1").arg(num)); } } + if(pType) *pType = packed_flag; return unpacked; } -QString Varicode::packDataMessage(const QString &input, QString * out, int *n){ +QString Varicode::packDataMessage(const QString &input, int *n){ QString frame; // [3][66] = 69 @@ -1380,7 +1495,7 @@ QString Varicode::packDataMessage(const QString &input, QString * out, int *n){ return frame; } -QString Varicode::unpackDataMessage(const QString &text){ +QString Varicode::unpackDataMessage(const QString &text, quint8 *pType){ QString unpacked; if(text.length() < 13 || text.contains(" ")){ @@ -1389,11 +1504,10 @@ QString Varicode::unpackDataMessage(const QString &text){ auto bits = Varicode::intToBits(Varicode::unpack64bits(text.left(12)), 64) + Varicode::intToBits(Varicode::unpack5bits(text.right(1)), 5); - quint8 flag = Varicode::bitsToInt(bits.mid(0, 3)); - - if(flag == FrameDataUnpadded){ + quint8 type = Varicode::bitsToInt(bits.mid(0, 3)); + if(type == FrameDataUnpadded){ bits = bits.mid(3); - } else if(flag == FrameDataPadded) { + } else if(type == FrameDataPadded) { int n = bits.lastIndexOf(0); bits = bits.mid(3, n-3); } else { @@ -1406,5 +1520,7 @@ QString Varicode::unpackDataMessage(const QString &text){ // then... unescape special characters unpacked = Varicode::huffUnescape(unpacked); + if(pType) *pType = type; + return unpacked; } diff --git a/varicode.h b/varicode.h index 3356d85..b59b5d2 100644 --- a/varicode.h +++ b/varicode.h @@ -27,14 +27,15 @@ public: }; enum FrameType { - FrameBeacon = 0, // [000] - FrameCompound = 1, // [001] - FrameDirectedPositive = 2, // [010] - FrameDirectedNegative = 3, // [011] - FrameDataUnpadded = 4, // [100] - FrameDataPadded = 5, // [101] - FrameReservedA = 6, // [110] - FrameReservedB = 7, // [111] + FrameUnknown = 255, // [11111111] <- only used as a sentinel + FrameBeacon = 0, // [000] + FrameCompound = 1, // [001] + FrameCompoundDirected = 2, // [010] + FrameDirectedPositive = 3, // [011] + FrameDirectedNegative = 4, // [100] + FrameDataUnpadded = 5, // [101] + FrameDataPadded = 6, // [110] + FrameReservedX = 7, // [111] }; //Varicode(); @@ -92,23 +93,28 @@ public: static quint16 packGrid(QString const& value); static QString unpackGrid(quint16 value); + 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 unpackCmd(quint8 value, quint8 *pNum); + static bool isCommandAllowed(const QString &cmd); static bool isCommandBuffered(const QString &cmd); static QString packBeaconMessage(QString const &text, QString const&callsign, int *n); - static QStringList unpackBeaconMessage(const QString &text, bool *isAlt); + static QStringList unpackBeaconMessage(const QString &text, quint8 *pType, bool *isAlt); static QString packCompoundMessage(QString const &text, int *n); - static QStringList unpackCompoundMessage(const QString &text); + static QStringList unpackCompoundMessage(const QString &text, quint8 *pType); static QString packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, quint8 type, quint16 num); static QStringList unpackCompoundFrame(const QString &text, quint8 *pType, quint16 *pNum); - static QString packDirectedMessage(QString const& text, QString const& callsign, QString *pTo, QString * pCmd, int *n); - static QStringList unpackDirectedMessage(QString const& text); + static QString packDirectedMessage(QString const& text, QString const& callsign, QString *pTo, QString * pCmd, QString *pNum, int *n); + static QStringList unpackDirectedMessage(QString const& text, quint8 *pType); - static QString packDataMessage(QString const& text, QString *out, int *n); - static QString unpackDataMessage(QString const& text); + static QString packDataMessage(QString const& text, int *n); + static QString unpackDataMessage(QString const& text, quint8 *pType); }; #endif // VARICODE_H