WSJT-X/decodedtext.cpp
Bill Somerville b048669c02 Merged from trunk:
------------------------------------------------------------------------
r8060 | k1jt | 2017-09-01 13:51:42 +0100 (Fri, 01 Sep 2017) | 2 lines

Add a link to G3WDG doc on using QRA64 for microwave EME.

------------------------------------------------------------------------
r8061 | k1jt | 2017-09-01 17:22:19 +0100 (Fri, 01 Sep 2017) | 1 line

Fix a misspelled word.
------------------------------------------------------------------------
r8062 | bsomervi | 2017-09-01 21:10:35 +0100 (Fri, 01 Sep 2017) | 7 lines

Rationalize NA contest mode

Generic message packing and unpacking routines now understand antipode
grid contest messages.  These messages  are now recognized as standard
messages in  message response processing and  dealt with appropriately
when contest mode is selected and applicable (currently FT8 and MSK144
only).
------------------------------------------------------------------------
r8063 | bsomervi | 2017-09-01 21:43:45 +0100 (Fri, 01 Sep 2017) | 1 line

Fix issue compiling with Qt older than v5.7
------------------------------------------------------------------------
r8064 | bsomervi | 2017-09-01 22:29:02 +0100 (Fri, 01 Sep 2017) | 7 lines

Fix issues with type 2 compound calls in contest mode

Message generation in  contest mode now generates the  correct Tx3 for
type 2 calls.

"<type-2> 73"  is a free  text so  needed special handling  in message
processing.
------------------------------------------------------------------------
r8065 | bsomervi | 2017-09-01 23:22:20 +0100 (Fri, 01 Sep 2017) | 11 lines

Improved message generation for type 2 calls in contest mode

These attempt  to ensure that  a prefix is  logged by the  QSO partner
even if the compound call holder user Tx3 to tail-end a QSO.

The  type  2 message  generation  options  are largely  overridden  in
contest mode as only a few options make sense. Key is that Tx1 may use
only the base call when calling split is necessary, this requires that
both Tx3 and Tx4 have the full compound call otherwise the QSO partner
will never see the  full call until it is possibly  too late i.e. post
logging.
------------------------------------------------------------------------
r8066 | bsomervi | 2017-09-02 00:28:44 +0100 (Sat, 02 Sep 2017) | 5 lines

Fix erroneous auto stop critera for auto reply in FT8

We cannot assume that a "DE <dx-call>  <anything>" is or is not for us
so  we must  continue calling  and  risk possible  QRM. Calling  split
avoids this issue.
------------------------------------------------------------------------



git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx-1.8@8067 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
2017-09-01 23:55:56 +00:00

219 lines
6.4 KiB
C++

#include "decodedtext.h"
#include <QStringList>
#include <QRegularExpression>
#include <QDebug>
extern "C" {
bool stdmsg_(char const * msg, bool contest_mode, char const * mygrid, int len_msg, int len_grid);
}
namespace
{
QRegularExpression words_re {R"(^(?:(?<word1>(?:CQ|DE|QRZ)(?:\s?DX|\s(?:[A-Z]{2}|\d{3}))|[A-Z0-9/]+)\s)(?:(?<word2>[A-Z0-9/]+)(?:\s(?<word3>[-+A-Z0-9]+)(?:\s(?<word4>(?:OOO|(?!RR73)[A-R]{2}[0-9]{2})))?)?)?)"};
}
DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString const& my_grid)
: string_ {the_string}
, padding_ {the_string.indexOf (" ") > 4 ? 2 : 0} // allow for
// seconds
, contest_mode_ {contest_mode}
, 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
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);
}
};
QStringList DecodedText::messageWords () const
{
if (is_standard_)
{
// extract up to the first four message words
return words_re.match (message_).capturedTexts ();
}
// simple word split for free text messages
auto words = message_.split (' ', QString::SkipEmptyParts);
// add whole message as item 0 to mimic RE capture list
words.prepend (message_);
return words;
}
QString DecodedText::CQersCall() const
{
QRegularExpression callsign_re {R"(^(CQ|DE|QRZ)(\s?DX|\s([A-Z]{2}|\d{3}))?\s(?<callsign>[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");
if (contest_mode_ && "R" == grid)
{
grid = match.captured ("word4");
}
}
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;
}