#include "decodedtext.h" #include #include extern "C" { bool stdmsg_(const char* msg, int len); } namespace { QRegularExpression words_re {R"(^(?:(?(?:CQ|DE|QRZ)(?:\s?DX|\s(?:[A-Z]{2}|\d{3}))|[A-Z0-9/]+)\s)(?:(?[A-Z0-9/]+)(?:\sR\s)?(?:\s(?[-+A-Z0-9]+)(?:\s(?OOO))?)?)?)"}; } DecodedText::DecodedText (QString const& the_string) : string_ {the_string} , padding_ {the_string.indexOf (" ") > 4 ? 2 : 0} // allow for // seconds , message_ {string_.mid (column_qsoText + padding_).trimmed ()} , is_standard_ {false} { string_ = string_.left (column_qsoText + padding_ + 25); 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 is_standard_ = stdmsg_ ((message_ + " ").toLatin1 ().constData (),22); } }; QStringList DecodedText::messageWords () const { if (is_standard_) { // extract up to the first four message words return words_re.match (message_).capturedTexts (); } return message_.split (' '); // simple word split for free text messages } QString DecodedText::CQersCall() const { QRegularExpression callsign_re {R"(^(CQ|DE|QRZ)(\s?DX|\s([A-Z]{2}|\d{3}))?\s(?[A-Z0-9/]{2,})(\s[A-R]{2}[0-9]{2})?)"}; return callsign_re.match (message_).captured ("callsign"); } bool DecodedText::isJT65() const { return string_.indexOf("#") == column_mode + padding_; } bool DecodedText::isJT9() const { return string_.indexOf("@") == column_mode + padding_; } bool DecodedText::isTX() const { int i = string_.indexOf("Tx"); return (i >= 0 && i < 15); // TODO guessing those numbers. Does Tx ever move? } bool DecodedText::isLowConfidence () const { return QChar {'?'} == string_.mid (padding_ + column_qsoText + 21, 1); } int DecodedText::frequencyOffset() const { return string_.mid(column_freq + padding_,4).toInt(); } int DecodedText::snr() const { int i1=string_.indexOf(" ")+1; return string_.mid(i1,3).toInt(); } float DecodedText::dt() const { return string_.mid(column_dt + padding_,5).toFloat(); } /* 2343 -11 0.8 1259 # YV6BFE F6GUU R-08 2343 -19 0.3 718 # VE6WQ SQ2NIJ -14 2343 -7 0.3 815 # KK4DSD W7VP -16 2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02 0605 Tx 1259 # CQ VK3ACF QF22 */ // find and extract any report. Returns true if this is a standard message bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report) const { if (message_.size () < 1) return false; QStringList const& w = message_.split(" ",QString::SkipEmptyParts); if (w.size () && is_standard_ && (w[0] == myBaseCall || w[0].endsWith ("/" + myBaseCall) || w[0].startsWith (myBaseCall + "/") || (w.size () > 1 && !dxBaseCall.isEmpty () && (w[1] == dxBaseCall || w[1].endsWith ("/" + dxBaseCall) || w[1].startsWith (dxBaseCall + "/"))))) { QString tt=""; if(w.size() > 2) tt=w[2]; bool ok; auto i1=tt.toInt(&ok); if (ok and i1>=-50 and i1<50) { report = tt; } else { if (tt.mid(0,1)=="R") { i1=tt.mid(1).toInt(&ok); if(ok and i1>=-50 and i1<50) { report = tt.mid(1); } } } } return is_standard_; } // get the first text word, usually the call QString DecodedText::call() const { return words_re.match (message_).captured ("word1"); } // get the second word, most likely the de call and the third word, most likely grid void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) const { auto const& match = words_re.match (message_); call = match.captured ("word2"); grid = match.captured ("word3"); // auto msg = string_; // if(msg.mid(4,1)!=" ") msg=msg.mid(0,4)+msg.mid(6,-1); //Remove seconds from UTC // msg = msg.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText + padding_); // int i1 = msg.indexOf (" "); // call = msg.mid (i1 + 1); // int i2 = call.indexOf (" "); // if (" R " == call.mid (i2, 3)) // MSK144 contest mode report // { // grid = call.mid (i2 + 3, 4); // } // else // { // grid = call.mid (i2 + 1, 4); // } // call = call.left (i2).replace (">", ""); } unsigned DecodedText::timeInSeconds() const { return 3600 * string_.mid (column_time, 2).toUInt () + 60 * string_.mid (column_time + 2, 2).toUInt() + (padding_ ? string_.mid (column_time + 2 + padding_, 2).toUInt () : 0U); } /* 2343 -11 0.8 1259 # YV6BFE F6GUU R-08 2343 -19 0.3 718 # VE6WQ SQ2NIJ -14 2343 -7 0.3 815 # KK4DSD W7VP -16 2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02 0605 Tx 1259 # CQ VK3ACF QF22 */ QString DecodedText::report() const // returns a string of the SNR field with a leading + or - followed by two digits { int sr = snr(); if (sr<-50) sr = -50; else if (sr > 49) sr = 49; QString rpt; rpt.sprintf("%d",abs(sr)); if (sr > 9) rpt = "+" + rpt; else if (sr >= 0) rpt = "+0" + rpt; else if (sr >= -9) rpt = "-0" + rpt; else rpt = "-" + rpt; return rpt; }