From e649a375cfe57d9f162e3ea80d0a730f2da53c63 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 2 Aug 2018 01:38:47 -0400 Subject: [PATCH] Split beacon packing from compound packing --- decodedtext.cpp | 112 +++++++++++++++++++++++++++++++----------------- decodedtext.h | 3 +- mainwindow.cpp | 40 +++++++++++------ varicode.cpp | 82 ++++++++++++++++++++++++++++++----- varicode.h | 3 ++ 5 files changed, 175 insertions(+), 65 deletions(-) diff --git a/decodedtext.cpp b/decodedtext.cpp index 623f9fe..8d61527 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -17,47 +17,42 @@ namespace DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString const& my_grid) : string_ {the_string.left (the_string.indexOf (QChar::Nbsp))} // discard appended info - , padding_ {string_.indexOf (" ") > 4 ? 2 : 0} // allow for - // seconds + , padding_ {string_.indexOf (" ") > 4 ? 2 : 0} // allow for seconds , contest_mode_ {contest_mode} , message_ {string_.mid (column_qsoText + padding_).trimmed ()} , is_standard_ {false} { - if (message_.length() >= 1) - { - message_ = message_.left (21).remove (QRegularExpression {"[<>]"}); - int i1 = message_.indexOf ('\r'); - if (i1 > 0) - { - message_ = message_.left (i1 - 1); + if(message_.length() >= 1) { + message_ = message_.left (21).remove (QRegularExpression {"[<>]"}); + int i1 = message_.indexOf ('\r'); + if (i1 > 0) { + message_ = message_.left (i1 - 1); } - if (message_.contains (QRegularExpression {"^(CQ|QRZ)\\s"})) - { - // TODO this magic position 16 is guaranteed to be after the - // last space in a decoded CQ or QRZ message but before any - // appended DXCC entity name or worked before information - auto eom_pos = message_.indexOf (' ', 16); - // we always want at least the characters to position 16 - if (eom_pos < 16) eom_pos = message_.size () - 1; - // remove DXCC entity and worked B4 status. TODO need a better way to do this - message_ = message_.left (eom_pos + 1); - } - // stdmsg is a fortran routine that packs the text, unpacks it - // and compares the result - auto message_c_string = message_.toLocal8Bit (); - message_c_string += QByteArray {22 - message_c_string.size (), ' '}; - auto grid_c_string = my_grid.toLocal8Bit (); - grid_c_string += QByteArray {6 - grid_c_string.size (), ' '}; - is_standard_ = stdmsg_ (message_c_string.constData () - , 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 (message_.contains (QRegularExpression {"^(CQ|QRZ)\\s"})) { + // TODO this magic position 16 is guaranteed to be after the + // last space in a decoded CQ or QRZ message but before any + // appended DXCC entity name or worked before information + auto eom_pos = message_.indexOf (' ', 16); + // we always want at least the characters to position 16 + if (eom_pos < 16) eom_pos = message_.size () - 1; + // remove DXCC entity and worked B4 status. TODO need a better way to do this + message_ = message_.left (eom_pos + 1); + } + + // stdmsg is a fortran routine that packs the text, unpacks it + // and compares the result + auto message_c_string = message_.toLocal8Bit (); + message_c_string += QByteArray {22 - message_c_string.size (), ' '}; + auto grid_c_string = my_grid.toLocal8Bit (); + grid_c_string += QByteArray {6 - grid_c_string.size (), ' '}; + is_standard_ = stdmsg_ (message_c_string.constData (), 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(); + } } tryUnpack(); @@ -78,15 +73,20 @@ bool DecodedText::tryUnpack(){ bool unpacked = false; if(!unpacked){ - unpacked = tryUnpackBeacon(); + unpacked = tryUnpackBeacon(); } if(!unpacked){ - unpacked = tryUnpackDirected(); + unpacked = tryUnpackCompound(); + } + + + if(!unpacked){ + unpacked = tryUnpackDirected(); } if(!unpacked){ - unpacked = tryUnpackData(); + unpacked = tryUnpackData(); } return unpacked; @@ -104,7 +104,7 @@ bool DecodedText::tryUnpackBeacon(){ QStringList parts = Varicode::unpackBeaconMessage(m, &isAlt); if(parts.isEmpty() || parts.length() < 2){ - return false; + return false; } // Beacon Alt Type @@ -128,6 +128,40 @@ bool DecodedText::tryUnpackBeacon(){ return true; } +bool DecodedText::tryUnpackCompound(){ + auto m = message().trimmed(); + // directed calls will always be 12+ chars and contain no spaces. + if(m.length() < 12 || m.contains(' ')){ + return false; + } + + auto parts = Varicode::unpackCompoundMessage(m); + 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)); + } + if(!parts.at(1).isEmpty()){ + cmp.append(parts.at(1)); + } + compound_ = cmp.join("/"); + + if(extra_.isEmpty()){ + message_ = QString("<%1> ").arg(compound_); + } else { + message_ = QString("<%1 %2> ").arg(compound_).arg(extra_); + } + + return true; +} + bool DecodedText::tryUnpackDirected(){ QString m = message().trimmed(); diff --git a/decodedtext.h b/decodedtext.h index 2aa39b8..29dc025 100644 --- a/decodedtext.h +++ b/decodedtext.h @@ -35,11 +35,12 @@ public: bool tryUnpack(); bool tryUnpackBeacon(); + bool tryUnpackCompound(); bool tryUnpackDirected(); bool tryUnpackData(); QString compoundCall() const { return compound_; } - bool isCompoundMessage() const { return !compound_.isEmpty(); } + bool isCompound() const { return !compound_.isEmpty(); } QString extra() const { return extra_; } bool isBeacon() const { return isBeacon_; } diff --git a/mainwindow.cpp b/mainwindow.cpp index fad585f..aa5c178 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3365,7 +3365,7 @@ void MainWindow::readFromStdout() //readFromStdout ActivityDetail d; d.isLowConfidence = decodedtext.isLowConfidence(); d.isFree = !decodedtext.isStandardMessage(); - d.isCompound = decodedtext.isCompoundMessage(); + d.isCompound = decodedtext.isCompound(); d.isDirected = decodedtext.isDirectedMessage(); d.bits = decodedtext.bits(); d.freq = offset; @@ -3376,7 +3376,7 @@ void MainWindow::readFromStdout() //readFromStdout m_rxActivityQueue.append(d); // if we have a data frame, and a message buffer has been established, buffer it... - if(m_messageBuffer.contains(d.freq/10*10) && !decodedtext.isCompoundMessage() && !decodedtext.isDirectedMessage()){ + if(m_messageBuffer.contains(d.freq/10*10) && !decodedtext.isCompound() && !decodedtext.isDirectedMessage()){ qDebug() << "buffering data" << (d.freq/10*10) << d.text; m_messageBuffer[d.freq/10*10].msgs.append(d); } @@ -3391,7 +3391,7 @@ void MainWindow::readFromStdout() //readFromStdout // Process compound callsign commands (put them in cache)" #if 1 bool shouldProcessCompound = true; - if(shouldProcessCompound && decodedtext.isCompoundMessage()){ + if(shouldProcessCompound && decodedtext.isCompound()){ CallDetail cd; cd.call = decodedtext.compoundCall(); cd.grid = decodedtext.extra(); // compound calls via beacons may contain grid... @@ -5903,6 +5903,7 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ bool useStd = false; bool useBcn = false; + bool useCmp = false; bool useDir = false; bool useDat = false; bool isFree = false; @@ -5911,6 +5912,9 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ int l = 0; QString bcnFrame = Varicode::packBeaconMessage(line, mycall, &l); + int o = 0; + QString cmpFrame = Varicode::packCompoundMessage(line, &o); + int n = 0; QString dirCmd; QString dirTo; @@ -5932,6 +5936,11 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ hasDirected = false; frame = bcnFrame; } + else if(isFree && !hasDirected && !hasData && o > 0){ + useCmp = true; + hasDirected = false; + frame = cmpFrame; + } else if(isFree && !hasDirected && !hasData && n > 0){ useDir = true; hasDirected = true; @@ -5970,26 +5979,29 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){ line = line.mid(l); } + if(useCmp){ + frames.append(frame); + line = line.mid(o); + } + if(useDir){ // from compound callsign if(compound){ - QString compoundMessage = QString("DE %1").arg(mygrid); - QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, mycall, nullptr); - if(!beaconFrame.isEmpty()){ - frames.append(beaconFrame); + QString compoundMessage = QString("<%1 %2>").arg(mycall).arg(mygrid); + QString compoundFrame = Varicode::packCompoundMessage(compoundMessage, nullptr); + if(!compoundFrame.isEmpty()){ + frames.append(compoundFrame); } } frames.append(frame); // to compound callsign - if(!dirTo.isEmpty()){ - if(dirTo.contains("/")){ - QString compoundMessage = QString("CALL"); - QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, dirTo, nullptr); - if(!beaconFrame.isEmpty()){ - frames.append(beaconFrame); - } + if(!dirTo.isEmpty() && dirTo.contains("/")){ + QString compoundMessage = QString("<%1>").arg(dirTo); + QString compoundFrame = Varicode::packCompoundMessage(compoundMessage, nullptr); + if(!compoundFrame.isEmpty()){ + frames.append(compoundFrame); } } diff --git a/varicode.cpp b/varicode.cpp index dc91d6f..536db16 100644 --- a/varicode.cpp +++ b/varicode.cpp @@ -74,6 +74,10 @@ QRegularExpression directed_re("^" "(?\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?" ); +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}))?[>])"); + QMap hufftable = { // char code weight // 3 bits @@ -985,10 +989,12 @@ bool Varicode::isCommandBuffered(const QString &cmd){ return directed_cmds.contains(cmd) && buffered_cmds.contains(directed_cmds[cmd]); } +// ALLCALL CQ EM73 +// ALLCALL BCN EM73 QString Varicode::packBeaconMessage(QString const &text, const QString &callsign, int *n){ QString frame; - auto parsedText = QRegularExpression(R"(^ALLCALL\s(?CQ|BCN)(?:\s(?[A-Z]{2}[0-9]{2}))?\b)").match(text); + auto parsedText = beacon_re.match(text); if(!parsedText.hasMatch()){ if(n) *n = 0; return frame; @@ -1043,12 +1049,12 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign } QStringList Varicode::unpackBeaconMessage(const QString &text, bool * isAlt){ - quint8 type = 0; - quint16 num = 0; + quint8 type = FrameBeacon; + quint16 num = nmaxgrid; QStringList unpacked = unpackCompoundFrame(text, &type, &num); - if(unpacked.isEmpty()){ - return unpacked; + if(unpacked.isEmpty() || type != FrameBeacon){ + return QStringList{}; } unpacked.append(Varicode::unpackGrid(num & ((1<<15)-1))); @@ -1058,6 +1064,62 @@ QStringList Varicode::unpackBeaconMessage(const QString &text, bool * isAlt){ return unpacked; } + +// KN4CRD/XXXX EM73 +// XXXX/KN4CRD EM73 +// KN4CRD/P +QString Varicode::packCompoundMessage(QString const &text, int *n){ + QString frame; + + auto parsedText = compound_re.match(text); + if(!parsedText.hasMatch()){ + if(n) *n = 0; + return frame; + } + + auto callsign = parsedText.captured("callsign"); + auto grid = parsedText.captured("grid"); + + auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign); + if(!parsedCall.hasMatch()){ + if(n) *n = 0; + return frame; + } + + auto base = parsedCall.captured("base"); + + bool isPrefix = false; + auto fix = parsedCall.captured("prefix"); + if(!fix.isEmpty()){ + isPrefix = true; + } + + if(!isPrefix){ + fix = parsedCall.captured("suffix"); + } + + quint16 extra = Varicode::packGrid(grid); + + frame = Varicode::packCompoundFrame(base, fix, isPrefix, FrameCompound, extra); + + if(n) *n = parsedText.captured(0).length(); + return frame; +} + +QStringList Varicode::unpackCompoundMessage(const QString &text){ + quint8 type = FrameCompound; + quint16 num = nmaxgrid; + + QStringList unpacked = unpackCompoundFrame(text, &type, &num); + if(unpacked.isEmpty() || type != FrameCompound){ + return QStringList {}; + } + + unpacked.append(Varicode::unpackGrid(num & ((1<<15)-1))); + + return unpacked; +} + QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, quint8 type, quint16 num){ QString frame; @@ -1081,12 +1143,6 @@ QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString & quint8 packed_5 = num & mask5; // [3][28][22][11],[5] = 69 - /* - if(extra.isEmpty()){ - if(n) *n = 0; - return frame; - } - */ auto bits = ( Varicode::intToBits(packed_flag, 3) + Varicode::intToBits(packed_base, 28) + @@ -1142,6 +1198,10 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu return unpacked; } +// J1Y ACK +// J1Y? +// KN4CRD: J1Y$ +// KN4CRD: J1Y! HELLO WORLD QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, QString *pTo, QString * pCmd, int *n){ QString frame; diff --git a/varicode.h b/varicode.h index 275580d..3356d85 100644 --- a/varicode.h +++ b/varicode.h @@ -98,6 +98,9 @@ public: static QString packBeaconMessage(QString const &text, QString const&callsign, int *n); static QStringList unpackBeaconMessage(const QString &text, bool *isAlt); + static QString packCompoundMessage(QString const &text, int *n); + static QStringList unpackCompoundMessage(const QString &text); + 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);